name: Release
on:
push:
branches:
- master
- main
permissions:
contents: write
issues: write
pull-requests: write
id-token: write
jobs:
release:
name: Semantic Release
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]')"
outputs:
new_release_published: ${{ steps.semantic.outputs.new_release_published }}
new_release_version: ${{ steps.semantic.outputs.new_release_version }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build project
run: npm run build
- name: Verify build artifacts
run: |
if [ ! -d "dist" ]; then
echo "Error: dist directory not created"
exit 1
fi
echo "Build artifacts verified successfully"
- name: Run tests
run: npm run test:coverage
env:
NODE_OPTIONS: --experimental-vm-modules
SKIP_HARDWARE_TESTS: true
- name: Semantic Release
id: semantic
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-release
publish:
name: Publish to npm
runs-on: ubuntu-latest
needs: release
if: needs.release.outputs.new_release_published == 'true'
permissions:
contents: read
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: v${{ needs.release.outputs.new_release_version }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build for production
run: npm run build
- name: Prepare package for publishing
run: |
npm version ${{ needs.release.outputs.new_release_version }} --no-git-tag-version --allow-same-version
- name: Publish to npm (OIDC)
run: npm publish --provenance --access public
- name: Verify publication
run: |
sleep 10
PACKAGE_NAME=$(node -p "require('./package.json').name")
VERSION=${{ needs.release.outputs.new_release_version }}
echo "Verifying publication of ${PACKAGE_NAME}@${VERSION}"
PUBLISHED_VERSION=$(npm view ${PACKAGE_NAME}@${VERSION} version 2>/dev/null || echo "not_found")
if [ "$PUBLISHED_VERSION" = "$VERSION" ]; then
echo "Package successfully published!"
else
echo "Warning: Could not verify package publication"
fi
notify:
name: Notify Release
runs-on: ubuntu-latest
needs: [release, publish]
if: needs.release.outputs.new_release_published == 'true'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: v${{ needs.release.outputs.new_release_version }}
- name: Get release notes
id: release_notes
run: |
RELEASE_URL="https://github.com/${{ github.repository }}/releases/tag/v${{ needs.release.outputs.new_release_version }}"
echo "release_url=$RELEASE_URL" >> $GITHUB_OUTPUT
- name: Create notification message
id: notification
run: |
VERSION="${{ needs.release.outputs.new_release_version }}"
RELEASE_URL="${{ steps.release_notes.outputs.release_url }}"
PACKAGE_NAME=$(node -p "require('./package.json').name")
MESSAGE="New release: ${PACKAGE_NAME} v${VERSION}\n"
MESSAGE="${MESSAGE}Release notes: ${RELEASE_URL}\n"
MESSAGE="${MESSAGE}npm: https://www.npmjs.com/package/${PACKAGE_NAME}/v/${VERSION}"
echo "message<<EOF" >> $GITHUB_OUTPUT
echo -e "$MESSAGE" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Post to Discord (if configured)
if: vars.DISCORD_WEBHOOK_URL != ''
run: |
MESSAGE='${{ steps.notification.outputs.message }}'
curl -X POST "${{ vars.DISCORD_WEBHOOK_URL }}" \
-H "Content-Type: application/json" \
-d "{\"content\": \"$MESSAGE\"}" \
|| echo "Discord notification failed (optional)"
- name: Post to Slack (if configured)
if: vars.SLACK_WEBHOOK_URL != ''
run: |
MESSAGE='${{ steps.notification.outputs.message }}'
curl -X POST "${{ vars.SLACK_WEBHOOK_URL }}" \
-H "Content-Type: application/json" \
-d "{\"text\": \"$MESSAGE\"}" \
|| echo "Slack notification failed (optional)"
- name: Comment on related issues
uses: actions/github-script@v7
with:
script: |
const version = '${{ needs.release.outputs.new_release_version }}';
const releaseUrl = '${{ steps.release_notes.outputs.release_url }}';
const { data: release } = await github.rest.repos.getReleaseByTag({
owner: context.repo.owner,
repo: context.repo.repo,
tag: `v${version}`
});
const issueNumbers = [...new Set(
(release.body || '').match(/#(\d+)/g)?.map(m => parseInt(m.slice(1))) || []
)];
for (const issueNumber of issueNumbers) {
try {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
body: `This issue has been resolved in version ${version}.\n\nRelease notes: ${releaseUrl}`
});
} catch (error) {
console.log(`Failed to comment on issue #${issueNumber}:`, error.message);
}
}
- name: Update release summary
run: |
PACKAGE_NAME=$(node -p "require('./package.json').name")
echo "## Release Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Version: ${{ needs.release.outputs.new_release_version }}" >> $GITHUB_STEP_SUMMARY
echo "Release URL: ${{ steps.release_notes.outputs.release_url }}" >> $GITHUB_STEP_SUMMARY
echo "npm URL: https://www.npmjs.com/package/${PACKAGE_NAME}/v/${{ needs.release.outputs.new_release_version }}" >> $GITHUB_STEP_SUMMARY
publish-mcp-registry:
name: Publish to MCP Registry
runs-on: ubuntu-latest
needs: [release, publish]
if: needs.release.outputs.new_release_published == 'true'
permissions:
contents: read
id-token: write
steps:
- name: Checkout code at tag
uses: actions/checkout@v4
with:
ref: v${{ needs.release.outputs.new_release_version }}
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Update server.json version
run: |
VERSION="${{ needs.release.outputs.new_release_version }}"
export VERSION
node -e "
const fs = require('fs');
const p = 'server.json';
if (!fs.existsSync(p)) { process.exit(0); }
const j = JSON.parse(fs.readFileSync(p, 'utf8'));
j.version = process.env.VERSION;
if (Array.isArray(j.packages) && j.packages[0]) {
j.packages[0].version = process.env.VERSION;
}
fs.writeFileSync(p, JSON.stringify(j, null, 2) + '\n');
"
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
- name: Install MCP Publisher CLI
run: |
git clone https://github.com/modelcontextprotocol/registry /tmp/mcp-registry
cd /tmp/mcp-registry
make publisher
sudo mv bin/mcp-publisher /usr/local/bin/
mcp-publisher --version
- name: Authenticate with GitHub OIDC
run: |
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
mcp-publisher login github-oidc
- name: Publish to MCP Registry
run: |
echo "Publishing ${GITHUB_REPOSITORY} to MCP Registry at version ${{ needs.release.outputs.new_release_version }}"
mcp-publisher publish
- name: MCP Registry summary
run: |
echo "## MCP Registry Publication" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Published io.github.ooples/console-automation-mcp v${{ needs.release.outputs.new_release_version }} to https://registry.modelcontextprotocol.io" >> $GITHUB_STEP_SUMMARY