Repository navigation
fix(provider-pi): running shell output no longer stores a snapshot ro… #114162
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| pull_request: | |
| push: | |
| branches: | |
| - main | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.event.pull_request.number || github.sha }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| jobs: | |
| # Skips jobs a pull request cannot affect: a web change does not run the | |
| # server shards, a server change does not run the mobile tests, and the macOS | |
| # lint (~6.7x a Linux minute) runs only for native mobile sources. | |
| # .github/scripts/ci-changes.cjs maps changed files to workspace packages and | |
| # their dependents. Pushes to main still run every Linux job. Detection fails | |
| # open: if the diff cannot be resolved, everything runs. | |
| changes: | |
| name: Changes | |
| runs-on: blacksmith-2vcpu-ubuntu-2404 | |
| timeout-minutes: 5 | |
| outputs: | |
| typecheck: ${{ steps.detect.outputs.typecheck }} | |
| build: ${{ steps.detect.outputs.build }} | |
| desktop_launch: ${{ steps.detect.outputs.desktop_launch }} | |
| test: ${{ steps.detect.outputs.test }} | |
| test_packages: ${{ steps.detect.outputs.test_packages }} | |
| test_web: ${{ steps.detect.outputs.test_web }} | |
| test_server: ${{ steps.detect.outputs.test_server }} | |
| rust: ${{ steps.detect.outputs.rust }} | |
| release_smoke: ${{ steps.detect.outputs.release_smoke }} | |
| mobile_native: ${{ steps.detect.outputs.mobile_native }} | |
| steps: | |
| # Only the package manifests the script reads to build the dependency graph. | |
| # Depth 2 brings the base commit the change is diffed against. | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| with: | |
| fetch-depth: 2 | |
| sparse-checkout: | | |
| /pnpm-workspace.yaml | |
| /.github/scripts/ci-changes.cjs | |
| /.github/actions/ci-changes/ | |
| /*/package.json | |
| /*/*/package.json | |
| !/.repos/ | |
| sparse-checkout-cone-mode: false | |
| - name: Detect affected jobs | |
| id: detect | |
| uses: ./.github/actions/ci-changes | |
| # Lint and the desktop build each get a runner so they run side by side. | |
| # Typecheck shares the build's install. Lint always runs: it formats every | |
| # file, docs included. The `Check` job at the end of this file passes only | |
| # when every other job passes or was skipped by `changes`. | |
| lint: | |
| permissions: | |
| contents: read | |
| id-token: write | |
| name: Lint | |
| runs-on: blacksmith-8vcpu-ubuntu-2404 | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| with: | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| !/apps/mobile/modules/t3-terminal/Vendor/**/*.a | |
| sparse-checkout-cone-mode: false | |
| - name: Reject repository-owned PR assets | |
| run: | | |
| files="$(git ls-files .github/pr-assets)" | |
| if test -n "$files"; then | |
| printf 'PR evidence must be uploaded to GitHub, not committed:\n%s\n' "$files" >&2 | |
| exit 1 | |
| fi | |
| - name: Setup Vite+ | |
| uses: ./.github/actions/setup-ci-deps | |
| with: | |
| task-cache-read-token: ${{ secrets.T3_TASK_CACHE_READ_TOKEN }} | |
| # electron's install.js (run by ensure:electron) reads this cache before it | |
| # downloads from GitHub. The key changes only with the Electron version. | |
| - name: Cache Electron download | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ~/.cache/electron | |
| key: electron-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('node_modules/.pnpm/electron@*/node_modules/electron/package.json') }} | |
| - name: Ensure Electron runtime is installed | |
| run: vp run --filter @t3tools/desktop ensure:electron | |
| # Files/dependencies are repo-wide; export checks cover clean workspaces only. | |
| - name: Check unused code | |
| run: vp run knip:check | |
| - name: Check generated GitHub GraphQL operations | |
| run: vp run --filter @t3tools/source-control-github generate --check | |
| - name: Check generated Expo GraphQL operations | |
| run: vp run --filter t3 generate --check | |
| - name: Check | |
| run: vp check | |
| build: | |
| permissions: | |
| contents: read | |
| id-token: write | |
| name: Build and Typecheck | |
| needs: changes | |
| if: ${{ !cancelled() && (needs.changes.outputs.build != 'false' || needs.changes.outputs.typecheck != 'false') }} | |
| runs-on: blacksmith-8vcpu-ubuntu-2404 | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| with: | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| !/apps/mobile/modules/t3-terminal/Vendor/**/*.a | |
| sparse-checkout-cone-mode: false | |
| - uses: ./.github/actions/setup-apt-mirrors | |
| if: needs.changes.outputs.build != 'false' | |
| # Runs in the background while Vite+ installs; the step before the build waits for it. | |
| # Xvfb is for the desktop launch check below. | |
| - name: Start installing browser secret helper build libraries | |
| if: needs.changes.outputs.build != 'false' | |
| run: | | |
| # Fetch only the indexes these main and universe packages need. The full set is ~42 MB, | |
| # which a slow mirror can stretch past the wait below. | |
| sudo sed -i -e 's|http://|https://|g' -e 's/^Components: .*/Components: main universe/' \ | |
| /etc/apt/blacksmith-ubuntu-mirrors.txt /etc/apt/sources.list.d/ubuntu.sources | |
| ( | |
| set +e | |
| sudo apt-get update -o Acquire::Languages=none \ | |
| -o Acquire::IndexTargets::deb::DEP-11::DefaultEnabled=false \ | |
| -o Acquire::IndexTargets::deb::CNF::DefaultEnabled=false && | |
| sudo apt-get install -y libsecret-1-dev pkg-config xvfb | |
| echo "$?" > "$RUNNER_TEMP/apt-status" | |
| ) > "$RUNNER_TEMP/apt.log" 2>&1 < /dev/null & | |
| - name: Setup Vite+ | |
| uses: ./.github/actions/setup-ci-deps | |
| with: | |
| task-cache-read-token: ${{ secrets.T3_TASK_CACHE_READ_TOKEN }} | |
| # electron's install.js (run by ensure:electron) reads this cache before it | |
| # downloads from GitHub. The key changes only with the Electron version. | |
| - name: Cache Electron download | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ~/.cache/electron | |
| key: electron-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('node_modules/.pnpm/electron@*/node_modules/electron/package.json') }} | |
| - name: Ensure Electron runtime is installed | |
| id: electron | |
| run: vp run --filter @t3tools/desktop ensure:electron | |
| # Runs in the background while the build runs, and reports in the last | |
| # step: neither result hides the other. | |
| - name: Start typecheck | |
| id: typecheck | |
| if: ${{ !cancelled() && steps.electron.outcome == 'success' && needs.changes.outputs.typecheck != 'false' }} | |
| run: | | |
| ( | |
| set +e | |
| vpr typecheck | |
| echo "$?" > "$RUNNER_TEMP/typecheck-status" | |
| ) > "$RUNNER_TEMP/typecheck.log" 2>&1 < /dev/null & | |
| - name: Finish installing browser secret helper build libraries | |
| id: apt | |
| if: ${{ !cancelled() && needs.changes.outputs.build != 'false' }} | |
| timeout-minutes: 5 | |
| run: | | |
| until test -s "$RUNNER_TEMP/apt-status"; do sleep 1; done | |
| cat "$RUNNER_TEMP/apt.log" | |
| exit "$(cat "$RUNNER_TEMP/apt-status")" | |
| - name: Build desktop pipeline | |
| id: build | |
| if: ${{ !cancelled() && steps.electron.outcome == 'success' && steps.apt.outcome == 'success' && needs.changes.outputs.build != 'false' }} | |
| run: vp run build:desktop | |
| - name: Verify preload bundle output | |
| if: ${{ !cancelled() && steps.build.outcome == 'success' }} | |
| run: node apps/desktop/scripts/verify-preload-bundle.mjs | |
| # The release launch check, run unpackaged against the same old-state fixture, for | |
| # startup and sidebar changes. Packaging and signing stay release-only. | |
| - name: Open the seeded thread in the built desktop app | |
| id: desktop_launch | |
| if: ${{ !cancelled() && steps.build.outcome == 'success' && needs.changes.outputs.desktop_launch != 'false' }} | |
| timeout-minutes: 4 | |
| run: | | |
| electron=$(node -p "require('./apps/desktop/node_modules/electron')") | |
| xvfb-run -a node scripts/smoke-desktop-app.ts --executable "$electron" --app apps/desktop \ | |
| --artifacts-dir "$RUNNER_TEMP/desktop-launch-check" | |
| - name: Upload desktop launch check | |
| if: ${{ failure() && steps.desktop_launch.outcome == 'failure' }} | |
| uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 | |
| with: | |
| name: desktop-launch-check | |
| path: ${{ runner.temp }}/desktop-launch-check | |
| if-no-files-found: ignore | |
| # Browser tests run source modules; this boots the production chunk graph. The | |
| # desktop build above already produced apps/web/dist, and the client waits about | |
| # 15s for a session that never comes, so this runs here instead of in Test Web, | |
| # which gates the whole run. Chromium comes from the cache Test Web shares. | |
| - name: Resolve Playwright version | |
| id: playwright_version | |
| if: ${{ !cancelled() && steps.build.outcome == 'success' && needs.changes.outputs.test_web != 'false' }} | |
| run: echo "version=$(node -p "require('./apps/web/node_modules/playwright/package.json').version")" >> "$GITHUB_OUTPUT" | |
| - name: Cache Chromium | |
| id: chromium_cache | |
| if: ${{ !cancelled() && steps.build.outcome == 'success' && needs.changes.outputs.test_web != 'false' }} | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ~/.cache/ms-playwright | |
| key: playwright-chromium-shell-${{ runner.os }}-${{ runner.arch }}-${{ steps.playwright_version.outputs.version }} | |
| - name: Install Chromium for the built web client boot check | |
| if: ${{ !cancelled() && steps.build.outcome == 'success' && needs.changes.outputs.test_web != 'false' && steps.chromium_cache.outputs.cache-hit != 'true' }} | |
| run: node apps/web/node_modules/playwright/cli.js install --only-shell chromium | |
| - name: Install missing Chromium system dependencies | |
| if: ${{ !cancelled() && steps.build.outcome == 'success' && needs.changes.outputs.test_web != 'false' }} | |
| run: | | |
| browser=$(find "$HOME/.cache/ms-playwright" -name chrome-headless-shell -type f -print -quit) | |
| test -n "$browser" | |
| if ldd "$browser" | grep '=> not found'; then | |
| node apps/web/node_modules/playwright/cli.js install-deps chromium | |
| fi | |
| - name: Boot the built web client | |
| if: ${{ !cancelled() && steps.build.outcome == 'success' && needs.changes.outputs.test_web != 'false' }} | |
| timeout-minutes: 3 | |
| run: node apps/web/scripts/verify-built-boot.ts | |
| - name: Typecheck | |
| if: ${{ !cancelled() && steps.typecheck.outcome == 'success' }} | |
| timeout-minutes: 5 | |
| run: | | |
| until test -s "$RUNNER_TEMP/typecheck-status"; do sleep 1; done | |
| cat "$RUNNER_TEMP/typecheck.log" | |
| exit "$(cat "$RUNNER_TEMP/typecheck-status")" | |
| # Everything except `t3` (apps/server) and the web app, which has its own job | |
| # below. `--parallel` drops the package dependency ordering that `vp run` | |
| # applies by default: these `test` tasks declare no `dependsOn` and resolve | |
| # workspace deps from source, so ordering only bought us idle runners between | |
| # dependency layers. The concurrency limit stays at the default 4 so peak load | |
| # per runner is unchanged. | |
| test: | |
| permissions: | |
| contents: read | |
| id-token: write | |
| name: Test | |
| needs: changes | |
| if: ${{ !cancelled() && needs.changes.outputs.test != 'false' }} | |
| runs-on: blacksmith-8vcpu-ubuntu-2404 | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| with: | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| !/apps/mobile/modules/t3-terminal/Vendor/**/*.a | |
| sparse-checkout-cone-mode: false | |
| - uses: ./.github/actions/setup-apt-mirrors | |
| # The desktop tests compile the browser secret helper. The install runs in | |
| # the background while Vite+ installs; the step before the tests waits for it. | |
| - name: Start installing browser secret helper build libraries | |
| run: | | |
| # Fetch only the indexes these main and universe packages need. The full set is ~42 MB, | |
| # which a slow mirror can stretch past the wait below. | |
| sudo sed -i -e 's|http://|https://|g' -e 's/^Components: .*/Components: main universe/' \ | |
| /etc/apt/blacksmith-ubuntu-mirrors.txt /etc/apt/sources.list.d/ubuntu.sources | |
| ( | |
| set +e | |
| sudo apt-get update -o Acquire::Languages=none \ | |
| -o Acquire::IndexTargets::deb::DEP-11::DefaultEnabled=false \ | |
| -o Acquire::IndexTargets::deb::CNF::DefaultEnabled=false && | |
| sudo apt-get install -y libsecret-1-dev pkg-config | |
| echo "$?" > "$RUNNER_TEMP/apt-status" | |
| ) > "$RUNNER_TEMP/apt.log" 2>&1 < /dev/null & | |
| - name: Setup Vite+ | |
| uses: ./.github/actions/setup-ci-deps | |
| with: | |
| task-cache-read-token: ${{ secrets.T3_TASK_CACHE_READ_TOKEN }} | |
| # electron's install.js (run by ensure:electron) reads this cache before it | |
| # downloads from GitHub. The key changes only with the Electron version. | |
| - name: Cache Electron download | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ~/.cache/electron | |
| key: electron-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('node_modules/.pnpm/electron@*/node_modules/electron/package.json') }} | |
| - name: Ensure Electron runtime is installed | |
| run: vp run --filter @t3tools/desktop ensure:electron | |
| - name: Test preview artifact validation | |
| run: python3 -B .github/scripts/stage-preview-bundle.test.py | |
| - name: Test release checks | |
| run: node --test .github/scripts/check-nightly-release.test.cjs .github/scripts/vercel-deployment-output.test.cjs .github/scripts/mobile-preview-comment.test.cjs .github/scripts/test-delta.test.cjs | |
| - name: Finish installing browser secret helper build libraries | |
| timeout-minutes: 5 | |
| run: | | |
| until test -s "$RUNNER_TEMP/apt-status"; do sleep 1; done | |
| cat "$RUNNER_TEMP/apt.log" | |
| exit "$(cat "$RUNNER_TEMP/apt-status")" | |
| # `changes` lists the affected packages, or nothing when all of them are. | |
| - name: Test | |
| env: | |
| TEST_PACKAGES: ${{ needs.changes.outputs.test_packages }} | |
| run: | | |
| set -o pipefail | |
| filters=(--filter '!t3' --filter '!@t3tools/monorepo' --filter '!@t3tools/web') | |
| if [[ -n "$TEST_PACKAGES" ]]; then | |
| filters=() | |
| for package in $TEST_PACKAGES; do filters+=(--filter "$package"); done | |
| fi | |
| # The runner's trace writer can crash on surrogate pairs in test output. | |
| vp run --parallel --concurrency-limit 4 "${filters[@]}" test 2>&1 | python3 -u -c ' | |
| import sys | |
| for line in sys.stdin: | |
| sys.stdout.write(line.encode("ascii", "backslashreplace").decode("ascii")) | |
| ' | |
| # The web suite is the largest outside the server. In the shared Test job it | |
| # started last and ran alone for about 40s after everything else finished. | |
| # Browser tests are bound by the Vite server transforming modules for each | |
| # file, not by Chromium, so a second runner (and Vite server) halves the job | |
| # that gated every run. release.yml mirrors this test, test_web, and test_server split. | |
| test_web: | |
| permissions: | |
| contents: read | |
| id-token: write | |
| name: Test Web ${{ matrix.shard }} | |
| needs: changes | |
| if: ${{ !cancelled() && needs.changes.outputs.test_web != 'false' }} | |
| runs-on: blacksmith-8vcpu-ubuntu-2404 | |
| timeout-minutes: 10 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [1, 2] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| with: | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| !/apps/mobile/modules/t3-terminal/Vendor/**/*.a | |
| sparse-checkout-cone-mode: false | |
| - name: Setup Vite+ | |
| uses: ./.github/actions/setup-ci-deps | |
| with: | |
| task-cache-read-token: ${{ secrets.T3_TASK_CACHE_READ_TOKEN }} | |
| # The browser project runs React tests in headless Chromium. Same cache key as | |
| # test_server, so both jobs share one downloaded browser. | |
| - name: Resolve Playwright version | |
| id: playwright_version | |
| run: echo "version=$(node -p "require('./apps/web/node_modules/playwright/package.json').version")" >> "$GITHUB_OUTPUT" | |
| - name: Cache Chromium | |
| id: chromium_cache | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ~/.cache/ms-playwright | |
| key: playwright-chromium-shell-${{ runner.os }}-${{ runner.arch }}-${{ steps.playwright_version.outputs.version }} | |
| - name: Install Chromium for web browser tests | |
| if: steps.chromium_cache.outputs.cache-hit != 'true' | |
| run: node apps/web/node_modules/playwright/cli.js install --only-shell chromium | |
| - name: Install missing Chromium system dependencies | |
| run: | | |
| browser=$(find "$HOME/.cache/ms-playwright" -name chrome-headless-shell -type f -print -quit) | |
| test -n "$browser" | |
| if ldd "$browser" | grep '=> not found'; then | |
| node apps/web/node_modules/playwright/cli.js install-deps chromium | |
| fi | |
| - name: Test | |
| run: vp run --filter @t3tools/web test --shard ${{ matrix.shard }}/${{ strategy.job-total }} | |
| # apps/server sets `fileParallelism: false`, so its test files run strictly | |
| # one at a time. Sharding spreads them over separate runners instead of | |
| # separate workers, so no two server test files ever share a machine and the | |
| # isolation that flag buys is preserved exactly. One file at a time keeps | |
| # about 1.5 cores busy, so 4 vCPU runners lose nothing against 8. The shards | |
| # split files by recorded duration (apps/server/src/testUtils/weightedShardSequencer.ts). | |
| # The shards are the longest jobs, so they gate themselves instead of waiting | |
| # ~15s for `changes`: a shard the change cannot affect stops after checkout. | |
| test_server: | |
| permissions: | |
| contents: read | |
| id-token: write | |
| name: Test Server ${{ matrix.shard }} | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| # Shards take 2-5 minutes. When the shared task cache throttles reads, each | |
| # failed fetch stalls vp for 15-90s, which pushed shards past 10 minutes. | |
| timeout-minutes: 20 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [1, 2, 3, 4, 5, 6, 7, 8] | |
| steps: | |
| # Depth 2 brings the base commit the change is diffed against. | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| with: | |
| fetch-depth: 2 | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| !/apps/mobile/modules/t3-terminal/Vendor/**/*.a | |
| sparse-checkout-cone-mode: false | |
| - name: Detect affected jobs | |
| id: changes | |
| uses: ./.github/actions/ci-changes | |
| - name: Setup Vite+ | |
| if: steps.changes.outputs.test_server != 'false' | |
| uses: ./.github/actions/setup-ci-deps | |
| with: | |
| task-cache-read-token: ${{ secrets.T3_TASK_CACHE_READ_TOKEN }} | |
| # No Electron setup here: `t3` (apps/server) has no Electron dependency | |
| # and none of its tests touch the runtime. Only the non-server `test` | |
| # job, which covers @t3tools/desktop, needs the download. | |
| # Cache the pinned browser and skip apt unless shared libraries are missing. | |
| - name: Resolve Playwright version | |
| id: playwright_version | |
| if: steps.changes.outputs.test_server != 'false' | |
| run: echo "version=$(node -p "require('./apps/server/node_modules/playwright-core/package.json').version")" >> "$GITHUB_OUTPUT" | |
| - name: Cache Chromium | |
| id: chromium_cache | |
| if: steps.changes.outputs.test_server != 'false' | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ~/.cache/ms-playwright | |
| key: playwright-chromium-shell-${{ runner.os }}-${{ runner.arch }}-${{ steps.playwright_version.outputs.version }} | |
| - name: Install Chromium for server browser integration tests | |
| if: steps.changes.outputs.test_server != 'false' && steps.chromium_cache.outputs.cache-hit != 'true' | |
| run: node apps/server/node_modules/playwright-core/cli.js install --only-shell chromium | |
| - name: Install missing Chromium system dependencies | |
| if: steps.changes.outputs.test_server != 'false' | |
| run: | | |
| browser=$(find "$HOME/.cache/ms-playwright" -name chrome-headless-shell -type f -print -quit) | |
| test -n "$browser" | |
| if ldd "$browser" | grep '=> not found'; then | |
| node apps/server/node_modules/playwright-core/cli.js install-deps chromium | |
| fi | |
| # Each server test file runs in its own Vitest process, and a fresh install starts | |
| # Vitest's content-keyed module cache empty, so every file re-transformed its imports. | |
| # Only pushes to main save it, so pull requests never evict each other's entries. | |
| - name: Restore Vitest module cache | |
| if: steps.changes.outputs.test_server != 'false' | |
| uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: node_modules/.vitest-cache | |
| key: vitest-modules-server-${{ matrix.shard }}-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}-${{ github.sha }} | |
| restore-keys: vitest-modules-server-${{ matrix.shard }}-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}- | |
| - name: Test | |
| if: steps.changes.outputs.test_server != 'false' | |
| env: | |
| T3CODE_TRANSFER_BUDGET_REPORT_PATH: ${{ runner.temp }}/t3code-transfer-budget.md | |
| T3CODE_TRANSFER_BUDGET_RESULT_PATH: ${{ runner.temp }}/thread-transfer-result.json | |
| T3CODE_TRANSFER_BUDGET_SCENARIOS_PATH: ${{ runner.temp }}/thread-transfer-scenarios.json | |
| # One process per file spends ~2s starting before its tests run; one file per vCPU overlaps that. | |
| run: >- | |
| vp run test:cached -- --shard ${{ matrix.shard }}/${{ strategy.job-total }} --concurrency 4 | |
| --uncached apps/server/integration/transferBudgetV2.integration.test.ts | |
| --verify-hits 1 apps/server | |
| - name: Save Vitest module cache | |
| if: github.event_name == 'push' && steps.changes.outputs.test_server != 'false' | |
| uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: node_modules/.vitest-cache | |
| key: vitest-modules-server-${{ matrix.shard }}-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}-${{ github.sha }} | |
| # integration/transferBudgetV2.integration.test.ts writes the report; exactly one shard | |
| # produces these files. Gating the upload on their presence keeps a | |
| # single `thread-transfer-results` artifact per run, which is the name | |
| # thread-transfer-report.yml resolves. | |
| - name: Detect transfer budget report | |
| id: transfer_budget | |
| if: always() | |
| run: | | |
| if test -f "${{ runner.temp }}/thread-transfer-result.json"; then | |
| echo "present=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "present=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Publish transfer budget report | |
| if: always() && steps.transfer_budget.outputs.present == 'true' | |
| run: | | |
| if test -f "${{ runner.temp }}/t3code-transfer-budget.md"; then | |
| tee -a "$GITHUB_STEP_SUMMARY" < "${{ runner.temp }}/t3code-transfer-budget.md" | |
| else | |
| echo "Transfer budget report was not produced." >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| - name: Upload thread transfer result | |
| if: always() && steps.transfer_budget.outputs.present == 'true' | |
| uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 | |
| with: | |
| name: thread-transfer-results | |
| path: | | |
| ${{ runner.temp }}/thread-transfer-result.json | |
| ${{ runner.temp }}/thread-transfer-scenarios.json | |
| if-no-files-found: ignore | |
| retention-days: 30 | |
| # 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: | |
| name: Rust | |
| needs: changes | |
| if: ${{ !cancelled() && needs.changes.outputs.rust != 'false' }} | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| with: | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| !/apps/mobile/modules/t3-terminal/Vendor/**/*.a | |
| sparse-checkout-cone-mode: false | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # master | |
| with: | |
| toolchain: stable | |
| components: rustfmt | |
| - name: Check Rust formatting | |
| run: | | |
| for crate in resource-monitor kde-snap-shot hyprland-snap-shot; do | |
| cargo fmt --manifest-path "native/$crate/Cargo.toml" -- --check | |
| done | |
| - name: Test Rust crates | |
| run: | | |
| for crate in resource-monitor kde-snap-shot hyprland-snap-shot; do | |
| cargo test --locked --manifest-path "native/$crate/Cargo.toml" | |
| done | |
| mobile_native_static_analysis: | |
| name: Mobile Native Static Analysis | |
| needs: changes | |
| if: ${{ !cancelled() && needs.changes.outputs.mobile_native != 'false' }} | |
| runs-on: blacksmith-6vcpu-macos-27 | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| with: | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| sparse-checkout-cone-mode: false | |
| - name: Setup Vite+ | |
| uses: voidzero-dev/setup-vp@250f29ce396baf5e8f24498e17c0dfdebabc26eb # v1.15.0 | |
| with: | |
| node-version-file: package.json | |
| cache: true | |
| run-install: | | |
| args: | |
| - --filter=@t3tools/scripts... | |
| - name: Install mobile native static analysis tools | |
| run: brew bundle install --file apps/mobile/Brewfile | |
| - name: Lint mobile native sources | |
| run: vp run lint:mobile | |
| release_smoke: | |
| permissions: | |
| contents: read | |
| id-token: write | |
| name: Release Smoke | |
| needs: changes | |
| if: ${{ !cancelled() && needs.changes.outputs.release_smoke != 'false' }} | |
| runs-on: blacksmith-8vcpu-ubuntu-2404 | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| with: | |
| sparse-checkout: | | |
| /* | |
| !/.repos/ | |
| !/apps/mobile/modules/t3-terminal/Vendor/**/*.a | |
| sparse-checkout-cone-mode: false | |
| - name: Setup Vite+ | |
| uses: ./.github/actions/setup-ci-deps | |
| with: | |
| task-cache-read-token: ${{ secrets.T3_TASK_CACHE_READ_TOKEN }} | |
| - name: Exercise release-only workflow steps | |
| run: node scripts/release-smoke.ts | |
| # Branch protection requires this job by its name, `Check`. It fails when any | |
| # other job fails, is cancelled, or is skipped without a reason, so splitting | |
| # or resharding jobs needs no ruleset change. Add every new job to `needs`. | |
| # `always()` rather than `!cancelled()`: a skipped job reports success to | |
| # branch protection, so a cancelled run must still produce a failing check. | |
| check: | |
| name: Check | |
| if: ${{ always() }} | |
| needs: | |
| [ | |
| changes, | |
| lint, | |
| build, | |
| test, | |
| test_web, | |
| test_server, | |
| rust, | |
| mobile_native_static_analysis, | |
| release_smoke, | |
| ] | |
| runs-on: blacksmith-2vcpu-ubuntu-2404 | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Require every job to pass | |
| env: | |
| RESULTS: ${{ toJSON(needs) }} | |
| 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 and the | |
| # macOS lint. | |
| echo "$RESULTS" | jq -e '.changes.outputs as $gates | |
| | {build: ["build", "typecheck", "desktop_launch"], 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@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 | |
| with: | |
| name: thread-transfer-results | |
| path: ${{ runner.temp }}/thread-transfer |