diff --git a/CLAUDE.md b/CLAUDE.md index acd1da91..bdce84dc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -165,11 +165,13 @@ These hold across the whole codebase; the skills explain the mechanisms. image tag, so `just version::check` (a CI stage) fails where these disagree. The frontend reads `VERSION` at build time (`vite.config.ts` → `$lib/version` → the footer) and displays it. -- **`main` is the only long-lived branch, and merging to it deploys to dev.** - One focused pull request per change, each carrying its manually added - `CHANGELOG.md` entry. Both clusters install the same chart from the GitOps - setup in `sdsc-ordes/cloud-infra` and differ only in image overrides: **dev** - runs `temporary/*:latest` with `pullPolicy: Always`, so every push to `main` +- **`main` is where development happens, and merging to it deploys to dev.** One + focused pull request per change, each carrying its manually added + `CHANGELOG.md` entry. Each released minor line has a `release/X.Y` branch that + takes hotfixes only, released from its own tags and cherry-picked back to + `main`. Both clusters install the same chart from the GitOps setup in + `sdsc-ordes/cloud-infra` and differ only in image overrides: **dev** runs + `temporary/*:latest` with `pullPolicy: Always`, so every push to `main` reaches it on the next container restart; **prod** takes the chart defaults, `release/*` at `appVersion`, so nothing merged can reach it until someone bumps `appVersion` and installs the new chart version. See `RELEASING.md`. diff --git a/RELEASING.md b/RELEASING.md index 02771f55..eba8cb06 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -10,17 +10,37 @@ them. ## Branches -`main` is the only long-lived branch. Work happens on a short-lived branch — -`feat/…`, `fix/…`, `docs/…`, `chore/…` — and merges into `main` through a pull -request, with its changelog entry in the same pull request. - -There is no integration branch. One focused pull request per change, reviewed -and merged on its own, is what keeps the history readable and the changelog -honest. +``` +main ──●──●──●──●──●──●──●──●──●──► development; dev follows it + │ + v0.12.1 + its chart bump + │ + └── release/0.12 ──●── v0.12.2 ──●── v0.12.3 hotfixes only; prod runs these tags + ▲ + hotfix/… PRs +``` -A pull request runs the full CI suite: codegen drift, lint, version consistency, -chart lint, build, tests, and an image build. That is the only gate in front of -`main` which matters for the reason below. +`main` is where development happens. Work happens on a short-lived branch — +`feat/…`, `fix/…`, `docs/…`, `chore/…` — and merges into `main` through a pull +request, with its changelog entry in the same pull request. One focused pull +request per change, reviewed and merged on its own, is what keeps the history +readable and the changelog honest. + +`release/X.Y` is one branch per released minor line, created when that line is +released ([Making an app release](#making-an-app-release)). It never receives +features, only the fixes that prod cannot wait for — see +[Making a hotfix](#making-a-hotfix). Its tags (`v0.12.2`, `v0.12.3`, …) are what +prod runs. Once the next minor line is released, the old branch simply stops +being used and can be deleted once the new release has been deployed to +production. + +So `main` can keep moving during a hackathon while prod stays on the release it +was installed with, and a hotfix for prod can be done on `release/X.Y`. + +A pull request runs the full CI suite, whichever branch it targets: codegen +drift, lint, version consistency, chart lint, build, tests, and an image build. +That is the only gate in front of `main` and `release/*`. A push to `release/*` +itself builds nothing; only its tags do. ## Deployment @@ -74,6 +94,9 @@ that exists because a `v*` tag built it. Nothing merged to `main` can reach prod. It changes only when someone bumps `appVersion`, publishes the chart, and installs that chart version. +While a hackathon is running on prod, install nothing there except hotfixes in +case the event needs them. + ## Three version numbers Two of them are the app, one is the chart, and they move independently. Read @@ -111,7 +134,7 @@ Concurrent pull requests both appending under `[Unreleased]` will conflict on those lines. The conflict is trivial to resolve — keep both entries — and is the price of a file anyone can read without tooling. -## Cutting an app release +## Making an app release From an up-to-date, clean `main`: @@ -134,6 +157,17 @@ Pushing the tag builds and pushes `release/backend-service:0.9.0` and `release/frontend-service:0.9.0`. A release image is never overwritten, so a botched tag is fixed by bumping again, not by re-tagging. +Then [make the release deployable](#making-a-release-deployable). Once that +chart pull request is merged, create the release branch from its merge commit — +not from the tag, which does not have the chart bump yet. `main` may have moved +on in the meantime, so name the commit: + +```bash +git fetch origin +git switch -c release/0.9 +git push -u origin release/0.9 +``` + ### If you bumped the wrong way `bump` commits and tags locally and pushes nothing, so a release you have not @@ -144,15 +178,59 @@ git reset --hard origin/main git tag -d v0.9.0 ``` -If `main` has moved past something you cannot ship and a released version needs -a fix, branch from the tag rather than from `main`: +## Making a hotfix -```bash -git switch -c hotfix/thing v0.8.0 -``` +A hotfix goes on the release branch prod runs, never through `main`. Take +`release/0.12` and a fix that becomes `v0.12.2`: + +1. **Branch from the release branch**, not from `main`: + + ```bash + git fetch origin + git switch -c hotfix/thing origin/release/0.12 + ``` + +2. **Fix it, in one pull request into `release/0.12`** — set that as the base; + GitHub suggests `main`. The pull request carries: + - the fix itself, + - its line under `[Unreleased]` → `### Fixed` in `CHANGELOG.md`, + - the chart bump for the release it will become: `Chart.yaml` `version` up + one patch, `appVersion: "0.12.2"`. + + The chart bump belongs here, not in a pull request afterwards: CI publishes + charts only for pushes to `main` and for tags, so on a release branch the + chart has to be in the commit that gets tagged. + +3. **Release it from the release branch**, once the pull request is merged: -Open the pull request against `main` if the fix belongs there too. Create such a -branch when a fix actually needs one — not in advance. + ```bash + git switch release/0.12 && git pull + just version::bump patch + git show # read the release commit before it leaves + git push && git push origin v0.12.2 + ``` + + The tag builds `release/*:0.12.2`, then publishes the chart from the tagged + commit. Installing that chart version on prod is what ships the fix. A wrong + bump is undone as [above](#if-you-bumped-the-wrong-way), resetting to + `origin/release/0.12` instead of `origin/main`. + +4. **Bring the fix to `main`**, or the next minor release brings the bug back. + Cherry-pick the fix commit onto a new branch and open a pull request into + `main`: + + ```bash + git switch -c fix/thing origin/main + git cherry-pick --no-commit + git restore --source=origin/main --staged --worktree helm-chart/Chart.yaml + git commit + ``` + + Pick only the fix. The hotfix commit also carries the release's chart bump, + which is why `Chart.yaml` is put back to `main`'s before committing; merging + the release branch into `main` would bring its release commits and changelog + headings as well. `CHANGELOG.md` will conflict: keep the entry, under + `main`'s own `[Unreleased]`. ## Making a release deployable @@ -173,6 +251,12 @@ no images yet. Publishing a chart is not deploying it. Once a real deployment exists, installing that chart version is the step that changes what runs. +`main` and a release branch both publish charts, so they must never pick the +same number. A release line keeps the chart patch versions of its release — +`release/0.12` started at chart `0.8.1` and goes on with `0.8.2`, `0.8.3`. On +`main`, the first chart change after cutting a release branch moves the chart to +the next minor (`0.9.0`). + ## A first release checklist Worth knowing once, then never again: