Files
RedBear-OS/local/docs/RELEASE-BUMP-WORKFLOW.md
T
vasilito 99e5641127 feat: release-bump pipeline + external graphics version sync
Pipeline (3 operator asks):
- bump-release.sh: canonical orchestrator (forks + sources + external)
- upgrade-forks.sh --to=<tag>: rebase forks with diverged-mode guard
- bump-graphics-recipes.sh: map-driven group-aware graphics bumps
- check-external-versions.sh: drift checker for Qt6/KF6/Plasma/Mesa/Wayland
- refresh-fork-upstream-map.sh: append-only map updater with --check
- post-checkout-version-sync.sh + install-git-hooks.sh: opt-in branch hook
- external_version_lib.py: shared version-parsing/bumping library
- external-upstream-map.toml: ~80 external package entries
- bump-fork.sh: deprecated (REDBEAR_I_KNOW_BUMP_FORK_IS_DEPRECATED=1)
- RELEASE-BUMP-WORKFLOW.md: operator runbook

Quality fixes (8 defects from two independent audits):
- blake2b stable cache keys (was hash(), non-portable)
- atomic cache writes via os.replace
- version_sort_key pre-release demotion (was sorting after finals)
- apply_ver_transform re.error tolerance
- grep || true (pipefail abort)
- cd failure detection in upgrade-forks
- sed URL escape (injection hardening)
- refresh-fork-upstream-map last-row drop fix

Doc cleanup:
- Archive 5 obsolete plans to local/docs/archived/
- Remove 14 stale/superseded docs
- Update 18 docs to reference bump-release.sh and fix inbound links
- TOOLS.md drift fixes
2026-07-18 14:45:41 +09:00

