Files
n8n-mcp/.github/workflows/docker-build.yml
Romuald Członkowski fb2d306dc3 fix: intercept stdout writes to prevent JSON-RPC corruption in stdio mode (#673)
* 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>
2026-03-27 17:26:43 +01:00

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