@@ -43,6 +43,33 @@ describe("GitHub Actions cache workflows", () => {
4343 action . indexOf ( "uses: actions/cache/save@v5" ) ,
4444 ) ;
4545
46+ // Non-persistent branch: pin a stable home store before `pnpm store path`
47+ // so actions/cache version hashes match across hosted and Nexu ARC fleets.
48+ const detectStep = sectionBetween (
49+ action ,
50+ "- name: Detect persistent pnpm store" ,
51+ "- name: Setup pnpm" ,
52+ ) ;
53+ // Isolate the else arm by its home-store pin (not "else"/"fi", which match
54+ // substrings like npm_config).
55+ const homeStoreIndex = detectStep . indexOf ( 'store_dir="$HOME/.pnpm-store"' ) ;
56+ expect ( homeStoreIndex ) . toBeGreaterThanOrEqual ( 0 ) ;
57+ const nonPersistentBranch = detectStep . slice ( homeStoreIndex ) ;
58+ expect ( nonPersistentBranch ) . toContain ( 'echo "NPM_CONFIG_STORE_DIR=$store_dir"' ) ;
59+ expect ( nonPersistentBranch ) . toContain ( 'echo "npm_config_store_dir=$store_dir"' ) ;
60+ expect ( nonPersistentBranch ) . toContain ( 'echo "enabled=false"' ) ;
61+ expect ( action . indexOf ( 'store_dir="$HOME/.pnpm-store"' ) ) . toBeLessThan (
62+ action . indexOf ( 'run: echo "path=$(pnpm store path --silent)"' ) ,
63+ ) ;
64+
65+ const restoreStep = sectionBetween (
66+ action ,
67+ "- name: Restore pnpm store" ,
68+ "- name: Install dependencies" ,
69+ ) ;
70+ expect ( restoreStep ) . toContain ( "restore-keys: |" ) ;
71+ expect ( restoreStep ) . toContain ( "pnpm-store-${{ runner.os }}-" ) ;
72+
4673 const saveStep = action . slice ( action . indexOf ( "- name: Save pnpm store" ) ) ;
4774 expect ( saveStep ) . toContain ( "inputs.save-pnpm-cache == 'true'" ) ;
4875 expect ( saveStep ) . toContain ( "steps.persistent-pnpm-store.outputs.enabled != 'true'" ) ;
@@ -53,9 +80,10 @@ describe("GitHub Actions cache workflows", () => {
5380 expect ( saveStep ) . toContain ( "github.event_name == 'schedule'" ) ;
5481 } ) ;
5582
56- it ( "[P1] seeds Windows from main and deletes only closed-PR BuildKit cache families" , async ( ) => {
83+ it ( "[P1] seeds Windows and Linux from main and deletes only closed-PR BuildKit cache families" , async ( ) => {
5784 const workflow = await readFile ( cacheMaintenanceWorkflow , "utf8" ) ;
58- const seedJob = sectionBetween ( workflow , " seed-pnpm-windows:" , " clean-closed-pr-buildkit:" ) ;
85+ const windowsSeedJob = sectionBetween ( workflow , " seed-pnpm-windows:" , " seed-pnpm-linux:" ) ;
86+ const linuxSeedJob = sectionBetween ( workflow , " seed-pnpm-linux:" , " clean-closed-pr-buildkit:" ) ;
5987 const cleanupJob = workflow . slice ( workflow . indexOf ( " clean-closed-pr-buildkit:" ) ) ;
6088
6189 expect ( workflow ) . toContain ( "pull_request_target:" ) ;
@@ -66,13 +94,19 @@ describe("GitHub Actions cache workflows", () => {
6694 expect ( workflow ) . toContain ( "actions: write" ) ;
6795 expect ( workflow ) . toContain ( "contents: read" ) ;
6896
69- expect ( seedJob ) . toContain ( "github.event_name == 'push'" ) ;
70- expect ( seedJob ) . toContain ( "github.event_name == 'workflow_dispatch'" ) ;
71- expect ( seedJob ) . toContain ( "github.ref == 'refs/heads/main'" ) ;
72- expect ( seedJob ) . toContain ( "runs-on: windows-latest" ) ;
73- expect ( seedJob ) . toContain ( "uses: actions/checkout@v6.0.2" ) ;
74- expect ( seedJob ) . toContain ( "uses: ./.github/actions/setup-workspace" ) ;
75- expect ( seedJob ) . toContain ( "save-pnpm-cache: 'true'" ) ;
97+ for ( const seedJob of [ windowsSeedJob , linuxSeedJob ] ) {
98+ expect ( seedJob ) . toContain ( "github.event_name == 'push'" ) ;
99+ expect ( seedJob ) . toContain ( "github.event_name == 'workflow_dispatch'" ) ;
100+ expect ( seedJob ) . toContain ( "github.ref == 'refs/heads/main'" ) ;
101+ expect ( seedJob ) . toContain ( "uses: actions/checkout@v6.0.2" ) ;
102+ expect ( seedJob ) . toContain ( "uses: ./.github/actions/setup-workspace" ) ;
103+ expect ( seedJob ) . toContain ( "save-pnpm-cache: 'true'" ) ;
104+ }
105+
106+ expect ( windowsSeedJob ) . toContain ( "runs-on: windows-latest" ) ;
107+ // One hosted Linux seed is enough: actions/cache is repo-scoped and shared
108+ // by every Nexu size (small/medium/large) once the store path is pinned.
109+ expect ( linuxSeedJob ) . toContain ( "runs-on: ubuntu-24.04" ) ;
76110
77111 expect ( cleanupJob ) . toContain ( "github.event_name == 'pull_request_target'" ) ;
78112 expect ( cleanupJob ) . toContain ( "refs/pull/${{ github.event.pull_request.number }}/merge" ) ;
@@ -85,7 +119,8 @@ describe("GitHub Actions cache workflows", () => {
85119 expect ( cleanupJob ) . not . toContain ( "github.event.pull_request.head" ) ;
86120 } ) ;
87121
88- it ( "[P1] uses the existing main visual baseline as the Linux pnpm seed" , async ( ) => {
122+
123+ it ( "[P1] keeps visual-baseline as a secondary Linux pnpm seed on main" , async ( ) => {
89124 const workflow = await readFile ( visualBaselineWorkflow , "utf8" ) ;
90125 const setupStep = sectionBetween ( workflow , " - name: Setup workspace" , " - name: Setup Playwright" ) ;
91126
@@ -95,6 +130,7 @@ describe("GitHub Actions cache workflows", () => {
95130 expect ( setupStep ) . toContain ( "save-pnpm-cache: 'true'" ) ;
96131 } ) ;
97132
133+
98134 it ( "[P1] keeps landing preview caches restore-only before merge and seeds them from main" , async ( ) => {
99135 const workflow = await readFile ( landingPageCiWorkflow , "utf8" ) ;
100136
0 commit comments