#783 added `type=semver,pattern=v{{version}}` to the merge-job metadata,
but metadata-action silently dropped it on prereleases — the 3.84.0-beta.0
build published only :3.84.0-beta.0 + :sha, not :v3.84.0-beta.0 (verified
in the merge-backend push log + GHCR: :v3.84.0-beta.0 → 404).
Replace the v{{version}}/v{{major}} semver patterns with type=ref,event=tag,
which emits the git-tag name verbatim (v3.45.0 / v3.84.0-beta.0) for both
stable and beta tags — exactly the string users pin (matches the GitHub
release). Applies to both backend + frontend merge metadata steps.
Takes effect on the next release build. The bare :3.84.0-beta.0 tags stay
(the {{version}} patterns are unchanged), so both forms resolve.
The two release-please tracks count independently — main bumps on every
merge, stable only on promotion — so they drifted far apart (main
v3.83.x-beta while stable sat at v3.45.0 for the same code). Document
the alignment convention: a promotion pins the stable version to main's
base version via a Release-As commit (new step 5 in the cut procedure),
so stable tracks main instead of lagging.
Also records the release-engineering note that release-please.yml must
keep target-branch: stable (the missing pin cut a bogus v2.7.0 once).
Adds a subtle 'View PicPeak on GitHub' link in the admin sidebar footer
(next to the version/storage widgets), so admins can reach the repo —
star it, browse source, report an issue — from anywhere in the dashboard,
not just the setup screen.
- Centralizes the repo URL as `repoUrl` in utils/githubReleaseUrl.ts
(githubReleaseUrl now derives from it) so the org URL lives in one place.
- target=_blank + rel=noopener noreferrer; EN + DE i18n
(`admin.viewOnGithub`); dark-mode aware, matches the muted footer style.
docker/metadata-action's type=semver strips the leading 'v', so releases
published only :3.45.0 / :3.83.1-beta.0. But git tags + GitHub releases
are named v3.45.0, so anyone pinning ghcr.io/.../backend:v3.45.0 (the
obvious choice) hit 'manifest unknown' — exactly #664.
Add v-prefixed semver patterns (v{{version}}, v{{major}}.{{minor}},
v{{major}}) alongside the existing bare ones, for both backend and
frontend. Now both :v3.45.0 and :3.45.0 resolve.
Applies to future releases; the already-published v3.45.0 only has the
bare :3.45.0 tag (retagging past releases is out of scope).
The stable release-please workflow (release-please.yml, triggered on
push to stable) had no `target-branch`, so it defaulted to the repo
default branch (main) and computed the next version from main's stale
`.release-please-manifest.json` (2.6.1) — cutting a spurious **v2.7.0**
stable release (a version regression from 3.44.0) when #771 landed on
stable, and bumping main's package.json + manifest to 2.7.0.
- release-please.yml: add `target-branch: stable` so it releases from
the stable branch (3.44.0 → 3.45.0), like release-please-beta.yml
already pins `target-branch: main`.
- Restore main's version to 3.83.0-beta.0 (backend + frontend
package.json), set `.release-please-manifest.json` to 3.44.0, and drop
the bogus 2.7.0 CHANGELOG section.
The v2.7.0 tag/release is deleted separately; the real v3.45.0 stable is
cut by re-running release-please on the stable branch after this lands.
tests.yml (the backend/frontend Jest+Vitest jobs) only triggered on
main/beta, but those two jobs are required status checks on the stable
branch. A beta→stable promote PR therefore hung forever on
'Expected — Waiting for status to be reported' for backend/frontend,
while docker-build / install-smoke / schema-drift (already listing
stable) ran fine. Add stable to the push + pull_request filters so the
Tests suite runs on promote PRs too.
2026-07-08 20:29:19 +02:00
13 changed files with 93 additions and 13 deletions
* **releasing:** align stable version to main on promote (Option A) ([df5aeab](https://github.com/PicPeak/picpeak/commit/df5aeaba416726cc0123f32ddf88e4a30dc28908))
* **releasing:** align stable version to main on promote (Option A) ([5dea0c9](https://github.com/PicPeak/picpeak/commit/5dea0c969558f50833973ff742257780f5842612))
@@ -52,13 +52,19 @@ The actual mechanics, in order:
- **`.release-please-manifest.json`** — keep `stable`'s; release-please owns this file.
- Any other auto-merged file — spot-check that the auto-merge produced something sensible, especially for security-sensitive files (`backend/src/middleware/`, `backend/src/utils/tokenUtils.js`).
5. **Wait for CI on the PR.** All ten checks (the original eight plus `merge-backend` and `merge-frontend`) must be green. If anything fails, fix on the release branch (NOT on `main` — `main` has already moved on).
5. **Pin the stable version to match `main` (number alignment — see Versioning).** Determine `X.Y.Z` = the `main` tip's **base** version (its `vX.Y.Z-beta.N`, dropping the `-beta.N` suffix), and add an empty commit on the release branch:
The `Release-As:` footer forces release-please to cut exactly `X.Y.Z`. Without it, release-please computes the next MINOR from the *previous stable* tag (e.g. `3.45.0` → `3.46.0`) while `main` is already at `3.84.x`, so the stable number drifts ever further behind for the same code.
6. **Merge.** Standard merge commit, not squash — the PR's history (the individual feature commits) carries forward into `stable`'s log.
6. **Wait for CI on the PR.** All ten checks (the original eight plus `merge-backend` and `merge-frontend`) must be green. If anything fails, fix on the release branch (NOT on `main` — `main` has already moved on).
7. **release-please picks it up.** Within minutes, release-please will open a new `chore(stable): release X.Y.Z` PR proposing the stable release. Review the auto-generated CHANGELOG.md entries for accuracy, edit if needed, and merge. That merge creates the `vX.Y.Z` git tag, publishes Docker images on the `:stable` and `:latest` tags, and creates the GitHub Release page.
7. **Merge.** Standard merge commit, not squash — the PR's history (the individual feature commits) carries forward into `stable`'s log.
8. **Close the loop.** Bulk-close any `bug` issues that were fixed-but-not-closed and now appear in the released changelog. Reference the merge commit so reporters know which version contains the fix.
8. **release-please picks it up.** Within minutes, release-please will open a new `chore(stable): release X.Y.Z` PR proposing the stable release. Review the auto-generated CHANGELOG.md entries for accuracy, edit if needed, and merge. That merge creates the `vX.Y.Z` git tag, publishes Docker images on the `:stable` and `:latest` tags, and creates the GitHub Release page.
9. **Close the loop.** Bulk-close any `bug` issues that were fixed-but-not-closed and now appear in the released changelog. Reference the merge commit so reporters know which version contains the fix.
## Hotfix path (backport to current stable)
@@ -83,6 +89,14 @@ PicPeak follows [Semantic Versioning](https://semver.org/) with one project-spec
release-please derives all of this from conventional commit prefixes (`feat:`, `fix:`, `BREAKING CHANGE:`, etc.) automatically.
### Stable ↔ pre-release number alignment
The two channels run **independent** release-please counters: `main` bumps on every merge (racing ahead), while `stable` only bumps on a promotion. Left to itself, `stable` computes each promotion as the next MINOR from the *previous stable tag*, so the two drift far apart — e.g. `main` at `v3.83.x-beta.0` while `stable` sat at `v3.45.0` for the **same code**, which reads as "stable is 38 versions behind" when it isn't.
To keep the numbers legible, **a promotion sets the stable version to the current `main` base version** (the `X.Y.Z` of the `main` tip's `vX.Y.Z-beta.N`, minus the suffix). Promoting a `main` at `v3.84.2-beta.0` therefore cuts stable `v3.84.2`, and the stable number tracks `main` instead of lagging. This is forced with the `Release-As:` commit in step 5 of the cut procedure — the one-time catch-up jump (e.g. `3.45.0 → 3.84.x`) is expected and happens only on the first aligned promotion.
> **Release-engineering note (2026-07):** `release-please.yml` (the stable workflow) *must* keep `target-branch: stable`. Without it, release-please defaults to the repo's default branch (`main`), reads `main`'s stale `.release-please-manifest.json`, and cuts a wrong/regressed version — this is what produced a bogus `v2.7.0` once. If a promotion ever yields an unexpected version, check that first.
## Things that don't go through this process
- **Documentation-only changes** can land on either `stable` or `main` directly (no release cut needed); release-please will pick them up on the next regular release.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.