make build work across server restart

tmp files disappear on server restart
This commit is contained in:
Girish Ramakrishnan
2026-02-14 19:01:00 +01:00
parent c8bc6f9ffe
commit 2597402496
6 changed files with 36 additions and 22 deletions
+8 -9
View File
@@ -16,6 +16,7 @@ import docker from './docker.js';
import domains from './domains.js';
import eventlog from './eventlog.js';
import fs from 'node:fs';
import fileUtils from './file-utils.js';
import Location from './location.js';
import locks from './locks.js';
import logs from './logs.js';
@@ -1794,6 +1795,8 @@ async function install(data, auditSource) {
let error = manifestFormat.parse(manifest);
if (error) throw new BoxError(BoxError.BAD_FIELD, `Manifest error: ${error.message}`);
if (data.sourceArchiveFilePath) manifest.dockerImage = `local/${manifest.id}:${manifest.version}-${Date.now()}`;
error = await checkManifest(manifest);
if (error) throw error;
@@ -1868,11 +1871,6 @@ async function install(data, auditSource) {
const appId = crypto.randomUUID();
debug(`Installing app ${appId}`);
// if we have a sourceArchive rename it to appId to be picked up later in the apptask
if (data.sourceArchiveFilePath) {
if (!safe.fs.renameSync(data.sourceArchiveFilePath, `/tmp/${appId}.tar.gz`)) throw new BoxError(BoxError.FS_ERROR, 'Error moving source archive');
}
const app = {
accessRestriction,
operators,
@@ -1906,6 +1904,8 @@ async function install(data, auditSource) {
if (addError && addError.reason === BoxError.ALREADY_EXISTS) throw getDuplicateErrorDetails(addError.message, locations, portBindings);
if (addError) throw addError;
if (data.sourceArchiveFilePath) await fileUtils.renameFile(data.sourceArchiveFilePath, `${paths.SOURCE_ARCHIVES_DIR}/${appId}.tar.gz`);
const task = {
args: { restoreConfig: null, skipDnsSetup, overwriteDns },
values: { },
@@ -2312,6 +2312,8 @@ async function updateApp(app, data, auditSource) {
error = manifestFormat.parse(manifest);
if (error) throw new BoxError(BoxError.BAD_FIELD, 'Manifest error:' + error.message);
if (data.sourceArchiveFilePath) manifest.dockerImage = `local/${manifest.id}:${manifest.version}-${Date.now()}`;
error = await checkManifest(manifest);
if (error) throw error;
@@ -2364,10 +2366,7 @@ async function updateApp(app, data, auditSource) {
const hasSso = !!updateConfig.manifest.addons?.proxyAuth || !!updateConfig.manifest.addons?.ldap || !!manifest.addons?.oidc;
if (!hasSso && app.sso) values.sso = false; // turn off sso flag, if the update removes sso options
// if we have a sourceArchive rename it to appId to be picked up later in the apptask
if (data.sourceArchiveFilePath) {
if (!safe.fs.renameSync(data.sourceArchiveFilePath, `/tmp/${appId}.tar.gz`)) throw new BoxError(BoxError.FS_ERROR, 'Error moving source archive');
}
if (data.sourceArchiveFilePath) await fileUtils.renameFile(data.sourceArchiveFilePath, `${paths.SOURCE_ARCHIVES_DIR}/${appId}.tar.gz`);
const task = {
args: { updateConfig },
+2 -1
View File
@@ -203,7 +203,7 @@ async function buildLocalImage(app) {
const sourceFilePath = path.join(paths.APPS_DATA_DIR, app.id, 'source.tar.gz');
// if we have a newly uploaded source archive, use that
const uploadedSourceArchiveFilePath = `/tmp/${app.id}.tar.gz`;
const uploadedSourceArchiveFilePath = `${paths.SOURCE_ARCHIVES_DIR}/${app.id}.tar.gz`;
if (fs.existsSync(uploadedSourceArchiveFilePath)) {
const [renameError] = await safe(fsPromises.rename(uploadedSourceArchiveFilePath, sourceFilePath));
if (renameError) {
@@ -340,6 +340,7 @@ async function uninstallCommand(app, args, progressCallback) {
await progressCallback({ percent: 60, message: 'Deleting image' });
await docker.deleteImage(app.manifest.dockerImage);
await safe(fsPromises.unlink(`${paths.SOURCE_ARCHIVES_DIR}/${app.id}.tar.gz`));
await progressCallback({ percent: 70, message: 'Unregistering domains' });
await dns.unregisterLocations([ { subdomain: app.subdomain, domain: app.domain } ].concat(app.secondaryDomains).concat(app.redirectDomains).concat(app.aliasDomains), progressCallback);
+23
View File
@@ -0,0 +1,23 @@
import assert from 'node:assert';
import { promises as fsPromises } from 'node:fs';
import BoxError from './boxerror.js';
import safe from 'safetydance';
// cross device file rename
async function renameFile(sourcePath, destPath) {
assert.strictEqual(typeof sourcePath, 'string');
assert.strictEqual(typeof destPath, 'string');
const [renameError] = await safe(fsPromises.rename(sourcePath, destPath));
if (renameError) {
if (renameError.code === 'EXDEV') {
const [copyError] = await safe(fsPromises.copyFile(sourcePath, destPath));
if (copyError) throw new BoxError(BoxError.FS_ERROR, copyError);
const [unlinkError] = await safe(fsPromises.unlink(sourcePath));
if (unlinkError) throw new BoxError(BoxError.FS_ERROR, unlinkError);
} else {
throw new BoxError(BoxError.FS_ERROR, renameError);
}
}
}
export default { renameFile };
+1
View File
@@ -31,6 +31,7 @@ export default {
PLATFORM_DATA_DIR: path.join(baseDir(), 'platformdata'),
APPS_DATA_DIR: path.join(baseDir(), 'appsdata'),
SOURCE_ARCHIVES_DIR: path.join(baseDir(), 'platformdata/source-archives'),
ACME_CHALLENGES_DIR: path.join(baseDir(), 'platformdata/acme'),
ADDON_CONFIG_DIR: path.join(baseDir(), 'platformdata/addons'),
-10
View File
@@ -145,11 +145,6 @@ async function install(req, res, next) {
data.sourceArchiveFilePath = req.files && req.files.sourceArchive?.path || null;
// if we have a source archive upload, craft a custom docker image URI for later
if (data.sourceArchiveFilePath) {
data.manifest.dockerImage = `local/${data.manifest.id}:${data.manifest.version}-${Date.now()}`;
}
[error, result] = await safe(apps.install(data, AuditSource.fromRequest(req)));
if (error) return next(BoxError.toHttpError(error));
@@ -659,11 +654,6 @@ async function update(req, res, next) {
data.sourceArchiveFilePath = req.files && req.files.sourceArchive?.path || null;
// if we have a source archive upload, craft a custom docker image URI for later
if (data.sourceArchiveFilePath) {
data.manifest.dockerImage = `local/${data.manifest.id}:${data.manifest.version}-${Date.now()}`;
}
[error, result] = await safe(apps.updateApp(req.resources.app, data, AuditSource.fromRequest(req)));
if (error) return next(BoxError.toHttpError(error));