369 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Red Bear OS — Release-Bump Workflow
**Status:** canonical operator runbook (2026-07-18)
**Audience:** operators performing a Red Bear OS release bump
**Scope:** moving version labels, fork sources, and the external desktop
stack forward across a release-branch switch.
This document is the **runbook** for the release-bump pipeline. It is the
companion to two contracts:
- `local/AGENTS.md` § "Version conventions — two categories" and
§ "No-fake-version-label rule" — the *policies* this pipeline enforces.
- `local/scripts/TOOLS.md` — the *tool inventory* (modes, flags, where each
tool runs).
If the docs disagree, AGENTS.md is the policy authority and TOOLS.md is the
behavioural contract of each script. This document is the workflow that
sits on top of both.
---
## TL;DR — the 30-second version
A Red Bear OS release bump is four steps. The first is human; the rest are
mechanical or operator-judgement.
| Step | What | Who/What | Touches source? |
|------|------|----------|-----------------|
| **1. Cut the branch** | Create the release branch on gitea (`0.3.2`) | Operator only | No |
| **2. Switch + label-sync** | `git checkout 0.3.2` → hook rewrites all version *labels* to match the branch (or run `./local/scripts/bump-release.sh` by hand) | post-checkout hook (opt-in) or operator | **No** — labels only |
| **3. Source upgrades** | `./local/scripts/bump-release.sh --with-sources --with-external` rebases eligible forks to newer upstream tags and bumps the Qt6/KF6/Plasma recipes | Operator | **Yes** — real rebases + recipe bumps |
| **4. Rebuild + stabilize** | Rebuild, fix patch fallout, regenerate lockfiles | Operator | Yes |
Nothing in steps 24 ever auto-commits. Every step that changes the tree
prints the exact `git add` / `git commit` sequence the operator should run.
---
## The four-step release model in detail
### Step 1 — Cut the release branch (operator only)
Per `local/AGENTS.md` § "BRANCH AND SUBMODULE POLICY", release branches are
created **by the operator only** — one per Red Bear OS release cycle. Agents
must never create branches. The branch name MUST be an anchored semver:
`0.3.1`, `0.3.2`, `0.4.0`. Non-semver names (`master`, `submodule/*`,
`recovered/*`) are never release branches and are ignored by every tool in
this pipeline.
The branch is created on the canonical gitea repo
(`https://gitea.redbearos.org/vasilito/RedBear-OS.git`) from the prior
release tip, then fetched locally:
```bash
git fetch origin
git checkout 0.3.2 # tracks origin/0.3.2
```
Nothing else happens at branch creation time. The bump machinery fires on
the *checkout*, not on the branch creation.
### Step 2 — Switch and label-sync
When the operator checks out the new release branch, two things can happen:
**A. If the post-checkout hook is installed** (opt-in; see
`local/docs/HOOKS.md`):
```bash
git checkout 0.3.2
# post-checkout-version-sync.sh fires automatically:
# - guards pass (semver branch, clean tree, no rebase in progress, …)
# - detects root Cargo.toml is at 0.3.1, not 0.3.2
# - runs: sync-versions.sh --no-regen (labels only)
# - prints the follow-up hint pointing at step 3
```
The hook does the **label-only** half of the bump:
- Cat 0 (cookbook root `Cargo.toml`): `version = "0.3.1"``"0.3.2"`.
- Cat 1 (in-house crates under `local/recipes/*/source/`): same.
- Cat 2 (upstream forks under `local/sources/*/`): the *suffix* moves —
`0.9.0+rb0.3.1``0.9.0+rb0.3.2`. The upstream base (`0.9.0`) is
preserved because no source rebase has happened yet.
The hook explicitly does NOT:
- regenerate lockfiles (`--regen` is never passed — it is a separate
explicit step in step 4),
- touch fork source content,
- commit anything,
- run on non-semver branches or dirty trees,
- block the checkout under any circumstance (always exits 0).
**B. If the hook is not installed**, run the orchestrator by hand:
```bash
./local/scripts/bump-release.sh # label sync + reports, no mutations beyond labels
# or equivalently for labels only:
./local/scripts/sync-versions.sh --no-regen
```
`bump-release.sh` (no args) delegates the label rewrite to
`sync-versions.sh --no-regen` and additionally prints a fork-upstream
report and an external-version report so the operator can see what step 3
will need to touch.
After step 2 the tree has correct labels but possibly stale sources and
lockfiles. The operator should commit the label bump:
```bash
git add -A
git commit -m "release: sync version labels to 0.3.2"
```
### Step 3 — Source upgrades
This is the expensive half. It is always operator-initiated via explicit
flags and never triggered by the hook.
#### 3a. Cat 2 fork source bumps — `--with-sources`
```bash
./local/scripts/bump-release.sh --with-sources
```
For each fork in `local/fork-upstream-map.toml`, `bump-release.sh`:
1. **Classifies** the fork by its map `mode` column (decision tree below).
2. For eligible forks (mode `snapshot` or `tracked`), `git ls-remote --tags`
the upstream URL, find the latest anchored-semver tag, and compare to
the fork's current base (the `<X.Y.Z>` before `+rb`).
3. If upstream moved: invoke `upgrade-forks.sh --to=<tag> <fork>`, which
fetches + pins `upstream_ref` to `refs/tags/<tag>`, creates a backup
branch (`rb-backup/<fork>`), resets, re-applies the Red Bear net-diff,
falls back to cherry-pick if needed, regenerates the fork lockfile, and
runs `verify-fork-functions.sh`.
4. On success: set the fork `Cargo.toml` `version = "<tag>+rb<branch>"`,
update map column 3 in place, and print the exact commit commands for
the parent repo and the `submodule/<fork>` branch.
5. On failure: report, leave map/label untouched. No partial state.
Per the **no-fake-label rule** (`local/AGENTS.md` § "No-fake-version-label
rule (STRICT)"), labels never move without matching source content. When
upstream is unchanged, only the `+rb<branch>` suffix moves (already done in
step 2). When upstream moved, the fork is really rebased first, then the
label moves to match.
#### 3b. External desktop stack — `--with-external`
```bash
./local/scripts/bump-release.sh --with-external
# or just the version report first:
./local/scripts/check-external-versions.sh
```
This drives the rewritten, map-driven `bump-graphics-recipes.sh` over the
Qt6/KF6/Plasma desktop stack and singletons (mesa, libdrm, libwayland,
sddm, …) declared in `local/external-upstream-map.toml`. Groups resolve
their version **once** (e.g. all KF6 frameworks move to the same
`frameworks/X.Y/`), symlinked recipes are deduped by realpath so qtbase is
bumped exactly once, and each recipe is validated with
`repo validate-patches <recipe>` after the bump. Results land in
`.redbear-recipe-bump/last-report.txt` with the line format:
```
recipe= old= new= patches_total= patches_pass= patches_fail= fail_details=
```
Failing patch validation is **reported, not auto-fixed** and never causes a
recipe to be removed (per the AGENTS.md ABSOLUTE RULE). Failures become
work items for step 4 (stabilization).
The two flags compose:
```bash
./local/scripts/bump-release.sh --with-sources --with-external
```
### Step 4 — Rebuild and stabilize
After sources move, the build invalidation machinery fires automatically:
- **Source fingerprints** — each recipe's content-hash cache (BLAKE3 of
build deps) detects that the fork tarball changed and forces a rebuild.
- **relibc ABI wipe** — when relibc itself moved, every downstream recipe
is rebuilt against the new `libc.a`.
- **Prefix auto-rebuild** — `build-redbear.sh` detects that relibc/kernel/
base have commits newer than `prefix/.../libc.a` and rebuilds the prefix
before any recipe build begins.
- **Preflight gates** — `build-preflight.sh` re-runs `sync-versions.sh
--check`, `verify-fork-versions.sh`, `verify-patch-content.py`, and
`verify-collision-detection.py` before the build proceeds.
Stabilization work then proceeds in the normal build-fix loop: patch
failures surfaced by step 3's report are fixed at the root cause (real
implementation, no stubs — see `local/AGENTS.md` § "STUB AND WORKAROUND
POLICY"). Lockfile regeneration is a separate explicit operator step:
```bash
./local/scripts/sync-versions.sh --regen # after labels + sources settle
```
---
## Fork decision tree (for `--with-sources`)
The fork's `mode` column in `local/fork-upstream-map.toml` decides what
`bump-release.sh --with-sources` is allowed to do. Modes currently in use:
| Mode | Meaning | Source-bump eligible? |
|------|---------|-----------------------|
| `snapshot` | Imported from an archived upstream snapshot; git history tracks a single upstream tag. (syscall, libredox, redoxfs, redox-scheme, relibc, userutils) | **Yes** — auto source-bump when upstream semver moves. |
| `tracked` | Fork follows an upstream ref (e.g. `main`); content checked but not byte-pinned. (base) | **Yes** — but `base`'s map tag is `main`, so it always resolves to suffix-only (see below). |
| `diverged` | Fork has diverged materially from upstream; no clean rebase path without operator work. (kernel, bootloader, installer) | **No** — report-only. Escape hatch: `--force-diverged`. |
Decision logic, per fork:
```
mode == snapshot or tracked:
latest_upstream = git ls-remote --tags <url> | latest anchored semver
if latest_upstream > current_base:
action = source-bump (upgrade-forks.sh --to=<latest_upstream>)
else:
action = suffix-only (already done in step 2)
mode == diverged:
action = report-only
# operator may pass --force-diverged to attempt a rebase anyway
tag == main (base special case):
action = suffix-only, always
# base member crates carry no +rb suffix and the verifier accepts that
# (see AGENTS.md § "Version conventions — two categories")
```
### Bootloader note (special case)
`bootloader` is `diverged` and additionally has **genuinely unrelated git
history** — there is no merge-base between the fork HEAD and upstream
`master`. `git merge-base HEAD upstream/master` fails. This means
`upgrade-forks.sh` cannot compute a net-diff to reapply and any attempt to
source-bump it would destroy committed work.
`bump-release.sh` NEVER attempts a bootloader source bump, even with
`--force-diverged`. It reports bootloader as `action=report-only` with the
guidance:
> Bootloader fork has no merge-base with upstream. A one-time
> `git replace --graft <fork-head> <upstream-tag>` intervention is required
> to synthesize a common ancestor before any rebase can be attempted. This
> is future work; the orchestrator never performs it.
The graft path (documented for the future, not run by any script today):
```bash
cd local/sources/bootloader
git replace --graft $(git rev-parse HEAD) <upstream-tag-sha>
# now upgrade-forks.sh can compute a merge-base and reapply the Red Bear delta
```
Until that graft lands, bootloader stays at its current upstream base and
only its `+rb<branch>` suffix moves with each release bump.
---
## Notes and invariants
### `Cargo.toml.orig` files are never touched
Every Cat 2 fork may carry a `Cargo.toml.orig` that mirrors the upstream
`Cargo.toml` byte-for-byte (Cargo leaves it behind when the fork edits the
real `Cargo.toml`). `sync-versions.sh`, `bump-release.sh`, and the
post-checkout hook all edit ONLY the live `Cargo.toml`. `Cargo.toml.orig`
is a read-only witness of upstream state and MUST stay byte-identical to
upstream so that `verify-fork-versions.sh` can compute the Red Bear delta.
### Lockfile regeneration is a separate explicit step
`sync-versions.sh --no-regen` (the default, and what the hook calls) rewrites
version labels but does not regenerate `Cargo.lock`. Lockfile regen is
opt-in via `sync-versions.sh --regen` and should be run after *both* labels
and sources have settled, because a source rebase can pull new transitive
deps that an earlier label-only regen would miss.
### Nothing auto-commits
Every step that changes the tree prints the exact commit sequence:
- **Parent repo** (`RedBear-OS` on the release branch): label bumps, map
column-3 updates, recipe tarball/blake3/rev updates.
- **`submodule/<fork>` branches**: fork source rebases.
The operator runs `git add` / `git commit` / `git push` themselves. This is
non-negotiable per `local/AGENTS.md` § "BRANCH AND SUBMODULE POLICY" and
the project's general "agents never commit unless asked" rule.
### Role of `provision-release.sh` and `REDBEAR_RELEASE`
`provision-release.sh` is the **freeze-time** tool: it provisions a new
immutable release archive from a Redox ref into `sources/redbear-<release>/`.
It is invoked explicitly and human-initiated, never by the bump pipeline.
`REDBEAR_RELEASE=<x.y.z>` is the env var that switches the build into
**sealed/release mode**: sources are extracted from the immutable archive,
online fetching is completely disabled, and local forks are ignored. In
development mode `REDBEAR_RELEASE` MUST be unset (see `.config` rules in
`AGENTS.md` § "BUILD COMMANDS"). The bump pipeline runs in development mode;
it has no behaviour in sealed mode.
---
## Cross-references
| Topic | Where |
|-------|-------|
| Version categories (Cat 1 / Cat 2), `+rb` suffix rules | `local/AGENTS.md` § "Version conventions — two categories" |
| No-fake-version-label rule (labels match source content) | `local/AGENTS.md` § "No-fake-version-label rule" |
| Latest-upstream-before-freeze rule | `local/AGENTS.md` § "Local fork dependency rule" |
| Local fork dependency rule (path deps, no version strings) | `local/AGENTS.md` § "Local fork dependency rule" |
| Branch and submodule policy (operator-only branches) | `local/AGENTS.md` § "BRANCH AND SUBMODULE POLICY" |
| Tool inventory + modes | `local/scripts/TOOLS.md` |
| Hook install / bypass / inventory | `local/docs/HOOKS.md` |
| Fork → upstream tag/mode map | `local/fork-upstream-map.toml` |
| External desktop-stack version map | `local/external-upstream-map.toml` |
| Post-checkout hook source | `local/scripts/post-checkout-version-sync.sh` |
| Installer source | `local/scripts/install-git-hooks.sh` |
| Orchestrator source | `local/scripts/bump-release.sh` |
---
## Appendix — full worked example
Bumping from `0.3.1` to `0.3.2` with the hook installed:
```bash
# Step 1 — operator creates 0.3.2 on gitea (out of band), then:
git fetch origin
git checkout 0.3.2
# ⟶ post-checkout hook fires:
# post-checkout-version-sync: branch '0.3.2' root Cargo.toml at '0.3.1' -> '0.3.2'
# post-checkout-version-sync: running sync-versions.sh --no-regen (labels only, no lockfile regen)
# ... sync output ...
# post-checkout-version-sync: label sync complete for branch '0.3.2'.
# follow-up steps (NOT run automatically): ...
git add -A && git commit -m "release: sync version labels to 0.3.2"
# Step 3 — source upgrades (operator decides)
./local/scripts/bump-release.sh --with-sources --with-external
# ⟶ per-fork decision lines, per-recipe validation report at
# .redbear-recipe-bump/last-report.txt. Prints commit commands.
# Step 4 — rebuild + stabilize
./local/scripts/sync-versions.sh --regen # lockfiles
./local/scripts/build-redbear.sh redbear-mini # build + preflight gates
# fix any patch fallout at the root cause; commit on 0.3.2 / submodule/<fork>
```
Without the hook, step 2 becomes:
```bash
git checkout 0.3.2
./local/scripts/bump-release.sh # labels + reports
git add -A && git commit -m "release: sync version labels to 0.3.2"
```
The rest is identical.