Manifestbasierter Production-Updater 3.3, Modulnavigation, optionales Produktbild Studio, Listing-Studio-Aktionsleiste sowie plattformübergreifende Windows-/Linux-Release-Gates.
130 lines
6.1 KiB
JavaScript
130 lines
6.1 KiB
JavaScript
import { existsSync, readFileSync } from 'node:fs';
|
||
import { resolve } from 'node:path';
|
||
|
||
const root = resolve(import.meta.dirname, '..');
|
||
const failures = [];
|
||
const required = [
|
||
'UPDATE_VENDOO.bat',
|
||
'.gitattributes',
|
||
'.editorconfig',
|
||
'scripts/update-vendoo-gui.ps1',
|
||
'scripts/updater/Vendoo.Updater.Preflight.ps1',
|
||
'scripts/updater/Vendoo.Updater.UI.ps1',
|
||
'scripts/updater/Vendoo.Updater.Host.ps1',
|
||
'scripts/updater/Vendoo.Updater.Worker.ps1',
|
||
'docs/PRODUCTION_UPDATER_2_9.md',
|
||
];
|
||
for (const file of required) {
|
||
if (!existsSync(resolve(root, file))) failures.push(`Pflichtdatei fehlt: ${file}`);
|
||
}
|
||
|
||
const batPath = resolve(root, 'UPDATE_VENDOO.bat');
|
||
const bat = readFileSync(batPath);
|
||
const batText = bat.toString('ascii');
|
||
|
||
if (bat.length < 16) failures.push('UPDATE_VENDOO.bat ist unerwartet leer.');
|
||
if (bat[0] === 0xef && bat[1] === 0xbb && bat[2] === 0xbf) failures.push('UPDATE_VENDOO.bat darf keine UTF-8-BOM besitzen.');
|
||
for (let i = 0; i < bat.length; i += 1) {
|
||
if (bat[i] > 0x7f) {
|
||
failures.push(`UPDATE_VENDOO.bat ist nicht reines ASCII (Byte ${i}: 0x${bat[i].toString(16)}).`);
|
||
break;
|
||
}
|
||
}
|
||
for (let i = 0; i < bat.length; i += 1) {
|
||
if (bat[i] === 0x0a && (i === 0 || bat[i - 1] !== 0x0d)) {
|
||
failures.push(`UPDATE_VENDOO.bat enthält LF ohne CR an Byte ${i}.`);
|
||
break;
|
||
}
|
||
if (bat[i] === 0x0d && (i + 1 >= bat.length || bat[i + 1] !== 0x0a)) {
|
||
failures.push(`UPDATE_VENDOO.bat enthält CR ohne LF an Byte ${i}.`);
|
||
break;
|
||
}
|
||
}
|
||
if (!batText.startsWith('@echo off\r\n')) failures.push('UPDATE_VENDOO.bat startet nicht exakt mit @echo off + CRLF.');
|
||
for (const needle of [
|
||
'cd /d "%~dp0"',
|
||
'%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe',
|
||
'Vendoo.Updater.Preflight.ps1',
|
||
'update-vendoo-gui.ps1',
|
||
':preflight_failed',
|
||
':gui_failed',
|
||
'pause',
|
||
]) {
|
||
if (!batText.includes(needle)) failures.push(`BAT-Startvertrag fehlt: ${needle}`);
|
||
}
|
||
for (const forbidden of ['chcp ', '-PackageRoot', 'start "', 'ä', 'ö', 'ü', 'ß']) {
|
||
if (batText.includes(forbidden)) failures.push(`BAT enthält verbotene Regression: ${forbidden}`);
|
||
}
|
||
|
||
const attrs = readFileSync(resolve(root, '.gitattributes'), 'ascii');
|
||
if (!attrs.includes('*.bat text eol=crlf')) failures.push('.gitattributes erzwingt CRLF für BAT nicht.');
|
||
if (!attrs.includes('*.cmd text eol=crlf')) failures.push('.gitattributes erzwingt CRLF für CMD nicht.');
|
||
const editor = readFileSync(resolve(root, '.editorconfig'), 'ascii');
|
||
for (const needle of ['[*.{bat,cmd}]', 'charset = latin1', 'end_of_line = crlf']) {
|
||
if (!editor.includes(needle)) failures.push(`.editorconfig fehlt: ${needle}`);
|
||
}
|
||
|
||
for (const rel of required.filter((file) => file.endsWith('.ps1'))) {
|
||
const bytes = readFileSync(resolve(root, rel));
|
||
if (!(bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf)) failures.push(`${rel} besitzt keine UTF-8-BOM.`);
|
||
const text = bytes.subarray(3).toString('utf8');
|
||
if (!text.includes('2.9.0') && rel.includes('updater/Vendoo.Updater.')) failures.push(`${rel} enthält nicht Updater-Version 2.9.0.`);
|
||
}
|
||
|
||
|
||
const workerText = readFileSync(resolve(root, 'scripts/updater/Vendoo.Updater.Worker.ps1'), 'utf8');
|
||
for (const marker of ['Coolify Auto Deploy', 'Es wird kein Webhook und kein GitHub Secret benötigt.', 'AddMinutes(15)', '/readyz']) {
|
||
if (!workerText.includes(marker)) failures.push(`Auto-Deploy-Vertrag fehlt: ${marker}`);
|
||
}
|
||
|
||
const packageJson = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8'));
|
||
for (const [name, command] of Object.entries(packageJson.scripts || {})) {
|
||
if (/\bpython(?:3)?\b|\bpy(?:\.exe)?\s+-/i.test(command)) {
|
||
failures.push(`Aktiver npm-Script ${name} enthält weiterhin eine Python-Abhängigkeit: ${command}`);
|
||
}
|
||
}
|
||
for (const requiredNodeGate of [
|
||
'tools/verify-identity-db-1.40.0.mjs',
|
||
'tools/verify-db-migrations.mjs',
|
||
'tools/verify-production-operations-db-1.42.0.mjs',
|
||
]) {
|
||
if (!existsSync(resolve(root, requiredNodeGate))) failures.push(`Node-only-DB-Gate fehlt: ${requiredNodeGate}`);
|
||
}
|
||
if (!workerText.includes('StandardOutputEncoding') || !workerText.includes('StandardErrorEncoding')) {
|
||
failures.push('Worker setzt die UTF-8-Dekodierung für externe Prozesse nicht explizit.');
|
||
}
|
||
for (const utf8Marker of ["NPM_CONFIG_COLOR']='false", "FORCE_COLOR']='0", "Text.UTF8Encoding($false)"]) {
|
||
if (!workerText.includes(utf8Marker)) failures.push(`UTF-8-/Ausgabevertrag fehlt: ${utf8Marker}`);
|
||
}
|
||
|
||
|
||
const cleanupHelper = readFileSync(resolve(root, 'tools/test-runtime-cleanup.mjs'), 'utf8');
|
||
for (const marker of ["db.close()", "wal_checkpoint(TRUNCATE)", "maxRetries = 30", "retryDelay = 100"]) {
|
||
if (!cleanupHelper.includes(marker)) failures.push(`Zentraler Windows-SQLite-Cleanup-Vertrag fehlt: ${marker}`);
|
||
}
|
||
for (const requiredCleanupGate of [
|
||
'tools/verify-sqlite-cleanup-contract-1.43.0.mjs',
|
||
'tools/verify-windows-sqlite-cleanup-regression-1.43.0.mjs',
|
||
]) {
|
||
if (!existsSync(resolve(root, requiredCleanupGate))) failures.push(`SQLite-Cleanup-Pflichtgate fehlt: ${requiredCleanupGate}`);
|
||
}
|
||
for (const rel of ['tools/verify-initial-disabled-modules-1.41.2.mjs', 'tools/verify-controlled-invitations-1.42.0.mjs', 'tools/verify-capability-graph-1.41.2.mjs', 'tools/verify-platform-architecture-1.41.2.mjs']) {
|
||
const cleanupSource = readFileSync(resolve(root, rel), 'utf8');
|
||
for (const marker of ['closeVendooDatabase', 'removeTempDirectory']) {
|
||
if (!cleanupSource.includes(marker)) failures.push(`${rel} verwendet den zentralen SQLite-Cleanup nicht: ${marker}`);
|
||
}
|
||
}
|
||
if (!packageJson.scripts?.['verify:sqlite-cleanup'] || !packageJson.scripts['verify:all'].includes('verify:sqlite-cleanup')) {
|
||
failures.push('verify:sqlite-cleanup ist nicht verbindlich in verify:all verdrahtet.');
|
||
}
|
||
|
||
for (const forbidden of ['COOLIFY_DEPLOY_WEBHOOK_URL', 'Request-Secret', "'secret','set'", "'workflow','run'"]) {
|
||
if (workerText.includes(forbidden)) failures.push(`Verbotener Webhook-/Workflow-Rest im Updater: ${forbidden}`);
|
||
}
|
||
if (failures.length) {
|
||
console.error(`Production-Updater-2.9-Binary-Gate fehlgeschlagen (${failures.length})`);
|
||
for (const failure of failures) console.error(`- ${failure}`);
|
||
process.exit(1);
|
||
}
|
||
console.log('OK: Production Updater 2.9 – BAT ist reines ASCII, BOM-frei und vollständig CRLF-normalisiert.');
|