* ci: build and check packages in parallel The Vue SDK's declarations used the tsc resolver, which took 21 of the 32 seconds a local package build takes; tsdown's default oxc resolver writes byte-identical output in 4 seconds. Packages now build level by level, each level's packages together, with their output printed whole. Package checks run npm and Bun packing side by side and ATTW on every core instead of two. * ci: skip the merge queue's suites for a tree its PR already passed The merge queue reran every check even when master had not moved, so the queued commit had exactly the tree the pull request's CI had just passed. A passing PR run now records that tree as a commit status on the PR head, and the queue's classification compares its own tree with it: a match runs only the always-on checks, anything else the full suites. Fork PRs cannot write the status and keep the full run. * ci: accept a verified tree only from its pull request's passing CI run Any writer can post a commit status, and another pull request's CI could post one on this head, so a status alone could skip the queue's suites. The record now links the run that wrote it, and the queue accepts it only when GitHub shows Actions created it and the run is this repository's CI workflow on pull_request, passed, and ran on this exact head. Recording no longer fails the gate when the status cannot be written. Parallel packs and builds now all settle before a failure is reported, so none writes into a directory that is being removed or rebuilt. * refactor(ci): group the verified-tree lookup and recorder in one folder
41 lines
1.7 KiB
TypeScript
41 lines
1.7 KiB
TypeScript
import { execFileSync } from 'node:child_process'
|
|
import { appendFile } from 'node:fs/promises'
|
|
|
|
import { classifyPaths } from './policy'
|
|
import { isVerifiedTree } from './verified-tree/status'
|
|
|
|
const base = process.env.CI_BASE_SHA
|
|
const output = process.env.GITHUB_OUTPUT
|
|
if (!base || !/^[a-f0-9]{40}$/.test(base) || !output)
|
|
throw new Error('Missing CI base SHA or output file')
|
|
|
|
// Disable rename detection so both the old and new paths participate in routing.
|
|
const paths = execFileSync('git', ['diff', '--no-renames', '--name-only', '-z', base, 'HEAD'], {
|
|
maxBuffer: 32 * 1024 * 1024
|
|
})
|
|
.toString('utf8')
|
|
.split('\0')
|
|
.filter(Boolean)
|
|
const scope = (await queuedTreeVerified()) ? 'verified' : classifyPaths(paths)
|
|
await appendFile(output, `scope=${scope}\n`)
|
|
console.log(`Selected ${scope} checks for ${paths.length} changed paths`)
|
|
|
|
/** In the merge queue, whether the queued commit's tree already passed its pull request's CI. */
|
|
async function queuedTreeVerified(): Promise<boolean> {
|
|
const { CI_EVENT, CI_HEAD_REF, GITHUB_REPOSITORY, GITHUB_TOKEN } = process.env
|
|
if (CI_EVENT !== 'merge_group' || !CI_HEAD_REF || !GITHUB_REPOSITORY || !GITHUB_TOKEN) {
|
|
return false
|
|
}
|
|
const tree = execFileSync('git', ['rev-parse', 'HEAD^{tree}']).toString('utf8').trim()
|
|
try {
|
|
const access = { repository: GITHUB_REPOSITORY, token: GITHUB_TOKEN }
|
|
const verified = await isVerifiedTree(access, CI_HEAD_REF, tree)
|
|
if (verified) console.log(`Tree ${tree} already passed its pull request's CI`)
|
|
return verified
|
|
} catch (error) {
|
|
// A failed lookup only costs the full run.
|
|
console.log(`Could not look up a verified tree: ${String(error)}`)
|
|
return false
|
|
}
|
|
}
|