Skip to content

Commit 4b97978

Browse files
fix(ci): make GitHub-hosted fallback actually build and test (#5814)
* fix(ci): make GitHub-hosted fallback actually build and test * fix(ci): drop unused build-args passthrough so the heap ceiling can't be overridden * chore(ci): trim comments to the non-obvious constraints * fix(ci): build the app image on a larger runner in GitHub mode
1 parent 995167d commit 4b97978

4 files changed

Lines changed: 44 additions & 48 deletions

File tree

.github/actions/cache-mount/action.yml

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,18 @@ description: Mount a build cache directory using Blacksmith sticky disks, or the
33

44
inputs:
55
provider:
6-
description: Empty or 'blacksmith' selects sticky disks. Any other value selects actions/cache. Must stay byte-identical to the runs-on predicate in the calling workflow.
6+
description: Empty or 'blacksmith' selects sticky disks; anything else selects actions/cache.
77
required: false
88
default: ''
99
key:
10-
description: Cache key. Shared by both backends, so callers keep one key regardless of provider.
10+
description: Cache key, shared by both backends.
1111
required: true
1212
path:
13-
description: Filesystem path the cache is mounted at.
13+
description: Path to mount.
1414
required: true
1515

16-
# The two conditions are exact complements of the runs-on expression the caller
17-
# uses. They must be inverted together: a job placed on ubuntu-latest that then
18-
# took the sticky-disk branch would hard-fail, since stickydisk hot-mounts an
19-
# ext4 volume from Blacksmith's storage agents and cannot run anywhere else.
16+
# Predicates must stay the exact complement of the caller's runs-on — stickydisk
17+
# only runs on Blacksmith.
2018
runs:
2119
using: composite
2220
steps:
@@ -27,10 +25,8 @@ runs:
2725
key: ${{ inputs.key }}
2826
path: ${{ inputs.path }}
2927

30-
# Sticky disks always re-commit the mounted path, so the cache rolls forward
31-
# every run. actions/cache skips its save step on an exact key hit, which
32-
# would freeze the cache at whatever the first run wrote — the run-scoped
33-
# suffix plus a prefix restore-key reproduces the rolling behaviour.
28+
# run_id suffix + prefix restore-key: actions/cache skips save on an exact hit,
29+
# which would freeze the cache at the first run's contents.
3430
- name: Restore and save cache
3531
if: inputs.provider != '' && inputs.provider != 'blacksmith'
3632
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5

.github/actions/docker-build/action.yml

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: Set up a buildx builder and build/push an image, using Blacksmith's
33

44
inputs:
55
provider:
6-
description: Empty or 'blacksmith' selects Blacksmith's builder. Any other value selects the upstream docker/* actions. Must stay byte-identical to the runs-on predicate in the calling workflow.
6+
description: Empty or 'blacksmith' selects Blacksmith's builder; anything else selects the docker/* actions.
77
required: false
88
default: ''
99
context:
@@ -20,10 +20,8 @@ inputs:
2020
description: Comma-separated list of tags to push.
2121
required: true
2222

23-
# Registry logins must already have run in the calling job — both backends read
24-
# the same ~/.docker/config.json. provenance/sbom stay off everywhere: the
25-
# attestation manifests they add break `imagetools create` retagging in the
26-
# promote-images and create-ghcr-manifests jobs.
23+
# Registry logins must precede this action. provenance/sbom stay off: attestation
24+
# manifests break `imagetools create` retagging in promote-images.
2725
runs:
2826
using: composite
2927
steps:
@@ -47,10 +45,7 @@ runs:
4745
if: inputs.provider != '' && inputs.provider != 'blacksmith'
4846
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
4947

50-
# No layer cache here. Blacksmith caches layers on the builder itself;
51-
# the GitHub equivalent (cache-to: type=gha) shares the same 10 GB repo
52-
# cache quota as the bun/node_modules/turbo mounts, and four images of
53-
# layers would evict them. Fallback builds are cold by design.
48+
# No cache-to: type=gha — it shares the 10 GB repo quota with the cache mounts.
5449
- name: Build and push (GitHub)
5550
if: inputs.provider != '' && inputs.provider != 'blacksmith'
5651
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7

.github/workflows/ci.yml

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,19 @@
11
name: CI
22

3-
# Runner provider toggle. Every job here and in the reusable workflows reads the
4-
# CI_PROVIDER repository variable:
3+
# Runner provider toggle, read from the CI_PROVIDER repo variable:
54
#
65
# gh variable set CI_PROVIDER --body github # fall back to GitHub-hosted
76
# gh variable delete CI_PROVIDER # back to Blacksmith (default)
87
#
9-
# It is a repo variable rather than a committed value on purpose: during a
10-
# Blacksmith outage there is no working CI to merge a switchover commit through,
11-
# and the variable takes effect on the next run with nothing to land.
8+
# A repo variable, not a committed value: during a Blacksmith outage there is no
9+
# working CI to merge a switchover through. Only unset/'blacksmith' selects
10+
# Blacksmith; anything unrecognized selects GitHub so a typo can't queue jobs
11+
# against the provider you're escaping. Every runs-on and both composite actions
12+
# share this predicate and must change together.
1213
#
13-
# Only unset or the exact value 'blacksmith' selects Blacksmith; everything else
14-
# selects GitHub-hosted. The unrecognized case resolves toward GitHub on purpose,
15-
# because that is the provider that is always available: a typo'd or whitespace-
16-
# padded break-glass value then yields slow-but-working CI instead of jobs queued
17-
# forever against the provider you were trying to escape. `==` is case-insensitive
18-
# in Actions expressions, so 'GitHub' and 'Blacksmith' both work. Every runs-on
19-
# expression and both composite actions use this same predicate — they must be
20-
# changed together, since a job on ubuntu-latest that took a Blacksmith-only
21-
# branch would hard-fail.
22-
#
23-
# GitHub mode is a break-glass fallback, not a peer. Public-repo ubuntu-latest is
24-
# 4 vCPU with no free 8-core tier, the 10 GB repo cache quota will thrash across
25-
# the bun/node_modules/turbo mounts, and image builds start from cold layers — so
26-
# expect slower runs, not different results. Blacksmith-only actions are reached
27-
# exclusively through .github/actions/cache-mount and .github/actions/docker-build,
28-
# which branch internally; a bare useblacksmith/* step added anywhere else will
29-
# hard-fail in GitHub mode rather than silently degrade.
14+
# GitHub mode is break-glass, not a peer — cold layers, slower runs. The app image
15+
# is the one job on a paid larger runner: next build needs ~32 GB and OOM-kills
16+
# (exit 137) on the free 16 GB runners at any heap ceiling.
3017

3118
on:
3219
push:
@@ -109,7 +96,7 @@ jobs:
10996
name: Build Dev ECR
11097
needs: [detect-version, migrate-dev]
11198
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
112-
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404' || 'ubuntu-latest' }}
99+
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404' || matrix.gh_runner }}
113100
timeout-minutes: 30
114101
permissions:
115102
contents: read
@@ -118,14 +105,20 @@ jobs:
118105
fail-fast: false
119106
matrix:
120107
include:
108+
# Only the app image needs the paid 8-core/32 GB runner: next build
109+
# exhausts the free 16 GB one (exit 137). The others build in <5 min.
121110
- dockerfile: ./docker/app.Dockerfile
122111
ecr_repo_secret: ECR_APP
112+
gh_runner: linux-x64-8-core
123113
- dockerfile: ./docker/db.Dockerfile
124114
ecr_repo_secret: ECR_MIGRATIONS
115+
gh_runner: ubuntu-latest
125116
- dockerfile: ./docker/realtime.Dockerfile
126117
ecr_repo_secret: ECR_REALTIME
118+
gh_runner: ubuntu-latest
127119
- dockerfile: ./docker/pii.Dockerfile
128120
ecr_repo_secret: ECR_PII
121+
gh_runner: ubuntu-latest
129122
steps:
130123
- name: Checkout code
131124
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
@@ -214,7 +207,7 @@ jobs:
214207
if: >-
215208
github.event_name == 'push' &&
216209
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging')
217-
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404' || 'ubuntu-latest' }}
210+
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404' || matrix.gh_runner }}
218211
timeout-minutes: 30
219212
permissions:
220213
contents: read
@@ -227,15 +220,19 @@ jobs:
227220
- dockerfile: ./docker/app.Dockerfile
228221
ghcr_image: ghcr.io/simstudioai/simstudio
229222
ecr_repo_secret: ECR_APP
223+
gh_runner: linux-x64-8-core
230224
- dockerfile: ./docker/db.Dockerfile
231225
ghcr_image: ghcr.io/simstudioai/migrations
232226
ecr_repo_secret: ECR_MIGRATIONS
227+
gh_runner: ubuntu-latest
233228
- dockerfile: ./docker/realtime.Dockerfile
234229
ghcr_image: ghcr.io/simstudioai/realtime
235230
ecr_repo_secret: ECR_REALTIME
231+
gh_runner: ubuntu-latest
236232
- dockerfile: ./docker/pii.Dockerfile
237233
ghcr_image: ghcr.io/simstudioai/pii
238234
ecr_repo_secret: ECR_PII
235+
gh_runner: ubuntu-latest
239236
steps:
240237
- name: Checkout code
241238
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
@@ -378,7 +375,7 @@ jobs:
378375
# never moves a documented tag.
379376
build-ghcr-arm64:
380377
name: Build ARM64 (GHCR Only)
381-
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404-arm' || 'ubuntu-24.04-arm' }}
378+
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404-arm' || matrix.gh_runner }}
382379
timeout-minutes: 30
383380
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
384381
permissions:
@@ -390,12 +387,16 @@ jobs:
390387
include:
391388
- dockerfile: ./docker/app.Dockerfile
392389
image: ghcr.io/simstudioai/simstudio
390+
gh_runner: linux-arm64-8-core
393391
- dockerfile: ./docker/db.Dockerfile
394392
image: ghcr.io/simstudioai/migrations
393+
gh_runner: ubuntu-24.04-arm
395394
- dockerfile: ./docker/realtime.Dockerfile
396395
image: ghcr.io/simstudioai/realtime
396+
gh_runner: ubuntu-24.04-arm
397397
- dockerfile: ./docker/pii.Dockerfile
398398
image: ghcr.io/simstudioai/pii
399+
gh_runner: ubuntu-24.04-arm
399400

400401
steps:
401402
- name: Checkout code

.github/workflows/test-build.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
# Cache keys are scoped by event name, and fork PRs get their own
3131
# namespace on top: untrusted fork runs must never share a cache with
3232
# push runs (whose caches feed production image builds) or with trusted
33-
# internal-PR runs. The scoping holds on both backends.
33+
# internal-PR runs.
3434
- name: Mount Bun cache
3535
uses: ./.github/actions/cache-mount
3636
with:
@@ -153,6 +153,11 @@ jobs:
153153
- name: Type-check realtime server
154154
run: bunx turbo run type-check --filter=@sim/realtime
155155

156+
# cloud-review-tools.test.ts runs the real helper on the runner, which shells
157+
# out to rg. Blacksmith's image ships it, GitHub's doesn't.
158+
- name: Install ripgrep
159+
run: command -v rg || (sudo apt-get update && sudo apt-get install -y ripgrep)
160+
156161
- name: Run tests with coverage
157162
env:
158163
NODE_OPTIONS: '--no-warnings --max-old-space-size=8192'
@@ -188,8 +193,7 @@ jobs:
188193
# with test-build (identical content from the same lockfile — LWW loss is
189194
# harmless), but the Turbo cache gets its own key: with a shared key, only
190195
# the last committer's new entries survive each run, so the test and build
191-
# Turbo entries would evict each other nondeterministically. The same split
192-
# is correct on actions/cache, which is likewise last-writer-wins per key.
196+
# Turbo entries would evict each other nondeterministically.
193197
build:
194198
name: Build App
195199
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404' || 'ubuntu-latest' }}

0 commit comments

Comments
 (0)