CI: cargo xtask ci is the gate, and the workflow runs it

This commit is contained in:
aiongg
2026-10-23 19:29:31 +00:00
parent 437b012794
commit a75a414310
3 changed files with 47 additions and 47 deletions
+5 -41
View File
@@ -1,8 +1,8 @@
name: CI
# Quality gates for every push to main and every pull request. The Linux gate runs through the
# suite's shared gate workflow in craft-common-actions; the Windows legs, the test variants and
# the corpus, wasm, web and fuzz jobs are PhotoCraft's own.
# suite's shared gate workflow in craft-common-actions (`cargo xtask ci`); the Windows legs, the
# test variant and the corpus, web and fuzz jobs are PhotoCraft's own.
on:
push:
branches: [main]
@@ -126,31 +126,15 @@ jobs:
# The tests build with the optional craft-fonts input (crates/text/build.rs), so the
# Japanese font tests that skip without it run. Same commit as release.yml.
craft-fonts-ref: abb83316d96aa59c1cf64784289e378fe9fa5695
# The wasm32 build is its own job.
targets: ""
# Cargo.toml's dev profile already keeps line tables only.
dev-debug: ""
env: |
PHOTOCRAFT_NO_PRINT=1
LPDEST=photocraft-no-such-printer
timeout-minutes: 100
# `--features heif`: official builds open HEIC photos via the optional photocraft-heif crate.
# `--lib --bins --tests` leaves out the examples, which cargo test would build and link but
# never run (Clippy's `--all-targets` still checks they compile). nextest skips doctests, so
# they run last. The `ci` profile fails fast and terminates hung tests (.config/nextest.toml).
# docs/scorecard.md must match its sources; the check reads committed files only.
# panic_hunt: every command against adversarial params must return Err, never panic or hang
# (AGENTS.md Rule 9). It is `#[ignore]`d (slow), so the test run skips it; the same packages,
# features and targets keep it from rebuilding anything.
command: |
cargo fmt --all -- --check
cargo clippy --workspace --all-targets --locked --features heif -- -D warnings
cargo xtask layers
cargo xtask scorecard --check
cargo nextest run --profile ci --workspace --locked --features heif --lib --bins --tests
cargo nextest run --profile ci --workspace --locked --features heif --lib --bins --tests \
--run-ignored only -E 'binary_id(photocraft-engine::panic_hunt)'
cargo test --workspace --locked --features heif --doc
# fmt, clippy, layers, scorecard, tests, panic_hunt, doctests and the wasm32 check, with
# `--locked --features heif` (xtask/src/main.rs).
command: cargo xtask ci
# Windows tests run as two legs in parallel: the unit tests and the integration tests. As one job
# they took ~28 min on the Windows runner; the legs take ~13 and ~15. Splitting the integration
@@ -316,26 +300,6 @@ jobs:
- name: Corpus tests
run: cargo xtask test-corpus
wasm:
if: github.event.action != 'labeled' || github.event.label.name == 'full-ci'
timeout-minutes: 30
name: wasm32 check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable
with:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3
with:
key: wasm32
save-if: ${{ github.ref == 'refs/heads/main' }}
cache-on-failure: true
- name: Check wasm-safe crates
run: cargo xtask wasm
web:
timeout-minutes: 60
name: web build and wasm size gate
+1 -1
View File
@@ -40,7 +40,7 @@ cargo run --release -p photocraft -- --control 7878 --control-token-file .privat
--automation-read-root work --automation-write-root work
cargo test --workspace # everything
cargo xtask test # tests (nextest when installed) + doctests
cargo xtask ci # fmt + clippy + tests + doctests + layers + wasm
cargo xtask ci # the CI gate: fmt, clippy, layers, scorecard, tests, panic_hunt, doctests, wasm
cargo xtask stats # tests and lines per crate
cargo xtask parity # Photoshop menu coverage -> docs/parity-checklist.md
```
+41 -5
View File
@@ -26,7 +26,9 @@ commands:
wasm cargo check --target wasm32-unknown-unknown for the wasm-safe crates (one call, plus
one per optional web feature)
test workspace tests (cargo nextest when installed, else cargo test), then doctests
ci fmt --check, clippy -D warnings, test, doctest, layers, wasm (stops at first failure)
ci the CI gate: fmt --check, clippy -D warnings, layers, scorecard --check, tests
(nextest --profile ci), panic_hunt, doctests and wasm, with --locked --features heif
(stops at first failure)
corpus [--all | --pngsuite | --psd | --psd-tools | --pixls | --atn | --photoshop | --gimp] [--local] [--update-manifest]
show where test corpora live and their pins (xtask/src/corpus_pins.rs), or fetch
them into corpus/ (pinned commits, sha256-verified; --all = every corpus but pixls,
@@ -213,6 +215,31 @@ fn cmd_test() -> Result<(), String> {
run(c, &format!("cargo {tool} {}", TEST_TARGETS.join(" ")))
}
/// What the CI gate adds to every build: `--locked`, and the optional photocraft-heif crate that
/// official builds open HEIC photos with.
const GATE_ARGS: [&str; 4] = ["--workspace", "--locked", "--features", "heif"];
/// The gate's tests. nextest's `ci` profile stops at the first failure and terminates hung tests
/// (.config/nextest.toml). `panic_hunt` runs only the engine's `#[ignore]`d panic hunt (AGENTS.md
/// Rule 9) with the same packages, features and targets as the tests, so nothing rebuilds.
fn cmd_gate_tests(panic_hunt: bool) -> Result<(), String> {
let mut c = cargo();
if nextest_available() {
c.args(["nextest", "run", "--profile", "ci"]).args(GATE_ARGS).args(&TEST_TARGETS[1..]);
if panic_hunt {
c.args(["--run-ignored", "only", "-E", "binary_id(photocraft-engine::panic_hunt)"]);
}
} else if panic_hunt {
// `-- --ignored` across the workspace would run every ignored test, not only the hunt.
c.args(["test", "-p", "photocraft-engine", "--locked", "--test", "panic_hunt", "--", "--ignored"]);
} else {
eprintln!("note: cargo-nextest is not installed, running cargo test (cargo install cargo-nextest --locked)");
c.arg("test").args(GATE_ARGS).args(&TEST_TARGETS[1..]);
}
let what = format!("cargo {}", c.get_args().map(|a| a.to_string_lossy()).collect::<Vec<_>>().join(" "));
run(c, &what)
}
/// nextest does not run doctests.
fn cmd_doctest() -> Result<(), String> {
let mut c = cargo();
@@ -241,13 +268,22 @@ fn cmd_ci() -> Result<(), String> {
"clippy",
Box::new(|| {
let mut c = cargo();
c.args(["clippy", "--workspace", "--all-targets", "--keep-going", "--", "-D", "warnings"]);
run(c, "cargo clippy --workspace --all-targets --keep-going -- -D warnings")
c.arg("clippy").args(GATE_ARGS).args(["--all-targets", "--keep-going", "--", "-D", "warnings"]);
run(c, "cargo clippy --workspace --locked --features heif --all-targets --keep-going -- -D warnings")
}),
),
("test", Box::new(cmd_test)),
("doctest", Box::new(cmd_doctest)),
("layers", Box::new(cmd_layers)),
("scorecard", Box::new(|| scorecard::run(&root(), &["--check"]))),
("test", Box::new(|| cmd_gate_tests(false))),
("panic_hunt", Box::new(|| cmd_gate_tests(true))),
(
"doctest",
Box::new(|| {
let mut c = cargo();
c.arg("test").args(GATE_ARGS).arg("--doc");
run(c, "cargo test --workspace --locked --features heif --doc")
}),
),
("wasm", Box::new(cmd_wasm)),
];
let mut done = Vec::new();