import { existsSync, lstatSync, readFileSync, readdirSync } from 'node:fs'; import { dirname, join, relative, sep } from 'node:path'; import { fileURLToPath } from 'node:url'; const root = dirname(dirname(fileURLToPath(import.meta.url))); const read = rel => readFileSync(join(root, rel), 'utf8'); const failures = []; const expect = (text, marker, label = marker) => { if (!text.includes(marker)) failures.push(`Fehlt: ${label}`); }; const forbid = (text, marker, label = marker) => { if (text.includes(marker)) failures.push(`Darf nicht enthalten sein: ${label}`); }; const pkg = JSON.parse(read('package.json')); const lock = JSON.parse(read('package-lock.json')); const manifest = JSON.parse(read('update-manifest.json')); const html = read('public/index.html'); const app = read('public/app.js'); const bootstrap = read('bootstrap.mjs'); const configStore = read('lib/config-store.mjs'); const runtimePaths = read('lib/runtime-paths.mjs'); const setupPs = read('setup.ps1'); const setupGui = read('setup-gui.ps1'); const setupBat = read('setup.bat'); const setupSh = read('setup.sh'); const compose = read('compose.yaml'); const composeVps = read('compose.vps.yaml'); const caddy = read('deploy/Caddyfile'); const env = read('.env.example'); const gitignore = read('.gitignore'); const ci = read('.github/workflows/ci.yml'); const release = read('.github/workflows/release.yml'); const gitDeploy = read('scripts/github-deploy.ps1'); const gitSafety = read('tools/verify-git-safety.mjs'); const gitSafetyRegression = read('tools/verify-git-safety-regression.mjs'); const releaseBuilder = read('tools/build-release.mjs'); const installerShellGate = read('tools/verify-installer-shell.sh'); const backupScript = read('scripts/docker-backup.sh'); const restoreScript = read('scripts/docker-restore.sh'); const gitStaging = read('tools/prepare-git-staging.mjs'); const gitStagingRegression = read('tools/verify-git-staging.mjs'); const architectureGate = read('tools/verify-platform-architecture-1.40.2.mjs'); const themeGate = read('tools/verify-theme-manager-1.40.0.mjs'); const securityGate = read('tools/verify-security-foundation-1.40.0.mjs'); const identityGate = read('tools/verify-identity-settings-1.40.0.mjs'); const catalogGate = read('tools/verify-catalog-media-1.40.0.mjs'); const moduleGate = read('tools/verify-module-manager-1.40.0.mjs'); const esmExportGate = read('tools/verify-esm-export-contracts.mjs'); const fluxHotfixGate = read('tools/verify-flux-loop-hotfix-1.40.2.mjs'); const capabilityGraphGate = read('tools/verify-capability-graph-1.40.2.mjs'); const moduleManager = read('public/modules/module-manager/module-manager.js'); const moduleService = read('app/modules/module-manager/service.mjs'); const themeManager = read('public/modules/theme-manager/theme-manager.js'); const tokenSource = read('public/design-system/tokens/source.json'); const tokenCss = read('public/design-system/tokens.css'); const moduleSchema = read('app/contracts/module.schema.json'); const boundaries = read('app/architecture-boundaries.json'); for (const [label, value] of [ ['package.json', pkg.version], ['package-lock.json', lock.version], ['package-lock root', lock.packages?.['']?.version], ['update-manifest', manifest.version], ]) if (value !== '1.40.2') failures.push(`${label}-Version ist ${value}`); for (const marker of ['content="1.40.2"', 'style.css?v=1.40.2', 'app.js?v=1.40.2']) expect(html, marker, `UI-Build ${marker}`); if (!app.startsWith("const VENDOO_UI_BUILD = '1.40.2';")) failures.push('UI-Build ist nicht 1.40.2'); for (const marker of ['VENDOO_CONFIG_PATH', "await import('./server.mjs')", 'override: true']) expect(bootstrap, marker, `Bootstrap ${marker}`); for (const marker of ['CONFIG_PATH', 'writeRuntimeConfig', 'updateRuntimeConfig', 'mode: 0o600']) expect(configStore, marker, `Konfigurationsspeicher ${marker}`); for (const marker of ['VENDOO_CONFIG_PATH', 'runtime_config: CONFIG_PATH', 'VENDOO_MODULES_DIR', 'module_state: MODULE_STATE_PATH']) expect(runtimePaths, marker, `Runtime-Pfade ${marker}`); for (const marker of ["setSecret('LOCAL_IMAGE_TOKEN', token, { source: 'token-regeneration' })", 'runtime_config_path: CONFIG_PATH']) expect(read('server.mjs'), marker, `Server-Konfiguration ${marker}`); for (const marker of ['readRuntimeConfig()', 'writeRuntimeConfig(envContent)', 'ALL_FIELDS', 'Unbekanntes Einstellungsfeld']) expect(read('app/modules/settings/service.mjs'), marker, `Settings-Modul ${marker}`); forbid(read('server.mjs'), "join(__dirname, '.env')", 'direktes Schreiben in schreibgeschütztes App-.env'); for (const marker of ["$Version = '1.40.2'", '?build=1.40.2', "'install-local'", "'install-docker'", 'Configure-VendooConsole', 'Initialize-VendooEnvironment']) expect(setupPs, marker, `Windows-Setup ${marker}`); for (const marker of ["foreach ($name in @('uploads','config','modules'))", "module-state\\modules-state.json", 'Modulpakete und Modulzustand wiederhergestellt']) expect(setupPs, marker, `Windows-Modul-Backup ${marker}`); for (const marker of [ 'Vendoo 1.40.2 – Installation', 'Lokal auf diesem Windows-PC', 'Docker Desktop auf diesem Windows-PC', 'Set-Configuration', 'VENDOO_SETUP_TOKEN', 'Clipboard', 'AI- und Marktplatz-Schlüssel werden später', ]) expect(setupGui, marker, `Windows-GUI ${marker}`); forbid(setupGui, 'PlaceholderText', 'nicht mit Windows PowerShell 5.1 kompatible PlaceholderText-Eigenschaft'); for (const marker of ['setup-gui.ps1', '-STA', 'Parser]::ParseFile']) expect(setupBat, marker, `setup.bat ${marker}`); for (const marker of [ 'Zero-Edit Deployment Assistant', 'configure_wizard', 'VPS / öffentlicher Server mit automatischem HTTPS', 'VENDOO_DOMAIN', 'compose.vps.yaml', 'Caddy übernimmt HTTPS automatisch', 'keine Datei musste manuell bearbeitet werden', 'Aktualisieren (mit Sicherheitsbackup)', 'docker-backup.sh', ]) expect(setupSh, marker, `Linux-Assistent ${marker}`); for (const marker of ['VENDOO_CONFIG_PATH: /app/data/config/vendoo.env', 'vendoo_config:/app/data/config', 'vendoo_modules:/app/data/modules']) expect(compose, marker, `Basis-Compose-Konfiguration ${marker}`); for (const marker of ['caddy:2-alpine', '80:80', '443:443', './deploy/Caddyfile', 'condition: service_healthy', 'read_only: true', 'vendoo_config:/app/data/config', 'vendoo_modules:/app/data/modules']) expect(composeVps, marker, `VPS-Compose ${marker}`); for (const marker of ['{$VENDOO_DOMAIN}', 'reverse_proxy vendoo:8124', 'Strict-Transport-Security']) expect(caddy, marker, `Caddy ${marker}`); for (const marker of ['VENDOO_DOMAIN=', 'VENDOO_SETUP_TOKEN=', 'VENDOO_COOKIE_SECURE=', 'LOCAL_IMAGE_ENABLED=false']) expect(env, marker, `.env.example ${marker}`); for (const marker of ['name: Vendoo CI', 'npm ci', 'npm run verify:git', 'npm run verify:git-regression', 'npm run verify:git-staging', 'npm run verify:esm-exports', 'npm run verify:flux-loop', 'npm run verify:capabilities', 'npm run verify:architecture', 'npm run verify:themes', 'npm run verify:modules', 'npm run verify:deployment', 'npm run verify:config', 'npm run verify:installer-shell', 'docker build --pull', 'compose.vps.yaml']) expect(ci, marker, `CI ${marker}`); for (const marker of ['name: Vendoo Release', 'Release-Tag gegen Paketversion prüfen', 'npm run verify:git-staging', 'npm run verify:esm-exports', 'npm run verify:flux-loop', 'npm run verify:capabilities', 'npm run verify:architecture', 'npm run verify:themes', 'npm run verify:modules', 'npm run verify:config', 'npm run verify:installer-shell', 'release-output', 'gh release upload', 'ghcr.io', 'docker/build-push-action@v6', 'actions/attest@v4', 'attestations: write', 'id-token: write', 'steps.push.outputs.digest', 'linux/amd64,linux/arm64']) expect(release, marker, `Release-Workflow ${marker}`); for (const marker of ['release/1.40.2-capability-graph-hotfix', 'verify-git-safety.mjs', 'prepare-git-staging.mjs', 'RedirectStandardOutput', 'github-deploy-safety-report.txt', 'Zum Fortfahren PUSH eingeben', 'vendoo/pull/15']) expect(gitDeploy, marker, `GitHub-Deploy ${marker}`); for (const marker of ['fileURLToPath', 'kompletten Arbeitsbaum', 'forbiddenPathPatterns', 'secretPatterns', 'Git-Sicherheitsgate erfolgreich']) expect(gitSafety, marker, `Git-Sicherheitsgate ${marker}`); for (const marker of ['unversioniert', '.env.local', 'untracked-secret.txt', 'Git-Sicherheitsgate-Regressionstest erfolgreich']) expect(gitSafetyRegression, marker, `Git-Sicherheitsgate-Regression ${marker}`); for (const marker of ['prepareGitStaging', 'shouldSkipStagingEntry', 'allowedEnvTemplates', 'skippedTopLevelDirs', 'operations', 'updates', 'Ziel ist kein geklontes Git-Repository']) expect(gitStaging, marker, `Git-Staging ${marker}`); for (const marker of ['.env.local', 'operations/staging/job.json', 'updates/backups/slice22_1_20260707-194953/setup.ps1', '.git/marker', 'Git-Staging-Regressionstest erfolgreich']) expect(gitStagingRegression, marker, `Git-Staging-Regression ${marker}`); for (const marker of ['release-output', 'FILE_CHECKSUMS_SHA256.txt', 'createHash', 'shouldSkipStagingEntry']) expect(releaseBuilder, marker, `Release-Builder ${marker}`); for (const marker of ['run_mode docker', 'run_mode nas', 'run_mode vps', 'VENDOO_SETUP_TOKEN', 'stat -c']) expect(installerShellGate, marker, `Shell-Installer-Gate ${marker}`); for (const marker of ['db uploads backups operations logs config modules', 'vendoo-data.tar.gz']) expect(backupScript, marker, `Docker-Backup ${marker}`); for (const marker of ['db uploads backups operations logs config modules', '--confirm', 'docker-backup.sh']) expect(restoreScript, marker, `Docker-Restore ${marker}`); for (const marker of ['.env', 'runtime/', 'modules/', 'updates/', 'local-ai/comfyui/', '.vendoo-install-mode', 'release-output/']) expect(gitignore, marker, `.gitignore ${marker}`); for (const marker of ['createPlatformKernel', "id: 'platform.status.read'", "policy: 'platform.security-admin'", 'await platformKernel.stop()']) expect(read('server.mjs'), marker, `Platform-Kernel ${marker}`); for (const marker of ['Capability-Graph-Gate 1.40.2 erfolgreich', 'settings.configuration', 'validateGraph']) expect(capabilityGraphGate, marker, `Capability-Graph-Gate ${marker}`); for (const marker of ['Platform-Architecture-Gate 1.40.2 erfolgreich', 'MODULE_DEPENDENCY_CYCLE', 'MODULE_DISABLED']) expect(architectureGate, marker, `Architektur-Gate ${marker}`); for (const marker of ['Theme-Manager-Gate 1.40.2 erfolgreich', 'THEME_VALUE_INVALID', 'theme_profiles', 'user_theme_preferences']) expect(themeGate, marker, `Theme-Gate ${marker}`); for (const marker of ['Security-Foundation-Gate 1.40.2 erfolgreich', 'SECRET_VALUE_INVALID', 'requestContextMiddleware']) expect(securityGate, marker, `Security-Gate ${marker}`); for (const marker of ['Identity-&-Settings-Gate 1.40.2 erfolgreich', 'SESSION_NOT_OWNED', 'Unbekanntes Einstellungsfeld']) expect(identityGate, marker, `Identity-/Settings-Gate ${marker}`); for (const marker of ['Catalog-&-Media-Gate 1.40.2 erfolgreich', 'LISTING_TRASH_NOT_FOUND', 'protectedPaths']) expect(catalogGate, marker, `Catalog-/Media-Gate ${marker}`); for (const marker of ['Module-Manager-Gate 1.40.2 erfolgreich', 'MODULE_PROTECTED', 'MODULE_NOT_ACTIVATABLE', '.vmod-Import/Export']) expect(moduleGate, marker, `Modulmanager-Gate ${marker}`); for (const marker of ['ESM-Export-Vertragsprüfung erfolgreich', 'collectExportsFromFile', 'importPattern']) expect(esmExportGate, marker, `ESM-Export-Gate ${marker}`); for (const marker of ['FLUX-Loop-Hotfix-Gate 1.40.2 erfolgreich', 'stopFluxPromptJobLoop', 'verify:esm-exports']) expect(fluxHotfixGate, marker, `FLUX-Hotfix-Gate ${marker}`); for (const marker of ['/api/admin/modules', 'dataset.moduleAction', 'module-manager-file', 'Deklarative Module sind aktivierbar']) expect(moduleManager, marker, `Modulmanager UI ${marker}`); for (const marker of ['PROTECTED_MODULE_IDS', 'builtin-reference', 'MODULE_DISABLE_BEFORE_REMOVE', 'MODULE_EXTENSION_HOST_UNAVAILABLE']) expect(moduleService, marker, `Modulmanager Service ${marker}`); for (const marker of ['/api/themes/preference', '/api/themes/system-default', 'previewTheme']) expect(themeManager, marker, `Theme Manager ${marker}`); for (const marker of ['contractVersion', 'color.brand', 'density.controlHeight', 'motion.normal']) expect(tokenSource, marker, `Tokenquelle ${marker}`); for (const marker of [':root {', '[data-theme="dark"]', '[data-theme="contrast"]', '--vd-brand:']) expect(tokenCss, marker, `Token-CSS ${marker}`); for (const marker of ['Vendoo Module Manifest', 'legacy-bridge', 'extension-host']) expect(moduleSchema, marker, `Modulschema ${marker}`); for (const marker of ['modular-monolith', 'denyImplicitRoutes', 'thirdPartyExtensionsInMainProcess', 'strangler']) expect(boundaries, marker, `Architekturgrenzen ${marker}`); for (const required of ['app/kernel/platform-kernel.mjs', 'app/kernel/module-registry.mjs', 'app/kernel/route-registry.mjs', 'app/kernel/policy-engine.mjs', 'app/kernel/input-schema.mjs', 'app/core/themes/theme-contract.mjs', 'app/core/security/secret-contract.mjs', 'app/modules/security/index.mjs', 'lib/secret-store.mjs', 'lib/structured-logger.mjs', 'public/design-system/theme-runtime.js', 'public/design-system/theme-contract.json', 'public/core/frontend-module-registry.js', 'public/core/safe-dom.js', 'public/core/api-client.js', 'docs/architecture/MODULE_SYSTEM_1_35_0.md', 'docs/architecture/DESIGN_SYSTEM_1_35_0.md', 'docs/architecture/SECURITY_ARCHITECTURE_1_35_0.md', 'docs/architecture/THEME_MANAGER_1_36_0.md', 'docs/architecture/MODULE_MANAGER_1_40_0.md', 'app/core/modules/module-package-contract.mjs', 'app/core/modules/module-package-store.mjs', 'app/core/modules/module-state-store.mjs', 'app/modules/module-manager/routes.mjs', 'public/modules/module-manager/module-manager.js']) if (!existsSync(join(root, required))) failures.push(`Architektur-Pflichtdatei fehlt: ${required}`); for (const marker of ['read_only: true', 'no-new-privileges:true', 'cap_drop:', '/readyz', 'host.docker.internal:host-gateway']) expect(compose, marker, `Basis-Compose ${marker}`); for (const required of [ '.github/workflows/ci.yml', '.github/workflows/release.yml', '.github/dependabot.yml', '.github/CODEOWNERS', 'GitHub-Deploy-Assistent.bat', 'scripts/github-deploy.ps1', 'scripts/github-deploy.sh', 'setup-gui.ps1', 'bootstrap.mjs', 'lib/config-store.mjs', 'compose.vps.yaml', 'deploy/Caddyfile', 'tools/verify-git-safety.mjs', 'tools/verify-git-safety-regression.mjs', 'tools/prepare-git-staging.mjs', 'tools/verify-git-staging.mjs', 'tools/verify-installer-shell.sh', 'tools/build-release.mjs', 'tools/verify-esm-export-contracts.mjs', 'tools/verify-flux-loop-hotfix-1.40.2.mjs', ]) if (!existsSync(join(root, required))) failures.push(`Pflichtdatei fehlt: ${required}`); function walk(dir, out = []) { for (const name of readdirSync(dir)) { if (name === 'node_modules' || name === 'release-output' || name === '.git') continue; const abs = join(dir, name); const rel = relative(root, abs).split(sep).join('/'); const stat = lstatSync(abs); if (stat.isDirectory()) walk(abs, out); else out.push(rel); } return out; } const files = walk(root); const forbiddenRuntime = files.filter(rel => rel === '.env' || rel === '.vendoo-install-mode' || /(^|\/)(runtime|node_modules|uploads|backups|logs|updates)(\/|$)/i.test(rel) || /^modules\//i.test(rel) || /^db\/.*\.(db|sqlite|sqlite3)(-wal|-shm)?$/i.test(rel) || /^local-ai\/(comfyui|downloads|extract-temp)\//i.test(rel) ); if (forbiddenRuntime.length) failures.push(`Verbotene Laufzeitdaten im Paket: ${forbiddenRuntime.slice(0, 10).join(', ')}`); forbid(read('README.md'), '.env.example` nach `.env` kopieren', 'alte manuelle .env-Anweisung im README'); if (failures.length) { console.error(`Deployment-Gate 1.40.2 fehlgeschlagen (${failures.length}):`); failures.forEach(item => console.error(`- ${item}`)); process.exit(1); } console.log('Deployment-Gate 1.40.2 erfolgreich: Capability-Graph-Hotfix, FLUX-Lifecycle, ESM-Exportverträge, 14 native Module, sicherer Modulmanager, Zero-Edit-Installer, CI und Release-Workflow geprüft.');