Commit Graph
5 Commits
Author SHA1 Message Date
Kai (Tam Nhu) TranandGitHub a4e16e0b09 fix: tighten parity check + catch service-key rename (#1261 loop 3) (#1274)
* fix(test): tighten compose-parity image-name match to exact expected values (REV7)

Replace loose substring grep (grep -q "ccs") with exact equality checks
against declared EXPECTED_CANONICAL_IMAGE and EXPECTED_INTEGRATED_IMAGE
constants. The integrated compose builds locally as ccs-cliproxy:latest
(not ghcr.io/kaitranntt/ccs), so both expected names are explicitly
documented at the top of the assertion block.

Fixes: a drift in integrated compose to a wrong owner/registry could
slip through the old "grep -q ccs" check; now any name other than the
declared constant is a hard failure.

* feat(ci): breaking-change-guard catches services.ccs rename — public DNS contract (REV8)

Docker's service-name DNS uses the compose service KEY as the hostname.
Sibling containers on ccs-net reach CCS via http://ccs:8317; renaming
services.ccs: to anything else silently breaks that contract even when
image name, network name, and container_name are unchanged.

Add check 4 to the guard:
- Extract top-level service keys from both base and HEAD versions of
  docker/compose.yaml using awk (no external YAML parser required).
- Fail if the "ccs" key is absent from HEAD.
- Fail if the sorted set of service keys differs from base.

Wraps inside the existing git cat-file guard so new-file PRs skip it.
2026-05-17 05:12:04 -04:00
Kai (Tam Nhu) TranandGitHub adf5a836fc fix: address reviewer findings round 2 (#1261 loop 2) (#1272)
* fix(docker): parameterize image with CCS_IMAGE env so smoke-test exercises new digest

docker/compose.yaml hardcoded image: ghcr.io/kaitranntt/ccs:latest, causing
network-contract.sh to always use the old published image regardless of what
IMAGE_OVERRIDE was passed. Switching to \${CCS_IMAGE:-ghcr.io/kaitranntt/ccs:latest}
lets CI pass CCS_IMAGE=<digest> so the smoke-test actually exercises the
just-built image. Default end-user behaviour is unchanged.

Resolves REV4 (reviewer loop 2, PR #1261).

* fix(test): image-size.sh fails loudly on manifest inspection failure

The --platform branch previously exited 0 when imagetools inspect returned
empty or zero bytes, silently passing the budget check. A broken image
could reach production undetected if buildx had any inspection issue.

Changed to exit 1 with a clear diagnostic message explaining possible causes
(old buildx, manifest format mismatch, image not yet pushed). No --allow-inspect-failure
escape hatch is provided; CI must fix the root cause.

Resolves REV5 (reviewer loop 2, PR #1261).

* test(docker): cover --platform branch in image-size-logic tests

Prior tests only exercised the local docker image inspect path. The
--platform branch (imagetools inspect) had zero coverage, meaning the
REV5 silent-pass regression would not have been caught by CI.

Added 5 mock-based test cases for the --platform branch:
- pass when platform-scoped compressed size < budget
- fail when platform-scoped compressed size > budget
- fail (exit 1) when imagetools inspect errors (REV5 guard)
- fail (exit 1) when reported size is "0" (REV5 guard)
- fail (exit 1) when size output is empty (REV5 guard)

Mocks follow the existing pattern (override docker in PATH with a temp
wrapper), extended to handle buildx imagetools inspect subcommand.

Resolves REV6 (reviewer loop 2, PR #1261).

* fix(test): compose-parity image_name() handles \${VAR:-default} syntax

After parameterizing compose.yaml's image field with \${CCS_IMAGE:-...},
the image_name() sed pipeline cut at the first colon in the shell variable
syntax (:-) instead of the tag separator, returning '\${CCS_IMAGE' and
failing the kaitranntt/ccs grep.

Added a sed step to unwrap \${VAR:-default} by extracting just the default
value before stripping the tag. Existing plain image: name:tag references
are unaffected.
2026-05-17 05:01:15 -04:00
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
Kai (Tam Nhu) TranandGitHub 28f08cbb50 docs(docker): document ccs-net contract for sibling containers (#1259)
Add "Connect your app to CLIProxy" section to docker/README.md with:
- Public contract table (network=ccs-net, service DNS=ccs, ports 8317/3000)
- Pattern A: same compose file with external network reference
- Pattern B: docker run --network ccs-net
- Troubleshooting subsection (DNS, missing network, conflict, Podman, MTU)

Add one-line link in README.md pointing to the new section.
Add CHANGELOG entry under Unreleased noting the contract as SemVer-major stable.
Add CONTRIBUTING.md note that changing services.ccs or networks.ccs-net requires major bump.
2026-05-16 12:46:33 -04:00
Kai (Tam Nhu) TranandGitHub d558cd2e36 feat(docker): publish ccs:latest + ccs:full integrated images (P1 of #1251) (#1257)
* feat(docker): parameterize Dockerfile.integrated with ARG FLAVOR=minimal|full

Add FLAVOR build arg that gates the AI CLI install layer (claude-code,
gemini-cli, grok-cli, opencode) so one Dockerfile produces both the
minimal (< 350 MB) and full (< 600 MB) integrated images.

Use BuildKit cache mount for /root/.npm to speed up repeated builds.
Part of #1251 (P1 — publish integrated images).

* feat(docker): emit startup deprecation warning in legacy ccs-dashboard entrypoint

Prepend a [WARN] line to stderr on every container start so operators
running ghcr.io/kaitranntt/ccs-dashboard:latest are notified to migrate
to ghcr.io/kaitranntt/ccs:latest. Sunset window: 2 releases. See #1251.

* test(docker): add image-size.sh budget assertion + unit test suite

image-size.sh: asserts docker image inspect .Size against a byte budget.
  Usage: image-size.sh <image:tag> <max-bytes>
  Budgets: minimal=350 MB (367001600), full=600 MB (629145600)

image-size-logic.test.sh: 6 mock-docker unit tests covering pass/fail
boundaries, bad arg count, and non-integer input. All 6 pass locally.
Part of #1251 (P1).

* ci(docker): extend docker-release.yml to publish ccs:latest and ccs:full

- Add publish-integrated job with matrix flavor: [minimal, full]
  - minimal -> ghcr.io/kaitranntt/ccs:<ver> + :latest (when promoted)
  - full    -> ghcr.io/kaitranntt/ccs:full-<ver> + :full (when promoted)
  - Multi-arch: linux/amd64 + linux/arm64 via buildx
  - Scoped GHA cache per flavor to avoid cross-contamination

- Add promote_to_latest workflow_dispatch input (default false)
  - rc.1 soak: first publish only pushes immutable version tag
  - Mutable :latest/:full promoted only on release event OR explicit opt-in

- Add smoke-test job (post-publish) for each flavor
  - Pulls the just-published version tag
  - Asserts image size via tests/docker/image-size.sh
  - Boots container, waits for healthcheck, probes :3000 and :8317

- Keep publish-dashboard job unchanged (legacy 2-release sunset)
  - Updated labels to note deprecation status

All jobs run on self-hosted cliproxy runners. Part of #1251 (P1).

* docs(docker): document new image tags, add ccs-dashboard deprecation notices

docker/README.md:
- Add "Choosing an image" table (ccs:latest / ccs:full / ccs-dashboard deprecated)
- Update Quick Start section to use ccs:latest as primary example
- Add legacy image note with sunset timeline

CHANGELOG.md:
- Add Unreleased > ### Deprecated entry for ccs-dashboard:latest
  pointing to migration path and #1251

README.md:
- Add one-line deprecation banner near top routing users to ccs:latest

Part of #1251 (P1).
2026-05-16 12:33:42 -04:00