1010
1111permissions :
1212 contents : read
13+ pull-requests : read
1314
1415# A new push to the PR supersedes the running validation — cancel it
1516# instead of stacking zombie pipelines.
@@ -35,10 +36,91 @@ jobs:
3536 # dry runs and releases where a flaky red does not block a merge.
3637 skip_perf : true
3738
39+ # ── Which files changed? Gate the (heavier) build+smoke on product code so
40+ # docs / CI / test-only PRs stay fast. ──
41+ changes :
42+ runs-on : ubuntu-latest
43+ timeout-minutes : 5
44+ outputs :
45+ product : ${{ steps.f.outputs.product }}
46+ steps :
47+ - name : Detect product-code changes
48+ id : f
49+ env :
50+ GH_TOKEN : ${{ github.token }}
51+ PR : ${{ github.event.pull_request.number }}
52+ REPO : ${{ github.repository }}
53+ run : |
54+ FILES=$(gh pr diff "$PR" --repo "$REPO" --name-only)
55+ printf '%s\n' "$FILES"
56+ if printf '%s\n' "$FILES" | grep -qE '^(src/|internal/|scripts/build\.sh|scripts/smoke-test\.sh|scripts/env\.sh|Makefile\.cbm)'; then
57+ echo "product=true" >> "$GITHUB_OUTPUT"
58+ else
59+ echo "product=false" >> "$GITHUB_OUTPUT"
60+ fi
61+
62+ # ── Light smoke: build the PRODUCTION binary and run the core smoke on the
63+ # three RELIABLE NATIVE platforms. Catches built-binary regressions at PR
64+ # time (e.g. the Windows CreateProcess argv-quoting class that previously
65+ # only surfaced in the release dry run). The full broad/emulated smoke stays
66+ # in the dry run. Only product-code PRs pay this; every leg gates.
67+ # SMOKE_DOWNLOAD_URL is intentionally unset — the download/update phases of
68+ # smoke-test.sh self-skip; the core index/search/trace phases still run. ──
69+ pr-smoke :
70+ needs : [changes]
71+ if : ${{ !cancelled() && needs.changes.outputs.product == 'true' }}
72+ strategy :
73+ fail-fast : false
74+ matrix :
75+ os : [ubuntu-latest, macos-14, windows-latest]
76+ runs-on : ${{ matrix.os }}
77+ timeout-minutes : 30
78+ steps :
79+ - uses : actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
80+
81+ - name : Install deps (Ubuntu)
82+ if : matrix.os == 'ubuntu-latest'
83+ run : sudo apt-get update && sudo apt-get install -y zlib1g-dev
84+
85+ - uses : msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2
86+ if : matrix.os == 'windows-latest'
87+ with :
88+ msystem : CLANG64
89+ path-type : inherit
90+ install : >-
91+ mingw-w64-clang-x86_64-clang
92+ mingw-w64-clang-x86_64-zlib
93+ mingw-w64-clang-x86_64-python3
94+ make
95+ coreutils
96+
97+ - name : Build prod + smoke (Ubuntu)
98+ if : matrix.os == 'ubuntu-latest'
99+ run : |
100+ scripts/build.sh CC=gcc CXX=g++
101+ scripts/smoke-test.sh "$(pwd)/build/c/codebase-memory-mcp"
102+
103+ - name : Build prod + smoke (macOS)
104+ if : matrix.os == 'macos-14'
105+ run : |
106+ scripts/build.sh CC=cc CXX=c++
107+ codesign --sign - --force build/c/codebase-memory-mcp
108+ scripts/smoke-test.sh "$(pwd)/build/c/codebase-memory-mcp"
109+
110+ - name : Build prod + smoke (Windows)
111+ if : matrix.os == 'windows-latest'
112+ shell : msys2 {0}
113+ run : |
114+ scripts/build.sh CC=clang CXX=clang++
115+ BIN="$(pwd)/build/c/codebase-memory-mcp"
116+ [ -f "${BIN}.exe" ] && BIN="${BIN}.exe"
117+ scripts/smoke-test.sh "$BIN"
118+
38119 ci-ok :
39120 # The one required context (besides dco) — fails unless every PR stage
40- # succeeded, so matrix renames can never silently deadlock merges.
41- needs : [security, lint, test]
121+ # succeeded, so matrix renames can never silently deadlock merges. `skipped`
122+ # is OK: pr-smoke skips on docs/CI/test-only PRs (changes.product == false).
123+ needs : [security, lint, test, changes, pr-smoke]
42124 if : ${{ always() }}
43125 runs-on : ubuntu-latest
44126 timeout-minutes : 5
50132 echo "$RESULTS" | python3 -c "
51133 import json, sys
52134 needs = json.load(sys.stdin)
53- bad = {k: v['result'] for k, v in needs.items() if v['result'] != 'success'}
135+ bad = {k: v['result'] for k, v in needs.items() if v['result'] not in ( 'success', 'skipped') }
54136 if bad:
55137 print('CI NOT OK:', bad)
56138 sys.exit(1)
0 commit comments