Ragnarok rodecomp deployment on memento

Changes to upstream:
- git.js: coordinator commit identity from TD_GIT_AUTHOR_NAME/EMAIL.
- build.js: docker runner can share job dirs through a named volume
  (jobVolume/jobVolumeRoot) for containerized workers.
- install.js: staged installer output with logs, start prompt for regions.
- web: feed layout, cache-busting asset versions, multi-agent help section.
- build-td: fetch checksummed Windows runtime when building on non-Windows.

Additions:
- deploy/rodecomp: compose stack, Dockerfiles, Caddyfile, server.json,
  bulk import/verify scripts, and README.md with the deployment notes
  (the root README stays identical to upstream).
- toolchain/msvc-v142: MSVC v142 under Wine (Docker), SDK 10.0.19041.0.
- tools/rodecomp: project preparation, Forgejo provisioning, history and
  identity tools.
This commit is contained in:
clank-bot 2026-10-25 14:38:52 +01:00
commit 022e0a2c78
39 changed files with 1232 additions and 33 deletions

View file

@ -0,0 +1,43 @@
const fs = require('node:fs');
const path = require('node:path');
const { DatabaseSync } = require('node:sqlite');
const { execFileSync } = require('node:child_process');
const crypto = require('node:crypto');
const root = '/var/lib/docker/volumes/rodecomp_data/_data';
const repo = path.join(root, 'repo');
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
const destination = path.join(root, 'admin-backups', `readable-source-${stamp}`);
fs.mkdirSync(destination, { recursive: true });
const git = (...args) => execFileSync('git', args, { cwd: repo, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 }).trim();
if (git('status', '--porcelain')) throw new Error('Canonical repository is dirty; no import performed');
const db = new DatabaseSync(path.join(root, 'td.sqlite'));
db.exec('PRAGMA busy_timeout=15000');
const running = db.prepare("SELECT COUNT(*) n FROM check_jobs WHERE state IN ('queued','running')").get().n;
const verifying = db.prepare("SELECT COUNT(*) n FROM batches WHERE state IN ('queued','verifying','merging')").get().n;
if (running || verifying) throw new Error('Live verification/merge jobs exist; no import performed');
const before = {
stamp,
head: git('rev-parse', 'HEAD'),
tasks: db.prepare('SELECT state,count(*) n FROM tasks GROUP BY state').all(),
attempts: db.prepare('SELECT state,kind,count(*) n FROM attempts GROUP BY state,kind').all(),
batchCounts: db.prepare('SELECT state,count(*) n FROM batches GROUP BY state').all(),
schemas: db.prepare("SELECT name,sql FROM sqlite_master WHERE type IN ('table','trigger')").all(),
funcStatus: db.prepare('SELECT * FROM func_status').all(),
symbols: db.prepare('SELECT id,unit,decorated,name,library FROM symbols').all(),
tasksFull: db.prepare('SELECT * FROM tasks').all(),
reviews: db.prepare('SELECT count(*) n FROM peer_reviews').get().n,
humanReviews: db.prepare('SELECT count(*) n FROM reviews').get().n
};
db.prepare('VACUUM INTO ?').run(path.join(destination, 'td.sqlite'));
execFileSync('git', ['bundle', 'create', path.join(destination, 'repo.bundle'), '--all'], { cwd: repo });
execFileSync('tar', ['-czf', path.join(destination, 'repo.tar.gz'), '-C', root, 'repo']);
fs.copyFileSync(path.join(root, 'server.json'), path.join(destination, 'server.json'));
fs.copyFileSync('/tmp/bulk-import-payload.json', path.join(destination, 'payload.json'));
fs.writeFileSync(path.join(destination, 'before.json'), JSON.stringify(before, null, 2));
const integrity = new DatabaseSync(path.join(destination, 'td.sqlite'), { readOnly: true });
if (integrity.prepare('PRAGMA integrity_check').get().integrity_check !== 'ok') throw new Error('Backup database integrity failed');
integrity.close();
const result = { destination, head: before.head, reviews: before.reviews, humanReviews: before.humanReviews, payloadSha256: crypto.createHash('sha256').update(fs.readFileSync(path.join(destination, 'payload.json'))).digest('hex'), backupIntegrity: 'ok' };
fs.writeFileSync(path.join(root, 'bulk-import-backup-latest.json'), JSON.stringify(result, null, 2));
console.log(JSON.stringify(result, null, 2));
db.close();