Skip to content

feat(cli): add self-update command and cargo-binstall support#9

Merged
lucifer1004 merged 1 commit intomainfrom
feat/self-update
Apr 14, 2026
Merged

feat(cli): add self-update command and cargo-binstall support#9
lucifer1004 merged 1 commit intomainfrom
feat/self-update

Conversation

@lucifer1004
Copy link
Copy Markdown
Contributor

@lucifer1004 lucifer1004 commented Apr 13, 2026

Summary

Add a govctl self-update command for GitHub Releases-based binary updates, and add cargo-binstall metadata so users can install prebuilt govctl binaries without building from source.

What Changed

  • add govctl self-update [--check]
  • use the self_update crate to check GitHub Releases and replace the current binary
  • add package.metadata.binstall so cargo binstall govctl can fetch release artifacts directly
  • document and govern the feature through RFC-0002 and ADR-0041
  • update release metadata and changelog for 0.8.3

Behavior

  • govctl self-update --check reports whether a newer release is available
  • govctl self-update downloads and installs the latest release binary for the current platform
  • cargo binstall govctl now resolves the published release archives using repo metadata

Governance

  • RFC-0002: add C-SELF-UPDATE and amend global command coverage
  • ADR-0041: accept self_update + cargo-binstall as the release and distribution strategy
  • WI-2026-04-13-001: completed

Verification

  • cargo test -q
  • cargo run --quiet -- check
  • cargo clippy --all-targets --all-features -- -D warnings

Notes

  • no artifact migration is required
  • this PR is additive; existing installation paths remain supported

Summary by CodeRabbit

  • New Features

    • Added govctl self-update command to update the binary from GitHub Releases.
    • Added --check flag to report available updates without installing.
    • Enabled packaging/install support for cargo-binstall to simplify installs.
  • Release

    • Bumped release to v0.8.3 and added changelog entry.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

Warning

Rate limit exceeded

@lucifer1004 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 37 minutes and 14 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 37 minutes and 14 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 18a40bd0-2a4a-4962-8e58-de233cc483eb

📥 Commits

Reviewing files that changed from the base of the PR and between c108579 and 5b84011.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (51)
  • .claude-plugin/marketplace.json
  • .claude/.claude-plugin/plugin.json
  • .claude/skills/commit/SKILL.md
  • CHANGELOG.md
  • Cargo.toml
  • build.rs
  • docs/rfc/RFC-0002.md
  • gov/adr/ADR-0041-self-update-and-cargo-binstall-binary-distribution.toml
  • gov/releases.toml
  • gov/rfc/RFC-0002/clauses/C-GLOBAL-COMMANDS.toml
  • gov/rfc/RFC-0002/clauses/C-SELF-UPDATE.toml
  • gov/rfc/RFC-0002/rfc.toml
  • gov/work/2026-04-13-implement-self-update-command-and-cargo-binstall-support.toml
  • src/cli.rs
  • src/cmd/edit/engine.rs
  • src/cmd/edit/mod.rs
  • src/cmd/edit/path.rs
  • src/cmd/edit/rules.rs
  • src/cmd/edit/runtime.rs
  • src/cmd/lifecycle.rs
  • src/cmd/mod.rs
  • src/cmd/self_update.rs
  • src/cmd/tag.rs
  • src/command_router.rs
  • src/render.rs
  • src/signature.rs
  • src/terminal_md.rs
  • src/tui/mod.rs
  • src/validate.rs
  • src/write.rs
  • tests/common/mod.rs
  • tests/test_agent_dir.rs
  • tests/test_changelog.rs
  • tests/test_delete.rs
  • tests/test_describe.rs
  • tests/test_display_paths.rs
  • tests/test_edit.rs
  • tests/test_errors.rs
  • tests/test_guard.rs
  • tests/test_happy_path.rs
  • tests/test_help.rs
  • tests/test_init.rs
  • tests/test_lifecycle.rs
  • tests/test_lock.rs
  • tests/test_migrate.rs
  • tests/test_move.rs
  • tests/test_rfc_lifecycle.rs
  • tests/test_scan.rs
  • tests/test_source_scan.rs
  • tests/test_tags.rs
  • tests/test_verify.rs
📝 Walkthrough

Walkthrough

