Use the measured pnpm lookup policy automatically in hosted root CI (#24951)

* Select lookup automatically for the measured hosted root-install profile

* Record hosted automatic-mode cold cache publication proof
This commit is contained in:
Neil
2026-10-02 22:32:04 -07:00
committed by GitHub
parent 843607b1bc
commit 8f26bfad22
6 changed files with 241 additions and 20 deletions
@@ -7,9 +7,9 @@ inputs:
required: false
default: 'true'
cache-pnpm-store-lookup-only:
description: On non-PR producers, refresh existing stores without downloading and save stores on a miss.
description: Auto uses measured hosted Node 24 root installs; true forces lookup, false retains archive restoration.
required: false
default: 'false'
default: auto
cache-pnpm-verification:
description: Restore pnpm's policy-checked lockfile verification record.
required: false
@@ -67,6 +67,30 @@ outputs:
runs:
using: composite
steps:
- name: Resolve pnpm store mode
id: pnpm-store-mode
if: >-
github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' &&
(inputs.cache-pnpm-store-lookup-only == 'true' ||
(inputs.cache-pnpm-store-lookup-only == 'auto' &&
inputs.cache-dependency-path == 'pnpm-lock.yaml' &&
runner.environment == 'github-hosted' && job.container.id == '' &&
(runner.os == 'Linux' || runner.os == 'macOS' || runner.os == 'Windows') &&
(runner.arch == 'X64' || runner.arch == 'ARM64') &&
(inputs.node-version == '' || inputs.node-version == '24')))
shell: bash
env:
LOOKUP_REQUEST: ${{ inputs.cache-pnpm-store-lookup-only }}
run: |
lookup_only=true
case "$LOOKUP_REQUEST" in
[aA][uU][tT][oO])
# Hosted runners have Node for this manifest-only check before toolchain setup.
lookup_only="$(node -p 'const p = require("./package.json"); p.engines?.node === "24" && typeof p.packageManager === "string" && p.packageManager.split("+")[0] === "pnpm@12.8.1"')"
;;
esac
printf 'lookup-only=%s\n' "$lookup_only" >> "$GITHUB_OUTPUT"
# setup-node needs pnpm on PATH to locate and restore its store.
- name: Setup pnpm
uses: pnpm/setup@v2
@@ -80,7 +104,7 @@ runs:
uses: actions/setup-node@v6
with:
node-version-file: package.json
cache: ${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && inputs.cache-pnpm-store-lookup-only != 'true' && 'pnpm' || '' }}
cache: ${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && steps.pnpm-store-mode.outputs.lookup-only != 'true' && 'pnpm' || '' }}
cache-dependency-path: ${{ inputs.cache-dependency-path }}
package-manager-cache: false
@@ -90,7 +114,7 @@ runs:
uses: actions/setup-node@v6
with:
node-version: ${{ inputs.node-version }}
cache: ${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && inputs.cache-pnpm-store-lookup-only != 'true' && 'pnpm' || '' }}
cache: ${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && steps.pnpm-store-mode.outputs.lookup-only != 'true' && 'pnpm' || '' }}
cache-dependency-path: ${{ inputs.cache-dependency-path }}
package-manager-cache: false
@@ -104,11 +128,11 @@ runs:
!(runner.arch == 'X64' && contains(inputs.cache-dependency-path, 'mobile/pnpm-lock.yaml')) &&
!((runner.arch == 'X64' || runner.arch == 'ARM64') && inputs.cache-dependency-path == 'pnpm-lock.yaml')) ||
(github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' &&
inputs.cache-pnpm-store-lookup-only == 'true')
steps.pnpm-store-mode.outputs.lookup-only == 'true')
shell: bash
env:
LOCKFILE_HASH: ${{ hashFiles(inputs.cache-dependency-path) }}
STORE_LOOKUP_ONLY: ${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store-lookup-only == 'true' }}
STORE_LOOKUP_ONLY: ${{ steps.pnpm-store-mode.outputs.lookup-only == 'true' }}
run: |
test -n "$LOCKFILE_HASH"
cache_path="$(pnpm store path --silent)"
@@ -137,12 +161,10 @@ runs:
# Producers can refresh access and publish misses without downloading existing archives.
- name: Keep pnpm download store without restoring
id: pnpm-store-lookup
if: >-
github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' &&
inputs.cache-pnpm-store-lookup-only == 'true'
if: steps.pnpm-store-mode.outputs.lookup-only == 'true'
uses: actions/cache@v5
with:
# Post-job saves cannot resolve the composite's internal step outputs.
# Twice-nested composite cleanup loses internal step outputs.
path: ${{ env.ORCA_PNPM_STORE_CACHE_PATH }}
key: node-cache-${{ runner.os }}-${{ steps.pnpm-store.outputs.arch }}-pnpm-${{ hashFiles(inputs.cache-dependency-path) }}
lookup-only: true
@@ -12,7 +12,7 @@ describe('CI dependency download caches', () => {
expect(action.inputs['cache-dependency-path'].default).toBe('pnpm-lock.yaml')
for (const step of action.runs.steps.filter((step) => step.uses === 'actions/setup-node@v6')) {
expect(step.with.cache).toBe(
"${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && inputs.cache-pnpm-store-lookup-only != 'true' && 'pnpm' || '' }}"
"${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && steps.pnpm-store-mode.outputs.lookup-only != 'true' && 'pnpm' || '' }}"
)
expect(step.with['cache-dependency-path']).toBe('${{ inputs.cache-dependency-path }}')
expect(step.with['package-manager-cache']).toBe(false)
@@ -41,7 +41,7 @@ describe('CI dependency download caches', () => {
"github.event_name == 'pull_request' && inputs.cache-pnpm-store != 'false' && !((runner.os == 'Linux' || runner.os == 'macOS') && (runner.arch == 'X64' || runner.arch == 'ARM64') && inputs.cache-dependency-path == 'pnpm-lock.yaml') && (runner.os != 'Windows' || !(runner.arch == 'X64' && contains(inputs.cache-dependency-path, 'mobile/pnpm-lock.yaml')) && !((runner.arch == 'X64' || runner.arch == 'ARM64') && inputs.cache-dependency-path == 'pnpm-lock.yaml'))"
)
expect(resolve.if).toBe(
`${restore.if} || (github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && inputs.cache-pnpm-store-lookup-only == 'true')`
`${restore.if} || (github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && steps.pnpm-store-mode.outputs.lookup-only == 'true')`
)
expect(restore.uses).toBe('actions/cache/restore@v5')
expect(restore.with.path).toBe('${{ steps.pnpm-store.outputs.path }}')
@@ -72,11 +72,9 @@ describe('CI dependency download caches', () => {
it('keeps producer lookup optional and compatible with the existing store archive', () => {
const lookup = action.runs.steps.find((step) => step.id === 'pnpm-store-lookup')
const restore = action.runs.steps.find((step) => step.id === 'pnpm-store-restore')
expect(action.inputs['cache-pnpm-store-lookup-only'].default).toBe('false')
expect(action.inputs['cache-pnpm-store-lookup-only'].default).toBe('auto')
expect(lookup.uses).toBe('actions/cache@v5')
expect(lookup.if).toBe(
"github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && inputs.cache-pnpm-store-lookup-only == 'true'"
)
expect(lookup.if).toBe("steps.pnpm-store-mode.outputs.lookup-only == 'true'")
expect(lookup.with).toEqual({
path: '${{ env.ORCA_PNPM_STORE_CACHE_PATH }}',
key: restore.with.key,
@@ -203,6 +201,16 @@ describe('CI dependency download caches', () => {
const context = {
github: { event_name: event },
runner: { os, arch },
steps: {
'pnpm-store-mode': {
outputs: {
'lookup-only':
event !== 'pull_request' && storeCache !== 'false' && lookupOnly === 'true'
? 'true'
: ''
}
}
},
inputs: {
'cache-pnpm-store': storeCache,
'cache-pnpm-store-lookup-only': lookupOnly,
@@ -218,6 +226,10 @@ describe('CI dependency download caches', () => {
const evaluate = (expression) =>
runInNewContext(
expression
.replaceAll(
'steps.pnpm-store-mode.outputs.lookup-only',
'steps["pnpm-store-mode"].outputs["lookup-only"]'
)
.replaceAll(
'inputs.cache-pnpm-store-lookup-only',
'inputs["cache-pnpm-store-lookup-only"]'
+150
View File
@@ -0,0 +1,150 @@
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { runInNewContext } from 'node:vm'
import { parse } from 'yaml'
import { describe, expect, it } from 'vitest'
import { runProcessSync } from './script-child-process.mjs'
const action = parse(readFileSync('.github/actions/install-node-dependencies/action.yml', 'utf8'))
const mode = action.runs.steps.find((step) => step.id === 'pnpm-store-mode')
const defaultContext = {
github: { event_name: 'push' },
runner: { os: 'Linux', arch: 'X64', environment: 'github-hosted' },
job: { container: { id: '' } },
inputs: {
'cache-pnpm-store': 'true',
'cache-pnpm-store-lookup-only': 'auto',
'cache-dependency-path': 'pnpm-lock.yaml',
'node-version': ''
}
}
const expression = mode.if.replaceAll(/inputs\.([\w-]+)/g, 'inputs["$1"]')
function eligible(changes) {
const context = structuredClone(defaultContext)
for (const [name, fields] of Object.entries(changes)) {
Object.assign(context[name], fields)
}
return runInNewContext(expression, context)
}
function resolveMode(request, node, manager) {
const directory = mkdtempSync(join(tmpdir(), 'orca-store-mode-'))
const output = join(directory, 'output')
try {
writeFileSync(
join(directory, 'package.json'),
JSON.stringify({ engines: { node }, packageManager: manager })
)
const result = runProcessSync({
program: 'bash',
args: ['-e', '-o', 'pipefail', '-c', mode.run],
cwd: directory,
env: { ...process.env, LOOKUP_REQUEST: request, GITHUB_OUTPUT: output }
})
expect(result.code, result.stderr || result.stdout).toBe(0)
return readFileSync(output, 'utf8')
} finally {
rmSync(directory, { recursive: true, force: true })
}
}
describe('automatic pnpm store mode', () => {
it.each([
['auto', '24', 'pnpm@12.8.1', '', true],
['auto', '25', 'pnpm@12.8.1', 'pnpm', false],
['auto', '24', 'pnpm@13.0.0', 'pnpm', false],
['true', '25', 'pnpm@13.0.0', '', true]
])(
'routes resolved %s mode for Node %s / %s into both cache steps',
(request, node, manager, cache, lookup) => {
const context = structuredClone(defaultContext)
context.inputs['cache-pnpm-store-lookup-only'] = request
const resolved = resolveMode(request, node, manager).split('=')[1].trim()
const evaluate = (value) =>
runInNewContext(
value
.replaceAll(/inputs\.([\w-]+)/g, 'inputs["$1"]')
.replaceAll(
'steps.pnpm-store-mode.outputs.lookup-only',
'steps["pnpm-store-mode"].outputs["lookup-only"]'
),
{ ...context, steps: { 'pnpm-store-mode': { outputs: { 'lookup-only': resolved } } } }
)
const nodeSetup = action.runs.steps.find((step) => step.id === 'default-node')
expect(evaluate(nodeSetup.with.cache.slice(3, -2))).toBe(cache)
expect(evaluate(action.runs.steps.find((step) => step.id === 'pnpm-store-lookup').if)).toBe(
lookup
)
}
)
it.each(
['Linux', 'Windows', 'macOS'].flatMap((os) => ['X64', 'ARM64'].map((arch) => [os, arch]))
)('qualifies the measured %s/%s hosted root context', (os, arch) => {
expect(eligible({ runner: { os, arch } })).toBe(true)
})
it.each([
['PR', { github: { event_name: 'pull_request' } }],
['opted-out store', { inputs: { 'cache-pnpm-store': 'false' } }],
['opted-out lookup', { inputs: { 'cache-pnpm-store-lookup-only': 'false' } }],
['unknown request', { inputs: { 'cache-pnpm-store-lookup-only': 'other' } }],
[
'mixed lockfiles',
{ inputs: { 'cache-dependency-path': 'pnpm-lock.yaml\nmobile/pnpm-lock.yaml' } }
],
['custom lockfile', { inputs: { 'cache-dependency-path': 'cloud/pnpm-lock.yaml' } }],
['Node 25', { inputs: { 'node-version': '25' } }],
['job container', { job: { container: { id: 'container-id' } } }],
['self-hosted runner', { runner: { environment: 'self-hosted' } }],
['unknown host kind', { runner: { environment: '' } }],
['unmeasured architecture', { runner: { arch: 'X86' } }],
['unmeasured OS', { runner: { os: 'other' } }]
])('retains the legacy policy for %s', (_name, changes) => {
expect(eligible(changes)).toBe(false)
})
it('allows an explicit request to preserve the existing force-lookup contract', () => {
expect(
eligible({
inputs: {
'cache-pnpm-store-lookup-only': 'true',
'node-version': '25',
'cache-dependency-path': 'custom-lock.yaml'
},
runner: { environment: 'self-hosted' },
job: { container: { id: 'container-id' } }
})
).toBe(true)
expect(
eligible({
github: { event_name: 'pull_request' },
inputs: { 'cache-pnpm-store-lookup-only': 'true' }
})
).toBe(false)
})
it.each([
['24', 'pnpm@12.8.1', 'true'],
['24', 'pnpm@12.8.1+sha512.fixture', 'true'],
['25', 'pnpm@12.8.1', 'false'],
['24.x', 'pnpm@12.8.1', 'false'],
['24', 'pnpm@12.8.2', 'false'],
['24', 'pnpm@12.8.10', 'false'],
['24', undefined, 'false'],
[undefined, 'pnpm@12.8.1', 'false'],
['24', 12, 'false']
])('checks manifest Node %s and manager %s before choosing lookup', (node, manager, expected) => {
expect(resolveMode('auto', node, manager)).toBe(`lookup-only=${expected}\n`)
})
it('checks uppercase auto requests consistently with GitHub expression comparisons', () => {
expect(resolveMode('AUTO', '25', 'pnpm@12.8.1')).toBe('lookup-only=false\n')
})
it('does not constrain an explicit request to the automatic manifest profile', () => {
expect(resolveMode('true', '25', 'pnpm@13.0.0')).toBe('lookup-only=true\n')
})
})
@@ -123,7 +123,7 @@ const REVIEWED_COMPUTED_PATHS = [
'${{ env.ORCA_PNPM_STORE_CACHE_PATH }}',
// Only pnpm's lockfile-verified.jsonl record, never Metro transforms.
'${{ steps.verification-cache.outputs.path }}',
"${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && inputs.cache-pnpm-store-lookup-only != 'true' && 'pnpm' || '' }} store"
"${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && steps.pnpm-store-mode.outputs.lookup-only != 'true' && 'pnpm' || '' }} store"
]
/** Every step a workflow runs, descending into the repository's own composite actions. */
@@ -290,7 +290,7 @@ describe('PR workflow parallelism', () => {
expect(steps[pnpmIndex].with.version).toBeUndefined()
expect(steps[pnpmIndex].with.install).toBe(false)
const saveOutsidePrs =
"${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && inputs.cache-pnpm-store-lookup-only != 'true' && 'pnpm' || '' }}"
"${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && steps.pnpm-store-mode.outputs.lookup-only != 'true' && 'pnpm' || '' }}"
expect(steps[nodeIndex].with.cache).toBe(saveOutsidePrs)
expect(steps[nodeIndex].if).toBe("inputs.node-version == ''")
expect(steps[requestedNodeIndex].if).toBe("inputs.node-version != ''")
+39 -2
View File
@@ -228,8 +228,8 @@ proved that lookup left the payload absent, refreshed the existing cache's acces
time, and published a miss that a fresh job restored. A
[nested composite control](https://github.com/stablyai/orca/actions/runs/37084946789)
then saved and restored a fresh payload using the actual environment-path pattern.
The installer exports its resolved store path through `GITHUB_ENV`: post-job saves
cannot resolve the composite's internal step outputs. The primary key is captured
The installer exports its resolved store path through `GITHUB_ENV`: twice-nested composite post-job saves
cannot resolve their internal step outputs. The primary key is captured
by the cache action before cleanup. Paths, architecture and lockfile keys match
`setup-node`, so existing default-branch archives remain reusable.
@@ -1814,3 +1814,40 @@ collector stopped its observer before signal routing, and the corrected trial
received the signal after both builders finished. The qualifying trial requested
normal cancellation earlier in the same preparation sequence to account for
observed delivery delay; no workload, wait or proof predicate was shortened.
## October 3 producer follow-up: automatic selection for the measured profile
The first producer rollout in [#24927](https://github.com/stablyai/orca/pull/24927)
passed all 46 PR checks, all five manual warmers and all 11 manual Headless
qualifications on `a2c489c0cca5e46d24333a4d40ba910af0de0208`. The same root installer
also serves recurring unit, browser and performance workflows that had not opted
in. The follow-up defaults the existing input to `auto`, reusing lookup mode for
non-PR root-only installs on GitHub-hosted Linux/macOS/Windows x64/ARM64 runners,
with no job container, the manifest's Node 24/pnpm 12.8.1 profile and no conflicting
Node override. Explicit `true` and `false` retain their previous meanings. Mixed
lockfiles, other toolchains, containers and self-hosted runners retain full cache
restoration; PR policies are unchanged. The manifest check runs only when the
context is potentially eligible, before setup-node chooses its cache behavior.
A second cleanup audit distinguished nesting depth. The
[twice-nested control](https://github.com/stablyai/orca/actions/runs/37087090689)
published the environment-path payload and lost the output-path payload with an
`Input required and not supplied: path` warning. The
[direct control](https://github.com/stablyai/orca/actions/runs/37087211236) published
and restored both payloads. Current Electron archive callers are direct, so they
need no cache-path change. Keeping the producer's exported path also makes its
new lookup mode safe for callers that nest the shared installer. These tiny
controls establish publication behavior, not installer time savings.
The [actual automatic-mode cold publisher control](https://github.com/stablyai/orca/actions/runs/37097980789)
passed both jobs on `7b8858bdc8f`. A twice-nested wrapper called the installer
without overriding its default input. The writer selected lookup, missed its
unique root-lockfile key, completed the frozen policy-checked install and saved
that key during cleanup. A fresh reader restored the exact key and installed the
same dependency successfully. The fixture retained the manifest toolchain and
applicable workspace policies; its one dependency keeps the publication check
small. Two earlier trials failed fixture assertions (the pnpm multi-document
header placement, then its empty cache-miss output), and are excluded. This proves
automatic selection and cold publication, not a new timing result. Local
verification passed eight suites / 184 tests, the changed-code quality gate and
compiled-composite actionlint.