Split MCP Gateway into download and start steps with gh CLI authentic… #27889
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| paths: | |
| - '**.go' | |
| - 'pkg/workflow/**' | |
| - 'actions/**' | |
| - '.github/workflows/ci.yml' | |
| - '.github/workflows/**/*.md' | |
| workflow_dispatch: | |
| jobs: | |
| test: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }}-test | |
| cancel-in-progress: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - name: Set up Go | |
| id: setup-go | |
| uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Report Go cache status | |
| run: | | |
| if [ "${{ steps.setup-go.outputs.cache-hit }}" == "true" ]; then | |
| echo "✅ Go cache hit" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Go cache miss" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| - name: Verify dependencies | |
| run: go mod verify | |
| - name: Run unit tests with coverage | |
| run: | | |
| go test -v -parallel=8 -timeout=3m -tags '!integration' -run='^Test' -coverprofile=coverage.out -json ./... | tee test-result-unit.json | |
| go tool cover -html=coverage.out -o coverage.html | |
| # Coverage reports for recent builds only - 7 days is sufficient for debugging recent changes | |
| - name: Upload coverage report | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: coverage-report | |
| path: coverage.html | |
| retention-days: 7 | |
| - name: Upload unit test results | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: test-result-unit | |
| path: test-result-unit.json | |
| retention-days: 14 | |
| integration: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| test-group: | |
| - name: "CLI Compile & Poutine" | |
| packages: "./pkg/cli" | |
| pattern: "^TestCompile[^W]|TestPoutine" # Exclude TestCompileWorkflows to avoid duplicates | |
| - name: "CLI MCP Playwright" | |
| packages: "./pkg/cli" | |
| pattern: "TestMCPInspectPlaywright" | |
| - name: "CLI MCP Gateway" | |
| packages: "./pkg/cli" | |
| pattern: "TestMCPGateway" | |
| - name: "CLI MCP Other" | |
| packages: "./pkg/cli" | |
| pattern: "TestMCPAdd|TestMCPInspectGitHub|TestMCPServer|TestMCPConfig" | |
| - name: "CLI Logs & Firewall" | |
| packages: "./pkg/cli" | |
| pattern: "TestLogs|TestFirewall|TestNoStopTime|TestLocalWorkflow" | |
| - name: "CLI Progress Flag" # Isolate slow test (~65s for TestProgressFlagSignature) | |
| packages: "./pkg/cli" | |
| pattern: "TestProgressFlagSignature" | |
| - name: "CLI HTTP MCP Connect" # Isolate slow HTTP MCP connection tests (~43s) | |
| packages: "./pkg/cli" | |
| pattern: "TestConnectHTTPMCPServer" | |
| - name: "CLI Compile Workflows" # Isolate slow workflow compilation test | |
| packages: "./pkg/cli" | |
| pattern: "TestCompileWorkflows_EmptyMarkdown" | |
| - name: "CLI Security Tools" # Group security tool compilation tests | |
| packages: "./pkg/cli" | |
| pattern: "TestCompileWithZizmor|TestCompileWithPoutine|TestCompileWithPoutineAndZizmor" | |
| - name: "CLI Add & List Commands" | |
| packages: "./pkg/cli" | |
| pattern: "^TestAdd|^TestList" | |
| - name: "CLI Update Command" | |
| packages: "./pkg/cli" | |
| pattern: "^TestUpdate" | |
| - name: "CLI Audit & Inspect" | |
| packages: "./pkg/cli" | |
| pattern: "^TestAudit|^TestInspect" | |
| - name: "CLI Completion & Other" # Remaining catch-all (reduced from original) | |
| packages: "./pkg/cli" | |
| pattern: "" # Catch-all for tests not matched by other CLI patterns | |
| skip_pattern: "^TestCompile[^W]|TestPoutine|TestMCPInspectPlaywright|TestMCPGateway|TestMCPAdd|TestMCPInspectGitHub|TestMCPServer|TestMCPConfig|TestLogs|TestFirewall|TestNoStopTime|TestLocalWorkflow|TestProgressFlagSignature|TestConnectHTTPMCPServer|TestCompileWorkflows_EmptyMarkdown|TestCompileWithZizmor|TestCompileWithPoutine|TestCompileWithPoutineAndZizmor|^TestAdd|^TestList|^TestUpdate|^TestAudit|^TestInspect" | |
| - name: "Workflow Compiler" | |
| packages: "./pkg/workflow" | |
| pattern: "TestCompile|TestWorkflow|TestGenerate|TestParse" | |
| - name: "Workflow Tools & MCP" | |
| packages: "./pkg/workflow" | |
| pattern: "TestMCP|TestTool|TestSkill|TestPlaywright|TestFirewall" | |
| - name: "Workflow Validation" | |
| packages: "./pkg/workflow" | |
| pattern: "TestValidat|TestLock|TestError|TestWarning" | |
| - name: "Workflow Safe Outputs" | |
| packages: "./pkg/workflow" | |
| pattern: "SafeOutputs|CreatePullRequest|OutputLabel|HasSafeOutputs" | |
| - name: "Workflow GitHub & Git" | |
| packages: "./pkg/workflow" | |
| pattern: "GitHub|Git|PushToPullRequest|BuildFromAllowed" | |
| - name: "Workflow Rendering & Bundling" | |
| packages: "./pkg/workflow" | |
| pattern: "Render|Bundle|Script|WritePromptText" | |
| - name: "Workflow Cache" | |
| packages: "./pkg/workflow" | |
| pattern: "^TestCache|TestCacheDependencies|TestCacheKey|TestValidateCache" | |
| - name: "Workflow Actions Pin Validation" | |
| packages: "./pkg/workflow" | |
| pattern: "^TestActionPinSHAsMatchVersionTags" | |
| - name: "Workflow Actions & Containers" | |
| packages: "./pkg/workflow" | |
| pattern: "^TestAction[^P]|Container" | |
| - name: "Workflow Dependabot & Security" | |
| packages: "./pkg/workflow" | |
| pattern: "Dependabot|Security|PII" | |
| - name: "CMD Tests" # All cmd/gh-aw integration tests | |
| packages: "./cmd/gh-aw" | |
| pattern: "" | |
| skip_pattern: "" # No other groups cover cmd tests | |
| - name: "Parser Remote Fetch & Cache" | |
| packages: "./pkg/parser" | |
| pattern: "TestDownloadFileFromGitHub|TestResolveIncludePath|TestDownloadIncludeFromWorkflowSpec|TestImportCache" | |
| - name: "Parser Location & Validation" | |
| packages: "./pkg/parser" | |
| pattern: "" # Catch-all for tests not matched by other Parser patterns | |
| skip_pattern: "TestDownloadFileFromGitHub|TestResolveIncludePath|TestDownloadIncludeFromWorkflowSpec|TestImportCache" | |
| - name: "Workflow Permissions" | |
| packages: "./pkg/workflow" | |
| pattern: "TestPermissions|TestPackageExtractor|TestCollectPackagesFromWorkflow" | |
| - name: "Workflow Misc Part 1" # Split large catch-all into two balanced groups | |
| packages: "./pkg/workflow" | |
| pattern: "TestAgent|TestCopilot|TestCustom|TestEngine|TestModel|TestNetwork|TestOpenAI|TestProvider" | |
| - name: "Workflow String & Sanitization" | |
| packages: "./pkg/workflow" | |
| pattern: "String|Sanitize|Normalize|Trim|Clean|Format" | |
| - name: "Workflow Runtime & Setup" | |
| packages: "./pkg/workflow" | |
| pattern: "Runtime|Setup|Install|Download|Version|Binary" | |
| - name: "Workflow Misc Part 2" # Remaining workflow tests | |
| packages: "./pkg/workflow" | |
| pattern: "" | |
| skip_pattern: "TestCompile|TestWorkflow|TestGenerate|TestParse|TestMCP|TestTool|TestSkill|TestPlaywright|TestFirewall|TestValidat|TestLock|TestError|TestWarning|SafeOutputs|CreatePullRequest|OutputLabel|HasSafeOutputs|GitHub|Git|PushToPullRequest|BuildFromAllowed|Render|Bundle|Script|WritePromptText|^TestCache|TestCacheDependencies|TestCacheKey|TestValidateCache|^TestActionPinSHAsMatchVersionTags|^TestAction[^P]|Container|Dependabot|Security|PII|TestPermissions|TestPackageExtractor|TestCollectPackagesFromWorkflow|TestAgent|TestCopilot|TestCustom|TestEngine|TestModel|TestNetwork|TestOpenAI|TestProvider|String|Sanitize|Normalize|Trim|Clean|Format|Runtime|Setup|Install|Download|Version|Binary" | |
| - name: "AWMG Gateway Tests" # MCP gateway integration tests | |
| packages: "./pkg/awmg" | |
| pattern: "" | |
| concurrency: | |
| group: ci-${{ github.ref }}-integration-${{ matrix.test-group.name }} | |
| cancel-in-progress: true | |
| name: "Integration: ${{ matrix.test-group.name }}" | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - name: Set up Go | |
| id: setup-go | |
| uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Report Go cache status | |
| run: | | |
| if [ "${{ steps.setup-go.outputs.cache-hit }}" == "true" ]; then | |
| echo "✅ Go cache hit" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Go cache miss" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| - name: Verify dependencies | |
| run: go mod verify | |
| - name: Run integration tests - ${{ matrix.test-group.name }} | |
| run: | | |
| # Sanitize the test group name for use in filename | |
| SAFE_NAME=$(echo "${{ matrix.test-group.name }}" | sed 's/[^a-zA-Z0-9]/-/g' | sed 's/--*/-/g') | |
| if [ -z "${{ matrix.test-group.pattern }}" ]; then | |
| # Catch-all group: run with -skip to exclude tests matched by other groups | |
| if [ -n "${{ matrix.test-group.skip_pattern || '' }}" ]; then | |
| go test -v -parallel=8 -timeout=5m -tags 'integration' -skip '${{ matrix.test-group.skip_pattern }}' -json ${{ matrix.test-group.packages }} | tee "test-result-integration-${SAFE_NAME}.json" | |
| else | |
| go test -v -parallel=8 -timeout=5m -tags 'integration' -json ${{ matrix.test-group.packages }} | tee "test-result-integration-${SAFE_NAME}.json" | |
| fi | |
| else | |
| go test -v -parallel=8 -timeout=5m -tags 'integration' -run '${{ matrix.test-group.pattern }}' -json ${{ matrix.test-group.packages }} | tee "test-result-integration-${SAFE_NAME}.json" | |
| fi | |
| - name: Upload integration test results | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: test-result-integration-${{ matrix.test-group.name }} | |
| path: test-result-integration-*.json | |
| retention-days: 14 | |
| update: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }}-update | |
| cancel-in-progress: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - name: Set up Go | |
| id: setup-go | |
| uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Report Go cache status | |
| run: | | |
| if [ "${{ steps.setup-go.outputs.cache-hit }}" == "true" ]; then | |
| echo "✅ Go cache hit" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Go cache miss" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| - name: Verify dependencies | |
| run: go mod verify | |
| - name: Build gh-aw binary | |
| run: make build | |
| - name: Test update command (dry-run) | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| echo "Testing update command to ensure it runs without issues..." | |
| # Run update with verbose flag to check for updates without making changes | |
| # The command checks for gh-aw updates, action updates, and workflow updates | |
| ./gh-aw update --verbose --no-actions | |
| echo "✅ Update command executed successfully" >> $GITHUB_STEP_SUMMARY | |
| build: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }}-build | |
| cancel-in-progress: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - name: Set up Node.js | |
| id: setup-node | |
| uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6 | |
| with: | |
| node-version: "24" | |
| cache: npm | |
| cache-dependency-path: actions/setup/js/package-lock.json | |
| - name: Report Node cache status | |
| run: | | |
| if [ "${{ steps.setup-node.outputs.cache-hit }}" == "true" ]; then | |
| echo "✅ Node cache hit" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Node cache miss" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| - name: Set up Go | |
| id: setup-go | |
| uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Report Go cache status | |
| run: | | |
| if [ "${{ steps.setup-go.outputs.cache-hit }}" == "true" ]; then | |
| echo "✅ Go cache hit" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Go cache miss" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| - name: npm ci | |
| run: npm ci | |
| working-directory: ./actions/setup/js | |
| - name: Build code | |
| run: make build | |
| - name: Rebuild lock files | |
| run: make recompile | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| js: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }}-js | |
| cancel-in-progress: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - name: Set up Node.js | |
| id: setup-node | |
| uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6 | |
| with: | |
| node-version: "24" | |
| cache: npm | |
| cache-dependency-path: actions/setup/js/package-lock.json | |
| - name: Report Node cache status | |
| run: | | |
| if [ "${{ steps.setup-node.outputs.cache-hit }}" == "true" ]; then | |
| echo "✅ Node cache hit" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Node cache miss" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| - name: Install npm dependencies | |
| run: cd actions/setup/js && npm ci | |
| - name: Run tests | |
| run: cd actions/setup/js && npm test | |
| bench: | |
| # Only run benchmarks on main branch for performance tracking | |
| if: github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }}-bench | |
| cancel-in-progress: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - name: Set up Go | |
| id: setup-go | |
| uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Report Go cache status | |
| run: | | |
| if [ "${{ steps.setup-go.outputs.cache-hit }}" == "true" ]; then | |
| echo "✅ Go cache hit" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Go cache miss" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| - name: Verify dependencies | |
| run: go mod verify | |
| - name: Run benchmarks | |
| run: make bench | |
| - name: Display benchmark summary | |
| run: | | |
| echo "## 📊 Benchmark Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY | |
| # Show compiler benchmarks from the results | |
| grep "BenchmarkCompile" bench_results.txt | head -20 >> $GITHUB_STEP_SUMMARY || echo "No benchmark results found" >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "📁 Full results saved to artifact: benchmark-results" >> $GITHUB_STEP_SUMMARY | |
| # Benchmark results for performance trend analysis - 14 days allows comparison across multiple runs | |
| - name: Save benchmark results | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: benchmark-results | |
| path: bench_results.txt | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| lint-go: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }}-lint-go | |
| cancel-in-progress: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - name: Set up Go | |
| id: setup-go | |
| uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Report Go cache status | |
| run: | | |
| if [ "${{ steps.setup-go.outputs.cache-hit }}" == "true" ]; then | |
| echo "✅ Go cache hit" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Go cache miss" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # Go formatting check (fast, no deps needed) | |
| - name: Check Go formatting | |
| run: | | |
| unformatted=$(go fmt ./...) | |
| if [ -n "$unformatted" ]; then | |
| echo "❌ Code is not formatted. Run 'make fmt' to fix." >> $GITHUB_STEP_SUMMARY | |
| echo "Unformatted files:" >> $GITHUB_STEP_SUMMARY | |
| echo "$unformatted" >> $GITHUB_STEP_SUMMARY | |
| echo "" | |
| echo "To fix this locally, run:" | |
| echo " make fmt" | |
| echo "" | |
| echo "Or format individual files with:" | |
| echo " go fmt ./path/to/file.go" | |
| exit 1 | |
| fi | |
| echo "✅ Go formatting check passed" >> $GITHUB_STEP_SUMMARY | |
| # Install only golangci-lint (the only tool needed for linting) | |
| # Other tools (actionlint, gosec, gopls, govulncheck) are not used in this job | |
| - name: Install golangci-lint | |
| run: go install github.com/golangci/golangci-lint/v2/cmd/[email protected] | |
| # Run golangci-lint via Makefile for consistency | |
| # Uses incremental linting on PRs for faster CI (50-75% speedup) | |
| # Performance optimizations in .golangci.yml: | |
| # - timeout: 5m prevents hanging | |
| # - modules-download-mode: readonly uses cached modules only | |
| - name: Run golangci-lint | |
| run: | | |
| export PATH="$PATH:$(go env GOPATH)/bin" | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| # Incremental linting on PRs - only check changed files | |
| # This provides 50-75% faster linting on typical PRs | |
| make golint-incremental BASE_REF=origin/${{ github.base_ref }} | |
| else | |
| # Full scan on main branch to ensure comprehensive coverage | |
| make golint | |
| fi | |
| # Error message linting (requires Go only) | |
| - name: Lint error messages | |
| run: make lint-errors | |
| lint-js: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }}-lint-js | |
| cancel-in-progress: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - name: Set up Node.js | |
| id: setup-node | |
| uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6 | |
| with: | |
| node-version: "24" | |
| cache: npm | |
| cache-dependency-path: actions/setup/js/package-lock.json | |
| - name: Report Node cache status | |
| run: | | |
| if [ "${{ steps.setup-node.outputs.cache-hit }}" == "true" ]; then | |
| echo "✅ Node cache hit" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Node cache miss" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| - name: Install npm dependencies | |
| run: cd actions/setup/js && npm ci | |
| # JavaScript and JSON formatting checks | |
| - name: Lint JavaScript files | |
| run: make lint-cjs | |
| - name: Check JSON formatting | |
| run: make fmt-check-json | |
| audit: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }}-audit | |
| cancel-in-progress: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - name: Set up Go | |
| id: setup-go | |
| uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Report Go cache status | |
| run: | | |
| if [ "${{ steps.setup-go.outputs.cache-hit }}" == "true" ]; then | |
| echo "✅ Go cache hit" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Go cache miss" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| - name: Verify dependencies | |
| run: go mod verify | |
| - name: Build gh-aw binary | |
| run: make build | |
| - name: Run dependency audit (human-readable) | |
| run: | | |
| echo "## Dependency Health Audit" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| ./gh-aw update --audit 2>&1 | tee audit_output.txt || true | |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY | |
| head -100 audit_output.txt >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY | |
| - name: Run dependency audit (JSON) | |
| id: audit_json | |
| run: | | |
| # Run audit with JSON output for agent-friendly parsing | |
| ./gh-aw update --audit --json > audit.json 2>&1 | |
| # Display summary in GitHub Actions | |
| echo "✅ Dependency audit completed" >> $GITHUB_STEP_SUMMARY | |
| # Extract key metrics | |
| TOTAL_DEPS=$(jq '.summary.total_dependencies' audit.json) | |
| OUTDATED=$(jq '.summary.outdated_count' audit.json) | |
| SECURITY=$(jq '.summary.security_advisories' audit.json) | |
| V0_PERCENT=$(jq '.summary.v0_percentage' audit.json) | |
| echo "📊 **Audit Results:**" >> $GITHUB_STEP_SUMMARY | |
| echo "- Total dependencies: $TOTAL_DEPS" >> $GITHUB_STEP_SUMMARY | |
| echo "- Outdated: $OUTDATED" >> $GITHUB_STEP_SUMMARY | |
| echo "- Security advisories: $SECURITY" >> $GITHUB_STEP_SUMMARY | |
| echo "- v0.x exposure: ${V0_PERCENT}%" >> $GITHUB_STEP_SUMMARY | |
| - name: Upload audit results | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: dependency-audit | |
| path: | | |
| audit.json | |
| audit_output.txt | |
| retention-days: 30 | |
| actions-build: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }}-actions-build | |
| cancel-in-progress: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - name: Set up Go | |
| id: setup-go | |
| uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Report Go cache status | |
| run: | | |
| if [ "${{ steps.setup-go.outputs.cache-hit }}" == "true" ]; then | |
| echo "✅ Go cache hit" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Go cache miss" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| - name: Verify dependencies | |
| run: go mod verify | |
| - name: Build actions | |
| run: make actions-build | |
| - name: Validate actions | |
| run: make actions-validate | |
| fuzz: | |
| # Only run fuzz tests on main branch (10s is insufficient for PRs) | |
| if: github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }}-fuzz | |
| cancel-in-progress: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - name: Set up Go | |
| id: setup-go | |
| uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Report Go cache status | |
| run: | | |
| if [ "${{ steps.setup-go.outputs.cache-hit }}" == "true" ]; then | |
| echo "✅ Go cache hit" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Go cache miss" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| - name: Verify dependencies | |
| run: go mod verify | |
| - name: Run fuzz tests | |
| run: | | |
| go test -run='^$' -fuzz=FuzzParseFrontmatter -fuzztime=10s ./pkg/parser/ | |
| go test -run='^$' -fuzz=FuzzScheduleParser -fuzztime=10s ./pkg/parser/ | |
| go test -run='^$' -fuzz=FuzzExpressionParser -fuzztime=10s ./pkg/workflow/ | |
| go test -run='^$' -fuzz=FuzzMentionsFiltering -fuzztime=10s ./pkg/workflow/ | |
| go test -run='^$' -fuzz=FuzzSanitizeOutput -fuzztime=10s ./pkg/workflow/ | |
| go test -run='^$' -fuzz=FuzzSanitizeIncomingText -fuzztime=10s ./pkg/workflow/ | |
| go test -run='^$' -fuzz=FuzzSanitizeLabelContent -fuzztime=10s ./pkg/workflow/ | |
| go test -run='^$' -fuzz=FuzzWrapExpressionsInTemplateConditionals -fuzztime=10s ./pkg/workflow/ | |
| go test -run='^$' -fuzz=FuzzYAMLParsing -fuzztime=10s ./pkg/workflow/ | |
| go test -run='^$' -fuzz=FuzzTemplateRendering -fuzztime=10s ./pkg/workflow/ | |
| go test -run='^$' -fuzz=FuzzInputValidation -fuzztime=10s ./pkg/workflow/ | |
| go test -run='^$' -fuzz=FuzzNetworkPermissions -fuzztime=10s ./pkg/workflow/ | |
| go test -run='^$' -fuzz=FuzzSafeJobConfig -fuzztime=10s ./pkg/workflow/ | |
| security: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }}-security | |
| cancel-in-progress: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - name: Set up Go | |
| id: setup-go | |
| uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Report Go cache status | |
| run: | | |
| if [ "${{ steps.setup-go.outputs.cache-hit }}" == "true" ]; then | |
| echo "✅ Go cache hit" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Go cache miss" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| - name: Verify dependencies | |
| run: go mod verify | |
| - name: Run security regression tests | |
| run: make test-security | |
| security-scan: | |
| # Only run security scans on main branch to reduce PR overhead | |
| if: github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 # Prevent jobs from hanging indefinitely | |
| permissions: | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| tool: | |
| - name: zizmor | |
| flag: --zizmor | |
| - name: actionlint | |
| flag: --actionlint | |
| - name: poutine | |
| flag: --poutine | |
| concurrency: | |
| group: ci-${{ github.ref }}-security-scan-${{ matrix.tool.name }} | |
| cancel-in-progress: true | |
| name: "Security Scan: ${{ matrix.tool.name }}" | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - name: Set up Go | |
| id: setup-go | |
| uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Report Go cache status | |
| run: | | |
| if [ "${{ steps.setup-go.outputs.cache-hit }}" == "true" ]; then | |
| echo "✅ Go cache hit" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Go cache miss" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| - name: Build gh-aw | |
| run: make build | |
| - name: Run ${{ matrix.tool.name }} security scan on poem workflow | |
| run: ./gh-aw compile poem-bot ${{ matrix.tool.flag }} --verbose | |
| logs-token-check: | |
| if: false | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| actions: read | |
| concurrency: | |
| group: ci-${{ github.ref }}-logs-token-check | |
| cancel-in-progress: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - name: Set up Go | |
| id: setup-go | |
| uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Build gh-aw | |
| run: make build | |
| - name: Run logs command with JSON output | |
| id: logs_check | |
| run: | | |
| set -e # Fail on first error | |
| # Run the logs command and capture only stdout (JSON output) | |
| # stderr is not redirected, so warning messages go to console | |
| ./gh-aw logs smoke-copilot -c 2 --json --verbose > logs_output.json | |
| # Display the output for debugging | |
| echo "Logs command output:" | |
| cat logs_output.json | |
| # Check if the JSON structure is valid | |
| echo "## Validating JSON Structure" | |
| # Check if token count is found in the JSON output | |
| if jq -e '.summary.total_tokens' logs_output.json > /dev/null 2>&1; then | |
| TOKEN_COUNT=$(jq '.summary.total_tokens' logs_output.json) | |
| echo "✅ Token count found: $TOKEN_COUNT" | |
| # Validate that token count is greater than 0 | |
| if [ "$TOKEN_COUNT" -gt 0 ]; then | |
| echo "✅ Token count is greater than 0: $TOKEN_COUNT" | |
| echo "token_count=$TOKEN_COUNT" >> $GITHUB_OUTPUT | |
| else | |
| echo "❌ Token count is 0 - expected tokens to be parsed from logs" | |
| exit 1 | |
| fi | |
| else | |
| echo "❌ Token count not found in JSON output" | |
| exit 1 | |
| fi | |
| # Check if runs array exists (even if empty) | |
| if jq -e '.runs' logs_output.json > /dev/null 2>&1; then | |
| RUNS_COUNT=$(jq '.runs | length' logs_output.json) | |
| echo "✅ Runs array found: $RUNS_COUNT runs" | |
| else | |
| echo "❌ Runs array not found in JSON output" | |
| exit 1 | |
| fi | |
| # If there are runs, validate that key fields are resolved | |
| if [ "$RUNS_COUNT" -gt 0 ]; then | |
| # Check if agent (engine_id) field exists in first run | |
| if jq -e '.runs[0] | has("agent")' logs_output.json > /dev/null 2>&1; then | |
| AGENT=$(jq -r '.runs[0].agent // "null"' logs_output.json) | |
| echo "✅ Agent field found in run: $AGENT" | |
| else | |
| echo "❌ Agent field not found in run data" | |
| exit 1 | |
| fi | |
| # Check if workflow_path field exists in first run | |
| if jq -e '.runs[0] | has("workflow_path")' logs_output.json > /dev/null 2>&1; then | |
| WORKFLOW_PATH=$(jq -r '.runs[0].workflow_path // "null"' logs_output.json) | |
| echo "✅ Workflow path field found in run: $WORKFLOW_PATH" | |
| else | |
| echo "❌ Workflow path field not found in run data" | |
| exit 1 | |
| fi | |
| # Check if workflow_name is present | |
| if jq -e '.runs[0].workflow_name' logs_output.json > /dev/null 2>&1; then | |
| WORKFLOW_NAME=$(jq -r '.runs[0].workflow_name' logs_output.json) | |
| echo "✅ Workflow name found in run: $WORKFLOW_NAME" | |
| else | |
| echo "❌ Workflow name not found in run data" | |
| exit 1 | |
| fi | |
| else | |
| echo "ℹ️ No runs found to validate (this is ok)" | |
| fi | |
| echo "✅ All JSON structure validations passed" | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| mcp-server-compile-test: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }}-mcp-server-compile-test | |
| cancel-in-progress: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 | |
| - name: Set up Go | |
| id: setup-go | |
| uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Report Go cache status | |
| run: | | |
| if [ "${{ steps.setup-go.outputs.cache-hit }}" == "true" ]; then | |
| echo "✅ Go cache hit" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Go cache miss" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| - name: Build gh-aw binary | |
| run: make build | |
| - name: Create test workflow with error | |
| run: | | |
| mkdir -p .github/workflows | |
| cat > .github/workflows/test-invalid.md << 'EOF' | |
| --- | |
| on: push | |
| engine: copilot | |
| invalid_field: this will cause an error | |
| --- | |
| # Test Invalid Workflow | |
| This workflow has an invalid field that will cause a compilation error. | |
| EOF | |
| - name: Test MCP server compile tool | |
| run: | | |
| # Create a test script using the MCP Go SDK | |
| cat > test_mcp_compile.go << 'GOEOF' | |
| package main | |
| import ( | |
| "context" | |
| "encoding/json" | |
| "fmt" | |
| "os" | |
| "os/exec" | |
| "time" | |
| "github.com/modelcontextprotocol/go-sdk/mcp" | |
| ) | |
| func main() { | |
| // Create MCP client | |
| client := mcp.NewClient(&mcp.Implementation{ | |
| Name: "ci-test-client", | |
| Version: "1.0.0", | |
| }, nil) | |
| // Start the MCP server as a subprocess with absolute path | |
| binaryPath := "./gh-aw" | |
| serverCmd := exec.Command(binaryPath, "mcp-server", "--cmd", binaryPath) | |
| transport := &mcp.CommandTransport{Command: serverCmd} | |
| ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) | |
| defer cancel() | |
| // Connect to the server | |
| session, err := client.Connect(ctx, transport, nil) | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "Failed to connect to MCP server: %v\n", err) | |
| os.Exit(1) | |
| } | |
| defer session.Close() | |
| fmt.Println("✅ Successfully connected to MCP server") | |
| // Call the compile tool with the invalid workflow | |
| params := &mcp.CallToolParams{ | |
| Name: "compile", | |
| Arguments: map[string]any{ | |
| "workflows": []string{"test-invalid.md"}, | |
| }, | |
| } | |
| result, err := session.CallTool(ctx, params) | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "MCP tool call returned error (this is expected): %v\n", err) | |
| // Check if the error contains expected error information | |
| fmt.Println("✅ Compile tool correctly returned an error") | |
| os.Exit(0) | |
| } | |
| // Get the result content | |
| if len(result.Content) == 0 { | |
| fmt.Fprintln(os.Stderr, "❌ Expected non-empty result from compile tool") | |
| os.Exit(1) | |
| } | |
| textContent, ok := result.Content[0].(*mcp.TextContent) | |
| if !ok { | |
| fmt.Fprintln(os.Stderr, "❌ Expected text content from compile tool") | |
| os.Exit(1) | |
| } | |
| fmt.Printf("Compile tool output:\n%s\n", textContent.Text) | |
| // Parse the JSON output to check for errors | |
| var compileResults []map[string]any | |
| if err := json.Unmarshal([]byte(textContent.Text), &compileResults); err != nil { | |
| fmt.Fprintf(os.Stderr, "Failed to parse JSON output: %v\n", err) | |
| os.Exit(1) | |
| } | |
| // Check if the workflow is marked as invalid | |
| if len(compileResults) == 0 { | |
| fmt.Fprintln(os.Stderr, "❌ Expected at least one workflow result") | |
| os.Exit(1) | |
| } | |
| result0 := compileResults[0] | |
| valid, ok := result0["valid"].(bool) | |
| if !ok { | |
| fmt.Fprintln(os.Stderr, "❌ Expected 'valid' field in result") | |
| os.Exit(1) | |
| } | |
| if valid { | |
| fmt.Fprintln(os.Stderr, "❌ Expected workflow to be invalid") | |
| os.Exit(1) | |
| } | |
| // Check that errors field exists and has at least one error | |
| errors, ok := result0["errors"].([]any) | |
| if !ok || len(errors) == 0 { | |
| fmt.Fprintln(os.Stderr, "❌ Expected errors array with at least one error") | |
| os.Exit(1) | |
| } | |
| fmt.Println("✅ Compile tool correctly reported validation errors:") | |
| errorsJSON, _ := json.MarshalIndent(errors, " ", " ") | |
| fmt.Printf(" %s\n", string(errorsJSON)) | |
| os.Exit(0) | |
| } | |
| GOEOF | |
| # Run the test | |
| go run test_mcp_compile.go | |
| - name: Report test results | |
| if: always() | |
| run: | | |
| echo "## MCP Server Compile Tool Test" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "This test verifies that:" >> $GITHUB_STEP_SUMMARY | |
| echo "1. The gh-aw MCP server can be started successfully" >> $GITHUB_STEP_SUMMARY | |
| echo "2. The compile tool can be invoked through the MCP server" >> $GITHUB_STEP_SUMMARY | |
| echo "3. The compile tool correctly detects and reports validation errors" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ Test completed successfully" >> $GITHUB_STEP_SUMMARY |