diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 49c83a26b7f..5ca53ff4334 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -234,8 +234,8 @@ jobs: PREFLIGHT_PRIOR_SUCCESS: ${{ job.status == 'success' }} ORCA_UNIT_SELECTION_MODE: ${{ vars.ORCA_UNIT_SELECTION_MODE || 'shadow' }} ORCA_PULL_REQUEST_DRAFT: ${{ github.event.pull_request.draft }} - # Five shards use fewer runner minutes; the repository variable can request 2 to 10. - ORCA_UNIT_FULL_SHARD_COUNT: ${{ vars.ORCA_UNIT_FULL_SHARD_COUNT || '5' }} + # Twelve 4-vCPU hosted shards match the old 8-vCPU wall time; the repository variable can request 2 to 16. + ORCA_UNIT_FULL_SHARD_COUNT: ${{ vars.ORCA_UNIT_FULL_SHARD_COUNT || '12' }} run: | if [ "$PREFLIGHT_PHASE_SELECTED" != true ] || [ "$PREFLIGHT_PRIOR_SUCCESS" != true ]; then exit 0; fi node config/scripts/ci-unit-plan.mjs @@ -896,7 +896,8 @@ jobs: uses: ./.github/workflows/unit-tests.yml with: node_versions: '["24"]' - runner: blacksmith-8vcpu-ubuntu-2404 + # Free hosted ARM: 12 shards measured 361s median cold vs ~390s for 5 warm Blacksmith 8-vCPU shards. + runner: ubuntu-24.04-arm phase_diagnostics: true # Restore main's transform cache; the `ci:cold-unit-cache` label or ORCA_UNIT_MODULE_CACHE=off forces cold. module_cache: ${{ (vars.ORCA_UNIT_MODULE_CACHE == 'off' || contains(github.event.pull_request.labels.*.name, 'ci:cold-unit-cache')) && 'off' || 'warm' }} diff --git a/.github/workflows/unit-module-cache.yml b/.github/workflows/unit-module-cache.yml index ff2683b8289..82cbd107017 100644 --- a/.github/workflows/unit-module-cache.yml +++ b/.github/workflows/unit-module-cache.yml @@ -1,29 +1,9 @@ name: Unit transform cache producer -# Cold full unit run on main that publishes per-shard Vitest transform caches for PRs. +# Cold full unit run on main that publishes per-shard Vitest transform caches. +# Dispatch only: PR shards run on hosted runners, which cannot read this Blacksmith-store cache. on: - schedule: - - cron: '17 */6 * * *' workflow_dispatch: - push: - branches: [main] - # Cache-key inputs (see ci-unit-module-cache.mjs) change the key; reseed right away. - paths: - - '.github/workflows/unit-module-cache.yml' - - '.github/workflows/unit-tests.yml' - - 'config/vitest.config.ts' - - 'config/scripts/vitest*' - - 'config/scripts/ci-unit-module-cache*.mjs' - - 'config/patches/**' - - 'config/scripts/workspace-source-exports.mjs' - - 'config/dark-build-switches.cjs' - - 'config/pnpmfile.cjs' - - '.npmrc' - - '**/package.json' - - '**/tsconfig*.json' - - 'pnpm-lock.yaml' - - 'pnpm-workspace.yaml' - - 'config/.bun-version' concurrency: group: unit-module-cache-${{ github.ref }} diff --git a/.github/workflows/unit-plan.yml b/.github/workflows/unit-plan.yml index 30c7cf10715..d3dc7700324 100644 --- a/.github/workflows/unit-plan.yml +++ b/.github/workflows/unit-plan.yml @@ -36,7 +36,7 @@ jobs: ORCA_UNIT_SELECTION_MODE: ${{ inputs.selection_mode }} ORCA_PULL_REQUEST_DRAFT: ${{ github.event.pull_request.draft }} # Same count as PR preflight so transform cache shard keys line up. - ORCA_UNIT_FULL_SHARD_COUNT: ${{ vars.ORCA_UNIT_FULL_SHARD_COUNT || '5' }} + ORCA_UNIT_FULL_SHARD_COUNT: ${{ vars.ORCA_UNIT_FULL_SHARD_COUNT || '12' }} run: node config/scripts/ci-unit-plan.mjs - uses: actions/upload-artifact@v7 continue-on-error: true diff --git a/config/scripts/ci-dependency-download-cache.test.mjs b/config/scripts/ci-dependency-download-cache.test.mjs index 495c8ae6680..c76b14ffb55 100644 --- a/config/scripts/ci-dependency-download-cache.test.mjs +++ b/config/scripts/ci-dependency-download-cache.test.mjs @@ -417,8 +417,8 @@ describe('CI dependency download caches', () => { expect(warm['runs-on']).toBe('${{ matrix.runner }}') expect(warm.strategy.matrix.runner).toEqual([ 'blacksmith-4vcpu-ubuntu-2404-arm', + pr.jobs.preflight['runs-on'], pr.jobs.test.with.runner, - 'ubuntu-24.04-arm', 'ubuntu-22.04' ]) expect(workflow('ci-cache-warmup').jobs.warm.strategy.matrix.runner).toEqual([ @@ -426,7 +426,7 @@ describe('CI dependency download caches', () => { 'ubuntu-24.04' ]) expect(pr.jobs.preflight['runs-on']).toBe('blacksmith-8vcpu-ubuntu-2404') - expect(pr.jobs.test.with.runner).toBe('blacksmith-8vcpu-ubuntu-2404') + expect(pr.jobs.test.with.runner).toBe('ubuntu-24.04-arm') expect(units['runs-on']).toBe('${{ inputs.runner }}') expect(JSON.parse(pr.jobs.test.with.node_versions)).toEqual(['24']) expect(warmInstall.if).toBeUndefined() diff --git a/config/scripts/ci-unit-plan.mjs b/config/scripts/ci-unit-plan.mjs index 3142094e3e8..e9f7e3475d3 100644 --- a/config/scripts/ci-unit-plan.mjs +++ b/config/scripts/ci-unit-plan.mjs @@ -8,9 +8,9 @@ import { readTimingBaseline, writeAssignment } from './ci-shard-assignment.mjs' export function prepareUnitPlan(env = process.env) { const requestedCount = env.ORCA_UNIT_FULL_SHARD_COUNT ?? String(FULL_SHARD_COUNT) - // Every consumer reads the count from the plan; ten bounds runner-slot use. - if (!/^([2-9]|10)$/.test(requestedCount)) { - throw new Error('ORCA_UNIT_FULL_SHARD_COUNT must be an integer from 2 to 10') + // Every consumer reads the count from the plan; sixteen bounds runner-slot use. + if (!/^([2-9]|1[0-6])$/.test(requestedCount)) { + throw new Error('ORCA_UNIT_FULL_SHARD_COUNT must be an integer from 2 to 16') } const fullShardCount = Number(requestedCount) const files = discoverUnitFiles() diff --git a/config/scripts/ci-unit-plan.test.mjs b/config/scripts/ci-unit-plan.test.mjs index 8a766b5ad99..fc9eeb5e547 100644 --- a/config/scripts/ci-unit-plan.test.mjs +++ b/config/scripts/ci-unit-plan.test.mjs @@ -86,7 +86,7 @@ it.each([ } ) -it.each(['5', '6', '10', 'invalid', '11'])( +it.each(['5', '6', '16', 'invalid', '17'])( 'validates the requested full shard count and keeps fallback coverage: %s', (requestedCount) => { const root = mkdtempSync(join(tmpdir(), 'orca-unit-count-')) @@ -105,10 +105,10 @@ it.each(['5', '6', '10', 'invalid', '11'])( ORCA_SHARD_SOURCE_SHA: 'full-count-fixture' } }) - if (['invalid', '11'].includes(requestedCount)) { + if (['invalid', '17'].includes(requestedCount)) { expect(result.code).not.toBe(0) expect(result.stderr).toContain( - 'ORCA_UNIT_FULL_SHARD_COUNT must be an integer from 2 to 10' + 'ORCA_UNIT_FULL_SHARD_COUNT must be an integer from 2 to 16' ) return } diff --git a/config/scripts/ci-unit-selection.mjs b/config/scripts/ci-unit-selection.mjs index 9ea258eb4f1..4f804f6503d 100644 --- a/config/scripts/ci-unit-selection.mjs +++ b/config/scripts/ci-unit-selection.mjs @@ -54,7 +54,7 @@ export function selectUnitFiles(files, changed, graphInput) { // Job slots, not cores, drive queueing: eight shards consumed 68% of daily slots. Five budget ~8.8min/shard versus 10.5min p95 queue. // Full PR/daily callers use five; selected drafts may use fewer. -export const FULL_SHARD_COUNT = 5 +export const FULL_SHARD_COUNT = 12 export function planUnitSelection({ files, diff --git a/config/scripts/ci-unit-selection.test.mjs b/config/scripts/ci-unit-selection.test.mjs index 601e3c269b2..59be57dddd5 100644 --- a/config/scripts/ci-unit-selection.test.mjs +++ b/config/scripts/ci-unit-selection.test.mjs @@ -167,7 +167,7 @@ describe('conservative unit selection', () => { ) }) - it('uses ten complete PR shards while selected drafts retain the five-shard cap', () => { + it('uses ten complete PR shards while selected drafts stay within the full-count cap', () => { const expandedSources = new Map(sources) for (let index = 0; index < 5; index++) { expandedSources.set(`src/additional-${index}.test.ts`, `import './leaf'`) @@ -196,7 +196,7 @@ describe('conservative unit selection', () => { mode: 'selected', event: { pull_request: { draft: true } } }) - expect(selected.shards).toHaveLength(5) + expect(selected.shards).toHaveLength(Math.min(FULL_SHARD_COUNT, selected.executionFiles.length)) expect(selected.executionFiles).toEqual( selectUnitFiles(expandedFiles, ['src/leaf.ts'], expandedGraph).files ) diff --git a/config/scripts/pr-preflight-gates.test.mjs b/config/scripts/pr-preflight-gates.test.mjs index 8634b541910..4c7ac395779 100644 --- a/config/scripts/pr-preflight-gates.test.mjs +++ b/config/scripts/pr-preflight-gates.test.mjs @@ -38,7 +38,7 @@ it('shares one setup and overlaps the compiler only after type-aware checks fini 'node config/scripts/ci-unit-plan.mjs' ]) expect(plan.env.ORCA_UNIT_SELECTION_MODE).toContain('vars.ORCA_UNIT_SELECTION_MODE') - expect(plan.env.ORCA_UNIT_FULL_SHARD_COUNT).toBe("${{ vars.ORCA_UNIT_FULL_SHARD_COUNT || '5' }}") + expect(plan.env.ORCA_UNIT_FULL_SHARD_COUNT).toBe("${{ vars.ORCA_UNIT_FULL_SHARD_COUNT || '12' }}") expect(steps.indexOf(plan)).toBeLessThan(compilerJoin) expect(steps.indexOf(compiler)).toBeGreaterThan( steps.findIndex((step) => step.wait === 'changed-code-quality') diff --git a/config/scripts/pr-unit-support-routing.test.mjs b/config/scripts/pr-unit-support-routing.test.mjs index 5a03d8192b9..6b50ca830ce 100644 --- a/config/scripts/pr-unit-support-routing.test.mjs +++ b/config/scripts/pr-unit-support-routing.test.mjs @@ -7,7 +7,7 @@ import { unitConsumers } from './ci-unit-dependency-graph.mjs' import { CROSS_VERSION_WIRE_DIR, discoverUnitFiles } from './ci-unit-files.mjs' -import { planUnitSelection } from './ci-unit-selection.mjs' +import { FULL_SHARD_COUNT, planUnitSelection } from './ci-unit-selection.mjs' const harness = 'src/renderer/src/runtime/web-session-tabs-sync-test-harness.ts' const structuredSessionHarness = @@ -123,7 +123,7 @@ describe('unit support import ownership', () => { }) expect(plan.mode).toBe('shadow') expect(plan.executionFiles).toEqual(files) - expect(plan.shards).toHaveLength(5) + expect(plan.shards).toHaveLength(FULL_SHARD_COUNT) }) it.each([ diff --git a/docs/readme/ci-runners.md b/docs/readme/ci-runners.md index c6dd386389b..8ed3e38b860 100644 --- a/docs/readme/ci-runners.md +++ b/docs/readme/ci-runners.md @@ -1,10 +1,12 @@ # CI runners Default to free GitHub-hosted standard runners: the repository is public and the -Enterprise plan allows 500 concurrent jobs (50 macOS). Use Blacksmith only for: +Enterprise plan allows 1,000 concurrent jobs (50 macOS). Use Blacksmith only for: -- PR critical-path jobs: unit test shards, preflight and typecheck, and the layout - oracle gate (`e2e.yml` with `fast_runners: true`). Advisory e2e lanes run hosted. +- PR critical-path jobs: preflight and typecheck, and the layout oracle gate + (`e2e.yml` with `fast_runners: true`). Advisory e2e lanes run hosted. + Unit tests run as 12 shards on hosted `ubuntu-24.04-arm`, which match the old + five 8-vCPU Blacksmith shards; `ORCA_UNIT_FULL_SHARD_COUNT` tunes the count. - `pr.yml` Git compatibility: its parallel real-Git lanes, two through Docker, exceed test timeouts on 4-vCPU hosted runners. - macOS packaging: adhoc, hourly, daily and `release-mac-build.yml`. Hosted