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
+2 -2
View File
@@ -62,7 +62,7 @@ mkdir -p "${BOX_DATA_DIR}" "${APPS_DATA_DIR}" "${MAIL_DATA_DIR}"
# keep these in sync with paths.js
log "Ensuring directories"
mkdir -p "${PLATFORM_DATA_DIR}/"{graphite,mysql,postgresql,mongodb,redis,tls,logrotate.d,acme,backup,update,firewall,sshfs,cifs,oidc,diskusage}
mkdir -p "${PLATFORM_DATA_DIR}/"{graphite,mysql,postgresql,mongodb,redis,tls,logrotate.d,acme,backup,update,firewall,sshfs,cifs,oidc,diskusage,source-archives}
mkdir -p "${PLATFORM_DATA_DIR}/addons/mail/"{banner,dkim}
mkdir -p "${PLATFORM_DATA_DIR}/logs/"{backup,updater,tasks}
mkdir -p "${PLATFORM_DATA_DIR}/sftp/ssh" # sftp keys
@@ -223,7 +223,7 @@ log "Changing ownership"
# note, change ownership after db migrate. this allow db migrate to move files around as root and then we can fix it up here
# be careful of what is chown'ed here. subdirs like mysql,redis etc are owned by the containers and will stop working if perms change
chown -R "${USER}" /etc/cloudron
chown "${USER}:${USER}" -R "${PLATFORM_DATA_DIR}/"{nginx,addons,acme,backup,logs,update,sftp,firewall,sshfs,cifs,tls,oidc,diskusage}
chown "${USER}:${USER}" -R "${PLATFORM_DATA_DIR}/"{nginx,addons,acme,backup,logs,update,sftp,firewall,sshfs,cifs,tls,oidc,diskusage,source-archives}
chown "${USER}:${USER}" "${PLATFORM_DATA_DIR}/INFRA_VERSION" 2>/dev/null || true
chown "${USER}:${USER}" "${PLATFORM_DATA_DIR}"
chown "${USER}:${USER}" "${APPS_DATA_DIR}"
+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));