const fs = require('node:fs') const path = require('node:path') const { execFileSync } = require('node:child_process') // Snapshot only this build, before adding older hashed chunks for open tabs. function preserveFrontendAssets(root, revision) { if (!/^[a-zA-Z0-9-]+$/.test(revision)) throw new Error('Invalid asset revision') const assets = path.join(root, 'src/frontend/dist/assets') const archive = path.join(root, 'frontend-asset-archives') fs.mkdirSync(archive, { recursive: true }) const previous = fs.readdirSync(archive).filter(name => fs.statSync(path.join(archive, name)).isDirectory()) .sort((a, b) => fs.statSync(path.join(archive, b)).mtimeMs - fs.statSync(path.join(archive, a)).mtimeMs).slice(0, 3) const snapshot = path.join(archive, revision) if (fs.existsSync(snapshot)) throw new Error('Asset snapshot already exists') fs.cpSync(assets, snapshot, { recursive: true, errorOnExist: true, force: false }) if (!previous.length) { const docker = (...args) => execFileSync('docker', ['--context', 'colima-yawan', ...args], { encoding: 'utf8' }).trim() const containers = docker('ps', '-a', '--filter', 'name=yawan-public-frontend', '--format', '{{.Names}}').split('\n').filter(Boolean) for (const container of containers) { if (!/^yawan-public-frontend(?:-before-[a-zA-Z0-9-]+)?$/.test(container)) continue const name = `bootstrap-${container}` const destination = path.join(archive, name) if (!fs.existsSync(destination)) docker('cp', `${container}:/usr/share/nginx/html/assets`, destination) previous.push(name) } } let retained = 0 for (const name of previous) retained += mergeAssets(path.join(archive, name), assets) const now = new Date() fs.utimesSync(snapshot, now, now) return { retained, previous_builds: previous.length } } function mergeAssets(source, destination) { let copied = 0 for (const entry of fs.readdirSync(source, { withFileTypes: true })) { const from = path.join(source, entry.name), to = path.join(destination, entry.name) if (entry.isSymbolicLink()) throw new Error('Asset symlinks are not supported') if (entry.isDirectory()) { fs.mkdirSync(to, { recursive: true }) copied += mergeAssets(from, to) } else if (entry.isFile() && !fs.existsSync(to)) { fs.copyFileSync(from, to, fs.constants.COPYFILE_EXCL) copied++ } } return copied } module.exports = { preserveFrontendAssets, mergeAssets }