# Conflicts:
#	status/dist/features.js
This commit is contained in:
Sami
2026-05-16 22:52:07 -07:00
33 changed files with 1785 additions and 476 deletions
+24 -34
View File
@@ -49,7 +49,7 @@ class SelfUpdater extends EventEmitter {
// hostUpdatesDir is the HOST path that maps to updatesDir inside the container.
// Used when writing trigger.json so the host-side script can find staging files.
hostUpdatesDir: options.hostUpdatesDir || (platformPaths.isWindows ? options.updatesDir || DEFAULTS.UPDATES_DIR : '/opt/dashcaddy/updates'),
apiSourceDir: options.apiSourceDir || process.env.DASHCADDY_API_SOURCE_DIR || DEFAULTS.API_SOURCE_DIR,
apiSourceDir: options.apiSourceDir || DEFAULTS.API_SOURCE_DIR,
frontendDir: options.frontendDir || DEFAULTS.FRONTEND_DIR,
maxBackups: parseInt(options.maxBackups || DEFAULTS.MAX_BACKUPS, 10),
channel: options.channel || process.env.DASHCADDY_UPDATE_CHANNEL || DEFAULTS.CHANNEL,
@@ -311,15 +311,25 @@ class SelfUpdater extends EventEmitter {
// Delete the result file so we don't process it again
await fsp.unlink(resultPath).catch(() => {});
// Update history
// Update the matching history entry, preferring the newest pending item
// for the same target version. Fall back to the newest pending item if
// older result files lack enough metadata to match more precisely.
const history = this.getUpdateHistory();
const updated = history.filter(h => h.status === 'pending');
if (updated.length > 0) {
for (const pending of updated) {
pending.status = result.success ? 'success' : 'rolled-back';
pending.duration = result.duration;
if (result.error) pending.error = result.error;
}
const pendingIndex = history.findIndex(
h => h.status === 'pending' && (!result.version || h.version === result.version)
);
const fallbackIndex = pendingIndex === -1
? history.findIndex(h => h.status === 'pending')
: -1;
const historyIndex = pendingIndex !== -1 ? pendingIndex : fallbackIndex;
if (historyIndex !== -1) {
const pending = history[historyIndex];
pending.status = result.success ? 'success' : 'rolled-back';
pending.duration = result.duration;
if (result.error) pending.error = result.error;
if (result.version) pending.version = result.version;
if (result.timestamp) pending.completedAt = result.timestamp;
this._saveHistory(history);
}
@@ -400,17 +410,10 @@ class SelfUpdater extends EventEmitter {
async _autoCheckAndApply() {
try {
const result = await this.checkForUpdate();
if (!result.available || !result.remote) return;
// Belt-and-suspenders: never auto-apply a same-version update. A bug here
// creates an infinite rebuild loop (each fresh container has commit='unknown'
// so it keeps comparing as different from remote forever).
if (this._compareVersions(result.local.version, result.remote.version) >= 0) {
console.log('[SelfUpdater] Skipping auto-apply: local %s is not older than remote %s',
result.local.version, result.remote.version);
return;
if (result.available && result.remote) {
console.log('[SelfUpdater] Update available: %s → %s', result.local.version, result.remote.version);
await this.applyUpdate(result.remote);
}
console.log('[SelfUpdater] Update available: %s → %s', result.local.version, result.remote.version);
await this.applyUpdate(result.remote);
} catch (e) {
console.error('[SelfUpdater] Auto-update error:', e.message);
}
@@ -503,24 +506,11 @@ class SelfUpdater extends EventEmitter {
const versionCompare = this._compareVersions(local.version || '0.0.0', remote.version);
if (versionCompare < 0) return true;
if (versionCompare > 0) return false;
// Same version — only flag as newer if BOTH commits are known and differ.
// If local.commit is missing or 'unknown' (Dockerfile default when no build arg
// is passed), we can't distinguish builds, so trust the version number and
// treat them as equivalent. Otherwise the updater loops forever applying
// the same version because every fresh container build has commit='unknown'.
const localCommit = this._normalizeCommit(local.commit);
const remoteCommit = this._normalizeCommit(remote.commit);
if (localCommit && remoteCommit && localCommit !== remoteCommit) return true;
// Same version — check commit hash
if (remote.commit && local.commit && remote.commit !== local.commit) return true;
return false;
}
_normalizeCommit(value) {
if (!value) return null;
const str = String(value).trim().toLowerCase();
if (!str || str === 'unknown' || str === 'null' || str === 'undefined') return null;
return str;
}
_compareVersions(a, b) {
const av = String(a || '0.0.0').split('.').map(part => parseInt(part, 10) || 0);
const bv = String(b || '0.0.0').split('.').map(part => parseInt(part, 10) || 0);