From 0aed60febb1752fab3b3f765fc2233f013af971f Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Mon, 23 Feb 2026 23:53:09 +0700 Subject: [PATCH 1/2] fix(ci): enforce parity gate and stabilize test execution --- .github/workflows/ci.yml | 2 +- .github/workflows/dev-release.yml | 2 +- .github/workflows/release.yml | 2 +- .husky/pre-commit | 4 +- .husky/pre-push | 3 ++ CLAUDE.md | 9 ++-- README.md | 10 ++++ package.json | 3 +- scripts/ci-parity-gate.sh | 51 +++++++++++++++++++++ tests/unit/utils/version-comparison.test.js | 12 ++--- 10 files changed, 81 insertions(+), 17 deletions(-) create mode 100755 .husky/pre-push create mode 100755 scripts/ci-parity-gate.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b8ae6ae..76254243 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 with: - bun-version: latest + bun-version: '1.3.9' - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml index 33168072..c32b9f91 100644 --- a/.github/workflows/dev-release.yml +++ b/.github/workflows/dev-release.yml @@ -26,7 +26,7 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 with: - bun-version: latest + bun-version: '1.3.9' - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0a31aa98..1fcbb8a6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v2 with: - bun-version: latest + bun-version: '1.3.9' - name: Install dependencies run: | diff --git a/.husky/pre-commit b/.husky/pre-commit index bb471b6e..c83fdfc4 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,5 +1,5 @@ -# Run quick checks before commit (typecheck + lint + format only) -# Tests are run by CI - no need to duplicate locally +# Run quick checks before commit (typecheck + lint + format only). +# Full CI parity is enforced by .husky/pre-push via validate:ci-parity. bun run typecheck && bun run lint:fix && bun run format:check # Validate UI if changes detected (typecheck + lint only) diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 00000000..012f1386 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,3 @@ +# Enforce CI parity before pushing to remote. +# Override in emergencies only: CCS_SKIP_PREPUSH_GATE=1 git push --no-verify +bun run validate:ci-parity diff --git a/CLAUDE.md b/CLAUDE.md index 5d33fa0e..80f46d55 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -51,7 +51,7 @@ CLI wrapper for instant switching between multiple provider accounts and alterna ## Quality Gates (MANDATORY) -Quality gates MUST pass before committing. **Both projects have identical workflow.** +Quality gates MUST pass before pushing. **Both projects have identical workflow.** ### Pre-Commit Sequence (FOLLOW THIS ORDER) @@ -59,7 +59,8 @@ Quality gates MUST pass before committing. **Both projects have identical workfl # Main project (from repo root) bun run format # Step 1: Fix formatting bun run lint:fix # Step 2: Fix lint issues -bun run validate # Step 3: Final check (must pass) +bun run validate # Step 3: Full test gate (must pass) +bun run validate:ci-parity # Step 4: CI parity gate (build + validate + base branch check) # UI project (if UI changed) cd ui @@ -105,7 +106,8 @@ bun run validate # Step 3: Final check (must pass) - `prepublishOnly` / `prepack` runs `build:all` + `validate` + `sync-version.js` - CI/CD runs `bun run validate` on every PR -- husky pre-commit hooks enforce conventional commits +- husky `pre-commit` runs quick lint/type/format checks +- husky `pre-push` runs `bun run validate:ci-parity` to block CI drift before push ## Critical Constraints (NEVER VIOLATE) @@ -355,6 +357,7 @@ rm -rf ~/.ccs # Clean environment **Quality (BLOCKERS):** - [ ] `bun run format` — formatting fixed - [ ] `bun run validate` — all checks pass +- [ ] `bun run validate:ci-parity` — CI parity passed (also enforced by pre-push hook) - [ ] `cd ui && bun run format && bun run validate` — if UI changed **Code:** diff --git a/README.md b/README.md index 47cf77ce..95ad1e20 100644 --- a/README.md +++ b/README.md @@ -241,6 +241,16 @@ ccs update --force # Force reinstall ccs update --beta # Install dev channel ``` +### CI Parity Gate (for contributors) + +Before opening or updating a PR, run: + +```bash +bun run validate:ci-parity +``` + +This mirrors CI behavior (build + validate + base-branch freshness check) and is also enforced by the local `pre-push` hook. + ### Sync Shared Items ```bash diff --git a/package.json b/package.json index 9a11c880..cc0b3733 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "node": ">=18.0.0", "bun": ">=1.0.0" }, - "packageManager": "bun@1.2.21", + "packageManager": "bun@1.3.9", "os": [ "darwin", "linux", @@ -66,6 +66,7 @@ "format": "prettier --write src/", "format:check": "prettier --check src/", "validate": "bun run typecheck && bun run lint:fix && bun run format:check && bun run maintainability:check && bun run test:all", + "validate:ci-parity": "bash scripts/ci-parity-gate.sh", "verify:bundle": "node scripts/verify-bundle.js", "maintainability:baseline": "node scripts/maintainability-baseline.js --out docs/metrics/maintainability-baseline.json", "maintainability:check": "node scripts/maintainability-baseline.js --check docs/metrics/maintainability-baseline.json", diff --git a/scripts/ci-parity-gate.sh b/scripts/ci-parity-gate.sh new file mode 100755 index 00000000..bb507ae8 --- /dev/null +++ b/scripts/ci-parity-gate.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT_DIR" + +if [[ "${CCS_SKIP_PREPUSH_GATE:-}" == "1" ]]; then + echo "[i] Skipping pre-push CI parity gate (CCS_SKIP_PREPUSH_GATE=1)." + exit 0 +fi + +if [[ ! -f AGENTS.md ]]; then + echo "[X] Missing AGENTS.md in this worktree." + echo " Ensure you are in a valid CCS repository/worktree before pushing." + exit 1 +fi + +CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)" +if [[ -z "$CURRENT_BRANCH" || "$CURRENT_BRANCH" == "HEAD" ]]; then + echo "[i] Detached HEAD detected. Skipping pre-push CI parity gate." + exit 0 +fi + +BASE_BRANCH="${CCS_PR_BASE:-}" +if [[ -z "$BASE_BRANCH" ]]; then + if [[ "$CURRENT_BRANCH" == "main" || "$CURRENT_BRANCH" =~ ^hotfix/ || "$CURRENT_BRANCH" =~ ^kai/hotfix- ]]; then + BASE_BRANCH="main" + else + BASE_BRANCH="dev" + fi +fi + +echo "[i] Pre-push CI parity gate" +echo " branch: $CURRENT_BRANCH" +echo " base: $BASE_BRANCH" + +git fetch origin "$BASE_BRANCH" --quiet || true +if git show-ref --verify --quiet "refs/remotes/origin/$BASE_BRANCH"; then + if ! git merge-base --is-ancestor "origin/$BASE_BRANCH" HEAD; then + echo "[X] Branch '$CURRENT_BRANCH' is behind origin/$BASE_BRANCH." + echo " Rebase or merge before pushing:" + echo " git pull --rebase origin $BASE_BRANCH" + exit 1 + fi +fi + +echo "[i] Running CI-equivalent local checks..." +bun run build:all +bun run validate + +echo "[OK] CI parity gate passed." diff --git a/tests/unit/utils/version-comparison.test.js b/tests/unit/utils/version-comparison.test.js index 0c6e38ac..2ee09b56 100644 --- a/tests/unit/utils/version-comparison.test.js +++ b/tests/unit/utils/version-comparison.test.js @@ -28,13 +28,9 @@ const os = require('os'); describe('Version Comparison Implementation (Phase 4)', () => { let updateCheckerModule; - // Build the project before running tests - const { execSync } = require('child_process'); - try { - execSync('bun run build', { cwd: path.resolve(__dirname, '../../..'), stdio: 'pipe' }); - } catch (error) { - console.warn('Build failed, tests may not work:', error.message); - } + // Dist artifacts are built by the pipeline before tests run. + // Avoid rebuilding inside test files because `bun run build` wipes `dist/` + // and can race with parallel npm/integration tests. // Import the built modules updateCheckerModule = require('../../../dist/utils/update-checker.js'); @@ -362,4 +358,4 @@ describe('Version Comparison Implementation (Phase 4)', () => { }); }); }); -}); \ No newline at end of file +}); From 2b7c992d502936750fa1f613e47fc76cd2fdf817 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 23 Feb 2026 16:55:44 +0000 Subject: [PATCH 2/2] chore(release): 7.48.1 [skip ci] ## [7.48.1](https://github.com/kaitranntt/ccs/compare/v7.48.0...v7.48.1) (2026-02-23) ### Bug Fixes * **ci:** enforce parity gate and stabilize test execution ([0aed60f](https://github.com/kaitranntt/ccs/commit/0aed60febb1752fab3b3f765fc2233f013af971f)) --- CHANGELOG.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95cb5457..ffb8640c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [7.48.1](https://github.com/kaitranntt/ccs/compare/v7.48.0...v7.48.1) (2026-02-23) + +### Bug Fixes + +* **ci:** enforce parity gate and stabilize test execution ([0aed60f](https://github.com/kaitranntt/ccs/commit/0aed60febb1752fab3b3f765fc2233f013af971f)) + ## [7.48.0](https://github.com/kaitranntt/ccs/compare/v7.47.0...v7.48.0) (2026-02-22) ### Features diff --git a/package.json b/package.json index cc0b3733..53b518f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "7.48.0", + "version": "7.48.1", "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6", "keywords": [ "cli",