Vetrix Docs

CI artifact collection (RUNNER_WORKSPACE_DIR + workspace mount)

Audience: operators deploying the containerized Vetrix CI worker (appmerc_vetrixworker / vetrix-worker image, compose --target worker) who expect pipeline artifacts — declared as artifacts.paths in vetrix-ci.yml (e.g. coverage-summary.json) — to be collected and retrievable after a job runs.

If a job's artifacts.paths produce zero stored artifacts (the pipeline Coverage Report shows "No coverage data" even though the coverage job succeeds), the runner has nowhere on the host to collect the files from. This doc is the remediation. When a job declares artifacts.paths but no host-side workspace is available, the worker emits a WARN (see "Diagnosing" below) so the failure mode is observable instead of a silent no-op.


Why this matters

The worker runs each pipeline job in a Docker container, then — on job success — copies the files matching the job's declared artifacts.paths and records each as a retrievable artifact (GET /api/v1/repos/<owner>/<repo>/pipelines/<id>/artifacts/<name>).

Collection reads from the host side of a per-job workspace, not from inside the job container. So two things must be true on the deployed runner:

  1. RUNNER_WORKSPACE_DIR is set — a host directory under which the worker provisions one per-job workspace dir and bind-mounts it into the job container at /workspace (the job's working directory and CI_PROJECT_DIR). When this is empty, no workspace is mounted (legacy / dev mode) and there is no host path for the runner to collect artifacts from — collection is skipped.
  2. That host directory is a real, persistent, writable mount — for the containerized worker it must be bind-mounted from the host into the worker container, owned by the worker's runtime identity (see "Worker compose" below).

A job writes its artifacts (e.g. coverage-summary.json) into /workspace; because /workspace is the container side of the host per-job dir, those files land on the host, where the runner globs them after the job exits.

Files written outside /workspace are not collected. artifacts.paths entries are file globs relative to the workspace root (/workspace). A glob that resolves to a directory is skipped — only regular files are uploaded. Make the job write its artifacts into the working directory.


Diagnosing: the missing-workspace WARN

The worker emits a loud WARN in its error.log whenever a job declares artifacts.paths but no host-side workspace is available to collect them from. A misconfigured deployment that lacks this workspace records zero artifacts — the cause of an empty pipeline Coverage Report in production.

WARN in worker error.log (RUNNER_LOG_DIR=/var/log/vetrix-worker) Meaning Fix
artifact collection: job declared artifact paths but no workspace locator is wired; nothing collected — check the runner deployment (RUNNER_WORKSPACE_DIR + workspace mount) No workspace locator is wired (typically RUNNER_WORKSPACE_DIR unset). Set RUNNER_WORKSPACE_DIR and mount it (below); redeploy.
artifact collection: job declared artifact paths but no host-side workspace is available; nothing collected — check the runner deployment (RUNNER_WORKSPACE_DIR + workspace mount) A locator is wired but it reports no bound workspace for this job. Confirm the host workspace dir exists, is mounted into the worker, and is writable by the worker identity.
artifact collection: no files matched declared path Workspace is wired, but the glob matched nothing — the job wrote the file elsewhere, or did not produce it. Have the job write the artifact into the working directory (/workspace); confirm the path/glob.

The first two WARNs point at this doc as the remediation. The third means the workspace is wired correctly and the problem is in the job, not the deployment.

A job that declares no artifacts.paths does not warn — the well-known coverage-summary auto-collect is best-effort by convention, not a misconfiguration. But it still needs a workspace: with no RUNNER_WORKSPACE_DIR there is nothing to auto-collect either.


Wiring it on the worker

Environment

# worker service (e.g. appmerc_vetrixworker) in the deployment compose
environment:
  - RUNNER_WORKSPACE_DIR=/var/lib/vetrix/runner-ws   # host dir for per-job workspaces

When set, the worker logs worker: workspace root "…" and worker: pipeline artifact collection enabled at startup. When unset it logs worker: RUNNER_WORKSPACE_DIR not set; jobs will run without a workspace mount — that line in your boot log is the signature of this misconfiguration.

Optionally set RUNNER_REPOS_ROOT alongside it to have the executor populate each job's workspace with the repo source at the build ref via git archive. It is not required for artifact collection — RUNNER_WORKSPACE_DIR alone is enough for a job to write artifacts into /workspace and have them collected. RUNNER_COVERAGE_SUMMARY_FILE (default coverage-summary.json) selects the well-known file auto-collected from the workspace root.

Worker compose (containerized worker)

The directory in RUNNER_WORKSPACE_DIR is a path inside the worker container, so it must be backed by a host bind mount owned by the worker's runtime identity (${DOCKERUSERID}:${DOCKERGROUPID} — the same user: pin discussed in worker-docker-socket.md):

appmerc_vetrixworker:
  user: "${DOCKERUSERID}:${DOCKERGROUPID}"            # REQUIRED — must own the mount
  environment:
    - RUNNER_WORKSPACE_DIR=/var/lib/vetrix/runner-ws
  volumes:
    - ./site/vetrix/data/runner-ws:/var/lib/vetrix/runner-ws

The host dir (./site/vetrix/data/runner-ws) must exist and be writable by ${DOCKERUSERID}:${DOCKERGROUPID}. If the worker cannot write it the worker fatals at startup and never polls — see the user: discussion in worker-docker-socket.md.


Verify

# 1. Worker booted with a workspace root and collection enabled:
docker compose logs appmerc_vetrixworker | grep -E "workspace root|artifact collection enabled"
#    Absence (or "RUNNER_WORKSPACE_DIR not set …") = not wired.

# 2. No collection WARNs after a run that declares artifacts.paths:
docker compose exec appmerc_vetrixworker grep "artifact collection" /var/log/vetrix-worker/error.log

# 3. End-to-end: trigger a pipeline whose coverage job writes coverage-summary.json,
#    then confirm the artifact is retrievable and the Coverage Report panel populates:
curl -s "https://<vetrix-host>/api/v1/repos/<owner>/<repo>/pipelines/<id>/artifacts" \
  -H "Authorization: Bearer <token>"
#    Expect a non-empty array including "coverage-summary.json" (not []).

Worker predating artifact collection

If the deployed worker image predates artifact collection + upload, wiring RUNNER_WORKSPACE_DIR alone is not enough — the collection code is not in the image. Redeploy a current worker image and set RUNNER_WORKSPACE_DIR + the mount above; the redeploy is the remediation, the env/mount is the prerequisite.


See also: runners.md (runner deployment), worker-docker-socket.md (worker Docker-socket access and the load-bearing user: pin), and pipeline-variables.md (per-branch CI/CD variables).