fix(updater): stop false-positive "update available" loop when commit is unknown
Dockerfile never received DASHCADDY_COMMIT at build, so /app/VERSION held
'unknown'. _isNewer then treated same-version-different-commit as newer,
making the auto-updater rebuild the container indefinitely (each rebuild
still produced commit='unknown').
- self-updater._isNewer: normalize commits; treat unknown/null/empty as no
commit info and fall back to pure version comparison
- self-updater._autoCheckAndApply + routes/updates: refuse to apply when
local version >= remote version (belt-and-suspenders)
- update-management.js: hide '(unknown)' from version label
- Dockerfile: COPY VERSION instead of writing from build arg
- VERSION: committed placeholder ('dev'); scripts/release.sh now writes
the real short SHA into the tarball's VERSION before tar-ing, so every
published release ships with an accurate commit
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -400,10 +400,17 @@ class SelfUpdater extends EventEmitter {
|
||||
async _autoCheckAndApply() {
|
||||
try {
|
||||
const result = await this.checkForUpdate();
|
||||
if (result.available && result.remote) {
|
||||
console.log('[SelfUpdater] Update available: %s → %s', result.local.version, result.remote.version);
|
||||
await this.applyUpdate(result.remote);
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -496,11 +503,24 @@ 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 — check commit hash
|
||||
if (remote.commit && local.commit && remote.commit !== local.commit) return true;
|
||||
// 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;
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user