Recipe: embed deploy metadata in your own artifacts#
Pattern: when your eval pipeline writes its own manifest.json or
results.json, embed the local git SHA + lockfile hash via
runpod-deploy capture-env. This replaces ad-hoc
GIT_SHA=$(git rev-parse HEAD) injection in Makefile targets.
Why this is a recipe, not a schema feature#
runpod-deploy run already embeds deploy_metadata in
runpod_deploy_pull_manifest.json for any run it manages — that’s the
deployment-primitive part. What’s consumer-domain is your own
artifact manifests: which fields you want, what your evaluation
pipeline writes, how you join the two. runpod-deploy exposes the
capture as a standalone CLI (capture-env) so your scripts can pull
the same metadata into wherever they want it, without baking your
manifest schema into ours.
Use this recipe when:
You produce additional artifacts that should be pinned to the same git SHA / lockfile hash but live in your project’s manifest, not runpod-deploy’s.
You run consumer-domain pipelines (audits, plot rendering) outside
runpod-deploy runand want them traceable to a state.
Pattern (Makefile)#
.PHONY: headline-cloud
headline-cloud:
runpod-deploy capture-env --project-root . > /tmp/runpod_env.json
runpod-deploy run \
--config configs/runpod/headline.yaml
# Merge runpod-deploy's capture into your own evals manifest:
uv run python scripts/merge_env_into_evals.py \
--env-file /tmp/runpod_env.json \
--evals-manifest evals/headline/manifest.json
Pattern (Python)#
import json
import subprocess
env = json.loads(
subprocess.run(
["runpod-deploy", "capture-env", "--project-root", "."],
check=True,
capture_output=True,
text=True,
).stdout
)
results_manifest = {
"git_sha": env["local_git_sha"],
"git_dirty": env["local_git_dirty"],
"uv_lock_sha256": env["payload_lockfile_sha256"],
# ... your own fields
}
What capture-env emits#
{
"local_git_sha": "abc123...",
"local_git_dirty": false,
"local_git_branch": "main",
"payload_lockfile": "uv.lock",
"payload_lockfile_sha256": "def456..."
}
Each field is null (with a WARNING) if the source isn’t available
(no git repo, no uv.lock or requirements.txt). The command never
raises.
What lives where#
Concern |
Owner |
|---|---|
Capturing git SHA, dirty flag, branch |
|
Capturing lockfile hash + filename |
|
Embedding metadata in the orchestrator-managed pull manifest |
|
Embedding metadata in your own evals/results manifest |
Your script (call |
Defining what fields your manifest schema requires |
Your project |
Anti-pattern to avoid#
Do not hand-roll git rev-parse HEAD injection in Makefiles when
capture-env exists. The captured fields are a superset of what
most consumers reach for (SHA + dirty flag + branch + lockfile hash +
lockfile path), they handle the “no git repo” and “no lockfile” cases
gracefully, and they emit the same fields that land in
runpod_deploy_pull_manifest.json automatically — so your manifest
stays consistent with runpod-deploy’s.
Do not pin your evals manifest schema to runpod-deploy’s manifest schema. They’re different concerns: ours is deploy-state, yours is eval-state. Copy the fields you want; don’t subclass or re-export.
See also#
cost-reconciliation.md— the same metadata fields are captured into every run’srunpod_deploy_pull_manifest.jsonautomatically; usecapture-envwhen you need them in your own manifest (separate from runpod-deploy’s).local-postprocess-after-run.md— consumer post-processing scripts often join thecapture-envJSON with the pulled artifacts to build a reproducibility-grade output.reproducibility.md— when paired withpod.python_version, the manifest records the third leg of the reproducibility tripod (git SHA + lockfile hash + interpreter pin).