diff --git a/.github/workflows/adhoc-mac-build.yml b/.github/workflows/adhoc-mac-build.yml index 7cabc3667bb..9f291345398 100644 --- a/.github/workflows/adhoc-mac-build.yml +++ b/.github/workflows/adhoc-mac-build.yml @@ -250,11 +250,19 @@ jobs: echo "tag=$TAG" >>"$GITHUB_OUTPUT" build-adhoc-mac-arch: - needs: [resolve-ref, orcad-template-support, release-javascript, prepare-adhoc-mac-release] - # Template qualification gates packaging after desktop preparation. + needs: + [ + resolve-ref, + orcad-template-support, + orcad-template, + release-javascript, + prepare-adhoc-mac-release + ] + # Start after template qualification: overlapping ~2.5 min of preparation left both 12-vCPU Macs idle ~7 min. if: >- !cancelled() && github.repository == 'stablyai/orca' && needs.orcad-template-support.result == 'success' && + (needs.orcad-template.result == 'success' || needs.orcad-template-support.outputs.ships != 'true') && needs.release-javascript.result == 'success' && needs.prepare-adhoc-mac-release.result == 'success' # Signing environment must restrict deployments to main after MAC_CERTS/password, APPLE_ID/app password/team and hourly App secrets move there; referencing it alone enforces nothing. @@ -374,30 +382,6 @@ jobs: # Leave build identity unset: telemetry accepts only official stable/rc, so branch artifacts stay silent. ORCA_DIAGNOSTICS_TOKEN_URL: https://www.onorca.dev/diagnostics/token - - name: Wait for the qualified orcad template - if: needs.orcad-template-support.outputs.ships == 'true' - uses: actions/github-script@v8 - with: - script: | - const deadline = Date.now() + 30 * 60_000 - while (Date.now() < deadline) { - const jobs = await github.paginate(github.rest.actions.listJobsForWorkflowRun, { - ...context.repo, run_id: context.runId, filter: 'latest', per_page: 100 - }) - const lanes = jobs.filter(job => job.name.startsWith('orcad-template / ')) - const failed = lanes.find(job => job.status === 'completed' && - ['failure', 'cancelled', 'timed_out', 'action_required', 'startup_failure'].includes(job.conclusion)) - if (failed) throw new Error(`Template qualification failed: ${failed.name}`) - const artifacts = await github.paginate(github.rest.actions.listWorkflowRunArtifacts, { - ...context.repo, run_id: context.runId, per_page: 100 - }) - const ready = artifacts.some(artifact => artifact.name === 'orcad-template' && !artifact.expired) - const completed = lanes.length > 0 && lanes.every(job => job.status === 'completed') - if (ready && (process.env.WAIT_FOR_TEMPLATE_COMPLETION !== '1' || completed)) return - await new Promise(resolve => setTimeout(resolve, 15_000)) - } - throw new Error('Timed out waiting for the qualified orcad template') - # After the app build so nothing that cleans out/ can drop it; electron-builder ships it. - name: Download the orcad deployment template if: needs.orcad-template-support.outputs.ships == 'true' diff --git a/config/scripts/adhoc-release-waits.test.mjs b/config/scripts/adhoc-release-waits.test.mjs index 13c03a7ca2b..bb48928bd85 100644 --- a/config/scripts/adhoc-release-waits.test.mjs +++ b/config/scripts/adhoc-release-waits.test.mjs @@ -5,7 +5,7 @@ import { parse } from 'yaml' const adhoc = parse(readFileSync('.github/workflows/adhoc-mac-build.yml', 'utf8')) const publisher = parse(readFileSync('.github/workflows/dev-channel-win-publish.yml', 'utf8')) const windows = parse(readFileSync('.github/workflows/dev-channel-win-build.yml', 'utf8')) -const macSteps = adhoc.jobs['build-adhoc-mac-arch'].steps +const macBuild = adhoc.jobs['build-adhoc-mac-arch'] const finalSteps = adhoc.jobs['build-adhoc-mac'].steps const publishSteps = publisher.jobs['publish-win'].steps const macWait = publishSteps.find((step) => step.name === 'Wait for successful macOS publication') @@ -53,50 +53,58 @@ const job = (name, conclusion) => ({ conclusion }) -describe.each([ - ['Mac', macSteps], - ['Windows', windows.jobs['build-win'].steps] -])('%s release waits retain qualification and publication gates', (_platform, buildSteps) => { - const templateWait = buildSteps.find( - (step) => step.name === 'Wait for the qualified orcad template' +it('starts paid Mac packaging only after template qualification succeeds', () => { + expect(macBuild.needs).toContain('orcad-template') + expect(macBuild.if).toContain("needs.orcad-template.result == 'success'") + expect(macBuild.steps.some((step) => step.name === 'Wait for the qualified orcad template')).toBe( + false ) - it('allows desktop setup first, but waits for a qualified artifact from this run', async () => { - const { result, calls } = execute(templateWait, [ - { jobs: [job('orcad-template / cache', 'success')] }, - { jobs: [job('orcad-template / qualify / persistence (windows-11-arm)')] }, - { artifacts: [{ name: 'orcad-template', expired: true }] }, - { artifacts: [{ name: 'orcad-template', expired: false }] } - ]) - await result - expect(calls.filter((call) => call.endpoint === 'artifacts')).toHaveLength(4) - expect(calls.every((call) => call.input.run_id === 42)).toBe(true) - expect(buildSteps.indexOf(templateWait)).toBeGreaterThan( - buildSteps.findIndex((step) => step.name === 'Build app') - ) - expect(buildSteps.indexOf(templateWait)).toBeLessThan( - buildSteps.findIndex((step) => step.name === 'Download the orcad deployment template') - ) - }) - - it.each(['failure', 'cancelled', 'timed_out', 'action_required', 'startup_failure'])( - 'refuses to package after template qualification ends with %s', - async (conclusion) => { - await expect( - execute(templateWait, [ - { - jobs: [job('orcad-template / qualify / persistence (windows-11-arm)', conclusion)], - artifacts: [{ name: 'orcad-template', expired: false }] - } - ]).result - ).rejects.toThrow('Template qualification failed') - } - ) - - it('times out when no qualified artifact is produced', async () => { - await expect(execute(templateWait, [{}]).result).rejects.toThrow('Timed out') - }) }) +describe.each([['Windows', windows.jobs['build-win'].steps]])( + '%s release waits retain qualification and publication gates', + (_platform, buildSteps) => { + const templateWait = buildSteps.find( + (step) => step.name === 'Wait for the qualified orcad template' + ) + it('allows desktop setup first, but waits for a qualified artifact from this run', async () => { + const { result, calls } = execute(templateWait, [ + { jobs: [job('orcad-template / cache', 'success')] }, + { jobs: [job('orcad-template / qualify / persistence (windows-11-arm)')] }, + { artifacts: [{ name: 'orcad-template', expired: true }] }, + { artifacts: [{ name: 'orcad-template', expired: false }] } + ]) + await result + expect(calls.filter((call) => call.endpoint === 'artifacts')).toHaveLength(4) + expect(calls.every((call) => call.input.run_id === 42)).toBe(true) + expect(buildSteps.indexOf(templateWait)).toBeGreaterThan( + buildSteps.findIndex((step) => step.name === 'Build app') + ) + expect(buildSteps.indexOf(templateWait)).toBeLessThan( + buildSteps.findIndex((step) => step.name === 'Download the orcad deployment template') + ) + }) + + it.each(['failure', 'cancelled', 'timed_out', 'action_required', 'startup_failure'])( + 'refuses to package after template qualification ends with %s', + async (conclusion) => { + await expect( + execute(templateWait, [ + { + jobs: [job('orcad-template / qualify / persistence (windows-11-arm)', conclusion)], + artifacts: [{ name: 'orcad-template', expired: false }] + } + ]).result + ).rejects.toThrow('Template qualification failed') + } + ) + + it('times out when no qualified artifact is produced', async () => { + await expect(execute(templateWait, [{}]).result).rejects.toThrow('Timed out') + }) + } +) + it('keeps the release a draft until qualification and its cleanup finish', async () => { const final = finalSteps.find( (step) => step.name === 'Confirm final template qualification before publication' diff --git a/config/scripts/adhoc-windows-parallelism.test.mjs b/config/scripts/adhoc-windows-parallelism.test.mjs index a1096645624..8414ceac3c0 100644 --- a/config/scripts/adhoc-windows-parallelism.test.mjs +++ b/config/scripts/adhoc-windows-parallelism.test.mjs @@ -25,9 +25,10 @@ function qualification(template = 'success', ships = 'true') { } describe('adhoc Windows compilation overlaps Mac without publishing early', () => { - it('starts both platforms during qualification without a Mac dependency', () => { - expect(mac.needs).toEqual([...windows.needs, 'prepare-adhoc-mac-release']) - expect(mac.needs).not.toContain('orcad-template') + it('starts Windows during qualification without a Mac dependency', () => { + expect(mac.needs).toEqual( + expect.arrayContaining([...windows.needs, 'orcad-template', 'prepare-adhoc-mac-release']) + ) expect(windows.needs).not.toContain('build-adhoc-mac') expect(windows.with.stage_only).toBe(true) expect(windows.with.fast_release).toBe(true)