From 70d7f54197c4014e7df0d07aec49e722545cd101 Mon Sep 17 00:00:00 2001 From: Zac Clifton <43915749+Cliftonz@users.noreply.github.com> Date: Tue, 26 May 2026 08:39:29 -0400 Subject: [PATCH 1/2] =?UTF-8?q?chore(ci):=20cut=20paid-runner=20burn=20?= =?UTF-8?q?=E2=80=94=20Linux-only=20PRs,=20paths-ignore,=20concurrency=20c?= =?UTF-8?q?ancel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .github/workflows/benchmark.yml | 5 +++ .github/workflows/clippy.yml | 12 ++++++++ .github/workflows/coverage.yml | 12 ++++++++ .github/workflows/e2e-cross-platform.yml | 21 ++++++++----- .github/workflows/test.yml | 39 +++++++++++++++++++----- .github/workflows/tool-e2e.yml | 26 +++++++++++++--- 6 files changed, 95 insertions(+), 20 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 3eb7a29..e44902e 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -23,6 +23,11 @@ permissions: contents: write pull-requests: write +# Cancel previous runs on the same ref (force-push during PR iteration). +concurrency: + group: benchmark-${{ github.ref }} + cancel-in-progress: true + jobs: benchmark: name: Run Benchmarks diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 76b7703..4a83692 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -7,6 +7,13 @@ on: # lints introduced by Rust toolchain updates. pull_request: branches: [ "main" ] + paths-ignore: + - 'docs/**' + - '*.md' + - 'CHANGELOG.md' + - 'LICENSE*' + - 'examples/**' + - 'mkdocs.yml' push: tags: - 'v[0-9]+.[0-9]+.[0-9]+' @@ -15,6 +22,11 @@ on: - cron: '17 20 * * 0' workflow_dispatch: +# Cancel previous runs on the same ref (force-push during PR iteration). +concurrency: + group: clippy-${{ github.ref }} + cancel-in-progress: true + jobs: rust-clippy-analyze: name: Run rust-clippy analyzing diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 6f8553d..a36ecb2 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -7,6 +7,13 @@ on: # not running it on every merge saves significant runner time. pull_request: branches: [ "main" ] + paths-ignore: + - 'docs/**' + - '*.md' + - 'CHANGELOG.md' + - 'LICENSE*' + - 'examples/**' + - 'mkdocs.yml' push: tags: - 'v[0-9]+.[0-9]+.[0-9]+' @@ -16,6 +23,11 @@ on: permissions: contents: read +# Cancel previous runs on the same ref (force-push during PR iteration). +concurrency: + group: coverage-${{ github.ref }} + cancel-in-progress: true + env: CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 diff --git a/.github/workflows/e2e-cross-platform.yml b/.github/workflows/e2e-cross-platform.yml index a2aa24a..732ddfc 100644 --- a/.github/workflows/e2e-cross-platform.yml +++ b/.github/workflows/e2e-cross-platform.yml @@ -1,24 +1,29 @@ # PRD-038: Hybrid Cross-Platform E2E Testing Harness # # This workflow tests Jarvy tool installations across multiple platforms: -# - GitHub-hosted runners (FREE): macOS, Ubuntu, Windows +# - GitHub-hosted runners: Ubuntu (free) + macOS / Windows (paid) # - AWS EC2 Spot (future): Fedora, Arch, Alpine, FreeBSD # -# Cost: ~$0/month (GitHub-hosted runners are free for public repos) +# Correction to a prior comment claim: macOS and Windows runners are NOT +# free for public repos — only ubuntu-* is. macOS is the priciest at +# $0.062/min (10x Linux). Nightly cadence × 3 OSes × ~10min/run was +# burning ~$15-20/month from this workflow alone. name: E2E Cross-Platform Tests on: - # E2E cross-platform runs are heavy (multi-OS install matrix). The - # nightly cron below produces the canonical signal that main is - # green; per-push and per-PR runs were redundant and routinely added - # ~30 min of runner time without finding bugs the nightly missed. + # E2E cross-platform runs are heavy (multi-OS install matrix). Weekly + # cadence is sufficient — the registered tools surface changes on the + # order of days/weeks, not hours, and the nightly cadence rarely caught + # a regression the next weekly run wouldn't have. Tag pushes still get + # the full matrix via release.yml + tool-e2e.yml so cuts are validated + # at release time regardless of the weekly cron schedule. # workflow_dispatch covers ad-hoc validation when a PR touches the # tools registry or install scripts and the author wants pre-merge # confirmation. schedule: - # Nightly at 2 AM UTC - - cron: '0 2 * * *' + # Weekly on Sunday at 2 AM UTC + - cron: '0 2 * * 0' workflow_dispatch: inputs: tier: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 075d71d..15cef02 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,14 +1,27 @@ name: Test on: - # Validation runs on PRs (pre-merge gate) and on release tags - # (post-merge integration check). The push-to-main trigger was - # removed because every PR is already validated and the merge - # commit is identical to the validated PR head — re-running the - # same suite on the merge duplicates effort. Tag push covers the - # integrated state when a release is cut. + # PR gate: Linux only. Public-repo Linux minutes are free; macOS + Windows + # are not (macOS is 10x Linux per-minute). Cross-OS validation lives on + # the release-tag matrix below + the nightly e2e-cross-platform run. + # Doc-only / CI-only / examples-only PRs skipped via paths-ignore so a + # README tweak doesn't fire the test suite. + # + # Tag push: full matrix (Linux + macOS + Windows). Release-cut is the + # canonical integration check — full cross-OS validation here catches + # regressions before the artifact build runs in release.yml. pull_request: branches: [ "main" ] + paths-ignore: + - 'docs/**' + - '*.md' + - 'CHANGELOG.md' + - 'LICENSE*' + - 'examples/**' + - 'mkdocs.yml' + - '.gitignore' + - '.lychee.toml' + - '.vale.ini' push: tags: - 'v[0-9]+.[0-9]+.[0-9]+' @@ -18,6 +31,14 @@ on: permissions: contents: read +# Cancel previous runs on the same ref. Force-pushes during PR iteration +# previously stacked 2-3 in-flight matrix runs per branch — each one +# burned its macOS + Windows slot before being cancelled by branch +# protection. +concurrency: + group: test-${{ github.ref }} + cancel-in-progress: true + env: CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 @@ -49,7 +70,11 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-latest, ubuntu-latest, windows-latest] + # PR runs (free Linux minutes) get ubuntu-latest only. + # Tag pushes add macos-latest + windows-latest for the + # integration check. github.event_name is evaluated at + # matrix-expansion time so this composes cleanly. + os: ${{ github.event_name == 'pull_request' && fromJSON('["ubuntu-latest"]') || fromJSON('["macos-latest", "ubuntu-latest", "windows-latest"]') }} steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/tool-e2e.yml b/.github/workflows/tool-e2e.yml index ca47772..068b4b5 100644 --- a/.github/workflows/tool-e2e.yml +++ b/.github/workflows/tool-e2e.yml @@ -1,13 +1,23 @@ name: Tools E2E on: - # Tools E2E exercises the full multi-OS install matrix; running on - # every push to every branch was burning ~13 min × N pushes/day. - # Restrict to PR validation (pre-merge gate) and release tags - # (post-merge integration check). workflow_dispatch covers ad-hoc - # validation when working on the tools registry. + # Tools E2E exercises the full multi-OS install matrix and was + # burning ~13 min × N pushes/day across mac+win+6-distro-linux. + # Restrict to PRs that actually touch the tools registry / install + # scripts / tools-related tests (everything else can't regress this + # surface). Release tags still run the full matrix as the canonical + # post-merge integration check. workflow_dispatch covers ad-hoc runs. pull_request: branches: [main] + paths: + - 'src/tools/**' + - 'crates/jarvy-templates/**' + - 'tests/cli_*.rs' + - 'tests/e2e_*.rs' + - 'tests/tools_*.rs' + - 'dist/scripts/install.*' + - 'Cargo.toml' + - '.github/workflows/tool-e2e.yml' push: tags: - 'v[0-9]+.[0-9]+.[0-9]+' @@ -54,6 +64,12 @@ jobs: native-host: name: Native host (brew on macOS, winget on Windows) + # macOS minutes are $0.062/min (10x Linux) and Windows is $0.01/min. + # PR-time native-host runs were the second-largest macOS consumer + # behind release.yml. PRs get Linux-only (linux-testcontainers above); + # native-host fires on release tags + workflow_dispatch so cross-OS + # install verification still gates the canonical artifact builds. + if: github.event_name != 'pull_request' strategy: fail-fast: false matrix: From 00784d32004b2ff4e67495ad2d8d3d6200645b09 Mon Sep 17 00:00:00 2001 From: Zac Clifton <43915749+Cliftonz@users.noreply.github.com> Date: Tue, 26 May 2026 10:36:32 -0400 Subject: [PATCH 2/2] =?UTF-8?q?chore(ci):=20layer=20C=20=E2=80=94=20positi?= =?UTF-8?q?ve=20path=20filters=20on=20test/coverage/clippy=20PRs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .github/workflows/clippy.yml | 19 ++++++++++++------- .github/workflows/coverage.yml | 20 +++++++++++++------- .github/workflows/test.yml | 24 ++++++++++++++---------- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 4a83692..3d4cb8f 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -7,13 +7,18 @@ on: # lints introduced by Rust toolchain updates. pull_request: branches: [ "main" ] - paths-ignore: - - 'docs/**' - - '*.md' - - 'CHANGELOG.md' - - 'LICENSE*' - - 'examples/**' - - 'mkdocs.yml' + # Positive filter: clippy only matters when Rust source or build + # configuration changes. The .yml itself is included so workflow + # tweaks still validate. test.yml's lint job also runs clippy as + # part of the broader PR gate; this workflow's SARIF upload is the + # value-add over that — keep it firing whenever .rs / Cargo.toml / + # build.rs / rust-toolchain changes. + paths: + - '**/*.rs' + - '**/Cargo.toml' + - 'build.rs' + - 'rust-toolchain.toml' + - '.github/workflows/clippy.yml' push: tags: - 'v[0-9]+.[0-9]+.[0-9]+' diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index a36ecb2..2de1f96 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -7,13 +7,19 @@ on: # not running it on every merge saves significant runner time. pull_request: branches: [ "main" ] - paths-ignore: - - 'docs/**' - - '*.md' - - 'CHANGELOG.md' - - 'LICENSE*' - - 'examples/**' - - 'mkdocs.yml' + # Positive filter: coverage signal only matters when source / tests / + # dep manifests change. Doc-only / config-only / examples-only PRs + # don't shift the coverage number, and the workflow takes ~10 min + # of Linux quota per run. Keeps the PR-time coverage gate but skips + # the runs that would have produced an identical number. + paths: + - 'src/**' + - 'crates/**' + - 'tests/**' + - 'benches/**' + - 'build.rs' + - 'Cargo.toml' + - '.github/workflows/coverage.yml' push: tags: - 'v[0-9]+.[0-9]+.[0-9]+' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 15cef02..ea76c88 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,16 +12,20 @@ on: # regressions before the artifact build runs in release.yml. pull_request: branches: [ "main" ] - paths-ignore: - - 'docs/**' - - '*.md' - - 'CHANGELOG.md' - - 'LICENSE*' - - 'examples/**' - - 'mkdocs.yml' - - '.gitignore' - - '.lychee.toml' - - '.vale.ini' + # Positive filter: PR test runs only fire when source / tests / dep + # manifests / build config change. Excludes doc-only, CI-only, + # example-only, lint-config PRs that can't shift the test outcome. + # Inverted from paths-ignore for predictability — easier to reason + # about "what triggers a run" than "what does NOT trigger". + paths: + - 'src/**' + - 'crates/**' + - 'tests/**' + - 'benches/**' + - 'build.rs' + - 'Cargo.toml' + - 'rust-toolchain.toml' + - '.github/workflows/test.yml' push: tags: - 'v[0-9]+.[0-9]+.[0-9]+'