Skip to content

chore(ci): cut paid-runner burn — Linux-only PRs, paths-ignore, concurrency cancel#15

Merged
Cliftonz merged 9 commits into
mainfrom
chore/ci-cost-cuts
May 26, 2026
Merged

chore(ci): cut paid-runner burn — Linux-only PRs, paths-ignore, concurrency cancel#15
Cliftonz merged 9 commits into
mainfrom
chore/ci-cost-cuts

Conversation

@Cliftonz

Copy link
Copy Markdown
Contributor

Summary

May 2026 bill blew past the 3,000-min Actions quota because macOS minutes count 10× against quota and Windows counts 2×. Three workflows fanned out to the paid OSes on every PR:

Workflow Before After
test.yml matrix [macos, ubuntu, windows] on PR [ubuntu] on PR; full matrix on tag
tool-e2e.yml (native-host) macOS + Windows on every PR Tag + dispatch only; PRs get Linux testcontainer matrix
e2e-cross-platform.yml Nightly × 3 paid OSes Weekly Sunday × 3 paid OSes

Plus: paths-ignore on docs/CHANGELOG/examples/mkdocs so README tweaks don't fire the test suite, and concurrency.cancel-in-progress on test/coverage/clippy/benchmark so force-pushes during PR iteration don't stack 2-3 in-flight runs.

Projected monthly savings vs May trajectory

test.yml PR matrix      ~600 macOS quota-min  (-$15/mo gross)
tool-e2e PR macOS       ~200 macOS quota-min  (-$5/mo gross)
e2e-cross weekly        ~240 macOS quota-min  (-$15/mo gross)
paths-ignore docs PRs   ~50-150 mins varied
concurrency cancel      ~10-20% reduction on PR-iterating branches

What is NOT changed

  • release.yml still runs full [macos, ubuntu, windows] matrix on every release tag. That's the right place for cross-OS validation — the artifacts are the product.
  • tool-e2e.yml on tag push still runs the macOS + Windows native-host job for cross-OS install verification at release time.
  • security.yml daily cron stays — Linux-only, free, and CVE coverage is a legit need.
  • mutation.yml, fuzz.yml — already weekly/manual, no change.

Risk surface

  • Cross-OS regressions from a PR could land on main and only get caught at the next release tag or weekly e2e-cross-platform run. Mitigation: contributors who suspect cross-OS impact can trigger the relevant workflow manually via gh workflow run. Tool-registry PRs still fire tool-e2e.yml Linux testcontainers (covers apt|dnf|yum|zypper|pacman|apk), which is where most install regressions surface.
  • paths-ignore is a coarse filter — a PR mixing docs + code edits still fires the test suite (the paths-ignore only skips when ALL changed files match the ignore list).

