Skip to content

feat(cli): add source-aware atomic update router#59

Merged
vinceblock99 merged 3 commits into
devfrom
feat/atomic-update-router
May 20, 2026
Merged

feat(cli): add source-aware atomic update router#59
vinceblock99 merged 3 commits into
devfrom
feat/atomic-update-router

Conversation

@vinceblock99
Copy link
Copy Markdown
Contributor

@vinceblock99 vinceblock99 commented May 16, 2026

Summary

This PR adds atomic update.

The command checks the latest Atomic release on GitHub, compares it with the currently running binary, and prints the right upgrade command for how that binary was installed.

It supports:

  • official installer
  • Homebrew
  • Cargo
  • manual installs

This is a routing command only. It does not replace the binary yet.

Why

Users should be able to run one command to find out whether their Atomic CLI is outdated and how to upgrade it.

Different install methods need different upgrade commands. For example, Homebrew users should run brew upgrade, Cargo users should run cargo install --force, and official installer users should re-run install.sh.

This PR pairs with atomic-storage PR #51:

https://github.com/atomicdotdev/atomic-storage/pull/51

That PR updates the hosted install.sh so it writes:

$XDG_STATE_HOME/atomic/install.json

This PR reads that manifest. If the manifest says the binary came from the official installer, and the recorded install_path matches the currently running binary, atomic update shows the installer upgrade command.

If there is no trusted manifest, atomic update falls back to path-based detection for Homebrew/Cargo/manual installs.

Behavior

Install source Upgrade hint
Official installer curl -sSf https://atomic.storage/install.sh | sh
Official installer with custom dir same command with ATOMIC_INSTALL=...
Homebrew brew upgrade atomicdotdev/tap/atomic
Cargo cargo install --git https://github.com/atomicdotdev/atomic atomic-cli --locked --force
Manual / unknown installer command plus GitHub releases link

atomic update --check is intended for CI:

  • exits 0 when up to date
  • exits 1 when outdated
  • exits 4 when the version cannot be checked or compared

Safety

The install manifest is user-writable, so this command does not blindly trust it.

It only treats the manifest as official-installer metadata when:

manifest.install_path == current_exe()

The manifest is used for routing and drift detection only. It is not tamper protection.

Verification

Unit tests cover:

  • manifest path resolution
  • trusted manifest detection
  • malformed / mismatched manifest fallback
  • Homebrew, Cargo, and manual path detection
  • stable version comparison
  • output formatting for each source
  • safe shell quoting for custom install paths
  • ahead-of-release behavior

Current focused test result:

cargo test -p atomic-cli commands::update::tests
34 passed

I also ran a real local E2E with atomic-storage PR #51:

  1. Ran the PR Fix status detection for modified files and improve vault handling #51 install.sh into a temporary install dir.
  2. Confirmed it wrote $XDG_STATE_HOME/atomic/install.json.
  3. Built a local older atomic 0.5.0 binary to force the outdated path.
  4. Placed that binary at the manifest install_path.
  5. Confirmed atomic update --check reports source: installer.
  6. Ran the same binary from a different path and confirmed it falls back to source: manual.

Detailed E2E commands and output are here:

https://github.com/atomicdotdev/atomic-storage/pull/51#issuecomment-4480872503

Out of Scope

This PR does not implement self-update / binary replacement. That should come later with release artifact signing, safer replacement mechanics, permission handling, and rollback behavior.

The test helper used format!-templating to embed install_path into the
manifest, producing "install_path": "C:\Users\..." on Windows — illegal
JSON because \U etc. aren't valid string escapes. detect_source then
failed to parse the manifest and fell through to path heuristics,
returning Manual instead of OfficialInstaller. CI green on
ubuntu-latest and macos-latest; failed on windows-latest.

- Manifest::install_path: String -> PathBuf so serde handles escaping
  at the deserialization boundary, and add a doc comment clarifying
  that install_path is platform-native (never compared across
  platforms, only canonicalized against current_exe()).
- write_manifest now builds the JSON with serde_json::json! instead
  of format!-templating, so the encoder owns escaping for any path
  shape.
- Add write_manifest_emits_valid_json_for_windows_paths: constructs a
  Windows-shaped path with a raw string and verifies round-trip parse
  on any host, so the regression is caught even without Windows CI.

install.sh writes the producer side of this manifest and lives in a
separate repo; it has the same pitfall and needs a follow-up.
@vinceblock99 vinceblock99 requested a review from a team May 19, 2026 14:42
Copy link
Copy Markdown
Contributor

@leefaus leefaus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. Let's merge to dev.

@vinceblock99 vinceblock99 merged commit 25b66be into dev May 20, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants