generateSamplesReplayStep emits the YAML that replaces the agentic `Execute coding agent` step when the hidden `gh aw compile --use-samples` flag is used. It spawns the safe-outputs MCP server over stdio and feeds it a `tools/call` for every collected sample, after pre-staging branches/patches for s
(yaml *strings.Builder, data *WorkflowData, logFile string)
| 133 | // a `tools/call` for every collected sample, after pre-staging branches/patches |
| 134 | // for samples that carry them. |
| 135 | func (c *Compiler) generateSamplesReplayStep(yaml *strings.Builder, data *WorkflowData, logFile string) { |
| 136 | entries := collectSampleEntries(data.SafeOutputs) |
| 137 | compilerYamlLog.Printf("Generating samples replay step: entries=%d", len(entries)) |
| 138 | |
| 139 | // Normalize a nil slice to an empty slice so json.Marshal emits "[]" not "null". |
| 140 | // The driver rejects anything that isn't a JSON array; emitting "null" here |
| 141 | // would crash the replay step with `GH_AW_SAMPLES must be a JSON array` for |
| 142 | // workflows that opt into --use-samples but configure no samples (or whose |
| 143 | // configured samples all live on disabled handlers). |
| 144 | if entries == nil { |
| 145 | entries = []SampleEntry{} |
| 146 | } |
| 147 | |
| 148 | // Serialize entries to JSON for the driver. Always emit valid JSON even when |
| 149 | // empty so the driver can produce a clear `no samples configured` message |
| 150 | // rather than crashing on an empty env var. |
| 151 | payload, err := json.Marshal(entries) |
| 152 | if err != nil { |
| 153 | // Should never happen for map[string]any payloads; fall back to empty |
| 154 | // array so the workflow still compiles and the driver reports cleanly. |
| 155 | compilerYamlLog.Printf("Warning: failed to marshal samples entries: %v", err) |
| 156 | payload = []byte("[]") |
| 157 | } |
| 158 | |
| 159 | // Build the per-repo token map so apply_samples.cjs can reach repos that |
| 160 | // require a non-default token (cross-repo `checkout:` entries with their |
| 161 | // own `github-token:` or `github-app:`). |
| 162 | repoTokens := collectSampleRepoTokens(data.CheckoutConfigs) |
| 163 | repoTokensPayload := marshalRepoTokens(repoTokens) |
| 164 | |
| 165 | yaml.WriteString(" - name: Replay safe-outputs samples (deterministic)\n") |
| 166 | yaml.WriteString(" id: agentic_execution\n") |
| 167 | yaml.WriteString(" env:\n") |
| 168 | yaml.WriteString(" GH_AW_SAMPLES: |\n") |
| 169 | for line := range strings.SplitSeq(string(payload), "\n") { |
| 170 | fmt.Fprintf(yaml, " %s\n", line) |
| 171 | } |
| 172 | fmt.Fprintf(yaml, " GH_AW_AGENT_STDIO_LOG: %s\n", logFile) |
| 173 | yaml.WriteString(" GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json\n") |
| 174 | yaml.WriteString(" GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl\n") |
| 175 | // GITHUB_TOKEN is the fallback used by apply_samples.cjs when resolving a |
| 176 | // pull-request head ref via the REST API for issue_comment / slash_command |
| 177 | // events. For cross-repo samples whose target repository has its own |
| 178 | // `checkout:` entry with `github-token:` or `github-app:`, the driver |
| 179 | // prefers the matching token from GH_AW_REPO_TOKENS below. |
| 180 | yaml.WriteString(" GITHUB_TOKEN: ${{ github.token }}\n") |
| 181 | if repoTokensPayload != nil { |
| 182 | yaml.WriteString(" GH_AW_REPO_TOKENS: |\n") |
| 183 | for line := range strings.SplitSeq(string(repoTokensPayload), "\n") { |
| 184 | fmt.Fprintf(yaml, " %s\n", line) |
| 185 | } |
| 186 | } |
| 187 | yaml.WriteString(" run: |\n") |
| 188 | yaml.WriteString(" set -euo pipefail\n") |
| 189 | yaml.WriteString(" mkdir -p \"$(dirname \"$GH_AW_AGENT_STDIO_LOG\")\"\n") |
| 190 | yaml.WriteString(" node \"${RUNNER_TEMP}/gh-aw/actions/apply_samples.cjs\"\n") |
| 191 | } |
no test coverage detected