runner: send empty Task.needs so multi-stage jobs run (generateWorkflow injects a runs-on-less mock per need, which act skips on if: success())
ed9a7cd · gmorell · 2026-07-02 16:47
Files changed
modified
lib/git_gud_web/controllers/runner_controller.ex
+9
−23
@@ -226,31 +226,17 @@ defmodule GitGudWeb.RunnerController do
| 226 | 226 | end |
| 227 | 227 | end |
| 228 | 228 | |
| 229 | − # Built from the snapshot captured when the job was promoted to | |
| 230 | − # `queued` (Workflows.resolve_dependents/1), so the upstream results are | |
| 231 | − # the definitively-terminal values — never re-read at fetch time, which | |
| 232 | − # raced and could yield RESULT_UNSPECIFIED (act then skips the job on | |
| 233 | − # `if: success()`). Snapshot shape: | |
| 234 | − # %{"<need>" => %{"status" => "...", "outputs" => %{}}} | |
| 235 | − defp build_needs(%GitGud.Workflows.WorkflowJob{needs_snapshot: snap}) | |
| 236 | − when is_map(snap) and map_size(snap) > 0 do | |
| 237 | − Map.new(snap, fn {jid, data} -> | |
| 238 | − {jid, | |
| 239 | − %Runner.V1.TaskNeed{ | |
| 240 | − result: status_to_result_enum(Map.get(data, "status")), | |
| 241 | − outputs: Map.get(data, "outputs") || %{} | |
| 242 | − }} | |
| 243 | − end) | |
| 244 | − end | |
| 245 | − | |
| 229 | + # Always send an EMPTY needs map. forgejo-runner's generateWorkflow | |
| 230 | + # injects a MOCK job (with no `runs-on`) for every Task.needs entry and | |
| 231 | + # rebuilds the target job's RawNeeds from them; act then can't resolve | |
| 232 | + # the runs-on-less mock and skips the job via `if: success()`. With no | |
| 233 | + # entries, no mock is injected and RawNeeds is emptied, so `success()` | |
| 234 | + # passes and the job runs. Dispatch is already gated on upstream success | |
| 235 | + # by Workflows.resolve_dependents/1, so the dependency stays honoured. | |
| 236 | + # Trade-off: `${{ needs.<id>.result/outputs }}` isn't surfaced to the | |
| 237 | + # job — none of our workflows read cross-job needs outputs. | |
| 246 | 238 | defp build_needs(_job), do: %{} |
| 247 | 239 | |
| 248 | − defp status_to_result_enum("success"), do: :RESULT_SUCCESS | |
| 249 | − defp status_to_result_enum("failure"), do: :RESULT_FAILURE | |
| 250 | − defp status_to_result_enum("cancelled"), do: :RESULT_CANCELLED | |
| 251 | − defp status_to_result_enum("skipped"), do: :RESULT_SKIPPED | |
| 252 | − defp status_to_result_enum(_), do: :RESULT_UNSPECIFIED | |
| 253 | − | |
| 254 | 240 | # Forgejo's runner proto switched `Task.context` from |
| 255 | 241 | # `map<string,string>` to `google.protobuf.Struct` so values can carry |
| 256 | 242 | # nested objects, numbers, and bools. Our context is still all |
Parents: 3aa2fef