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:

  1. Fetch upstream: Runs git fetch against the canonical remote (lines 75-78).
  2. Checkout system paths: Checks out only the paths listed in SYSTEM_PATHS, leaving all other files untouched (lines 80-108).
  3. Verify user-layer integrity: Validates that no files in USER_PATHS were modified during the operation. If any user-layer file is touched, the procedure aborts and reverts all changes (lines 110-173).
  4. Dependency refresh: Executes npm install to synchronize any new dependencies introduced in the update (lines 174-178).
  5. Commit and cleanup: Stages the updated system files, clears the .update-dismissed flag if present, and commits with a descriptive message (lines 179-194).
  6. Lock removal: Deletes the .update-lock file in a finally block 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 local VERSION against remote sources using compareVersions, returning JSON status codes including dismissed when .update-dismissed exists.
  • apply: Creates a .update-lock and backup-pre-update-<version> branch, checks out only SYSTEM_PATHS, validates USER_PATHS integrity, runs npm install, and commits atomically.
  • dismiss: Persists a timestamp to .update-dismissed, causing future checks to skip network requests.
  • rollback: Restores files from the latest backup-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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →