Skip to content

feat: MCP-safe self-upgrade primitive + agentflare dev-install - #206

Merged
getappz merged 6 commits into
masterfrom
task/127
Jul 16, 2026
Merged

feat: MCP-safe self-upgrade primitive + agentflare dev-install#206
getappz merged 6 commits into
masterfrom
task/127

Conversation

@getappz

@getappz getappz commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Bundles two stacked, dependency-linked changes (one CI run).

#122 — MCP-safe self-upgrade primitive

Refactors single-file src/update.rs into a module dir src/update/{mod,github,swap}.rs.

swap::replace_binary is the reusable MCP-safe binary-replacement primitive: it only touches the file on disk and never signals/kills any process, so a live agentflare mcp stdio server keeps running its already-loaded image and picks up the new binary on next launch.

  • Windows: rename-aside + copy, with rollback on copy failure, and a deferred .bat fallback when the exe is hard-locked against renaming.
  • Unix: same-fs staged atomic rename.
  • find_killable_pids() reports other running instances (never kills them).
  • github.rs isolates release discovery/download/checksum, with pure unit-tested parse_checksum/verify_checksum.

#127agentflare dev-install

Build the current source tree and atomically install it over the running binary, reusing update::swap::replace_binary.

  • cargo build (release by default; --debug for debug)
  • resolves the built artifact via cargo metadata (honors CARGO_TARGET_DIR / shared workspace targets)
  • verifies the fresh binary answers --version under a timeout before swapping
  • refuses to overwrite the build output itself; --dry-run reports without swapping

Verification

  • cargo fmt --check: clean
  • cargo clippy -p agentflare --no-deps -D warnings (allowing the crate''s pre-existing intentional unsafe_code warnings): clean
  • cargo test -p agentflare update:: dev_install::: 14 passing
  • end-to-end drive: agentflare dev-install --dry-run builds the workspace (release), resolves the artifact through a shared CARGO_TARGET_DIR, verifies --version, and reports the swap — exit 0

Summary by CodeRabbit

  • New Features
    • Added dev-install to build and install the current version, with debug and dry-run options.
    • Introduced a revamped self-update flow that downloads release archives, verifies checksums, and performs cross-platform binary replacement.
    • After updating, it now reports other running instances that may need restarting.
  • Bug Fixes
    • Binary replacement is now staged/atomic with rollback and supports deferred replacement when needed.
    • dev-install validates the built binary (--version with a timeout) and prevents installing over the freshly built output itself.

getappz added 2 commits July 16, 2026 13:55
Split single-file src/update.rs into src/update/{mod,github,swap}.rs.

swap.rs is the reusable MCP-safe binary-replacement primitive:
- never signals/kills any process, so a live `agentflare mcp` server keeps
  running its loaded image and picks up the new binary on next launch
- Windows: rename-aside + copy, with rollback on failure and a deferred
  .bat fallback when the exe is hard-locked against renaming
- Unix: same-fs staged atomic rename
- find_killable_pids() reports (never kills) other running instances

github.rs isolates release discovery/download/checksum, with pure
parse_checksum/verify_checksum helpers now unit-tested.

