Historical release documentation: These instructions describe the PyPI handoff before it occurred. Current PyPI releases install the Go CLI after the version break; older pinned releases are the Python implementation. See installation.

Release and deployment runbook #

A release is complete only when the tested tag, published artifacts, pinned workloads, completed content builds, and live sites agree. Creating a GitHub Release or updating a container tag is not enough.

1. Freeze a tested source revision #

  • Choose the exact main-branch commit after CI passes; record the 40-character SHA and release version. Maintain Go on 0.x; the next intended version after v0.13.0 is v0.14.0, not 1.0.
  • Require green lint, Linux core/CLI/site integration shards, macOS and Windows tests, and the snapshot package check. No unresolved red CI jobs.
  • In particular, TestCacheDeterminism_ColdVsHot_WithFeedsAllFormats must not depend on whether a public CDN happened to answer. If the test fails due to an intermittent download, investigate and rerun; record the diagnosis and a durable fix rather than treating a red run as pass.
  • Check any open release-affecting PRs and verify the approved source contains the intended code and docs.
  • Do not tag a moving main branch by accident: record the exact commit and compare it to the final release ref.

Local maintainer commands (Go 1.27 required):

git fetch origin --tags
git checkout main
git pull --ff-only
go test -race ./...
go vet ./...
goreleaser check
go run ./scripts/rendering-contract --check
# See the dedicated snapshot workflow and check-rc-artifacts.sh.
git rev-parse HEAD

After all checks pass, an authorized maintainer creates and pushes an annotated version tag:

git tag -a v0.14.0 <verified-full-commit-sha> -m "Markata v0.14.0"
git push origin v0.14.0

Do not push that tag while the release CI is red. This repository’s tag-driven Release workflow tests the tagged Go source, uses GoReleaser, and publishes signed binaries/checksums and SBOMs. The Docker workflow publishes tagged GHCR images including compatibility aliases.

2. Verify artifacts and document the release #

  • Check the canonical GitHub Release, specifically that it is not draft or failed and points to the expected commit.
  • Confirm markata_0.14.0_* and markata-go_0.14.0_* archives for Linux, macOS and Windows, checksums, Cosign signatures and SBOMs. Validate one installed binary of each name.
  • Confirm ghcr.io/waylonwalker/markata-builder:0.14.0 and its markata-go-builder:0.14.0 compatibility alias, as well as applicable search image sources. Do not infer publication solely from a workflow that was queued.
  • Include a curated summary, important compatibility notes, upgrade/rollback instructions and a link to v0.14.0 release notes in the published GitHub release body; the autogenerated commit changelog is supplemental.
  • Never replace a published tag with a different commit. Issue a subsequent patch release for corrections.

3. Publish PyPI independently, if its handoff gate is ready #

The current PyPI project name markata historically belongs to Python Markata. Publishing Go wheels is an intentional breaking installation-path handoff with its own review gate. See PyPI binary wheels.

  • Verify PyPI trusted publisher for owner WaylonWalker, repo markata, workflow pypi-go-wheels.yml and environment pypi.
  • Verify protected GitHub pypi environment and freeze/disable the old Python repository’s publishing workflow.
  • Run Go Markata PyPI wheels on main with version 0.14.0, publish=true and the workflow’s required confirmation only after the above prerequisites are satisfied.
  • Confirm all eight native wheel files on PyPI and test a new virtual environment with python -m pip install --only-binary=:all: markata==0.14.0, markata version and python -m markata version. Unsupported platforms may otherwise fall back to an older Python package.
  • Keep GitHub Release and PyPI states separately labelled until both have succeeded.

4. Deploy one immutable release through Argo CD #

Never change production GitOps to reference an unbuilt tag or a mutable latest image. In each multi-source Helm Application pin the chart targetRevision: v0.14.0 and align every Markata fetch, builder and search image tag: "0.14.0" with that source. Preserve other application settings, storage modes, secrets and feature flags. If search is intentionally independently pinned, verify the compatible search version first; record the exception explicitly.

Use a separate GitOps PR that can be held until all artifacts are verified. Progressive rollout:

Order Surface GitOps source Expected action
1 go.waylonwalker.com argo-apps/apps/go-waylonwalker-com.yaml Canary chart/builder/search pins; rebuild and smoke
2 waylonwalker.com argo-apps/apps/waylonwalker-com-prod.yaml Full site build; smoke archive/images/sidebar/navigation
3 rhiannonwalker.com argo-apps/apps/rhiannonwalker-com-prod.yaml Build via Builder Admin and confirm site
4 wyattbubbylee.com argo-apps/apps/wyattbubbylee-com-prod.yaml Build via Builder Admin and confirm site
5 recipes.waylonwalker.com k8s/recipes-waylonwalker-com/deployment.yaml and site-config.yaml Replace both mutable latest references with 0.14.0; confirm recipe build

The markata-docs Deployment uses a separate Python-based documentation generator and separately versioned docs images; it is not a Markata CLI runtime and should not be falsely counted as an old Markata deployment. platgo currently has an example/commented Markata builder setting, not an active runtime. A historical rhiannonwalker source-seed bootstrap Job has a pinned image; audit it only if bootstrap is still required.

For each Argo Application: compare desired manifests with running pod image digests and chart revision; confirm it synced successfully and is healthy. On Builder Admin, compare running binary revision with most recent successfully published build. If the feature’s automatic build-on-new-revision does not run, invoke a full build through the authenticated builder. Verify site content before counting it complete.

Example read-only checks (use your configured kubectl context; do not assume remote cluster access from CI):

kubectl -n argocd get applications
kubectl -n waylonwalker-com-prod-notes get deployments,pods
kubectl -n go-waylonwalker-com-notes get deployments,pods
kubectl -n rhiannonwalker-com-prod-notes get deployments,pods
kubectl -n wyattbubbylee-com-prod-notes get deployments,pods
kubectl -n recipes-waylonwalker-com get deployments,pods
curl -fIsS https://waylonwalker.com/
curl -fIsS https://waylonwalker.com/archive/
curl -fIsS https://waylonwalker.com/images/

5. Rollback and closeout #

Before each promotion, record old chart SHA, old image digests, the most recent good site release identifier and an application-specific rollback procedure. If the new build fails, pause further promotions, restore the old immutable chart/image pins in GitOps, reconcile, and activate the last known-good site release. A content rollback and a binary rollback are different operations; verify both.

Keep a deployment record with: release version, tag commit, binary artifacts, PyPI status, container digests, Argo commit, per-site desired/running image, build revision, site release ID, full-build result, public smoke results and timestamp. Mark each site verified, blocked, or not applicable; never call the release “deployed everywhere” with unknowns.