Files
vendoo/tools/verify-production-operations-1.42.0.mjs
Masterluke77andGitHub 40071f718e Vendoo 1.43.0 – Production Update
Manifestbasierter Production-Updater 3.3, Modulnavigation, optionales Produktbild Studio, Listing-Studio-Aktionsleiste sowie plattformübergreifende Windows-/Linux-Release-Gates.
2026-07-10 13:16:21 +02:00

91 lines
5.2 KiB
JavaScript

import { readFileSync } from 'node:fs';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import {
DEPLOYMENT_STATES,
ACTIVE_DEPLOYMENT_STATES,
canTransitionDeployment,
isActiveDeploymentState,
} from '../app/modules/deployments/state-machine.mjs';
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const read = path => readFileSync(join(root, path), 'utf8');
const failures = [];
const assert = (condition, message) => { if (!condition) failures.push(message); };
const contains = (file, markers) => {
const source = read(file);
for (const marker of markers) assert(source.includes(marker), `${file}: Vertrag fehlt: ${marker}`);
return source;
};
const pkg = JSON.parse(read('package.json'));
assert(pkg.version === '1.43.0', `package.json ist ${pkg.version}`);
assert(pkg.scripts['verify:production-operations']?.includes('verify-production-operations-1.42.0.mjs'), 'NPM-Gate für Production Operations fehlt');
assert(DEPLOYMENT_STATES.length === 10, `State-Machine hat ${DEPLOYMENT_STATES.length} statt 10 Zustände`);
for (const state of ['checking','backup_pending','backup_running','backup_verified','deploy_requested','deploy_running','health_wait','succeeded','failed','rollback_required']) {
assert(DEPLOYMENT_STATES.includes(state), `Zustand fehlt: ${state}`);
}
for (const [from, to] of [
['checking','backup_pending'], ['backup_running','backup_verified'], ['backup_verified','deploy_requested'],
['deploy_requested','deploy_running'], ['deploy_running','health_wait'], ['health_wait','succeeded'],
['health_wait','rollback_required'], ['rollback_required','failed'],
]) assert(canTransitionDeployment(from, to), `Legaler Übergang blockiert: ${from} -> ${to}`);
for (const [from, to] of [['checking','succeeded'], ['succeeded','deploy_requested'], ['failed','checking'], ['rollback_required','succeeded']]) {
assert(!canTransitionDeployment(from, to), `Illegaler Übergang erlaubt: ${from} -> ${to}`);
}
assert(ACTIVE_DEPLOYMENT_STATES.includes('rollback_required'), 'Rollbackbedarf blockiert keine Folgeaufträge');
assert(isActiveDeploymentState('health_wait') && !isActiveDeploymentState('succeeded'), 'Aktivstatus ist fehlerhaft');
contains('lib/db.mjs', [
'CREATE TABLE IF NOT EXISTS deployment_runs', 'idempotency_key TEXT NOT NULL UNIQUE',
'CREATE TABLE IF NOT EXISTS deployment_events', 'CREATE TABLE IF NOT EXISTS backup_verifications',
'CREATE TABLE IF NOT EXISTS production_checks', "production-operations-1.42.0",
]);
const repository = contains('app/modules/deployments/repository.mjs', [
'DEPLOYMENT_ALREADY_ACTIVE', 'findByIdempotencyKey', 'recordBackupVerification',
'createProductionCheck', 'canTransitionDeployment',
]);
assert(!repository.includes('DELETE FROM deployment_runs'), 'Deployment-Repository löscht Verlauf');
const service = contains('app/modules/deployments/service.mjs', [
"input.confirmation !== 'DEPLOY'", "input.confirmation !== 'CONFIRM'",
'verifyStoredBackup(backup.id)', 'DEPLOYMENT_BACKUP_INVALID', 'DEPLOYMENT_TARGET_NOT_APPROVED',
'UPDATE_CHECKSUM_MISSING', 'UPDATE_URL_INSECURE', 'deployment_status_endpoint',
'probePublicReadiness', 'targetMatches', 'rollback_required', 'runProductionCheck',
'VENDOO_COOLIFY_AUTO_DEPLOY', 'docker_socket_access: false',
]);
for (const forbidden of ['docker.sock', 'git pull', 'npm install', 'docker compose', 'child_process']) {
assert(!service.toLowerCase().includes(forbidden), `Deployment-Service enthält verbotenen Selbstupdate-/Hostzugriff: ${forbidden}`);
}
contains('app/modules/deployments/routes.mjs', [
"policy: 'updates.manage'", "csrf: true", '/api/admin/deployment/runs',
'/api/admin/deployment/runs/:id/refresh', '/api/admin/deployment/runs/:id/confirm',
'/api/admin/deployment/production-check', "req.get('Idempotency-Key')",
]);
contains('server.mjs', ['verifyStoredBackup,', 'getPlatformSnapshot: () => platformKernel.snapshot()', 'registerDeploymentRoutes']);
contains('public/index.html', [
'deployment-production-overview', 'deployment-production-check', 'deployment-status-endpoint',
'deployment-target-version', 'deployment-run-list', 'content="1.43.0"',
]);
contains('public/app.js', [
"const VENDOO_UI_BUILD = '1.43.0';", 'deploymentStateLabel', 'data-deployment-refresh',
'data-deployment-confirm', 'data-deployment-resolve', 'idempotency_key',
]);
contains('.env.example', [
'COOLIFY_DEPLOYMENT_STATUS_ENDPOINT=', 'VENDOO_DEPLOY_TIMEOUT_SECONDS=900',
'VENDOO_DEPLOY_HEALTH_POLL_SECONDS=15', 'VENDOO_COOLIFY_AUTO_DEPLOY=false',
]);
const moduleManifest = JSON.parse(read('app/modules/deployments/module.json'));
assert(moduleManifest.version === '1.1.0', `Deployment-Modulversion ist ${moduleManifest.version}`);
assert(moduleManifest.provides.includes('deployments.state-machine'), 'State-Machine-Capability fehlt');
assert(moduleManifest.provides.includes('deployments.production-checks'), 'Production-Check-Capability fehlt');
if (failures.length) {
console.error(`Production-Operations-Gate 1.42.0 fehlgeschlagen (${failures.length}):`);
for (const failure of failures) console.error(`- ${failure}`);
process.exit(1);
}
console.log('Production-Operations-Gate 1.42.0 erfolgreich: State-Machine, Idempotenz, Backup-Gate, Coolify-Status, Readiness, Rollback-Sperre, Produktionscheck, API und UI geprüft.');