Repository navigation
367 lines (351 loc) · 14.5 KB
/
Copy pathci.yml
File metadata and controls
367 lines (351 loc) · 14.5 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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
# The quality gate from docs/verification.md, as CI. `make gate` runs the same
# list locally; keep the Makefile, that page and this file in sync.
#
# Job shape follows the other vyncint projects: one job per concern, a single
# `required-green` that branch protection requires, and every action pinned to
# a full commit SHA. Dependency caches accelerate ordinary CI; release calls
# keep a clean full gate. Test execution is never conditional on a cache hit.
name: CI
on:
pull_request:
push:
branches: [main]
# Reused by release.yml so a release re-runs the exact same gates.
workflow_call:
inputs:
clean:
description: Bypass dependency caches
type: boolean
default: true
workflow_dispatch:
inputs:
clean:
description: Run the full gate without dependency caches
type: boolean
default: false
permissions:
contents: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
# A newer main tip replaces ordinary CI, but never a caller's release gate.
cancel-in-progress: ${{ github.workflow == 'CI' && (github.event_name == 'push' || github.event_name == 'pull_request') }}
env:
CARGO_TERM_COLOR: always
RUSTDOCFLAGS: -D warnings
# Without this the debug target dir (arrow + datafusion + ballista test
# binaries with full debuginfo) exceeds the hosted runners' free disk and
# rust-lld dies with SIGBUS mid-link. Debuginfo adds nothing to the gate.
CARGO_PROFILE_DEV_DEBUG: "false"
jobs:
changes:
name: CI policy and changes
runs-on: ubuntu-latest
outputs:
rust: ${{ steps.changes.outputs.rust }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
fetch-depth: 0
- run: make ci-scripts
- name: Classify the actual checkout against its base
id: changes
env:
EVENT: ${{ github.event_name }}
WORKFLOW: ${{ github.workflow }}
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
HEAD_SHA: ${{ github.sha }}
run: python3 .github/scripts/ci-policy.py changes >> "$GITHUB_OUTPUT"
fmt:
name: fmt
needs: changes
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f # v1
with:
toolchain: stable
components: rustfmt
- run: make fmt-check
# Default features are pure CPU (ADR-0006). Every optional capability is a
# feature, and a feature no job builds is a feature that is already broken —
# so each one is linted here, on a runner that has none of the hardware.
clippy:
name: clippy (${{ matrix.features.name }})
needs: changes
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
features:
- { name: default, target: lint }
- { name: cuda, target: lint-cuda }
- { name: predict, target: lint-predict }
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f # v1
with:
toolchain: stable
components: clippy
- name: Free runner disk space
run: |
sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc \
/usr/local/.ghcup /usr/local/share/powershell 2>/dev/null || true
- name: clippy
env:
TARGET: ${{ matrix.features.target }}
# Through the environment, never interpolated into the script:
# a matrix value is data, and data does not get to be shell.
run: make "$TARGET"
test:
name: test (linux, ${{ matrix.lane.name }})
needs: changes
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
lane:
- { name: default, target: test }
- { name: io-uring, target: test-io-uring }
- { name: predict, target: test-predict }
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f # v1
with:
toolchain: stable
- name: Free runner disk space
run: |
sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc \
/usr/local/.ghcup /usr/local/share/powershell 2>/dev/null || true
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
if: github.workflow == 'CI' && !inputs.clean
with:
key: ${{ runner.os }}-${{ runner.arch }}-test-${{ matrix.lane.name }}
cache-bin: false
cache-workspace-crates: false
- name: Test the selected feature configuration
env:
TARGET: ${{ matrix.lane.target }}
# Every screen a failing termlens wait carries is also written
# here, so the `report` step below can put it in the job summary
# instead of leaving it in a log someone has to scroll. The TUI
# PTY suite is a flake target (that is what stress.yml is for) and
# a flake in CI used to leave nothing but scrollback (termlens
# 0.10).
TERMLENS_ARTIFACT_DIR: ${{ runner.temp }}/termlens
run: make "$TARGET"
# `#[ignore]`d so a plain `cargo test` never installs anything behind a
# contributor's back — these crates are published. CI is where they
# should run, so CI asks for them by name. The install is the version
# Cargo.lock names, so the tool and the library are one release.
# Prebuilt, not compiled: `cargo install termlens-cli` rebuilt the tool
# from source on every run, and `cache-bin: false` means it was never
# reused. Every other tool here arrives prebuilt (cargo-deny,
# cargo-semver-checks, zizmor), and this is the rule for this
# ecosystem's workflows -- prebuilt tools, not cached builds (#48).
# The version comes from the lockfile so the tool and the library stay
# one release, and the test re-reads it and refuses a mismatch.
- name: Read the pinned termlens version
if: matrix.lane.name == 'default'
id: termlens
run: |
set -euo pipefail
version="$(awk '/^name = "termlens"$/{found=1; next} found && /^version = /{gsub(/[",]/, "", $3); print $3; exit}' Cargo.lock)"
[ -n "$version" ] || { echo "::error::no termlens version in Cargo.lock"; exit 1; }
echo "version=$version" >> "$GITHUB_OUTPUT"
- uses: taiki-e/install-action@83ac0ad63c0167e6f06796fab0fce28db1bf3db0 # v2.87.22
if: matrix.lane.name == 'default'
with:
tool: termlens-cli@${{ steps.termlens.outputs.version }}
- name: termlens-cli against the committed screens
if: matrix.lane.name == 'default'
env:
TERMLENS_ARTIFACT_DIR: ${{ runner.temp }}/termlens
TERMLENS_CLI: termlens
run: make test-termlens-cli
- name: predict adds no second CUDA stack
if: matrix.lane.name == 'predict'
run: make check-predict-no-second-cuda
- name: cuda feature compiles with no CUDA installed
if: matrix.lane.name == 'default'
run: make check-cuda
- name: dependency chain is coherent
if: matrix.lane.name == 'default'
run: make coherence
# A PTY failure is a picture, and a log is the worst place to read one.
# This renders every screen the suite left behind — and every insta
# `.snap.new` — into the job summary, with SVG and HTML uploaded. It
# installs termlens-cli itself, so nothing else here has to.
- uses: vyncint/termlens/.github/actions/report@3926896a407c6c444afb971296337135de94c650 # v0.11.6
if: failure()
with:
name: termlens-report-linux-${{ matrix.lane.name }}
- name: Upload Cargo build timings
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: timings-linux-${{ matrix.lane.name }}
path: target/cargo-timings/*.html
retention-days: 7
if-no-files-found: ignore
# The Metal lane. Device-independent tests always; the on-device conformance
# suite only when the runner exposes a Metal device (asked of the Metal API
# itself, not system_profiler — virtualised runners lie to the latter).
metal:
name: metal (macos)
needs: changes
if: needs.changes.outputs.rust == 'true'
runs-on: macos-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f # v1
with:
toolchain: stable
components: clippy
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
if: github.workflow == 'CI' && !inputs.clean
with:
key: ${{ runner.os }}-${{ runner.arch }}-metal
cache-bin: false
cache-workspace-crates: false
- run: make lint-metal
- name: Metal package tests and macOS PTY suite
env:
TERMLENS_ARTIFACT_DIR: ${{ runner.temp }}/termlens
run: make test-metal
- name: conformance suite on the Metal device
env:
TERMLENS_ARTIFACT_DIR: ${{ runner.temp }}/termlens
run: make test-metal-device
# macOS is the leg where the PTY suite times out first, and the one
# nobody can reproduce locally on a Linux box.
- uses: vyncint/termlens/.github/actions/report@3926896a407c6c444afb971296337135de94c650 # v0.11.6
if: failure()
with:
name: termlens-report-metal
- name: Upload Cargo build timings
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: timings-metal
path: target/cargo-timings/*.html
retention-days: 7
if-no-files-found: ignore
# The declared rust-version must actually build the workspace against the
# committed lockfile. This is what makes `rust-version` a verified claim.
msrv:
name: msrv
needs: changes
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: Read MSRV from Cargo.toml
id: msrv
run: |
msrv="$(sed -n 's/^rust-version *= *"\(.*\)"/\1/p' Cargo.toml)"
echo "MSRV: ${msrv}"
echo "msrv=${msrv}" >> "$GITHUB_OUTPUT"
- uses: dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f # v1
with:
toolchain: ${{ steps.msrv.outputs.msrv }}
- run: cargo check --workspace --all-targets --locked
docs:
name: docs
needs: changes
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f # v1
with:
toolchain: stable
- run: make doc
# Supply chain: RustSec advisories, license allow-list, banned crates and
# registry sources — exactly what `make deny` runs locally (deny.toml).
deny:
name: deny
needs: changes
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: EmbarkStudios/cargo-deny-action@3c6349835b2b7b196a839186cb8b78e02f7b5f25 # v2
with:
command: check
arguments: --all-features
# The release path's one otherwise-untested script: it turns CHANGELOG.md
# into release notes, and an empty section must fail before publish.
release-scripts:
name: release scripts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- run: ./.github/scripts/test-extract-changelog.sh
# What crates.io will show for every crate: README, repository,
# keywords, categories, docs.rs link. Inherited fields must be opted
# into per manifest; 0.1.0 shipped without them.
- uses: dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f # v1
with:
toolchain: stable
- run: ./.github/scripts/check-crate-metadata.sh
# The vendored termlens skill is refreshed by hand, and a stale copy is
# silent: agents working here get guidance for a version that is no longer
# the dependency. Nothing can diff it against upstream — the published
# crate does not ship the skill — but the two versions agreeing is
# checkable, and that is the drift that happens (`make skill-version`).
skill-version:
name: skill version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- run: make skill-version
# Workflow security audit: unpinned actions, credential persistence,
# template injection. Accepted findings live in .github/zizmor.yml.
zizmor:
name: zizmor
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: Audit workflows at pedantic level
env:
GH_TOKEN: ${{ github.token }}
run: pipx run zizmor==1.29.0 --persona=pedantic .github/workflows/
# Single stable job name for branch protection: require this one check and
# matrix or job changes never break the required-checks configuration.
required-green:
name: required-green
if: always()
needs: [changes, fmt, clippy, test, metal, msrv, docs, deny, release-scripts, skill-version, zizmor]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: Verify every needed job succeeded
env:
NEEDS: ${{ toJSON(needs) }}
run: python3 .github/scripts/ci-policy.py gate