Skip to content

Commit 79682f4

Browse files
committed
fix(release): keep GHCR latest on stable images
Publish prerelease self-hosted Web images under explicit version and SHA tags while reserving the mutable GHCR latest alias for stable tag refs. Refs: - Change: spec/changes/active/release-0-6-0-version-alignment - Trace: FR-REL-003 / DES-REL-004 / TEST-REL-006 / T-REL-006 Verification: - focused workflow regression: red then 2/2 passed - pnpm verify:release: 42/42 passed - pnpm spec:traceability: passed - git diff --check: passed
1 parent 532424f commit 79682f4

8 files changed

Lines changed: 74 additions & 7 deletions

File tree

.github/workflows/web-self-hosted.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ jobs:
8484
tags: |
8585
type=semver,pattern={{version}}
8686
type=semver,pattern=v{{version}}
87-
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
87+
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-') }}
8888
type=ref,event=tag
8989
type=sha
9090

apps/desktop/tests/unit/scripts/web-self-hosted-workflow.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ const webPackage = JSON.parse(
1515
) as { version: string };
1616

1717
describe("self-hosted Web release workflow", () => {
18-
it("publishes Web images from the standard Desktop release tag", () => {
18+
it("publishes versioned Web images from release tags without moving stable latest for prereleases", () => {
1919
expect(workflowSource).toContain('- "packages/core/**"');
2020
expect(workflowSource).toContain('- "v*"');
2121
expect(workflowSource).toContain("type=semver,pattern={{version}}");
2222
expect(workflowSource).toContain("type=semver,pattern=v{{version}}");
2323
expect(workflowSource).toContain(
24+
"type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-') }}",
25+
);
26+
expect(workflowSource).not.toContain(
2427
"type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}",
2528
);
2629
});

docs/web-self-hosted.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,17 @@ Useful root-level commands:
167167

168168
`apps/web` already includes a production `Dockerfile` and ready-to-use compose files.
169169

170-
When a standard `v<version>` release tag is built in CI, PromptHub also
171-
publishes a container image to GHCR:
170+
When a `v<version>` release tag is built in CI, PromptHub also publishes a
171+
container image to GHCR:
172172

173173
- `ghcr.io/legeling/prompthub-web:<version>`
174174
- `ghcr.io/legeling/prompthub-web:v<version>`
175-
- `ghcr.io/legeling/prompthub-web:latest`
175+
- `ghcr.io/legeling/prompthub-web:latest` for stable tags only
176+
177+
Prerelease tags such as `v0.6.0-beta.1` publish their explicit versioned image
178+
without moving `latest`. Use the explicit prerelease tag for manual beta
179+
testing; production deployments that track `latest` remain on the latest
180+
stable image.
176181

177182
### Quick Start with Docker Compose
178183

@@ -210,6 +215,8 @@ multiple containers against the same data volume.
210215

211216
### Deploy from the Published GHCR Image
212217

218+
The `latest` alias below tracks stable releases only.
219+
213220
```bash
214221
docker pull ghcr.io/legeling/prompthub-web:latest
215222
docker run -d \

spec/changes/active/release-0-6-0-version-alignment/design.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ Add a `0.6.0` release record with `Preparation` status and an `Unreleased`
2828
changelog summary. Do not change localized README stable badges or fixed
2929
download links until publication.
3030

31+
## `DES-REL-004`: Stable GHCR Alias Isolation
32+
33+
The Self-Hosted Web tag workflow always publishes immutable prerelease tags
34+
such as `0.6.0-beta.1`, `v0.6.0-beta.1`, the source tag, and commit SHA. It
35+
publishes the mutable `latest` alias only when `github.ref_name` has no semver
36+
prerelease suffix. The decision is constant-time string classification and does
37+
not change image contents, build inputs, registry authentication, or the
38+
stable-versioned tags already present in GHCR.
39+
3140
## Affected Areas
3241

3342
- Data model: none
@@ -68,3 +77,4 @@ download links until publication.
6877
| `FR-REL-001` | `DES-REL-001` | `TEST-REL-001` | `T-REL-001` |
6978
| `FR-REL-002` | `DES-REL-002`, `DES-REL-003` | `TEST-REL-002`, `TEST-REL-003`, `TEST-REL-005` | `T-REL-002`, `T-REL-003` |
7079
| `NFR-REL-001` | `DES-REL-002` | `TEST-REL-003`, `TEST-REL-004` | `T-REL-002` |
80+
| `FR-REL-003` | `DES-REL-004` | `TEST-REL-006` | `T-REL-006`, `T-REL-007` |

spec/changes/active/release-0-6-0-version-alignment/implementation.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@
1616
- The `0.6.0` preparation record and unreleased changelog note are synchronized.
1717
- Public website badges, introduction copy, and download URLs remain on the
1818
published `0.5.9` stable release.
19+
- During the `v0.6.0-beta.1` replacement, the first Self-Hosted Web run was
20+
cancelled before its Docker job because the existing any-`v*` metadata rule
21+
would also publish the mutable GHCR `latest` alias. No beta image was pushed
22+
by that cancelled run.
23+
- The GHCR metadata rule now emits `latest` only for a tag ref whose ref name
24+
has no prerelease suffix. Beta images retain explicit semver, source-tag and
25+
commit-SHA tags.
1926

2027
## Verification
2128

@@ -51,6 +58,21 @@
5158
lines each, above the preferred 1500-line limit. All shared, database, core,
5259
CLI, Desktop, Web, Worker, and Mobile typecheck/test/lint checks in the
5360
profile passed.
61+
- `TEST-REL-006` first failed against the any-`v*` raw `latest` rule, then
62+
passed after the stable-ref guard was added. The cancelled Web run completed
63+
Web verification and Docker Compose validation; its Docker metadata and
64+
build-push steps never started.
65+
- The corrected candidate passed `pnpm verify:release` with 42/42 checks, zero
66+
failed or blocked checks, and a maximum concurrency of two. Performance,
67+
Desktop unit/integration/build/bundle/E2E, CLI/Web builds, Web smoke,
68+
Cloudflare dry-run and Mobile gates all passed.
69+
- Read-only GHCR inspection confirmed the pre-existing `latest` alias still
70+
points at withdrawn beta revision `2ed96c7f` with manifest digest
71+
`sha256:c9b4c4f3b7a463146685144a4f4f4b8ef23e2b07c5d16c9d51a9c1e2fcd33664`.
72+
The explicit stable `0.5.9` image remains available at digest
73+
`sha256:e8d6214030a27090a443ca38d4b5f07d4331af09d9a20b71e5138090bd8a2e40`;
74+
`T-REL-007` must restore `latest` to that stable image before beta
75+
publication completes.
5476

5577
## Analyze
5678

@@ -81,5 +103,8 @@ this change has no visible product UI delta.
81103

82104
## Follow-ups
83105

84-
- Tagging, signing, artifact publication, and remote verification are separate
85-
publication tasks and are not performed by this version-alignment change.
106+
- Complete `T-REL-006` and retag the replacement candidate, then verify the
107+
versioned beta image is present while stable `latest` keeps the `0.5.9`
108+
manifest digest.
109+
- Desktop signing, artifact publication, and promotion remain governed by the
110+
`v0.6.0-beta.1` replacement release change.

spec/changes/active/release-0-6-0-version-alignment/proposal.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ advertising nonexistent `0.6.0` downloads.
2727
- record `0.6.0` as a release preparation
2828
- keep public stable downloads and badges on the latest explicit stable
2929
release record
30+
- publish prerelease Web images under versioned GHCR tags without moving the
31+
stable `latest` image
3032
- verify the explicit public stable release boundary
3133
- Out of scope:
3234
- creating or pushing a `v0.6.0` tag
@@ -38,6 +40,8 @@ advertising nonexistent `0.6.0` downloads.
3840

3941
- A missed product manifest could produce artifacts with inconsistent versions.
4042
- Premature website synchronization could create broken `0.6.0` download links.
43+
- A prerelease tag could overwrite GHCR `latest` and silently move stable
44+
self-hosted deployments onto a beta image.
4145
- The dirty worktree contains unrelated feature work; release edits must remain
4246
isolated and must not rewrite those files.
4347

spec/changes/active/release-0-6-0-version-alignment/specs/release/spec.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@ Release metadata selection must scan the release index and changelog linearly,
3434
use bounded working memory, and fail explicitly when the published stable
3535
record or its dated changelog entry is missing.
3636

37+
### `FR-REL-003`: Prerelease GHCR Isolation
38+
39+
Self-Hosted Web prerelease tags MUST publish versioned GHCR images without
40+
moving the mutable stable `latest` alias.
41+
42+
#### `AC-REL-004`
43+
44+
Given a tag such as `v0.6.0-beta.1`, the workflow emits prerelease-version and
45+
commit-specific image tags but does not emit `latest`. Given a stable tag such
46+
as `v0.6.0`, the same workflow emits `latest` in addition to versioned tags.
47+
3748
## Verification
3849

3950
- `TEST-REL-001`: assert all authoritative product manifests equal `0.6.0`.
@@ -45,3 +56,5 @@ record or its dated changelog entry is missing.
4556
entries fail explicitly.
4657
- `TEST-REL-005`: run website synchronization and verify generated public
4758
metadata remains on the latest published stable version.
59+
- `TEST-REL-006`: assert the Self-Hosted Web workflow gates the raw `latest`
60+
image tag on a stable ref name and rejects the previous any-`v*` condition.

spec/changes/active/release-0-6-0-version-alignment/tasks.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,8 @@
1515
actual results and residual release risks.
1616
- [x] `T-REL-005` Complete converge records; keep the change active until
1717
`0.6.0` publication is complete.
18+
- [x] `T-REL-006` Add the failing GHCR prerelease-isolation regression, then
19+
restrict the mutable `latest` alias to stable tag refs (`FR-REL-003`,
20+
`DES-REL-004`, `TEST-REL-006`).
21+
- [ ] `T-REL-007` Verify the corrected tag workflow publishes beta-versioned
22+
images without changing stable `latest`, then record remote evidence.

0 commit comments

Comments
 (0)