Bumps govctl to v0.8.3 and adds a built-in govctl self-update [--check] command (GitHub Releases-backed). Adds self_update dependency, cargo-binstall metadata, RFC/ADR/work docs for the feature, command wiring and implementation, and numerous test improvements to propagate errors.

Changes

Cohort / File(s) Summary
Version & Release Metadata
\.claude-plugin/marketplace.json, \.claude/.claude-plugin/plugin.json, gov/releases.toml, CHANGELOG.md
Bumped plugin/crate/release metadata to 0.8.3 and recorded release entry/date/ref; added changelog entry for 0.8.3.
Package Config & Lints
Cargo.toml, build.rs
Bumped crate version to 0.8.3, added self_update dependency with relevant features, added [package.metadata.binstall] for cargo-binstall, and strengthened Clippy lint rules; build.rs suppresses some clippy lints for the build script.
RFC / Spec / Clauses
docs/rfc/RFC-0002.md, gov/rfc/RFC-0002/rfc.toml, gov/rfc/RFC-0002/clauses/C-GLOBAL-COMMANDS.toml, gov/rfc/RFC-0002/clauses/C-SELF-UPDATE.toml
Updated RFC version to 0.8.0 and added normative C-SELF-UPDATE clause plus global-commands entry describing govctl self-update [--check] behavior and error semantics.
ADR & Work Manifest
gov/adr/ADR-0041-self-update-and-cargo-binstall-binary-distribution.toml, gov/work/2026-04-13-implement-self-update-command-and-cargo-binstall-support.toml
Added ADR documenting decision to use self_update and add binstall metadata; added work item manifest recording implementation and acceptance criteria.
CLI & Command Routing
src/cli.rs, src/cmd/mod.rs, src/command_router.rs
Added SelfUpdate { check: bool } CLI subcommand and flag, exported self_update module, extended BuiltinOp with SelfUpdate, mapped parsed command → plan → execution, and updated tests covering routing and lock disposition.
Self-Update Implementation
src/cmd/self_update.rs
New module implementing pub fn self_update(check_only: bool) -> anyhow::Result<Vec<Diagnostic>>, version-compare helpers, GitHub Releases querying/download/verify/replace logic, TTY progress handling, and unit tests for version parsing/comparison.
Docs / Marketplace / Skills
.claude/skills/commit/SKILL.md, other docs
Adjusted VCS detection steps in a skill doc to prefer jj root before Git detection and enforce sequential execution; other documentation updates reflecting new feature.
Many Tests: propagate errors
tests/..., tests/common/mod.rs, src/*/mod.rs (tests across many files)
Widespread test refactors: tests now return Result<..., Box<dyn Error>> / common::TestResult, replace panics/unwraps with ? propagation, update helper signatures, and adjust assertions to return diagnostics instead of panicking.
Internal error handling & helpers
src/cmd/edit/*, src/cmd/tag.rs, src/render.rs, src/terminal_md.rs, src/validate.rs, src/write.rs, src/tui/mod.rs, src/signature.rs, src/cmd/lifecycle.rs
Replaced many expect/unwrap points with fallible returns/Diagnostics, converted some exported regex singleton to fallible accessor, compiled regexes locally in one function, and adjusted various internal helpers/tests to propagate errors.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI / User
    participant Router as Command Router
    participant SelfUpdate as self_update Module
    participant GitHub as GitHub Releases API
    participant FS as Filesystem

    CLI->>Router: govctl self-update [--check]
    Router->>SelfUpdate: self_update(check_only)
    SelfUpdate->>SelfUpdate: Read CARGO_PKG_VERSION

    alt check_only = true
        SelfUpdate->>GitHub: Query latest release
        GitHub-->>SelfUpdate: Latest version tag
        SelfUpdate->>SelfUpdate: Normalize tag, compare semver
        alt update available
            SelfUpdate-->>CLI: Emit Diagnostic (update available)
        else up-to-date
            SelfUpdate-->>CLI: Print "no update needed"
        end
    else check_only = false
        SelfUpdate->>GitHub: Query latest release & assets
        GitHub-->>SelfUpdate: Release metadata + asset
        SelfUpdate->>SelfUpdate: Select platform asset
        SelfUpdate->>GitHub: Download asset (with progress if TTY)
        GitHub-->>SelfUpdate: Binary data
        SelfUpdate->>SelfUpdate: Verify integrity
        SelfUpdate->>FS: Replace running executable
        FS-->>SelfUpdate: Replace result
        SelfUpdate->>SelfUpdate: Print old/new versions
        SelfUpdate-->>CLI: Success (exit 0)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I nibble at tags and chase the release,

From v0.8.2 to v0.8.3 with ease,
A hop, a download, an executable swap,
Carrots of versions—no more manual stop,
Hooray, says the rabbit, updates never cease!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly summarizes the main changes: adding a self-update command and cargo-binstall support to the CLI, which are the primary objectives of the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/self-update

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 13, 2026

Codecov Report

❌ Patch coverage is 73.15341% with 189 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/cmd/edit/mod.rs 42.33% 94 Missing ⚠️
src/cmd/self_update.rs 58.92% 46 Missing ⚠️
src/cmd/edit/runtime.rs 60.00% 18 Missing ⚠️
src/command_router.rs 90.37% 13 Missing ⚠️
src/validate.rs 33.33% 6 Missing ⚠️
src/cmd/lifecycle.rs 50.00% 4 Missing ⚠️
src/cmd/tag.rs 71.42% 4 Missing ⚠️
src/terminal_md.rs 80.00% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/command_router.rs (1)

873-875: Add planner tests for the new SelfUpdate route.

Lines 873-875 and Line 512 add new routing branches, but this file’s tests do not yet assert Commands::SelfUpdate { check } mapping and lock disposition behavior. A small unit test here would prevent silent regressions.

Also applies to: 512-512

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/command_router.rs` around lines 873 - 875, Add unit tests that exercise
the new Commands::SelfUpdate { check } branch and assert it maps to
global(Op::Builtin(BuiltinOp::SelfUpdate { check })) and has the correct lock
disposition; specifically, add tests that call the planner/routing helper used
elsewhere in this module (the same helper used by other command tests) with a
SelfUpdate command variant and assert the returned Route/Op equals
global(Op::Builtin(BuiltinOp::SelfUpdate { check: true })) and
global(Op::Builtin(BuiltinOp::SelfUpdate { check: false })) respectively, and
verify the route's lock behavior matches the intended disposition (no lock or
whatever the module's convention is) so future changes will fail the test if
mapping or lock semantics regress.
gov/rfc/RFC-0002/clauses/C-GLOBAL-COMMANDS.toml (1)

172-175: Keep global-command entries structurally uniform.

Optional: add explicit Syntax:/Behavior: lines here (even if brief with a cross-reference) to match the established pattern used by commands 1-10 and keep parsing/readability consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gov/rfc/RFC-0002/clauses/C-GLOBAL-COMMANDS.toml` around lines 172 - 175, The
"govctl self-update" global-command entry is missing the structural `Syntax:`
and `Behavior:` lines used by entries 1–10; update the "11. `govctl
self-update`" stanza to include a brief `Syntax:` (e.g., "govctl self-update
[--channel <channel>]") and a short `Behavior:` that either summarizes the
action ("Updates the govctl binary to the latest release") or cross-references
`[[RFC-0002:C-SELF-UPDATE]]`, so the entry matches the established format and
remains machine/reader-friendly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@gov/adr/ADR-0041-self-update-and-cargo-binstall-binary-distribution.toml`:
- Around line 41-47: Remove execution-specific bullets from the ADR-0041
"Implementation Notes" section: delete or move the GitHub endpoint line, the
platform mapping/target-triple detail, the `--check` flag behavior, the
`cargo-binstall` URL template (`govctl-v{version}-{target}.tar.gz`), and the
amendment note referencing [[RFC-0002:C-GLOBAL-COMMANDS]] out of the ADR
document; leave ADR-0041 focused on decision, rationale, and consequences only,
and relocate the removed items into the RFC or implementation docs (e.g., the
CLI command RFC and code-level README) so command semantics and asset naming are
documented where execution details belong.

In `@src/cmd/self_update.rs`:
- Around line 37-48: The current check treats any string-inequality as
"newer"—parse both current and latest_version as semver::Version (using
semver::Version::parse) and compare them semantically (if latest_version <=
current then treat as up-to-date and return success; only when latest_version >
current produce the Diagnostic via Diagnostic::new). Update the code paths
around the equality check in self_update.rs (the block that uses
ui::success/ui::info and returns Ok(vec![]) or Ok(vec![Diagnostic::new(...)]))
to use the semantic comparison and handle parse errors (return an appropriate
error/diagnostic or fallback) rather than simple string inequality.

In `@src/command_router.rs`:
- Around line 215-216: The planner currently treats
Op::Builtin(BuiltinOp::SelfUpdate { .. }) as lock-free; change the mapping so
that non-check SelfUpdate requests require LockDisposition::GovRootExclusive
(i.e., treat Op::Builtin(BuiltinOp::SelfUpdate { check: false }) like other
mutating ops), update the branch in the lock disposition resolver that groups
SelfUpdate with TagList to return LockDisposition::GovRootExclusive for mutating
SelfUpdate, and add a unit test assertion in
test_lock_disposition_requires_lock_for_mutating_commands (e.g.,
assert_eq!(global(Op::Builtin(BuiltinOp::SelfUpdate { check: false
})).lock_disposition(), LockDisposition::GovRootExclusive)) to cover the new
behavior while keeping SelfUpdate { check: true } lock-free.

---

Nitpick comments:
In `@gov/rfc/RFC-0002/clauses/C-GLOBAL-COMMANDS.toml`:
- Around line 172-175: The "govctl self-update" global-command entry is missing
the structural `Syntax:` and `Behavior:` lines used by entries 1–10; update the
"11. `govctl self-update`" stanza to include a brief `Syntax:` (e.g., "govctl
self-update [--channel <channel>]") and a short `Behavior:` that either
summarizes the action ("Updates the govctl binary to the latest release") or
cross-references `[[RFC-0002:C-SELF-UPDATE]]`, so the entry matches the
established format and remains machine/reader-friendly.

In `@src/command_router.rs`:
- Around line 873-875: Add unit tests that exercise the new Commands::SelfUpdate
{ check } branch and assert it maps to global(Op::Builtin(BuiltinOp::SelfUpdate
{ check })) and has the correct lock disposition; specifically, add tests that
call the planner/routing helper used elsewhere in this module (the same helper
used by other command tests) with a SelfUpdate command variant and assert the
returned Route/Op equals global(Op::Builtin(BuiltinOp::SelfUpdate { check: true
})) and global(Op::Builtin(BuiltinOp::SelfUpdate { check: false }))
respectively, and verify the route's lock behavior matches the intended
disposition (no lock or whatever the module's convention is) so future changes
will fail the test if mapping or lock semantics regress.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 25c7b688-939a-42d2-9db4-aaee2ab38000

📥 Commits

Reviewing files that changed from the base of the PR and between 7ee2910 and dd6ca2f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (16)
  • .claude-plugin/marketplace.json
  • .claude/.claude-plugin/plugin.json
  • .claude/skills/commit/SKILL.md
  • CHANGELOG.md
  • Cargo.toml
  • docs/rfc/RFC-0002.md
  • gov/adr/ADR-0041-self-update-and-cargo-binstall-binary-distribution.toml
  • gov/releases.toml
  • gov/rfc/RFC-0002/clauses/C-GLOBAL-COMMANDS.toml
  • gov/rfc/RFC-0002/clauses/C-SELF-UPDATE.toml
  • gov/rfc/RFC-0002/rfc.toml
  • gov/work/2026-04-13-implement-self-update-command-and-cargo-binstall-support.toml
  • src/cli.rs
  • src/cmd/mod.rs
  • src/cmd/self_update.rs
  • src/command_router.rs

Comment thread gov/adr/ADR-0041-self-update-and-cargo-binstall-binary-distribution.toml Outdated
Comment thread src/cmd/self_update.rs Outdated
Comment thread src/command_router.rs
Comment on lines +215 to 216
| Op::Builtin(BuiltinOp::SelfUpdate { .. })
| Op::Builtin(BuiltinOp::TagList { .. })
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Confirm self-update is currently lock-free in planner:"
rg -n -C3 'lock_disposition|SelfUpdate' src/command_router.rs

echo
echo "2) Confirm non-check self-update mutates executable:"
rg -n -C4 'perform_update|Update::configure|\\.update\\(\\)' src/cmd/self_update.rs

echo
echo "3) Check for any dedicated update/process lock mechanism in source:"
rg -n -C2 'lockfile|flock|fs2|FileExt|advisory|named mutex|mutex' src

Repository: govctl-org/govctl

Length of output: 5153


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Find the full perform_update implementation to see what Update::update does:"
rg -n -A 15 'fn perform_update' src/cmd/self_update.rs

echo
echo "2) Check if there are any SelfUpdate-specific tests:"
rg -n 'SelfUpdate' src/command_router.rs | grep -i test

echo
echo "3) Verify the lock mechanism is actually being used elsewhere for mutations:"
rg -n 'GovRootExclusive' src/command_router.rs | head -20

echo
echo "4) Check if there's any protective locking in the self_update module itself:"
rg -n -C 3 'lock|Lock|Mutex|RwLock' src/cmd/self_update.rs

Repository: govctl-org/govctl

Length of output: 794


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Show all GovRootExclusive assignments to confirm pattern for mutations:"
rg -n 'GovRootExclusive' src/command_router.rs

echo
echo "2) Check if SelfUpdate appears in any tests section:"
sed -n '1100,1300p' src/command_router.rs | rg -n 'SelfUpdate|lock_disposition|test'

echo
echo "3) Search entire test module for any SelfUpdate references:"
rg -n 'fn test' src/command_router.rs | wc -l
rg -n 'SelfUpdate' src/command_router.rs | tail -10

Repository: govctl-org/govctl

Length of output: 1971


🌐 Web query:

self_update rust crate internal locking file mutex

💡 Result:

The self_update Rust crate (latest v0.44.0 as of 2026-04-05) enables in-place self-updates for standalone executables by downloading from backends like GitHub releases, extracting archives, and replacing the running binary using the re-exported self_replace crate. It does not use std::sync::Mutex or explicit file locking mechanisms internally for concurrency control. self_update's update process: - Fetches release info (e.g., via GitHub API). - Downloads the asset to a temp file using tempfile. - Extracts (tar/zip/gz) to a temp directory. - Calls self_replace::self_replace(&new_exe_path) to atomically replace the current executable. self_replace handles platform-specific replacement without Mutex: - Unix: Uses fs::rename for atomic move (no lock needed as running process doesn't hold write lock). - Windows: Copies self to temp with DELETE_ON_CLOSE flag, renames self aside, copies new binary, spawns temp copy to wait and delete old via Windows API (CreateFile with FILE_FLAG_DELETE_ON_CLOSE, no Rust Mutex). No evidence of internal Mutex usage in self_update or self_replace source snippets. Concurrency safety relies on atomic filesystem ops and platform self-replacement tricks. For multi-threaded apps, users must externally synchronize update calls.

Citations:


Mutating self-update path requires exclusive lock to prevent concurrent update races.

Line 215 treats all SelfUpdate invocations as lock-free, but the non---check branch invokes self_update::backends::github::Update::configure().build()?.update()?, which performs an atomic executable replacement. The self_update crate provides no internal Mutex or file-locking mechanism—it relies entirely on OS atomic operations and explicitly requires callers to externally synchronize concurrent update attempts. Without an application-level exclusive lock, concurrent govctl self-update invocations can race and corrupt the binary.

Additionally, the test module includes comprehensive lock disposition assertions for mutations (Init, Edit, WorkItem) but lacks coverage for SelfUpdate.

💡 Proposed minimal planner fix
         match &self.op {
             Op::Builtin(BuiltinOp::Check { .. })
             | Op::Builtin(BuiltinOp::Status)
             | Op::Builtin(BuiltinOp::Verify { .. })
             | Op::Builtin(BuiltinOp::Describe { .. })
             | Op::Builtin(BuiltinOp::Completions { .. })
-            | Op::Builtin(BuiltinOp::SelfUpdate { .. })
+            | Op::Builtin(BuiltinOp::SelfUpdate { check: true })
             | Op::Builtin(BuiltinOp::TagList { .. })
             | Op::Get
             | Op::List { .. }
             | Op::Show { .. } => LockDisposition::None,
+            Op::Builtin(BuiltinOp::SelfUpdate { check: false }) => {
+                LockDisposition::GovRootExclusive
+            }

Follow with test assertions (e.g., assert_eq!(global(Op::Builtin(BuiltinOp::SelfUpdate { check: false })).lock_disposition(), LockDisposition::GovRootExclusive);) added to the test_lock_disposition_requires_lock_for_mutating_commands test.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| Op::Builtin(BuiltinOp::SelfUpdate { .. })
| Op::Builtin(BuiltinOp::TagList { .. })
| Op::Builtin(BuiltinOp::Check { .. })
| Op::Builtin(BuiltinOp::Status)
| Op::Builtin(BuiltinOp::Verify { .. })
| Op::Builtin(BuiltinOp::Describe { .. })
| Op::Builtin(BuiltinOp::Completions { .. })
| Op::Builtin(BuiltinOp::SelfUpdate { check: true })
| Op::Builtin(BuiltinOp::TagList { .. })
| Op::Get
| Op::List { .. }
| Op::Show { .. } => LockDisposition::None,
Op::Builtin(BuiltinOp::SelfUpdate { check: false }) => {
LockDisposition::GovRootExclusive
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/command_router.rs` around lines 215 - 216, The planner currently treats
Op::Builtin(BuiltinOp::SelfUpdate { .. }) as lock-free; change the mapping so
that non-check SelfUpdate requests require LockDisposition::GovRootExclusive
(i.e., treat Op::Builtin(BuiltinOp::SelfUpdate { check: false }) like other
mutating ops), update the branch in the lock disposition resolver that groups
SelfUpdate with TagList to return LockDisposition::GovRootExclusive for mutating
SelfUpdate, and add a unit test assertion in
test_lock_disposition_requires_lock_for_mutating_commands (e.g.,
assert_eq!(global(Op::Builtin(BuiltinOp::SelfUpdate { check: false
})).lock_disposition(), LockDisposition::GovRootExclusive)) to cover the new
behavior while keeping SelfUpdate { check: true } lock-free.

@lucifer1004 lucifer1004 force-pushed the feat/self-update branch 2 times, most recently from 9e0d43e to c108579 Compare April 14, 2026 00:45
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@src/terminal_md.rs`:
- Around line 13-29: The regexes html_comment, html_anchor, html_del, and
md_link are being compiled on every call and silently return the raw markdown on
compile errors; move these known-valid literal patterns to static, precompiled
Regex instances (e.g., using std::sync::LazyLock or once_cell::sync::Lazy) so
compilation happens once at startup, and replace the Err(_) branches with
expect() so the program fails fast with a clear message if a pattern is wrong;
update the code that currently defines html_comment, html_anchor, html_del, and
md_link to reference the newly created static Regex values instead of calling
Regex::new() per-invocation.

In `@src/validate.rs`:
- Around line 913-917: The code currently swallows failures from
crate::cmd::tag::tag_re() by using Err(_) => continue, which disables both
E1101TagInvalidFormat and E1105TagUnknown checks for that tag; instead, when
tag_re() returns Err, record a validation error for that tag (the same error
surfaced by tag_new — e.g., E1101TagInvalidFormat or the same message used
there) and do not skip remaining validation flow in validate_project; replace
the Err(_) => continue arm in the tag_re lookup with logic that
constructs/pushes the appropriate ValidationError for the tag (including the
underlying error text) so the validator fails visibly rather than silently
continuing.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: d2239650-d3f4-4166-91d1-cf0c7a60a549

📥 Commits

Reviewing files that changed from the base of the PR and between dd6ca2f and c108579.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (51)
  • .claude-plugin/marketplace.json
  • .claude/.claude-plugin/plugin.json
  • .claude/skills/commit/SKILL.md
  • CHANGELOG.md
  • Cargo.toml
  • build.rs
  • docs/rfc/RFC-0002.md
  • gov/adr/ADR-0041-self-update-and-cargo-binstall-binary-distribution.toml
  • gov/releases.toml
  • gov/rfc/RFC-0002/clauses/C-GLOBAL-COMMANDS.toml
  • gov/rfc/RFC-0002/clauses/C-SELF-UPDATE.toml
  • gov/rfc/RFC-0002/rfc.toml
  • gov/work/2026-04-13-implement-self-update-command-and-cargo-binstall-support.toml
  • src/cli.rs
  • src/cmd/edit/engine.rs
  • src/cmd/edit/mod.rs
  • src/cmd/edit/path.rs
  • src/cmd/edit/rules.rs
  • src/cmd/edit/runtime.rs
  • src/cmd/lifecycle.rs
  • src/cmd/mod.rs
  • src/cmd/self_update.rs
  • src/cmd/tag.rs
  • src/command_router.rs
  • src/render.rs
  • src/signature.rs
  • src/terminal_md.rs
  • src/tui/mod.rs
  • src/validate.rs
  • src/write.rs
  • tests/common/mod.rs
  • tests/test_agent_dir.rs
  • tests/test_changelog.rs
  • tests/test_delete.rs
  • tests/test_describe.rs
  • tests/test_display_paths.rs
  • tests/test_edit.rs
  • tests/test_errors.rs
  • tests/test_guard.rs
  • tests/test_happy_path.rs
  • tests/test_help.rs
  • tests/test_init.rs
  • tests/test_lifecycle.rs
  • tests/test_lock.rs
  • tests/test_migrate.rs
  • tests/test_move.rs
  • tests/test_rfc_lifecycle.rs
  • tests/test_scan.rs
  • tests/test_source_scan.rs
  • tests/test_tags.rs
  • tests/test_verify.rs
✅ Files skipped from review due to trivial changes (7)
  • .claude/.claude-plugin/plugin.json
  • .claude-plugin/marketplace.json
  • build.rs
  • src/cmd/mod.rs
  • gov/releases.toml
  • gov/work/2026-04-13-implement-self-update-command-and-cargo-binstall-support.toml
  • gov/adr/ADR-0041-self-update-and-cargo-binstall-binary-distribution.toml
🚧 Files skipped from review as they are similar to previous changes (4)
  • .claude/skills/commit/SKILL.md
  • Cargo.toml
  • src/cmd/self_update.rs
  • gov/rfc/RFC-0002/clauses/C-SELF-UPDATE.toml

Comment thread src/terminal_md.rs Outdated
Comment on lines +13 to +29
// Compile regexes inline; these patterns are known-valid literals.
let html_comment = match Regex::new(r"<!--[\s\S]*?-->") {
Ok(r) => r,
Err(_) => return md.to_string(),
};
let html_anchor = match Regex::new(r#"\s*<a\s+id="[^"]*"\s*></a>"#) {
Ok(r) => r,
Err(_) => return md.to_string(),
};
let html_del = match Regex::new(r"</?del>") {
Ok(r) => r,
Err(_) => return md.to_string(),
};
let md_link = match Regex::new(r"\[([^\]]+)\]\([^)]+\.md(?:#[^)]*)?\)") {
Ok(r) => r,
Err(_) => return md.to_string(),
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "terminal_md.rs" | head -20

Repository: govctl-org/govctl

Length of output: 81


🏁 Script executed:

git ls-files src/ | grep -E "\.(rs|ts|tsx|js|jsx)$" | head -20

Repository: govctl-org/govctl

Length of output: 436


🏁 Script executed:

cat -n src/terminal_md.rs

Repository: govctl-org/govctl

Length of output: 6938


🏁 Script executed:

cat Cargo.toml | head -50

Repository: govctl-org/govctl

Length of output: 1249


🏁 Script executed:

grep -E "^\[package\]|^edition|^rust-version" Cargo.toml

Repository: govctl-org/govctl

Length of output: 109


🏁 Script executed:

grep -A 20 "dependencies" Cargo.toml | grep -E "regex|lazy"

Repository: govctl-org/govctl

Length of output: 72


🏁 Script executed:

grep -E "use std::sync" src/terminal_md.rs

Repository: govctl-org/govctl

Length of output: 43


🏁 Script executed:

grep -E "lazy_static|once_cell" Cargo.toml

Repository: govctl-org/govctl

Length of output: 43


🌐 Web query:

LazyLock std::sync Rust stabilized version

💡 Result:

std::sync::LazyLock was stabilized in Rust 1.80.0, released on July 25, 2024.

Citations:


🏁 Script executed:

rg "strip_for_terminal" src/ --type rs

Repository: govctl-org/govctl

Length of output: 87


🏁 Script executed:

rg "strip_for_terminal" --type rs

Repository: govctl-org/govctl

Length of output: 87


🏁 Script executed:

rg "strip_for_terminal" src/

Repository: govctl-org/govctl

Length of output: 746


🏁 Script executed:

rg "Regex::new" src/terminal_md.rs -A 2 -B 2

Repository: govctl-org/govctl

Length of output: 709


Avoid per-call regex compilation and silent fallback on compile failure.

At Lines 14/18/22/26, compiling regexes on each invocation introduces avoidable overhead in a rendering helper called frequently; at Lines 16/20/24/28, returning raw markdown on Err(_) silently bypasses normalization. For literal, known-valid patterns, precompile once with LazyLock and fail fast with expect().

Proposed fix
+use std::sync::LazyLock;
 use regex::Regex;

+static HTML_COMMENT_RE: LazyLock<Regex> =
+    LazyLock::new(|| Regex::new(r"<!--[\s\S]*?-->").expect("valid HTML comment regex"));
+static HTML_ANCHOR_RE: LazyLock<Regex> =
+    LazyLock::new(|| Regex::new(r#"\s*<a\s+id="[^"]*"\s*></a>"#).expect("valid HTML anchor regex"));
+static HTML_DEL_RE: LazyLock<Regex> =
+    LazyLock::new(|| Regex::new(r"</?del>").expect("valid HTML del regex"));
+static MD_LINK_RE: LazyLock<Regex> =
+    LazyLock::new(|| Regex::new(r"\[([^\]]+)\]\([^)]+\.md(?:#[^)]*)?\)").expect("valid markdown link regex"));
+
 pub fn strip_for_terminal(md: &str) -> String {
-    // Compile regexes inline; these patterns are known-valid literals.
-    let html_comment = match Regex::new(r"<!--[\s\S]*?-->") {
-        Ok(r) => r,
-        Err(_) => return md.to_string(),
-    };
-    let html_anchor = match Regex::new(r#"\s*<a\s+id="[^"]*"\s*></a>"#) {
-        Ok(r) => r,
-        Err(_) => return md.to_string(),
-    };
-    let html_del = match Regex::new(r"</?del>") {
-        Ok(r) => r,
-        Err(_) => return md.to_string(),
-    };
-    let md_link = match Regex::new(r"\[([^\]]+)\]\([^)]+\.md(?:#[^)]*)?\)") {
-        Ok(r) => r,
-        Err(_) => return md.to_string(),
-    };
-
-    let s = html_comment.replace_all(md, "");
-    let s = html_anchor.replace_all(&s, "");
-    let s = html_del.replace_all(&s, "");
-    let s = md_link.replace_all(&s, "$1");
+    let s = HTML_COMMENT_RE.replace_all(md, "");
+    let s = HTML_ANCHOR_RE.replace_all(&s, "");
+    let s = HTML_DEL_RE.replace_all(&s, "");
+    let s = MD_LINK_RE.replace_all(&s, "$1");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/terminal_md.rs` around lines 13 - 29, The regexes html_comment,
html_anchor, html_del, and md_link are being compiled on every call and silently
return the raw markdown on compile errors; move these known-valid literal
patterns to static, precompiled Regex instances (e.g., using std::sync::LazyLock
or once_cell::sync::Lazy) so compilation happens once at startup, and replace
the Err(_) branches with expect() so the program fails fast with a clear message
if a pattern is wrong; update the code that currently defines html_comment,
html_anchor, html_del, and md_link to reference the newly created static Regex
values instead of calling Regex::new() per-invocation.

Comment thread src/validate.rs
Add [lints.clippy] with deny rules for unwrap_used, expect_used, and
panic. Refactor all production code and tests to use Result + ? instead.

- Production: replace LazyLock<Regex>.expect() with inline Regex::new()?,
  .parent().unwrap() with .ok_or_else()?, and all .expect() on Options
  with .ok_or_else()?
- Tests: convert all test functions to return Result<(), Box<dyn Error>>,
  replace unwrap/expect with ?, update common test helpers to return Result
- Build script: exempt via crate-level allow (panicking on failure is correct)
@lucifer1004 lucifer1004 merged commit f47fb82 into main Apr 14, 2026
8 checks passed
@lucifer1004 lucifer1004 deleted the feat/self-update branch April 14, 2026 01:20
@coderabbitai coderabbitai Bot mentioned this pull request Apr 21, 2026
Merged
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