name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
name: Test & Build
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
fail-fast: false # Continue testing other versions if one fails
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
- name: Cache TypeScript build
uses: actions/cache@v4
with:
path: |
build/
tsconfig.tsbuildinfo
key: ${{ runner.os }}-build-${{ matrix.node-version }}-${{ hashFiles('lib/**/*.ts', 'tsconfig.json') }}
restore-keys: |
${{ runner.os }}-build-${{ matrix.node-version }}-
${{ runner.os }}-build-
- name: Install dependencies
run: npm ci
- name: Build project
run: npm run build
- name: Run tests
run: npm test
- name: Test stdio transport
run: |
echo "π§ͺ Testing stdio transport..."
RESPONSE=$(timeout 10s bash -c 'echo "{\"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"tools/list\"}" | npm start 2>&1' || echo "TIMEOUT")
if echo "$RESPONSE" | grep -q "tools/list"; then
echo "β
Stdio transport test passed"
else
echo "β οΈ Stdio transport test inconclusive (this is non-critical)"
echo "Response: $RESPONSE"
fi
- name: Test HTTP transport
run: |
echo "π§ͺ Testing HTTP transport..."
MCP_TRANSPORT=http npm start &
SERVER_PID=$!
# Wait for server to start with timeout
echo "β³ Waiting for server to start..."
for i in {1..10}; do
if curl -sf http://localhost:3000/health > /dev/null 2>&1; then
echo "β
Server is ready"
break
fi
if [ $i -eq 10 ]; then
echo "β Server failed to start within 10 seconds"
kill $SERVER_PID 2>/dev/null || true
exit 1
fi
sleep 1
done
# Test MCP endpoint
if curl -sf -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}' > /dev/null; then
echo "β
HTTP transport test passed"
else
echo "β HTTP transport test failed"
kill $SERVER_PID 2>/dev/null || true
exit 1
fi
kill $SERVER_PID 2>/dev/null || true
lint:
name: Code Quality
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: Cache TypeScript build
uses: actions/cache@v4
with:
path: |
build/
tsconfig.tsbuildinfo
key: ${{ runner.os }}-build-lint-${{ hashFiles('lib/**/*.ts', 'tsconfig.json') }}
restore-keys: |
${{ runner.os }}-build-lint-
${{ runner.os }}-build-
- name: Install dependencies
run: npm ci
- name: Check TypeScript
run: npx tsc --noEmit
- name: Check line count constraint
run: |
TARGET_FILE="lib/core/mcp-server.ts"
if [ ! -f "$TARGET_FILE" ]; then
echo "βΉοΈ Skipping line count check - $TARGET_FILE not found"
exit 0
fi
LINE_COUNT=$(wc -l < "$TARGET_FILE")
echo "Current line count in $TARGET_FILE: $LINE_COUNT"
if [ "$LINE_COUNT" -gt 350 ]; then
echo "β $TARGET_FILE exceeds 350 lines ($LINE_COUNT lines)"
exit 1
else
echo "β
$TARGET_FILE is within acceptable limits ($LINE_COUNT lines)"
fi
cloudflare:
name: Cloudflare Workers Build
runs-on: ubuntu-latest
needs: [test] # Removed lint dependency - can run in parallel
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: Cache worker build
uses: actions/cache@v4
with:
path: |
build/
tsconfig.tsbuildinfo
key: ${{ runner.os }}-worker-build-${{ hashFiles('lib/**/*.ts', 'tsconfig.worker.json') }}
restore-keys: |
${{ runner.os }}-worker-build-
${{ runner.os }}-build-
- name: Install dependencies
run: npm ci
- name: Build for Cloudflare Workers
run: npm run build:worker
- name: Upload worker artifact
uses: actions/upload-artifact@v4
with:
name: worker-build
path: build/lib/workers/
retention-days: 1
- name: Check worker build output
run: |
if [ ! -f "build/lib/workers/worker.js" ]; then
echo "β Worker build failed - build/lib/workers/worker.js not found"
exit 1
fi
# Check if the worker file is not empty
if [ ! -s "build/lib/workers/worker.js" ]; then
echo "β Worker build failed - build/lib/workers/worker.js is empty"
exit 1
fi
echo "β
Cloudflare Worker build successful"
# Show build size
WORKER_SIZE=$(wc -c < build/lib/workers/worker.js)
echo "Worker bundle size: ${WORKER_SIZE} bytes"
- name: Check worker bundle size
run: |
WORKER_FILE="build/lib/workers/worker.js"
if [ ! -f "$WORKER_FILE" ]; then
echo "β οΈ Worker file not found at $WORKER_FILE"
exit 0
fi
WORKER_SIZE=$(wc -c < "$WORKER_FILE")
MAX_SIZE=1048576 # 1MB in bytes
SIZE_MB=$(echo "scale=2; $WORKER_SIZE / 1048576" | bc)
echo "π¦ Worker bundle size: ${SIZE_MB}MB ($WORKER_SIZE bytes)"
if [ $WORKER_SIZE -gt $MAX_SIZE ]; then
echo "β Worker exceeds Cloudflare's 1MB limit"
exit 1
else
echo "β
Worker is within size limits"
fi
publish-dev:
name: Publish Development Version
runs-on: ubuntu-latest
needs: [test, lint] # Don't wait for cloudflare build
# Only publish on push to main (not PRs) and only if NPM_TOKEN is available
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'cristianoaredes/mcp-dadosbr'
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: Build project
run: npm run build
- name: Configure NPM authentication
run: |
if [ -z "$NPM_TOKEN" ]; then
echo "β οΈ NPM_TOKEN not available, skipping publication"
exit 0
fi
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Check if version already published
id: check_version
run: |
PACKAGE_NAME="@aredes.me/mcp-dadosbr"
PACKAGE_VERSION=$(node -p "require('./package.json').version")
echo "π¦ Checking if $PACKAGE_NAME@$PACKAGE_VERSION is already published..."
if npm view "$PACKAGE_NAME@$PACKAGE_VERSION" version 2>/dev/null; then
echo "βΉοΈ Version $PACKAGE_VERSION already exists on NPM"
echo "published=true" >> $GITHUB_OUTPUT
else
echo "β
Version $PACKAGE_VERSION not found on NPM, will publish"
echo "published=false" >> $GITHUB_OUTPUT
fi
- name: Publish development version to NPM
if: steps.check_version.outputs.published == 'false'
run: |
PACKAGE_VERSION=$(node -p "require('./package.json').version")
echo "π¦ Publishing development version $PACKAGE_VERSION to NPM..."
# Publish with 'dev' tag for development versions
npm publish --access public --tag dev
echo "β
Development version published successfully"
echo "π¦ Install with: npm install -g @aredes.me/mcp-dadosbr@dev"
- name: Verify development publication
if: steps.check_version.outputs.published == 'false'
run: |
PACKAGE_NAME="@aredes.me/mcp-dadosbr"
PACKAGE_VERSION=$(node -p "require('./package.json').version")
echo "β³ Waiting for NPM registry to update..."
sleep 5
# Try to fetch the package info with dev tag
if npm view "$PACKAGE_NAME@dev" version > /dev/null 2>&1; then
echo "β
Development version successfully published to NPM"
echo "π¦ Install: npm install -g $PACKAGE_NAME@dev"
echo "π¦ View: https://www.npmjs.com/package/$PACKAGE_NAME"
else
echo "β οΈ Development version may still be processing on NPM"
fi
summary:
name: CI Summary
runs-on: ubuntu-latest
needs: [test, lint, cloudflare, publish-dev]
if: always()
steps:
- name: Generate CI summary
run: |
echo "## π CI/CD Pipeline Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Test results
if [[ "${{ needs.test.result }}" == "success" ]]; then
echo "β
**Tests**: All passed (Node.js 18.x, 20.x)" >> $GITHUB_STEP_SUMMARY
else
echo "β **Tests**: Failed" >> $GITHUB_STEP_SUMMARY
fi
# Lint results
if [[ "${{ needs.lint.result }}" == "success" ]]; then
echo "β
**Code Quality**: Passed" >> $GITHUB_STEP_SUMMARY
else
echo "β **Code Quality**: Failed" >> $GITHUB_STEP_SUMMARY
fi
# Cloudflare build results
if [[ "${{ needs.cloudflare.result }}" == "success" ]]; then
echo "β
**Cloudflare Workers**: Build successful" >> $GITHUB_STEP_SUMMARY
else
echo "β **Cloudflare Workers**: Build failed" >> $GITHUB_STEP_SUMMARY
fi
# Development publish results
if [[ "${{ needs.publish-dev.result }}" == "success" ]]; then
echo "β
**NPM Dev Publish**: Published" >> $GITHUB_STEP_SUMMARY
echo " - Install: \`npm install -g @aredes.me/mcp-dadosbr@dev\`" >> $GITHUB_STEP_SUMMARY
elif [[ "${{ needs.publish-dev.result }}" == "skipped" ]]; then
echo "βοΈ **NPM Dev Publish**: Skipped (no token or not main branch)" >> $GITHUB_STEP_SUMMARY
else
echo "β **NPM Dev Publish**: Failed" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Package Info:**" >> $GITHUB_STEP_SUMMARY
echo "- **Name**: \`@aredes.me/mcp-dadosbr\`" >> $GITHUB_STEP_SUMMARY
echo "- **Version**: $(node -p "require('./package.json').version")" >> $GITHUB_STEP_SUMMARY
echo "- **NPM**: [View on NPM](https://www.npmjs.com/package/@aredes.me/mcp-dadosbr)" >> $GITHUB_STEP_SUMMARY
echo "- **Cloudflare**: [Live Demo](https://mcp-dadosbr.aredes.me)" >> $GITHUB_STEP_SUMMARY