The CI tier is whatever the workload actually needs. Build it from marketplace actions, version-pin every dependency, and treat releases as permanent.The policy on this page applies to every workflow under
JacobPEvans/* and dryvist/*. The
companion page git-signing covers identity and
signed-commit attribution. This page covers everything else.
Prefer native CI/CD constructs
Scripts belong in dedicated files (
.github/scripts/*.sh, scripts/*.py), not inlined into YAML. See No scripts in non-script files for the full rule.
GitHub Releases
Treat published releases as permanent. Once a release is promoted from draft to published, do not modify or delete it, ever. GitHub technically allows edits and deletions. The policy here forbids it. If a correction is needed, create a new release.- Open releases as drafts until fully complete (all assets uploaded, notes finalized).
- Promote draft to published only when everything is ready.
- Every repo uses release-please for automated version bumps:
- Patch bumps come from
fix:commits - Minor bumps come from
feat:commits - Major bumps are human-initiated only: edit
.release-please-manifest.jsonmanually. The release workflow blocks automated major bumps, including fromBREAKING CHANGE:footers
- Patch bumps come from
- Conventional-commit style preference:
fix:for config tweaks, small improvements, incremental adjustments, dependency updatesfeat:reserved for genuinely new capabilities, integrations, or significant behavioral changes
- Reusable workflows are homed in
dryvist/.github(the canonical org home). See Reusable workflow ownership for the few still being relocated
Dependency versioning
See Dependency automation for the full trust-tier, scheduling, automerge, and version-pinning policy, including which action sources ride@main and
which are SHA-pinned.
Scanner posture for self-references
@main on a self-owned workflow is deliberate. Consumers ride upstream fixes the moment they land,
with no per-repo Renovate PR. SemVer tags still exist, and release-please keeps bumping them for the
changelog and the per-run resolved-SHA audit trail, but callers ride @main. Every org scanner is
configured to allow it for dryvist/* action sources:
- Renovate.
pinDigests: falsefordryvist/**overrides the global digest-pinning, so@mainis never rewritten to a SHA. - zizmor.
unpinned-usespolicydryvist/*: ref-pin. - CodeQL. Code scanning default setup on public repos only (free; private repos excluded to
avoid the paid Code Security charge), managed as IaC in
tofu-github(per-repo, pending the provider resource). No committed query-filter config. Same-org@mainfalse positives (github/codeql#18316) are dismissed in the code scanning UI. - OSV-Scanner. Reports dependency vulnerabilities, not ref pinning, so
@mainis never flagged.
dryvist/.github
→ SECURITY.md.
Reusable workflow ownership
Reusable GitHub Actions workflows are referenced by a literalowner/repo in uses:. GitHub
allows no variables there, and it does not follow repository move or rename redirects for those
references. So when a shared-CI repository changes ownership, every consumer must update its
uses: reference at the same time.
dryvist is the canonical home for everything dryvist uses. JacobPEvans-personal/* may depend
on dryvist/*, never the reverse. Reference each workflow by its literal current owner below until
a pending relocation lands:
Runner choice
RunsOn (AWS EC2 spot viatofu-runs-on) is the default for
Linux jobs. The control plane carries a small fixed monthly cost regardless of job volume. Once it
is paid for, workflows that stay on ubuntu-latest spend GitHub Actions minutes that do not need to
be spent.
On-prem self-hosted is the documented exception for jobs that genuinely need local hardware. It is not a routine choice.
The leading
runs-on=${{ github.run_id }} segment is required so the RunsOn control plane can
correlate the GitHub Actions workflow_job webhook back to the originating run. Without it, the
job hangs in queued. The shared reusable workflows accept a runner_label input (default
ubuntu-latest). Callers opt in by passing the RunsOn label string.
Full RunsOn label catalog, prereqs (GitHub App allowlist), rollout playbook, and verification steps
live in
tofu-runs-on/docs/migration-guide.md.
On-prem runner requirements
On-prem self-hosted runners are a single point of failure when the job has E2E coverage and the runner is broken or stale. Every on-prem runner must:- Authenticate via a GitHub App (
APP_ID+APP_PRIVATE_KEY), never a personal access token. App installation tokens auto-refresh. PATs silently expire and break the runner with no upstream visibility. - Pin the runner image (or VM template) by digest, with Renovate tracking the digest. No floating tags.
- Expose a process-level healthcheck (Docker
healthcheck:, systemdWatchdogSec, or equivalent) so failed state surfaces in standard inspection tools. - Emit a periodic heartbeat to a dead-man’s-switch endpoint (healthchecks.io or equivalent) so silent death pages someone.
- Pre-flight check the secret-injection step so it fails loud when required secrets are missing.
- Build the runner image off-node. Build it on a dedicated build host and
docker loadit onto the runner host. Never build it in-place on a hypervisor node. A heavy image build can overload the node that also runs production guests.
orbstack-kubernetes/docker/actions-runner/. See that repo’s Makefile runner-* targets and TESTING.md.
Where to go next
Git signing
Identity per execution context and the App-token pattern that signs every AI-authored commit.
tofu-runs-on
The RunsOn runner pool itself — provisioning, OIDC trust, migration guide.
OpenTofu check placement
Static checks in pre-commit, credentialed operations in CI via OIDC.
CI/CD overview
Runner tiers in context, plan/apply gating, branch rulesets.