Skip to content

Commit 00a39ae

Browse files
authored
perf(ci): only boot a macOS runner when native sources actually change (#51)
Mobile Native Static Analysis runs swiftlint, ktlint, and detekt, which between them read only .swift, .kt, and .kts files under apps/mobile, skipping the generated android/ and ios/ folders. It needs macOS, the most expensive runner tier this repo buys, and it ran on every pull request and every push regardless of what changed: 157 times over the last 30 days for about four seconds of actual linting behind roughly forty seconds of runner setup. Moving it into its own workflow lets GitHub path-filter it natively, with no extra gate job and no new action dependency. Filtering on apps/mobile/** would have been the obvious choice and the wrong one: 18 of the last 40 merged pull requests touched something under apps/mobile, but only 3 touched native sources or the linter configuration, so the broad filter would still boot macOS six times more often than the check can do any work. The paths list is load-bearing. Widen it whenever the check learns to read something new, or it will quietly stop running. Verified with actionlint: no new findings against the base. Model: Claude Opus 5. Harness: Claude Code.
1 parent 8dd0d6e commit 00a39ae

3 files changed

Lines changed: 80 additions & 32 deletions

File tree

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: CI Mobile Native
2+
3+
# Split out of ci.yml so it can be path-filtered. The job needs a macOS runner
4+
# for swiftlint, and macOS is the most expensive tier we buy, but the check it
5+
# runs only ever reads .swift/.kt/.kts under apps/mobile -- roughly four seconds
6+
# of actual linting behind about forty seconds of runner setup. Filtering on
7+
# apps/mobile/** would still be wrong: only 3 of the last 40 merged PRs touched
8+
# native sources, while 18 touched something under apps/mobile, so the broad
9+
# filter would boot macOS six times more often than the check can do any work.
10+
on:
11+
pull_request:
12+
paths:
13+
- "apps/mobile/**/*.swift"
14+
- "apps/mobile/**/*.kt"
15+
- "apps/mobile/**/*.kts"
16+
- "apps/mobile/.swiftlint.yml"
17+
- "apps/mobile/detekt.yml"
18+
- "apps/mobile/.editorconfig"
19+
- "apps/mobile/Brewfile"
20+
- "scripts/mobile-native-static-check.ts"
21+
- ".github/workflows/ci-mobile-native.yml"
22+
push:
23+
branches:
24+
- pylon
25+
paths:
26+
- "apps/mobile/**/*.swift"
27+
- "apps/mobile/**/*.kt"
28+
- "apps/mobile/**/*.kts"
29+
- "apps/mobile/.swiftlint.yml"
30+
- "apps/mobile/detekt.yml"
31+
- "apps/mobile/.editorconfig"
32+
- "apps/mobile/Brewfile"
33+
- "scripts/mobile-native-static-check.ts"
34+
- ".github/workflows/ci-mobile-native.yml"
35+
36+
concurrency:
37+
group: ci-mobile-native-${{ github.event.pull_request.number || github.sha }}
38+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
39+
40+
permissions:
41+
contents: read
42+
43+
jobs:
44+
mobile_native_static_analysis:
45+
name: Mobile Native Static Analysis
46+
runs-on: blacksmith-6vcpu-macos-26
47+
timeout-minutes: 10
48+
steps:
49+
- name: Checkout
50+
uses: actions/checkout@v6
51+
with:
52+
sparse-checkout: |
53+
/*
54+
!/.repos/
55+
sparse-checkout-cone-mode: false
56+
57+
- name: Setup Vite+
58+
uses: voidzero-dev/setup-vp@v1
59+
with:
60+
node-version-file: package.json
61+
cache: true
62+
run-install: |
63+
args:
64+
- --filter=@t3tools/scripts...
65+
66+
- name: Install mobile native static analysis tools
67+
run: brew bundle install --file apps/mobile/Brewfile
68+
69+
- name: Lint mobile native sources
70+
run: vp run lint:mobile

.github/workflows/ci.yml

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -118,34 +118,6 @@ jobs:
118118
- name: Test resource monitor
119119
run: cargo test --locked --manifest-path native/resource-monitor/Cargo.toml
120120

121-
mobile_native_static_analysis:
122-
name: Mobile Native Static Analysis
123-
runs-on: blacksmith-6vcpu-macos-26
124-
timeout-minutes: 10
125-
steps:
126-
- name: Checkout
127-
uses: actions/checkout@v6
128-
with:
129-
sparse-checkout: |
130-
/*
131-
!/.repos/
132-
sparse-checkout-cone-mode: false
133-
134-
- name: Setup Vite+
135-
uses: voidzero-dev/setup-vp@v1
136-
with:
137-
node-version-file: package.json
138-
cache: true
139-
run-install: |
140-
args:
141-
- --filter=@t3tools/scripts...
142-
143-
- name: Install mobile native static analysis tools
144-
run: brew bundle install --file apps/mobile/Brewfile
145-
146-
- name: Lint mobile native sources
147-
run: vp run lint:mobile
148-
149121
release_smoke:
150122
name: Release Smoke
151123
runs-on: blacksmith-8vcpu-ubuntu-2404

docs/internals/ci.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,25 @@
22

33
> For maintainers. Using T3 Code? See [docs/user](../user/).
44
5-
[`.github/workflows/ci.yml`](../../.github/workflows/ci.yml) runs four jobs on pull requests and
6-
pushes to `main`:
5+
[`.github/workflows/ci.yml`](../../.github/workflows/ci.yml) runs three jobs on pull requests and
6+
pushes to `pylon`:
77

88
- **Check**: `vp check` (format and lint; this repo sets `typeCheck: false` in its lint options),
99
then `vpr typecheck` for the workspace type check. The same job
1010
builds the desktop pipeline (`vp run build:desktop`) and verifies the preload bundle exists and
1111
still exports its expected symbols.
1212
- **Test**: `vp run test` across the workspace.
13-
- **Mobile Native Static Analysis**: `vp run lint:mobile` on macOS, wrapping
14-
`scripts/mobile-native-static-check.ts`.
1513
- **Release Smoke**: exercises release-only workflow steps through `scripts/release-smoke.ts`, so
1614
release breakage surfaces on PRs rather than at tag time.
1715

16+
[`.github/workflows/ci-mobile-native.yml`](../../.github/workflows/ci-mobile-native.yml) runs
17+
**Mobile Native Static Analysis** (`vp run lint:mobile`, wrapping
18+
`scripts/mobile-native-static-check.ts`) on the same events. It lives in its own workflow so it can
19+
be path-filtered: the check only reads `.swift`, `.kt`, and `.kts` sources under `apps/mobile`, and
20+
it needs a macOS runner, which is the most expensive tier we buy. Filtering on `apps/mobile/**`
21+
would not be enough — most changes there are TypeScript the native linters never look at. Widen the
22+
`paths:` list whenever the check learns to read something new, or it will silently stop running.
23+
1824
`.github/workflows/release.yml` builds macOS (`arm64` and `x64`), Linux (`x64`), and Windows (`x64`)
1925
desktop artifacts from a single `v*.*.*` tag and publishes one GitHub release. It auto-enables
2026
signing only when platform credentials are present. macOS passkey builds additionally require

0 commit comments

Comments
 (0)