Release infrastructure checklist¶
Use this after creating a new privaci repository, rotating credentials, or
when release / Pages / PyPI workflows fail with auth errors.
Repository secrets (Settings → Secrets and variables → Actions)¶
| Secret | Required for | Notes |
|---|---|---|
GHCR_TOKEN |
Release (container + Helm push) | Classic PAT with write:packages + read:packages. Org GHCR rejects GITHUB_TOKEN with permission_denied: write_package. |
GHCR_USERNAME |
Release (optional) | PAT owner's GitHub login. Defaults to the user who pushed the tag (github.actor). Set when PAT owner ≠ tag pusher. |
PACK_SIGNING_PRIVATE_KEY |
Future pack-signing in CI | Hex Ed25519 private key from scripts/generate_pack_signing_key.py. Not consumed by release.yml today; store for when pack publish is wired in. |
GITHUB_TOKEN is injected automatically — do not create it.
Repository secrets, not environment secrets: release.yml does not use a
GitHub Environment. Put GHCR_TOKEN under Repository secrets.
GitHub Environments (Settings → Environments)¶
| Environment | Created by | Secrets needed | Notes |
|---|---|---|---|
github-pages |
First Pages deploy | None | Uses workflow permissions: pages: write. Auto-created when Pages is enabled. |
testpypi |
First manual PyPI workflow run (or create manually) | None (OIDC) | Configure Trusted Publishing on TestPyPI for workflow publish-pypi.yml, environment testpypi. |
pypi |
Same | None (OIDC) | Configure Trusted Publishing on PyPI for workflow publish-pypi.yml, environment pypi. This repo's pypi environment may require a reviewer approval before publish. |
Trusted Publisher fields (both indexes):
- Owner:
BoundaryLogic - Repository:
privaci - Workflow:
publish-pypi.yml - Environment:
testpypiorpypi(must match the workflow input)
Re-create Trusted Publishers if the repository was deleted and recreated — old bindings do not transfer.
Organization settings¶
| Setting | Location | Value |
|---|---|---|
| Workflow permissions | Org → Settings → Actions → General | Read and write (repo can override, but org must not block writes) |
| GHCR package access | ghcr.io/boundarylogic/privaci → Package settings → Manage Actions access |
Add BoundaryLogic/privaci with Write (necessary but not sufficient — still need GHCR_TOKEN) |
| Helm chart package | ghcr.io/boundarylogic/charts (created on first push) |
Same Actions access + public visibility after first publish |
Tag re-runs vs workflow updates¶
Re-running an old Release workflow uses the workflow file from the tag commit,
not main. If you fixed release.yml on main after tagging, re-runs of that
tag will not pick up the fix.
Options:
- Cut a new tag (e.g.
v0.1.0-beta.4) onmainafter secrets and workflow fixes land. - Move the tag (force-push tag to current
main) — only if no partial release artifacts exist. Afterrelease.ymlidempotent release step lands, a tag re-push updates GHCR/Helm and edits the existing GitHub Release instead of failing withRelease.tag_name already exists. - Add
workflow_dispatchtorelease.yml(future) to run against a chosen ref.
After a successful Release workflow¶
- GHCR visibility — Package settings → Change visibility → Public (so
docker pull ghcr.io/boundarylogic/privaci:…works without auth). - Helm OCI chart — Same for
ghcr.io/boundarylogic/chartsif published. - GitHub Release — Created automatically at the end of
release.ymlwhen push steps succeed (earlier failures skip this step). - PyPI — Still manual: Actions → Publish to PyPI → choose
testpypiorpypiafter Trusted Publishing is configured.
What each workflow needs (quick reference)¶
| Workflow | Trigger | Secrets / env |
|---|---|---|
ci.yml |
Push / PR to main |
None |
release.yml |
Tag v* |
GHCR_TOKEN (+ optional GHCR_USERNAME) |
docs-pages.yml |
Push to main (docs paths) |
Environment github-pages (auto) |
publish-pypi.yml |
Manual dispatch | Environments testpypi / pypi + PyPI Trusted Publisher |
Common failures¶
| Symptom | Cause | Fix |
|---|---|---|
Release.tag_name already exists on Create GitHub release |
Tag was force-pushed after a successful release | Merge idempotent release.yml fix, then re-push the tag or re-run the workflow; GHCR images may already be published |
403 Forbidden / permission_denied: write_package on GHCR push |
Org blocks GITHUB_TOKEN package writes |
Add GHCR_TOKEN PAT; use a new tag (not a re-run of an old one) |
Missing repository secret GHCR_TOKEN |
Secret not created | Settings → Secrets → GHCR_TOKEN |
| Pages 404 | Pages not enabled | Settings → Pages → Source: GitHub Actions; or run docs-pages.yml once |
| PyPI publish 403 | Trusted Publisher not configured | Re-register on pypi.org / test.pypi.org for this repo |
| Release waits for approval | pypi environment has required reviewers |
Approve in Actions, or adjust environment protection rules |