/
/
/
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 on nightly the
55 `music_assistant_dev` add-on as well, then dispatch the frontend version from
56 `source_sha` to `app.music-assistant.io`.
57
58The exact image tag is already the full version (`X.Y.Z`, `X.Y.ZbN`, `X.Y.ZrcN`, or
59`X.Y.Z.devN`). Rolling aliases are never pushed before the immutable release verifies.
60Normal channel releases are forward-only. This workflow does not define a legacy-branch
61backport process for publishing an older version after a newer channel release.
62
63## Starting a release
64
65The scheduled workflow checks nightly releases automatically. For a manual version:
66
671. Run **Auto Release** from the `dev` branch.
682. Select `stable`, `rc`, `beta`, or `nightly`.
693. Add important notes when needed.
70
71Auto-release calculates the next version and invokes **Create Release** with its captured
72source SHA. **Create Release** can also be dispatched directly with an explicit version;
73for direct runs it resolves and freezes the current channel branch head itself unless you
74pass `source_sha` to recover an exact draft or published release source.
75
76Do not create or publish a GitHub release manually. A draft created outside the workflow
77is accepted only when its exact tag name and target SHA match; conflicting tags,
78published mutable releases, and mismatched drafts fail closed.
79
80## Authentication
81
82Same-repository tags, releases, assets, attestations, and GHCR writes use the job's
83built-in `GITHUB_TOKEN` and `github-actions[bot]`.
84
85Administrative and cross-repository work uses a fresh installation token in each job
86from the private `music-assistant-bot` GitHub App. Tokens are never passed between jobs
87and are restricted to one repository and the permission needed by that job.
88
89The server repository must define:
90
91- Repository variable `MUSIC_ASSISTANT_BOT_CLIENT_ID`
92- Repository secret `MUSIC_ASSISTANT_BOT_PRIVATE_KEY`
93
94The App installation must include these selected repositories:
95
96| Repository | Token permission used by this workflow |
97|---|---|
98| `music-assistant/server` | Administration: read |
99| `music-assistant/appvars` | Contents: read |
100| `music-assistant/home-assistant-addon` | Contents: write |
101| `music-assistant/app.music-assistant.io` | Contents: write |
102
103The App itself must be approved for Administration read and Contents write. Each minted
104token is downscoped from those installation permissions. The workflow verifies that the
105token's App slug is `musicassistant-bot` and its installation ID is `146062122`.
106
107## Recovery
108
109Rerun the failed workflow with the same version and `source_sha`.
110
111If you need to resume an exact draft or published release after the branch has advanced,
112reuse the workflow-created source SHA from the matching tag or draft target commit:
113
114```bash
115gh workflow run release.yml --ref dev -f version=2.10.0.dev2026072510 -f channel=nightly -f source_sha=a087405a28d2c0991803dbd9c037dc76fd05a631
116```
117
118Use the exact commit recorded by the workflow-created tag or draft. There is no moving
119branch recovery path.
120
121- **Before publication:** The matching draft remains mutable. Complete assets are
122 downloaded and reused byte-for-byte; incomplete assets are replaced. If the exact
123 image already exists, its source, wheel digest, platforms, and OCI digest must match,
124 otherwise the rerun stops without overwriting it. Run-scoped build artifacts remain
125 available to GitHub's **Re-run failed jobs** path. A crash after exact tag creation but
126 before draft creation can resume only when the annotated tag has the workflow's exact
127 source marker and `github-actions[bot]` identity.
128- **After publication:** Tests, package builds, draft mutation, and publication are
129 skipped. The immutable release, assets, attestations, tag, and exact image are verified
130 again, then rolling aliases and downstream updates resume. If a newer release already
131 superseded this version, verification still runs but older downstream state is not
132 restored.
133
134An immutable release with incorrect contents cannot be repaired. Publish a new version
135that supersedes it. A published mutable release is also never adopted by this workflow.
136
137The add-on changelog update removes duplicate entries for the same version, prepends one
138canonical entry using the GitHub publication date, and retains three distinct releases.
139The add-on repository's default branch is resolved through GitHub, and non-fast-forward
140updates retry a bounded pull/rebase/push sequence.
141
142A nightly release also sets the `music_assistant_dev` add-on to the same version, in the
143same commit. That add-on has no prebuilt image: the Supervisor builds it locally from
144`ghcr.io/music-assistant/server:nightly`, so moving its version is what offers installed
145add-ons the rebuild that picks up the new base image and the current wrapper files. It
146gets no changelog entry, because it runs whichever source branch the user configured
147rather than the published release.
148
149Frontend recovery reads the target repository's `channels.json` first. Equal or newer
150frontend state suppresses the dispatch; an older state receives a payload containing a
151stable `server@$VERSION` idempotency key, channel, frontend and server versions, source
152SHA, and image digest. The receiver does not yet persist the idempotency key itself, so
153an immediate rerun while the first dispatch is still in flight can enqueue a replacement;
154the receiver's per-channel concurrency cancels the older in-flight run.
155
156## Rollout
157
158Perform rollout in this order:
159
1601. Merge the support hardening PR for existing `music-assistant-bot` token usage.
1612. Add/approve the App's Administration read and Contents write permissions and the four
162 selected repositories above.
1633. Merge the server release-workflow PR.
1644. Enable immutable releases for `music-assistant/server`.
1655. Run a unique nightly canary and verify its release, two assets, exact multi-arch image,
166 rolling `nightly` alias, add-on update, and frontend dispatch.
167
168Keep the immutable-release setting disabled until the server PR has merged. Enabling it
169against the previous publish-first workflow can strand incomplete public releases.
170