Skip to content

Releasing and safe updates

How to change Operator ETL without breaking trust signals (CI, tests, share PDFs) and without overwriting a published version.

Versions: VERSIONING.md — tags publish, master does not.

Agents: merge-feature-pr · cut-release · skill operator-release


Every change (any size)

make e2e

This runs OKF validate, pytest, and the FOIA demo on a fresh warehouse. Do not push if it fails. Current count is in the verify banner (today 95).

For OKF-only doc changes:

python3 scripts/okf_validate.py okf --strict

Pull request workflow

Follow okf/playbooks/merge-feature-pr.md. Summary:

  1. Branch from master
  2. Make changes
  3. Run make e2e locally (./scripts/verify.sh on a fresh clone)
  4. If user-visible: add a bullet under ## [Unreleased] in CHANGELOG.md
  5. Do not bump pyproject.toml version unless this PR is the release (see below)
  6. Open PR — template checklist must be complete
  7. Wait for all required checks green: ci-gate, CodeQL Analyze (see BUILD-HYGIENE.md)
  8. Do not merge while any required check is red or pending
  9. Squash-merge (preferred) after checks pass

Repo admins: enforce this with a GitHub ruleset — Settings UI click-path + exact contexts in PUBLIC-READINESS.md. Legacy ruleset main is disabled until an admin activates it. Without Active enforcement, humans can still click merge on a red PR; treat that as a process failure.

Pushes to master update the wiki (Pages). They do not publish GitHub Packages or GHCR. Only a v* tag does.


Cut a release (freeze)

Do this when you intend to ship a beta or stable, not on every docs PR. Full agent playbook: okf/playbooks/cut-release.md.

  1. Release PR:
  2. Move [Unreleased] to ## [X.Y.Z] or ## [X.Y.Z-beta.N] (today’s date)
  3. Set version in pyproject.toml (PEP 440: 0.5.0b1 for tag v0.5.0-beta.1)
  4. Set extra.operator_etl_version in mkdocs.yml
  5. make e2e
  6. Merge the PR
  7. Annotated tag on that merge commit, then push the tag only (not a force-push to master):
git tag -a v0.5.0-beta.1 -m "Operator ETL 0.5.0-beta.1"
git push origin v0.5.0-beta.1
  1. Confirm Release is green: GitHub Release, ghcr.io/khaosans/operator-etl:<version>, GitHub Packages wheel

If GHCR push fails with a permission error, add a PAT (write:packages) as repository secret GHCR_TOKEN. Details: VERSIONING.md.

Never move or retag v0.5.0-beta.1. Next drop is v0.5.0-beta.2 or v0.5.0. Full rules: VERSIONING.md. GitHub Release creation uses --prerelease for betas only — not a --latest flag (gh does not have one).

First tagged freeze after this process: v0.5.0-beta.1 (separate release PR; do not invent tags for 0.4.2–0.4.9).


Install a published version

See VERSIONING.md. Short form:

docker pull ghcr.io/khaosans/operator-etl:0.7.0
pip install operator-etl --index-url https://pypi.pkg.github.com/khaosans/simple/

Wheels are also attached to the GitHub Release (no Packages auth).


Dependency updates

Dependabot opens weekly PRs for pip and GitHub Actions.

Before merging a Dependabot PR:

  • [ ] CI all green (including secret scan)
  • [ ] Skim changelog for breaking changes
  • [ ] If lockfile changes: uv sync --frozen --extra dev && make e2e locally
  • [ ] Unreleased changelog bullet if the bump is user-visible; no package version bump

Manual bump:

uv lock --upgrade-package <name>
uv sync --frozen --extra dev
make e2e

Commit uv.lock with the dependency change.


External share (PDFs)

Before LinkedIn, interviews, or proposals:

make share    # runs e2e first, then rebuilds PDFs

Checklist: FINAL-REVIEW.md pre-scale · okf/playbooks/qa-before-share.md

While preparing external posts: link repo + optional PDFs from share/README.md.

When repo is public: link https://github.com/khaosans/operator-etl and invite make e2e. Name a tag if you are pointing at a freeze, not floating master.


Infrastructure changes

Terraform:

cd infra/gcp
terraform init    # commit .terraform.lock.hcl when providers change
terraform plan

Never commit terraform.tfvars — use terraform.tfvars.example.

GCP deploy: follow okf/playbooks/deploy-gcp-staging.md. Do not wire Cloud Build deploy to production without review.


What never ships in git

See CONTRIBUTING.md:

  • .env, vault keys, warehouse DBs
  • Real FOIA / production PII
  • .cursor/mcp.json (local paths)
  • Dated share archives (docs/share/releases/)

GitHub Pages wiki

Docs in docs/ publish via MkDocs (.github/workflows/pages.yml) to https://khaosans.github.io/operator-etl/

The site always reflects master (living docs). Installable artifacts are tags. Footer version in mkdocs.yml is the last released package version; bump it only in a release PR.

Local check:

pip install mkdocs-material
mkdocs build --strict

GitHub Settings → Pages → source GitHub Actions (one-time).

GitHub Wiki tab

Do not copy every article into the GitHub Wiki (it drifts). Paste once from the repo:

  1. Wiki Home ← contents of docs/wiki/Home.md
  2. Wiki _Sidebar ← contents of docs/wiki/_Sidebar.md

Update those files in git when nav changes; re-paste if the Wiki tab is in use.


Going public

Complete every blocker in PUBLIC-READINESS.md before changing visibility.

After the first package publish: GitHub → Packages → GHCR package → enable tag immutability so :0.5.0-beta.1 cannot be overwritten in the registry either.


See also