Windows CI / verify (push) Failing after 4m52s
The workspace cutter drop check waited a fixed 250ms before asserting the populated cutter controls, but the drop triggers asynchronous IPC and media probing, so loaded runners regularly lost that race and failed with a misleading error. The check now polls the exact same UI conditions with a bounded deadline before asserting. All window screenshots in the cutter and workspace smokes carry an explicit generous timeout because large two-times-DPR captures exceeded the Playwright default under load. The focused Electron smoke and the cutter media matrix additionally retry exactly once in both CI workflows, mirroring the established packaging retries for transient runner turbulence, and the CI contract now enforces those retries instead of the former single-line steps.
279 lines
10 KiB
YAML
279 lines
10 KiB
YAML
name: Windows CI
|
|
|
|
on:
|
|
push:
|
|
pull_request:
|
|
workflow_dispatch:
|
|
inputs:
|
|
live_gate:
|
|
description: Optional live gate to run after verification
|
|
required: true
|
|
default: none
|
|
type: choice
|
|
options:
|
|
- none
|
|
- twitch
|
|
- updater-postpublish
|
|
source_version:
|
|
description: Published source version for the updater gate
|
|
required: false
|
|
type: string
|
|
source_sha256:
|
|
description: SHA-256 of the published source installer
|
|
required: false
|
|
type: string
|
|
update_version:
|
|
description: Newly published target version for the updater gate
|
|
required: false
|
|
type: string
|
|
update_sha512:
|
|
description: SHA-512 from the newly published latest.yml
|
|
required: false
|
|
type: string
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
verify:
|
|
runs-on: windows-latest
|
|
timeout-minutes: 120
|
|
env:
|
|
CI: 'true'
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
timeout-minutes: 10
|
|
- uses: actions/setup-node@v4
|
|
timeout-minutes: 10
|
|
with:
|
|
node-version: '24.11.1'
|
|
cache: npm
|
|
- name: Clean install
|
|
run: npm ci
|
|
timeout-minutes: 10
|
|
- name: Provision Electron binary
|
|
run: |
|
|
npx install-electron --no
|
|
if ($LASTEXITCODE -ne 0) {
|
|
npx install-electron --no
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
}
|
|
timeout-minutes: 10
|
|
- name: Lint
|
|
run: npm run lint
|
|
timeout-minutes: 10
|
|
- name: Lint configuration contract
|
|
run: npm run test:lint-config
|
|
timeout-minutes: 10
|
|
- name: Security contracts
|
|
run: npm run security:check
|
|
timeout-minutes: 10
|
|
- name: Security regression tests
|
|
run: npm run test:security
|
|
timeout-minutes: 10
|
|
- name: CI contract
|
|
run: npm run test:ci-contract
|
|
timeout-minutes: 10
|
|
- name: Installer contract
|
|
run: npm run test:installer-contract
|
|
timeout-minutes: 10
|
|
- name: Managed tools contract
|
|
run: npm run test:managed-tools-contract
|
|
timeout-minutes: 10
|
|
- name: Cutter matrix provisioning contract
|
|
run: npm run test:cutter-matrix-contract
|
|
timeout-minutes: 10
|
|
- name: Build
|
|
run: npm run build
|
|
timeout-minutes: 10
|
|
- name: Live integration contract
|
|
run: npm run test:live-integration-contract
|
|
timeout-minutes: 10
|
|
- name: Unit tests
|
|
run: npm run test:unit
|
|
timeout-minutes: 10
|
|
- name: Focused Electron smoke
|
|
run: |
|
|
npm run test:e2e:focused
|
|
if ($LASTEXITCODE -ne 0) {
|
|
npm run test:e2e:focused
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
}
|
|
timeout-minutes: 20
|
|
- name: Cutter media matrix
|
|
run: |
|
|
node scripts/smoke-test-cutter-media-matrix.js
|
|
if ($LASTEXITCODE -ne 0) {
|
|
node scripts/smoke-test-cutter-media-matrix.js
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
}
|
|
timeout-minutes: 20
|
|
- name: Clean managed tools provision and repair
|
|
run: npm run test:managed-tools-live
|
|
timeout-minutes: 10
|
|
- name: Package directory
|
|
run: |
|
|
npm run pack
|
|
if ($LASTEXITCODE -ne 0) {
|
|
npm run pack
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
}
|
|
timeout-minutes: 10
|
|
- name: Packaged launch smoke
|
|
run: npm run test:packaged-launch
|
|
timeout-minutes: 10
|
|
- name: Build installer
|
|
run: |
|
|
npm run dist:ci
|
|
if ($LASTEXITCODE -ne 0) {
|
|
npm run dist:ci
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
}
|
|
timeout-minutes: 10
|
|
- name: Installer smoke
|
|
run: npm run test:installer
|
|
timeout-minutes: 10
|
|
|
|
twitch-live:
|
|
if: github.event_name == 'workflow_dispatch' && github.event.inputs.live_gate == 'twitch'
|
|
needs: verify
|
|
runs-on: windows-latest
|
|
timeout-minutes: 60
|
|
env:
|
|
CI: 'true'
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
timeout-minutes: 10
|
|
- uses: actions/setup-node@v4
|
|
timeout-minutes: 10
|
|
with:
|
|
node-version: '24.11.1'
|
|
cache: npm
|
|
- name: Clean install
|
|
run: npm ci
|
|
timeout-minutes: 10
|
|
- name: Build
|
|
run: npm run build
|
|
timeout-minutes: 10
|
|
- name: Verify live integration contract
|
|
run: npm run test:live-integration-contract
|
|
timeout-minutes: 10
|
|
- name: Provision pinned media tools
|
|
run: |
|
|
$env:TWITCH_VOD_MANAGER_LIVE_TOOL_ROOT = Join-Path $env:RUNNER_TEMP "tvm-live-tools-$env:GITHUB_RUN_ID-$env:GITHUB_RUN_ATTEMPT"
|
|
@'
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const tools = require('./dist/tools.js');
|
|
|
|
(async () => {
|
|
const root = process.env.TWITCH_VOD_MANAGER_LIVE_TOOL_ROOT;
|
|
const streamlinkDirectory = path.join(root, 'streamlink');
|
|
const ffmpegDirectory = path.join(root, 'ffmpeg');
|
|
const temporaryDirectory = path.join(root, 'temporary');
|
|
fs.mkdirSync(temporaryDirectory, { recursive: true });
|
|
tools.initToolDirs(streamlinkDirectory, ffmpegDirectory, () => temporaryDirectory);
|
|
const result = await tools.repairManagedTools();
|
|
if (!result.success) throw new Error(`Pinned media tool provisioning failed: ${JSON.stringify(result.statuses)}`);
|
|
if (!process.env.GITHUB_ENV) throw new Error('Actions environment export file is unavailable');
|
|
fs.appendFileSync(process.env.GITHUB_ENV, [
|
|
`TWITCH_VOD_MANAGER_LIVE_STREAMLINK_PATH=${tools.getStreamlinkPath()}`,
|
|
`TWITCH_VOD_MANAGER_LIVE_FFPROBE_PATH=${tools.getFFprobePath()}`,
|
|
''
|
|
].join('\n'));
|
|
})().catch((error) => {
|
|
console.error(error instanceof Error ? error.message : String(error));
|
|
process.exitCode = 1;
|
|
});
|
|
'@ | node
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
timeout-minutes: 10
|
|
- name: Twitch provider OAuth, Helix and bounded VOD gate
|
|
env:
|
|
TWITCH_VOD_MANAGER_LIVE_INTEGRATION: '1'
|
|
TWITCH_VOD_MANAGER_LIVE_TWITCH_CLIENT_ID: ${{ secrets.TWITCH_VOD_MANAGER_LIVE_TWITCH_CLIENT_ID }}
|
|
TWITCH_VOD_MANAGER_LIVE_TWITCH_CLIENT_SECRET: ${{ secrets.TWITCH_VOD_MANAGER_LIVE_TWITCH_CLIENT_SECRET }}
|
|
TWITCH_VOD_MANAGER_LIVE_TWITCH_LOGIN: ${{ secrets.TWITCH_VOD_MANAGER_LIVE_TWITCH_LOGIN }}
|
|
TWITCH_VOD_MANAGER_LIVE_TWITCH_VOD_ID: ${{ secrets.TWITCH_VOD_MANAGER_LIVE_TWITCH_VOD_ID }}
|
|
run: npm run test:live:twitch
|
|
timeout-minutes: 10
|
|
|
|
updater-live-postpublish:
|
|
if: github.event_name == 'workflow_dispatch' && github.event.inputs.live_gate == 'updater-postpublish'
|
|
needs: verify
|
|
runs-on: windows-latest
|
|
timeout-minutes: 60
|
|
env:
|
|
CI: 'true'
|
|
TWITCH_VOD_MANAGER_LIVE_INTEGRATION: '1'
|
|
TWITCH_VOD_MANAGER_LIVE_SOURCE_VERSION: ${{ github.event.inputs.source_version }}
|
|
TWITCH_VOD_MANAGER_LIVE_SOURCE_SHA256: ${{ github.event.inputs.source_sha256 }}
|
|
TWITCH_VOD_MANAGER_LIVE_UPDATE_VERSION: ${{ github.event.inputs.update_version }}
|
|
TWITCH_VOD_MANAGER_LIVE_UPDATE_SHA512: ${{ github.event.inputs.update_sha512 }}
|
|
TWITCH_VOD_MANAGER_LIVE_UPDATE_COMMIT_SHA: ${{ github.sha }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
timeout-minutes: 10
|
|
with:
|
|
ref: ${{ github.sha }}
|
|
- uses: actions/setup-node@v4
|
|
timeout-minutes: 10
|
|
with:
|
|
node-version: '24.11.1'
|
|
cache: npm
|
|
- name: Clean install
|
|
run: npm ci
|
|
timeout-minutes: 10
|
|
- name: Require explicit post-publish updater inputs
|
|
run: |
|
|
$required = @(
|
|
'TWITCH_VOD_MANAGER_LIVE_SOURCE_VERSION',
|
|
'TWITCH_VOD_MANAGER_LIVE_SOURCE_SHA256',
|
|
'TWITCH_VOD_MANAGER_LIVE_UPDATE_VERSION',
|
|
'TWITCH_VOD_MANAGER_LIVE_UPDATE_SHA512',
|
|
'TWITCH_VOD_MANAGER_LIVE_UPDATE_COMMIT_SHA'
|
|
)
|
|
foreach ($name in $required) {
|
|
if ([string]::IsNullOrWhiteSpace([Environment]::GetEnvironmentVariable($name))) {
|
|
throw "Missing required post-publish updater input: $name"
|
|
}
|
|
}
|
|
if ($env:TWITCH_VOD_MANAGER_LIVE_UPDATE_COMMIT_SHA -notmatch '^[0-9a-fA-F]{40}$') {
|
|
throw 'Pinned updater commit provenance must be a 40-character hexadecimal SHA'
|
|
}
|
|
if (-not [string]::Equals($env:TWITCH_VOD_MANAGER_LIVE_UPDATE_COMMIT_SHA, $env:GITHUB_SHA, [StringComparison]::OrdinalIgnoreCase)) {
|
|
throw 'Pinned updater commit provenance must match GITHUB_SHA'
|
|
}
|
|
$sourceVersionText = $env:TWITCH_VOD_MANAGER_LIVE_SOURCE_VERSION.Trim()
|
|
$updateVersionText = $env:TWITCH_VOD_MANAGER_LIVE_UPDATE_VERSION.Trim()
|
|
$versionPattern = '^(0|[1-9][0-9]{0,8})\.(0|[1-9][0-9]{0,8})\.(0|[1-9][0-9]{0,8})$'
|
|
if ($sourceVersionText -notmatch $versionPattern) {
|
|
throw 'Pinned source version must be an exact three-segment numeric release version'
|
|
}
|
|
if ($updateVersionText -notmatch $versionPattern) {
|
|
throw 'Pinned update version must be an exact three-segment numeric release version'
|
|
}
|
|
$sourceVersion = [version]$sourceVersionText
|
|
$updateVersion = [version]$updateVersionText
|
|
if ($sourceVersion -ge $updateVersion) {
|
|
throw 'Pinned source version must be older than the update version'
|
|
}
|
|
$packageVersion = (Get-Content -Raw -LiteralPath package.json | ConvertFrom-Json).version
|
|
if ($updateVersionText -ne $packageVersion) {
|
|
throw "Pinned update version must match package.json version $packageVersion"
|
|
}
|
|
$expectedRef = "refs/tags/v$updateVersionText"
|
|
if ($env:GITHUB_REF -ne $expectedRef) {
|
|
throw "Post-publish updater gate must run from release tag $expectedRef"
|
|
}
|
|
timeout-minutes: 10
|
|
- name: Build
|
|
run: npm run build
|
|
timeout-minutes: 10
|
|
- name: Verify live integration contract
|
|
run: npm run test:live-integration-contract
|
|
timeout-minutes: 10
|
|
- name: Verify published updater path
|
|
run: npm run test:live:updater-postpublish
|
|
timeout-minutes: 25
|