Repository navigation
203 lines (185 loc) · 8.33 KB
/
Copy pathpr.yml
File metadata and controls
203 lines (185 loc) · 8.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# PR validation: security gates + lint + full test suite. Builds, smoke and
# soak stay maintainer-driven via the dry-run workflow_dispatch. Branch
# protection requires `dco` + `ci-ok` — a single stable summary context that
# fails unless every PR stage succeeded.
name: PR
on:
pull_request:
branches: [main]
permissions:
contents: read
pull-requests: read
# A new push to the PR supersedes the running validation — cancel it
# instead of stacking zombie pipelines.
concurrency:
group: pr-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
security:
uses: ./.github/workflows/_security.yml
# The called job requests security-events:read to count code-scanning
# alerts; a reusable workflow cannot request more than its caller grants,
# so withholding this here fails the whole run at startup.
permissions:
contents: read
security-events: read
actions: read
secrets: inherit
lint:
uses: ./.github/workflows/_lint.yml
test:
needs: [lint]
if: ${{ !cancelled() && needs.lint.result == 'success' }}
uses: ./.github/workflows/_test.yml
with:
# Perf assertions are timing-sensitive on shared runners; they stay in
# dry runs and releases where a flaky red does not block a merge.
skip_perf: true
# Iteration speed: split each C-suite leg across parallel shard jobs
# (ubuntu x3, windows x2; coverage re-proven every run by the
# shard-completeness job). Dry runs and releases shard identically.
shard_suites: true
# ── Which files changed? Gate the (heavier) build+smoke on product code so
# docs / CI / test-only PRs stay fast. ──
changes:
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
product: ${{ steps.f.outputs.product }}
steps:
- name: Detect product-code changes
id: f
env:
GH_TOKEN: ${{ github.token }}
PR: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
# The full .diff endpoint rejects large-but-valid PRs at 20k lines.
# The paginated files endpoint remains filename-only for this gate.
if FILES=$(gh api --paginate "repos/$REPO/pulls/$PR/files?per_page=100" --jq '.[].filename'); then
echo "file list: pulls/files API"
else
# The files endpoint renders the diff server-side too, and answers
# 422 "this diff is taking too long to generate" for a PR that
# regenerates a vendored parser (#2246: a 42 MB sql/parser.c) --
# so does the compare endpoint. The PR merge ref's first parent is
# the base, so a name-only diff across it is the same file list.
# depth 2 + blob:none fetches commits and trees only (well under
# 1 MB, under a second); nothing from the PR is checked out or run.
echo "::notice::pulls/files API failed; file list taken from refs/pull/$PR/merge"
CHANGES_GIT="$RUNNER_TEMP/changes.git"
git init -q --bare "$CHANGES_GIT"
git --git-dir="$CHANGES_GIT" fetch -q --depth=2 --filter=blob:none \
"https://github.com/$REPO" "refs/pull/$PR/merge"
FILES=$(git --git-dir="$CHANGES_GIT" diff --name-only --no-renames FETCH_HEAD^1 FETCH_HEAD)
fi
printf '%s\n' "$FILES"
# A here-string, not a pipe: under this shell's pipefail, grep -q
# exits on its first match, printf dies of SIGPIPE once the list
# outgrows the pipe buffer, and the MATCH is then reported as a
# failure -- product=false, smoke skipped, on the largest PRs.
if grep -qE '^(src/|internal/|install\.(sh|ps1)|scripts/build\.sh|scripts/smoke-test\.sh|scripts/smoke-local\.sh|scripts/smoke-fixture-server\.py|scripts/gen-third-party-notices\.sh|scripts/env\.sh|test-infrastructure/vm/(vm-smoke\.sh|windows-user-path-guard\.ps1)|Makefile\.cbm)' <<<"$FILES"; then
echo "product=true" >> "$GITHUB_OUTPUT"
else
echo "product=false" >> "$GITHUB_OUTPUT"
fi
# ── Light smoke: build the PRODUCTION binary and run the core smoke on the
# three RELIABLE NATIVE platforms. Catches built-binary regressions at PR
# time (e.g. the Windows CreateProcess argv-quoting class that previously
# only surfaced in the release dry run). The full broad/emulated smoke stays
# in the dry run. Only product-code PRs pay this; every leg gates.
# The maintained local wrappers add a race-free release fixture so the
# download/checksum/install/update phases run here as well. ──
pr-smoke:
needs: [changes]
if: ${{ !cancelled() && needs.changes.outputs.product == 'true' }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-14, windows-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install deps (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update && sudo apt-get install -y zlib1g-dev ccache
- name: Install ccache (macOS)
if: matrix.os == 'macos-14'
run: command -v ccache >/dev/null 2>&1 || brew install ccache
- uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2
if: matrix.os == 'windows-latest'
with:
msystem: CLANG64
path-type: inherit
install: >-
mingw-w64-clang-x86_64-clang
mingw-w64-clang-x86_64-zlib
mingw-w64-clang-x86_64-python3
mingw-w64-clang-x86_64-ccache
make
coreutils
curl
zip
unzip
# Verified compiler cache — content-keyed, stale hits impossible by
# construction (see scripts/env.sh).
- name: Compiler cache (content-verified)
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ github.workspace }}/.ccache
key: ccache-smoke-${{ matrix.os }}-${{ github.ref }}-${{ github.sha }}
restore-keys: |
ccache-smoke-${{ matrix.os }}-${{ github.ref }}-
- name: Build prod + smoke (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: |
scripts/build.sh CC=gcc CXX=g++
scripts/smoke-local.sh "$(pwd)/build/c/codebase-memory-mcp"
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1000M
- name: Build prod + smoke (macOS)
if: matrix.os == 'macos-14'
run: |
scripts/build.sh CC=cc CXX=c++
codesign --sign - --force build/c/codebase-memory-mcp
scripts/smoke-local.sh "$(pwd)/build/c/codebase-memory-mcp"
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1000M
- name: Build prod + smoke (Windows)
if: matrix.os == 'windows-latest'
shell: msys2 {0}
run: |
scripts/build.sh CC=clang CXX=clang++
SMOKE_ARCH=amd64 bash test-infrastructure/vm/vm-smoke.sh
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1000M
# ── Waste sanitizer, REPORT-ONLY: memory + CPU waste on this repository and
# the per-function scaling lane (Linux). Deliberately NOT in ci-ok's needs:
# it uploads evidence and prints the ratchet, it never blocks a merge. Only
# product-code PRs pay for it. ──
memwaste:
needs: [changes]
if: ${{ !cancelled() && needs.changes.outputs.product == 'true' }}
uses: ./.github/workflows/_memwaste.yml
with:
mode: pr
ci-ok:
# The one required context (besides dco) — fails unless every PR stage
# succeeded, so matrix renames can never silently deadlock merges. `skipped`
# is OK: pr-smoke skips on docs/CI/test-only PRs (changes.product == false).
needs: [security, lint, test, changes, pr-smoke]
if: ${{ always() }}
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
# Needs a checkout (unlike before): the gate logic lives in the canonical
# scripts/ci entry, not inline YAML (venue-parity contract).
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: All PR stages must have succeeded
env:
RESULTS: ${{ toJSON(needs) }}
run: scripts/ci/require-all-green.sh