Update system hardening: breaking-release gate + release-mode rollback + health budgets + success archival
Four fixes building on the prior upgrade-path work. All observed on
marcelle across today's v2.9.2 → v2.9.5 cycles and addressed here.
- Fix 1 (breaking-release gate). upgrade-check.sh now parses the first
line of each Gitea release body for `BREAKING: <reason>` and threads
`breaking`/`breakingReason` through status.json into the API status
response. Admin UI renders a red Alert with a typed-tag confirmation
input and gates the Start Upgrade button. auto-upgrade.service.ts
refuses to apply breaking releases, logging a skip and holding off
until the operator confirms manually.
- Fix 2 (release-mode rollback). print_rollback_help and the --rollback
flow both used `git checkout`, which silently fails in release
installs (no .git). Added INSTALL_MODE branches: release mode
downloads the prior tarball from Gitea using a new VERSION.rollback
marker seeded at Phase 3 start. Source mode retains the existing
git-based flow.
- Fix 3 (Phase 7 health budgets). admin verify_service_health budget
30s → 90s (matches the admin container's start_period from commit
47704667). Gancio + MkDocs switched from one-shot to the existing
verify_service_health retry wrapper. Cuts the cry-wolf
"services may still be starting" warning from every upgrade result.
- Fix 4 (symmetric success archival). Bash archive_failure_to_history
already logs failures on exit; added a matching archive_success_to_
history called after write_result on the success path. API-side
archiveResult now dedupes on completedAt so double-recording (bash
+ post-restart handler) can't land twice in history.json.
Release the bundle as v2.9.6.
Bunker Admin
This commit is contained in:
@@ -36,6 +36,8 @@ export interface UpgradeStatus {
|
||||
date: string;
|
||||
author: string;
|
||||
}>;
|
||||
breaking?: boolean;
|
||||
breakingReason?: string;
|
||||
checkedAt: string;
|
||||
error: string | null;
|
||||
}
|
||||
@@ -206,10 +208,16 @@ function clearStaleProgress(): void {
|
||||
}
|
||||
}
|
||||
|
||||
/** Archive a completed upgrade result to the persistent history file. */
|
||||
/** Archive a completed upgrade result to the persistent history file.
|
||||
* Dedupes on completedAt so the bash-side success archival (upgrade.sh
|
||||
* archive_success_to_history) and this API-side call can't double-record. */
|
||||
function archiveResult(result: UpgradeResult): void {
|
||||
try {
|
||||
const history = readJsonFile<UpgradeResult[]>(HISTORY_FILE) || [];
|
||||
if (history[0]?.completedAt === result.completedAt) {
|
||||
logger.info('Skipping archive — most recent history entry has same completedAt');
|
||||
return;
|
||||
}
|
||||
history.unshift(result);
|
||||
// Trim to max entries
|
||||
if (history.length > MAX_HISTORY_ENTRIES) {
|
||||
|
||||
@@ -122,6 +122,17 @@ class AutoUpgradeService {
|
||||
return;
|
||||
}
|
||||
|
||||
// Refuse to auto-apply releases flagged `BREAKING:` in their Gitea body.
|
||||
// The admin UI gate ensures manual confirmation — auto-upgrade holds off
|
||||
// and keeps checking, so the block clears once the operator upgrades.
|
||||
if (status.breaking) {
|
||||
logger.warn(
|
||||
`Auto-upgrade: refusing to apply breaking release (${status.remoteCommit}): ${status.breakingReason || '(no reason given)'}. Manual confirmation required via admin UI.`,
|
||||
);
|
||||
upgradeService.clearTriggeredBy();
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(`Auto-upgrade: ${status.commitsBehind} commits behind, triggering upgrade`);
|
||||
|
||||
// Read settings for pullServices and registry options
|
||||
|
||||
Reference in New Issue
Block a user