cleanup
This commit is contained in:
138
scripts/build.ps1
Normal file
138
scripts/build.ps1
Normal file
@@ -0,0 +1,138 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Builds both the VSCode extension (.vsix) and the standalone Windows exe.
|
||||
Both artifacts land in dist/.
|
||||
|
||||
.EXAMPLE
|
||||
.\build.ps1
|
||||
.\build.ps1 -Standalone # Skip the extension; build only the exe
|
||||
.\build.ps1 -Extension # Skip the standalone; build only the .vsix
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[switch]$Extension,
|
||||
[switch]$Standalone
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$repoRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
Set-Location $repoRoot
|
||||
|
||||
# If neither was specified, build both.
|
||||
if (-not $Extension -and -not $Standalone) {
|
||||
$Extension = $true
|
||||
$Standalone = $true
|
||||
}
|
||||
|
||||
$distDir = Join-Path $repoRoot "dist"
|
||||
if (Test-Path $distDir) {
|
||||
Remove-Item -Path (Join-Path $distDir "*") -Recurse -Force
|
||||
}
|
||||
New-Item -ItemType Directory -Path $distDir -Force | Out-Null
|
||||
|
||||
$pkg = Get-Content (Join-Path $repoRoot "package.json") -Raw | ConvertFrom-Json
|
||||
$version = $pkg.version
|
||||
|
||||
function Write-Step($msg) {
|
||||
Write-Host ""
|
||||
Write-Host "==> $msg" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
function Get-FileSize($path) {
|
||||
if (-not (Test-Path $path)) { return $null }
|
||||
$bytes = (Get-Item $path).Length
|
||||
if ($bytes -ge 1MB) { return "{0:N2} MB" -f ($bytes / 1MB) }
|
||||
if ($bytes -ge 1KB) { return "{0:N1} KB" -f ($bytes / 1KB) }
|
||||
return "$bytes B"
|
||||
}
|
||||
|
||||
# Ensure Go is on PATH (winget install doesn't update current session)
|
||||
if (-not (Get-Command go -ErrorAction SilentlyContinue)) {
|
||||
$goBin = "C:\Program Files\Go\bin"
|
||||
if (Test-Path "$goBin\go.exe") {
|
||||
$env:Path = "$goBin;$env:Path"
|
||||
} else {
|
||||
Write-Error "go not found on PATH. Install Go from https://go.dev/dl/"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
$summary = @()
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# VSCode extension -> .vsix
|
||||
# ----------------------------------------------------------------------
|
||||
if ($Extension) {
|
||||
Write-Step "Building VSCode extension"
|
||||
if (-not (Test-Path "node_modules")) {
|
||||
Write-Host " node_modules missing - running npm install" -ForegroundColor Yellow
|
||||
npm install
|
||||
if ($LASTEXITCODE -ne 0) { Write-Error "npm install failed"; exit 1 }
|
||||
}
|
||||
npm run compile
|
||||
if ($LASTEXITCODE -ne 0) { Write-Error "tsc compile failed"; exit 1 }
|
||||
|
||||
Write-Step "Packaging .vsix -> dist/"
|
||||
$vsixTargetDir = $distDir
|
||||
if (Get-Command vsce -ErrorAction SilentlyContinue) {
|
||||
vsce package --out $vsixTargetDir
|
||||
} else {
|
||||
Write-Host " vsce not found - using npx @vscode/vsce" -ForegroundColor Yellow
|
||||
npx --yes @vscode/vsce package --out $vsixTargetDir
|
||||
}
|
||||
if ($LASTEXITCODE -ne 0) { Write-Error "vsce package failed"; exit 1 }
|
||||
|
||||
$vsix = Get-ChildItem -Path $distDir -Filter "*.vsix" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||
if ($vsix) {
|
||||
$summary += [pscustomobject]@{
|
||||
Artifact = $vsix.Name
|
||||
Path = "dist\$($vsix.Name)"
|
||||
Size = Get-FileSize $vsix.FullName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Standalone Windows exe -> dist/hpl-toolbox.exe
|
||||
# ----------------------------------------------------------------------
|
||||
if ($Standalone) {
|
||||
Write-Step "Building standalone exe -> dist/"
|
||||
Push-Location (Join-Path $repoRoot "standalone")
|
||||
try {
|
||||
# If rsrc.syso is missing (e.g. fresh clone before icon embedding), regenerate.
|
||||
if (-not (Test-Path "rsrc.syso") -and (Test-Path "hpl.ico")) {
|
||||
if (Get-Command rsrc -ErrorAction SilentlyContinue) {
|
||||
Write-Host " Regenerating rsrc.syso" -ForegroundColor Yellow
|
||||
rsrc -ico hpl.ico -o rsrc.syso
|
||||
} else {
|
||||
Write-Host " rsrc tool missing - exe will build without embedded icon" -ForegroundColor Yellow
|
||||
Write-Host " (install with: go install github.com/akavel/rsrc@latest)" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
|
||||
$exeName = "hpl-toolbox-$version.exe"
|
||||
$exeOut = Join-Path $distDir $exeName
|
||||
$embeddedReadme = Join-Path (Get-Location) "README.md"
|
||||
Copy-Item -Path (Join-Path $repoRoot "README.md") -Destination $embeddedReadme -Force
|
||||
try {
|
||||
go build -tags release -ldflags="-s -w -H windowsgui -X main.AppVersion=$version" -trimpath -o $exeOut .
|
||||
if ($LASTEXITCODE -ne 0) { Write-Error "go build failed"; exit 1 }
|
||||
} finally {
|
||||
Remove-Item -Path $embeddedReadme -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
$summary += [pscustomobject]@{
|
||||
Artifact = $exeName
|
||||
Path = "dist\$exeName"
|
||||
Size = Get-FileSize $exeOut
|
||||
}
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Summary
|
||||
# ----------------------------------------------------------------------
|
||||
Write-Step "Build complete"
|
||||
$summary | Format-Table -AutoSize | Out-Host
|
||||
@@ -1,177 +0,0 @@
|
||||
import { chromium } from 'playwright';
|
||||
import { createServer } from 'node:http';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { mkdtemp, readdir, readFile, rm } from 'node:fs/promises';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
|
||||
const root = process.cwd();
|
||||
const dogCatRoot = process.env.DOGCAT_ROOT || path.join(root, 'aiAssets', 'DogCat3');
|
||||
const waitMs = Number(process.env.RUNTIME_WAIT_MS || 9000);
|
||||
|
||||
const mraidStub = `
|
||||
window.mraid = window.mraid || {
|
||||
getState: function(){ return 'default'; },
|
||||
isViewable: function(){ return true; },
|
||||
addEventListener: function(name, cb){ if (name === 'ready') setTimeout(cb, 0); },
|
||||
removeEventListener: function(){},
|
||||
open: function(url){ console.log('[stub mraid.open]', url); },
|
||||
close: function(){ console.log('[stub mraid.close]'); },
|
||||
supports: function(){ return false; }
|
||||
};
|
||||
`;
|
||||
|
||||
const exitApiStub = `
|
||||
window.ExitApi = window.ExitApi || {
|
||||
exit: function(){ console.log('[stub ExitApi.exit]'); }
|
||||
};
|
||||
`;
|
||||
|
||||
async function collectCases(tempDir) {
|
||||
const cases = [];
|
||||
const networkFolders = [
|
||||
'Applovin',
|
||||
'Facebook',
|
||||
'Ironsource',
|
||||
'Moloco',
|
||||
'Unity',
|
||||
'GoogleAds',
|
||||
'Mintegral',
|
||||
'Vungle',
|
||||
];
|
||||
for (const name of networkFolders) {
|
||||
const dir = path.join(dogCatRoot, name);
|
||||
if (!existsSync(dir)) continue;
|
||||
const entries = await readdir(dir);
|
||||
const htmlName = entries.find((entry) => /\.html?$/i.test(entry));
|
||||
if (htmlName) {
|
||||
cases.push([name, await readFile(path.join(dir, htmlName))]);
|
||||
continue;
|
||||
}
|
||||
const zipName = entries.find((entry) => /\.zip$/i.test(entry));
|
||||
if (!zipName) continue;
|
||||
const zip = path.join(dir, zipName);
|
||||
const out = path.join(tempDir, name);
|
||||
execFileSync('powershell', [
|
||||
'-NoProfile',
|
||||
'-Command',
|
||||
`Expand-Archive -LiteralPath '${zip.replaceAll("'", "''")}' -DestinationPath '${out.replaceAll("'", "''")}' -Force`,
|
||||
], { stdio: 'ignore' });
|
||||
const expanded = await readdir(out);
|
||||
const indexName = expanded.find((entry) => entry.toLowerCase() === 'index.html');
|
||||
if (indexName) cases.push([name, await readFile(path.join(out, indexName))]);
|
||||
}
|
||||
return cases;
|
||||
}
|
||||
|
||||
async function startServer(cases) {
|
||||
const html = new Map(cases.map(([name, body]) => [`/${name}/index.html`, body]));
|
||||
const server = createServer((req, res) => {
|
||||
const url = new URL(req.url || '/', 'http://127.0.0.1');
|
||||
if (url.pathname.endsWith('/mraid.js')) {
|
||||
res.writeHead(200, { 'content-type': 'application/javascript' });
|
||||
res.end(mraidStub);
|
||||
return;
|
||||
}
|
||||
if (url.pathname.endsWith('/exitapi.js')) {
|
||||
res.writeHead(200, { 'content-type': 'application/javascript' });
|
||||
res.end(exitApiStub);
|
||||
return;
|
||||
}
|
||||
const body = html.get(url.pathname);
|
||||
if (body) {
|
||||
res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
|
||||
res.end(body);
|
||||
return;
|
||||
}
|
||||
res.writeHead(404, { 'content-type': 'text/plain' });
|
||||
res.end('not found');
|
||||
});
|
||||
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
|
||||
return {
|
||||
baseUrl: `http://127.0.0.1:${server.address().port}`,
|
||||
close: () => new Promise((resolve) => server.close(resolve)),
|
||||
};
|
||||
}
|
||||
|
||||
function simplifyUrl(url) {
|
||||
return url.replace(/^https?:\/\/127\.0\.0\.1:\d+\//, '/');
|
||||
}
|
||||
|
||||
async function runCase(browser, baseUrl, name) {
|
||||
const page = await browser.newPage({ viewport: { width: 390, height: 844 }, deviceScaleFactor: 2, isMobile: true });
|
||||
const issues = [];
|
||||
|
||||
page.on('pageerror', (error) => {
|
||||
issues.push({ type: 'pageerror', text: error.stack || error.message });
|
||||
});
|
||||
page.on('console', (message) => {
|
||||
if (['error', 'assert'].includes(message.type())) {
|
||||
const location = message.location();
|
||||
const suffix = location.url ? ` @ ${simplifyUrl(location.url)}:${location.lineNumber}:${location.columnNumber}` : '';
|
||||
issues.push({ type: `console.${message.type()}`, text: `${message.text()}${suffix}` });
|
||||
}
|
||||
});
|
||||
page.on('requestfailed', (request) => {
|
||||
issues.push({
|
||||
type: 'requestfailed',
|
||||
text: `${simplifyUrl(request.url())} :: ${request.failure()?.errorText || 'failed'}`,
|
||||
});
|
||||
});
|
||||
page.on('response', (response) => {
|
||||
if (response.status() >= 400) {
|
||||
issues.push({
|
||||
type: `http.${response.status()}`,
|
||||
text: simplifyUrl(response.url()),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto(`${baseUrl}/${name}/index.html`, { waitUntil: 'domcontentloaded', timeout: 20000 });
|
||||
await page.waitForTimeout(waitMs);
|
||||
|
||||
const bodyText = await page.locator('body').evaluate((body) => body.innerText.slice(0, 2000)).catch(() => '');
|
||||
const canvasCount = await page.locator('canvas').count().catch(() => 0);
|
||||
const title = await page.title().catch(() => '');
|
||||
await page.close();
|
||||
|
||||
return { issues, canvasCount, title, bodyText };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const tempDir = await mkdtemp(path.join(tmpdir(), 'dogcat3-playwright-'));
|
||||
let server;
|
||||
let browser;
|
||||
try {
|
||||
const cases = await collectCases(tempDir);
|
||||
server = await startServer(cases);
|
||||
browser = await chromium.launch({ channel: 'msedge', headless: true });
|
||||
|
||||
for (const [name] of cases) {
|
||||
const result = await runCase(browser, server.baseUrl, name);
|
||||
console.log(`\n## ${name}`);
|
||||
console.log(`canvas=${result.canvasCount}`);
|
||||
if (result.title) console.log(`title=${result.title}`);
|
||||
if (!result.issues.length) {
|
||||
console.log('No page errors, console errors, failed requests, or HTTP 4xx/5xx responses captured.');
|
||||
} else {
|
||||
for (const issue of result.issues) {
|
||||
console.log(`- ${issue.type}: ${issue.text.replace(/\s+/g, ' ').slice(0, 700)}`);
|
||||
}
|
||||
}
|
||||
if (result.bodyText && /error|exception|cannot|failed/i.test(result.bodyText)) {
|
||||
console.log(`body_error_text=${result.bodyText.replace(/\s+/g, ' ').slice(0, 700)}`);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (browser) await browser.close();
|
||||
if (server) await server.close();
|
||||
await rm(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
11
scripts/run-standalone.ps1
Normal file
11
scripts/run-standalone.ps1
Normal file
@@ -0,0 +1,11 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$standalone = Join-Path $root 'standalone'
|
||||
|
||||
Push-Location $standalone
|
||||
try {
|
||||
go run .
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
Reference in New Issue
Block a user