From a06ab437a5a30fc9f82d39053e2128ad780e333d Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Tue, 21 Jul 2026 23:29:08 -0700 Subject: [PATCH 01/11] add skip guard for electron postinstall rebuild --- apps/code/scripts/postinstall.sh | 37 +++++++++++++++++++------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/apps/code/scripts/postinstall.sh b/apps/code/scripts/postinstall.sh index 4f12df1a62..405c654a46 100755 --- a/apps/code/scripts/postinstall.sh +++ b/apps/code/scripts/postinstall.sh @@ -8,23 +8,30 @@ set -e REPO_ROOT="$(cd ../.. && pwd)" SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)" -# Self-heal missing Electron binary. -# pnpm skips package-level postinstall scripts when the lockfile is already -# satisfied, so if node_modules/electron/dist gets wiped (interrupted download, -# cache eviction, arch change, manual cleanup), `pnpm install` won't notice — -# and `electron-vite dev` then fails with "Electron failed to install -# correctly, please delete node_modules/electron and try installing again". -# Detect the missing binary and invoke Electron's own install script to fetch it. -ELECTRON_DIST="$REPO_ROOT/node_modules/electron/dist" -if [ ! -d "$ELECTRON_DIST" ] || [ -z "$(ls -A "$ELECTRON_DIST" 2>/dev/null)" ]; then - echo "Electron binary missing at $ELECTRON_DIST — downloading..." - node "$REPO_ROOT/node_modules/electron/install.js" -fi +# CI jobs that never launch or package Electron (typecheck, lint, unit tests, +# storybook) set SKIP_ELECTRON_REBUILD=1 to skip the binary download and the +# Electron-ABI node-gyp rebuild, which otherwise adds ~60s to every install. +if [ "$SKIP_ELECTRON_REBUILD" = "1" ]; then + echo "SKIP_ELECTRON_REBUILD=1: skipping Electron binary check and native rebuild." +else + # Self-heal missing Electron binary. + # pnpm skips package-level postinstall scripts when the lockfile is already + # satisfied, so if node_modules/electron/dist gets wiped (interrupted download, + # cache eviction, arch change, manual cleanup), `pnpm install` won't notice — + # and `electron-vite dev` then fails with "Electron failed to install + # correctly, please delete node_modules/electron and try installing again". + # Detect the missing binary and invoke Electron's own install script to fetch it. + ELECTRON_DIST="$REPO_ROOT/node_modules/electron/dist" + if [ ! -d "$ELECTRON_DIST" ] || [ -z "$(ls -A "$ELECTRON_DIST" 2>/dev/null)" ]; then + echo "Electron binary missing at $ELECTRON_DIST — downloading..." + node "$REPO_ROOT/node_modules/electron/install.js" + fi -echo "Rebuilding native modules for Electron..." + echo "Rebuilding native modules for Electron..." -cd "$REPO_ROOT" -node scripts/rebuild-better-sqlite3-electron.mjs + cd "$REPO_ROOT" + node scripts/rebuild-better-sqlite3-electron.mjs +fi # Restore the execute bit on node-pty's spawn-helper. pnpm extracts node-pty's # prebuilt binaries without preserving the executable mode, so the helper lands From 3713efb48a90b33bfcaf8a4fd351972b4cb36d96 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Tue, 21 Jul 2026 23:29:08 -0700 Subject: [PATCH 02/11] warm shared CI caches on main pushes --- .github/workflows/warm-caches.yml | 111 ++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 .github/workflows/warm-caches.yml diff --git a/.github/workflows/warm-caches.yml b/.github/workflows/warm-caches.yml new file mode 100644 index 0000000000..3fb982d378 --- /dev/null +++ b/.github/workflows/warm-caches.yml @@ -0,0 +1,111 @@ +name: Warm caches + +# Actions caches saved on a PR's merge ref are invisible to every other PR; +# only caches saved on main are shared with all branches. The PR workflows +# only run on pull_request, so without this job nothing ever seeds the macOS +# pnpm/Electron/Playwright caches (or the turbo cache) on main and every new +# PR pays the full install plus a multi-minute cache re-save. This workflow +# runs on each push to main and saves those caches under the exact keys the +# PR jobs restore. + +on: + push: + branches: [main] + workflow_dispatch: + +concurrency: + group: warm-caches-${{ github.ref }} + cancel-in-progress: true + +jobs: + linux: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Setup pnpm + uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 + + - name: Setup Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: 22 + cache: "pnpm" + + # Saved fresh per main commit (no restore) so the cache holds exactly the + # artifacts for this sha and never grows unbounded. PR jobs restore it + # via the turbo- prefix and get cache hits for unchanged packages. + - name: Cache turbo artifacts + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: .turbo/cache + key: turbo-${{ github.sha }} + + - name: Install dependencies + run: pnpm install --frozen-lockfile + env: + SKIP_ELECTRON_REBUILD: "1" + ELECTRON_SKIP_BINARY_DOWNLOAD: "1" + + - name: Seed turbo cache (build + typecheck) + run: | + pnpm exec turbo build --filter='@posthog/code^...' + pnpm exec turbo typecheck + + macos: + runs-on: macos-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Setup pnpm + uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 + + - name: Setup Node.js + id: node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: 22 + cache: "pnpm" + + # Same keys as test.yml's integration-test job. + - name: Cache Electron binary + id: electron-cache + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ~/Library/Caches/electron + key: electron-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} + + - name: Cache Playwright browsers + id: playwright-cache + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ~/Library/Caches/ms-playwright + key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} + + # Everything below only needs to run when a cache is missing; macOS + # runner minutes are 10x, so exit cheaply when main is already warm. + - name: Install dependencies + if: >- + steps.node.outputs.cache-hit != 'true' || + steps.electron-cache.outputs.cache-hit != 'true' || + steps.playwright-cache.outputs.cache-hit != 'true' + # Electron's own postinstall populates ~/Library/Caches/electron, so + # the binary download must stay on. The Electron-ABI sqlite rebuild + # writes only to node_modules, which is not cached, so skip it. + run: pnpm install --frozen-lockfile + env: + SKIP_ELECTRON_REBUILD: "1" + + - name: Install Playwright Chromium + if: steps.playwright-cache.outputs.cache-hit != 'true' + run: pnpm --filter code exec playwright install chromium From 357b22f1a580083e1b0b2abe9368e8246e62f12f Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Tue, 21 Jul 2026 23:29:08 -0700 Subject: [PATCH 03/11] skip electron work and use turbo in CI jobs --- .github/workflows/build.yml | 37 +++++++-------- .github/workflows/code-storybook.yml | 49 +++++++------------- .github/workflows/test.yml | 68 +++++++++++++++++++--------- .github/workflows/typecheck.yml | 15 ++++++ 4 files changed, 96 insertions(+), 73 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9fb00dacda..5cb7338f10 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,29 +59,26 @@ jobs: node-version: 22 cache: "pnpm" + # Restore-only: warm-caches.yml seeds this on main pushes. PR jobs never + # save it, so the cache quota is not churned by per-PR copies. + - name: Restore turbo cache + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: .turbo/cache + key: turbo-${{ github.sha }} + restore-keys: | + turbo- + - name: Install dependencies run: pnpm install --frozen-lockfile + env: + # The packaged --dir app below is never launched, so the Electron-ABI + # sqlite rebuild is dead weight. Electron's own binary download stays + # on because electron-builder packages that binary. + SKIP_ELECTRON_REBUILD: "1" - - name: Build electron-trpc - run: pnpm --filter @posthog/electron-trpc build - - - name: Build platform - run: pnpm --filter @posthog/platform build - - - name: Build shared - run: pnpm --filter @posthog/shared build - - - name: Build git - run: pnpm --filter @posthog/git build - - - name: Build enricher - run: pnpm --filter @posthog/enricher build - - - name: Build harness - run: pnpm --filter @posthog/harness build - - - name: Build agent - run: pnpm --filter agent build + - name: Build packages + run: pnpm exec turbo build --filter='@posthog/code^...' - name: Build code run: pnpm --filter code build diff --git a/.github/workflows/code-storybook.yml b/.github/workflows/code-storybook.yml index 6b96e92983..dae696dd03 100644 --- a/.github/workflows/code-storybook.yml +++ b/.github/workflows/code-storybook.yml @@ -115,8 +115,22 @@ jobs: restore-keys: | playwright-chromium-${{ runner.os }}- + # Restore-only: warm-caches.yml seeds this on main pushes. PR jobs never + # save it, so the cache quota is not churned by per-PR copies. + - name: Restore turbo cache + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: .turbo/cache + key: turbo-${{ github.sha }} + restore-keys: | + turbo- + - name: Install dependencies run: pnpm install --frozen-lockfile + env: + # Stories render in Chromium; Electron and its ABI rebuild are unused. + SKIP_ELECTRON_REBUILD: "1" + ELECTRON_SKIP_BINARY_DOWNLOAD: "1" - name: Install Playwright Chromium if: steps.playwright-cache.outputs.cache-hit != 'true' @@ -130,38 +144,9 @@ jobs: run: pnpm exec playwright install-deps chromium - name: Build workspace packages - # Bare `wait` returns the exit status of only the last job passed to - # it, silently swallowing failures from the others. `wait_all` waits - # on each PID individually and checks its own status so any failed - # build fails the step. - run: | - wait_all() { - status=0 - for pid in "$@"; do - wait "$pid" || status=$? - done - [ "$status" -eq 0 ] - } - - pnpm --filter @posthog/electron-trpc build & - pid1=$! - (pnpm --filter @posthog/shared build && pnpm --filter @posthog/platform build) & - pid2=$! - wait_all "$pid1" "$pid2" - - # @posthog/agent imports the dist of @posthog/git and - # @posthog/harness, so both must finish before the last group starts. - pnpm --filter @posthog/git build & - pid3=$! - pnpm --filter @posthog/harness build & - pid4=$! - wait_all "$pid3" "$pid4" - - pnpm --filter @posthog/enricher build & - pid5=$! - pnpm --filter @posthog/agent build & - pid6=$! - wait_all "$pid5" "$pid6" + # turbo parallelizes from the dependency graph and fails the step on + # any failed build, replacing the hand-rolled fork/wait choreography. + run: pnpm exec turbo build --filter='@posthog/code^...' - name: Build Storybook working-directory: apps/code diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 837103044c..8644159b3b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -62,15 +62,30 @@ jobs: node-version: 22 cache: "pnpm" + # Restore-only: warm-caches.yml seeds this on main pushes. `turbo test` + # builds each package's dependencies first (dependsOn ^build), so hits + # here skip those builds. Test tasks themselves are never in the seed + # and always run. + - name: Restore turbo cache + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: .turbo/cache + key: turbo-${{ github.sha }} + restore-keys: | + turbo- + - name: Install dependencies run: pnpm install --frozen-lockfile + env: + # Unit tests run under plain Node; the Electron binary and the + # Electron-ABI sqlite rebuild are unused (the rebuild is immediately + # undone by the Node rebuild below). + SKIP_ELECTRON_REBUILD: "1" + ELECTRON_SKIP_BINARY_DOWNLOAD: "1" - name: Rebuild native modules for Node run: node scripts/rebuild-better-sqlite3-node.mjs - - name: Build dependencies - run: pnpm --filter @posthog/electron-trpc build - - name: Run tests run: pnpm test @@ -127,22 +142,23 @@ jobs: restore-keys: | electron-${{ runner.os }}- + # Restore-only: warm-caches.yml seeds this on main pushes. turbo cache + # artifacts are plain JS/dts, so the Linux-seeded cache is valid here. + - name: Restore turbo cache + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: .turbo/cache + key: turbo-${{ github.sha }} + restore-keys: | + turbo- + + # apps/code's postinstall already rebuilds better-sqlite3 for Electron + # during install; no separate rebuild step needed. - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Rebuild better-sqlite3 for Electron - run: node scripts/rebuild-better-sqlite3-electron.mjs - - name: Build packages - run: | - pnpm --filter @posthog/electron-trpc build & - pnpm --filter @posthog/platform build & - pnpm --filter @posthog/shared build - pnpm --filter @posthog/git build - pnpm --filter @posthog/enricher build - wait - pnpm --filter @posthog/harness build - pnpm --filter agent build + run: pnpm exec turbo build --filter='@posthog/code^...' - name: Package Electron app run: pnpm --filter code run package @@ -230,22 +246,32 @@ jobs: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0 + uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - name: Setup Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 22 cache: "pnpm" + # Restore-only: warm-caches.yml seeds this on main pushes. + - name: Restore turbo cache + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: .turbo/cache + key: turbo-${{ github.sha }} + restore-keys: | + turbo- + - name: Install dependencies run: pnpm install --frozen-lockfile + env: + # The agent e2e suite runs under plain Node; Electron is unused. + SKIP_ELECTRON_REBUILD: "1" + ELECTRON_SKIP_BINARY_DOWNLOAD: "1" - name: Build agent dependencies - run: | - pnpm --filter @posthog/shared run build - pnpm --filter @posthog/git run build - pnpm --filter @posthog/enricher run build + run: pnpm exec turbo build --filter=@posthog/shared --filter=@posthog/git --filter=@posthog/enricher - name: Download native codex binary # Non-fatal at the STEP so a failure surfaces as the fail-loud binary guard diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index c469e80216..96e79233c3 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -59,8 +59,23 @@ jobs: node-version: 22 cache: "pnpm" + # Restore-only: warm-caches.yml seeds this on main pushes. PR jobs never + # save it, so the cache quota is not churned by per-PR copies. + - name: Restore turbo cache + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: .turbo/cache + key: turbo-${{ github.sha }} + restore-keys: | + turbo- + - name: Install dependencies run: pnpm install --frozen-lockfile + env: + # Typecheck never launches Electron; skip its binary download and + # the Electron-ABI native rebuild. + SKIP_ELECTRON_REBUILD: "1" + ELECTRON_SKIP_BINARY_DOWNLOAD: "1" - name: Run type check run: pnpm run typecheck From 6dbab202692922e0851fddeb78168f2dc37c02b2 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Tue, 21 Jul 2026 23:48:54 -0700 Subject: [PATCH 04/11] remove no-op electron skip var, add timeouts --- .github/workflows/code-storybook.yml | 1 - .github/workflows/test.yml | 2 -- .github/workflows/typecheck.yml | 1 - .github/workflows/warm-caches.yml | 3 ++- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/code-storybook.yml b/.github/workflows/code-storybook.yml index dae696dd03..8421756e8a 100644 --- a/.github/workflows/code-storybook.yml +++ b/.github/workflows/code-storybook.yml @@ -130,7 +130,6 @@ jobs: env: # Stories render in Chromium; Electron and its ABI rebuild are unused. SKIP_ELECTRON_REBUILD: "1" - ELECTRON_SKIP_BINARY_DOWNLOAD: "1" - name: Install Playwright Chromium if: steps.playwright-cache.outputs.cache-hit != 'true' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8644159b3b..1600a4159e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,7 +81,6 @@ jobs: # Electron-ABI sqlite rebuild are unused (the rebuild is immediately # undone by the Node rebuild below). SKIP_ELECTRON_REBUILD: "1" - ELECTRON_SKIP_BINARY_DOWNLOAD: "1" - name: Rebuild native modules for Node run: node scripts/rebuild-better-sqlite3-node.mjs @@ -268,7 +267,6 @@ jobs: env: # The agent e2e suite runs under plain Node; Electron is unused. SKIP_ELECTRON_REBUILD: "1" - ELECTRON_SKIP_BINARY_DOWNLOAD: "1" - name: Build agent dependencies run: pnpm exec turbo build --filter=@posthog/shared --filter=@posthog/git --filter=@posthog/enricher diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 96e79233c3..49034695a4 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -75,7 +75,6 @@ jobs: # Typecheck never launches Electron; skip its binary download and # the Electron-ABI native rebuild. SKIP_ELECTRON_REBUILD: "1" - ELECTRON_SKIP_BINARY_DOWNLOAD: "1" - name: Run type check run: pnpm run typecheck diff --git a/.github/workflows/warm-caches.yml b/.github/workflows/warm-caches.yml index 3fb982d378..a794b95c82 100644 --- a/.github/workflows/warm-caches.yml +++ b/.github/workflows/warm-caches.yml @@ -20,6 +20,7 @@ concurrency: jobs: linux: runs-on: ubuntu-latest + timeout-minutes: 15 permissions: contents: read steps: @@ -50,7 +51,6 @@ jobs: run: pnpm install --frozen-lockfile env: SKIP_ELECTRON_REBUILD: "1" - ELECTRON_SKIP_BINARY_DOWNLOAD: "1" - name: Seed turbo cache (build + typecheck) run: | @@ -59,6 +59,7 @@ jobs: macos: runs-on: macos-latest + timeout-minutes: 30 permissions: contents: read steps: From 0bdca6ec5b64261218ae48a4beec9d6ba968cb73 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Tue, 21 Jul 2026 23:49:46 -0700 Subject: [PATCH 05/11] trim CI comments to essentials --- .github/workflows/build.yml | 7 ++----- .github/workflows/code-storybook.yml | 6 +----- .github/workflows/test.yml | 20 +++++++------------- .github/workflows/typecheck.yml | 5 +---- .github/workflows/warm-caches.yml | 21 ++++++--------------- apps/code/scripts/postinstall.sh | 4 +--- 6 files changed, 18 insertions(+), 45 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5cb7338f10..96358ce126 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,8 +59,7 @@ jobs: node-version: 22 cache: "pnpm" - # Restore-only: warm-caches.yml seeds this on main pushes. PR jobs never - # save it, so the cache quota is not churned by per-PR copies. + # Restore-only; warm-caches.yml seeds this on main. - name: Restore turbo cache uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -72,9 +71,7 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile env: - # The packaged --dir app below is never launched, so the Electron-ABI - # sqlite rebuild is dead weight. Electron's own binary download stays - # on because electron-builder packages that binary. + # The packaged app is never launched, so the Electron-ABI rebuild is unused. SKIP_ELECTRON_REBUILD: "1" - name: Build packages diff --git a/.github/workflows/code-storybook.yml b/.github/workflows/code-storybook.yml index 8421756e8a..635b7d60a0 100644 --- a/.github/workflows/code-storybook.yml +++ b/.github/workflows/code-storybook.yml @@ -115,8 +115,7 @@ jobs: restore-keys: | playwright-chromium-${{ runner.os }}- - # Restore-only: warm-caches.yml seeds this on main pushes. PR jobs never - # save it, so the cache quota is not churned by per-PR copies. + # Restore-only; warm-caches.yml seeds this on main. - name: Restore turbo cache uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -128,7 +127,6 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile env: - # Stories render in Chromium; Electron and its ABI rebuild are unused. SKIP_ELECTRON_REBUILD: "1" - name: Install Playwright Chromium @@ -143,8 +141,6 @@ jobs: run: pnpm exec playwright install-deps chromium - name: Build workspace packages - # turbo parallelizes from the dependency graph and fails the step on - # any failed build, replacing the hand-rolled fork/wait choreography. run: pnpm exec turbo build --filter='@posthog/code^...' - name: Build Storybook diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1600a4159e..61c42c1959 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -62,10 +62,8 @@ jobs: node-version: 22 cache: "pnpm" - # Restore-only: warm-caches.yml seeds this on main pushes. `turbo test` - # builds each package's dependencies first (dependsOn ^build), so hits - # here skip those builds. Test tasks themselves are never in the seed - # and always run. + # Restore-only; warm-caches.yml seeds this on main. Only dependency + # builds are cached, tests themselves always run. - name: Restore turbo cache uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -77,9 +75,7 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile env: - # Unit tests run under plain Node; the Electron binary and the - # Electron-ABI sqlite rebuild are unused (the rebuild is immediately - # undone by the Node rebuild below). + # The Electron-ABI rebuild would be undone by the Node rebuild below. SKIP_ELECTRON_REBUILD: "1" - name: Rebuild native modules for Node @@ -141,8 +137,8 @@ jobs: restore-keys: | electron-${{ runner.os }}- - # Restore-only: warm-caches.yml seeds this on main pushes. turbo cache - # artifacts are plain JS/dts, so the Linux-seeded cache is valid here. + # Seeded on Linux by warm-caches.yml; turbo artifacts are plain JS, so + # cross-OS restore is fine. - name: Restore turbo cache uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -151,8 +147,7 @@ jobs: restore-keys: | turbo- - # apps/code's postinstall already rebuilds better-sqlite3 for Electron - # during install; no separate rebuild step needed. + # apps/code's postinstall rebuilds better-sqlite3 for Electron here. - name: Install dependencies run: pnpm install --frozen-lockfile @@ -253,7 +248,7 @@ jobs: node-version: 22 cache: "pnpm" - # Restore-only: warm-caches.yml seeds this on main pushes. + # Restore-only; warm-caches.yml seeds this on main. - name: Restore turbo cache uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -265,7 +260,6 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile env: - # The agent e2e suite runs under plain Node; Electron is unused. SKIP_ELECTRON_REBUILD: "1" - name: Build agent dependencies diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 49034695a4..c440ec559b 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -59,8 +59,7 @@ jobs: node-version: 22 cache: "pnpm" - # Restore-only: warm-caches.yml seeds this on main pushes. PR jobs never - # save it, so the cache quota is not churned by per-PR copies. + # Restore-only; warm-caches.yml seeds this on main. - name: Restore turbo cache uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -72,8 +71,6 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile env: - # Typecheck never launches Electron; skip its binary download and - # the Electron-ABI native rebuild. SKIP_ELECTRON_REBUILD: "1" - name: Run type check diff --git a/.github/workflows/warm-caches.yml b/.github/workflows/warm-caches.yml index a794b95c82..10f1556285 100644 --- a/.github/workflows/warm-caches.yml +++ b/.github/workflows/warm-caches.yml @@ -1,12 +1,7 @@ name: Warm caches -# Actions caches saved on a PR's merge ref are invisible to every other PR; -# only caches saved on main are shared with all branches. The PR workflows -# only run on pull_request, so without this job nothing ever seeds the macOS -# pnpm/Electron/Playwright caches (or the turbo cache) on main and every new -# PR pays the full install plus a multi-minute cache re-save. This workflow -# runs on each push to main and saves those caches under the exact keys the -# PR jobs restore. +# GitHub only shares caches saved on main with other branches, and the PR +# workflows never run on main. This job seeds the caches they restore. on: push: @@ -38,9 +33,7 @@ jobs: node-version: 22 cache: "pnpm" - # Saved fresh per main commit (no restore) so the cache holds exactly the - # artifacts for this sha and never grows unbounded. PR jobs restore it - # via the turbo- prefix and get cache hits for unchanged packages. + # No restore-keys: a fresh save per commit keeps the cache from growing forever. - name: Cache turbo artifacts uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -93,16 +86,14 @@ jobs: path: ~/Library/Caches/ms-playwright key: playwright-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} - # Everything below only needs to run when a cache is missing; macOS - # runner minutes are 10x, so exit cheaply when main is already warm. + # macOS minutes are 10x; do nothing when every cache is already warm. - name: Install dependencies if: >- steps.node.outputs.cache-hit != 'true' || steps.electron-cache.outputs.cache-hit != 'true' || steps.playwright-cache.outputs.cache-hit != 'true' - # Electron's own postinstall populates ~/Library/Caches/electron, so - # the binary download must stay on. The Electron-ABI sqlite rebuild - # writes only to node_modules, which is not cached, so skip it. + # Electron's own postinstall fills ~/Library/Caches/electron; the sqlite + # rebuild only touches node_modules, which is not cached, so skip it. run: pnpm install --frozen-lockfile env: SKIP_ELECTRON_REBUILD: "1" diff --git a/apps/code/scripts/postinstall.sh b/apps/code/scripts/postinstall.sh index 405c654a46..fd64a266c1 100755 --- a/apps/code/scripts/postinstall.sh +++ b/apps/code/scripts/postinstall.sh @@ -8,9 +8,7 @@ set -e REPO_ROOT="$(cd ../.. && pwd)" SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)" -# CI jobs that never launch or package Electron (typecheck, lint, unit tests, -# storybook) set SKIP_ELECTRON_REBUILD=1 to skip the binary download and the -# Electron-ABI node-gyp rebuild, which otherwise adds ~60s to every install. +# CI jobs that never run Electron set this to skip ~60s of node-gyp per install. if [ "$SKIP_ELECTRON_REBUILD" = "1" ]; then echo "SKIP_ELECTRON_REBUILD=1: skipping Electron binary check and native rebuild." else From d556903ce0e7fe00120381be0d9ddbe5815ae82d Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Wed, 22 Jul 2026 10:40:29 -0700 Subject: [PATCH 06/11] extract turbo cache restore composite action --- .../actions/restore-turbo-cache/action.yml | 13 +++++++++ .github/workflows/build.yml | 8 +----- .github/workflows/code-storybook.yml | 8 +----- .github/workflows/test.yml | 28 ++++--------------- .github/workflows/typecheck.yml | 8 +----- 5 files changed, 21 insertions(+), 44 deletions(-) create mode 100644 .github/actions/restore-turbo-cache/action.yml diff --git a/.github/actions/restore-turbo-cache/action.yml b/.github/actions/restore-turbo-cache/action.yml new file mode 100644 index 0000000000..32c446c948 --- /dev/null +++ b/.github/actions/restore-turbo-cache/action.yml @@ -0,0 +1,13 @@ +name: Restore turbo cache +description: Restore-only turbo cache. warm-caches.yml seeds it on main pushes; PR workflows never save, so per-PR entries don't pollute the cache store. + +runs: + using: composite + steps: + - name: Restore turbo cache + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: .turbo/cache + key: turbo-${{ github.sha }} + restore-keys: | + turbo- diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 96358ce126..f554f38ad3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,14 +59,8 @@ jobs: node-version: 22 cache: "pnpm" - # Restore-only; warm-caches.yml seeds this on main. - name: Restore turbo cache - uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: .turbo/cache - key: turbo-${{ github.sha }} - restore-keys: | - turbo- + uses: ./.github/actions/restore-turbo-cache - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/.github/workflows/code-storybook.yml b/.github/workflows/code-storybook.yml index 635b7d60a0..a7e0ddb6a0 100644 --- a/.github/workflows/code-storybook.yml +++ b/.github/workflows/code-storybook.yml @@ -115,14 +115,8 @@ jobs: restore-keys: | playwright-chromium-${{ runner.os }}- - # Restore-only; warm-caches.yml seeds this on main. - name: Restore turbo cache - uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: .turbo/cache - key: turbo-${{ github.sha }} - restore-keys: | - turbo- + uses: ./.github/actions/restore-turbo-cache - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 61c42c1959..8688ce11fa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -62,15 +62,9 @@ jobs: node-version: 22 cache: "pnpm" - # Restore-only; warm-caches.yml seeds this on main. Only dependency - # builds are cached, tests themselves always run. + # Only dependency builds are cached, tests themselves always run. - name: Restore turbo cache - uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: .turbo/cache - key: turbo-${{ github.sha }} - restore-keys: | - turbo- + uses: ./.github/actions/restore-turbo-cache - name: Install dependencies run: pnpm install --frozen-lockfile @@ -137,15 +131,9 @@ jobs: restore-keys: | electron-${{ runner.os }}- - # Seeded on Linux by warm-caches.yml; turbo artifacts are plain JS, so - # cross-OS restore is fine. + # Seeded on Linux; turbo artifacts are plain JS, so cross-OS restore is fine. - name: Restore turbo cache - uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: .turbo/cache - key: turbo-${{ github.sha }} - restore-keys: | - turbo- + uses: ./.github/actions/restore-turbo-cache # apps/code's postinstall rebuilds better-sqlite3 for Electron here. - name: Install dependencies @@ -248,14 +236,8 @@ jobs: node-version: 22 cache: "pnpm" - # Restore-only; warm-caches.yml seeds this on main. - name: Restore turbo cache - uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: .turbo/cache - key: turbo-${{ github.sha }} - restore-keys: | - turbo- + uses: ./.github/actions/restore-turbo-cache - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index c440ec559b..cb17490b83 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -59,14 +59,8 @@ jobs: node-version: 22 cache: "pnpm" - # Restore-only; warm-caches.yml seeds this on main. - name: Restore turbo cache - uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: .turbo/cache - key: turbo-${{ github.sha }} - restore-keys: | - turbo- + uses: ./.github/actions/restore-turbo-cache - name: Install dependencies run: pnpm install --frozen-lockfile From 6f6a1fece22ea51302d52e721de49eba0acf14b6 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Wed, 22 Jul 2026 10:40:46 -0700 Subject: [PATCH 07/11] reuse build:deps script in CI workflows --- .github/workflows/build.yml | 2 +- .github/workflows/code-storybook.yml | 2 +- .github/workflows/test.yml | 2 +- .github/workflows/warm-caches.yml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f554f38ad3..38e6391502 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -69,7 +69,7 @@ jobs: SKIP_ELECTRON_REBUILD: "1" - name: Build packages - run: pnpm exec turbo build --filter='@posthog/code^...' + run: pnpm run build:deps - name: Build code run: pnpm --filter code build diff --git a/.github/workflows/code-storybook.yml b/.github/workflows/code-storybook.yml index a7e0ddb6a0..92a42fd72b 100644 --- a/.github/workflows/code-storybook.yml +++ b/.github/workflows/code-storybook.yml @@ -135,7 +135,7 @@ jobs: run: pnpm exec playwright install-deps chromium - name: Build workspace packages - run: pnpm exec turbo build --filter='@posthog/code^...' + run: pnpm run build:deps - name: Build Storybook working-directory: apps/code diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8688ce11fa..bf7f9ce51a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -140,7 +140,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Build packages - run: pnpm exec turbo build --filter='@posthog/code^...' + run: pnpm run build:deps - name: Package Electron app run: pnpm --filter code run package diff --git a/.github/workflows/warm-caches.yml b/.github/workflows/warm-caches.yml index 10f1556285..a81fe894ed 100644 --- a/.github/workflows/warm-caches.yml +++ b/.github/workflows/warm-caches.yml @@ -47,8 +47,8 @@ jobs: - name: Seed turbo cache (build + typecheck) run: | - pnpm exec turbo build --filter='@posthog/code^...' - pnpm exec turbo typecheck + pnpm run build:deps + pnpm run typecheck macos: runs-on: macos-latest From ad2f37705cef43b6a0a73af37b8068d9a7cd1ec7 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Wed, 22 Jul 2026 10:40:59 -0700 Subject: [PATCH 08/11] build full agent dep graph in agent test job --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bf7f9ce51a..39541a37f1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -245,7 +245,7 @@ jobs: SKIP_ELECTRON_REBUILD: "1" - name: Build agent dependencies - run: pnpm exec turbo build --filter=@posthog/shared --filter=@posthog/git --filter=@posthog/enricher + run: pnpm exec turbo build --filter='@posthog/agent^...' - name: Download native codex binary # Non-fatal at the STEP so a failure surfaces as the fail-loud binary guard From aa923546b9b5799019d894c9e18e22d4dede5956 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Wed, 22 Jul 2026 10:41:24 -0700 Subject: [PATCH 09/11] seed storybook playwright cache on main --- .github/workflows/warm-caches.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/warm-caches.yml b/.github/workflows/warm-caches.yml index a81fe894ed..7e5e915216 100644 --- a/.github/workflows/warm-caches.yml +++ b/.github/workflows/warm-caches.yml @@ -40,6 +40,14 @@ jobs: path: .turbo/cache key: turbo-${{ github.sha }} + # Same key as code-storybook.yml's storybook job. + - name: Cache Playwright browsers + id: playwright-cache + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ~/.cache/ms-playwright + key: playwright-chromium-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} + - name: Install dependencies run: pnpm install --frozen-lockfile env: @@ -50,6 +58,11 @@ jobs: pnpm run build:deps pnpm run typecheck + - name: Install Playwright Chromium + if: steps.playwright-cache.outputs.cache-hit != 'true' + working-directory: apps/code + run: pnpm exec playwright install chromium + macos: runs-on: macos-latest timeout-minutes: 30 From 7d82eb4253b44acc1b1e720c0fa67852c06dfb0f Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Wed, 22 Jul 2026 10:41:48 -0700 Subject: [PATCH 10/11] note cold-build cost of exact turbo cache key --- .github/workflows/warm-caches.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/warm-caches.yml b/.github/workflows/warm-caches.yml index 7e5e915216..a88e2e784c 100644 --- a/.github/workflows/warm-caches.yml +++ b/.github/workflows/warm-caches.yml @@ -33,7 +33,8 @@ jobs: node-version: 22 cache: "pnpm" - # No restore-keys: a fresh save per commit keeps the cache from growing forever. + # No restore-keys: a fresh save per commit keeps the cache from growing + # forever, at the cost of a cold build on every main push. - name: Cache turbo artifacts uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: From e6d8f5b137c84b8bbd835a062c567dd0c74435cf Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Wed, 22 Jul 2026 10:41:48 -0700 Subject: [PATCH 11/11] accept true for SKIP_ELECTRON_REBUILD --- apps/code/scripts/postinstall.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/code/scripts/postinstall.sh b/apps/code/scripts/postinstall.sh index fd64a266c1..7c33b064f8 100755 --- a/apps/code/scripts/postinstall.sh +++ b/apps/code/scripts/postinstall.sh @@ -9,8 +9,8 @@ REPO_ROOT="$(cd ../.. && pwd)" SCRIPTS_DIR="$(cd "$(dirname "$0")" && pwd)" # CI jobs that never run Electron set this to skip ~60s of node-gyp per install. -if [ "$SKIP_ELECTRON_REBUILD" = "1" ]; then - echo "SKIP_ELECTRON_REBUILD=1: skipping Electron binary check and native rebuild." +if [ "$SKIP_ELECTRON_REBUILD" = "1" ] || [ "$SKIP_ELECTRON_REBUILD" = "true" ]; then + echo "SKIP_ELECTRON_REBUILD=$SKIP_ELECTRON_REBUILD: skipping Electron binary check and native rebuild." else # Self-heal missing Electron binary. # pnpm skips package-level postinstall scripts when the lockfile is already