defmodule GitGud.Workflows.RunnerPayload do
@moduledoc """
Encodes a `WorkflowJob` into the `workflow_payload` bytes that
forgejo-runner expects.
Format on the wire: **plain YAML bytes** of the workflow document
scoped to the one job the runner is about to execute. v12.10.1's
`internal/app/run/runner.go` reads `task.WorkflowPayload` directly
as a UTF-8 string and feeds it to the act engine — no
decompression. Earlier versions of this module gzip'd; the runner
treated the gzip magic bytes as control characters in YAML and
rejected the task with "yaml: control characters are not allowed".
We snapshot the parsed YAML at run-creation time as
`WorkflowJob.definition` (the raw map). Here we shrink it back to a
single-job workflow and re-emit YAML via `:ymlr`.
"""
alias GitGud.Workflows.WorkflowJob
alias GitGud.Workflows.WorkflowRun
@doc "Return plain YAML bytes ready to hand to forgejo-runner."
def encode(%WorkflowJob{} = job) do
run = GitGud.Repo.get!(WorkflowRun, job.workflow_run_id)
# Drop `needs:` from the single-job payload. The referenced upstream
# jobs aren't included here, so act can't resolve them — it warns
# "'runs-on' key not defined in ci/<need>" and the implicit
# `if: success()` evaluates false, silently skipping the job. That
# broke every multi-stage (test -> image) workflow. The dependency is
# already honoured out-of-band: the forge only dispatches a job once
# its needs are satisfied (Workflows.resolve_dependents), and upstream
# results/outputs reach the job via the Task `needs` field.
definition = Map.drop(job.definition, ["needs", :needs])
workflow_doc = %{
"name" => run.workflow_name || run.workflow_path,
"on" => "push",
"jobs" => %{
job.job_id => definition
}
}
Ymlr.document!(workflow_doc)
end
end
1.8 KiB · text
5af55d9