Skip to content
61 changes: 17 additions & 44 deletions .github/actions/setup-ci-deps/action.yml
Original file line number Diff line number Diff line change
@@ -1,57 +1,30 @@
name: Setup CI dependencies
description: Install Vite+ dependencies using a Blacksmith sticky pnpm store on Linux.
inputs:
run-install:
description: Install arguments forwarded to setup-vp.
required: false
default: "true"
description: Install Vite+ and the workspace dependencies from cache.
runs:
using: composite
steps:
# Cache packages only; node_modules and prepare scripts stay fresh each job.
- name: Mount pnpm store
id: sticky
continue-on-error: true
uses: useblacksmith/stickydisk@v1
with:
key: ${{ github.repository }}-ci-pnpm-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml', 'patches/**') }}
path: ${{ runner.temp }}/t3-ci-pnpm-store
# Each job gets a private clone. PRs must not publish into main's snapshot.
commit: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}

- name: Check pnpm store mount
id: store
# setup-vp reuses an installed exact version, so restoring its home skips the
# installer download on every job.
- name: Resolve Vite+ version
id: vp
shell: bash
env:
STICKY_OUTCOME: ${{ steps.sticky.outcome }}
STICKY_STORE_PATH: ${{ runner.temp }}/t3-ci-pnpm-store
PUBLISHES: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run: |
# stickydisk can return success after falling back to an ordinary directory.
# An empty disk (no pnpm `v*` store from a main snapshot yet) only helps the
# main push that fills it; everyone else keeps the setup-vp cache.
if [[ "$STICKY_OUTCOME" == success ]] && mountpoint -q "$STICKY_STORE_PATH" && test -w "$STICKY_STORE_PATH" \
&& { [[ "$PUBLISHES" == true ]] || compgen -G "$STICKY_STORE_PATH/v*" > /dev/null; }; then
echo 'mounted=true' >> "$GITHUB_OUTPUT"
else
echo 'mounted=false' >> "$GITHUB_OUTPUT"
echo '::warning::Sticky pnpm store unavailable or empty; using setup-vp dependency cache.'
fi
echo "version=$(sed -nE 's/^ vite-plus: ([0-9][^ ]*)$/\1/p' pnpm-workspace.yaml)" >> "$GITHUB_OUTPUT"

- name: Setup Vite+ with sticky store
if: steps.store.outputs.mounted == 'true'
uses: voidzero-dev/setup-vp@v1
env:
pnpm_config_store_dir: ${{ runner.temp }}/t3-ci-pnpm-store
- name: Cache Vite+
if: steps.vp.outputs.version != ''
uses: actions/cache@v6
with:
node-version-file: package.json
cache: false
run-install: ${{ inputs.run-install }}
path: ~/.vite-plus
key: vite-plus-home-v1-${{ runner.os }}-${{ runner.arch }}-${{ steps.vp.outputs.version }}

- name: Setup Vite+ with cache fallback
if: steps.store.outputs.mounted != 'true'
# The pnpm store stays a cache tarball on the runner's own disk. A store on a
# Blacksmith sticky disk mounts in under a second, but pnpm then copies every
# package across filesystems instead of hard-linking it: install took 28-49s
# against 9s plus an 11s restore.
- name: Setup Vite+
uses: voidzero-dev/setup-vp@v1
with:
node-version-file: package.json
cache: true
run-install: ${{ inputs.run-install }}
run-install: true
51 changes: 23 additions & 28 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ jobs:
sparse-checkout: |
/*
!/.repos/
!/apps/mobile/modules/t3-terminal/Vendor/**/*.a
sparse-checkout-cone-mode: false

- name: Reject repository-owned PR assets
Expand Down Expand Up @@ -105,6 +106,7 @@ jobs:
sparse-checkout: |
/*
!/.repos/
!/apps/mobile/modules/t3-terminal/Vendor/**/*.a
sparse-checkout-cone-mode: false

- uses: ./.github/actions/setup-apt-mirrors
Expand Down Expand Up @@ -185,6 +187,7 @@ jobs:
sparse-checkout: |
/*
!/.repos/
!/apps/mobile/modules/t3-terminal/Vendor/**/*.a
sparse-checkout-cone-mode: false

