mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-11 12:09:03 +00:00
* 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).
133 lines
3.5 KiB
Bash
Executable File
133 lines
3.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Unit tests for image-size.sh pass/fail logic using mock docker output.
|
|
# Does NOT require a real Docker daemon or pulled images.
|
|
# Run: bash tests/docker/image-size-logic.test.sh
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
SCRIPT="${SCRIPT_DIR}/image-size.sh"
|
|
|
|
PASS=0
|
|
FAIL=0
|
|
|
|
run_test() {
|
|
local name="$1"
|
|
local expected_exit="$2"
|
|
shift 2
|
|
# Remaining args: env vars to set before calling the script
|
|
local actual_exit=0
|
|
(
|
|
# Override docker with a mock that returns a fixed size
|
|
eval "$@"
|
|
bash "$SCRIPT" "mock-image:tag" "$MAX_BYTES" > /dev/null 2>&1
|
|
) || actual_exit=$?
|
|
|
|
if [[ "$actual_exit" -eq "$expected_exit" ]]; then
|
|
echo "[OK] ${name}"
|
|
(( PASS++ )) || true
|
|
else
|
|
echo "[X] ${name}: expected exit ${expected_exit}, got ${actual_exit}"
|
|
(( FAIL++ )) || true
|
|
fi
|
|
}
|
|
|
|
# ------------------------------------------------------------------
|
|
# We mock docker by injecting a wrapper into PATH that echoes a fixed
|
|
# size value for `docker image inspect` and pretends inspect succeeds.
|
|
# ------------------------------------------------------------------
|
|
|
|
MOCK_DIR="$(mktemp -d)"
|
|
trap 'rm -rf "$MOCK_DIR"' EXIT
|
|
|
|
make_mock_docker() {
|
|
local size="$1"
|
|
cat > "${MOCK_DIR}/docker" <<EOF
|
|
#!/usr/bin/env bash
|
|
# Mock docker for image-size.sh tests
|
|
if [[ "\$1" == "image" && "\$2" == "inspect" ]]; then
|
|
echo "${size}"
|
|
exit 0
|
|
fi
|
|
# pull / other sub-commands: succeed silently
|
|
exit 0
|
|
EOF
|
|
chmod +x "${MOCK_DIR}/docker"
|
|
}
|
|
|
|
run_mock_test() {
|
|
local name="$1"
|
|
local expected_exit="$2"
|
|
local mock_size="$3"
|
|
local max_bytes="$4"
|
|
|
|
make_mock_docker "$mock_size"
|
|
|
|
local actual_exit=0
|
|
PATH="${MOCK_DIR}:${PATH}" bash "$SCRIPT" "mock-image:tag" "$max_bytes" > /dev/null 2>&1 || actual_exit=$?
|
|
|
|
if [[ "$actual_exit" -eq "$expected_exit" ]]; then
|
|
echo "[OK] ${name}"
|
|
(( PASS++ )) || true
|
|
else
|
|
echo "[X] ${name}: expected exit ${expected_exit}, got ${actual_exit}"
|
|
(( FAIL++ )) || true
|
|
fi
|
|
}
|
|
|
|
# ------------------------------------------------------------------
|
|
# Tests
|
|
# ------------------------------------------------------------------
|
|
|
|
echo ""
|
|
echo "Running image-size.sh unit tests..."
|
|
echo ""
|
|
|
|
# Pass: actual < budget
|
|
run_mock_test "pass when actual size < budget" 0 \
|
|
"300000000" "367001600"
|
|
|
|
# Pass: actual == budget (boundary)
|
|
run_mock_test "pass when actual size == budget (boundary)" 0 \
|
|
"367001600" "367001600"
|
|
|
|
# Fail: actual > budget by 1 byte
|
|
run_mock_test "fail when actual size exceeds budget by 1 byte" 1 \
|
|
"367001601" "367001600"
|
|
|
|
# Fail: actual is much larger than budget
|
|
run_mock_test "fail when actual size greatly exceeds budget" 1 \
|
|
"900000000" "629145600"
|
|
|
|
# Error: wrong arg count (no args)
|
|
actual_exit=0
|
|
bash "$SCRIPT" > /dev/null 2>&1 || actual_exit=$?
|
|
if [[ "$actual_exit" -ne 0 ]]; then
|
|
echo "[OK] fail when called with no args"
|
|
(( PASS++ )) || true
|
|
else
|
|
echo "[X] fail when called with no args: expected non-zero exit"
|
|
(( FAIL++ )) || true
|
|
fi
|
|
|
|
# Error: non-integer max-bytes
|
|
actual_exit=0
|
|
PATH="${MOCK_DIR}:${PATH}" bash "$SCRIPT" "mock-image:tag" "not-a-number" > /dev/null 2>&1 || actual_exit=$?
|
|
if [[ "$actual_exit" -ne 0 ]]; then
|
|
echo "[OK] fail when max-bytes is not an integer"
|
|
(( PASS++ )) || true
|
|
else
|
|
echo "[X] fail when max-bytes is not an integer: expected non-zero exit"
|
|
(( FAIL++ )) || true
|
|
fi
|
|
|
|
# ------------------------------------------------------------------
|
|
# Summary
|
|
# ------------------------------------------------------------------
|
|
echo ""
|
|
echo "Results: ${PASS} passed, ${FAIL} failed"
|
|
echo ""
|
|
|
|
if [[ "$FAIL" -gt 0 ]]; then
|
|
exit 1
|
|
fi
|