mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-15 22:21:20 +00:00
Reverts the loop-1 prerelease channel that was publishing npm as vX.Y.Z-rc.N to the `rc` dist-tag instead of `latest`. `main` is now a stable semantic-release channel again: every merge publishes vX.Y.Z immediately to npm @latest. The rc.1 soak window that guards Docker mutable tags is moved entirely into the Docker publish workflow: - `.releaserc.cjs`: remove `prerelease: 'rc'` from productionConfig, restore `branches: ['main']` (stable). Restore full successComment and `released` label. Keep loop-1 releaseNotesGenerator additions (revert section, breaking change comment). - `docker-release.yml`: every `release: published` event publishes only the immutable `:<ver>` Docker tag. `promote-mutable-tags` job now gates exclusively on `workflow_dispatch` with `promote_to_latest=true` — no longer triggered automatically by non-prerelease release events. - `promote-release.yml`: rewritten as a dispatch wrapper that validates the stable tag exists and is not a prerelease, verifies the immutable Docker image is in the registry, then dispatches docker-release.yml with `promote_to_latest=true`. Removes the `gh release edit --prerelease=false` approach that required the rc soak to be wired through GitHub release state. - `docs/release-process.md`: updated to reflect the decoupled model — npm @latest is immediate; Docker :latest requires manual promote after soak. Documents the `why` split between npm and Docker soak windows.
107 lines
4.2 KiB
Markdown
107 lines
4.2 KiB
Markdown
# CCS Release Process
|
|
|
|
CCS uses a decoupled release model: every merge to `main` immediately publishes
|
|
a stable npm `@latest` release and an immutable Docker `:<ver>` tag. Docker
|
|
mutable tags (`:latest`, `:<MAJOR>`, `:<MINOR>`) require a separate manual
|
|
promote step after an operator-verified soak window. This decouples the npm
|
|
ecosystem from the Docker stability gate.
|
|
|
|
## Phase 1 — Automatic stable release (on every merge to `main`)
|
|
|
|
1. A PR is merged into `main` with a conventional commit (`feat:`, `fix:`, etc.).
|
|
2. `release.yml` triggers semantic-release, which reads `.releaserc.cjs`.
|
|
3. Because `main` is a stable channel, semantic-release cuts a GitHub release
|
|
tagged `vX.Y.Z` and publishes the npm package to the `@latest` dist-tag
|
|
immediately. No rc channel, no soak delay on npm.
|
|
4. `docker-release.yml` triggers on the `release: published` event and:
|
|
- Validates the tag as stable semver (`vX.Y.Z`).
|
|
- Builds the integrated image for `linux/amd64` and `linux/arm64`.
|
|
- Pushes **only the immutable** `ghcr.io/kaitranntt/ccs:X.Y.Z` tag.
|
|
- Signs the image with cosign (keyless OIDC).
|
|
- Runs smoke tests (`smoke-test` job).
|
|
- Mutable tags (`:latest`, `:<MAJOR>`, `:<MINOR>`) are **not** added at
|
|
this stage — `promote-mutable-tags` only runs on explicit
|
|
`workflow_dispatch` with `promote_to_latest=true`.
|
|
|
|
## Phase 2 — Manual promotion to Docker mutable tags (rc.1 soak window)
|
|
|
|
After the immutable `:<ver>` Docker image has soaked (typically 24 h with no
|
|
reported issues), the operator promotes mutable tags:
|
|
|
|
1. Verify the immutable image is healthy:
|
|
|
|
```bash
|
|
docker pull ghcr.io/kaitranntt/ccs:X.Y.Z
|
|
docker run --rm -p 3000:3000 -p 8317:8317 ghcr.io/kaitranntt/ccs:X.Y.Z
|
|
# check http://localhost:3000 and http://localhost:8317
|
|
```
|
|
|
|
2. Optionally verify the cosign signature:
|
|
|
|
```bash
|
|
cosign verify \
|
|
--certificate-identity-regexp "https://github.com/kaitranntt/ccs/.github/workflows/docker-release.yml" \
|
|
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
|
|
ghcr.io/kaitranntt/ccs:X.Y.Z
|
|
```
|
|
|
|
3. Run the `promote-release` workflow via GitHub Actions UI or CLI:
|
|
|
|
```bash
|
|
gh workflow run promote-release.yml \
|
|
--field tag=vX.Y.Z
|
|
```
|
|
|
|
This dispatches `docker-release.yml` with `promote_to_latest=true`, which
|
|
triggers the `promote-mutable-tags` job to add `:latest`, `:<MAJOR>`, and
|
|
`:<MINOR>` via `docker buildx imagetools create`.
|
|
|
|
Alternatively, dispatch `docker-release.yml` directly:
|
|
|
|
```bash
|
|
gh workflow run "Publish Docker Image" \
|
|
--field tag=vX.Y.Z \
|
|
--field promote_to_latest=true
|
|
```
|
|
|
|
## Why npm and Docker have different soak windows
|
|
|
|
- **npm `@latest`**: Published immediately on every `main` merge. npm users who
|
|
pin a version are unaffected; users who run `npm install -g @kaitranntt/ccs`
|
|
get the latest immediately. Rollback is `npm install -g @kaitranntt/ccs@X.Y.Z`.
|
|
- **Docker `:latest`**: Promoted only after operator confirmation. Users who
|
|
pull `:latest` or run `docker pull` without a pinned tag are shielded from
|
|
a bad image. The immutable `:<ver>` tag is always available for pinned usage
|
|
from the moment of release.
|
|
|
|
## Verifying the promotion
|
|
|
|
```bash
|
|
# Confirm :latest points to the promoted digest
|
|
docker buildx imagetools inspect ghcr.io/kaitranntt/ccs:latest
|
|
|
|
# Confirm npm @latest updated (happens automatically at Phase 1)
|
|
npm view @kaitranntt/ccs dist-tags
|
|
```
|
|
|
|
## Rollback
|
|
|
|
If a promoted release is found to be bad:
|
|
|
|
```bash
|
|
# Repoint :latest to the previous known-good immutable tag
|
|
docker buildx imagetools create \
|
|
--tag ghcr.io/kaitranntt/ccs:latest \
|
|
ghcr.io/kaitranntt/ccs:PREVIOUS.VERSION
|
|
|
|
# For npm, publish a fix as a new patch release (do not unpublish)
|
|
# Unpublishing npm packages causes downstream breakage for pinned consumers.
|
|
```
|
|
|
|
## Branch / tag taxonomy
|
|
|
|
| Branch | Semantic-release channel | npm dist-tag | Docker tag (on release event) | Docker mutable (on promote) |
|
|
|--------|--------------------------|--------------|-------------------------------|------------------------------|
|
|
| `main` | stable | `@latest` | `:<ver>` (immutable, immediate) | `:latest`, `:<MAJOR>`, `:<MINOR>` (after soak) |
|
|
| `dev` | `dev` prerelease | `@dev` | not published | not published |
|