Skip to content

chore(deps): update dependency @playwright/mcp to v0.0.72 (#596) #938

chore(deps): update dependency @playwright/mcp to v0.0.72 (#596)

chore(deps): update dependency @playwright/mcp to v0.0.72 (#596) #938

name: Build MCP Server Containers
on:
push:
branches: [ main ]
paths:
- 'npx/**/*.yaml'
- 'uvx/**/*.yaml'
- 'go/**/*.yaml'
- 'cmd/dockhand/**'
- 'go.mod'
- 'go.sum'
pull_request:
branches: [ main ]
paths:
- 'npx/**/*.yaml'
- 'uvx/**/*.yaml'
- 'go/**/*.yaml'
- 'cmd/dockhand/**'
- 'go.mod'
- 'go.sum'
workflow_dispatch:
permissions: {}
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
discover-configs:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
configs: ${{ steps.find-configs.outputs.configs }}
changed-configs: ${{ steps.find-configs.outputs.changed-configs }}
scan-configs: ${{ steps.find-configs.outputs.scan-configs }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0 # Need full history for change detection
- name: Find configuration files to build
id: find-configs
env:
EVENT_NAME: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
run: |
# Find all spec.yaml files in protocol directories
all_configs=$(find npx uvx go -name "spec.yaml" -type f 2>/dev/null | sort)
if [ "$EVENT_NAME" == "workflow_dispatch" ]; then
# For manual triggers, build all configs
configs_to_build="$all_configs"
configs_to_scan="$all_configs"
echo "Manual trigger - building all configurations"
elif [ "$EVENT_NAME" == "pull_request" ]; then
# For PRs, build configs that changed compared to target branch
changed_files=$(git diff --name-only origin/"$BASE_REF"...HEAD)
configs_to_build=""
configs_to_scan=""
# Find configs whose spec.yaml files actually changed
for config in $all_configs; do
config_dir=$(dirname "$config")
if echo "$changed_files" | grep -q "^$config$" || echo "$changed_files" | grep -q "^$config_dir/"; then
configs_to_build="$configs_to_build$config"$'\n'
configs_to_scan="$configs_to_scan$config"$'\n'
fi
done
# If dockhand source, go.mod, or go.sum changed, rebuild all containers
# but only scan the spec.yaml files that actually changed
if echo "$changed_files" | grep -E "(cmd/dockhand/|go\.mod|go\.sum)"; then
echo "Core files changed - rebuilding all containers (scanning only changed specs)"
configs_to_build="$all_configs"
fi
else
# For pushes to main, build configs that changed in this push
changed_files=$(git diff --name-only HEAD~1..HEAD)
configs_to_build=""
configs_to_scan=""
# Find configs whose spec.yaml files actually changed
for config in $all_configs; do
config_dir=$(dirname "$config")
if echo "$changed_files" | grep -q "^$config$" || echo "$changed_files" | grep -q "^$config_dir/"; then
configs_to_build="$configs_to_build$config"$'\n'
configs_to_scan="$configs_to_scan$config"$'\n'
fi
done
# If dockhand source, go.mod, or go.sum changed, rebuild all containers
# but only scan the spec.yaml files that actually changed
if echo "$changed_files" | grep -E "(cmd/dockhand/|go\.mod|go\.sum)"; then
echo "Core files changed - rebuilding all containers (scanning only changed specs)"
configs_to_build="$all_configs"
fi
fi
# Convert to JSON array, filtering out empty lines
configs_json=$(echo "$configs_to_build" | grep -v '^$' | jq -R -s -c 'split("\n")[:-1]')
scan_configs_json=$(echo "$configs_to_scan" | grep -v '^$' | jq -R -s -c 'split("\n")[:-1]')
all_configs_json=$(echo "$all_configs" | jq -R -s -c 'split("\n")[:-1]')
echo "configs=$all_configs_json" >> $GITHUB_OUTPUT
echo "changed-configs=$configs_json" >> $GITHUB_OUTPUT
echo "scan-configs=$scan_configs_json" >> $GITHUB_OUTPUT
echo "All configurations: $all_configs_json"
echo "Configurations to build: $configs_json"
verify-provenance:
needs: discover-configs
runs-on: ubuntu-latest
# Verify package provenance when we have configs to build
if: ${{ needs.discover-configs.outputs.changed-configs != '[]' }}
strategy:
matrix:
config: ${{ fromJson(needs.discover-configs.outputs.changed-configs) }}
fail-fast: false
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: 'go.mod'
- name: Extract metadata from config
id: meta
env:
CONFIG_FILE: ${{ matrix.config }}
run: |
config_file="$CONFIG_FILE"
protocol=$(echo "$config_file" | cut -d'/' -f1)
server_name=$(echo "$config_file" | cut -d'/' -f2)
echo "config_file=$config_file" >> $GITHUB_OUTPUT
echo "protocol=$protocol" >> $GITHUB_OUTPUT
echo "server_name=$server_name" >> $GITHUB_OUTPUT
- name: Build dockhand
run: go build -o /tmp/dockhand ./cmd/dockhand
- name: Verify package provenance
id: provenance
env:
SERVER_NAME: ${{ steps.meta.outputs.server_name }}
CONFIG_FILE: ${{ matrix.config }}
run: |
echo "🔍 Verifying package provenance for $SERVER_NAME"
# Run provenance verification
if /tmp/dockhand verify-provenance -c "$CONFIG_FILE" > "/tmp/provenance-${SERVER_NAME}.txt" 2>&1; then
echo "provenance_passed=true" >> $GITHUB_OUTPUT
echo "✅ Provenance verification passed" >> $GITHUB_STEP_SUMMARY
else
echo "provenance_passed=false" >> $GITHUB_OUTPUT
echo "⚠️ Provenance verification failed or package has no provenance" >> $GITHUB_STEP_SUMMARY
fi
# Display results
cat "/tmp/provenance-${SERVER_NAME}.txt"
# Save for artifact
cp "/tmp/provenance-${SERVER_NAME}.txt" .
- name: Upload provenance verification results
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: provenance-${{ steps.meta.outputs.server_name }}
path: provenance-${{ steps.meta.outputs.server_name }}.txt
retention-days: 30
mcp-security-scan:
needs: [discover-configs, verify-provenance]
runs-on: ubuntu-latest
timeout-minutes: 10
# Only scan configs whose spec.yaml files actually changed
# (not all configs when only go.mod/dockhand changed, since scans don't use dockhand)
if: ${{ needs.discover-configs.outputs.scan-configs != '[]' }}
strategy:
matrix:
config: ${{ fromJson(needs.discover-configs.outputs.scan-configs) }}
fail-fast: false
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: '3.13'
- name: Set up uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
enable-cache: true
- name: Install dependencies
id: install-deps
run: |
# Install pinned Python dependencies from requirements.txt
uv pip install --system -r scripts/mcp-scan/requirements.txt
# Verify installation
mcp-scanner --help || true
# Capture scanner version for attestation
SCANNER_VERSION=$(uv pip show cisco-ai-mcp-scanner | grep "^Version:" | cut -d' ' -f2)
echo "scanner_version=$SCANNER_VERSION" >> $GITHUB_OUTPUT
echo "Installed mcp-scanner version: $SCANNER_VERSION"
- name: Extract metadata from config
id: meta
env:
CONFIG_FILE: ${{ matrix.config }}
run: |
config_file="$CONFIG_FILE"
# Extract protocol and server name from path like "npx/context7/spec.yaml"
protocol=$(echo "$config_file" | cut -d'/' -f1)
server_name=$(echo "$config_file" | cut -d'/' -f2)
echo "config_file=$config_file" >> $GITHUB_OUTPUT
echo "protocol=$protocol" >> $GITHUB_OUTPUT
echo "server_name=$server_name" >> $GITHUB_OUTPUT
- name: Pre-install npm package
if: steps.meta.outputs.protocol == 'npx'
env:
CONFIG_FILE: ${{ matrix.config }}
run: |
# mcp-scanner has a 60s timeout for server startup. npx downloading
# a package can exceed this, so pre-install it globally first.
package=$(python3 -c "
import yaml
with open('$CONFIG_FILE') as f:
data = yaml.safe_load(f)
spec = data['spec']
print(spec['package'] + '@' + spec.get('version', 'latest'))
")
echo "Pre-installing $package..."
npm install --global "$package"
- name: Run MCP Security Scan
id: scan
env:
MCP_SCANNER_ENABLE_LLM: ${{ vars.MCP_SCANNER_ENABLE_LLM || 'false' }}
MCP_SCANNER_LLM_API_KEY: ${{ secrets.MCP_SCANNER_LLM_API_KEY }}
MCP_SCANNER_LLM_MODEL: ${{ vars.MCP_SCANNER_LLM_MODEL || '' }}
# Severity threshold for blocking. Findings below this severity are
# surfaced as warnings but do not fail the job. One of: INFO, LOW,
# MEDIUM, HIGH, CRITICAL. Tune via PR.
MCP_SCANNER_BLOCK_SEVERITY: HIGH
SERVER_NAME: ${{ steps.meta.outputs.server_name }}
PROTOCOL: ${{ steps.meta.outputs.protocol }}
CONFIG_FILE: ${{ matrix.config }}
SCANNER_VERSION: ${{ steps.install-deps.outputs.scanner_version }}
run: |
echo "🔍 Scanning MCP server: $SERVER_NAME"
# Generate config (outputs JSON with command/args/mock_env)
config_json=$(python3 scripts/mcp-scan/generate_mcp_config.py \
"$CONFIG_FILE" \
"$PROTOCOL" \
"$SERVER_NAME")
# Write config to file for run_scan.py
scan_config="/tmp/scan-config-${SERVER_NAME}.json"
echo "$config_json" > "$scan_config"
# Run scan using Cisco AI Defense mcp-scanner
# Note: stderr is redirected to a separate file to avoid corrupting JSON output
scan_output="/tmp/mcp-scan-${SERVER_NAME}.json"
scan_stderr="/tmp/mcp-scan-${SERVER_NAME}.stderr"
if python3 scripts/mcp-scan/run_scan.py --config "$scan_config" \
> "$scan_output" 2> "$scan_stderr"; then
echo "scan_passed=true" >> $GITHUB_OUTPUT
else
echo "scan_passed=false" >> $GITHUB_OUTPUT
# Show stderr in logs for debugging
if [ -s "$scan_stderr" ]; then
echo "Scanner stderr output:"
cat "$scan_stderr"
fi
fi
# Process results (pass the config file to check for allowed issues)
python3 scripts/mcp-scan/process_scan_results.py \
"$scan_output" \
"$SERVER_NAME" \
"$CONFIG_FILE" \
> scan-summary.json
# Copy scan output to current directory for artifact upload
cp "$scan_output" "mcp-scan-${SERVER_NAME}.json"
# Save scanner version for attestation (from install-deps step)
echo "$SCANNER_VERSION" > scanner-version.txt
- name: Upload scan results
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: mcp-scan-${{ steps.meta.outputs.server_name }}
path: |
mcp-scan-${{ steps.meta.outputs.server_name }}.json
scan-summary.json
scanner-version.txt
retention-days: 30
build-containers:
needs: [discover-configs, verify-provenance, mcp-security-scan]
runs-on: ubuntu-latest
timeout-minutes: 60
# Only proceed if security scans passed or were skipped (provenance check is informational only)
if: ${{ needs.discover-configs.outputs.changed-configs != '[]' && (needs.mcp-security-scan.result == 'success' || needs.mcp-security-scan.result == 'skipped') }}
strategy:
matrix:
config: ${{ fromJson(needs.discover-configs.outputs.changed-configs) }}
fail-fast: false
permissions:
contents: read
packages: write
id-token: write # Needed for OIDC token (sigstore)
attestations: write # Needed for attestations
security-events: write # Needed for Grype SARIF upload
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: 'go.mod'
- name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
- name: Install Cosign
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
- name: Install yq
uses: mikefarah/yq@751d8ad57b84f1794661bc70c0afb92a22ad7b3c # v4.53.2
- name: Log in to Container Registry
# Only login when we're going to push (main branch or manual trigger)
if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata from config
id: meta
env:
CONFIG_FILE: ${{ matrix.config }}
run: |
config_file="$CONFIG_FILE"
echo "config_file=$config_file" >> $GITHUB_OUTPUT
# Extract protocol from directory name (first part of path)
protocol=$(echo "$config_file" | cut -d'/' -f1)
echo "protocol=$protocol" >> $GITHUB_OUTPUT
# Extract server name from directory name (second part of path)
server_name=$(echo "$config_file" | cut -d'/' -f2)
echo "server_name=$server_name" >> $GITHUB_OUTPUT
# Extract version from YAML file (spec.version)
spec_version=$(yq '.spec.version' "$config_file" 2>/dev/null || echo "")
# Use spec.version if available, otherwise "latest"
if [ -n "$spec_version" ]; then
version="$spec_version"
else
version="latest"
fi
echo "version=$version" >> $GITHUB_OUTPUT
# Generate image name
image_name="${REGISTRY}/${IMAGE_NAME}/${protocol}/${server_name}"
echo "image_name=$image_name" >> $GITHUB_OUTPUT
- name: Generate Dockerfile
id: dockerfile
env:
CONFIG_FILE: ${{ steps.meta.outputs.config_file }}
run: |
echo "Generating Dockerfile for $CONFIG_FILE"
# Create a temporary directory for the Dockerfile
dockerfile_dir=$(mktemp -d)
dockerfile_path="${dockerfile_dir}/Dockerfile"
# Build and run dockhand to generate the Dockerfile
go build -o /tmp/dockhand ./cmd/dockhand
/tmp/dockhand build --config "$CONFIG_FILE" --output "${dockerfile_path}"
echo "dockerfile_dir=$dockerfile_dir" >> $GITHUB_OUTPUT
echo "dockerfile_path=$dockerfile_path" >> $GITHUB_OUTPUT
# Display the generated Dockerfile for debugging
echo "Generated Dockerfile:"
cat "${dockerfile_path}"
- name: Build and push Docker image
id: build
timeout-minutes: 30
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
with:
context: ${{ steps.dockerfile.outputs.dockerfile_dir }}
file: ${{ steps.dockerfile.outputs.dockerfile_path }}
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: |
${{ steps.meta.outputs.image_name }}:${{ steps.meta.outputs.version }}
${{ steps.meta.outputs.image_name }}:latest
labels: |
org.opencontainers.image.title=${{ steps.meta.outputs.server_name }}
org.opencontainers.image.description=MCP server for ${{ steps.meta.outputs.server_name }}
org.opencontainers.image.vendor=Stacklok
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.version=${{ steps.meta.outputs.version }}
cache-from: type=gha
cache-to: type=gha,mode=max
sbom: true
provenance: true
- name: Build single-platform image for Grype scan
id: build-for-scan
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
with:
context: ${{ steps.dockerfile.outputs.dockerfile_dir }}
file: ${{ steps.dockerfile.outputs.dockerfile_path }}
load: true
tags: local-scan:${{ steps.meta.outputs.server_name }}-${{ steps.meta.outputs.version }}
cache-from: type=gha
- name: Sign container images with Cosign
if: github.event_name != 'pull_request'
env:
DIGEST: ${{ steps.build.outputs.digest }}
IMAGE_NAME: ${{ steps.meta.outputs.image_name }}
VERSION: ${{ steps.meta.outputs.version }}
run: |
echo "Signing image ${IMAGE_NAME}@${DIGEST}"
cosign sign --yes "${IMAGE_NAME}@${DIGEST}"
# Also sign the tagged versions for better UX
cosign sign --yes "${IMAGE_NAME}:${VERSION}"
cosign sign --yes "${IMAGE_NAME}:latest"
echo "✅ Images signed with Sigstore/Cosign" >> $GITHUB_STEP_SUMMARY
- name: Download MCP security scan results
id: download-scan
if: github.event_name != 'pull_request'
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: mcp-scan-${{ steps.meta.outputs.server_name }}
path: /tmp/scan-results
continue-on-error: false # Fail if scan results are missing
- name: Create security scan attestation (SCAI format)
if: github.event_name != 'pull_request'
env:
DIGEST: ${{ steps.build.outputs.digest }}
IMAGE_NAME: ${{ steps.meta.outputs.image_name }}
CONFIG_FILE: ${{ matrix.config }}
COMMIT_SHA: ${{ github.sha }}
RUN_ID: ${{ github.run_id }}
SERVER_URL: ${{ github.server_url }}
REPO: ${{ github.repository }}
run: |
echo "Creating SCAI security scan attestation for ${IMAGE_NAME}@${DIGEST}"
# Read scanner version from scan results
SCANNER_VERSION=""
if [ -f /tmp/scan-results/scanner-version.txt ]; then
SCANNER_VERSION=$(cat /tmp/scan-results/scanner-version.txt | tr -d '\n')
echo "Scanner version: $SCANNER_VERSION"
fi
# Generate SCAI attestation using Python script
# Reference: https://github.com/in-toto/attestation/blob/main/spec/predicates/scai.md
# Note: Uses github.server_url to support GitHub Enterprise Server
# Note: Analyzers are read from scan-summary.json (extracted from scanner output)
python3 scripts/mcp-scan/generate_scai_attestation.py \
/tmp/scan-results/scan-summary.json \
"$IMAGE_NAME" \
"${DIGEST}" \
--config-file "$CONFIG_FILE" \
--commit-sha "$COMMIT_SHA" \
--run-id "$RUN_ID" \
--run-url "${SERVER_URL}/${REPO}/actions/runs/${RUN_ID}" \
--producer-uri "${SERVER_URL}/${REPO}" \
--scanner-version "$SCANNER_VERSION" \
--scanner-uri "https://github.com/cisco-ai-defense/mcp-scanner" \
--validate \
--output /tmp/security-attestation.json
# Show the generated attestation for debugging
echo "Generated SCAI attestation:"
cat /tmp/security-attestation.json
# Attest the security scan results with SCAI predicate type
cosign attest --yes \
--predicate /tmp/security-attestation.json \
--type https://in-toto.io/attestation/scai/v0.3 \
"${IMAGE_NAME}@${DIGEST}"
# Extract attribute for summary
ATTRIBUTE=$(jq -r '.predicate.attributes[0].attribute' /tmp/security-attestation.json)
echo "✅ SCAI security scan attestation created (${ATTRIBUTE})" >> $GITHUB_STEP_SUMMARY
# Clean up
rm -f /tmp/security-attestation.json
- name: Run Grype vulnerability scanner
id: grype-scan
uses: anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2 # v7.4.0
with:
image: "local-scan:${{ steps.meta.outputs.server_name }}-${{ steps.meta.outputs.version }}"
severity-cutoff: "high"
only-fixed: "true"
output-format: "sarif"
- name: Upload Grype results to GitHub Security
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
if: always()
with:
sarif_file: ${{ steps.grype-scan.outputs.sarif }}
category: 'grype-${{ steps.meta.outputs.server_name }}'
- name: Generate image summary
env:
CONFIG_FILE: ${{ steps.meta.outputs.config_file }}
PROTOCOL: ${{ steps.meta.outputs.protocol }}
SERVER_NAME: ${{ steps.meta.outputs.server_name }}
IMAGE_NAME: ${{ steps.meta.outputs.image_name }}
VERSION: ${{ steps.meta.outputs.version }}
DIGEST: ${{ steps.build.outputs.digest }}
EVENT_NAME: ${{ github.event_name }}
run: |
echo "## Container Build Summary" >> $GITHUB_STEP_SUMMARY
echo "- **Config**: $CONFIG_FILE" >> $GITHUB_STEP_SUMMARY
echo "- **Protocol**: $PROTOCOL" >> $GITHUB_STEP_SUMMARY
echo "- **Server**: $SERVER_NAME" >> $GITHUB_STEP_SUMMARY
echo "- **Image**: $IMAGE_NAME" >> $GITHUB_STEP_SUMMARY
echo "- **Version**: $VERSION" >> $GITHUB_STEP_SUMMARY
echo "- **Platforms**: linux/amd64, linux/arm64" >> $GITHUB_STEP_SUMMARY
if [ "$EVENT_NAME" != "pull_request" ]; then
echo "- **SBOM**: ✅ Attested" >> $GITHUB_STEP_SUMMARY
echo "- **Build Provenance**: ✅ Attested" >> $GITHUB_STEP_SUMMARY
echo "- **Security Scan**: ✅ Attested" >> $GITHUB_STEP_SUMMARY
echo "- **Signatures**: ✅ Signed with Sigstore/Cosign" >> $GITHUB_STEP_SUMMARY
echo "- **Grype Scan**: ✅ Completed (see Security tab)" >> $GITHUB_STEP_SUMMARY
echo "- **Status**: ✅ Built, pushed, signed, and attested" >> $GITHUB_STEP_SUMMARY
echo "- **Tags**:" >> $GITHUB_STEP_SUMMARY
echo " - ${IMAGE_NAME}:${VERSION}" >> $GITHUB_STEP_SUMMARY
echo " - ${IMAGE_NAME}:latest" >> $GITHUB_STEP_SUMMARY
echo "- **Digest**: $DIGEST" >> $GITHUB_STEP_SUMMARY
else
echo "- **Grype Scan**: ✅ Completed (see Security tab)" >> $GITHUB_STEP_SUMMARY
echo "- **Status**: ✅ Built (not pushed - PR)" >> $GITHUB_STEP_SUMMARY
fi
# Save PR number as artifact for the mcp-scan-report workflow.
# This is needed because workflow_run doesn't reliably provide PR info for fork PRs.
save-pr-number:
runs-on: ubuntu-latest
permissions: {}
if: github.event_name == 'pull_request'
steps:
- name: Save PR number
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
run: echo "$PR_NUMBER" > pr-number.txt
- name: Upload PR number
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: pr-number
path: pr-number.txt
retention-days: 5
summary:
needs: [discover-configs, verify-provenance, mcp-security-scan, build-containers]
runs-on: ubuntu-latest
permissions: {}
if: always()
steps:
- name: Build Summary
env:
EVENT_NAME: ${{ github.event_name }}
REF: ${{ github.ref }}
ALL_CONFIGS: ${{ needs.discover-configs.outputs.configs }}
CHANGED_CONFIGS: ${{ needs.discover-configs.outputs.changed-configs }}
PROVENANCE_RESULT: ${{ needs.verify-provenance.result }}
SCAN_RESULT: ${{ needs.mcp-security-scan.result }}
BUILD_RESULT: ${{ needs.build-containers.result }}
run: |
echo "## Dockyard Build Summary" >> $GITHUB_STEP_SUMMARY
echo "- **Trigger**: $EVENT_NAME" >> $GITHUB_STEP_SUMMARY
echo "- **Ref**: $REF" >> $GITHUB_STEP_SUMMARY
echo "- **Total Configs**: $ALL_CONFIGS" >> $GITHUB_STEP_SUMMARY
echo "- **Changed Configs**: $CHANGED_CONFIGS" >> $GITHUB_STEP_SUMMARY
if [ "$PROVENANCE_RESULT" == "success" ]; then
echo "- **Provenance Verification**: ✅ All packages verified" >> $GITHUB_STEP_SUMMARY
elif [ "$PROVENANCE_RESULT" == "failure" ]; then
echo "- **Provenance Verification**: ⚠️ Some packages have provenance issues (non-blocking)" >> $GITHUB_STEP_SUMMARY
elif [ "$PROVENANCE_RESULT" == "skipped" ]; then
echo "- **Provenance Verification**: ⏭️ Skipped" >> $GITHUB_STEP_SUMMARY
fi
if [ "$SCAN_RESULT" == "success" ]; then
echo "- **Security Scan**: ✅ All MCP servers passed security scanning" >> $GITHUB_STEP_SUMMARY
elif [ "$SCAN_RESULT" == "failure" ]; then
echo "- **Security Scan**: ❌ Some MCP servers have security issues" >> $GITHUB_STEP_SUMMARY
elif [ "$SCAN_RESULT" == "skipped" ]; then
echo "- **Security Scan**: ⏭️ Skipped" >> $GITHUB_STEP_SUMMARY
fi
if [ "$BUILD_RESULT" == "success" ]; then
echo "- **Build Status**: ✅ All changed containers built successfully" >> $GITHUB_STEP_SUMMARY
echo "- **Features**:" >> $GITHUB_STEP_SUMMARY
echo " - 🏗️ Multi-architecture support (amd64, arm64)" >> $GITHUB_STEP_SUMMARY
echo " - 📦 SBOM (Software Bill of Materials) included" >> $GITHUB_STEP_SUMMARY
echo " - 🔐 Provenance attestation for supply chain security" >> $GITHUB_STEP_SUMMARY
echo " - ✍️ Sigstore/Cosign signatures for image verification" >> $GITHUB_STEP_SUMMARY
echo " - 🚀 GitHub Actions cache for faster builds" >> $GITHUB_STEP_SUMMARY
elif [ "$BUILD_RESULT" == "failure" ]; then
echo "- **Build Status**: ❌ Some containers failed to build" >> $GITHUB_STEP_SUMMARY
elif [ "$BUILD_RESULT" == "skipped" ]; then
echo "- **Build Status**: ⏭️ No configuration changes detected" >> $GITHUB_STEP_SUMMARY
else
echo "- **Build Status**: ⚠️ Build status unknown" >> $GITHUB_STEP_SUMMARY
fi
# Add efficiency note
changed_count=$(echo "$CHANGED_CONFIGS" | jq length)
total_count=$(echo "$ALL_CONFIGS" | jq length)
echo "- **Efficiency**: Built $changed_count out of $total_count configurations" >> $GITHUB_STEP_SUMMARY