# Releasing This document describes how a production release reaches the `master` branch and how a GitHub release is published. It complements the **Branch Model** section of [DEVELOPMENT.md](DEVELOPMENT.md). This file lives only on the `source`/`test` development branches. It is **not** part of `dist/`, so it never reaches `source-build` or `master` — users who `git clone` the `master` branch onto their servers get only the files needed to run the tool. ## Why `master` is not a merge target `master` plays two roles at once: - It is the **distribution branch** — users clone it directly onto their servers, so it must stay minimal (just the monolithic script and the few files needed to run/build the container). - It is the repository's **default branch**, which is the only place GitHub runs *scheduled* workflows from. The `autoupdate`, `stale` and `vuln-watch` workflows are therefore **master-only** and deliberately kept off the `source`/`test` line (see commit "remove from test branch workflows that must live on master"). `source-build` is a build **output** branch and does **not** carry those master-only workflows (the build's `rsync --delete` strips `.github/` down to what `source` ships). Merging the whole `source-build` tree into `master` would therefore drag the *absence* of those workflows into `master`, producing recurring `modify/delete` conflicts — and, worse, a clean merge could silently delete them when they hadn't been edited since the last release. So we never merge `source-build` into `master`. Instead we **copy only the assembled artifact files** across, and cut GitHub releases from `master` directly. ## The `release` workflow `.github/workflows/release.yml` (which, like the other master-only workflows, lives **only on `master`**) is triggered manually via `workflow_dispatch` and offers two independent actions selected from the `action` dropdown. Run it against the `master` branch. ### 1. `sync-from-source-build` Copies every top-level entry on `source-build` **except `.github/`** (`spectre-meltdown-checker.sh`, `README.md`, `doc/`, `Dockerfile`, `docker-compose.yml`) onto `master` as a single commit, mirroring exactly (deletions and renames included). `master`'s own `.github/` — its master-only CI — is never touched. The script's contents are copied byte-for-byte, so its `VERSION` (generated by the `source-build` build) is preserved unchanged. No version bump happens here. The job is a no-op if `master` is already up to date. ### 2. `draft-github-release` Reads the `VERSION` from the script currently on `master`, and creates a **draft** GitHub release tagged `v`, with the monolithic script attached as an asset. Because it is a draft, **no tag is created and nothing is published** until you press *Publish* in the Releases UI — so this step is fully reversible. The changelog is auto-drafted by locating the `source-build` commit whose built `VERSION` matches the previous published release, then listing every assembled commit since. Treat it as a starting point and edit it before publishing. ## Release procedure 1. Confirm `source-build` holds the artifact you want to release (CI is green, version string looks right). 2. Run the **`release`** workflow on `master` with `action = sync-from-source-build`. Review the resulting commit/diff on `master`. 3. Run the **`release`** workflow on `master` with `action = draft-github-release`. 4. Open the draft release, review/edit the auto-generated changelog, then **Publish** it. Publishing creates the `v` tag. Steps 2 and 3 are decoupled on purpose: you can refresh `master` from `source-build` (step 2) without cutting a formal GitHub release yet. ## Rollback - Before publishing: delete the draft release in the UI (no tag exists yet). - After a bad `sync-from-source-build`: `master` history is intact — revert the sync commit with a normal `git revert` (never force-push `master`).