Files
Sucukdeluxe 35364ca588
Windows CI / verify (push) Failing after 4m52s
fix(ci): stabilize Electron smoke flakiness at its sources
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.
2026-08-14 18:46:35 +02:00

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