From de290c072fd19a332979dc71b099ab69fa3e4795 Mon Sep 17 00:00:00 2001 From: NessZerra <90105158+Finesssee@users.noreply.github.com> Date: Fri, 24 Jul 2026 12:17:46 +0700 Subject: [PATCH 1/2] ci: thin Blacksmith PR Check and budget modes for shared free tier --- .github/CI.md | 51 +++++++++++++++++ .github/workflows/ci.yml | 57 ++++++++++--------- .github/workflows/release.yml | 28 ++++++--- CONTEXT.md | 20 +++++++ .../0001-pr-check-blacksmith-local-release.md | 3 + docs/adr/0002-ci-budget-modes.md | 3 + 6 files changed, 126 insertions(+), 36 deletions(-) create mode 100644 .github/CI.md create mode 100644 CONTEXT.md create mode 100644 docs/adr/0001-pr-check-blacksmith-local-release.md create mode 100644 docs/adr/0002-ci-budget-modes.md diff --git a/.github/CI.md b/.github/CI.md new file mode 100644 index 0000000..824853d --- /dev/null +++ b/.github/CI.md @@ -0,0 +1,51 @@ +# CI Operator Guide + +linear-cli shares a Blacksmith free-tier runner pool with sibling repos. This +guide tells an operator how to control who runs. + +## Budget mode + +Set the org/repo variable `CI_BUDGET_MODE` (Settings → Secrets and variables → +Actions → Variables). Unset/empty is treated as `normal`. + +| Mode | PR Check | Release (dispatch) | When to use | +|----------|----------|--------------------|-----------------------------------------------| +| `normal` | runs | runs | Default. A real gate on every PR. | +| `thin` | skipped | runs | Defer linear-cli so Win-CodexBar gets the pool.| +| `off` | skipped | skipped | Pause all CI for this repo. | + +- PR Check `if`: `vars.CI_BUDGET_MODE != 'off' && vars.CI_BUDGET_MODE != 'thin'` +- Release `if`: `vars.CI_BUDGET_MODE != 'off'` + +## Intended split + +Across the shared repos the intended split is roughly **60 / 30 / 10**: +- **60%** of time in `normal` — PR Check runs on every PR. +- **30%** in `thin` — linear-cli defers so Win-CodexBar (the priority repo) + gets uninterrupted pool access. +- **10%** in `off` — paused, e.g. during a pool freeze or investigation. + +This is a planning target, not a hard cap. Move to `thin` whenever Win-CodexBar +has open PRs competing for the pool. + +## $0 spend alert + +Blacksmith bills the free pool per runner-minute, and **Windows bills ~2x +Linux**. PR Check is Linux-only and macOS release builds stay on GitHub-hosted +`macos-latest` specifically to avoid burning the Blacksmith pool. If you see +free-tier minutes dropping faster than the 60/30/10 plan accounts for, set +`CI_BUDGET_MODE=off` on the non-priority repo first. + +## Release + +Releases are **local by default**. See `docs/manual-release.md` for the +`cargo publish` + `gh release` sequence. The `release.yml` workflow is +**dispatch-only** (Actions → Release → Run workflow) and never auto-runs on a +tag or GitHub release. When dispatched: + +- Linux x86_64 / aarch64 → `blacksmith-4vcpu-ubuntu-2404` +- Windows x86_64 → `blacksmith-4vcpu-windows-2025` +- macOS x86_64 / aarch64 → `macos-latest` (GitHub-hosted, not Blacksmith) + +A dispatched release still runs in `thin` mode (operator intent overrides the +defer), but is skipped in `off`. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d76b4ea..ea10edf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,31 +1,53 @@ name: CI +# Thin PR Check for the linear-cli repo. Runs only on Blacksmith's Linux pool +# so the shared Blacksmith free tier is not burned by macOS/Windows builds. +# See .github/CI.md and docs/adr/0001-pr-check-blacksmith-local-release.md for +# why this is Linux-only and how the budget modes gate it. + on: push: branches: [master, main] + paths-ignore: + - "docs/**" + - "skills/**" + - "**/*.md" + - "LICENSE" pull_request: branches: [master, main] + paths-ignore: + - "docs/**" + - "skills/**" + - "**/*.md" + - "LICENSE" + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true env: CARGO_TERM_COLOR: always jobs: - test: - name: Test - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + pr-check: + name: PR Check + runs-on: blacksmith-4vcpu-ubuntu-2404 + # Budget mode gate: + # normal (unset/empty) -> run + # thin -> skip (Win-CodexBar priority) + # off -> skip + if: vars.CI_BUDGET_MODE != 'off' && vars.CI_BUDGET_MODE != 'thin' steps: - uses: actions/checkout@v4 + - name: Install Linux keyring build deps - if: runner.os == 'Linux' run: sudo apt-get update && sudo apt-get install -y libdbus-1-dev pkg-config - - name: Install Rust uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy - name: Cache cargo uses: Swatinem/rust-cache@v2 @@ -43,22 +65,3 @@ jobs: # secure-storage still compile. - name: Build without secure-storage run: cargo build --verbose - - build: - name: Build - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Install Linux keyring build deps - run: sudo apt-get update && sudo apt-get install -y libdbus-1-dev pkg-config - - - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - - - name: Cache cargo - uses: Swatinem/rust-cache@v2 - - - name: Build release - run: cargo build --release --features secure-storage diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a773c83..7bc5096 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,8 +1,12 @@ name: Release +# Dispatch-only release. Never auto-runs on tag/release so the shared +# Blacksmith free tier is not consumed by surprise release builds. The +# workflow stays local-by-default: an operator dispatches it after a manual +# `cargo publish`. See docs/adr/0001-pr-check-blacksmith-local-release.md. + on: - release: - types: [created] + workflow_dispatch: env: CARGO_TERM_COLOR: always @@ -10,36 +14,40 @@ env: jobs: build: name: Build ${{ matrix.target }} - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.runner }} + # Budget gate: release only runs when not in `off` mode. `thin` still + # allows a manually dispatched release (operator intent), only PR Check + # honors `thin`. + if: vars.CI_BUDGET_MODE != 'off' permissions: contents: read strategy: matrix: include: - target: x86_64-pc-windows-msvc - os: windows-latest + runner: blacksmith-4vcpu-windows-2025 ext: .exe - target: x86_64-apple-darwin - os: macos-latest + runner: macos-latest ext: "" - target: aarch64-apple-darwin - os: macos-latest + runner: macos-latest ext: "" - target: x86_64-unknown-linux-gnu - os: ubuntu-latest + runner: blacksmith-4vcpu-ubuntu-2404 ext: "" - target: aarch64-unknown-linux-gnu - os: ubuntu-latest + runner: blacksmith-4vcpu-ubuntu-2404 ext: "" use_cross: true steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - name: Install Linux keyring build deps if: runner.os == 'Linux' && !matrix.use_cross run: sudo apt-get update && sudo apt-get install -y libdbus-1-dev pkg-config - - name: Install Rust uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable with: @@ -79,6 +87,7 @@ jobs: name: Upload Release Assets needs: build runs-on: ubuntu-latest + if: vars.CI_BUDGET_MODE != 'off' permissions: contents: write @@ -96,6 +105,7 @@ jobs: name: Publish to crates.io needs: upload runs-on: ubuntu-latest + if: vars.CI_BUDGET_MODE != 'off' permissions: contents: read diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 0000000..276e54c --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,20 @@ +# Shared CI Budget + +linear-cli shares a Blacksmith free-tier runner pool with other repos (notably Win-CodexBar). To keep one repo from starving the others, CI runs are gated by a single org/repo variable, `CI_BUDGET_MODE`, and PR Check is trimmed to a single Linux job. Releases are local by default and the release workflow is dispatch-only. This file is the shared vocabulary so operators and contributors mean the same thing across repos. + +## Language + +**PR Check**: The gate that runs on every push/PR. For linear-cli it is a single Linux job on `blacksmith-4vcpu-ubuntu-2404`: `cargo test`, `cargo fmt --check`, `cargo clippy` (all with `--features secure-storage`), plus one default-features `cargo build`. Windows/macOS are intentionally absent from PR Check to protect the shared pool. +_Avoid_: "the test matrix", "CI" (CI is the whole workflow, not just the gate). + +**Release**: Cutting and publishing a version — `cargo publish` plus GitHub release assets. For linear-cli this is local by default; the `release.yml` workflow exists only as a dispatch fallback and never auto-runs on tag/release. +_Avoid_: "deploy" (nothing is deployed; binaries are uploaded to a GitHub release). + +**Blacksmith Pool**: The shared free tier of Blacksmith runners (`blacksmith-4vcpu-ubuntu-2404`, `blacksmith-4vcpu-windows-2025`) that linear-cli and sibling repos draw from. Windows builds bill ~2x against this pool versus Linux, which is why PR Check stays Linux-only and macOS release builds stay on GitHub-hosted `macos-latest`. +_Avoid_: "Blacksmith runners" without naming the shared-pool constraint; "the cluster". + +**Local Release**: The default way to release — an operator runs `cargo publish` and `gh release create/upload` by hand (see `docs/manual-release.md`). The dispatch-only `release.yml` is a fallback, not the primary path. +_Avoid_: "manual release" interchangeably with the workflow; the workflow is "dispatch release". + +**Budget Mode**: The value of org/repo variable `CI_BUDGET_MODE` — `normal` (PR Check runs), `thin` (linear-cli PR Check skips so Win-CodexBar gets priority), `off` (all CI skips). Unset/empty is treated as `normal`. PR Check honors `thin`; a manually dispatched Release still runs in `thin` because the operator asked for it. +_Avoid_: "spend mode", "throttle". diff --git a/docs/adr/0001-pr-check-blacksmith-local-release.md b/docs/adr/0001-pr-check-blacksmith-local-release.md new file mode 100644 index 0000000..38933d3 --- /dev/null +++ b/docs/adr/0001-pr-check-blacksmith-local-release.md @@ -0,0 +1,3 @@ +# ADR 0001: PR Check on Blacksmith, local release by default + +The linear-cli repo shared a Blacksmith free-tier pool with other projects, and the prior CI ran Linux/Windows/macOS test jobs plus a release workflow that auto-fired on every GitHub release — burning the pool on OS matrix builds and on surprise release runs. We move the PR Check to a single Linux-only job on `blacksmith-4vcpu-ubuntu-2404` (cargo test + fmt + clippy with `--features secure-storage`, plus one default-features build), make the Release workflow dispatch-only (never auto on tag/release), and route release Linux/Windows builds through Blacksmith while keeping macOS on GitHub-hosted `macos-latest` so an operator-dispatched full release does not exhaust the Blacksmith pool. The decision protects the shared pool while keeping a real gate on every PR and preserving the ability to cut a full release when an operator explicitly asks for one. diff --git a/docs/adr/0002-ci-budget-modes.md b/docs/adr/0002-ci-budget-modes.md new file mode 100644 index 0000000..b532093 --- /dev/null +++ b/docs/adr/0002-ci-budget-modes.md @@ -0,0 +1,3 @@ +# ADR 0002: CI budget modes + +We needed a way to share one Blacksmith free tier across multiple projects without a runaway repo exhausting the pool for the others. We introduce an org/repo variable `CI_BUDGET_MODE` with three modes — `normal` (run PR Check), `thin` (skip linear-cli PR Check jobs so the higher-priority Win-CodexBar flow gets the pool), and `off` (skip everything) — treating unset/empty as `normal`. The PR Check job is gated by `vars.CI_BUDGET_MODE != 'off' && vars.CI_BUDGET_MODE != 'thin'`; Release jobs are gated by `vars.CI_BUDGET_MODE != 'off'` (a manual dispatch still runs in `thin`, since an operator explicitly asked). This gives operators a single knob to defer or shut off CI spend per repo without editing workflow files, and the intended split is roughly 60/30/10 across normal/thin/off so the pool covers priority work first. From ce18b158c0725f6f1502606615691b6ce5b3c114 Mon Sep 17 00:00:00 2001 From: NessZerra <90105158+Finesssee@users.noreply.github.com> Date: Fri, 24 Jul 2026 12:21:22 +0700 Subject: [PATCH 2/2] docs: fix CI budget split to mean repo minute share --- .github/CI.md | 20 ++++++++++++-------- CONTEXT.md | 2 +- docs/adr/0002-ci-budget-modes.md | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/CI.md b/.github/CI.md index 824853d..43519e1 100644 --- a/.github/CI.md +++ b/.github/CI.md @@ -19,14 +19,18 @@ Actions → Variables). Unset/empty is treated as `normal`. ## Intended split -Across the shared repos the intended split is roughly **60 / 30 / 10**: -- **60%** of time in `normal` — PR Check runs on every PR. -- **30%** in `thin` — linear-cli defers so Win-CodexBar (the priority repo) - gets uninterrupted pool access. -- **10%** in `off` — paused, e.g. during a pool freeze or investigation. - -This is a planning target, not a hard cap. Move to `thin` whenever Win-CodexBar -has open PRs competing for the pool. +The shared Blacksmith free tier is ~3000 runner-minutes/month. The intended +share of that pool across repos is roughly **60 / 30 / 10**: +- **~60%** → Win-CodexBar (the priority repo). +- **~30%** → linear-cli. +- **~10%** → buffer for spikes and overruns. + +This is a share of pool minutes per repo, **not** calendar time spent in each +`CI_BUDGET_MODE`. The budget mode is the knob that holds linear-cli near its +~30%: `thin` skips the **entire** linear-cli PR Check job (not individual +matrix legs), and `off` pauses all of this repo's CI. It is a planning target, +not a hard cap — move to `thin` whenever Win-CodexBar has open PRs competing +for the pool. ## $0 spend alert diff --git a/CONTEXT.md b/CONTEXT.md index 276e54c..a01a725 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -16,5 +16,5 @@ _Avoid_: "Blacksmith runners" without naming the shared-pool constraint; "the cl **Local Release**: The default way to release — an operator runs `cargo publish` and `gh release create/upload` by hand (see `docs/manual-release.md`). The dispatch-only `release.yml` is a fallback, not the primary path. _Avoid_: "manual release" interchangeably with the workflow; the workflow is "dispatch release". -**Budget Mode**: The value of org/repo variable `CI_BUDGET_MODE` — `normal` (PR Check runs), `thin` (linear-cli PR Check skips so Win-CodexBar gets priority), `off` (all CI skips). Unset/empty is treated as `normal`. PR Check honors `thin`; a manually dispatched Release still runs in `thin` because the operator asked for it. +**Budget Mode**: The value of org/repo variable `CI_BUDGET_MODE` — `normal` (PR Check runs), `thin` (the **entire** linear-cli PR Check job is skipped — not individual matrix legs — so Win-CodexBar gets priority), `off` (all CI skips). Unset/empty is treated as `normal`. PR Check honors `thin`; a manually dispatched Release still runs in `thin` because the operator asked for it. The intended share of the ~3000 free pool minutes/month is roughly 60 / 30 / 10 (Win-CodexBar / linear-cli / buffer) — a per-repo minute share, not calendar time spent in each mode. _Avoid_: "spend mode", "throttle". diff --git a/docs/adr/0002-ci-budget-modes.md b/docs/adr/0002-ci-budget-modes.md index b532093..cfb0a93 100644 --- a/docs/adr/0002-ci-budget-modes.md +++ b/docs/adr/0002-ci-budget-modes.md @@ -1,3 +1,3 @@ # ADR 0002: CI budget modes -We needed a way to share one Blacksmith free tier across multiple projects without a runaway repo exhausting the pool for the others. We introduce an org/repo variable `CI_BUDGET_MODE` with three modes — `normal` (run PR Check), `thin` (skip linear-cli PR Check jobs so the higher-priority Win-CodexBar flow gets the pool), and `off` (skip everything) — treating unset/empty as `normal`. The PR Check job is gated by `vars.CI_BUDGET_MODE != 'off' && vars.CI_BUDGET_MODE != 'thin'`; Release jobs are gated by `vars.CI_BUDGET_MODE != 'off'` (a manual dispatch still runs in `thin`, since an operator explicitly asked). This gives operators a single knob to defer or shut off CI spend per repo without editing workflow files, and the intended split is roughly 60/30/10 across normal/thin/off so the pool covers priority work first. +We needed a way to share one Blacksmith free tier (~3000 runner-minutes/month) across multiple projects without a runaway repo exhausting the pool for the others. We introduce an org/repo variable `CI_BUDGET_MODE` with three modes — `normal` (run PR Check), `thin` (skip the entire linear-cli PR Check job — not individual matrix legs — so the higher-priority Win-CodexBar flow gets the pool), and `off` (skip everything) — treating unset/empty as `normal`. The PR Check job is gated by `vars.CI_BUDGET_MODE != 'off' && vars.CI_BUDGET_MODE != 'thin'`; Release jobs are gated by `vars.CI_BUDGET_MODE != 'off'` (a manual dispatch still runs in `thin`, since an operator explicitly asked). This gives operators a single knob to defer or shut off CI spend per repo without editing workflow files, and the intended split is roughly 60/30/10 — a per-repo share of the free pool minutes (Win-CodexBar / linear-cli / buffer), not calendar time spent in each mode — so the pool covers priority work first.