mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-03-28 13:13:08 +00:00
* fix: intercept process.stdout.write to prevent JSON-RPC corruption in stdio mode (#628, #627, #567) Console method suppression alone was insufficient — native modules, n8n packages, and third-party code can call process.stdout.write() directly, leaking debug output (refCount, dbPath, clientVersion, protocolVersion, etc.) into the MCP JSON-RPC stream. Added stdout write interceptor that only allows JSON-RPC messages through (objects containing "jsonrpc" field). All other writes are redirected to stderr. This fixes the flood of "Unexpected token is not valid JSON" warnings on every new Claude Desktop chat. Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: add Docker Hub login to fix buildx bootstrap rate limiting GitHub-hosted runners hit Docker Hub anonymous pull limits when setup-buildx-action pulls moby/buildkit. Add docker/login-action for Docker Hub before setup-buildx-action in all 4 workflows: docker-build.yml, docker-build-fast.yml, docker-build-n8n.yml, release.yml. Uses DOCKERHUB_USERNAME and DOCKERHUB_TOKEN repository secrets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
237 lines
7.2 KiB
YAML
237 lines
7.2 KiB
YAML
# .github/workflows/docker-build.yml
|
|
name: Build and Push Docker Images
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
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'
|
|
pull_request:
|
|
branches:
|
|
- main
|
|
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'
|
|
workflow_dispatch:
|
|
|
|
# Prevent concurrent Docker pushes across all workflows (shared with release.yml)
|
|
# This ensures docker-build.yml and release.yml never push to 'latest' simultaneously
|
|
concurrency:
|
|
group: docker-push-${{ github.ref }}
|
|
cancel-in-progress: false
|
|
|
|
env:
|
|
REGISTRY: ghcr.io
|
|
IMAGE_NAME: ${{ github.repository }}
|
|
|
|
jobs:
|
|
build:
|
|
name: Build Docker Image
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
with:
|
|
lfs: true
|
|
|
|
- name: Sync runtime version
|
|
run: |
|
|
VERSION=$(node -p "require('./package.json').version")
|
|
node -e "
|
|
const fs = require('fs');
|
|
const pkg = JSON.parse(fs.readFileSync('package.runtime.json'));
|
|
pkg.version = '$VERSION';
|
|
fs.writeFileSync('package.runtime.json', JSON.stringify(pkg, null, 2) + '\n');
|
|
"
|
|
echo "✅ Synced package.runtime.json to version $VERSION"
|
|
|
|
- name: Log in to Docker Hub
|
|
uses: docker/login-action@v3
|
|
with:
|
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@v3
|
|
|
|
- name: Set up Docker Buildx
|
|
id: buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Log in to GitHub Container Registry
|
|
if: github.event_name != 'pull_request'
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Extract metadata
|
|
id: meta
|
|
uses: docker/metadata-action@v5
|
|
with:
|
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
tags: |
|
|
type=ref,event=branch
|
|
type=ref,event=pr
|
|
type=semver,pattern={{version}}
|
|
type=semver,pattern={{major}}.{{minor}}
|
|
type=semver,pattern={{major}}
|
|
type=sha,format=short
|
|
type=raw,value=latest,enable={{is_default_branch}}
|
|
|
|
- name: Build and push Docker image
|
|
uses: docker/build-push-action@v5
|
|
with:
|
|
context: .
|
|
no-cache: false
|
|
platforms: linux/amd64,linux/arm64
|
|
push: ${{ github.event_name != 'pull_request' }}
|
|
tags: ${{ steps.meta.outputs.tags }}
|
|
labels: ${{ steps.meta.outputs.labels }}
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
provenance: false
|
|
|
|
- name: Verify multi-arch manifest for latest tag
|
|
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
|
run: |
|
|
echo "Verifying multi-arch manifest for latest tag..."
|
|
|
|
# Retry with exponential backoff (registry propagation can take time)
|
|
MAX_ATTEMPTS=5
|
|
ATTEMPT=1
|
|
WAIT_TIME=2
|
|
|
|
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
|
|
echo "Attempt $ATTEMPT of $MAX_ATTEMPTS..."
|
|
|
|
MANIFEST=$(docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest 2>&1 || true)
|
|
|
|
# Check for both platforms
|
|
if echo "$MANIFEST" | grep -q "linux/amd64" && echo "$MANIFEST" | grep -q "linux/arm64"; then
|
|
echo "✅ Multi-arch manifest verified: both amd64 and arm64 present"
|
|
echo "$MANIFEST"
|
|
exit 0
|
|
fi
|
|
|
|
if [ $ATTEMPT -lt $MAX_ATTEMPTS ]; then
|
|
echo "⏳ Registry still propagating, waiting ${WAIT_TIME}s before retry..."
|
|
sleep $WAIT_TIME
|
|
WAIT_TIME=$((WAIT_TIME * 2)) # Exponential backoff: 2s, 4s, 8s, 16s
|
|
fi
|
|
|
|
ATTEMPT=$((ATTEMPT + 1))
|
|
done
|
|
|
|
echo "❌ ERROR: Multi-arch manifest incomplete after $MAX_ATTEMPTS attempts!"
|
|
echo "$MANIFEST"
|
|
exit 1
|
|
|
|
build-railway:
|
|
name: Build Railway Docker Image
|
|
runs-on: ubuntu-latest
|
|
needs: build
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
with:
|
|
lfs: true
|
|
|
|
- name: Sync runtime version
|
|
run: |
|
|
VERSION=$(node -p "require('./package.json').version")
|
|
node -e "
|
|
const fs = require('fs');
|
|
const pkg = JSON.parse(fs.readFileSync('package.runtime.json'));
|
|
pkg.version = '$VERSION';
|
|
fs.writeFileSync('package.runtime.json', JSON.stringify(pkg, null, 2) + '\n');
|
|
"
|
|
echo "✅ Synced package.runtime.json to version $VERSION"
|
|
|
|
- name: Log in to Docker Hub
|
|
uses: docker/login-action@v3
|
|
with:
|
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@v3
|
|
|
|
- name: Set up Docker Buildx
|
|
id: buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Log in to GitHub Container Registry
|
|
if: github.event_name != 'pull_request'
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Extract metadata for Railway
|
|
id: meta-railway
|
|
uses: docker/metadata-action@v5
|
|
with:
|
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-railway
|
|
tags: |
|
|
type=ref,event=branch
|
|
type=ref,event=pr
|
|
type=semver,pattern={{version}}
|
|
type=semver,pattern={{major}}.{{minor}}
|
|
type=semver,pattern={{major}}
|
|
type=sha,format=short
|
|
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
|
|
no-cache: false
|
|
platforms: linux/amd64
|
|
push: ${{ github.event_name != 'pull_request' }}
|
|
tags: ${{ steps.meta-railway.outputs.tags }}
|
|
labels: ${{ steps.meta-railway.outputs.labels }}
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
provenance: false
|
|
|
|
# Nginx build commented out until Phase 2
|
|
# build-nginx:
|
|
# name: Build nginx-enhanced Docker Image
|
|
# runs-on: ubuntu-latest
|
|
# permissions:
|
|
# contents: read
|
|
# packages: write |