This commit is contained in:
@@ -4,23 +4,20 @@ on: push
|
|||||||
jobs:
|
jobs:
|
||||||
diag:
|
diag:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
timeout-minutes: 20
|
timeout-minutes: 10
|
||||||
env:
|
env:
|
||||||
CI: 'true'
|
CI: 'true'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Env dump
|
||||||
timeout-minutes: 10
|
run: |
|
||||||
- uses: actions/setup-node@v4
|
"CI=$env:CI"
|
||||||
timeout-minutes: 10
|
"GITHUB_ACTIONS=$env:GITHUB_ACTIONS"
|
||||||
with:
|
"GITEA_ACTIONS=$env:GITEA_ACTIONS"
|
||||||
node-version: '24.11.1'
|
"GITHUB_SERVER_URL=$env:GITHUB_SERVER_URL"
|
||||||
cache: npm
|
"GITHUB_RUN_ID=$env:GITHUB_RUN_ID"
|
||||||
- name: Clean install
|
"RUNNER_OS=$env:RUNNER_OS"
|
||||||
run: npm ci
|
"RUNNER_TEMP=$env:RUNNER_TEMP"
|
||||||
timeout-minutes: 10
|
"RUNNER_ENVIRONMENT=$env:RUNNER_ENVIRONMENT"
|
||||||
- name: Build
|
"RUNNER_NAME=$env:RUNNER_NAME"
|
||||||
run: npm run build
|
Get-ChildItem env: | Where-Object { $_.Name -match '^(GITHUB|GITEA|RUNNER|ACTIONS)' } | ForEach-Object { "$($_.Name)=$($_.Value)" }
|
||||||
timeout-minutes: 10
|
exit 0
|
||||||
- name: Repair repro with full diagnostics
|
|
||||||
run: node tvm-diag-repair.js
|
|
||||||
timeout-minutes: 10
|
|
||||||
|
|||||||
@@ -1,43 +1,13 @@
|
|||||||
name: Windows CI
|
name: Windows CI
|
||||||
|
on: push
|
||||||
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:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
verify:
|
surface-diag:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
timeout-minutes: 120
|
timeout-minutes: 90
|
||||||
env:
|
env:
|
||||||
CI: 'true'
|
CI: 'true'
|
||||||
steps:
|
steps:
|
||||||
@@ -59,48 +29,26 @@ jobs:
|
|||||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||||
}
|
}
|
||||||
timeout-minutes: 10
|
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
|
- name: Build
|
||||||
run: npm run build
|
run: npm run build
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: Live integration contract
|
- name: Surface after build
|
||||||
run: npm run test:live-integration-contract
|
run: node scripts/../surface-check.js after-build
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Unit tests
|
|
||||||
run: npm run test:unit
|
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Focused Electron smoke
|
- name: Focused Electron smoke
|
||||||
run: npm run test:e2e:focused
|
run: npm run test:e2e:focused
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
- name: Surface after focused smoke
|
||||||
|
run: node surface-check.js after-focused
|
||||||
- name: Cutter media matrix
|
- name: Cutter media matrix
|
||||||
run: node scripts/smoke-test-cutter-media-matrix.js
|
run: node scripts/smoke-test-cutter-media-matrix.js
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
- name: Surface after matrix
|
||||||
|
run: node surface-check.js after-matrix
|
||||||
- name: Clean managed tools provision and repair
|
- name: Clean managed tools provision and repair
|
||||||
run: npm run test:managed-tools-live
|
run: npm run test:managed-tools-live
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
- name: Surface after managed tools live
|
||||||
|
run: node surface-check.js after-managed-tools
|
||||||
- name: Package directory
|
- name: Package directory
|
||||||
run: |
|
run: |
|
||||||
npm run pack
|
npm run pack
|
||||||
@@ -109,9 +57,13 @@ jobs:
|
|||||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||||
}
|
}
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
- name: Surface after pack
|
||||||
|
run: node surface-check.js after-pack
|
||||||
- name: Packaged launch smoke
|
- name: Packaged launch smoke
|
||||||
run: npm run test:packaged-launch
|
run: npm run test:packaged-launch
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
- name: Surface after packaged launch
|
||||||
|
run: node surface-check.js after-packaged-launch
|
||||||
- name: Build installer
|
- name: Build installer
|
||||||
run: |
|
run: |
|
||||||
npm run dist:ci
|
npm run dist:ci
|
||||||
@@ -120,149 +72,5 @@ jobs:
|
|||||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||||
}
|
}
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: Installer smoke
|
- name: Surface after dist
|
||||||
run: npm run test:installer
|
run: node surface-check.js after-dist
|
||||||
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
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
const fs = require('node:fs');
|
||||||
|
const os = require('node:os');
|
||||||
|
const path = require('node:path');
|
||||||
|
const { spawnSync } = require('node:child_process');
|
||||||
|
|
||||||
|
const label = process.argv[2] || 'unknown';
|
||||||
|
const guid = '08429788-303d-53b6-a4f9-894401712c7e';
|
||||||
|
const keys = [
|
||||||
|
`HKCU\\Software\\${guid}`,
|
||||||
|
`HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${guid}`,
|
||||||
|
`HKLM\\Software\\${guid}`,
|
||||||
|
`HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${guid}`
|
||||||
|
];
|
||||||
|
const links = [
|
||||||
|
path.join(process.env.APPDATA || '', 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Twitch VOD Manager.lnk'),
|
||||||
|
path.join(os.homedir(), 'Desktop', 'Twitch VOD Manager.lnk')
|
||||||
|
];
|
||||||
|
|
||||||
|
console.log(`=== SURFACE ${label} ===`);
|
||||||
|
for (const key of keys) {
|
||||||
|
const result = spawnSync('reg.exe', ['query', key], { encoding: 'utf8', windowsHide: true });
|
||||||
|
console.log(`${key} -> ${result.status === 0 ? 'EXISTS' : 'absent'}`);
|
||||||
|
if (result.status === 0) console.log(result.stdout.trim().split(/\r?\n/).slice(0, 8).join('\n'));
|
||||||
|
}
|
||||||
|
for (const link of links) {
|
||||||
|
console.log(`${link} -> ${fs.existsSync(link) ? 'EXISTS' : 'absent'}`);
|
||||||
|
}
|
||||||
|
process.exit(0);
|
||||||
Reference in New Issue
Block a user