-
Notifications
You must be signed in to change notification settings - Fork 0
345 lines (332 loc) · 14.3 KB
/
Copy pathci.yml
File metadata and controls
345 lines (332 loc) · 14.3 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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
name: CI
on:
push:
branches: [main]
pull_request:
# A new push to the same ref supersedes the running build. main keeps every
# run (github.run_id) so merged pushes always populate the bazel caches.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && github.run_id || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
bazel:
name: bazel (${{ matrix.name }})
strategy:
fail-fast: false
matrix:
include:
- name: linux-gcc
os: ubuntu-24.04
cc: gcc
cxx: g++
- name: linux-clang
os: ubuntu-24.04
cc: clang
cxx: clang++
# Hermetic zero-sysroot LLVM with libc++: the only linux libc++ cell,
# and the toolchain serious Bazel consumers run (its parse_headers
# validation of our REPO.bazel-shipped feature included).
- name: linux-llvm-libcxx
os: ubuntu-24.04
extra_config: --config=llvm
- name: macos-clang
os: macos-26
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: bazel-contrib/setup-bazel@0.19.0
with:
bazelisk-cache: true
repository-cache: true
disk-cache: ${{ matrix.name }}
- name: bazel test
env:
CC: ${{ matrix.cc }}
CXX: ${{ matrix.cxx }}
run: bazelisk test //... --config=ci ${{ matrix.extra_config }}
# Linux catches most UB, but the transport/socket layer has Apple-specific
# paths (SO_NOSIGPIPE, libc++) that linux/clang never exercises under a
# sanitizer (issue #48).
sanitizers:
name: bazel (asan + ubsan, ${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, macos-26]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: bazel-contrib/setup-bazel@0.19.0
with:
bazelisk-cache: true
repository-cache: true
disk-cache: asan-ubsan-${{ matrix.os }}
- name: bazel test with sanitizers
env:
CC: clang
CXX: clang++
run: bazelisk test //... --config=ci --config=asan --config=ubsan
# TSan needs its own build (incompatible with asan) and earns its keep
# on the concurrency heart: the registry's writer/chain/grace threads
# (ADR-0017/0019/0020), the coroutine layer's cross-thread resumes
# (ADR-0021's StreamTask rides the same exchange protocol), and the
# Beast sessions' mutex/CV + executor-thread interplay (blocking
# facade over async completions, Close-from-any-thread escalation,
# three wire modes sharing one read loop — ADR-0015/0018/0019/0023).
# The suites' churn and parked-completion tests exist for this run.
# Single-threaded suites (codecs, the in-memory-pair wrapper tests)
# stay out: TSan has nothing to observe there.
- name: bazel test concurrency suites under tsan
if: runner.os == 'Linux'
env:
CC: clang
CXX: clang++
run: >
bazelisk test //runtime:session_registry_test
//runtime:async_event_stream_test
//runtime:beast_transport_test
//runtime:beast_websocket_test --config=ci --config=tsan
noexcept:
name: bazel (-fno-exceptions runtime)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: bazel-contrib/setup-bazel@0.19.0
with:
bazelisk-cache: true
repository-cache: true
disk-cache: noexcept-ubuntu
# ADR-0003 enforcement gate: the dependency-light runtime must compile
# with exceptions disabled, because many large C++ consumers build
# -fno-exceptions and could not otherwise link it. Boost.Asio/Beast are
# not -fno-exceptions-clean, so //runtime:http_beast and its dependents
# are out of scope; every other runtime library is in. A build (not a
# test) is the gate — the goal is that the object code exists without a
# single `throw` or `catch` reachable.
- name: bazel build (exceptions disabled)
run: >
bazelisk build --config=ci --config=noexcept
//runtime:core
//runtime:json
//runtime:cbor
//runtime:eventstream
//runtime:eventstream_json
//runtime:eventstream_jsonrpc
//runtime:compression
//runtime:http
//runtime:server
//runtime:client
fuzz:
name: fuzz (libFuzzer smoke)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: bazel-contrib/setup-bazel@0.19.0
with:
bazelisk-cache: true
repository-cache: true
disk-cache: fuzz
# Each harness runs a short bounded libFuzzer session (a "smoke" run, not
# a soak) so regressions in the parsers surface on every PR. The
# deterministic-driver variants also run as ordinary tests in the bazel
# matrix above; this job additionally exercises the real fuzzer runtime.
# Build only the libFuzzer binaries — not //fuzz:all, which would also
# pull in the *_fuzz_smoke cc_tests. Those link their own main() (the
# deterministic driver) and would clash with the libFuzzer main() that
# --config=fuzz injects. The smoke tests run in the bazel matrix above.
- name: build and run each harness (30s)
env:
CC: clang
CXX: clang++
run: |
set -euo pipefail
for target in json_decode cbor_decode uri server_dispatch regex http1 access_log; do
echo "== fuzzing $target"
bazelisk build --config=fuzz "//fuzz:${target}_fuzz"
./bazel-bin/fuzz/${target}_fuzz -max_total_time=30 -print_final_stats=1
done
consumer:
name: bazel consumer (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, macos-26]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: bazel-contrib/setup-bazel@0.19.0
with:
bazelisk-cache: true
repository-cache: true
disk-cache: consumer
# The quick-start acceptance test (docs/quickstart.md): a standalone
# out-of-tree Bazel module consumes opal_cpp via the rules in
# bazel/defs.bzl — generation runs inside the consumer's build graph,
# on every OS the runtime itself supports. --config=werror gates the
# consumer's own code (hand-written and generated alike) at
# -Wall -Wextra -Werror, mirroring the main matrix (issue #65).
- name: bazel test (out-of-tree module)
working-directory: examples/bazel-consumer
run: bazelisk test //... --config=werror --verbose_failures
# The day-2 model-evolution loop (docs/model-evolution.md): an additive
# member rebuilds handlers and tests untouched; a new operation fails
# compilation until handlers implement it. The properties are
# platform-independent, so one OS suffices.
- name: model evolution loop
if: matrix.os == 'ubuntu-24.04'
working-directory: examples/bazel-consumer
run: ./model-evolution-check.sh
# The consumer's resolved boringssl must be exactly the version
# opal_cpp pins — MVS would silently select anything higher that a
# transitive dep requests, and every leg above stays green while
# consumers link a TLS library no job tested. Resolution is
# platform-independent, so one OS suffices.
- name: resolved boringssl matches the pin
if: matrix.os == 'ubuntu-24.04'
working-directory: examples/bazel-consumer
run: ./boringssl-resolution-check.sh
# MODULE.bazel.lock records the registry state resolution actually saw.
# Every other leg runs with the default --lockfile_mode=update, which
# silently re-resolves — so a dependency bump that forgets to repin still
# rides green and the diff no longer shows which versions CI tested. This
# leg makes staleness a red check instead: a MODULE.bazel edit lands
# together with its lockfile, and dependency PRs (Renovate's included)
# stay auditable from the diff alone. Same posture as the codegen job's
# golden-fixture freshness check. Repin with:
# bazelisk mod deps --lockfile_mode=update (root and examples/bazel-consumer)
lockfiles:
name: lockfiles (resolution freshness)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: bazel-contrib/setup-bazel@0.19.0
with:
bazelisk-cache: true
repository-cache: true
disk-cache: lockfiles
- name: root module lockfile is current
run: bazelisk mod deps --lockfile_mode=error
- name: consumer module lockfile is current
working-directory: examples/bazel-consumer
run: bazelisk mod deps --lockfile_mode=error
codegen:
name: codegen (gradle)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
distribution: temurin
java-version: "17"
- uses: gradle/actions/setup-gradle@v6
with:
gradle-version: "9.6.1"
- name: gradle build
run: gradle build spotlessCheck
working-directory: codegen
# The checked-in generated code under examples/*/generated and
# protocol-tests/ is the golden output; regeneration must be
# byte-identical.
- name: check generated fixtures are current
run: |
(cd codegen && gradle generateFixtures generateProtocolTests)
git diff --exit-code -- examples protocol-tests
# Coverage measurement for the runtime (issue #48): the combined lcov
# report + rendered HTML land as a build artifact and the per-module
# summary prints in the log, so srcs<->tests gaps stop needing a manual
# audit. Measurement only — no threshold gate yet; add one once the
# baseline has soaked (same posture as benchmarks below).
coverage:
name: coverage (runtime, lcov)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: bazel-contrib/setup-bazel@0.19.0
with:
bazelisk-cache: true
repository-cache: true
disk-cache: coverage
- name: bazel coverage
run: |
bazelisk coverage //... --config=ci --combined_report=lcov \
--instrumentation_filter='//runtime[/:]'
- name: summarize and render
run: |
sudo apt-get update -q || true
sudo apt-get install -y -q lcov
REPORT="$(bazelisk info output_path)/_coverage/_coverage_report.dat"
# The multithreaded runtime tests can tear gcov's non-atomic
# profile counters, leaving spurious negative hit counts that
# lcov 2.x treats as fatal (1.x only warned). Downgrade to a
# warning — this job is measurement-only, with no threshold gate.
lcov --ignore-errors negative --summary "$REPORT"
lcov --ignore-errors negative --list "$REPORT"
genhtml --ignore-errors negative --output-directory coverage-html "$REPORT"
cp "$REPORT" coverage-html/coverage.lcov
- uses: actions/upload-artifact@v7
with:
name: coverage-report
path: coverage-html
# Informational (PLAN Phase 7): publish numbers, no pass/fail threshold yet.
benchmarks:
name: benchmarks (informational)
runs-on: ubuntu-24.04
continue-on-error: true
steps:
- uses: actions/checkout@v7
- uses: bazel-contrib/setup-bazel@0.19.0
with:
bazelisk-cache: true
repository-cache: true
disk-cache: benchmarks-opt
- name: serde benchmarks
run: bazel run -c opt //benchmarks:serde_benchmark -- --benchmark_min_time=0.2s
- name: request benchmarks
run: bazel run -c opt //benchmarks:request_benchmark -- --benchmark_min_time=0.2s
- name: transport benchmarks (socket, beast, beast tls)
run: bazel run -c opt //benchmarks:beast_benchmark -- --benchmark_min_time=0.2s
lint:
name: lint
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
# Boost headers so clang-tidy can process the Beast transport. libboost-dev
# comes from the Ubuntu archive; tolerate update failures from the runner
# image's unrelated third-party repos (packages.microsoft.com flakes).
- name: install boost headers
run: |
sudo apt-get update -q || true
sudo apt-get install -y -q libboost-dev
# Generated code (examples/*/generated) is exempt from format checks;
# its shape is locked by the golden diff check in the codegen job.
- name: clang-format
run: |
find runtime examples codegen/compile-tests protocol-tests \
\( -name '*.h' -o -name '*.cc' \) \
! -path '*/generated/*' | xargs clang-format --dry-run --Werror
# Excluded from tidy: src/json/json.cc includes the nlohmann backend,
# which only exists inside the Bazel build graph; beast_src.cc is the
# Boost implementation TU and only compiles with the BCR modules'
# BOOST_*_SEPARATE_COMPILATION defines (no first-party code in it);
# examples/bazel-consumer generates its acme/* headers inside its own
# module's build graph (nothing checked in), so its sources cannot
# resolve here — clang-format still covers them, and the consumer CI
# jobs compile them with full warnings.
- name: clang-tidy
run: |
# Example sources include their generated headers short-form; give
# tidy every generated include root so new examples need no edit here.
GEN_INCLUDES=$(find examples -type d -path '*/generated/include' | sed 's/^/-I/')
# -P: one tidy per core — each TU re-parses its whole include graph
# and runs the static analyzer, so the sweep is embarrassingly
# parallel (xargs still exits non-zero if any invocation fails;
# interleaved output is the accepted cost).
find runtime/src examples -name '*.cc' ! -name '*_test.cc' ! -path '*/src/json/*' \
! -name 'beast_src.cc' ! -path '*/generated/*' \
! -path '*/bazel-consumer/*' -print0 \
| xargs -0 -P "$(nproc)" -I{} clang-tidy --quiet {} -- -Iruntime/include -I. \
$GEN_INCLUDES -std=c++20
- name: buildifier
run: npx -y @bazel/buildifier@8.2.1 --lint=warn --mode=check -r .