/
/
/
1# Release Workflow Guide
2
3The server release workflows publish one verified source commit as an immutable GitHub
4release and an exact multi-architecture GHCR image before changing any rolling channel.
5The server package is attached to GitHub releases only; it is not published to PyPI.
6
7## Release channels
8
9| Channel | Source branch | Version format | GitHub release |
10|---|---|---|---|
11| Stable | `stable` | `X.Y.Z` | Release |
12| RC | `stable` | `X.Y.ZrcN` | Prerelease |
13| Beta | `dev` | `X.Y.ZbN` | Prerelease |
14| Nightly | `dev` | `X.Y.Z.devN` | Prerelease |
15
16Auto-release checks out the channel branch explicitly and captures its full commit SHA.
17That `source_sha` is passed to the reusable release workflow and remains the source for
18tests, release notes, package contents, the Git tag, the exact container image, and
19frontend-version extraction even if the branch advances during the run.
20
21Automatic versions come from Git tags, not release records. The workflow validates the
22previous channel tag's relationship to `source_sha` and counts its Git commit range.
23Deleted release records therefore cannot make a tag/version reusable. Stable automatic
24releases continue to increment the patch component only. Nightlies require at least two
25commits after the previous nightly tag.
26
27## Publishing sequence
28
29Only one release workflow runs at a time, and queued runs are never cancelled.
30
311. Resolve the channel branch and exact `source_sha`.
322. Require the repository's immutable-release setting using a short-lived GitHub App
33 token with Administration read access.
343. If the version is not already published, run the full test workflow against
35 `source_sha`.
364. Build the wheel and source distribution, or recover the exact two verified assets
37 from a matching draft.
385. Create an annotated exact tag with `github-actions[bot]`, then create or update only a
39 draft whose version, tag, and target SHA match. Replace incomplete draft assets, then
40 verify both asset names, sizes, upload state, and SHA-256 digests.
416. Build and push only `ghcr.io/music-assistant/server:$VERSION`. The image index must
42 contain `linux/amd64` and `linux/arm64`, identify `source_sha` and the wheel digest,
43 and produce one captured OCI digest. An existing exact tag is verified and never
44 overwritten.
457. Revalidate the draft, recheck the immutable-release setting, and publish once.
468. Require an immutable release, the tag at `source_sha`, matching release assets,
47 successful release and asset attestations, and the same exact OCI digest.
489. Promote that digest without rebuilding to the channel aliases:
49 - Stable: `X.Y`, `X`, `stable`, `latest`
50 - RC and beta: `beta`
51 - Nightly: `nightly`
52 Each alias's current amd64 and arm64 image labels are checked independently. An alias
53 that already points to a newer release is never moved backward.
5410. Deterministically update the matching Home Assistant add-on and dispatch the
55 frontend version from `source_sha` to `app.music-assistant.io`.
56
57The exact image tag is already the full version (`X.Y.Z`, `X.Y.ZbN`, `X.Y.ZrcN`, or
58`X.Y.Z.devN`). Rolling aliases are never pushed before the immutable release verifies.
59Normal channel releases are forward-only. This workflow does not define a legacy-branch
60backport process for publishing an older version after a newer channel release.
61
62## Starting a release
63
64The scheduled workflow checks nightly releases automatically. For a manual version:
65
661. Run **Auto Release** from the `dev` branch.
672. Select `stable`, `rc`, `beta`, or `nightly`.
683. Add important notes when needed.
69
70Auto-release calculates the next version and invokes **Create Release** with its captured
71source SHA. **Create Release** can also be dispatched directly with an explicit version;
72for direct runs it resolves and freezes the current channel branch head itself unless you
73pass `source_sha` to recover an exact draft or published release source.
74
75Do not create or publish a GitHub release manually. A draft created outside the workflow
76is accepted only when its exact tag name and target SHA match; conflicting tags,
77published mutable releases, and mismatched drafts fail closed.
78
79## Authentication
80
81Same-repository tags, releases, assets, attestations, and GHCR writes use the job's
82built-in `GITHUB_TOKEN` and `github-actions[bot]`.
83
84Administrative and cross-repository work uses a fresh installation token in each job
85from the private `music-assistant-bot` GitHub App. Tokens are never passed between jobs
86and are restricted to one repository and the permission needed by that job.
87
88The server repository must define:
89
90- Repository variable `MUSIC_ASSISTANT_BOT_CLIENT_ID`
91- Repository secret `MUSIC_ASSISTANT_BOT_PRIVATE_KEY`
92
93The App installation must include these selected repositories:
94
95| Repository | Token permission used by this workflow |
96|---|---|
97| `music-assistant/server` | Administration: read |
98| `music-assistant/appvars` | Contents: read |
99| `music-assistant/home-assistant-addon` | Contents: write |
100| `music-assistant/app.music-assistant.io` | Contents: write |
101
102The App itself must be approved for Administration read and Contents write. Each minted
103token is downscoped from those installation permissions. The workflow verifies that the
104token's App slug is `musicassistant-bot` and its installation ID is `146062122`.
105
106## Recovery
107
108Rerun the failed workflow with the same version and `source_sha`.
109
110If you need to resume an exact draft or published release after the branch has advanced,
111reuse the workflow-created source SHA from the matching tag or draft target commit:
112
113```bash
114gh workflow run release.yml --ref dev -f version=2.10.0.dev2026072510 -f channel=nightly -f source_sha=a087405a28d2c0991803dbd9c037dc76fd05a631
115```
116
117Use the exact commit recorded by the workflow-created tag or draft. There is no moving
118branch recovery path.
119
120- **Before publication:** The matching draft remains mutable. Complete assets are
121 downloaded and reused byte-for-byte; incomplete assets are replaced. If the exact
122 image already exists, its source, wheel digest, platforms, and OCI digest must match,
123 otherwise the rerun stops without overwriting it. Run-scoped build artifacts remain
124 available to GitHub's **Re-run failed jobs** path. A crash after exact tag creation but
125 before draft creation can resume only when the annotated tag has the workflow's exact
126 source marker and `github-actions[bot]` identity.
127- **After publication:** Tests, package builds, draft mutation, and publication are
128 skipped. The immutable release, assets, attestations, tag, and exact image are verified
129 again, then rolling aliases and downstream updates resume. If a newer release already
130 superseded this version, verification still runs but older downstream state is not
131 restored.
132
133An immutable release with incorrect contents cannot be repaired. Publish a new version
134that supersedes it. A published mutable release is also never adopted by this workflow.
135
136The add-on changelog update removes duplicate entries for the same version, prepends one
137canonical entry using the GitHub publication date, and retains three distinct releases.
138The add-on repository's default branch is resolved through GitHub, and non-fast-forward
139updates retry a bounded pull/rebase/push sequence.
140
141Frontend recovery reads the target repository's `channels.json` first. Equal or newer
142frontend state suppresses the dispatch; an older state receives a payload containing a
143stable `server@$VERSION` idempotency key, channel, frontend and server versions, source
144SHA, and image digest. The receiver does not yet persist the idempotency key itself, so
145an immediate rerun while the first dispatch is still in flight can enqueue a replacement;
146the receiver's per-channel concurrency cancels the older in-flight run.
147
148## Rollout
149
150Perform rollout in this order:
151
1521. Merge the support hardening PR for existing `music-assistant-bot` token usage.
1532. Add/approve the App's Administration read and Contents write permissions and the four
154 selected repositories above.
1553. Merge the server release-workflow PR.
1564. Enable immutable releases for `music-assistant/server`.
1575. Run a unique nightly canary and verify its release, two assets, exact multi-arch image,
158 rolling `nightly` alias, add-on update, and frontend dispatch.
159
160Keep the immutable-release setting disabled until the server PR has merged. Enabling it
161against the previous publish-first workflow can strand incomplete public releases.
162