Files
ccs/docs/release-process.md
T
Kai (Tam Nhu) TranandGitHub 107b5b5db4 fix(docker): apply red-team findings — drop :full, rc.1 soak, healthcheck, signing (#1251) (#1262)
* docs(quickstart): fix raw URL for corporate-proxy fallback (H1)

* feat(docker)!: drop :full image variant — use sibling containers on ccs-net (Q3)

No AI CLIs (claude-code/gemini-cli/grok-cli/opencode) are bundled in the image.
Use sibling containers attached to ccs-net instead.
See docker/README.md#connect-your-app-to-cliproxy.

Also removes bash from apk deps (entrypoint uses #!/bin/sh — L2).

ci(docker): publish only immutable :<ver> tag pre-smoke; promote-mutable-tags
job adds :latest/:MAJOR/:MINOR aliases only after smoke tests pass (H3)

ci(docker): smoke-test-compose-url runs network-contract.sh against the
downloaded /tmp/ccs-compose.yaml instead of re-cloning the repo (H4)

ci(docker): sign published images with cosign keyless OIDC + attach
provenance/SBOM via build-push-action (M8)

test(docker): network-contract.sh now accepts compose-file and image-ref
positional args; replaces python3 healthcheck parser with jq (L4)

* chore(release): cut every main release as rc.N prerelease, manual promote flow (H2)

- .releaserc.cjs: main branch now uses prerelease 'rc' channel — every
  semantic-release cut becomes vX.Y.Z-rc.N
- add promote-release.yml: workflow_dispatch flips rc → stable via
  'gh release edit --prerelease=false'; triggers docker promote-mutable-tags
- add docs/release-process.md: full soak + promote procedure, rollback steps,
  cosign verification command
- releaseNotesGenerator: add revert section, document chore hidden behaviour (L8)

* fix(docker): healthcheck probes both dashboard and cliproxy ports (M1)

compose.yaml healthcheck now checks :3000 and :8317 concurrently with
a 4.5s internal timeout, within Docker's 5s timeout budget.

Also:
- docs(docker): document npm lockfile ephemeral tradeoff above install
  layer; note size-budget regression test as the practical safeguard (M3)
- docs(docker): drop :full row from Choosing an image table; add sibling
  container note pointing to connect-your-app-to-cliproxy (Q3/docs)
- docs(docker): remove :full docker run block; fix release-tag sentence (Q3)
- docs(docker): fix raw URL in migration section (H1 parity)
- docs(docker): add Volume warning — 'down -v' deletes named volumes (L13)
- docs(docker): update What changes table — remove :full reference (Q3)
- docs(docker): add Image Signatures and SBOM section with cosign verify
  and sbom download commands (M8/docs)
- changelog: add Unreleased entries for rc soak, cosign signing, :full
  removal with migration guidance

* ci(docker): assert image-size budget per platform; add compose parity + breaking-change guard (M6/L9/L12)

- image-size.sh: add --platform flag; uses 'docker buildx imagetools
  inspect' to sum compressed layer sizes from registry manifest for
  linux/amd64 and linux/arm64 separately (M6)
- docker-release.yml smoke-test: runs size check for both platforms
- compose-parity.sh: diffs docker/compose.yaml vs
  docker/docker-compose.integrated.yml for image name, ports 3000/8317,
  volume mounts /root/.ccs and /var/log/ccs, ccs-net definition (L12)
- ci.yml: add compose-parity job wired to cliproxy runner (L12)
- breaking-change-guard.yml: fails PR if compose.yaml changes image name,
  network name, or container_name without a feat!/fix! commit (L9)

* chore(ci): fix cosign shell substitution — use tr instead of bash @L expansion (L6/nit)

* test(docker): fix compose-parity port regex for variable-interpolated host ports
2026-05-16 13:33:12 -04:00

93 lines
3.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CCS Release Process
CCS uses a two-phase release model: every merge to `main` auto-cuts a
pre-release rc build, and a separate manual promotion step flips it to stable.
This gives a soak window before `:latest` Docker tags and npm `@latest` reach
end users.
## Phase 1 — Automatic rc cut (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 configured as `{ name: 'main', prerelease: 'rc' }`,
semantic-release cuts a GitHub pre-release tagged `vX.Y.Z-rc.N`.
4. The npm package is published to the `rc` dist-tag
(`npm install @kaitranntt/ccs@rc`).
5. `docker-release.yml` triggers on the `release: published` event and:
- Builds the integrated image for `linux/amd64` and `linux/arm64`.
- Pushes **only the immutable** `ghcr.io/kaitranntt/ccs:X.Y.Z-rc.N` 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` is gated on `!github.event.release.prerelease`.
## Phase 2 — Manual promotion to stable
After the rc image has soaked (typically 2448 h with no reported issues):
1. Verify the rc image is healthy:
```bash
docker pull ghcr.io/kaitranntt/ccs:X.Y.Z-rc.N
docker run --rm -p 3000:3000 -p 8317:8317 ghcr.io/kaitranntt/ccs:X.Y.Z-rc.N
# 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-rc.N
```
3. Run the `promote-release` workflow via GitHub Actions UI or CLI:
```bash
gh workflow run promote-release.yml \
--field rc_tag=vX.Y.Z-rc.N
```
4. The workflow calls `gh release edit vX.Y.Z-rc.N --prerelease=false --latest`,
which fires a new `release: published` event with `prerelease=false`.
5. `docker-release.yml` picks up the stable event and the `promote-mutable-tags`
job runs, adding `:latest`, `:<MAJOR>`, `:<MINOR>` via
`docker buildx imagetools create`.
6. npm `@latest` is already set by semantic-release during the rc phase for the
stable semver portion — no separate npm step is needed.
## Verifying the promotion
```bash
# Confirm :latest points to the promoted digest
docker buildx imagetools inspect ghcr.io/kaitranntt/ccs:latest
# Confirm npm @latest updated
npm view @kaitranntt/ccs dist-tags
```
## Rollback
If a promoted release is found to be bad:
```bash
# Re-mark as prerelease on GitHub (stops new users from pulling :latest via UI)
gh release edit vX.Y.Z-rc.N --prerelease=true --latest=false
# Repoint :latest to the previous known-good version
docker buildx imagetools create \
--tag ghcr.io/kaitranntt/ccs:latest \
ghcr.io/kaitranntt/ccs:PREVIOUS.VERSION
```
## Branch / tag taxonomy
| Branch | Semantic-release channel | npm dist-tag | Docker tag |
|--------|--------------------------|--------------|------------|
| `main` | `rc` prerelease | `@rc` | `:<ver>-rc.N` (immutable only) |
| `main` (after promote) | stable | `@latest` | `:latest`, `:<MAJOR>`, `:<MINOR>`, `:<ver>` |
| `dev` | `dev` prerelease | `@dev` | not published |