Releasing Podgrove
Podgrove uses reviewed release pull requests, GitHub release assets, and an optional Homebrew tap. Release automation is off until a maintainer enables it. Preparing or pushing the repository does not publish a version, create a tap, install Podgrove for existing users, or change their running environments.
The intended Homebrew command is brew install podgrove/tap/podgrove. It becomes
available only after the public podgrove/homebrew-tap repository exists and its
first verified Formula/podgrove.rb pull request has been merged. There is no
PyPI publication job in this repository.
Maintainer setup
Section titled “Maintainer setup”- Push the reviewed public source to
podgrove/podgrove, with private rollout notes, credentials, generated manifests, and local state excluded. Follow the public source preparation guide when exporting from an existing private development checkout. Passing package checks does not replace reviewing the public files and Git history. - Enable GitHub Actions and allow it to create pull requests. Protect
main, require CI, and require review of release pull requests. Action dependencies use full commit SHAs; review Dependabot updates rather than replacing those pins with moving tags. - Create the
releaseenvironment, restrict deployment tomain, and configure required reviewers if publication must require a second maintainer. Enable GitHub release immutability before the first release. The workflow uploads all assets to a draft before publishing; after publication it never replaces assets or moves a tag. - Optionally configure
RELEASE_PLEASE_TOKENas a fine-grained token limited to this source repository, with contents, issues, and pull-request write access. Without this secret, release-please usesGITHUB_TOKEN. That is sufficient for the publication jobs chained in this workflow. PRopened,synchronize, andreopenedevents created with that token produce approval-required CI runs: a maintainer with write access can select Approve workflows to run in the PR, or explicitly dispatch CI on its branch. Verify that the successful checks cover the PR’s current head before merging; do not disable required checks. A scoped App token or PAT allows these PR runs to start without that approval prompt. Other events, including release and tag-push events, remain suppressed when created withGITHUB_TOKEN. GitHub documents the event exceptions and approval behavior. - When the repository and workflow are ready, set the repository Actions variable
RELEASE_AUTOMATION_ENABLEDto the literal stringtrue. Leaving it absent or setting it tofalsekeeps both automatic and manual publication disabled.
The workflow grants read access by default. Only release-please can write release
PRs/tags; only the publication job can upload assets. Untrusted pull requests run
with read access and have no release or tap credentials. CI runs local tests and
an inert browser fixture; it never invokes live cluster tests or the destructive
Docker acceptance harness. Hosted macOS jobs install Docker CLI, the Compose
plugin, and GNU coreutils; Linux jobs use the hosted runner’s CLI and GNU tools.
A required preflight fails if Compose or the receiver tools are missing, so those
semantic checks cannot silently skip. Jobs use an empty temporary Docker config
and an unreachable daemon address. Every matrix lane also validates the public
documentation, schema examples, offline bootstrap, and dry-run rendering through
scripts/verify_docs.py, preserving its report as a workflow artifact. These
runner setup steps do not install anything on a maintainer’s machine.
Version and release flow
Section titled “Version and release flow”Use Conventional Commits (fix:, feat:, and feat!: for breaking changes).
Release-please prepares a PR updating CHANGELOG.md, the Python package version,
podgrove/__init__.py, .release-please-manifest.json, and just the local
podgrove package version in uv.lock. Dependency versions remain locked.
The recorded manifest baseline is not evidence that a public GitHub release exists.
Review the proposed first public version and release notes explicitly.
Merging a release PR starts one workflow:
- Release-please creates a stable
vX.Y.Ztag and draft release. - The resolver verifies the tag and obtains an exact commit SHA. The reusable CI workflow tests that commit on Linux/Python 3.11 and 3.14, macOS/Python 3.14, and Chromium with an inert fixture provider.
- The release build creates an sdist, then builds its wheel from that sdist.
uv.lockcontrols runtime dependencies;.github/build-constraints.txtpins the isolated build backend.scripts/verify_release.pycompares package bytes to the checkout, checks all version declarations, and installs the exact wheel into a disposable virtual environment with hash-locked runtime dependencies. CLI help/version, dashboard assets, and offline bootstrap are exercised outside the checkout with network and subprocess access blocked during probes. - The same job generates the Homebrew formula and a checksum manifest. The five
assets are
podgrove-X.Y.Z.tar.gz,podgrove-X.Y.Z-py3-none-any.whl,podgrove.rb,release-manifest.json, andSHA256SUMS. The manifest records the source commit, package/formula hashes, and installed-package checks. - The publication job downloads those exact workflow artifacts, uploads missing
assets without
--clobber, downloads them again to compare SHA256 hashes, verifies the tag still identifies the expected commit, and publishes the draft. A conflicting existing asset or mismatched recovery artifact stops the job. - If the tap is configured, a separate job tests the formula on a disposable macOS runner and opens a tap PR. Publication does not auto-merge that PR.
This chaining deliberately avoids a separate release: published workflow:
releases created with GITHUB_TOKEN do not start another workflow. Draft-first
publication also supports GitHub’s immutable release model.
Homebrew setup
Section titled “Homebrew setup”Create podgrove/homebrew-tap as a public repository with a default branch and an
initial commit. Its layout is intentionally small:
homebrew-tap/ Formula/ podgrove.rb # generated by a verified Podgrove release README.md LICENSE .github/workflows/ # tap CI for future edits, if configuredA tap README should describe brew install podgrove/tap/podgrove, brew upgrade podgrove, and the separate prerequisites: kubectl, Docker CLI with Compose v2,
and an explicitly configured Kubernetes context/namespace. Installing the Python
CLI does not configure cluster RBAC or start a local Docker daemon.
In podgrove/podgrove, create the homebrew environment and add its
HOMEBREW_TAP_TOKEN secret: a fine-grained token scoped only to the tap, with
contents and pull-request write permissions. Set the repository variable
HOMEBREW_TAP_REPOSITORY=podgrove/homebrew-tap only after that repository and secret
exist. The tap token is never used by source PR tests or stored in Git remotes.
A GitHub App installation token with equivalent narrow permissions can replace
this secret if the workflow is extended to mint it immediately before checkout.
The formula generator traverses the runtime dependency closure in uv.lock,
including conditional backports conservatively. It excludes test/browser extras,
rejects missing/ambiguous/private dependency sources, and includes every selected
PyPI source URL with SHA256. The current closure has seven packages. rpds-py
requires Rust at build time, so the formula declares it. Python is installed in
Homebrew’s private libexec virtual environment following
Homebrew’s Python packaging guidance.
Generated formulae refer to the exact release sdist asset, not a moving branch or GitHub’s automatically generated tag archive. No placeholder hash or empty formula is committed before a release exists. The release job runs:
brew install --build-from-source podgrove/verification/podgrovebrew test podgrove/verification/podgrovebrew audit --strict podgrove/verification/podgrovepodgrove/verification is an ephemeral tap created only on the hosted runner.
Homebrew’s test block exercises version/help and offline manifest generation; it
never contacts a cluster. A failed Homebrew build blocks the tap PR and leaves the
already verified Python release available. Formula installation is tested on
macOS; Linux Homebrew support is not claimed by that check.
Local release rehearsal
Section titled “Local release rehearsal”From a clean public source checkout, with Python 3.11+ and the pinned uv version:
uv sync --locked --extra test --extra web-testuv run --no-sync ruff check podgrove scripts testsuv run --no-sync pytest -q -m 'not cluster and not integration and not browser'uv run --no-sync playwright install chromiumuv run --no-sync pytest -q -m browseruv build --no-sources --build-constraint .github/build-constraints.txt --out-dir release-distpython3 scripts/verify_release.py --dist release-dist --source-sha "$(git rev-parse HEAD)"Use a new output directory for every rehearsal. The verifier requires exactly one matching sdist and wheel before sealing the directory and refuses existing extra artifacts. It downloads only hash-locked Python runtime packages into a disposable venv, never installs globally, and performs no Git mutation or publication. The source commit argument must identify the actual clean checkout; local dirty rehearsals do not establish release provenance.
Inspect release-dist/podgrove.rb, release-manifest.json, and SHA256SUMS.
Consumers can download all release assets into a new directory and run
shasum -a 256 -c SHA256SUMS (macOS) or sha256sum -c SHA256SUMS (Linux).
Checksums detect mismatched bytes; GitHub immutable releases additionally bind the
published assets and tag through the platform’s release attestation.
Recovering a failed publication
Section titled “Recovering a failed publication”Prefer Re-run failed jobs on the original Release workflow. It preserves the
successful build’s uploaded verified-release artifact instead of rebuilding the
same version with potentially different archive bytes. GitHub also supports
rerunning an individual failed job.
If a fresh recovery run is needed, dispatch Release on main, supplying both
tag and artifact_run_id from the original successful build. For example, replace
these illustrative values with the actual release and run ID:
gh workflow run release.yml --repo podgrove/podgrove --ref main \ -f tag=v0.2.0 -f artifact_run_id=123456789Recovery verifies the original run belongs to this repository’s main-branch Release push for that exact source commit, retests that commit, and reuses its artifact. It does not create tags, rebuild assets, or accept another version’s bundle. If all published assets already match, publication is a read-only check and the tap job can proceed. Missing/different published assets are an error. Workflow artifacts are retained for 30 days; do not assume an expired artifact can be reproduced byte-for-byte. Resolve the failure or publish a new version.
A source fix always goes through a new version. Existing users choose when to
upgrade; no release job runs podgrove up, down, or reap, changes an installed
versioned venv, or touches a running worktree.