The Problem
Engineers with many git checkouts lose visibility into which repos have uncommitted work, unpushed commits, or overdue releases. Manually auditing each directory is time-consuming and error-prone, especially when side branches and half-finished merges hide the true state of a fleet.
What This Does
drydock is a live TUI dashboard that scans configured directories and surfaces three axes per repo: working state (dirty, stashed, conflicted), push state (unpushed across all branches), and release state (unreleased, released, needs release). It uses colour coding so rows needing action stand out without reading text. The codebase is organized around a central model (crates/drydock/src/model.rs) that defines label, branch, unpushed, and unpulled, while crates/drydock/src/git.rs handles git operations via run_git and try_git. The TUI enters through crates/drydock/src/tui/mod.rs (entry point run, reaching 91 functions) and renders via crates/drydock/src/tui/ui.rs. Filters and search are handled by crates/drydock/src/filter.rs (29 functions, called from 8 files) and crates/drydock/src/probe.rs (7 functions, defines discover_repos, sweep). Release-state determination lives in crates/drydock/src/report.rs (10 functions, defines table, summary, groups_table). The entry point main in crates/drydock/src/main.rs:27 reaches 67 functions and is the starting point for all CLI and TUI flows.
How It Is Wired
Execution starts at crates/drydock/src/main.rs:27 (main), which calls init_tracing, load_config, and gather. gather flows into crates/drydock/src/probe.rs to discover_repos and sweep, then into git.rs via run_git and try_git. Call data shows now_unix, current, and spawn are each called from 10+ places, making them high-impact touchpoints. The internal call graph has 339 resolved edges; handle_normal_key toggles and moves with x10 and x6 frequencies, and run_loop calls spawn five times. Key hubs with wide blast radius: recompute (called from 9 places), emit (from 3+ paths across sweep, emit), and release_state (from 6 places). Files exceeding 800 lines—crates/drydock/src/git.rs, crates/drydock/src/tui/mod.rs, crates/drydock/src/tui/ui.rs—carry the highest ripple risk; a change in any of these ripples widely across the call graph.
How To Use It
Setup: Clone from source:
git clone https://github.com/moses-y/drydock
cd drydock
make install # installs into ~/.local/bin
Alternatively, cargo install drydock or use Homebrew brew install yetidevworks/drydock/drydock.
Configuration: Default config directory is ~/Projects. No explicit config file is required at startup; directories are scanned from the configured path. Environment variables or custom paths can be adjusted in crates/drydock/src/config.rs (21 functions, defines default, as_git_arg).
Running it: Launch the live dashboard:
drydock
For machine-readable output:
drydock list --unpushed --group acme --json
For a one-shot table then exit:
drydock list --dirty --since 1d
For releasable filtering:
drydock releasable --min-commits 3
For status of a single repo:
drydock status .
Real-World Use
A team maintaining 120 internal services runs drydock on a daily cron. The TUI shows which repos have unpushed side-branch work, allowing the lead to tag and release only those in needs release state (◆). A developer filters to dirty before committing, sees the exact count of unstaged/untracked files per repo via the detail view (⏎), and uses d/u/b filters to isolate repos needing attention. After a release pass, a clears filters and the dashboard resets to the clean overview.
Code Health & Issues
- [HIGH/cognitive_load] Deep nesting x8 across
crates/drydock/src/report.rs,crates/drydock/src/discover.rs,crates/drydock/src/filter.rs; max indentation depth 8 makes control flow hard to follow. Fix: flatten with early returns/guard clauses; extract inner blocks. - [MEDIUM/clarity] Duplicated code blocks: 4 repeated 6-line segments across
crates/drydock/src/report.rsandcrates/drydock/src/tui/ui.rs. Fix: extract shared helpers; DRY the repeated logic. - [MEDIUM/cognitive_load] Oversized file x3:
crates/drydock/src/git.rs(835 lines),crates/drydock/src/tui/mod.rs,crates/drydock/src/tui/ui.rs. Fix: split into cohesive units by responsibility. - [HIGH] Pin third-party GitHub Actions to commit SHA –
.github/workflows/ci.ymlusesdtolnay/rust-toolchain@stable,softprops/action-gh-release@v3. A tag can move, so CI runs with whatever owner last pushed, risking secret exposure. Fix: replace@vNwith 40-character commit SHA; let Dependabot bump SHAs. - [HIGH] Add a test suite – 15 source files, no test files. Any change ships with no signal existing behaviour holds, so regressions reach production undetected. Fix: add one test per public entry point, then a CI step that runs them.
- [MEDIUM] Declare least-privilege permissions for GITHUB_TOKEN –
.github/workflows/ci.ymldeclares no permissions; token inherits repo default, allowing injected steps to push or mint releases. Fix: addpermissions: contents: readat the top of the workflow and widen per job only where needed. - [MEDIUM] Enable Dependabot or Renovate – 2 manifest files (
Cargo.toml,Cargo.lock), no update bot configured. Without a bot, advisories sit unpatched. Fix: commit.github/dependabot.ymlcovering Rust and GitHub Actions ecosystems. - [MEDIUM] Gate pull requests on a dependency vulnerability scan – No dependency scan in CI. This is the one gate that would catch a known-vulnerable package before it reaches a build. Fix: add
dependency-review-actiononpull_request, orosv-scanneron push and schedule. - [LOW] Set timeout-minutes on workflow jobs – 2 workflows declare no job timeout; a wedged step runs to the six-hour platform default. Fix: add
timeout-minuteswith a realistic bound to each job.
The Bottom Line
drydock fills a concrete visibility gap for engineers managing many git repos, delivering a live, colour-coded dashboard of working, push, and release state without leaving the terminal. The codebase is functional but contains three structural pain points—deep nesting, duplicated blocks, and oversized files—that moderate refactoring effort for improved maintainability. The SDLC gaps (no tests, unpinned Action SHAs, missing Dependabot) are standard for small Rust projects and should be addressed if the tool is to be sustained long-term. It’s well-suited for teams with 30+ checked-out repos who need a single source of truth for repo state without scripting git commands manually.