topgrade-rs/topgrade
Upgrade all the things
Mixed signals — read the receipts
weakest axiscopyleft license (GPL-3.0) — review compatibility; no tests detected
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit today
- ✓21+ active contributors
- ✓Distributed ownership (top contributor 33% of recent commits)
Show all 7 evidence items →Show less
- ✓GPL-3.0 licensed
- ✓CI configured
- ⚠GPL-3.0 is copyleft — check downstream compatibility
- ⚠No test directory detected
What would change the summary?
- →Use as dependency Concerns → Mixed if: relicense under MIT/Apache-2.0 (rare for established libs)
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/topgrade-rs/topgrade)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/topgrade-rs/topgrade on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: topgrade-rs/topgrade
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/topgrade-rs/topgrade shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Mixed signals — read the receipts
- Last commit today
- 21+ active contributors
- Distributed ownership (top contributor 33% of recent commits)
- GPL-3.0 licensed
- CI configured
- ⚠ GPL-3.0 is copyleft — check downstream compatibility
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live topgrade-rs/topgrade
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/topgrade-rs/topgrade.
What it runs against: a local clone of topgrade-rs/topgrade — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in topgrade-rs/topgrade | Confirms the artifact applies here, not a fork |
| 2 | License is still GPL-3.0 | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 30 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of topgrade-rs/topgrade. If you don't
# have one yet, run these first:
#
# git clone https://github.com/topgrade-rs/topgrade.git
# cd topgrade
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of topgrade-rs/topgrade and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "topgrade-rs/topgrade(\\.git)?\\b" \\
&& ok "origin remote is topgrade-rs/topgrade" \\
|| miss "origin remote is not topgrade-rs/topgrade (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(GPL-3\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"GPL-3\\.0\"" package.json 2>/dev/null) \\
&& ok "license is GPL-3.0" \\
|| miss "license drift — was GPL-3.0 at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "src/main.rs" \\
&& ok "src/main.rs" \\
|| miss "missing critical file: src/main.rs"
test -f "src/step.rs" \\
&& ok "src/step.rs" \\
|| miss "missing critical file: src/step.rs"
test -f "src/steps/mod.rs" \\
&& ok "src/steps/mod.rs" \\
|| miss "missing critical file: src/steps/mod.rs"
test -f "src/config.rs" \\
&& ok "src/config.rs" \\
|| miss "missing critical file: src/config.rs"
test -f "src/executor.rs" \\
&& ok "src/executor.rs" \\
|| miss "missing critical file: src/executor.rs"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 30 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~0d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/topgrade-rs/topgrade"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Topgrade is a cross-platform CLI tool written in Rust that automatically detects and runs the appropriate upgrade commands for all installed package managers and development tools on a user's system. Instead of running manual shell one-liners for apt, brew, cargo, pip, npm, etc., users run topgrade once to upgrade everything—from system packages to language-specific tools, JetBrains IDEs, Vim plugins, and more. It's self-updating and runs on Linux, macOS, and Windows. Single-crate binary project (Cargo.toml at root). Core architecture: src/command.rs defines upgrade steps, src/executor.rs runs them via src/execution_context.rs, src/config.rs handles TOML config from config.example.toml, src/ctrlc/ provides interrupt handling (Unix/Windows variants), src/error.rs uses thiserror for error types. I18n via rust-i18n with locales/app.yml. Build script in build.rs. Extensive GitHub Actions workflows in .github/workflows/ for CI, releases, and distribution.
👥Who it's for
System administrators, DevOps engineers, and developers who maintain multiple systems or development environments and want to keep all their tools (system packages, language runtimes, package managers, editor plugins) up-to-date with a single command instead of remembering tool-specific upgrade procedures.
🌱Maturity & risk
Production-ready and actively maintained. Version 17.4.0 with comprehensive CI/CD (GitHub Actions workflows for releases, testing, security scanning, and package distribution across Homebrew, AUR, PyPI, Winget, Fedora Copr). The MSRV is Rust 1.88.0 and the codebase is 478KB of Rust with organized error handling and multi-platform support, indicating a mature, well-tested tool.
Low risk for production use. Single-maintainer concern (fork of r-darwish/topgrade now maintained by topgrade-rs org), but has active release cycles and CI/CD. Moderate dependency footprint (tokio, serde, clap, regex, etc.) but all are well-established. The breaking_changes.rs module suggests attention to backward compatibility. Main risk: relies on external tool detection (which crate) and correct package manager invocation—issues could affect multiple systems simultaneously.
Active areas of work
Active maintenance with release automation (release-plz.toml for automated versioning and releases). Recent focus: i18n checks (.github/workflows/check_i18n.yml), security scanning (scorecards, dependency-review), and multi-platform distribution (AUR, Homebrew, PyPI, Winget, Fedora Copr). CHANGELOG.md tracks user-facing changes; pre-commit config and PR templates enforce code quality.
🚀Get running
git clone https://github.com/topgrade-rs/topgrade.git
cd topgrade
cargo build --release
./target/release/topgrade --help
Or install from crates.io: cargo install topgrade. Run topgrade to upgrade all detected tools, or topgrade --config config.example.toml to test with the example configuration.
Daily commands:
cargo build --release
./target/release/topgrade
For development with logging: RUST_LOG=debug cargo run. Configuration via ~/.config/topgrade/topgrade.toml (Linux/macOS) or platform-specific config paths (see etcetera crate).
🗺️Map of the codebase
src/main.rs— Application entry point orchestrating the upgrade execution flow and CLI interface initialization.src/step.rs— Core trait defining the Step abstraction that all upgrade tasks implement; foundation for extensibility.src/steps/mod.rs— Module registry and factory for all upgrade steps; must be updated when adding new upgrade tasks.src/config.rs— Configuration parsing and validation logic that controls which steps run; critical for user customization.src/executor.rs— Execution engine managing step ordering, parallelism, and error handling across the upgrade pipeline.Cargo.toml— Project manifest defining dependencies (clap, serde, toml, strum) and minimum Rust version 1.88.0.src/steps/os/mod.rs— OS-specific step dispatcher selecting platform-appropriate upgrade logic for 40+ Linux distributions and BSD variants.
🧩Components & responsibilities
- CLI (main.rs + clap) — Parse command-line arguments, load config, bootstrap execution engine
🛠️How to make changes
Add a new OS-agnostic upgrade step (e.g., new language runtime)
- Create a new public function in src/steps/mod.rs returning Box<dyn Step> (
src/steps/mod.rs) - Implement the Step trait with execute(&self, ctx: &ExecutionContext) method (
src/steps/generic.rs (as reference) or new src/steps/my_tool.rs) - Add step instance to the steps vector in main.rs or registration in executor.rs (
src/executor.rs) - Add optional config section in Config struct to gate the step conditionally (
src/config.rs)
Add support for a new Linux distribution
- Create os_release file under src/steps/os/os_release/{distro_name}/ (
src/steps/os/os_release/ (see existing dirs like debian, fedora)) - Add distro detection logic in src/steps/os/linux.rs identify_distro() function (
src/steps/os/linux.rs) - Create distro-specific step module src/steps/os/{distro_name}.rs with package manager integration (
src/steps/os/mod.rs (import and export new module)) - Implement Step trait with execute() calling appropriate package managers (pacman, apt, dnf, etc.) (
src/steps/os/{distro_name}.rs)
Add a new configuration option to control step behavior
- Add optional field to appropriate struct in src/config.rs (e.g., new_tool: Option<NewToolConfig>) (
src/config.rs) - Document field in config.example.toml with usage examples (
config.example.toml) - Read config value in step's execute() method and conditionally skip or configure behavior (
src/steps/my_tool.rs (or relevant step file)) - Add CLI override flag via clap derive macro in main.rs if needed for user convenience (
src/main.rs)
🔧Why these technologies
- Rust + Cargo — Type-safe systems programming with zero-cost abstractions; compile-time safety for managing complex upgrade orchestration across 40+ distributions
- Clap (CLI parsing) — Declarative command-line argument handling with automatic help generation, shell completion, and man page generation (via clap_mangen)
- Serde + TOML — Human-readable configuration files with strong type safety; preserve_order feature maintains user-authored config structure
- Strum (enum utilities) — Procedural macro for converting enums to/from strings; simplifies OS and distro identification
- Walkdir (filesystem traversal) — Efficient recursive directory scanning for generic git-based and file-watching upgrade tasks
⚖️Trade-offs already made
-
Sequential step execution by default with optional parallelism
- Why: Prevents resource contention and ensures predictable output ordering for user debugging
- Consequence: Slower overall runtime on multicore systems, but safer and more maintainable; parallelism requires careful step design
-
Shell command execution via subprocesses (runner.rs) rather than direct library calls
- Why: Decouples topgrade from package manager internals; supports any tool that has a CLI
- Consequence: Less efficient (subprocess overhead) and vulnerable to shell injection if not careful; requires parsing textual output
-
40+ hardcoded Linux distribution checks in src/steps/os/os_release/
- Why: Precise per-distro package manager selection and quirk handling
- Consequence: High maintenance burden; scales linearly with new distro support requests
-
Single-threaded main loop with explicit interrupt handling (src/ctrlc/)
- Why: Predictable signal delivery; avoids race conditions during step cleanup
- Consequence: Cannot interrupt mid-command; user waits for current step to finish
🚫Non-goals (don't propose these)
- Does not manage system dependencies or handle version conflicts between upgrades
- Not a package manager itself; only orchestrates existing package managers
- Does not provide rollback capability or snapshots of pre-upgrade state
- Does not handle authentication interactively; assumes SSH keys and credentials are pre-configured
- Not designed for unattended/headless server environments without manual config (requires user-authored TOML)
- Does not parallelize step execution across multiple machines or containers
🪤Traps & gotchas
- Config paths are platform-specific (etcetera crate handles this—Linux ~/.config/topgrade/topgrade.toml, macOS ~/Library/Application Support/topgrade/topgrade.toml). 2) Shell detection: executor relies on detecting shell from environment or system; some tools expect specific shells (bash, zsh, fish). 3) Tool availability assumptions: if a package manager isn't installed, topgrade silently skips it (by design), but misconfiguration can cause unexpected behavior. 4) Async runtime: tokio requires rt-multi-thread feature—blocking operations in upgrade commands could deadlock if not careful. 5) Ctrl-C handling: custom implementation in src/ctrlc/ replaces default behavior; platform-specific differences (Unix signals vs Windows console events) could cause subtle bugs.
🏗️Architecture
💡Concepts to learn
- Cross-platform abstraction (execution_context.rs) — Topgrade runs on Linux, macOS, and Windows with different tool ecosystems and file paths—learning how execution_context.rs provides platform-aware context (via etcetera for config paths, feature gates for OS-specific code) is critical to extending topgrade safely
- Asynchronous task orchestration (tokio, futures) — Topgrade uses tokio's multi-threaded runtime to run multiple upgrade steps concurrently and handle I/O-bound operations (waiting for external processes)—understanding how executor.rs uses futures::join_all or tokio::task is essential for performance and debugging hanging upgrades
- Signal handling (ctrlc/unix.rs, ctrlc/windows.rs) — Graceful shutdown on Ctrl-C is critical in a tool that runs long-lived external processes—topgrade's custom implementation differs from default panic, requiring platform-specific signal/console event handling not typical in Rust apps
- Configuration as code (TOML parsing, config.rs) — Users customize which tools topgrade upgrades via config.example.toml—understanding how serde + toml deserialize into Rust structs and how to add new config options is essential for feature requests
- Internationalization via Rust macros (rust-i18n) — Topgrade supports multiple locales (locales/app.yml)—learning how rust-i18n macros translate strings at compile-time with locale fallback is needed to add or fix translations
- Error handling with thiserror and color-eyre — Topgrade's error handling (src/error.rs using thiserror for types, color-eyre for pretty output) provides context-rich diagnostics—understanding these patterns helps debug and extend error reporting without losing stack context
- Tool detection via PATH and which crate — Topgrade dynamically detects installed tools (via which_crate) rather than hardcoding paths—this determines which upgrade steps run, making detection logic critical and platform-dependent (PATH variables differ on Windows)
🔗Related repos
r-darwish/topgrade— Original upstream project that topgrade-rs forked from to maintain after upstream became inactive—useful for historical contextaquaproj/aqua— Alternative tool that manages and updates CLI tools (declarative YAML-based) instead of auto-detecting them—different philosophy but solves similar problemJohnnyMorganz/stylua— Rust CLI tool in similar ecosystem using clap, thiserror, and tokio—good reference for Rust CLI patterns topgrade employssharkdp/bat— Rust CLI with console output and multi-platform support—shares similar dependency stack (console crate, error handling patterns)rustwasm/wasm-pack— Rust tool that orchestrates multiple external tools and package managers—comparable complexity in tool detection and invocation
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add integration tests for step execution and error handling
The repo has comprehensive step implementations across src/steps/ (containers.rs, git.rs, go.rs, node.rs, os/*.rs, etc.) but lacks visible integration tests. Given the complexity of executing different package managers and handling platform-specific logic, integration tests would prevent regressions when adding new steps or modifying existing ones. This is especially valuable since topgrade coordinates multiple external tools.
- [ ] Create tests/integration/ directory structure mirroring src/steps/
- [ ] Add tests for src/steps/generic.rs (generic upgrade logic) with mocked command execution
- [ ] Add platform-specific tests for src/steps/os/archlinux.rs and src/steps/os/freebsd.rs using conditional compilation
- [ ] Test error handling in src/error.rs and src/executor.rs with simulated command failures
- [ ] Add a CI workflow in .github/workflows/ to run integration tests on multiple platforms
Add missing documentation for localization system
The repo uses rust-i18n (dependency visible in Cargo.toml) with locales/app.yml present, but there's no LOCALIZATION.md or i18n guide in the docs. New contributors wanting to add language support or translations have no clear starting point. Documentation explaining the workflow for adding translations would lower the barrier to contribution.
- [ ] Create LOCALIZATION.md documenting the rust-i18n setup and locales/app.yml structure
- [ ] Document how to add a new language (e.g., adding locales/fr.yml for French)
- [ ] Reference the .github/workflows/check_i18n.yml workflow and explain its validation checks
- [ ] Add examples of macro usage from src/ files that reference i18n keys
- [ ] Link the new guide from CONTRIBUTING.md
Create a plugin/step development guide with example template
Adding new upgrade steps (for new package managers or tools) requires understanding src/steps/mod.rs, src/step.rs, and the pattern used across src/steps/*.rs files. A structured guide with a template step implementation would help contributors add support for new package managers, which is a clear gap given the number of OS-specific steps already present.
- [ ] Create doc/STEP_DEVELOPMENT.md explaining the Step trait from src/step.rs
- [ ] Document required methods: execute(), describe(), are_you_interested_in_this_system()
- [ ] Add a minimal template file (e.g., examples/step_template.rs) showing a new package manager step
- [ ] Reference examples from src/steps/node.rs (simple) and src/steps/os/archlinux.rs (platform-specific)
- [ ] Explain integration with src/steps/mod.rs and how to register new steps
- [ ] Link from CONTRIBUTING.md and README.md
🌿Good first issues
- Add detection and upgrade support for a new package manager (e.g.,
uvormise)—start in src/command.rs by adding a Step variant, then add detection logic in src/executor.rs. Example: search the codebase for how 'cargo' or 'pip' are handled. - Write unit tests for src/config.rs config parsing—the module lacks dedicated tests. Create tests/config_test.rs or add #[cfg(test)] module to config.rs covering parsing of enable/disable flags for various steps from TOML.
- Improve i18n coverage—locales/app.yml is incomplete for all user-facing strings. Audit src/ for hardcoded English strings (search for println!, eprintln!, error messages) and move them to locales/app.yml with rust-i18n macros.
⭐Top contributors
Click to expand
Top contributors
- @renovate[bot] — 33 commits
- @GideonBear — 24 commits
- @uwuclxdy — 10 commits
- @github-actions[bot] — 6 commits
- @pre-commit-ci[bot] — 5 commits
📝Recent commits
Click to expand
Recent commits
0e7b871— chore(pre-commit): autoupdate (#1997) (pre-commit-ci[bot])b998eee— chore(deps): update github/codeql-action action to v4.35.3 (#1994) (renovate[bot])06170db— chore(deps): lock file maintenance (#1996) (renovate[bot])ea24cb1— feat(mise): add options for quiet, silent, verbose, and add assume_yes support (#1990) (rubiin)ded64a8— feat(clamav): don't run if systemd autoupdater is active (#1988) (uwuclxdy)518f48a— chore(deps): lock file maintenance (#1987) (renovate[bot])286fa31— feat: passwordless reboot and shutdown usingsystem_shutdown(#1985) (uwuclxdy)7ff2403— feat(cargo): add locked option for cargo install-update (#1980) (benner)ae6cc7d— chore: release v17.4.0 (#1954) (github-actions[bot])790208a— fix(mise): handle all self-update errors (#1978) (salim-b)
🔒Security observations
The Topgrade project demonstrates reasonable security practices for a system upgrade utility. The primary concerns are the invalid Rust edition declaration (likely a documentation error), and the inherent risks of executing system commands across multiple platforms. The codebase uses well-maintained dependencies and implements proper error handling with thiserror. Security controls appear adequate for the application's purpose, but should focus on consistent input validation across all OS-specific code paths and user input handling. The project has a security policy in place for vulnerability reporting. No hardcoded credentials, secrets, or injection vulnerabilities were identified in the provided file structure.
- Medium · Outdated Rust Edition Declaration —
Cargo.toml. The Cargo.toml specifies edition = "2024" which is not a valid Rust edition. Valid editions are 2015, 2018, and 2021. This could cause build failures or unexpected behavior. Fix: Change edition to "2021" (the latest stable Rust edition as of the rust-version requirement of 1.88.0) - Low · Broad Dependency on System Command Execution —
src/steps, src/execution_context.rs, src/executor.rs. The codebase uses tokio::process and shell-words for executing system commands across multiple OS platforms. While necessary for the application's purpose, this increases attack surface if user input is not properly validated. Fix: Ensure all user inputs and configuration values are validated and sanitized before being passed to system commands. Use allowlists for command execution where possible. - Low · Multiple OS-Specific Command Execution Paths —
src/steps/os/. The application executes different commands across multiple operating systems (Linux, macOS, BSD, Android). Platform-specific implementations may have inconsistent security controls. Fix: Implement consistent input validation and command sanitization across all OS-specific implementations. Regularly audit platform-specific code paths. - Low · Dynamic Configuration File Parsing —
src/config.rs, Cargo.toml (toml dependency). The application parses TOML configuration files with features like 'preserve_order' enabled. Malformed or malicious configuration files could potentially cause issues. Fix: Validate configuration file structure and values after parsing. Implement maximum file size limits and safe defaults for all configuration options. - Low · Shell Expansion in Configuration —
Cargo.toml (shellexpand dependency), src/config.rs. The shellexpand dependency is used, which expands environment variables and user home directories in configuration values. This could expose sensitive environment variables if configuration is logged or displayed. Fix: Be cautious when logging or displaying expanded configuration values. Consider masking sensitive variables (API keys, tokens) before display. - Low · Regex Dependency Usage —
Cargo.toml (regex dependency). Regular expression processing is used in the codebase. Untrusted regex patterns could potentially cause ReDoS (Regular Expression Denial of Service) attacks. Fix: If regex patterns come from user input or external sources, validate them first. Set execution timeouts for regex operations. - Low · No Apparent Input Validation for Command-Line Arguments —
src/main.rs, src/command.rs. While clap is used for argument parsing, file structure doesn't show explicit validation of user-provided values before they're used in system operations. Fix: Implement explicit validation for all user inputs. Define and enforce constraints on argument values, file paths, and configuration parameters.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.