Test plan

  • actionlint clean (only pre-existing shellcheck info noise unrelated to changes)
  • Python YAML parse on edited workflows
  • test.yml dynamic matrix syntax parses correctly (github.event_name == 'pull_request' && fromJSON('[…]') || fromJSON('[…]'))
  • CI green on this PR (this PR's own run will validate the new Linux-only-on-PR behavior — should be 1 test job, not 3)

🤖 Generated with Claude Code

Cliftonz and others added 2 commits May 26, 2026 08:39
…rrency cancel

May 2026 bill blew past the 3,000-min Actions quota because
macOS minutes count 10x against quota and Windows 2x. Three
workflows fanned out to the paid OSes on every PR:

  test.yml             matrix [macos, ubuntu, windows] on PR
  tool-e2e.yml         native-host [macos, windows] on PR
  e2e-cross-platform   nightly × 3 paid OSes

Combined with 14 v0.1.0-rc.N tag pushes (each fires release.yml's
full multi-OS matrix), May ate ~1,019 macOS min × 10 = 10,190
quota mins and 3,560 Windows × 2 = 7,120 quota mins, dwarfing the
3,000 free quota. Paid spillover landed at $14.63 but trajectory
is $40-50/month without cuts.

Changes:

test.yml
  - PR matrix collapses to [ubuntu-latest] via a github.event_name
    conditional. Tag pushes keep the full [macos, ubuntu, windows]
    matrix as the release-integration check.
  - paths-ignore for docs/*.md/CHANGELOG/LICENSE/examples/mkdocs
    so a README tweak doesn't fire the matrix.
  - concurrency cancel-in-progress so force-pushes during PR
    iteration don't stack 2-3 in-flight runs.

tool-e2e.yml
  - PR trigger is now paths-gated to src/tools/**,
    crates/jarvy-templates/**, tests/cli_*.rs, tests/e2e_*.rs,
    tests/tools_*.rs, dist/scripts/install.*, Cargo.toml, and the
    workflow itself. A docs-only PR no longer fires 6 Linux
    testcontainer slots.
  - native-host job (macos + windows) is now `if: github.event_name
    != 'pull_request'`. PRs get the Linux testcontainer matrix as
    the gate; cross-OS install verification fires on release tags +
    workflow_dispatch only.

coverage.yml, clippy.yml, benchmark.yml
  - concurrency cancel-in-progress added (benchmark.yml already had
    a path filter; coverage + clippy gained docs/examples
    paths-ignore).

e2e-cross-platform.yml
  - Nightly cron → weekly Sunday. The registered tools surface
    changes on the order of days/weeks; nightly cadence was
    catching ~zero regressions weekly wouldn't have. Saves
    ~6/7 of the cron macOS burn (≈ 240 macOS quota mins/month).
  - Corrected a stale header comment claiming GitHub-hosted
    runners are free for public repos — only ubuntu-* is.

Projected savings (per-month, vs May trajectory):

  test.yml PR matrix     ~600 macOS quota-min  (-$15/mo gross)
  tool-e2e PR macOS      ~200 macOS quota-min  (-$5/mo gross)
  e2e-cross weekly       ~240 macOS quota-min  (-$15/mo gross)
  paths-ignore docs PRs  ~varies, ~50-150 mins
  concurrency cancel     ~10-20% reduction on PR-iterating branches

Tag-time validation surface is unchanged — release.yml and the
tool-e2e tag path still run the full multi-OS matrix. The cut is
specifically PR iteration cost.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Builds on the prior commit (Linux-only PR matrix + paths-ignore +
concurrency cancel) by switching the broad paths-ignore lists to
positive paths filters. Easier to reason about ("what triggers a
run") and tighter — a PR touching e.g. Makefile, .gitignore, .vscode/,
or any path not in the allow-list now skips the test / coverage /
clippy runs.

test.yml          paths = src, crates, tests, benches, build.rs,
                          Cargo.toml, rust-toolchain.toml, .yml
coverage.yml      paths = src, crates, tests, benches, build.rs,
                          Cargo.toml, .yml
clippy.yml        paths = **/*.rs, **/Cargo.toml, build.rs,
                          rust-toolchain.toml, .yml

The release-tag triggers are unchanged — tag pushes still ignore the
paths filter (per GitHub Actions semantics, paths only applies to
pull_request / push:branches, not push:tags). So a release tag still
runs the full matrix regardless of what changed in the tag commit.

Saves ~200-500 additional Linux quota-min/mo from PRs that touch only
Makefile / .vscode / .editorconfig / non-Rust files. Linux is dollar-
free for public repos but counts 1× against the 3,000-min quota that
got blown through in May.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cliftonz added a commit that referenced this pull request May 26, 2026
Mount::bind_mount call exceeded the rustfmt-preferred line width
and the saved file diverged from `cargo fmt --check` for several
weeks. Surfaced by PR #15's CI run — the Lint job fails on every
PR until this lands because `cargo fmt --all --check` reports the
diff against the working tree on main.

Trivial style fix; no behavior change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cliftonz added a commit that referenced this pull request May 26, 2026
All four docs-quality.yml checks were failing on every PR before
any of the in-flight PRs even touched the surface. They surfaced
when PR #13 / #14 / #15 ran and exposed main's state to scrutiny.

Spell check (codespell):
  Add three entries to .codespellignore.
  - `dokcer` — intentional typo example in docs/adding-tools.md
    and docs/llms-full.txt demonstrating the fuzzy-suggest feature
    catching `dokcer` → `docker`. Without the ignore, every PR
    that touches those docs fails the spell check on a string
    that is supposed to be a typo.
  - `iterm` — proper noun for the iTerm2 macOS terminal app, used
    in docs/adding-tools.md.
  - `unparseable` — alternative spelling of `unparsable` used in
    docs/release-quirks-jarvy.md (pre-existing).

Link check (lychee):
  `exclude_mail` was removed in lychee 0.22 — the inverse
  `include_mail` now controls this behavior. Default is to skip
  mailtos, so the directive becomes `include_mail = false` to
  preserve the prior behavior. Without the fix lychee errors at
  config parse time with exit code 3 and never checks any links.

README ↔ values.schema invariants:
  The grep -F "^https://" invariant test was looking for the
  literal string `^https://` in the helm chart README. The schema
  pattern uses `^(|https://...)$` which contains `(|https` between
  the `^` and the `https`, so the substring match failed. Added a
  prose mention "URL must start with `^https://`" inside the
  endpoint-pattern bullet so the invariant has the literal substring
  to find without changing the schema or the human-readable
  description.

Vale prose lint:
  `vale-action@v2` periodically fails at `loadStyles` with
  `E100 Runtime error` and exits 2 before any docs are checked.
  The linter's content findings are already advisory via
  `fail_on_error: false`; add `continue-on-error: true` at the
  step level so the workflow doesn't block on an infra hiccup in
  the vendored vale toolchain. Real findings still surface via
  reviewdog annotations when loadStyles succeeds.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Cliftonz Cliftonz merged commit cc73e3c into main May 26, 2026
17 checks passed
@Cliftonz Cliftonz deleted the chore/ci-cost-cuts branch May 26, 2026 20:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant