Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/actions/setup-iron/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Setup iron CLI
description: >-
Download the pre-built `iron` + `__iron_runner` binaries (uploaded once by
the build workflow), put them on PATH, and — on macOS — install the Metal
Toolchain the offline `metal` compiler and the runtime `makeLibrary`
JIT both need. Used by every job that runs `iron build` / `iron test` /
`iron bench` so none of them re-compile the CLI. The calling JOB must run
`actions/checkout` first — a local composite action is read from the
checked-out workspace, and the iron commands read repo files (iron.toml,
baselines/).

inputs:
artifact:
description: Name of the uploaded iron-binary artifact to download.
required: false
default: iron-binary

runs:
using: composite
steps:
- name: Download iron binary
uses: actions/download-artifact@v4
with:
name: ${{ inputs.artifact }}
path: target/ci

- name: Make iron executable
shell: bash
run: chmod +x target/ci/iron target/ci/__iron_runner

- name: Add iron to PATH
shell: bash
run: echo "${GITHUB_WORKSPACE}/target/ci" >> "$GITHUB_PATH"

# The macos-26 image ships Xcode WITHOUT the Metal Toolchain component;
# `xcrun metal` ("cannot execute tool 'metal'") and the runtime
# `makeLibrary(source:)` JIT for cooperative-tensor kernels both need it.
# No-op on non-macOS runners (future CUDA/HIP), so the action stays
# backend-agnostic. `sw_vers` is logged because the cooperative-tensor
# skips are an OS-version gap (< 26.5 can't lower dynamic-extent coop
# tensors) — when the image rolls to 26.5+ the skips should vanish, and
# this line makes that visible instead of mysterious.
- name: Install Metal Toolchain
if: runner.os == 'macOS'
shell: bash
run: |
sw_vers
xcodebuild -downloadComponent MetalToolchain
40 changes: 40 additions & 0 deletions .github/actions/setup-rust/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Setup Rust
description: >-
Install the pinned Rust toolchain and restore the cargo build cache. Shared
by the lint and build workflows so toolchain + cache config lives in one
place. The calling JOB must run `actions/checkout` first — a local composite
action is read from the checked-out workspace, so it can't check itself out.

inputs:
components:
description: Comma-separated rustup components (e.g. "clippy" or "rustfmt").
required: false
default: ""
cache-key:
description: >-
Distinguishing key for Swatinem/rust-cache (shared-key). Jobs with
incompatible artifacts (instrumented vs plain) must use different keys.
required: false
default: "shared"
cache:
description: Set "false" to skip the cargo cache entirely (e.g. release builds).
required: false
default: "true"

runs:
using: composite
steps:
# rust-toolchain.toml pins the channel/edition; this action installs the
# default stable and cargo picks up the toml override automatically. The
# `components` input adds rustfmt / clippy when a job needs them.
- uses: dtolnay/rust-toolchain@stable
with:
components: ${{ inputs.components }}

- uses: Swatinem/rust-cache@v2
if: ${{ inputs.cache == 'true' }}
with:
shared-key: ${{ inputs.cache-key }}
# Only the default branch writes the cache; PRs read it. Keeps the
# cache from thrashing on every feature branch.
save-if: ${{ github.ref == 'refs/heads/main' }}
12 changes: 12 additions & 0 deletions .github/configs/codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ flag_management:
target: auto
threshold: 0.5%
if_ci_failed: success
# wh-iron-std MINUS the `#[kernel]` DSL bodies (excluded globally in
# `ignore:` below — the proc-macro consumes them, so they never execute
# as Rust). This flag is the kernel HOST / dispatch / registry code, the
# coverable half of the "kernels" the >= 80% goal refers to.
- name: std
paths:
- crates/wh-iron-std/
statuses:
- type: project
target: auto
threshold: 0.5%
if_ci_failed: success

# Files excluded from the line-coverage denominator:
# - facade re-exports (no logic)
Expand Down
72 changes: 72 additions & 0 deletions .github/workflows/bench.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Bench

