fix(upgrade): error path completeness + observability

5 issues raised by code-quality reviewer on c12013a:

A. exec() wrapper now captures stderr from execSync failures and
   re-throws with `phase X failed: <stderr>` instead of the terse
   "Command failed: ..." default. Operators see the actual git/npm
   error.
B. runFullUpgrade body wrapped in try/catch; any error after phase 2
   (snapshot written) carries snapshotPath + phases + hint pointing
   at `ocp update --rollback`. Aligns with the post-flight failure
   pattern.
C. CLI entrypoint now prints snapshotPath + hint on error.

Plus minor:
- snapshot.mjs tryCopy logs a [snapshot] warn line instead of silently
  swallowing copy errors (e.g. permission-denied admin-key)
- heads-up window 1s → 3s, more operable per the policy intent
- opts.yes intent comment added (Bundle 3 will use)

One regression test added.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-11 03:58:32 +10:00
co-authored by Claude Sonnet 4.6
parent c12013a094
commit 7959f25801
3 changed files with 93 additions and 52 deletions
+3 -1
View File
@@ -15,7 +15,9 @@ export function writeSnapshot({ homeDir, fromCommit, fromVersion, toVersion, ext
const tryCopy = (src, dst) => {
try {
if (existsSync(src)) copyFileSync(src, dst);
} catch { /* swallow */ }
} catch (err) {
console.error(`[snapshot] warn: could not copy ${src} (${err.code || err.message})`);
}
};
tryCopy(join(homeDir, "Library", "LaunchAgents", "dev.ocp.proxy.plist"), join(root, "plist"));
tryCopy(join(homeDir, ".config", "systemd", "user", "ocp-proxy.service"), join(root, "service"));