ci: skip redundant PR platform warmers (#28917)

This commit is contained in:
Neil
2026-10-14 18:48:13 -07:00
committed by GitHub
parent eeb4311b81
commit 8df0f67c82
5 changed files with 157 additions and 3 deletions
@@ -8,6 +8,7 @@ permissions:
jobs:
warm-linux-arm:
if: github.event_name != 'pull_request'
concurrency:
group: ci-cache-warmup-linux-arm-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name != 'schedule' }}
@@ -85,6 +86,7 @@ jobs:
arches: arm64
warm-linux-package-fixtures:
if: github.event_name != 'pull_request'
concurrency:
group: ci-cache-warmup-linux-package-fixtures-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name != 'schedule' }}
@@ -45,7 +45,6 @@ on:
- 'resources/build/icon.ico'
pull_request:
paths:
- '.github/workflows/ci-cache-warmup.yml'
- '.github/workflows/ci-cache-warmup-platforms.yml'
- '.github/workflows/ci-cache-warmup-platform-jobs.yml'
-2
View File
@@ -50,8 +50,6 @@ on:
pull_request:
paths:
- '.github/workflows/ci-cache-warmup.yml'
- '.github/workflows/ci-cache-warmup-platform-jobs.yml'
- '.github/workflows/ci-cache-warmup-platforms.yml'
- '.github/actions/prepare-headless-compiler/**'
- 'config/scripts/headless-detector-compiler-cache.mjs'
- '.github/actions/prepare-wayland-clipboard/**'
@@ -2,6 +2,7 @@ import { readFileSync } from 'node:fs'
import { runInNewContext } from 'node:vm'
import { describe, expect, it } from 'vitest'
import { parse } from 'yaml'
import { classifyPrJobs } from './pr-code-change-scope.mjs'
const read = (path) => parse(readFileSync(path, 'utf8'))
const workflow = (name) => read(`.github/workflows/${name}.yml`)
@@ -300,6 +301,116 @@ describe('CI dependency download caches', () => {
}
})
it('keeps PR warmer validation within the workflow family that runs it', () => {
const shared = workflow('ci-cache-warmup')
const platforms = workflow('ci-cache-warmup-platforms')
expect(shared.on.pull_request.paths).toEqual([
'.github/workflows/ci-cache-warmup.yml',
'.github/actions/prepare-headless-compiler/**',
'config/scripts/headless-detector-compiler-cache.mjs',
'.github/actions/prepare-wayland-clipboard/**'
])
expect(platforms.on.pull_request.paths).toEqual([
'.github/workflows/ci-cache-warmup-platforms.yml',
'.github/workflows/ci-cache-warmup-platform-jobs.yml'
])
expect(shared.jobs['warm-platforms'].uses).toBe(platforms.jobs['warm-platforms'].uses)
expect(shared.on.schedule).toEqual([{ cron: '41 */6 * * *' }])
expect(shared.on).toHaveProperty('workflow_dispatch')
for (const producer of [shared, platforms]) {
expect(producer.on.push.branches).toEqual(['main'])
expect(producer.on.push.paths).toEqual(
expect.arrayContaining([
'.github/workflows/ci-cache-warmup.yml',
'.github/workflows/ci-cache-warmup-platforms.yml',
'.github/workflows/ci-cache-warmup-platform-jobs.yml',
'.github/actions/install-node-dependencies/**',
'.github/actions/prepare-native-runtime/**',
'pnpm-lock.yaml'
])
)
}
})
it.each([
['pull_request', false, false],
['push', true, false],
['schedule', true, true],
['workflow_dispatch', true, true]
])('retains cache producers and Windows validation on %s', (event, armRuns, sharedPlatforms) => {
const shared = workflow('ci-cache-warmup')
const platforms = read(workflow('ci-cache-warmup-platforms').jobs['warm-platforms'].uses)
const context = { github: { event_name: event } }
expect(runInNewContext(platforms.jobs['warm-linux-arm'].if, context)).toBe(armRuns)
expect(runInNewContext(shared.jobs['warm-platforms'].if, context)).toBe(sharedPlatforms)
expect(shared.jobs.warm.if).toBeUndefined()
expect(platforms.jobs['warm-windows'].if).toBeUndefined()
expect(platforms.jobs['warm-windows'].strategy.matrix.os).toEqual([
'windows-2022',
'windows-11-arm'
])
})
it('validates the omitted ARM warmer preparation in the real PR consumers', () => {
const platforms = read(workflow('ci-cache-warmup-platforms').jobs['warm-platforms'].uses)
const warm = platforms.jobs['warm-linux-arm']
const pr = workflow('pr')
const units = workflow('unit-tests').jobs.test
const installer = './.github/actions/install-node-dependencies'
const warmInstall = warm.steps.find((step) => step.uses === installer)
const unitInstall = units.steps.find((step) => step.uses === installer)
expect(warm['runs-on']).toBe('ubuntu-24.04-arm')
expect(pr.jobs.preflight['runs-on']).toBe(warm['runs-on'])
expect(pr.jobs.test.with.runner).toBe(warm['runs-on'])
expect(units['runs-on']).toBe('${{ inputs.runner }}')
expect(JSON.parse(pr.jobs.test.with.node_versions)).toEqual(['24'])
expect(warmInstall.if).toBeUndefined()
expect(unitInstall.if).toBeUndefined()
expect(warmInstall.with['native-runtime']).toBe('node')
expect(unitInstall.with['native-runtime']).toBe(warmInstall.with['native-runtime'])
expect(warmInstall.with['node-version']).toBe('24')
expect(unitInstall.with['node-version']).toBe('${{ matrix.node }}')
expect(unitInstall.with['cache-electron-package']).toBe(
warmInstall.with['cache-electron-package']
)
const preflightInstalls = pr.jobs.preflight.steps.filter((step) => step.uses === installer)
expect(preflightInstalls.map((step) => step.if)).toEqual([
"needs.code_paths.outputs.mobile_dependencies != 'true'",
"needs.code_paths.outputs.mobile_dependencies == 'true'"
])
for (const step of preflightInstalls) {
expect(step.with['native-runtime']).toBe('node')
expect(step.with['node-version']).toBe('24')
}
const populate = 'node config/scripts/install-electron-package-binary.mjs'
const unitPopulate = units.steps.find((step) => step.run === populate)
expect(unitPopulate.if).toBeUndefined()
expect(warm.steps.map((step) => step.uses ?? step.run)).toEqual([
'actions/checkout@v6',
installer,
populate,
'node config/scripts/ensure-native-runtime.mjs --check-only'
])
const native = read('.github/actions/prepare-native-runtime/action.yml')
const identity = native.runs.steps.find((step) => step.id === 'native-cache-scope')
const inputs = [...identity.env.NATIVE_SOURCE_HASH.matchAll(/'([^']+)'/g)].map(
(match) => match[1]
)
for (const path of [
...inputs,
'.github/actions/install-node-dependencies/action.yml',
...workflow('ci-cache-warmup-platforms').on.pull_request.paths,
'.github/workflows/ci-cache-warmup.yml'
]) {
expect(classifyPrJobs([path], { allowMobileCiTools: true }), path).toMatchObject({
static_analysis: true,
typecheck: true,
test: true,
mobile_ci_tools: false
})
}
})
it('restores Windows packaging downloads from the release cache without a PR upload', () => {
const packaging = workflow('pr').jobs.package_windows
const restore = packaging.steps.find((step) => step.name === 'Cache electron-builder downloads')
@@ -1,9 +1,11 @@
import { chmodSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { runInNewContext } from 'node:vm'
import { delimiter, join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { parse } from 'yaml'
import { runProcessSync } from '@orca/process-host'
import { classifyPrJobs } from './pr-code-change-scope.mjs'
const action = parse(
readFileSync('.github/actions/prepare-linux-package-fixture/action.yml', 'utf8')
@@ -132,3 +134,45 @@ it('uses exact context/platform keys and the same restore-only action in package
.every((step) => step.with['save-cache'] === "${{ github.event_name != 'pull_request' }}")
).toBe(true)
})
it.each([
['pull_request', false],
['push', true],
['schedule', true],
['workflow_dispatch', true]
])('keeps fixture seeding available without a redundant PR runner on %s', (event, runs) => {
const source = workflow('ci-cache-warmup-platforms').jobs['warm-platforms'].uses
const warm = parse(readFileSync(source, 'utf8')).jobs['warm-linux-package-fixtures']
const context = { github: { event_name: event } }
expect(warm['runs-on']).toBe('ubuntu-latest')
expect(warm.steps.map((step) => step.uses)).toEqual([
'actions/checkout@v6',
'./.github/actions/prepare-linux-package-fixture',
'./.github/actions/prepare-linux-package-fixture',
'./.github/actions/prepare-linux-package-fixture'
])
expect(runInNewContext(warm.if, context)).toBe(runs)
for (const step of warm.steps.slice(1)) {
expect(runInNewContext(step.with['save-cache'].slice(3, -2), context)).toBe(runs)
}
const consumers = workflow('pr').jobs.package.steps.filter((step) =>
step.uses?.endsWith('/prepare-linux-package-fixture')
)
expect(consumers.every((step) => step.with['save-cache'] === undefined)).toBe(true)
expect(consumers.map((step) => step.with.fixture)).toEqual(
warm.steps.slice(1).map((step) => step.with.fixture)
)
})
it.each([
'.github/actions/prepare-linux-package-fixture/action.yml',
'config/docker/daemon-shutdown-descendants/Dockerfile',
'config/docker/headless-serve-shutdown/Dockerfile',
'config/docker/cli-launch-contract/Dockerfile'
])('validates a changed fixture in Linux packaging: %s', (file) => {
expect(classifyPrJobs([file])).toMatchObject({
test: true,
package: true,
package_windows: false
})
})