Spaces:
Running
Running
| /* | |
| Creates a complete release bundle for fast restore: | |
| - Tags frontend and backend repos with an annotated tag | |
| - Copies code snapshots (tar.gz) for frontend and backend | |
| - Dumps MongoDB collections to JSON (users, sourcetexts, submissions, subtitles, subtitlesubmissions) | |
| - Writes a manifest.json with commit SHAs, tag, counts, and timestamps | |
| Usage: node create-release-bundle.js [--tag TAG_NAME] | |
| Requires: MONGODB_URI env (or falls back to local), git available | |
| */ | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const { execSync } = require('child_process'); | |
| const mongoose = require('mongoose'); | |
| const ROOT = path.resolve(__dirname, '..', '..'); | |
| const BACKEND_DIR = path.resolve(__dirname); | |
| const FRONTEND_DIR = path.resolve(__dirname, '../frontend'); | |
| const BACKUPS_DIR = path.join(BACKEND_DIR, 'backups'); | |
| const RELEASES_DIR = path.join(BACKUPS_DIR, 'releases'); | |
| const argTag = process.argv.includes('--tag') ? process.argv[process.argv.indexOf('--tag') + 1] : null; | |
| const ts = new Date().toISOString().replace(/[:.]/g, '-'); | |
| const tagName = argTag || `prod-${ts}`; | |
| function ensureDir(p) { | |
| fs.mkdirSync(p, { recursive: true }); | |
| } | |
| function getGitInfo(repoDir) { | |
| const sha = execSync('git rev-parse HEAD', { cwd: repoDir }).toString().trim(); | |
| const branch = execSync('git rev-parse --abbrev-ref HEAD', { cwd: repoDir }).toString().trim(); | |
| return { sha, branch }; | |
| } | |
| function tagAndPush(repoDir, tag) { | |
| try { | |
| execSync(`git tag -a ${tag} -m "Release ${tag}"`, { cwd: repoDir, stdio: 'inherit' }); | |
| } catch (e) { | |
| // if tag exists, continue | |
| } | |
| // Prefer pushing to 'huggingface' remote if present, else default | |
| try { | |
| const remotes = execSync('git remote', { cwd: repoDir }).toString().split('\n').map(r => r.trim()).filter(Boolean); | |
| if (remotes.includes('huggingface')) { | |
| execSync('git push --tags huggingface', { cwd: repoDir, stdio: 'inherit' }); | |
| } else { | |
| execSync('git push --tags', { cwd: repoDir, stdio: 'inherit' }); | |
| } | |
| } catch (e) { | |
| console.warn('β οΈ Warning: failed to push tags for', repoDir, '- continuing'); | |
| } | |
| } | |
| async function dumpCollections(outDir) { | |
| const uri = process.env.MONGODB_URI || 'mongodb://localhost:27017/transcreation-sandbox'; | |
| await mongoose.connect(uri, { serverSelectionTimeoutMS: 5000 }); | |
| const conn = mongoose.connection; | |
| const collections = ['users', 'sourcetexts', 'submissions', 'subtitles', 'subtitlesubmissions']; | |
| const counts = {}; | |
| for (const name of collections) { | |
| try { | |
| const docs = await conn.db.collection(name).find({}).toArray(); | |
| fs.writeFileSync(path.join(outDir, `${name}.json`), JSON.stringify(docs, null, 2)); | |
| counts[name] = docs.length; | |
| } catch (e) { | |
| counts[name] = 0; | |
| } | |
| } | |
| await mongoose.disconnect(); | |
| return counts; | |
| } | |
| (async () => { | |
| console.log('π¦ Creating release bundle...'); | |
| ensureDir(RELEASES_DIR); | |
| const bundleDir = path.join(RELEASES_DIR, `release-${ts}`); | |
| ensureDir(bundleDir); | |
| // Git info and tags | |
| console.log('π Tagging repositories...'); | |
| const feInfo = getGitInfo(FRONTEND_DIR); | |
| const beInfo = getGitInfo(BACKEND_DIR); | |
| tagAndPush(FRONTEND_DIR, tagName); | |
| tagAndPush(BACKEND_DIR, tagName); | |
| // Code archives | |
| console.log('ποΈ Archiving code...'); | |
| const feTar = path.join(bundleDir, `frontend-${feInfo.sha.slice(0,7)}.tar.gz`); | |
| const beTar = path.join(bundleDir, `backend-${beInfo.sha.slice(0,7)}.tar.gz`); | |
| execSync(`tar -czf "${feTar}" -C "${FRONTEND_DIR}" .`); | |
| execSync(`tar -czf "${beTar}" -C "${BACKEND_DIR}" .`); | |
| // DB dump | |
| console.log('πΎ Dumping database...'); | |
| const dbDir = path.join(bundleDir, 'db'); | |
| ensureDir(dbDir); | |
| const counts = await dumpCollections(dbDir); | |
| // Manifest | |
| const manifest = { | |
| createdAt: new Date().toISOString(), | |
| tag: tagName, | |
| frontend: feInfo, | |
| backend: beInfo, | |
| archives: { | |
| frontend: path.basename(feTar), | |
| backend: path.basename(beTar) | |
| }, | |
| db: { | |
| path: 'db', | |
| counts | |
| } | |
| }; | |
| fs.writeFileSync(path.join(bundleDir, 'manifest.json'), JSON.stringify(manifest, null, 2)); | |
| console.log('\nπ Release bundle created'); | |
| console.log('π Location:', bundleDir); | |
| console.log('π Tag:', tagName); | |
| })(); | |