How update-system.mjs Handles Version Checking, Applying Updates, Dismissing, and Rollbacks in Career-Ops
The update-system.mjs script in the Career-Ops repository provides four sub-commands—check, apply, dismiss, and rollback—that enable safe, atomic system-layer updates while strictly isolating user data through lock files, backup branches, and path-specific guards.
The update-system.mjs module serves as the canonical maintenance utility for the Career-Ops repository. Operating exclusively on the system layer, it ensures user-layer files such as cv.md or config/profile.yml remain untouched during version upgrades. Written as a self-contained Node.js ES module in the repository root, it implements a safety-first architecture that prevents concurrent runs and enables full recovery via Git-based backup branches.
Version Checking with the check Command
The check command determines whether the local installation lags behind the remote canonical version.
Local and Remote Resolution
The script reads the local VERSION file via the localVersion() function, then fetches two remote sources in parallel: the raw upstream VERSION file and the latest GitHub release via the API (as implemented in lines 59-67). It parses both responses and selects the highest semantic version using the internal compareVersions helper.
Status Emissions
The command outputs a JSON object with one of five statuses: up-to-date, update-available, offline, no-remote-version, or dismissed. If a .update-dismissed flag exists, the function returns immediately with {"status": "dismissed"} without hitting the network (lines 60-64).
Applying Updates Safely with apply
The apply command executes a multi-stage, atomic update process that modifies only paths defined in the SYSTEM_PATHS constant.
Pre-Update Safety Measures
Before touching any files, the script creates a lock file named .update-lock to prevent overlapping invocations (lines 55-61). It then generates a backup branch named backup-pre-update-<localVersion> to preserve the pre-update state (lines 66-73).
Atomic Update Execution
The update proceeds through the following guarded steps:
- Fetch upstream: Runs
git fetchagainst the canonical remote (lines 75-78). - Checkout system paths: Checks out only the paths listed in
SYSTEM_PATHS, leaving all other files untouched (lines 80-108). - Verify user-layer integrity: Validates that no files in
USER_PATHSwere modified during the operation. If any user-layer file is touched, the procedure aborts and reverts all changes (lines 110-173). - Dependency refresh: Executes
npm installto synchronize any new dependencies introduced in the update (lines 174-178). - Commit and cleanup: Stages the updated system files, clears the
.update-dismissedflag if present, and commits with a descriptive message (lines 179-194). - Lock removal: Deletes the
.update-lockfile in afinallyblock to ensure cleanup even if an error occurs (lines 195-199).
Dismissing Update Notifications
The dismiss command allows users to silence update prompts without applying the upgrade.
When invoked, the script writes a timestamp to .update-dismissed and exits (lines 190-194). Subsequent check calls detect this file and immediately return the dismissed status, bypassing remote version lookups until the flag is cleared by a successful apply operation.
Recovery via rollback
The rollback command restores the system to the state captured by the most recent backup branch.
Backup Restoration Process
The script identifies the latest backup-pre-update-* branch using git for-each-ref. For each entry in SYSTEM_PATHS, it attempts git checkout <backup> -- <path>. If a path did not exist in the backup, the script removes it from the working tree using git rm followed by rmSync (lines 210-276). Finally, it stages the restored files, commits a rollback message, and prints a summary indicating how many paths were restored versus removed (lines 277-282).
Practical Usage Examples
Check for available updates (typically run on startup):
node update-system.mjs check
# {"status":"update-available","local":"1.6.0","remote":"1.7.0","changelog":"..."}
Apply the update after user confirmation:
node update-system.mjs apply
# Backup branch created: backup-pre-update-1.6.0
# Fetching latest from upstream...
# Updating system files...
# Update complete: v1.6.0 → v1.7.0
# Updated 45 system paths.
# Rollback available: node update-system.mjs rollback
Silence the update prompt:
node update-system.mjs dismiss
# "Update check dismissed. Run \"node update-system.mjs check\" to check manually."
Recover from a failed or unwanted update:
node update-system.mjs rollback
# Rolling back to: backup-pre-update-1.6.0
# Rollback complete. Restored 45 path(s) from backup-pre-update-1.6.0, removed 0 path(s).
# Your data (CV, profile, tracker, reports) was not affected.
Summary
check: Compares localVERSIONagainst remote sources usingcompareVersions, returning JSON status codes includingdismissedwhen.update-dismissedexists.apply: Creates a.update-lockandbackup-pre-update-<version>branch, checks out onlySYSTEM_PATHS, validatesUSER_PATHSintegrity, runsnpm install, and commits atomically.dismiss: Persists a timestamp to.update-dismissed, causing future checks to skip network requests.rollback: Restores files from the latestbackup-pre-update-*branch, removing any new paths that did not exist in the backup.
Frequently Asked Questions
What happens if user-layer files are modified during an apply operation?
The script validates USER_PATHS after checking out SYSTEM_PATHS (lines 110-173). If any user-layer file is detected as modified, the operation aborts, reverts all staged changes, and exits with an error, leaving the working directory in its original state.
How long does a dismissed update remain hidden?
The dismissal persists indefinitely until the user runs node update-system.mjs apply successfully, which automatically removes the .update-dismissed flag, or until the user manually deletes the .update-dismissed file from the repository root.
Can you rollback to any previous version or only the immediate last one?
The rollback command selects the most recent backup-pre-update-* branch via git for-each-ref sorting. While the script is designed to recover the immediate previous state, manual Git operations on specific backup branches allow recovery to any prior version created by previous apply runs.
Does the apply command handle dependency updates?
Yes. After updating system files, apply automatically executes npm install (lines 174-178) to install any new dependencies declared in the updated package.json, ensuring the runtime environment matches the new system version.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →