Skip to content
Merged
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
55 changes: 55 additions & 0 deletions .github/CI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 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

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

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`.
57 changes: 30 additions & 27 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
28 changes: 19 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,45 +1,53 @@
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

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:
Expand Down Expand Up @@ -79,6 +87,7 @@ jobs:
name: Upload Release Assets
needs: build
runs-on: ubuntu-latest
if: vars.CI_BUDGET_MODE != 'off'
permissions:
contents: write

Expand All @@ -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

Expand Down
20 changes: 20 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -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` (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".
3 changes: 3 additions & 0 deletions docs/adr/0001-pr-check-blacksmith-local-release.md
Original file line number Diff line number Diff line change
@@ -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.
3 changes: 3 additions & 0 deletions docs/adr/0002-ci-budget-modes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# ADR 0002: CI budget modes

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.
Loading