mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 14:16:43 +00:00
* 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
93 lines
3.4 KiB
Markdown
93 lines
3.4 KiB
Markdown
# 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 24–48 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 |
|