50 lines
2.4 KiB
JavaScript
50 lines
2.4 KiB
JavaScript
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 }
|