name: ๐ Release & Publish
on:
push:
branches: [ main ]
workflow_dispatch:
inputs:
release_type:
description: 'Release type (auto, patch, minor, major)'
required: false
default: 'auto'
type: choice
options:
- auto
- patch
- minor
- major
dry_run:
description: 'Dry run (no actual release)'
required: false
default: false
type: boolean
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
# ============================================================================
# RELEASE VALIDATION
# ============================================================================
validate-release:
name: ๐ Validate Release Readiness
runs-on: ubuntu-latest
timeout-minutes: 10
outputs:
should-release: ${{ steps.check.outputs.should-release }}
release-type: ${{ steps.check.outputs.release-type }}
steps:
- name: ๐ฅ Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: ๐ข Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
registry-url: 'https://registry.npmjs.org'
- name: ๐ฆ Install Dependencies
run: npm ci --prefer-offline --no-audit
- name: ๐ Check Release Conditions
id: check
run: |
# Check if this is a manual dispatch with dry run
if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ inputs.dry_run }}" == "true" ]]; then
echo "should-release=dry-run" >> $GITHUB_OUTPUT
echo "release-type=${{ inputs.release_type }}" >> $GITHUB_OUTPUT
echo "๐งช Dry run mode enabled"
exit 0
fi
# Enhanced commit analysis for PR merges
echo "๐ Analyzing commits for release eligibility..."
# Get the last release tag
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [[ -z "$LAST_TAG" ]]; then
echo "๐ No previous tags found, checking last 10 commits"
COMMIT_RANGE="HEAD~10..HEAD"
else
echo "๐ Last release: $LAST_TAG"
COMMIT_RANGE="$LAST_TAG..HEAD"
fi
# Check for conventional commits that warrant a release
RELEASABLE_COMMITS=$(git log $COMMIT_RANGE --oneline --grep="^feat" --grep="^fix" --grep="^perf" --grep="^revert" --grep="BREAKING CHANGE" --extended-regexp)
# Also check commit messages directly (for squashed PR merges)
CONVENTIONAL_COMMITS=$(git log $COMMIT_RANGE --pretty=format:"%s" | grep -E '^(feat|fix|perf|revert)(\(.+\))?!?:' || true)
BREAKING_CHANGES=$(git log $COMMIT_RANGE --pretty=format:"%B" | grep -E 'BREAKING CHANGE|BREAKING:' || true)
if [[ -n "$RELEASABLE_COMMITS" ]] || [[ -n "$CONVENTIONAL_COMMITS" ]] || [[ -n "$BREAKING_CHANGES" ]]; then
echo "should-release=true" >> $GITHUB_OUTPUT
echo "release-type=auto" >> $GITHUB_OUTPUT
echo "โ
Releasable changes detected:"
[[ -n "$CONVENTIONAL_COMMITS" ]] && echo " ๐ Conventional commits found"
[[ -n "$BREAKING_CHANGES" ]] && echo " ๐ฅ Breaking changes found"
[[ -n "$RELEASABLE_COMMITS" ]] && echo " ๐ Releasable commits found"
else
echo "should-release=false" >> $GITHUB_OUTPUT
echo "release-type=none" >> $GITHUB_OUTPUT
echo "โน๏ธ No releasable changes found in range $COMMIT_RANGE"
echo "๐ก Tip: Use conventional commits (feat:, fix:, etc.) to trigger releases"
fi
# Log recent commits for debugging
echo "๐ Recent commits analyzed:"
git log $COMMIT_RANGE --oneline --max-count=5 || echo "No commits in range"
- name: ๐จ Build & Test
if: steps.check.outputs.should-release != 'false'
timeout-minutes: 10
run: |
npm run build
npm run test:unit:fast
npm run lint:check
npm run type-check
echo "โ
Pre-release validation completed"
# ============================================================================
# SEMANTIC RELEASE
# ============================================================================
semantic-release:
name: ๐ฆ Semantic Release
runs-on: ubuntu-latest
needs: validate-release
if: needs.validate-release.outputs.should-release != 'false'
timeout-minutes: 15
permissions:
contents: write
issues: write
pull-requests: write
id-token: write
outputs:
released: ${{ steps.release.outputs.released }}
version: ${{ steps.release.outputs.version }}
major: ${{ steps.release.outputs.major }}
minor: ${{ steps.release.outputs.minor }}
patch: ${{ steps.release.outputs.patch }}
steps:
- name: ๐ฅ Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: ๐ข Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
registry-url: 'https://registry.npmjs.org'
- name: ๐ฆ Install Dependencies
run: npm ci --prefer-offline --no-audit
- name: ๐จ Build Production Assets
run: |
npm run build:clean
npm run build:standalone
npm run build:templates
echo "โ
Production build completed"
- name: ๐ Create Distribution Package
run: |
mkdir -p dist
npm pack --pack-destination=dist
echo "โ
Distribution package created"
- name: ๐ Generate Documentation
run: |
npm run docs:generate
echo "โ
Documentation generated"
- name: ๐ Semantic Release
id: release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
if [[ "${{ needs.validate-release.outputs.should-release }}" == "dry-run" ]]; then
echo "๐งช Running semantic-release in dry-run mode"
npm run release:dry
echo "released=false" >> $GITHUB_OUTPUT
echo "version=dry-run" >> $GITHUB_OUTPUT
else
echo "๐ Running semantic-release"
npm run release
# Check if a release was actually created
if git describe --tags --exact-match HEAD 2>/dev/null; then
NEW_VERSION=$(git describe --tags --exact-match HEAD | sed 's/^v//')
echo "released=true" >> $GITHUB_OUTPUT
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "major=$(echo $NEW_VERSION | cut -d. -f1)" >> $GITHUB_OUTPUT
echo "minor=$(echo $NEW_VERSION | cut -d. -f2)" >> $GITHUB_OUTPUT
echo "patch=$(echo $NEW_VERSION | cut -d. -f3)" >> $GITHUB_OUTPUT
echo "โ
Released version $NEW_VERSION"
else
echo "released=false" >> $GITHUB_OUTPUT
echo "version=none" >> $GITHUB_OUTPUT
echo "โน๏ธ No release created (no releasable changes)"
fi
fi
- name: ๐ Release Summary
run: |
echo "## ๐ Release Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Released | ${{ steps.release.outputs.released }} |" >> $GITHUB_STEP_SUMMARY
echo "| Version | ${{ steps.release.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
echo "| Trigger | ${{ github.event_name }} |" >> $GITHUB_STEP_SUMMARY
echo "| Branch | ${{ github.ref_name }} |" >> $GITHUB_STEP_SUMMARY
echo "| Commit | ${{ github.sha }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [[ "${{ steps.release.outputs.released }}" == "true" ]]; then
echo "๐ **Successfully released version ${{ steps.release.outputs.version }}!**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### ๐ฆ Package Information" >> $GITHUB_STEP_SUMMARY
echo "- **NPM Package**: [@dipseth/dataproc-mcp-server@${{ steps.release.outputs.version }}](https://www.npmjs.com/package/@dipseth/dataproc-mcp-server/v/${{ steps.release.outputs.version }})" >> $GITHUB_STEP_SUMMARY
echo "- **GitHub Release**: [v${{ steps.release.outputs.version }}](https://github.com/${{ github.repository }}/releases/tag/v${{ steps.release.outputs.version }})" >> $GITHUB_STEP_SUMMARY
echo "- **Installation**: \`npm install @dipseth/dataproc-mcp-server@${{ steps.release.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY
else
echo "โน๏ธ **No release created** - No releasable changes detected." >> $GITHUB_STEP_SUMMARY
fi
# ============================================================================
# DOCUMENTATION UPDATE
# ============================================================================
update-documentation:
name: ๐ Update Documentation
runs-on: ubuntu-latest
needs: semantic-release
if: needs.semantic-release.outputs.released == 'true'
timeout-minutes: 10
permissions:
contents: write
pull-requests: write
steps:
- name: ๐ฅ Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: ๐ข Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
registry-url: 'https://registry.npmjs.org'
- name: ๐ฆ Install Dependencies
run: npm ci --prefer-offline --no-audit
- name: ๐ Update README Version Badges
run: |
echo "๐ Updating README version badges..."
NEW_VERSION="${{ needs.semantic-release.outputs.version }}"
# Update npm version badge
sed -i "s|npm/v/@dipseth/dataproc-mcp-server\.svg|npm/v/@dipseth/dataproc-mcp-server.svg|g" README.md
# Update package references in README
sed -i "s|@dipseth/dataproc-mcp-server@[0-9]\+\.[0-9]\+\.[0-9]\+|@dipseth/dataproc-mcp-server@$NEW_VERSION|g" README.md
# Update installation commands
sed -i "s|npm install -g @dataproc/mcp-server|npm install -g @dipseth/dataproc-mcp-server@$NEW_VERSION|g" README.md
echo "โ
README version badges updated to v$NEW_VERSION"
- name: ๐ Update Documentation Links
run: |
echo "๐ Updating documentation links..."
NEW_VERSION="${{ needs.semantic-release.outputs.version }}"
# Update package.json version references in docs
find docs/ -name "*.md" -type f -exec sed -i "s|@dipseth/dataproc-mcp-server@[0-9]\+\.[0-9]\+\.[0-9]\+|@dipseth/dataproc-mcp-server@$NEW_VERSION|g" {} \;
# Update docs/index.md with latest version
if [ -f "docs/index.md" ]; then
sed -i "s|version: [0-9]\+\.[0-9]\+\.[0-9]\+|version: $NEW_VERSION|g" docs/index.md
fi
echo "โ
Documentation links updated"
- name: ๐ Regenerate Documentation
run: |
echo "๐ Regenerating documentation..."
# Regenerate API documentation with new version
npm run docs:generate
# Update GitHub Pages if needed
if [ -f "docs/_config.yml" ]; then
sed -i "s|version: [0-9]\+\.[0-9]\+\.[0-9]\+|version: ${{ needs.semantic-release.outputs.version }}|g" docs/_config.yml
fi
echo "โ
Documentation regenerated"
- name: ๐ Update Package Metrics
run: |
echo "๐ Updating package metrics..."
NEW_VERSION="${{ needs.semantic-release.outputs.version }}"
# Create or update package info file
cat > docs/package-info.json << EOF
{
"name": "@dipseth/dataproc-mcp-server",
"version": "$NEW_VERSION",
"released": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"npmUrl": "https://www.npmjs.com/package/@dipseth/dataproc-mcp-server/v/$NEW_VERSION",
"githubRelease": "https://github.com/${{ github.repository }}/releases/tag/v$NEW_VERSION",
"installCommand": "npm install @dipseth/dataproc-mcp-server@$NEW_VERSION"
}
EOF
echo "โ
Package metrics updated"
- name: ๐ Update NPM Documentation
run: |
echo "๐ Updating NPM package documentation..."
# Ensure package.json has correct repository and homepage URLs
npm pkg set homepage="https://dipseth.github.io/dataproc-mcp/"
npm pkg set repository.url="git+https://github.com/${{ github.repository }}.git"
npm pkg set bugs.url="https://github.com/${{ github.repository }}/issues"
# Update package keywords for better discoverability
npm pkg set keywords='["mcp", "dataproc", "google-cloud", "model-context-protocol", "typescript", "nodejs"]'
echo "โ
NPM documentation metadata updated"
- name: ๐ Create Documentation Update Summary
run: |
echo "## ๐ Documentation Update Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Updated documentation for version **v${{ needs.semantic-release.outputs.version }}**:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### โ
Updated Components" >> $GITHUB_STEP_SUMMARY
echo "- ๐ README.md version badges and installation commands" >> $GITHUB_STEP_SUMMARY
echo "- ๐ Documentation links and version references" >> $GITHUB_STEP_SUMMARY
echo "- ๐ Regenerated API documentation" >> $GITHUB_STEP_SUMMARY
echo "- ๐ Package metrics and metadata" >> $GITHUB_STEP_SUMMARY
echo "- ๐ NPM package documentation" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### ๐ Updated Links" >> $GITHUB_STEP_SUMMARY
echo "- **NPM Package**: [v${{ needs.semantic-release.outputs.version }}](https://www.npmjs.com/package/@dipseth/dataproc-mcp-server/v/${{ needs.semantic-release.outputs.version }})" >> $GITHUB_STEP_SUMMARY
echo "- **GitHub Pages**: [Documentation](https://dipseth.github.io/dataproc-mcp/)" >> $GITHUB_STEP_SUMMARY
echo "- **Installation**: \`npm install @dipseth/dataproc-mcp-server@${{ needs.semantic-release.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY
- name: ๐พ Commit Documentation Updates
run: |
# Configure git
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
# Check if there are changes to commit
if git diff --quiet && git diff --staged --quiet; then
echo "โน๏ธ No documentation changes to commit"
else
echo "๐ Committing documentation updates..."
git add .
git commit -m "docs: update documentation for v${{ needs.semantic-release.outputs.version }}
- Update README version badges and installation commands
- Update documentation links and version references
- Regenerate API documentation
- Update package metrics and NPM metadata
[skip ci]"
# Pull latest changes to avoid non-fast-forward errors
echo "๐ Pulling latest changes..."
git pull origin main --rebase
# Push with retry logic
echo "๐ Pushing documentation updates..."
for i in {1..3}; do
if git push origin main; then
echo "โ
Documentation updates committed and pushed"
break
else
echo "โ ๏ธ Push failed (attempt $i/3), retrying..."
git pull origin main --rebase
sleep 2
fi
done
fi
# ============================================================================
# POST-RELEASE VALIDATION
# ============================================================================
post-release-validation:
name: โ
Post-Release Validation
runs-on: ubuntu-latest
needs: [semantic-release, update-documentation]
if: needs.semantic-release.outputs.released == 'true'
timeout-minutes: 10
steps:
- name: ๐ฅ Checkout Repository
uses: actions/checkout@v4
- name: ๐ข Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- name: ๐ Verify NPM Package
run: |
echo "๐ Verifying NPM package publication..."
# Wait for NPM registry propagation
sleep 30
# Check if package is available
PACKAGE_VERSION="${{ needs.semantic-release.outputs.version }}"
if npm view @dipseth/dataproc-mcp-server@$PACKAGE_VERSION version; then
echo "โ
Package @dipseth/dataproc-mcp-server@$PACKAGE_VERSION is available on NPM"
else
echo "โ Package not found on NPM registry"
exit 1
fi
- name: ๐งช Test Package Installation
run: |
echo "๐งช Testing package installation..."
# Create temporary directory and test installation
mkdir -p /tmp/test-install
cd /tmp/test-install
npm init -y
npm install @dipseth/dataproc-mcp-server@${{ needs.semantic-release.outputs.version }}
# Verify installation
if [ -f "node_modules/@dipseth/dataproc-mcp-server/package.json" ]; then
echo "โ
Package installation successful"
INSTALLED_VERSION=$(node -p "require('./node_modules/@dipseth/dataproc-mcp-server/package.json').version")
echo "๐ฆ Installed version: $INSTALLED_VERSION"
else
echo "โ Package installation failed"
exit 1
fi
- name: ๐ Verify GitHub Release
run: |
echo "๐ Verifying GitHub release..."
# Check if GitHub release exists
RELEASE_TAG="v${{ needs.semantic-release.outputs.version }}"
if gh release view $RELEASE_TAG --repo ${{ github.repository }}; then
echo "โ
GitHub release $RELEASE_TAG exists"
else
echo "โ GitHub release not found"
exit 1
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ============================================================================
# NOTIFICATION & CLEANUP
# ============================================================================
notify-success:
name: ๐ข Success Notification
runs-on: ubuntu-latest
needs: [semantic-release, post-release-validation]
if: needs.semantic-release.outputs.released == 'true' && needs.post-release-validation.result == 'success'
steps:
- name: ๐ Success Notification
run: |
echo "## ๐ Release v${{ needs.semantic-release.outputs.version }} Successfully Published!" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### ๐ฆ Package Details" >> $GITHUB_STEP_SUMMARY
echo "- **NPM Package**: [@dipseth/dataproc-mcp-server@${{ needs.semantic-release.outputs.version }}](https://www.npmjs.com/package/@dipseth/dataproc-mcp-server)" >> $GITHUB_STEP_SUMMARY
echo "- **GitHub Release**: [v${{ needs.semantic-release.outputs.version }}](https://github.com/${{ github.repository }}/releases/tag/v${{ needs.semantic-release.outputs.version }})" >> $GITHUB_STEP_SUMMARY
echo "- **Installation**: \`npm install @dipseth/dataproc-mcp-server@latest\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### ๐ Next Steps" >> $GITHUB_STEP_SUMMARY
echo "1. Update documentation if needed" >> $GITHUB_STEP_SUMMARY
echo "2. Announce the release to the community" >> $GITHUB_STEP_SUMMARY
echo "3. Monitor for any issues or feedback" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**The Dataproc MCP Server v${{ needs.semantic-release.outputs.version }} is now live! ๐**" >> $GITHUB_STEP_SUMMARY
notify-failure:
name: โ Failure Notification
runs-on: ubuntu-latest
needs: [semantic-release, post-release-validation]
if: failure()
steps:
- name: โ Failure Notification
run: |
echo "## โ Release Process Failed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "The release process encountered an error. Please check the workflow logs for details." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### ๐ Troubleshooting Steps" >> $GITHUB_STEP_SUMMARY
echo "1. Check the failed job logs above" >> $GITHUB_STEP_SUMMARY
echo "2. Verify NPM_TOKEN secret is configured" >> $GITHUB_STEP_SUMMARY
echo "3. Ensure semantic-release configuration is correct" >> $GITHUB_STEP_SUMMARY
echo "4. Check for any blocking issues in the repository" >> $GITHUB_STEP_SUMMARY