Files
vendoo/tools/prepare-git-staging.mjs
T
Masterluke77andGitHub 6f48827f4d Vendoo 1.41.2 – Git Ignore Boundary & Module Tracking Hotfix (#18)
* docs: prepare Vendoo 1.41.2 git-ignore boundary hotfix

* fix: track native source modules with root-anchored runtime ignores
2026-07-09 17:09:00 +02:00

109 lines
4.1 KiB
JavaScript

import {
chmodSync,
copyFileSync,
existsSync,
lstatSync,
mkdirSync,
readdirSync,
rmSync,
} from 'node:fs';
import { basename, dirname, extname, join, relative, resolve, sep } from 'node:path';
import { fileURLToPath } from 'node:url';
const normalize = value => value.split(sep).join('/').replace(/^\.\//, '');
const allowedEnvTemplates = new Set(['.env.example', '.env.docker.example']);
const skippedTopLevelDirs = new Set([
'node_modules', 'runtime', 'docker-data', 'uploads', 'backups', 'logs',
'sessions', 'tmp', 'temp', '__pycache__', 'release-output', 'operations', 'updates', 'modules',
'archiv', 'archive', 'archives',
]);
const skippedLocalAiDirs = new Set(['local-ai/comfyui', 'local-ai/downloads', 'local-ai/extract-temp']);
const skippedExactFiles = new Set([
'.vendoo-install-mode', '.vendoo-github-state.json',
'pending-operation.json', 'last-operation.json', 'first-admin-code.txt',
]);
const skippedExtensions = new Set([
'.db', '.sqlite', '.sqlite3', '.log', '.key', '.pem', '.pfx', '.p12',
]);
export function shouldSkipStagingEntry(relativePath, stat) {
const rel = normalize(relativePath);
const parts = rel.split('/');
const name = basename(rel);
const lower = rel.toLowerCase();
if (!rel || rel === '.git' || lower.startsWith('.git/')) return true;
if (skippedTopLevelDirs.has(parts[0].toLowerCase())) return true;
if (parts.some(part => part.toLowerCase() === '__pycache__') || /\.pyc$/i.test(rel)) return true;
if ([...skippedLocalAiDirs].some(dir => lower === dir || lower.startsWith(`${dir}/`))) return true;
if (/^\.env(?:\..+)?$/i.test(name) && !allowedEnvTemplates.has(rel)) return true;
if (skippedExactFiles.has(name.toLowerCase())) return true;
if (/\.(?:db|sqlite|sqlite3)-(?:wal|shm)$/i.test(name)) return true;
if (skippedExtensions.has(extname(name).toLowerCase())) return true;
if (stat.isSymbolicLink()) return true;
return false;
}
function cleanDestination(destination) {
if (!existsSync(join(destination, '.git'))) {
throw new Error(`Ziel ist kein geklontes Git-Repository: ${destination}`);
}
for (const name of readdirSync(destination)) {
if (name === '.git') continue;
rmSync(join(destination, name), { recursive: true, force: true });
}
}
export function prepareGitStaging(sourceRoot, destinationRoot) {
const source = resolve(sourceRoot);
const destination = resolve(destinationRoot);
if (source === destination) throw new Error('Quell- und Zielordner dürfen nicht identisch sein.');
if (!existsSync(source)) throw new Error(`Quellordner fehlt: ${source}`);
if (!existsSync(destination)) throw new Error(`Zielordner fehlt: ${destination}`);
cleanDestination(destination);
let copied = 0;
let skipped = 0;
function walk(current) {
for (const name of readdirSync(current)) {
const absolute = join(current, name);
const rel = normalize(relative(source, absolute));
const stat = lstatSync(absolute);
if (shouldSkipStagingEntry(rel, stat)) {
skipped += 1;
continue;
}
const target = join(destination, rel);
if (stat.isDirectory()) {
mkdirSync(target, { recursive: true });
walk(absolute);
} else if (stat.isFile()) {
mkdirSync(dirname(target), { recursive: true });
copyFileSync(absolute, target);
try { chmodSync(target, stat.mode & 0o777); } catch {}
copied += 1;
}
}
}
walk(source);
return { source, destination, copied, skipped };
}
const invokedPath = process.argv[1] ? resolve(process.argv[1]) : '';
if (invokedPath === fileURLToPath(import.meta.url)) {
const [, , source, destination] = process.argv;
if (!source || !destination) {
console.error('Aufruf: node tools/prepare-git-staging.mjs <Quellordner> <geklontes Zielrepository>');
process.exit(64);
}
try {
const result = prepareGitStaging(source, destination);
console.log(`Git-Staging vorbereitet: ${result.copied} Quelldateien kopiert; ${result.skipped} lokale Einträge ausgeschlossen.`);
} catch (error) {
console.error(`[FEHLER] Git-Staging konnte nicht vorbereitet werden: ${error.message}`);
process.exit(1);
}
}