-
-
Notifications
You must be signed in to change notification settings - Fork 1
Release Policy
This is the policy the publish workflows and the changelog point at. It defines the three-part version scheme, the two release channels, and — the substance of this document — the beta soak: the formal canary stage a build passes through before a stable tag is cut.
For an authentication plugin a bad stable release is a mass-lockout event (see Rollback). The soak stage is the availability control that sits before rollback: it puts every stable candidate in front of real, opt-in beta installs for a defined window so a load-time or login-path regression is caught on beta testers, not on the whole fleet.
Versions are three-part X.Y.Z, class-encoded:
- X — breaking (Jellyfin ABI / platform), Y — feature, Z — bug-fix or security (the two share the digit and differ by release cadence, not by number).
The maintainer sets the next release target X.Y.Z in build.yaml
(build-jf12.yaml for the JF12 line) per the change class of what has landed —
a bug-fix or security patch bumps Z, a feature bumps Y, a breaking change
bumps X. That target is what the beta builds toward and what the matching
-stable tag releases; the publish job checks the tag's numeric prefix equals
build.yaml's version.
The channel and Jellyfin generation live only in the git tag / GitHub release
name as a suffix (-stable, -beta.<run>, -JF12-beta, -JF12-stable), never
in the installed numeric version — Jellyfin plugin versions must be numeric. A beta
is built as X.Y.Z.<run>, where the fourth octet is only the workflow run number
that keeps the tag unique and the update order monotonic; a stable is X.Y.Z.
Jellyfin has no in-manifest channel flag, so the channels are separate manifest branches:
| Channel | Manifest branch | ignorePrereleases |
Trigger |
|---|---|---|---|
| beta | manifest-beta |
false |
Daily scheduler (nightly-betas.yml) |
| stable | manifest-release |
true |
A maintainer-pushed -stable tag |
-
Beta is minted by
publish-beta.yml(JF10.11, frommain) andpublish-jf12-beta.yml(JF12, from4.2), which the dailynightly-betas.ymlscheduler dispatches at 04:00 UTC. Each leg has a gate job that skips the build unless its branch advanced since that lineage's last beta, so an unchanged day mints nothing. The beta version is the next targetX.Y.Zwith the workflow run number as the fourth octet (X.Y.Z.<run>), released as the prereleaseX.Y.Z-beta. -
Stable is cut only by a maintainer pushing a tag by hand —
X.Y.Z-stable(publish.yml, JF10.11) orX.Y.Z-JF12-stable(publish-jf12-stable.yml, the 5.0 line, dormant until Jellyfin 12.0 GA). The tag push is protected-branch / version-tag gated byguard-git.ps1. There is no automatic promotion from beta to stable — the promotion gate below is a human gate, and the stable tag is the act of clearing it.
Releases are immutable (issue #146): assets are sealed on publish and tags are single-use. That is why the beta job uses a draft → attach → publish flow and why a burned tag can never be re-cut.
Each stable version climbs this ladder before it reaches the stable manifest. This is a per-release ladder (distinct from the product-wide maturity ladder in the README — In-Development → Alpha → … — which describes the plugin as a whole).
| Rung | Where it lives | Audience | What it means |
|---|---|---|---|
| Unreleased |
main HEAD (or 4.2 for JF12) |
CI only | Merged, green, but not yet built for install. |
| Beta (canary soak) |
manifest-beta, X.Y.Z-beta
|
Opt-in beta installers | Installable prerelease of the exact commit a stable would tag. |
| Stable |
manifest-release, X.Y.Z-stable
|
Everyone on the stable URL | Promoted after clearing the soak + the promotion gate. |
The beta build is the canary. It is a real installable build of the same
commit a stable tag would point at, served automatically to everyone on the beta
repository URL. Promoting it to stable is publishing the identical source under a
-stable tag; the soak is the observation window in between.
Default N = 7 calendar days. A stable candidate must have been published as
the newest beta on manifest-beta, unchanged, for at least N days with no
regression report filed against it before its -stable tag is pushed.
Definition and rules:
-
Newest-and-unchanged. The soak clock is on the specific commit the stable
tag will point at. To run the soak, ensure the current newest beta is built
from that commit (dispatch
publish-beta.ymlifmainhas moved past the last daily beta), then holdmain— merge only fixes that are themselves stable-blockers. - A new beta resets the clock. If any commit lands during the window (a soak fix or otherwise), a new beta supersedes the candidate and the N-day clock restarts on the new commit. You cannot promote a commit older than the newest soaked beta.
- "No regression report" means no open bug attributing a fault to that beta (or a newer beta) — a load failure, a broken login path, or any behaviour the previous stable did not have. Pre-existing, unrelated open issues do not block.
- Install count is not measured. This plugin ships no telemetry by design (compliance/privacy posture), so the gate is time-plus-evidence, not an install counter: the N-day window plus the manual E2E checklist (§5) stand in for a measured install threshold. N is a floor the maintainer may lengthen for a risky change; §6 is the only rule that shortens it.
All of the following must hold before pushing an X.Y.Z-stable (or
X.Y.Z-JF12-stable) tag. This is the checklist /release-prep runs, and it
composes the two existing pre-publish checklists rather than duplicating them.
- Soak satisfied. The candidate commit's beta has been the newest
manifest-betabuild for ≥ N days (default 7) with no regression report against it (§4) — or §6's security-shortening rule is invoked with a written reason. - Manual E2E checklist passed against the beta build on a real server: every item in Release QA Checklist (real-IdP OIDC/SAML login, proxy attribution, packaging/install/upgrade, dashboard rendering). These are exactly the behaviours CI cannot exercise, so the soak is where they are verified.
- Rollback path confirmed. The pre-publish downgrade smoke check in
Rollback §7
passes: the new manifest retains prior versions and a downgrade to the
previous stable still loads and logs in without secret re-entry (or, if a
format boundary is crossed, Rollback §3 and
CHANGELOG.mdare updated). - Version matches the tag.
build.yaml(build-jf12.yamlfor JF12)versionequals the tag's numericX.Y.Zprefix (the publish job also enforces this) and the digit bumped matches the change class (X/Y/Z). - Changelog updated.
CHANGELOG.mdhas the release's entry.
Clearing every box is the precondition for the tag push; the tag push is the promotion. Nothing promotes a beta automatically.
A fix for an exploitable vulnerability or an active mass-lockout regression may shorten N — down to zero — because shipping the fix fast outweighs a full soak. When N is shortened:
- Record the reason in the release notes and
CHANGELOG.md(a security fix shares theZdigit with a bug-fix, so the entry is what documents the class). - Still run the §5 manual E2E checklist and the Rollback §7 downgrade smoke — those guard against the fix itself causing a mass-lockout, which the soak would otherwise have caught. The soak time is what shortens, not the E2E gate.
- Prefer fix-forward (Rollback §6): a higher patch version pulls the fleet forward. A shortened soak is the release-side counterpart to a fix-forward.
The soak lowers the probability that a stable needs rollback; it does not remove the need for one. The two are complementary controls:
- Before promotion: the soak (this document) is the preventive gate.
-
After a bad promotion: Rollback is the containment —
fix-forward with a higher patch version, or de-list the bad version from
manifest-release, never delete the immutable release or reuse its tag.
The Rollback §7 downgrade smoke is shared by both: it is a promotion-gate item here and the rollback runbook's pre-publish check, so the rollback path is proven to work on the same candidate the soak just cleared.
Issue #146 (done) replaced the old mutable rolling nightly tag with the
immutable daily beta channel used here: the scheduler mints a dated,
single-use X.Y.Z-beta.<run> release into manifest-beta instead of moving one
tag. This soak policy is defined on that redesigned channel — the canary is the
daily beta, not a rolling nightly tag. The JF12 line (publish-jf12-beta.yml →
X.Y.Z-JF12-beta, shared manifest-beta) soaks the same way; its stable leg
stays dormant until Jellyfin 12.0 GA, at which point a X.Y.Z-JF12-stable tag
clears this same gate.
Repository · Issues · Releases · Security policy - report vulnerabilities privately, never in a public issue. Pages describe what is implemented today; if the wiki disagrees with the code, the code wins.
Getting started
- Installation
- Provider Setup
- Hardening & Options Reference
- Migrating from 9p4
- Troubleshooting
- Rollback
How it works
Security
- Security Model
- Security Conformance (ASVS / RFC 9700)
- SSO-Only Login - design record
- Single Logout - design record
Standards & process (internal / maintainer)