AGENTS/plans/2_19/workflow-artifacts.md
Status: implemented (initial cut, runner-local execution).
Until now Semaphore could not pass arbitrary key/value pairs from one task
template to another inside the same workflow. The only value that ever
flowed across nodes was the version of an upstream Build template (exposed
to a Deploy template as incoming_version / SEMAPHORE_TASK_INCOMING_VERSION).
AWX solves this with set_stats and "workflow artifacts": a producer task
calls ansible.builtin.set_stats: data: {...} and the AWX controller merges
the resulting dictionary into the extra_vars of every downstream node in
the same workflow run. Semaphore now does the same.
SEMAPHORE_ARTIFACTS_FILE pointing at a per-task JSON file path.ansible.builtin.set_stats:. Semaphore registers
a built-in aggregate callback plugin (semaphore_artifacts) that
captures the play-level stats and writes the file for you.echo '{"deployed_version":"1.2.3"}' > "$SEMAPHORE_ARTIFACTS_FILE".^[A-Za-z_][A-Za-z0-9_]*$, max 256 KB),
and persists it on the task row (task.artifacts).WorkflowRun starts, Semaphore merges
artifacts from every finished upstream task and:
extra_vars (so they are
available as plain Jinja vars, just like in AWX),semaphore_workflow_artifacts for callers that prefer explicitness,SEMAPHORE_WF_<UPPER_KEY> for shell/Terraform
templates.Later tasks override earlier ones, mirroring AWX's merge semantics.
- hosts: all
tasks:
- name: Capture deployed version for downstream templates
ansible.builtin.set_stats:
data:
deployed_version: "1.2.3"
ready: true
- hosts: all
tasks:
- name: Use upstream value
debug:
msg: "Promoting {{ deployed_version }}"
cat > "$SEMAPHORE_ARTIFACTS_FILE" <<'JSON'
{ "deployed_version": "1.2.3" }
JSON
echo "Promoting ${SEMAPHORE_WF_DEPLOYED_VERSION}"
The following keys cannot be used as artifact names; they will be rejected at validation time to prevent collisions with Semaphore-injected variables:
semaphore_varssemaphore_workflow_artifactstask_detailsincoming_version^[A-Za-z_][A-Za-z0-9_]*$.task.artifacts is now part of every task JSON response.GET /api/project/{project_id}/workflows/{workflow_id}/runs/{run_id}/artifacts
returns the merged artifact map for a run (useful for UIs and debugging).useRemoteRunner: true) do not yet stream artifacts back
to the server; this requires extending the runner→server protocol.terraform output -json as a sub-key.