-
Notifications
You must be signed in to change notification settings - Fork 12
451 lines (436 loc) · 19.3 KB
/
Copy pathci.yml
File metadata and controls
451 lines (436 loc) · 19.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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main, release, "patch_*"]
# Cancel in-progress runs on PR pushes to save CI minutes and avoid cache-save
# races (two concurrent runs trying to save to the same cache key). Pushes to
# `main` are not cancelled so every landed commit produces a full signal.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
RUSTUP_MAX_RETRIES: 10
RUST_BACKTRACE: 1
permissions:
contents: read
jobs:
fmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install rust nightly with rustfmt
run: |
rustup toolchain install nightly --profile minimal --component rustfmt
rustup override set nightly
- name: Run cargo fmt
run: cargo fmt --all -- --check
- name: Restore helper manifest
run: cp crates/openjd-sessions/src/helper/Cargo.bundled.toml crates/openjd-sessions/src/helper/Cargo.toml
- name: Run helper cargo fmt
run: cargo fmt --manifest-path crates/openjd-sessions/src/helper/Cargo.toml --all -- --check
cargo-deny:
name: cargo-deny (licenses, advisories, bans, sources)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install cargo-deny
run: cargo install cargo-deny --locked
- name: Restore helper manifest
run: cp crates/openjd-sessions/src/helper/Cargo.bundled.toml crates/openjd-sessions/src/helper/Cargo.toml
# Licenses, bans, and sources apply to every crate we compile,
# including dev-dependencies, so scan the full graph.
- name: Run cargo deny check licenses, bans, sources (workspace)
run: cargo deny check licenses bans sources
- name: Run cargo deny check licenses, bans, sources (helper sub-crate)
run: cargo deny --manifest-path crates/openjd-sessions/src/helper/Cargo.toml check licenses bans sources
# Advisories are checked across the full dependency graph, including
# dev-dependencies.
- name: Run cargo deny check advisories (workspace)
run: cargo deny check advisories
- name: Run cargo deny check advisories (helper sub-crate)
run: cargo deny --manifest-path crates/openjd-sessions/src/helper/Cargo.toml check advisories
compliance:
name: Compliance (copyright headers)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Check copyright headers
run: bash scripts/check_copyright_headers.sh
# Build, test, and conformance share a single release-profile target/ cache.
# This avoids maintaining separate debug and release caches for the same
# workspace and keeps the total cache footprint well under 10 GB.
build-test:
name: Build & Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v7
- name: Install rust stable with clippy
shell: bash
run: |
rustup toolchain install stable --profile minimal --component clippy
rustup override set stable
# Include the rustc version in the cache key so that a toolchain upgrade
# automatically invalidates the target/ cache (stale rustc artifacts are
# one of the main reasons cache restore-key hits produce slow rebuilds).
# Also include the workspace .cargo/config.toml hash, since rustflags
# changes (e.g. linker switches) invalidate every artifact's fingerprint
# but don't show up in Cargo.lock.
- name: Compute rustc hash
id: rustc
shell: bash
run: echo "hash=$(rustc --version --verbose | sha256sum | cut -c1-16)" >> "$GITHUB_OUTPUT"
- name: Restore cargo cache
id: cache
uses: actions/cache@v6
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
target
crates/openjd-sessions/src/helper/target
key: rust-release-${{ matrix.os }}-${{ steps.rustc.outputs.hash }}-${{ hashFiles('.cargo/config.toml') }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-release-${{ matrix.os }}-${{ steps.rustc.outputs.hash }}-${{ hashFiles('.cargo/config.toml') }}-
rust-release-${{ matrix.os }}-${{ steps.rustc.outputs.hash }}-
rust-release-${{ matrix.os }}-
# On a restore-key (non-exact) hit, target/ contains artifacts built
# against a different Cargo.lock. Cargo will spend a long time scanning
# fingerprints and discovering staleness, especially on Windows. Starting
# from an empty target/ with a warm registry cache is faster. We only
# drop target/, not the registry, so dependency downloads still skip.
- name: Discard stale target/ on restore-key-only hit
if: steps.cache.outputs.cache-hit != 'true'
shell: bash
run: |
rm -rf target crates/openjd-sessions/src/helper/target
# Single unified compile pass: builds lib/bin/test targets with one
# feature-unified dep graph. This also produces the release binary
# at target/release/openjd as a side effect, so no explicit
# `cargo build` step is needed before uploading the artifact.
# openjd-for-js is a wasm32 crate built in its own job below; compiling
# it natively here wastes ~45-60s recompiling wasm-bindgen, js-sys,
# web-sys, and serde-wasm-bindgen.
- name: Restore helper manifest
shell: bash
run: cp crates/openjd-sessions/src/helper/Cargo.bundled.toml crates/openjd-sessions/src/helper/Cargo.toml
- name: Build workspace (lib, bin, tests)
run: cargo test --release --workspace --exclude openjd-for-js --all-targets --no-run
- name: Run cargo clippy
run: cargo clippy --release --all-targets --workspace --exclude openjd-for-js -- -D warnings
- name: Run helper clippy
run: cargo clippy --release --manifest-path crates/openjd-sessions/src/helper/Cargo.toml -- -D warnings
# Use cargo-nextest for the workspace test step. Nextest runs each
# `#[test]` in its own process, which means a leaked grandchild that
# holds an inherited stdio pipe open only stalls *that* test process
# rather than the whole binary — and nextest's leak-timeout
# (configured in `.config/nextest.toml`) actively detects that case
# and reports it as a failed test instead of hanging the runner. The
# `timeout-minutes` job-step guard converts a worst-case stall into a
# clean step failure with full logs preserved, rather than a
# "runner lost communication" abort whose logs are truncated.
# Install cargo-nextest from crates.io. We avoid third-party
# marketplace actions for this repo; `cargo install --locked` adds
# ~3-5 min on a cold cache but is fully reproducible from the
# registry and doesn't introduce a new supply-chain dependency.
# The cargo cache (set up earlier in this job) carries the binary
# across runs so steady-state cost is negligible.
- name: Install cargo-nextest
run: cargo install cargo-nextest --locked --version ^0.9
- name: Run tests (nextest)
timeout-minutes: 20
run: cargo nextest run --release --workspace --exclude openjd-for-js --no-fail-fast
# Nextest does not run doctests; run them separately so we don't lose
# coverage. Doctests are quick (~6 across the workspace today) and
# don't have any subprocess paths that could stall.
- name: Run doctests
timeout-minutes: 5
run: cargo test --release --workspace --exclude openjd-for-js --doc
- name: Run helper integration tests
timeout-minutes: 5
run: cargo test --release --manifest-path crates/openjd-sessions/src/helper/Cargo.toml
# On any failure (including timeout-minutes expiry), dump the process
# tree so we can see which workload subprocesses, if any, are still
# alive when the runner gives up. The previous "runner lost
# communication" failure (50 min silence on windows-latest) gave us
# zero info about what was holding the runner alive — this step
# closes that gap cheaply.
- name: Capture diagnostics on failure (Unix)
if: failure() && runner.os != 'Windows'
shell: bash
continue-on-error: true
run: |
echo "=== ps aux (top 50 by RSS) ==="
ps aux --sort=-%mem | head -n 50
echo "=== process tree ==="
ps -ef -H || ps -ejH
- name: Capture diagnostics on failure (Windows)
if: failure() && runner.os == 'Windows'
shell: pwsh
continue-on-error: true
run: |
Write-Host "=== Top processes by working set ==="
Get-Process | Sort-Object WorkingSet -Descending | Select-Object -First 30 |
Format-Table Id, ProcessName, WorkingSet, StartTime, CPU -AutoSize
Write-Host "=== Suspect leaked workloads (ping/cmd/openjd_helper/openjd) ==="
Get-Process -ErrorAction SilentlyContinue ping, cmd, openjd_helper, openjd |
Format-List Id, ProcessName, StartTime, Path
Write-Host "=== Process tree (PID, ParentPID, Name, CommandLine) ==="
Get-CimInstance Win32_Process |
Select-Object ProcessId, ParentProcessId, Name, CommandLine |
Sort-Object ParentProcessId, ProcessId |
Format-Table -AutoSize
- name: Upload CLI binary
uses: actions/upload-artifact@v7
with:
name: openjd-cli-${{ matrix.os }}
path: |
target/release/openjd
target/release/openjd.exe
if-no-files-found: error
# Full-feature clippy exercises optional-feature code paths (like the
# openjd-snapshots `bench` feature that pulls in clap, rand, tempfile, and
# aws-config). It adds ~1-2 minutes on top of the regular clippy run and
# rarely surfaces issues the feature-unified clippy misses, so run it only
# on `main` pushes instead of gating every PR on it.
clippy-all-features:
name: Clippy (all features)
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install rust stable with clippy
run: |
rustup toolchain install stable --profile minimal --component clippy
rustup override set stable
- name: Compute rustc hash
id: rustc
shell: bash
run: echo "hash=$(rustc --version --verbose | sha256sum | cut -c1-16)" >> "$GITHUB_OUTPUT"
# Restore-only: build-test(ubuntu) owns the cache write. Saving from
# here too would race with that job on pushes to main.
- name: Restore build-test cache (read-only)
uses: actions/cache/restore@v6
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
target
crates/openjd-sessions/src/helper/target
key: rust-release-ubuntu-latest-${{ steps.rustc.outputs.hash }}-${{ hashFiles('.cargo/config.toml') }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-release-ubuntu-latest-${{ steps.rustc.outputs.hash }}-${{ hashFiles('.cargo/config.toml') }}-
rust-release-ubuntu-latest-${{ steps.rustc.outputs.hash }}-
rust-release-ubuntu-latest-
- name: Run cargo clippy (all features)
run: cargo clippy --release --all-features --all-targets --workspace --exclude openjd-for-js -- -D warnings
conformance:
name: Conformance (${{ matrix.os }})
needs: build-test
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
include:
- os: ubuntu-latest
binary: openjd
- os: windows-latest
binary: openjd.exe
- os: macos-latest
binary: openjd
steps:
- name: Download CLI binary
uses: actions/download-artifact@v8
with:
name: openjd-cli-${{ matrix.os }}
path: bin
- name: Make binary executable
if: runner.os != 'Windows'
run: chmod +x bin/${{ matrix.binary }}
- name: Checkout openjd-specifications
uses: actions/checkout@v7
with:
repository: OpenJobDescription/openjd-specifications
path: openjd-specifications
- name: Install uv
uses: astral-sh/setup-uv@v9.0.0
- name: Run conformance tests (Unix)
if: runner.os != 'Windows'
working-directory: openjd-specifications/conformance-tests
run: PATH="$GITHUB_WORKSPACE/bin:$PATH" uv run run_openjd_cli_tests.py '2023-09/*'
- name: Run conformance tests (Windows)
if: runner.os == 'Windows'
working-directory: openjd-specifications/conformance-tests
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
$env:PATH = "$env:GITHUB_WORKSPACE\bin;$env:PATH"
uv run run_openjd_cli_tests.py '2023-09/*'
msrv:
name: MSRV
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install MSRV toolchain
run: |
rustup toolchain install 1.94.1 --profile minimal
rustup override set 1.94.1
- uses: actions/cache@v6
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
target
key: rust-msrv-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-msrv-
- name: Check compilation
run: cargo check --workspace
doc:
name: Documentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install rust stable
run: |
rustup toolchain install stable --profile minimal
rustup override set stable
- uses: actions/cache@v6
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
target
key: rust-doc-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-doc-
- name: Run cargo doc
run: cargo doc --no-deps --workspace
env:
RUSTDOCFLAGS: -D warnings
cross-user:
name: Cross-User Tests (${{ matrix.variant }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
variant: [localuser, ldap]
steps:
- uses: actions/checkout@v7
- name: Run cross-user tests
run: |
if [ "${{ matrix.variant }}" = "ldap" ]; then
bash scripts/run_cross_user_tests.sh --ldap
else
bash scripts/run_cross_user_tests.sh
fi
# Reuses the Windows build-test cache via a restore-only step so we don't
# pay another 3+ minutes recompiling openjd-expr/model/sessions. The
# test-utils feature used here isn't enabled in build-test, so cargo will
# recompile openjd-sessions with that feature — but all of its transitive
# dependencies are already cached.
cross-user-windows:
name: Cross-User Tests (Windows)
needs: build-test
runs-on: windows-latest
steps:
- uses: actions/checkout@v7
- name: Install rust stable
shell: bash
run: |
rustup toolchain install stable --profile minimal
rustup override set stable
- name: Compute rustc hash
id: rustc
shell: bash
run: echo "hash=$(rustc --version --verbose | sha256sum | cut -c1-16)" >> "$GITHUB_OUTPUT"
- name: Restore build-test cache (read-only)
uses: actions/cache/restore@v6
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
target
crates/openjd-sessions/src/helper/target
key: rust-release-windows-latest-${{ steps.rustc.outputs.hash }}-${{ hashFiles('.cargo/config.toml') }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
rust-release-windows-latest-${{ steps.rustc.outputs.hash }}-${{ hashFiles('.cargo/config.toml') }}-
rust-release-windows-latest-${{ steps.rustc.outputs.hash }}-
rust-release-windows-latest-
- name: Create test user
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $true
$password = -join ((48..57) + (65..90) + (97..122) + (33,35,36,37,38,42,64) | Get-Random -Count 10 | ForEach-Object {[char]$_})
$password = "A1a!" + $password
echo "Y" | net user openjdtestuser $password /add
echo "OPENJD_TEST_WIN_USER_PASSWORD=$password" >> $env:GITHUB_ENV
# The individual test_cross_user_windows.rs and test_windows_permissions.rs
# files are now included as modules inside the single `integration` test
# binary (see crates/openjd-sessions/tests/integration.rs). Select them by
# module-path substring. On Windows the sibling `test_cross_user.rs`
# module is `#![cfg(unix)]`-gated and compiles to zero tests, so
# `cross_user_windows` unambiguously selects only the Windows tests.
- name: Run cross-user tests (test_cross_user_windows)
env:
OPENJD_TEST_WIN_USER_NAME: openjdtestuser
run: "cargo test --release -p openjd-sessions --features test-utils --test integration -- --include-ignored --test-threads=1 test_cross_user_windows::"
- name: Run cross-user tests (test_windows_permissions)
env:
OPENJD_TEST_WIN_USER_NAME: openjdtestuser
run: "cargo test --release -p openjd-sessions --features test-utils --test integration -- --include-ignored --test-threads=1 test_windows_permissions::"
- name: Remove test user
if: always()
run: net user openjdtestuser /delete
shell: cmd
# Builds the openjd-for-js crate as WebAssembly and runs its vitest suite.
# Kept in a separate job with its own cache so that the wasm32 build doesn't
# interleave with the native workspace builds in `build-test`. The main
# workspace jobs explicitly `--exclude openjd-for-js` to avoid recompiling
# wasm-bindgen, js-sys, web-sys, and serde-wasm-bindgen there.
openjd-for-js:
name: openjd-for-js (WASM + JS tests)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install rust stable with wasm32 target
shell: bash
run: |
rustup toolchain install stable --profile minimal
rustup override set stable
rustup target add wasm32-unknown-unknown
- uses: actions/setup-node@v7
with:
node-version: 20
- name: Cache cargo
uses: actions/cache@v6
with:
path: |
~/.cargo/bin/wasm-bindgen
~/.cargo/registry
target
key: wasm-${{ hashFiles('Cargo.lock') }}
- name: Install wasm-bindgen-cli
run: which wasm-bindgen || cargo install wasm-bindgen-cli --version 0.2.126
- name: Install JS deps
run: cd crates/openjd-for-js && npm install
- name: Build WASM + generate JS bindings
run: cd crates/openjd-for-js && npm run build
- name: Run JS tests
run: cd crates/openjd-for-js && npx vitest run