From 0a25c99102767a4cec2f58a12891cbac4d2b15e1 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe <259325684+Sucukdeluxe@users.noreply.github.com> Date: Fri, 14 Aug 2026 15:06:25 +0200 Subject: [PATCH] ci-diag: repair repro with full diagnostics --- .gitea/workflows/windows-ci.yml | 40 +++++++++--------- tvm-diag-repair.js | 72 +++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 21 deletions(-) create mode 100644 tvm-diag-repair.js diff --git a/.gitea/workflows/windows-ci.yml b/.gitea/workflows/windows-ci.yml index 54a010c..36f149b 100644 --- a/.gitea/workflows/windows-ci.yml +++ b/.gitea/workflows/windows-ci.yml @@ -4,25 +4,23 @@ on: push jobs: diag: runs-on: windows-latest - timeout-minutes: 10 + timeout-minutes: 20 + env: + CI: 'true' steps: - - name: Probe tool sources - run: | - whoami - "TEMP=$env:TEMP" - "HTTP_PROXY=$env:HTTP_PROXY HTTPS_PROXY=$env:HTTPS_PROXY NO_PROXY=$env:NO_PROXY" - try { [System.Net.Dns]::GetHostAddresses('www.gyan.dev') | ForEach-Object { "DNS gyan: $($_.IPAddressToString)" } } catch { "DNS gyan ERR: $($_.Exception.Message)" } - try { [System.Net.Dns]::GetHostAddresses('github.com') | ForEach-Object { "DNS github: $($_.IPAddressToString)" } } catch { "DNS github ERR: $($_.Exception.Message)" } - $ProgressPreference = 'SilentlyContinue' - try { $r = Invoke-WebRequest -Uri 'https://api.github.com/' -UseBasicParsing -TimeoutSec 20; "api.github: $($r.StatusCode)" } catch { "api.github ERR: $($_.Exception.Message)" } - try { $r = Invoke-WebRequest -Uri 'https://www.gyan.dev/ffmpeg/builds/packages/ffmpeg-8.1.2-essentials_build.zip' -Method Head -UseBasicParsing -TimeoutSec 30; "gyan HEAD: $($r.StatusCode) len=$($r.Headers['Content-Length'])" } catch { "gyan ERR: $($_.Exception.Message)" } - try { $r = Invoke-WebRequest -Uri 'https://github.com/streamlink/windows-builds/releases/download/8.4.0-1/streamlink-8.4.0-1-py314-x86_64.zip' -Method Head -UseBasicParsing -TimeoutSec 30; "github release HEAD: $($r.StatusCode) len=$($r.Headers['Content-Length'])" } catch { "github release ERR: $($_.Exception.Message)" } - $node = Get-Command node -ErrorAction SilentlyContinue - if ($node) { - "node: $($node.Source) $(node --version)" - node -e "const https=require('https');https.get('https://www.gyan.dev/ffmpeg/builds/packages/ffmpeg-8.1.2-essentials_build.zip',{timeout:20000},r=>{console.log('node gyan status',r.statusCode);r.destroy();}).on('error',e=>console.log('node gyan ERR',e.code||'',e.message)).on('timeout',function(){this.destroy();console.log('node gyan TIMEOUT')})" - Start-Sleep -Seconds 3 - } else { - 'node not on PATH' - } - exit 0 + - 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: Repair repro with full diagnostics + run: node tvm-diag-repair.js + timeout-minutes: 10 diff --git a/tvm-diag-repair.js b/tvm-diag-repair.js new file mode 100644 index 0000000..dd1e9c3 --- /dev/null +++ b/tvm-diag-repair.js @@ -0,0 +1,72 @@ +const fs = require('node:fs'); +const os = require('node:os'); +const path = require('node:path'); +const { spawn } = require('node:child_process'); +const axios = require(path.join(process.cwd(), 'node_modules', 'axios')); +const { createManagedToolInstaller } = require(path.join(process.cwd(), 'dist', 'main', 'domain', 'managed-tools.js')); +const { APPLICATION_TOOL_MANIFEST } = require(path.join(process.cwd(), 'dist', 'main', 'domain', 'tool-manifest.js')); + +async function downloadFile(url, destinationPath) { + const response = await axios.get(url, { responseType: 'stream', timeout: 120000 }); + await new Promise((resolve, reject) => { + const writer = fs.createWriteStream(destinationPath); + response.data.on('error', (err) => { writer.destroy(); reject(err); }); + response.data.pipe(writer); + writer.on('finish', () => resolve()); + writer.on('error', (err) => reject(err)); + }); + const size = fs.statSync(destinationPath).size; + console.log('downloaded', url, '->', destinationPath, size, 'bytes'); +} + +async function extractZip(zipPath, destinationDir) { + fs.mkdirSync(destinationDir, { recursive: true }); + const command = `Expand-Archive -Path '${zipPath.replace(/'/g, "''")}' -DestinationPath '${destinationDir.replace(/'/g, "''")}' -Force`; + await new Promise((resolve, reject) => { + const proc = spawn('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', command], { windowsHide: true }); + let stderr = ''; + proc.stderr?.on('data', (data) => { stderr += data.toString(); }); + proc.on('close', (code) => { + console.log('extract close code', code, 'stderr:', stderr.trim().slice(0, 500)); + if (code === 0) resolve(); else reject(new Error(`Expand-Archive exit code ${code}: ${stderr.trim()}`)); + }); + proc.on('error', (err) => { console.log('extract spawn error', err.message); reject(err); }); + }); +} + +(async () => { + console.log('user temp:', os.tmpdir()); + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'tvm-diag-repair-')); + console.log('sandbox:', root); + for (const toolId of ['streamlink', 'ffmpeg']) { + const manifest = APPLICATION_TOOL_MANIFEST[toolId]; + const installationDirectory = path.join(root, 'tools', toolId); + const temporaryDirectory = path.join(root, 'managed-tools-temp'); + fs.mkdirSync(installationDirectory, { recursive: true }); + fs.mkdirSync(temporaryDirectory, { recursive: true }); + const installer = createManagedToolInstaller({ + installationDirectory, + temporaryDirectory, + download: downloadFile, + extract: extractZip, + diagnostic: (message, details) => console.log('DIAG', toolId, message, JSON.stringify(details)) + }); + const started = Date.now(); + try { + const result = await installer.repair(manifest); + console.log(toolId, 'repair after', Date.now() - started, 'ms:', JSON.stringify({ + success: result.success, + error: result.error, + detail: result.detail, + status: result.status, + diagnostics: result.diagnostics + }, null, 2)); + } catch (error) { + console.log(toolId, 'repair THREW after', Date.now() - started, 'ms:', String(error && error.stack || error)); + } + } + fs.rmSync(root, { recursive: true, force: true }); +})().catch((error) => { + console.error('FATAL', error); + process.exitCode = 1; +});