mirror of
https://github.com/tiennm99/ccs.git
synced 2026-08-01 22:21:31 +00:00
* 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.
150 lines
5.6 KiB
Bash
150 lines
5.6 KiB
Bash
#!/usr/bin/env bash
|
|
# tests/docker/compose-parity.sh
|
|
#
|
|
# Asserts that docker/compose.yaml and docker/docker-compose.integrated.yml
|
|
# agree on the fields that form the stable network contract:
|
|
# - image name (without tag)
|
|
# - exposed host ports
|
|
# - named volume mounts
|
|
#
|
|
# This prevents drift between the public quickstart compose and the
|
|
# `ccs docker` CLI compose that is bundled with the package.
|
|
#
|
|
# Usage: bash tests/docker/compose-parity.sh
|
|
# (called from repo root)
|
|
#
|
|
set -euo pipefail
|
|
|
|
CANONICAL="docker/compose.yaml"
|
|
INTEGRATED="docker/docker-compose.integrated.yml"
|
|
|
|
fail=0
|
|
|
|
log() { printf '[i] %s\n' "$*"; }
|
|
ok() { printf '[OK] %s\n' "$*"; }
|
|
fail() { printf '[X] %s\n' "$*" >&2; fail=1; }
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helper: extract image name (repo path without tag) for a service.
|
|
# Handles both plain image references and ${VAR:-default} shell variable
|
|
# syntax (e.g. image: ${CCS_IMAGE:-ghcr.io/kaitranntt/ccs:latest}).
|
|
# ---------------------------------------------------------------------------
|
|
image_name() {
|
|
local file="$1" service="$2"
|
|
# Match lines like:
|
|
# image: ghcr.io/owner/repo:tag
|
|
# image: ${CCS_IMAGE:-ghcr.io/owner/repo:tag}
|
|
grep -A 50 "^ ${service}:" "$file" \
|
|
| grep -m1 '^\s*image:' \
|
|
| sed 's/.*image:\s*//' \
|
|
| sed 's/\${[^:-]*:-\([^}]*\)}/\1/' \
|
|
| sed 's/:.*//' \
|
|
| tr -d ' '
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helper: extract sorted list of internal container ports exposed
|
|
# ---------------------------------------------------------------------------
|
|
exposed_ports() {
|
|
local file="$1"
|
|
# Match port mappings: "HOST:CONTAINER" — extract CONTAINER port number
|
|
grep -E '^\s+- "[0-9]+:[0-9]+"' "$file" \
|
|
| sed 's/.*:\([0-9]*\)".*/\1/' \
|
|
| sort -n
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helper: extract sorted list of named volume mount targets (container paths)
|
|
# ---------------------------------------------------------------------------
|
|
volume_targets() {
|
|
local file="$1"
|
|
# Match volume entries: - name:/container/path
|
|
grep -E '^\s+- [a-z_]+:/' "$file" \
|
|
| sed 's/.*:\(\/[^[:space:]]*\).*/\1/' \
|
|
| sort
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. Image name (repo without tag) — both should reference kaitranntt/ccs
|
|
# ---------------------------------------------------------------------------
|
|
log "Checking image name parity..."
|
|
|
|
CANONICAL_IMAGE=$(image_name "$CANONICAL" "ccs")
|
|
INTEGRATED_IMAGE=$(image_name "$INTEGRATED" "ccs-cliproxy")
|
|
|
|
# Both must contain kaitranntt/ccs (integrated builds locally but from the same Dockerfile)
|
|
if echo "$CANONICAL_IMAGE" | grep -q "kaitranntt/ccs" && \
|
|
echo "$INTEGRATED_IMAGE" | grep -q "ccs"; then
|
|
ok "Image names reference expected repo (canonical: ${CANONICAL_IMAGE}, integrated: ${INTEGRATED_IMAGE})"
|
|
else
|
|
fail "Image name mismatch — canonical='${CANONICAL_IMAGE}' integrated='${INTEGRATED_IMAGE}'"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. Exposed ports — both must expose 3000 and 8317
|
|
# Matches both quoted ("HOST:CONTAINER") and unquoted (HOST:CONTAINER) forms,
|
|
# as well as variable-interpolated host ports like "${VAR:-3000}:3000".
|
|
# ---------------------------------------------------------------------------
|
|
log "Checking exposed port parity..."
|
|
|
|
port_exposed() {
|
|
local file="$1" port="$2"
|
|
# Match container port in: "anything:PORT" or anything:PORT (quoted or bare)
|
|
grep -E "(\"[^\"]*:${port}\"|[[:space:]]-[[:space:]]+[^\"]*:${port}[^0-9])" "$file" \
|
|
> /dev/null 2>&1
|
|
}
|
|
|
|
REQUIRED_PORTS=("3000" "8317")
|
|
for port in "${REQUIRED_PORTS[@]}"; do
|
|
IN_CANONICAL=0
|
|
IN_INTEGRATED=0
|
|
port_exposed "$CANONICAL" "$port" && IN_CANONICAL=1 || true
|
|
port_exposed "$INTEGRATED" "$port" && IN_INTEGRATED=1 || true
|
|
|
|
if [[ "$IN_CANONICAL" -eq 1 && "$IN_INTEGRATED" -eq 1 ]]; then
|
|
ok "Port ${port} exposed in both compose files"
|
|
elif [[ "$IN_CANONICAL" -eq 0 ]]; then
|
|
fail "Port ${port} missing from ${CANONICAL}"
|
|
else
|
|
fail "Port ${port} missing from ${INTEGRATED}"
|
|
fi
|
|
done
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. Named volume mount targets — both must mount /root/.ccs and /var/log/ccs
|
|
# ---------------------------------------------------------------------------
|
|
log "Checking volume mount parity..."
|
|
|
|
REQUIRED_MOUNTS=("/root/.ccs" "/var/log/ccs")
|
|
for mount in "${REQUIRED_MOUNTS[@]}"; do
|
|
if grep -q ":${mount}" "$CANONICAL" && grep -q ":${mount}" "$INTEGRATED"; then
|
|
ok "Volume mount ${mount} present in both compose files"
|
|
elif ! grep -q ":${mount}" "$CANONICAL"; then
|
|
fail "Volume mount ${mount} missing from ${CANONICAL}"
|
|
else
|
|
fail "Volume mount ${mount} missing from ${INTEGRATED}"
|
|
fi
|
|
done
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 4. Network name — canonical must define ccs-net; integrated inherits default
|
|
# ---------------------------------------------------------------------------
|
|
log "Checking ccs-net network definition..."
|
|
if grep -q "name: ccs-net" "$CANONICAL"; then
|
|
ok "ccs-net network defined in ${CANONICAL}"
|
|
else
|
|
fail "ccs-net network definition missing from ${CANONICAL}"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Result
|
|
# ---------------------------------------------------------------------------
|
|
if [[ "$fail" -ne 0 ]]; then
|
|
echo "" >&2
|
|
echo "[X] Compose parity check FAILED — update ${INTEGRATED} to match ${CANONICAL}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo ""
|
|
ok "Compose parity check passed"
|