name: Release
on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
inputs:
version_type:
description: "Version bump type"
required: true
default: "patch"
type: choice
options:
- patch
- minor
- major
jobs:
test:
name: Pre-release Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20.x"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Run full test suite
run: npm test -- --coverage
- name: Verify 85% coverage threshold
run: |
coverage=$(npm test -- --coverage --silent 2>&1 | grep "All files" | awk '{print $4}' | sed 's/%//')
if (( $(echo "$coverage < 85" | bc -l) )); then
echo "Coverage $coverage% is below 85% threshold"
exit 1
fi
echo "Coverage $coverage% meets requirement (target: 85%)"
- name: Performance benchmarks
run: npm run test:performance
- name: Build verification
run: npm run build
release:
name: Create Release
runs-on: ubuntu-latest
needs: test
permissions:
contents: write
packages: write
id-token: write # Required for OIDC trusted publishing
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20.x"
cache: "npm"
registry-url: "https://registry.npmjs.org"
- name: Update npm to latest version
run: npm install -g npm@latest
# npm 11.5.1+ required for trusted publishing (OIDC)
- name: Install dependencies
run: npm ci
- name: Build project
run: npm run build
- name: Validate commit messages
run: |
echo "Validating commit messages follow conventional format..."
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
# For manual releases, validate commits since last tag
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -n "$LAST_TAG" ]; then
npx commitlint --from "$LAST_TAG" --to HEAD --verbose || {
echo "Commit message validation failed"
echo "All commits must follow conventional commit format: type(scope): subject"
exit 1
}
fi
else
# For tag-based releases, validate the tag commit
npx commitlint --from HEAD~1 --to HEAD --verbose || {
echo "Commit message validation failed"
exit 1
}
fi
echo "✅ All commit messages are valid"
- name: Generate changelog and release
id: release
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
printf "Manual release triggered with version type: %s\n" "${{ github.event.inputs.version_type }}"
# Fetch latest changes and ensure we're up to date before creating release
echo "Fetching latest changes from remote..."
git fetch origin main || {
echo "Failed to fetch from remote"
exit 1
}
# Pull latest changes to ensure we're on the latest commit
# Use rebase to avoid merge commits in release workflow
git pull --rebase origin main || {
echo "Failed to pull latest changes. Remote may have diverged."
echo "Attempting to merge instead..."
git pull origin main || {
echo "Failed to sync with remote repository"
exit 1
}
}
# Disable husky hooks for automated standard-version commits
# These commits are already validated by CI pipeline
export HUSKY=0
export CI=true
export GITHUB_ACTIONS=true
# Also disable git hooks entirely for standard-version commits
git config core.hooksPath /dev/null || true
# Run standard-version to generate changelog and bump version
npm run "release:${{ github.event.inputs.version_type }}" || {
echo "standard-version failed"
exit 1
}
# Restore git hooks path after standard-version
git config --unset core.hooksPath || true
# Get the new version and changelog
NEW_VERSION=$(node -p "require('./package.json').version")
printf "new_version=v%s\n" "$NEW_VERSION" >> "$GITHUB_OUTPUT"
# Extract changelog for this version from CHANGELOG.md
if [ -f "CHANGELOG.md" ]; then
# Get changelog section for current version (between ## [version] and next ##)
CHANGELOG_CONTENT=$(awk "/## \[$NEW_VERSION\]/{flag=1; next} /^## \[/{flag=0} flag" CHANGELOG.md | sed '/^$/d' || printf "## Changes\n\nAutomated release %s\n" "$NEW_VERSION")
{
echo "changelog_content<<EOF"
printf "%s\n" "$CHANGELOG_CONTENT"
echo "EOF"
} >> "$GITHUB_OUTPUT"
else
echo "Warning: CHANGELOG.md not found after standard-version"
printf "changelog_content=Automated release v%s\n" "$NEW_VERSION" >> "$GITHUB_OUTPUT"
fi
# Push the changes (version bump, changelog, and tag)
git push --follow-tags origin main || {
echo "Failed to push changes to repository"
exit 1
}
else
echo "Tag-based release"
tag="${GITHUB_REF#refs/tags/}"
printf "new_version=%s\n" "$tag" >> "$GITHUB_OUTPUT"
# Extract changelog for tagged version
if [ -f "CHANGELOG.md" ]; then
VERSION_NUM=$(echo "$tag" | sed 's/^v//')
CHANGELOG_CONTENT=$(awk "/## \[$VERSION_NUM\]/{flag=1; next} /^## \[/{flag=0} flag" CHANGELOG.md | sed '/^$/d' || printf "Release %s\n" "$tag")
{
echo "changelog_content<<EOF"
printf "%s\n" "$CHANGELOG_CONTENT"
echo "EOF"
} >> "$GITHUB_OUTPUT"
else
printf "changelog_content=Release %s\n" "$tag" >> "$GITHUB_OUTPUT"
fi
fi
- name: Create GitHub Release
uses: ncipollo/release-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ steps.release.outputs.new_version }}
name: Release ${{ steps.release.outputs.new_version }}
body: |
## DocuMCP Release ${{ steps.release.outputs.new_version }}
${{ steps.release.outputs.changelog_content }}
### Installation
```bash
npm install -g documcp@${{ steps.release.outputs.new_version }}
```
### System Requirements
- Node.js 20.x or higher
- npm 9.x or higher
### Quick Start
```bash
# Install globally
npm install -g documcp
# Use with MCP client
documcp analyze-repository --path ./my-project
```
draft: false
prerelease: false
skipIfReleaseExists: true
makeLatest: true
- name: Publish to npm
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
id: publish
run: |
echo "Publishing to npm using trusted publishing (OIDC)..."
# Verify npm version supports trusted publishing (11.5.1+ required)
NPM_VERSION=$(npm --version)
echo "npm version: $NPM_VERSION"
# Simple version check: compare major.minor.patch
NPM_MAJOR=$(echo "$NPM_VERSION" | cut -d. -f1)
NPM_MINOR=$(echo "$NPM_VERSION" | cut -d. -f2)
NPM_PATCH=$(echo "$NPM_VERSION" | cut -d. -f3)
if [ "$NPM_MAJOR" -lt 11 ] || ([ "$NPM_MAJOR" -eq 11 ] && [ "$NPM_MINOR" -lt 5 ]) || ([ "$NPM_MAJOR" -eq 11 ] && [ "$NPM_MINOR" -eq 5 ] && [ "$NPM_PATCH" -lt 1 ]); then
echo "Warning: npm version $NPM_VERSION may not support trusted publishing"
echo "npm 11.5.1+ is required for trusted publishing (OIDC)"
else
echo "✅ npm version $NPM_VERSION supports trusted publishing"
fi
# Get package version
PACKAGE_VERSION=$(node -p "require('./package.json').version")
echo "Publishing version: $PACKAGE_VERSION"
# Publish with retry mechanism
# Trusted publishing uses OIDC - no NODE_AUTH_TOKEN needed
MAX_RETRIES=3
RETRY_COUNT=0
PUBLISH_SUCCESS=false
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if npm publish --access public; then
PUBLISH_SUCCESS=true
break
else
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then
echo "Publication attempt $RETRY_COUNT failed, retrying in 5 seconds..."
sleep 5
else
echo "Publication failed after $MAX_RETRIES attempts"
echo ""
echo "Troubleshooting tips:"
echo "1. Verify trusted publisher is configured on npmjs.com"
echo "2. Check that workflow filename matches exactly: release.yml"
echo "3. Ensure repository and organization match npmjs.com configuration"
echo "4. Verify id-token: write permission is set in workflow"
exit 1
fi
fi
done
if [ "$PUBLISH_SUCCESS" = true ]; then
echo "✅ Successfully published to npm using trusted publishing!"
echo "package_version=$PACKAGE_VERSION" >> "$GITHUB_OUTPUT"
fi
- name: Verify npm publication
if: steps.publish.outcome == 'success'
run: |
PACKAGE_VERSION="${{ steps.publish.outputs.package_version }}"
echo "Verifying package availability: documcp@$PACKAGE_VERSION"
# Wait a moment for npm registry to update
sleep 10
# Verify package exists on npm registry
if npm view "documcp@$PACKAGE_VERSION" version > /dev/null 2>&1; then
echo "✅ Package verification successful: documcp@$PACKAGE_VERSION is available on npm"
npm view "documcp@$PACKAGE_VERSION"
else
echo "❌ Package verification failed: documcp@$PACKAGE_VERSION not found on npm registry"
echo "This may be a temporary registry delay. Please verify manually:"
echo " npm view documcp@$PACKAGE_VERSION"
exit 1
fi
- name: Test package installation
if: steps.publish.outcome == 'success'
run: |
PACKAGE_VERSION="${{ steps.publish.outputs.package_version }}"
echo "Testing package installation: documcp@$PACKAGE_VERSION"
# Install the published package globally in a clean environment
npm install -g "documcp@$PACKAGE_VERSION" || {
echo "Package installation test failed"
exit 1
}
# Verify the installed package works
if command -v documcp > /dev/null 2>&1; then
echo "✅ Package installation successful"
documcp --version || echo "Version command available"
else
echo "❌ Package installation test failed: documcp command not found"
exit 1
fi
docs:
name: Deploy Documentation
runs-on: ubuntu-latest
needs: [test, release]
permissions:
pages: write
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20.x"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Generate API documentation
run: npm run docs:generate
- name: Remove problematic media directory
run: rm -rf docs/api/media
- name: Install Docusaurus dependencies
run: cd docs && npm ci
- name: Build Docusaurus site
run: cd docs && npm run build
env:
NODE_ENV: production
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Upload to GitHub Pages
uses: actions/upload-pages-artifact@v4
with:
path: ./docs/build
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4