Prepares update::swap::replace_binary for reuse by dev-install (#127).
…nary

New `agentflare dev-install` subcommand: cargo build (release by default),
resolve the built binary via `cargo metadata` (honors CARGO_TARGET_DIR and
workspace shared targets), verify it answers `--version` under a timeout, then
swap it over the running binary using the MCP-safe update::swap::replace_binary
primitive (#122) — so a live `agentflare mcp` server is never disturbed.

- refuses to overwrite the build output itself (guards against running from the
  freshly built binary instead of the installed one)
- --debug builds debug; --dry-run reports the swap without performing it
- pure cargo-metadata target-dir parsing + same-file guard are unit-tested
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9ca8a9fd-46bf-48dd-903a-c297f3cb4d4b

📥 Commits

Reviewing files that changed from the base of the PR and between db628b5 and 0104f6c.

📒 Files selected for processing (1)
  • src/dev_install/cargo.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/dev_install/cargo.rs

📝 Walkthrough

Walkthrough

Adds GitHub-based self-updates with checksum verification, cross-platform binary replacement, and a dev-install command that builds, verifies, and optionally installs the local binary.

Changes

Self-update and development installation

Layer / File(s) Summary
Release discovery, verification, and extraction
src/update/github.rs, src/update/mod.rs
GitHub release metadata and platform assets are selected, downloaded, verified against SHA-256 checksums, extracted, and passed to the replacement flow.
Cross-platform binary replacement and process discovery
src/update/swap.rs
Unix and Windows replacement strategies, deferred Windows swapping, process discovery, and related tests are implemented.
Build, verify, and install local binaries
src/dev_install/...
The dev-install flow builds and locates debug or release binaries, verifies --version with a timeout, supports dry runs, and installs through the shared swap primitive.
Dev-install command wiring
src/cli/dev_install.rs, src/cli/mod.rs, src/main.rs
The dev-install subcommand is registered with --debug and --dry-run flags and dispatched to the implementation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Agentflare
  participant UpdateRoutine
  participant GitHub
  participant BinarySwap
  Agentflare->>UpdateRoutine: run(version, check_only, quiet)
  UpdateRoutine->>GitHub: fetch release metadata, asset, and checksum
  GitHub-->>UpdateRoutine: release data and asset bytes
  UpdateRoutine->>UpdateRoutine: verify checksum and extract binary
  UpdateRoutine->>BinarySwap: replace_binary(new_binary, current_executable)
Loading

Possibly related PRs

  • getappz/agentflare#60: Modifies the CLI command enum and dispatch layer used by the new dev-install command.
  • getappz/agentflare#201: Changes the asset_name scheme used by the update asset and checksum lookup flow.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the new self-upgrade primitive and dev-install command.
Description check ✅ Passed It covers the main summary and verification, though it does not follow the exact template headings for test plan and reviewer notes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task/127

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/update/mod.rs (1)

127-156: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Extracted temp directory is never cleaned up.

extract_binary creates agentflare-update-<pid> under the OS temp dir and unpacks the full release archive into it, but nothing removes it after swap::replace_binary succeeds (line 65 in run). Every successful (and failed) update leaves the full extracted payload behind, growing unbounded over repeated updates.

♻️ Proposed cleanup after a successful swap
     if let Err(e) = swap::replace_binary(&new_binary, &current) {
         eprintln!("error replacing binary: {e}");
         std::process::exit(1);
     }
+    if let Some(tmpdir) = new_binary.parent() {
+        let _ = std::fs::remove_dir_all(tmpdir);
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/update/mod.rs` around lines 127 - 156, Ensure the temporary directory
created by extract_binary is removed after the update completes, including after
swap::replace_binary succeeds and when later update steps fail. Preserve the
extracted binary’s use through replacement, then clean up the corresponding
agentflare-update-<pid> directory in the run flow or via a guaranteed cleanup
mechanism.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/dev_install/cargo.rs`:
- Around line 28-44: The built_binary_path function must account for Cargo
target-specific output directories. Use Cargo metadata’s reported
target/executable path when available, or resolve the configured target triple
and insert it between target_directory and profile_dir(release), while
preserving the existing host-target path behavior.

In `@src/update/github.rs`:
- Around line 53-59: Update gh_get to configure an explicit per-request timeout
on the ureq request before call(), using the project’s established timeout value
if one exists. Keep the timeout applied to all callers, including
latest_version, download_asset, and expected_checksum, while preserving the
existing error mapping.

In `@src/update/swap.rs`:
- Around line 39-57: Update staging_path and its use in unix_replace so the
staged filename is unique per process, incorporating the current process ID
while retaining the target-directory location and filename-based staging
behavior. Preserve same-filesystem atomic rename and cleanup-on-error semantics,
and match the existing process-scoped naming convention used by other temporary
artifacts.
- Around line 59-86: Update the copy-error branch in windows_replace so the
rollback rename result is checked instead of discarded. If restoring old to
target fails, return an error that distinctly reports both the copy failure and
rollback failure, including the old path so the stranded binary can be
recovered; preserve the existing copy-error response when rollback succeeds.

---

Nitpick comments:
In `@src/update/mod.rs`:
- Around line 127-156: Ensure the temporary directory created by extract_binary
is removed after the update completes, including after swap::replace_binary
succeeds and when later update steps fail. Preserve the extracted binary’s use
through replacement, then clean up the corresponding agentflare-update-<pid>
directory in the run flow or via a guaranteed cleanup mechanism.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 416b3eb2-20aa-49dc-a508-0c18191a907a

📥 Commits

Reviewing files that changed from the base of the PR and between bd67d87 and 3312e30.

📒 Files selected for processing (9)
  • src/cli/dev_install.rs
  • src/cli/mod.rs
  • src/dev_install/cargo.rs
  • src/dev_install/mod.rs
  • src/main.rs
  • src/update.rs
  • src/update/github.rs
  • src/update/mod.rs
  • src/update/swap.rs
💤 Files with no reviewable changes (1)
  • src/update.rs

Comment thread src/dev_install/cargo.rs Outdated
Comment thread src/update/github.rs
Comment thread src/update/swap.rs
Comment thread src/update/swap.rs
getappz added 2 commits July 16, 2026 14:19
…ofile>

CodeRabbit (PR #206): built_binary_path assumed target_directory/<profile>/,
which is wrong when build.target / CARGO_BUILD_TARGET adds a <triple>/ segment
(or a custom profile renames the dir) -- the lookup would miss the binary.

Read the executable path from cargo's compiler-artifact JSON message instead of
reconstructing it, so any target/profile layout is handled. build_and_locate()
replaces the target-dir/profile-dir reconstruction and its parser.
… net timeout

PR #206 CodeRabbit findings on the #122 swap/github code:
- swap.rs: surface Windows rollback failure (and the recoverable old-binary
  path) instead of swallowing it, honoring the "never leave without a binary"
  invariant.
- swap.rs: pid-scope the Unix staging filename so concurrent swaps against the
  same target cannot clobber each other's staged file.
- github.rs: give the release HTTP client connect + per-read timeouts so a hung
  network cannot block an update indefinitely (no overall timeout, to avoid
  capping large asset downloads).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/dev_install/cargo.rs`:
- Around line 38-44: Update the child-process handling around the stdout read
and `child.wait()` flow so a `read_to_string` failure terminates Cargo and waits
for it before returning the error; alternatively, use `wait_with_output()` to
manage collection and reaping together while preserving the existing error
message.
- Around line 16-28: Update the Cargo command construction in verify_runs to
handle configured non-host targets: either force the build target to the current
host or validate build.target/CARGO_BUILD_TARGET and return a clear error before
building. Preserve the existing release and binary arguments while ensuring
dev-install never attempts to execute a binary built for a different target.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9e38ed2c-cf6f-4e9e-9d2d-e7dc49bfca61

📥 Commits

Reviewing files that changed from the base of the PR and between 3312e30 and db628b5.

📒 Files selected for processing (4)
  • src/dev_install/cargo.rs
  • src/dev_install/mod.rs
  • src/update/github.rs
  • src/update/swap.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/dev_install/mod.rs
  • src/update/swap.rs
  • src/update/github.rs

Comment thread src/dev_install/cargo.rs
Comment thread src/dev_install/cargo.rs Outdated
getappz added 2 commits July 16, 2026 14:49
…ead error

CodeRabbit (PR #206) on build_and_locate:
- Reject a non-host CARGO_BUILD_TARGET early with a clear message: dev-install
  replaces the running binary, so a cross target would build something that
  cannot run here. verify_runs() remains the backstop for a .cargo/config
  build.target.
- Always child.wait() even when reading cargo stdout fails, so a failed read
  never orphans the cargo process.
@getappz
getappz merged commit 4d8d4c5 into master Jul 16, 2026
15 checks passed
@getappz
getappz deleted the task/127 branch July 16, 2026 09:25
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.

1 participant