name: Automated Release on: push: branches: [main] paths: - 'package.json' - 'package.runtime.json' paths-ignore: - '**.md' - '**.txt' - 'docs/**' - 'examples/**' - '.github/FUNDING.yml' - '.github/ISSUE_TEMPLATE/**' - '.github/pull_request_template.md' - '.gitignore' - 'LICENSE*' - 'ATTRIBUTION.md' - 'SECURITY.md' - 'CODE_OF_CONDUCT.md' permissions: contents: write packages: write issues: write pull-requests: write # Prevent concurrent releases concurrency: group: release cancel-in-progress: false env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} jobs: detect-version-change: name: Detect Version Change runs-on: ubuntu-latest outputs: version-changed: ${{ steps.check.outputs.changed }} new-version: ${{ steps.check.outputs.version }} previous-version: ${{ steps.check.outputs.previous-version }} is-prerelease: ${{ steps.check.outputs.is-prerelease }} steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 2 - name: Check for version change id: check run: | # Get current version from package.json CURRENT_VERSION=$(node -e "console.log(require('./package.json').version)") # Get previous version from git history safely PREVIOUS_VERSION=$(git show HEAD~1:package.json 2>/dev/null | node -e " try { const data = require('fs').readFileSync(0, 'utf8'); const pkg = JSON.parse(data); console.log(pkg.version || '0.0.0'); } catch (e) { console.log('0.0.0'); } " || echo "0.0.0") echo "Previous version: $PREVIOUS_VERSION" echo "Current version: $CURRENT_VERSION" # Check if version changed if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then echo "changed=true" >> $GITHUB_OUTPUT echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT echo "previous-version=$PREVIOUS_VERSION" >> $GITHUB_OUTPUT # Check if it's a prerelease (contains alpha, beta, rc, dev) if echo "$CURRENT_VERSION" | grep -E "(alpha|beta|rc|dev)" > /dev/null; then echo "is-prerelease=true" >> $GITHUB_OUTPUT else echo "is-prerelease=false" >> $GITHUB_OUTPUT fi echo "🎉 Version changed from $PREVIOUS_VERSION to $CURRENT_VERSION" else echo "changed=false" >> $GITHUB_OUTPUT echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT echo "previous-version=$PREVIOUS_VERSION" >> $GITHUB_OUTPUT echo "is-prerelease=false" >> $GITHUB_OUTPUT echo "â„šī¸ No version change detected" fi extract-changelog: name: Extract Changelog runs-on: ubuntu-latest needs: detect-version-change if: needs.detect-version-change.outputs.version-changed == 'true' outputs: release-notes: ${{ steps.extract.outputs.notes }} has-notes: ${{ steps.extract.outputs.has-notes }} steps: - name: Checkout repository uses: actions/checkout@v4 - name: Extract changelog for version id: extract run: | VERSION="${{ needs.detect-version-change.outputs.new-version }}" CHANGELOG_FILE="docs/CHANGELOG.md" if [ ! -f "$CHANGELOG_FILE" ]; then echo "Changelog file not found at $CHANGELOG_FILE" echo "has-notes=false" >> $GITHUB_OUTPUT echo "notes=No changelog entries found for version $VERSION" >> $GITHUB_OUTPUT exit 0 fi # Use the extracted changelog script if NOTES=$(node scripts/extract-changelog.js "$VERSION" "$CHANGELOG_FILE" 2>/dev/null); then echo "has-notes=true" >> $GITHUB_OUTPUT # Use heredoc to properly handle multiline content { echo "notes<> $GITHUB_OUTPUT echo "✅ Successfully extracted changelog for version $VERSION" else echo "has-notes=false" >> $GITHUB_OUTPUT echo "notes=No changelog entries found for version $VERSION" >> $GITHUB_OUTPUT echo "âš ī¸ Could not extract changelog for version $VERSION" fi create-release: name: Create GitHub Release runs-on: ubuntu-latest needs: [detect-version-change, extract-changelog] if: needs.detect-version-change.outputs.version-changed == 'true' outputs: release-id: ${{ steps.create.outputs.id }} upload-url: ${{ steps.create.outputs.upload_url }} steps: - name: Checkout repository uses: actions/checkout@v4 - name: Create Git Tag run: | VERSION="${{ needs.detect-version-change.outputs.new-version }}" git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" # Create annotated tag git tag -a "v$VERSION" -m "Release v$VERSION" git push origin "v$VERSION" - name: Create GitHub Release id: create env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | VERSION="${{ needs.detect-version-change.outputs.new-version }}" IS_PRERELEASE="${{ needs.detect-version-change.outputs.is-prerelease }}" # Create release body cat > release_body.md << 'EOF' # Release v${{ needs.detect-version-change.outputs.new-version }} ${{ needs.extract-changelog.outputs.release-notes }} --- ## Installation ### NPM Package ```bash # Install globally npm install -g n8n-mcp # Or run directly npx n8n-mcp ``` ### Docker ```bash # Standard image docker run -p 3000:3000 ghcr.io/czlonkowski/n8n-mcp:v${{ needs.detect-version-change.outputs.new-version }} # Railway optimized docker run -p 3000:3000 ghcr.io/czlonkowski/n8n-mcp-railway:v${{ needs.detect-version-change.outputs.new-version }} ``` ## Documentation - [Installation Guide](https://github.com/czlonkowski/n8n-mcp#installation) - [Docker Deployment](https://github.com/czlonkowski/n8n-mcp/blob/main/docs/DOCKER_README.md) - [n8n Integration](https://github.com/czlonkowski/n8n-mcp/blob/main/docs/N8N_DEPLOYMENT.md) - [Complete Changelog](https://github.com/czlonkowski/n8n-mcp/blob/main/docs/CHANGELOG.md) 🤖 *Generated with [Claude Code](https://claude.ai/code)* EOF # Create release using gh CLI if [ "$IS_PRERELEASE" = "true" ]; then PRERELEASE_FLAG="--prerelease" else PRERELEASE_FLAG="" fi gh release create "v$VERSION" \ --title "Release v$VERSION" \ --notes-file release_body.md \ $PRERELEASE_FLAG # Output release info for next jobs RELEASE_ID=$(gh release view "v$VERSION" --json id --jq '.id') echo "id=$RELEASE_ID" >> $GITHUB_OUTPUT echo "upload_url=https://uploads.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID/assets{?name,label}" >> $GITHUB_OUTPUT build-and-test: name: Build and Test runs-on: ubuntu-latest needs: detect-version-change if: needs.detect-version-change.outputs.version-changed == 'true' steps: - name: Checkout repository uses: actions/checkout@v4 - 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: Rebuild database run: npm run rebuild - name: Run tests run: npm test env: CI: true - name: Run type checking run: npm run typecheck publish-npm: name: Publish to NPM runs-on: ubuntu-latest needs: [detect-version-change, build-and-test, create-release] if: needs.detect-version-change.outputs.version-changed == 'true' steps: - name: Checkout repository uses: actions/checkout@v4 - 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 - name: Build project run: npm run build - name: Rebuild database run: npm run rebuild - name: Sync runtime version run: npm run sync:runtime-version - name: Prepare package for publishing run: | # Create publish directory PUBLISH_DIR="npm-publish-temp" rm -rf $PUBLISH_DIR mkdir -p $PUBLISH_DIR # Copy necessary files cp -r dist $PUBLISH_DIR/ cp -r data $PUBLISH_DIR/ cp README.md $PUBLISH_DIR/ cp LICENSE $PUBLISH_DIR/ cp .env.example $PUBLISH_DIR/ # Use runtime package.json as base cp package.runtime.json $PUBLISH_DIR/package.json cd $PUBLISH_DIR # Update package.json with complete metadata node -e " const pkg = require('./package.json'); pkg.name = 'n8n-mcp'; pkg.description = 'Integration between n8n workflow automation and Model Context Protocol (MCP)'; pkg.bin = { 'n8n-mcp': './dist/mcp/index.js' }; pkg.repository = { type: 'git', url: 'git+https://github.com/czlonkowski/n8n-mcp.git' }; pkg.keywords = ['n8n', 'mcp', 'model-context-protocol', 'ai', 'workflow', 'automation']; pkg.author = 'Romuald Czlonkowski @ www.aiadvisors.pl/en'; pkg.license = 'MIT'; pkg.bugs = { url: 'https://github.com/czlonkowski/n8n-mcp/issues' }; pkg.homepage = 'https://github.com/czlonkowski/n8n-mcp#readme'; pkg.files = ['dist/**/*', 'data/nodes.db', '.env.example', 'README.md', 'LICENSE']; delete pkg.private; require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2)); " echo "Package prepared for publishing:" echo "Name: $(node -e "console.log(require('./package.json').name)")" echo "Version: $(node -e "console.log(require('./package.json').version)")" - name: Publish to NPM with retry uses: nick-invision/retry@v2 with: timeout_minutes: 5 max_attempts: 3 command: | cd npm-publish-temp npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Clean up if: always() run: rm -rf npm-publish-temp build-docker: name: Build and Push Docker Images runs-on: ubuntu-latest needs: [detect-version-change, build-and-test] if: needs.detect-version-change.outputs.version-changed == 'true' permissions: contents: read packages: write steps: - name: Checkout repository uses: actions/checkout@v4 with: lfs: true - name: Check disk space run: | echo "Disk usage before Docker build:" df -h # Check available space (require at least 2GB) AVAILABLE_GB=$(df / --output=avail --block-size=1G | tail -1) if [ "$AVAILABLE_GB" -lt 2 ]; then echo "❌ Insufficient disk space: ${AVAILABLE_GB}GB available, 2GB required" exit 1 fi echo "✅ Sufficient disk space: ${AVAILABLE_GB}GB available" - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata for standard image id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=semver,pattern={{version}},value=v${{ needs.detect-version-change.outputs.new-version }} type=semver,pattern={{major}}.{{minor}},value=v${{ needs.detect-version-change.outputs.new-version }} type=semver,pattern={{major}},value=v${{ needs.detect-version-change.outputs.new-version }} type=raw,value=latest,enable={{is_default_branch}} - name: Build and push standard Docker image uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max - name: Extract metadata for Railway image id: meta-railway uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-railway tags: | type=semver,pattern={{version}},value=v${{ needs.detect-version-change.outputs.new-version }} type=semver,pattern={{major}}.{{minor}},value=v${{ needs.detect-version-change.outputs.new-version }} type=semver,pattern={{major}},value=v${{ needs.detect-version-change.outputs.new-version }} type=raw,value=latest,enable={{is_default_branch}} - name: Build and push Railway Docker image uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile.railway platforms: linux/amd64 push: true tags: ${{ steps.meta-railway.outputs.tags }} labels: ${{ steps.meta-railway.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max update-documentation: name: Update Documentation runs-on: ubuntu-latest needs: [detect-version-change, create-release, publish-npm, build-docker] if: needs.detect-version-change.outputs.version-changed == 'true' && !failure() steps: - name: Checkout repository uses: actions/checkout@v4 with: token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 20 - name: Update version badges in README run: | VERSION="${{ needs.detect-version-change.outputs.new-version }}" # Update README version badges if [ -f "README.md" ]; then # Update npm version badge sed -i.bak "s|npm/v/n8n-mcp/[^)]*|npm/v/n8n-mcp/$VERSION|g" README.md # Update any other version references sed -i.bak "s|version-[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*|version-$VERSION|g" README.md # Clean up backup file rm -f README.md.bak echo "✅ Updated version badges in README.md to $VERSION" fi - name: Commit documentation updates env: VERSION: ${{ needs.detect-version-change.outputs.new-version }} run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" if git diff --quiet; then echo "No documentation changes to commit" else git add README.md git commit -m "docs: update version badges to v${VERSION}" git push echo "✅ Committed documentation updates" fi notify-completion: name: Notify Release Completion runs-on: ubuntu-latest needs: [detect-version-change, create-release, publish-npm, build-docker, update-documentation] if: always() && needs.detect-version-change.outputs.version-changed == 'true' steps: - name: Create release summary run: | VERSION="${{ needs.detect-version-change.outputs.new-version }}" RELEASE_URL="https://github.com/${{ github.repository }}/releases/tag/v$VERSION" echo "## 🎉 Release v$VERSION Published Successfully!" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### ✅ Completed Tasks:" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY # Check job statuses if [ "${{ needs.create-release.result }}" = "success" ]; then echo "- ✅ GitHub Release created: [$RELEASE_URL]($RELEASE_URL)" >> $GITHUB_STEP_SUMMARY else echo "- ❌ GitHub Release creation failed" >> $GITHUB_STEP_SUMMARY fi if [ "${{ needs.publish-npm.result }}" = "success" ]; then echo "- ✅ NPM package published: [npmjs.com/package/n8n-mcp](https://www.npmjs.com/package/n8n-mcp)" >> $GITHUB_STEP_SUMMARY else echo "- ❌ NPM publishing failed" >> $GITHUB_STEP_SUMMARY fi if [ "${{ needs.build-docker.result }}" = "success" ]; then echo "- ✅ Docker images built and pushed" >> $GITHUB_STEP_SUMMARY echo " - Standard: \`ghcr.io/czlonkowski/n8n-mcp:v$VERSION\`" >> $GITHUB_STEP_SUMMARY echo " - Railway: \`ghcr.io/czlonkowski/n8n-mcp-railway:v$VERSION\`" >> $GITHUB_STEP_SUMMARY else echo "- ❌ Docker image building failed" >> $GITHUB_STEP_SUMMARY fi if [ "${{ needs.update-documentation.result }}" = "success" ]; then echo "- ✅ Documentation updated" >> $GITHUB_STEP_SUMMARY else echo "- âš ī¸ Documentation update skipped or failed" >> $GITHUB_STEP_SUMMARY fi echo "" >> $GITHUB_STEP_SUMMARY echo "### đŸ“Ļ Installation:" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY echo "# NPM" >> $GITHUB_STEP_SUMMARY echo "npx n8n-mcp" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "# Docker" >> $GITHUB_STEP_SUMMARY echo "docker run -p 3000:3000 ghcr.io/czlonkowski/n8n-mcp:v$VERSION" >> $GITHUB_STEP_SUMMARY echo "\`\`\`" >> $GITHUB_STEP_SUMMARY echo "🎉 Release automation completed for v$VERSION!"