Skip to content

ci: restructure into reusable workflows + PR coverage gating - #79

Closed
ekryski wants to merge 3 commits into
ek/fix-depthwise-conv-boundsfrom
ek/ci-restructure
Closed

ci: restructure into reusable workflows + PR coverage gating#79
ekryski wants to merge 3 commits into
ek/fix-depthwise-conv-boundsfrom
ek/ci-restructure

Conversation

@ekryski

@ekryski ekryski commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

What

Overhauls GitHub Actions into focused, reusable workflows driven by one orchestrator, and turns coverage into a PR gate.

Stacked on #77 (ek/fix-depthwise-conv-bounds) — it depends on that PR's MPP toolchain-probe skips + Metal Toolchain install. Base will be retargeted to dev once #77 merges; review only the commits on this branch.

Structure

changes ─ lint ─┬─ build ── correctness
                ├─ test
                └─ coverage
                      (tag) ─ release   [needs build + test + correctness]

Composite actions (DRY): setup-rust (toolchain + cache), setup-iron (download the once-built binary + install the macos-26 Metal Toolchain).

Reusable workflows called by ci.yml:

File Role Runner
lint.yml typos, fmt, clippy, commit-hygiene — every PR + release ubuntu
build.yml compile the CLI once (cached + artifact) → iron build kernel check. Consolidates iron.yml's redundant compile+build macOS
test.yml Iron Core (ubuntu) cargo logic + macOS GPU-integration cargo tests (minus the per-kernel harness, owned by correctness) both
correctness.yml iron test vs CPU oracle — parameterized by runner+backend for future CUDA/HIP macOS
coverage.yml instrumented llvm-cov --workspace, uploads to codecov macOS
bench.yml full suite, consolidated (no heavy/light split), manual dispatch only macOS

check.yml + iron.yml are deleted; coverage.yml/release.yml became reusable; pr.yml unchanged.

Behavior

  • lint runs on every PR and release; build/test/correctness/coverage run after lint, on code PRs (a light path filter skips docs-only PRs) and always on pushes/tags/dispatch.
  • The iron binary is built once per run and shared via artifact — no re-compiles across jobs.
  • Releases are gated: a v* tag runs the full suite before release.yml publishes.

Coverage gating

  • Coverage now runs on PRs (was push-to-main only) → codecov posts the diff-coverage comment.
  • codecov.yml enforces: patch ≥ 80% on new/changed code (so 0%-covered new code fails) and a per-crate + project ratchet that fails on any coverage decrease. Added a std flag for wh-iron-std's host/dispatch code.
  • #[kernel] DSL bodies stay excluded from line coverage — the proc-macro consumes them so they never execute as Rust; they're validated via GPU correctness + bench.

Two follow-ups for the repo owner:

  1. Add the codecov project + patch checks to the branch-protection rule so coverage actually blocks merges.
  2. To make the ≥80% goal a hard project floor (vs the current no-regression ratchet), flip project target: autotarget: 80% once the codebase reaches 80% — the first PR coverage run shows where it stands.

Validation

actionlint clean on all workflows + composites. The reusable-workflow + composite wiring was proven live on a prior push (lint jobs ran under the orchestrator as Lint / *); this PR's own CI run exercises the full graph end-to-end.

ekryski added 2 commits July 29, 2026 15:41
…bench)

Replaces the monolithic `check.yml` + `iron.yml` with focused, reusable
workflows driven by one orchestrator, so each concern is its own file, the
iron binary is built once per run, and adding CUDA/HIP runners later is a
one-line matrix entry.

  ci.yml (orchestrator)   changes → lint → {build, test, correctness}
                          release (tag only, needs build+test+correctness)

  lint.yml    typos, fmt, clippy, commit-hygiene (ubuntu). Every PR + release.
  build.yml   compile the iron CLI once (cached, uploaded) → `iron build`
              kernel compile-check. Consolidates iron.yml's compile+build.
  test.yml    cargo nextest: Rust logic on ubuntu (GPU tests cfg out there) +
              a macOS job for the GPU integration tests, minus the per-kernel
              harness (owned by correctness).
  correctness.yml  `iron test` (GPU-vs-CPU-oracle), parameterised by
              runner+backend for future NVIDIA/AMD hosts.
  bench.yml   full suite (no heavy/light split), manual dispatch only.

Shared setup is DRY'd into composite actions (.github/actions/setup-rust,
setup-iron — the latter also installs the macos-26 Metal Toolchain). Light
path filter skips docs-only PRs for the heavy jobs; lint still runs on them.
Releases are gated: on a `v*` tag ci.yml runs the full suite before calling
release.yml. coverage.yml + pr.yml keep their separate concerns.
- Rename test.yml's ubuntu cargo job `logic` -> `iron` / "Iron Core (ubuntu)".
- Make coverage.yml reusable (workflow_call) and wire it into ci.yml so
  instrumented `llvm-cov --workspace` runs on every code PR (was push-to-main
  only). codecov now posts the diff-coverage comment on PRs and its status
  checks can gate merges.
- codecov.yml already enforces the requested gates — patch (new/changed code)
  >= 80% so 0%-covered new code fails, and a per-crate + project ratchet that
  fails on any coverage decrease. Add a `std` flag for wh-iron-std's host /
  dispatch code (the coverable half of "kernels"; the `#[kernel]` DSL bodies
  stay excluded — the proc-macro consumes them so they never execute as Rust,
  and are validated via GPU correctness / bench instead).

To make coverage BLOCK merges, add the codecov "project" and "patch" checks
to the branch protection rule. To turn the >= 80% goal into a hard project
floor (vs the current no-regression ratchet), flip project `target: auto` ->
`target: 80%` once the codebase is at 80% — the first PR coverage run will
show where it stands.
…ers, timeouts)

Addresses the "no false skips/passes/failures" review concern now that the
restructure routes GPU kernel correctness through `iron test` (correctness.yml)
instead of the cargo `all_registered_kernel_tests_pass` harness.

- `iron test`: with NO filter and zero registered `#[test_kernel]`s, FAIL
  instead of returning Ok. Zero kernels means the inventory failed to link
  (dead-strip / registration regression), not that everything passed — a
  correctness gate that dispatched nothing must never report green. This
  restores the cargo harness's `total > 0` guard on the CLI path. (A
  user-supplied filter matching nothing stays a benign warning.)
  The runner's MPP skip is already precise — it requires BOTH
  `requires_cooperative_tensors()` AND the `deferred-static-alloca` marker
  (harness.rs), so no MPP kernel is skipped by a coarse family gate.

- Log `sw_vers` on every macOS job (via the setup-iron composite + the
  standalone Metal Toolchain steps). The coop-tensor skips are an OS-version
  gap (< 26.5); when the image rolls forward the skips vanish on their own and
  this makes that visible. (Tom's review rec.)

- Bump the macOS GPU-sweep timeouts 30 -> 45 min (correctness / test-gpu /
  coverage) — headroom as the kernel registry grows, per the review's
  timeout watch-item. The ubuntu logic job stays at 30.
@ekryski
ekryski deleted the branch ek/fix-depthwise-conv-bounds July 29, 2026 22:04
@ekryski ekryski closed this Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ignore-for-release Hidden from release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant