diff --git a/.github/aw/github-agentic-workflows.md b/.github/aw/github-agentic-workflows.md index 43a40bdea24..fb6749f4370 100644 --- a/.github/aw/github-agentic-workflows.md +++ b/.github/aw/github-agentic-workflows.md @@ -211,7 +211,7 @@ The YAML frontmatter supports these fields: - **`name:`** - Workflow name (string) - **`pre-steps:`** - Custom workflow steps to run at the very beginning of the agent job, before checkout (object). Use for token minting or setup that must happen before the repository is checked out. Step outputs are available via `${{ steps..outputs. }}` and can be referenced in `checkout.github-token` to avoid masked-value cross-job boundary issues. Same security restrictions apply as for `steps:`. - **`steps:`** - Custom workflow steps before AI execution (object). **Security Notice**: Custom steps run OUTSIDE the firewall sandbox with standard GitHub Actions security but NO network egress controls. Use only for deterministic data preparation, not agentic compute. **Secrets restriction**: Using `${{ secrets.* }}` expressions (other than `secrets.GITHUB_TOKEN`) in custom steps is an error in strict mode and a warning otherwise — move secret-dependent operations to a separate job outside the agent job. -- **`pre-agent-steps:`** - Custom workflow steps to run immediately before AI execution, after all initialization and setup steps (runtimes, MCP servers, tools) have been configured (object or array). Use when preparation requires the full environment to be ready. Same security restrictions apply as for `steps:`. +- **`pre-agent-steps:`** - Custom workflow steps to run before MCP gateway startup (object or array). Use when preparation must install or configure MCP dependencies before the gateway starts. Same security restrictions apply as for `steps:`. - **`post-steps:`** - Custom workflow steps after AI execution (object). **Security Notice**: Post-execution steps run OUTSIDE the firewall sandbox. Use only for deterministic cleanup, artifact uploads, or notifications—not agentic compute or untrusted AI execution. Same secrets restriction applies as for `steps:`. - **`environment:`** - Environment that the job references for protection rules (string or object) - **`container:`** - Container to run job steps in (string or object) diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 431f9acfe05..ee6a49faf27 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -931,6 +931,19 @@ jobs: script: | const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); + - name: Download APM bundle artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ needs.activation.outputs.artifact_prefix }}apm + path: /tmp/gh-aw/apm-bundle + - id: apm_bundle + name: Find APM bundle path + run: echo "path=$(find /tmp/gh-aw/apm-bundle -name '*.tar.gz' | head -1)" >> "$GITHUB_OUTPUT" + - name: Restore APM packages + uses: microsoft/apm-action@a190b0b1a91031057144dc136acf9757a59c9e4d # v1.4.1 + with: + bundle: ${{ steps.apm_bundle.outputs.path }} + - name: Download container images run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.28@sha256:a8834e285807654bf680154faa710d43fe4365a0868142f5c20e48c85e137a7a ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28@sha256:93290f2393752252911bd7c39a047f776c0b53063575e7bde4e304962a9a61cb ghcr.io/github/gh-aw-firewall/squid:0.25.28@sha256:844c18280f82cd1b06345eb2f4e91966b34185bfc51c9f237c3e022e848fb474 ghcr.io/github/gh-aw-mcpg:v0.2.30@sha256:e950e6d39f003862d33bfb8d4eb93e242d919cf6ca874b90728e5e0ea7434c6f ghcr.io/github/github-mcp-server:v1.0.0@sha256:d2550953f8050bc5a1c8f80d1678766f66f60bbfbcd953fdeaf661fe4269bd95 ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5 mcr.microsoft.com/playwright/mcp@sha256:7b82f29c6ef83480a97f612d53ac3fd5f30a32df3fea1e06923d4204d3532bb2 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f - name: Install gh-aw extension @@ -2160,19 +2173,6 @@ jobs: - name: Clean git credentials continue-on-error: true run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh" - - name: Download APM bundle artifact - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: ${{ needs.activation.outputs.artifact_prefix }}apm - path: /tmp/gh-aw/apm-bundle - - id: apm_bundle - name: Find APM bundle path - run: echo "path=$(find /tmp/gh-aw/apm-bundle -name '*.tar.gz' | head -1)" >> "$GITHUB_OUTPUT" - - name: Restore APM packages - uses: microsoft/apm-action@a190b0b1a91031057144dc136acf9757a59c9e4d # v1.4.1 - with: - bundle: ${{ steps.apm_bundle.outputs.path }} - - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/docs/src/content/docs/reference/frontmatter-full.md b/docs/src/content/docs/reference/frontmatter-full.md index d8de552dcd8..c11bd3d2578 100644 --- a/docs/src/content/docs/reference/frontmatter-full.md +++ b/docs/src/content/docs/reference/frontmatter-full.md @@ -1469,8 +1469,8 @@ pre-steps: pre-steps: [] # Array items: undefined -# Custom workflow steps to run immediately before AI execution, after all -# initialization and setup steps in the agent job. +# Custom workflow steps to run before MCP gateway startup in the agent job, +# so prerequisite MCP installation/configuration can happen first. # (optional) # This field supports multiple formats (oneOf): diff --git a/docs/src/content/docs/reference/frontmatter.md b/docs/src/content/docs/reference/frontmatter.md index c5f94313058..1c2d823e9b6 100644 --- a/docs/src/content/docs/reference/frontmatter.md +++ b/docs/src/content/docs/reference/frontmatter.md @@ -688,7 +688,7 @@ Custom steps run outside the firewall sandbox. These steps execute with standard ## Pre-Agent Steps (`pre-agent-steps:`) -Add custom steps immediately before the agent execution step, after all initialization/setup logic in the agent job. +Add custom steps before MCP gateway startup in the agent job so prerequisite MCP installation/configuration can happen first. ```yaml wrap pre-agent-steps: diff --git a/pkg/workflow/compiler_pre_agent_steps_test.go b/pkg/workflow/compiler_pre_agent_steps_test.go index b8bb38789bf..ebdc5d00c17 100644 --- a/pkg/workflow/compiler_pre_agent_steps_test.go +++ b/pkg/workflow/compiler_pre_agent_steps_test.go @@ -49,14 +49,14 @@ Test pre-agent-steps. t.Error("Expected pre-agent-step to be in generated workflow") } - cleanGitCredsIndex := indexInNonCommentLines(lockContent, "- name: Clean git credentials") + startMCPGatewayIndex := indexInNonCommentLines(lockContent, "- name: Start MCP Gateway") preAgentStepIndex := indexInNonCommentLines(lockContent, "- name: Finalize prompt context") aiStepIndex := indexInNonCommentLines(lockContent, "- name: Execute Claude Code CLI") - if cleanGitCredsIndex == -1 || preAgentStepIndex == -1 || aiStepIndex == -1 { + if startMCPGatewayIndex == -1 || preAgentStepIndex == -1 || aiStepIndex == -1 { t.Fatal("Could not find expected steps in generated workflow") } - if preAgentStepIndex <= cleanGitCredsIndex { - t.Errorf("Pre-agent-step (%d) should appear after clean git credentials (%d)", preAgentStepIndex, cleanGitCredsIndex) + if preAgentStepIndex >= startMCPGatewayIndex { + t.Errorf("Pre-agent-step (%d) should appear before Start MCP Gateway (%d)", preAgentStepIndex, startMCPGatewayIndex) } if preAgentStepIndex >= aiStepIndex { t.Errorf("Pre-agent-step (%d) should appear before AI execution step (%d)", preAgentStepIndex, aiStepIndex) @@ -113,13 +113,17 @@ Main workflow. importedIdx := indexInNonCommentLines(lockContent, "- name: Imported pre-agent step") mainIdx := indexInNonCommentLines(lockContent, "- name: Main pre-agent step") + startMCPGatewayIdx := indexInNonCommentLines(lockContent, "- name: Start MCP Gateway") aiStepIdx := indexInNonCommentLines(lockContent, "- name: Execute Claude Code CLI") - if importedIdx == -1 || mainIdx == -1 || aiStepIdx == -1 { - t.Fatal("Could not find expected pre-agent and AI steps in generated workflow") + if importedIdx == -1 || mainIdx == -1 || startMCPGatewayIdx == -1 || aiStepIdx == -1 { + t.Fatal("Could not find expected pre-agent, MCP gateway, and AI steps in generated workflow") } if importedIdx >= mainIdx { t.Errorf("Imported pre-agent-step (%d) should appear before main pre-agent-step (%d)", importedIdx, mainIdx) } + if mainIdx >= startMCPGatewayIdx { + t.Errorf("Main pre-agent-step (%d) should appear before Start MCP Gateway (%d)", mainIdx, startMCPGatewayIdx) + } if mainIdx >= aiStepIdx { t.Errorf("Main pre-agent-step (%d) should appear before AI execution step (%d)", mainIdx, aiStepIdx) } @@ -179,14 +183,14 @@ Main workflow. } lockContent := string(content) - restoreBaseIdx := indexInNonCommentLines(lockContent, "- name: Restore agent config folders from base branch") restoreAPMIdx := indexInNonCommentLines(lockContent, "- name: Restore APM packages") + startMCPGatewayIdx := indexInNonCommentLines(lockContent, "- name: Start MCP Gateway") aiStepIdx := indexInNonCommentLines(lockContent, "- name: Execute Claude Code CLI") - if restoreBaseIdx == -1 || restoreAPMIdx == -1 || aiStepIdx == -1 { - t.Fatal("Could not find expected PR restore, pre-agent, and AI steps in generated workflow") + if restoreAPMIdx == -1 || startMCPGatewayIdx == -1 || aiStepIdx == -1 { + t.Fatal("Could not find expected pre-agent, MCP gateway, and AI steps in generated workflow") } - if restoreBaseIdx >= restoreAPMIdx { - t.Errorf("PR base restore step (%d) should appear before imported pre-agent step (%d)", restoreBaseIdx, restoreAPMIdx) + if restoreAPMIdx >= startMCPGatewayIdx { + t.Errorf("Imported pre-agent step (%d) should appear before Start MCP Gateway (%d)", restoreAPMIdx, startMCPGatewayIdx) } if restoreAPMIdx >= aiStepIdx { t.Errorf("Imported pre-agent step (%d) should appear before AI execution step (%d)", restoreAPMIdx, aiStepIdx) diff --git a/pkg/workflow/compiler_yaml_main_job.go b/pkg/workflow/compiler_yaml_main_job.go index 5e2214f6283..200a6c76c1f 100644 --- a/pkg/workflow/compiler_yaml_main_job.go +++ b/pkg/workflow/compiler_yaml_main_job.go @@ -302,6 +302,10 @@ func (c *Compiler) generateMainJobSteps(yaml *strings.Builder, data *WorkflowDat // to avoid double-filtering: the gateway uses the same guard policy for the agent phase. c.generateStopDIFCProxyStep(yaml, data) + // Add pre-agent-steps (if any) before MCP setup so they can install/configure MCP dependencies + // that the gateway may reference when it starts. + c.generatePreAgentSteps(yaml, data) + // Add MCP setup if err := c.generateMCPSetup(yaml, data.Tools, engine, data); err != nil { return fmt.Errorf("failed to generate MCP setup: %w", err) @@ -384,9 +388,6 @@ func (c *Compiler) generateMainJobSteps(yaml *strings.Builder, data *WorkflowDat // connects to via host.docker.internal:18443. c.generateStartCliProxyStep(yaml, data) - // Add pre-agent-steps (if any) immediately before AI execution. - c.generatePreAgentSteps(yaml, data) - // Add AI execution step using the agentic engine compilerYamlLog.Printf("Generating engine execution steps for %s", engine.GetID()) c.generateEngineExecutionSteps(yaml, data, engine, logFileFull)