# Manual, full-suite GPU benchmark. Not run on PRs (perf needs dedicated,
# consistent hardware to be meaningful — that arrives with the future
# per-arch runners). Dispatch it by hand to capture a baseline for a chip.
#
# `runner` is an input so the same workflow drives macOS / CUDA / HIP hosts
# once those runners exist; it reuses build.yml so the iron binary is built
# the same way as everywhere else.
on:
workflow_dispatch:
inputs:
runner:
description: Runner label to bench on (e.g. macos-26).
required: false
default: macos-26

permissions:
contents: read

env:
CLICOLOR_FORCE: 1

jobs:
build:
uses: ./.github/workflows/build.yml

bench:
name: Bench (full suite)
needs: build
runs-on: ${{ inputs.runner }}
timeout-minutes: 120
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v5.0.0
- uses: ./.github/actions/setup-iron

# Whole suite in one pass — no heavy/light shard split. If we bench,
# we bench everything.
- name: iron bench
run: iron bench -vv --allow-dirty --json /tmp/bench.json

- name: Snapshot result
run: |
iron snap \
--from /tmp/bench.json -o /tmp/bench-snapshot.json \
--note "manual bench @ ${{ github.sha }} (${{ github.ref_name }})"

# Best-effort trend vs the committed baseline for this chip (never
# fails the run — the runner chip often has no committed baseline).
- name: Diff vs baseline
run: |
set -uo pipefail
DEVICE_NAME=$(iron device --json | jq -r '.device')
SLUG=$(printf '%s' "$DEVICE_NAME" \
| tr '[:upper:]' '[:lower:]' \
| sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//')
BL="baselines/${SLUG}.json"
echo "chip='${DEVICE_NAME}' slug='${SLUG}' baseline='${BL}'"
if [ -f "$BL" ]; then
iron diff "$BL" /tmp/bench.json || true
else
echo "No committed baseline for this chip — snapshot uploaded as the new capture."
fi

- name: Upload bench snapshot
uses: actions/upload-artifact@v4
with:
name: bench-snapshot
path: |
/tmp/bench.json
/tmp/bench-snapshot.json
if-no-files-found: error
78 changes: 78 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: Build

# Reusable. Two stages:
# compile — cargo-build the `iron` + `__iron_runner` binaries once, cache
# them, and upload as an artifact every downstream job reuses.
# kernels — run `iron build`: MSL codegen + `xcrun metal` compile-check of
# every registered kernel × dtype.
# This replaces the old iron.yml `compile` + `build` pair (they were a
# pipeline, not a redundancy) and hands the same artifact to test /
# correctness / bench so the CLI is built exactly once per CI run.
on:
workflow_call:
inputs:
artifact:
description: Name to upload the built iron binaries under.
required: false
default: iron-binary
type: string

permissions:
contents: read

env:
CLICOLOR_FORCE: 1

jobs:
compile:
name: Compile CLI
runs-on: macos-26
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v5.0.0
- uses: dtolnay/rust-toolchain@stable

# A hit on the binary cache (keyed on every Rust input) skips the cargo
# build entirely (~3-5 min). The dep cache below is only restored on a
# miss, so a clean build still gets warm ~/.cargo.
- name: Cache iron binary
id: binary-cache
uses: actions/cache@v4
with:
path: |
target/ci/iron
target/ci/__iron_runner
key: iron-binary-${{ runner.os }}-${{ hashFiles('Cargo.lock', 'Cargo.toml', 'crates/**/Cargo.toml', 'crates/**/*.rs', 'rust-toolchain.toml') }}

- uses: Swatinem/rust-cache@v2
if: steps.binary-cache.outputs.cache-hit != 'true'
with:
shared-key: iron-macos
save-if: ${{ github.ref == 'refs/heads/main' }}

- name: Build iron CLI and runner
if: steps.binary-cache.outputs.cache-hit != 'true'
run: cargo build --profile ci -p wh-iron-cli -p wh-iron-std

- name: Upload iron binary
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.artifact }}
path: |
target/ci/iron
target/ci/__iron_runner
if-no-files-found: error
retention-days: 1

kernels:
name: Build kernels
runs-on: macos-26
timeout-minutes: 30
needs: compile
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v5.0.0
- uses: ./.github/actions/setup-iron
with:
artifact: ${{ inputs.artifact }}
- name: iron build
run: iron build
Loading
Loading