# Blacksmith boots GitHub's Ubuntu runner image (gcc is usually present),
Expand Down Expand Up @@ -256,6 +259,7 @@ jobs:
sparse-checkout: |
/*
!/.repos/
!/apps/mobile/modules/t3-terminal/Vendor/**/*.a
sparse-checkout-cone-mode: false

- name: Setup Vite+
Expand All @@ -279,7 +283,7 @@ jobs:
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4, 5, 6]
shard: [1, 2, 3, 4, 5, 6, 7, 8]
steps:
# Depth 2 brings the base commit the change is diffed against.
- name: Checkout
Expand All @@ -289,6 +293,7 @@ jobs:
sparse-checkout: |
/*
!/.repos/
!/apps/mobile/modules/t3-terminal/Vendor/**/*.a
sparse-checkout-cone-mode: false

- name: Detect affected jobs
Expand Down Expand Up @@ -368,22 +373,6 @@ jobs:
if-no-files-found: ignore
retention-days: 30

transfer-report:
name: Transfer report artifact
needs: [changes, test_server]
if: needs.changes.outputs.test_server != 'false'
# On Blacksmith like every other job here: the GitHub-hosted queue is shared
# with the PR bots and can leave this job waiting until it is abandoned,
# which fails `Check` on an otherwise green run.
runs-on: blacksmith-2vcpu-ubuntu-2404
timeout-minutes: 5
steps:
- name: Require transfer measurements
uses: actions/download-artifact@v8
with:
name: thread-transfer-results
path: ${{ runner.temp }}/thread-transfer

# Split out of Check and Test: both paid ~7-9s to install a Rust toolchain
# for checks that take under 3s, on the critical path of every PR.
rust:
Expand All @@ -399,6 +388,7 @@ jobs:
sparse-checkout: |
/*
!/.repos/
!/apps/mobile/modules/t3-terminal/Vendor/**/*.a
sparse-checkout-cone-mode: false

- name: Setup Rust
Expand Down Expand Up @@ -461,16 +451,13 @@ jobs:
sparse-checkout: |
/*
!/.repos/
!/apps/mobile/modules/t3-terminal/Vendor/**/*.a
sparse-checkout-cone-mode: false

# A full install: a filtered one that misses the cache first saves its
# partial store under the shared lockfile key for every later job.
- name: Setup Vite+
uses: voidzero-dev/setup-vp@v1
with:
node-version-file: package.json
cache: true
run-install: |
args:
- --filter=@t3tools/scripts...
uses: ./.github/actions/setup-ci-deps

- name: Exercise release-only workflow steps
run: node scripts/release-smoke.ts
Expand All @@ -491,7 +478,6 @@ jobs:
test,
test_web,
test_server,
transfer-report,
rust,
mobile_native_static_analysis,
release_smoke,
Expand All @@ -505,11 +491,20 @@ jobs:
run: |
echo "$RESULTS" | jq -r 'to_entries[] | "\(.key): \(.value.result)"'
# A job may skip only when `changes` said every gate it represents was
# not needed. Gates are keyed by job name, except merged Build, the
# macOS lint, and the transfer report.
# not needed. Gates are keyed by job name, except merged Build and the
# macOS lint.
echo "$RESULTS" | jq -e '.changes.outputs as $gates
| {build: ["build", "typecheck"], mobile_native_static_analysis: ["mobile_native"], "transfer-report": ["test_server"]} as $renamed
| {build: ["build", "typecheck"], mobile_native_static_analysis: ["mobile_native"]} as $renamed
| all(to_entries[];
.value.result == "success"
or (.value.result == "skipped"
and (($renamed[.key] // [.key]) | all(.[]; $gates[.] == "false"))))' > /dev/null

# thread-transfer-report.yml reads this artifact after CI, so a run whose
# server tests ran must have produced it.
- name: Require transfer measurements
if: needs.test_server.result == 'success' && needs.changes.outputs.test_server != 'false'
uses: actions/download-artifact@v8
with:
name: thread-transfer-results
path: ${{ runner.temp }}/thread-transfer
Loading
Loading