Skip to content

Latest commit

 

History

History
99 lines (84 loc) · 14 KB

File metadata and controls

99 lines (84 loc) · 14 KB

Release Skill

Beta Release (release beta)

  1. Sync local main with origin/maingit fetch origin main && git merge --ff-only origin/main (run this from a plain main checkout, not a feature branch). If this fails to fast-forward, something is wrong locally — do not force it, investigate first.

  2. Check beta's only unique commits are its own past release stampsgit fetch beta main && git log --oneline origin/main..beta/main. Each prior beta release adds exactly one commit here (titled release: v<version>, or chore: reset beta/main to mirror origin/main... for the one-time migration reset) — that's expected, not a problem. What's NOT expected is anything else: a feature commit, a fix, an unexplained message. If you see a commit that isn't one of this skill's own release-stamp commits, stop — something landed on beta directly, breaking the one-directional flow this skill exists to enforce. Do not silently overwrite it; surface it to the user. (Note: bess-manager-beta's branch protection forbids force-push, so this history accumulates one merge commit per release rather than staying literally empty — that's fine, the check is about content, not commit count.)

  3. Build the release commit locally, on top of origin/main, before touching the beta remotegit checkout -b beta-release-tmp origin/main. Bump bess_manager/config.yaml's version field to the next beta number (check git show beta/main:bess_manager/config.yaml | grep '^version:' and gh release list -L 5 -R johanzander/bess-manager-beta first — e.g. 9.9.0b99.9.0b10, or start X.Y.0b1 if promoting past what main last shipped as stable). In the same commit, re-apply the beta identity fields, which never exist on main by design:

    • bess_manager/config.yaml: name: "BESS Manager (Beta)", slug: "bess_manager_beta", image: "ghcr.io/johanzander/bess-manager-beta-{arch}"
    • repository.yaml: name: BESS Battery Manager (Beta) Repository, url: https://github.com/johanzander/bess-manager-beta

    Commit as git commit -am "release: v<beta-version>". Pushing this single commit (not raw origin/main) is what keeps the beta repo from ever momentarily claiming to be the prod add-on.

  4. Copy the changelog, don't author it — on the same beta-release-tmp branch from step 3, take the current ## [Unreleased] section verbatim from origin/main's CHANGELOG.md (synced in step 1) and rename it to ## [<beta-version>] - <date> in CHANGELOG.md. Amend it into the same commit (git commit --amend) rather than adding a second commit.

    First, detect PRs merged since the last beta with no changelog entry at all. Get the previous beta release's publish timestamp (gh release view v<prev-beta-version> -R johanzander/bess-manager-beta --json publishedAt), then list every (#N)-tagged merge commit on origin/main since it: git log origin/main --oneline --since="<prev-beta-release-timestamp>". For each PR number found, grep CHANGELOG.md's Unreleased section for a matching #N link. Any PR with no match is a real gap — it merged without a changelog entry, not just already-shipped content to curate away. Do not silently drop these: read the PR's diff/description (gh pr view <N> --repo johanzander/bess-manager), judge whether it's user-facing (a real user would notice the behavior/UI change) or purely internal (refactor, CI wiring, doc/skill-only) — internal PRs correctly have no entry and need none — then for user-facing gaps draft a one-line entry in the existing style and present it in chat for confirmation. Once confirmed, add it both to origin/main (a small separate PR against origin/main's CHANGELOG.md Unreleased section, since that file is the canonical source of truth for every future release, per CLAUDE.md) and to this beta's ## [<beta-version>] section.

    Then curate, don't dump. origin/main's Unreleased section only ever grows — it's cleared by a stable release, not a beta one — so by the second beta release it typically contains content already shipped in an earlier bN. Check each entry's PR against git log --oneline origin/main | grep '(#N)' relative to the previous beta release's sync-point commit (the last "chore: re-sync..." or release PR merge on origin/main); anything that merged before that point already shipped and must be dropped from this release's section, not re-listed. Keep only what's new since the last beta, plus a one-line note pointing at the previous release for context (see the v9.9.0b10/v9.9.0b11 entries for the pattern). Getting this wrong silently double-announces old work as new in every subsequent release — it compounds.

  5. Merge beta/main into this branch — expect exactly two conflicts, and that's normal, not an error:

    git fetch beta main && git merge beta/main --no-ff
    

    beta/main's tip is never an ancestor of origin/main after the very first beta release (its own version-stamp commit only exists there), so this is never a fast-forward and a plain merge is the correct tool going forward — a failed --ff-only here does not mean the "beta never gets its own commits" rule was broken. Two conflicts are guaranteed by construction and mechanical to resolve:

    • bess_manager/config.yaml's version: line — keep ours (the new bN this release just set in step 3).
    • CHANGELOG.md's heading — keep ours (the new version heading + step 4's curated entries), then make sure the previous release's already-published section (which beta/main has and this branch doesn't) still appears immediately below it. If your merge tool put it somewhere else or dropped it, fix that before committing — the historical section must survive.

    Any other file conflicting is not expected and needs real investigation (usually: origin/main moved between when this branch's base was chosen and now, surfacing content beta/main hasn't seen — resolve by taking the newer, origin/main-based side, since that's always the more current code). Commit the resolution as git commit -m "merge: reconcile beta/main history for v<beta-version> release".

  6. Run tests locally — ALL of these must pass before proceeding:

    • If this branch/worktree has no .venv yet (e.g. it was cut fresh via git worktree add ... origin/main, per the Worktree Conventions in docs/agents/local-agent-environment.md), create one before running anything: check .python-version for the pinned interpreter (use pyenv exec if the system python3 is older — mismatched versions surface as unrelated-looking TypeErrors on X | None syntax deep in imports, not as a version error), then python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt -r backend/requirements.txt. .venv is gitignored, so it never comes along with the worktree/branch even though other worktrees in the repo each have their own.
    • pytest -m "not slow" (includes scenario discovery regression tests)
    • pytest core/bess/tests/unit/test_scenario_discovery.py -v (show individual scenario results)
    • npx vitest run (frontend tests)
    • cd frontend && npx tsc --noEmit (TypeScript type check — catches errors that vitest and vite build miss)
    • If any fix during this session revealed another bug, fix it now. Do not cut a release per fix — batch fixes locally until all tests pass.
  7. Run black --check . and ruff check . — fix any formatting issues before committing.

  8. Commit all changes to the beta-release-tmp branch.

  9. Push branch to beta remote: git push beta beta-release-tmp:beta-release-tmp

  10. Create PR against beta/main:

gh pr create --repo johanzander/bess-manager-beta \
  --base main --head beta-release-tmp \
  --title "release: v<version>" --body "<changelog>"

If beta/main's history still contains an old squash-merged release (from before step 12 switched to a regular merge), the Commits tab on this PR will show ~100 unrelated-looking commits spanning weeks — that's expected, not a sign something went wrong. A squashed release commit has no ancestry to origin/main's individual commits, so a branch cut fresh from origin/main (step 3) sees all of that history as "not yet in beta/main," even though most of it already shipped to beta users in substance. Once every release in the chain has used a regular merge (step 12), this stops happening — beta/main stays a true descendant of origin/main and each release's diff shrinks to the real delta. To review what's actually new regardless, use the Files Changed diff (gh pr diff <pr-number> --stat), not the Commits tab. 11. Monitor CI on the PR. Check with:

gh pr checks <pr-number> --repo johanzander/bess-manager-beta --watch

If any check fails: read the failure logs with gh run view <run-id> --repo johanzander/bess-manager-beta --log-failed, fix the issue locally, commit, push, and re-check. Do NOT proceed to merge until all required checks pass. Also run npx tsc --noEmit locally before pushing — the CI type-check catches errors that npm run build misses. 12. Merge PR with a regular merge commit, not squash: gh pr merge <pr-number> --repo johanzander/bess-manager-beta --merge. Squashing discards beta/main's ancestry to origin/main's real commit history, so every future release branch (cut fresh from origin/main, step 3) sees beta/main's file content as unrelated blobs instead of an older snapshot of the same lineage — producing spurious merge conflicts in step 5 on any file that changed in between, even when one side is simply stale. A regular merge keeps beta/main a true descendant of origin/main, so subsequent releases get a clean 3-way merge with only the two structurally-expected conflicts. 13. Tag and push tag: git fetch beta main git tag v<version> beta/main git push beta v<version> 14. Create a published GitHub Release — pushing the tag alone does NOT trigger the image build; release-addon.yml only fires on release: published: gh release create v<version> --repo johanzander/bess-manager-beta \ --title "v<version>" --prerelease --notes "<changelog>" 15. Verify the build and images: gh run list --repo johanzander/bess-manager-beta --workflow release-addon.yml -L 1 podman pull ghcr.io/johanzander/bess-manager-beta-amd64:<version> A successful anonymous pull confirms both that the build succeeded and that the GHCR package is public (first release of a new package name needs a manual visibility toggle otherwise).

Required CI checks on beta/main

  • Fast tests
  • Frontend checks
  • E2E tests
  • Code quality

Production Release (release or release prod)

  1. Check the current stable version: gh release list -L 5 (origin repo) and git show origin/main:bess_manager/config.yaml | grep '^version:' — they should match; if not, stop and investigate before releasing.
  2. Confirm the commit being promoted has already shipped as a betagit log --oneline on origin/main should show the exact commit was previously synced to beta/main and released there (check gh release list -L 10 -R johanzander/bess-manager-beta for a matching bN version pointing at content you recognize). Promoting a commit that was never validated on beta defeats the point of having a beta channel — if this is a small, fully self-validated change (see project memory on beta-vs-prod channel choice), that's fine, just confirm it deliberately rather than by default.
  3. Run the full test suite locally, including pytest -m slow.
  4. Bump config.yaml — drop the bN suffix (e.g. 9.9.0b129.9.0).
  5. Rename the changelog heading## [Unreleased] becomes ## [<version>] - <date> in CHANGELOG.md on origin/main. This is the only changelog edit a production release makes; do not also hand-add entries, they should already be there from each PR's merge.
  6. Run black --check . and ruff check . — fix any formatting issues.
  7. Create a PR against origin/main (a version-bump-only PR, branched from origin/main), wait for CI.
  8. Get explicit user approval, then merge, tag, and push the tag to origin.
  9. Create a GitHub Release: gh release create v<version> --title "v<version>" --notes "<changelog>".

Hotfix Release (release hotfix)

Use when a bug is found in the currently-published stable version and origin/main has since moved on with unrelated, unreleased work you don't want to ship alongside the fix. If origin/main is still close to the last stable tag (no risky or unvalidated work merged since), skip this — just fix on main and run a normal Production Release instead.

  1. Fix on main first, via a normal PR. main remains the only place any fix is ever authored — this procedure only moves it backward to where users already are, never authors it directly on a release branch.
  2. Branch from the stable tag: git fetch origin --tags && git checkout -b release-X.Y vX.Y.Z (the currently-published stable tag, not main).
  3. Cherry-pick the fix commit(s) from main onto release-X.Y: git cherry-pick <sha>.
  4. Bump the patch version in config.yaml (X.Y.ZX.Y.(Z+1)) and add a changelog entry directly on release-X.Y (this content also needs to make it back into origin/main's next Unreleased section by hand, since release-X.Y isn't merged back into main — the fix code already is, only the changelog line and version bump are release-branch-only).
  5. Run the fast test suite on release-X.Y: pytest -m "not slow".
  6. Push, tag, and release from origin: git push origin release-X.Y, then tag vX.Y.(Z+1) on release-X.Y and gh release create as in a normal production release — get explicit user approval before each push/tag/release.
  7. Delete release-X.Y once the patch is published: git push origin --delete release-X.Y. It is not long-lived.
  8. Sync beta: run the normal release beta flow afterward so beta picks up both the original fix (already on main) and stays ahead — no special handling needed since main already has the fix from step 1.