#!/usr/bin/env node /** * Synchronisiert Kursstruktur und generierte Übungen nach Kursänderungen. * * Dieses Script ist für CI/CD gedacht und darf keinen Lernfortschritt löschen. * Es führt bewusst nicht apply-bisaya-course-refresh.js aus, weil dieses Script * Lektions- und Übungsfortschritte zurücksetzt. * * Verwendung: * node backend/scripts/sync-vocab-course-content.js * node backend/scripts/sync-vocab-course-content.js --scope=bisaya * node backend/scripts/sync-vocab-course-content.js --scope=german-for-bisaya * VOCAB_COURSE_SYNC_DRY_RUN=1 node backend/scripts/sync-vocab-course-content.js */ import { spawn } from 'child_process'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const repoRoot = path.resolve(__dirname, '../..'); const SAFE_SYNC_STEPS = { bisaya: [ 'backend/scripts/migrate-bisaya-zahlen-split.js', 'backend/scripts/update-bisaya-didactics.js', 'backend/scripts/extend-bisaya-course-phase3.js', 'backend/scripts/extend-bisaya-course-phase4.js', 'backend/scripts/extend-bisaya-course-phase5.js', 'backend/scripts/create-bisaya-course-content.js' ], 'german-for-bisaya': [ 'backend/scripts/extend-german-for-bisaya-course-phase3.js', 'backend/scripts/extend-german-for-bisaya-course-phase4.js', 'backend/scripts/extend-german-for-bisaya-course-phase5.js', 'backend/scripts/create-german-for-bisaya-course-content.js' ] }; function parseScopes() { const scopeArg = process.argv.find((arg) => arg.startsWith('--scope=')); const rawScope = scopeArg?.slice('--scope='.length) || process.env.VOCAB_COURSE_SYNC_SCOPE || 'all'; const requested = rawScope .split(',') .map((entry) => entry.trim()) .filter(Boolean); if (requested.length === 0 || requested.includes('all')) { return Object.keys(SAFE_SYNC_STEPS); } const unknownScopes = requested.filter((scope) => !SAFE_SYNC_STEPS[scope]); if (unknownScopes.length > 0) { throw new Error(`Unbekannter Kurs-Sync-Scope: ${unknownScopes.join(', ')}`); } return requested; } function runNodeScript(scriptPath) { return new Promise((resolve, reject) => { const child = spawn(process.execPath, [scriptPath], { cwd: repoRoot, env: process.env, stdio: 'inherit' }); child.on('error', reject); child.on('exit', (code, signal) => { if (code === 0) { resolve(); return; } reject(new Error(`${scriptPath} beendet mit ${signal || `Exit-Code ${code}`}`)); }); }); } async function main() { const scopes = parseScopes(); const dryRun = process.env.VOCAB_COURSE_SYNC_DRY_RUN === '1' || process.argv.includes('--dry-run'); const steps = scopes.flatMap((scope) => SAFE_SYNC_STEPS[scope]); console.log('Vokabelkurs-Sync'); console.log(`Scopes: ${scopes.join(', ')}`); console.log(`Schritte: ${steps.length}`); for (const step of steps) { console.log(`\n→ ${step}`); if (dryRun) continue; await runNodeScript(step); } if (dryRun) { console.log('\nDry-Run abgeschlossen. Es wurden keine Scripts ausgeführt.'); } else { console.log('\nVokabelkurs-Sync abgeschlossen.'); } } main().catch((error) => { console.error('Vokabelkurs-Sync fehlgeschlagen:', error); process.exit(1); });