Skip to content

Add status subcommand: read-only deployment view across hosts - #30

Merged
jfding merged 14 commits into
masterfrom
feat/status-subcommand
May 24, 2026
Merged

Add status subcommand: read-only deployment view across hosts#30
jfding merged 14 commits into
masterfrom
feat/status-subcommand

Conversation

@jfding

@jfding jfding commented May 24, 2026

Copy link
Copy Markdown
Owner

Summary

Adds git-supervisor status — a read-only subcommand that SSHes each configured host in parallel and prints a grouped per-host/per-repo view of deployed branches, releases, the prod.latest symlink target, stale dirs, and user dot-flag files. Read-only: no fetch, no clone, no docker actions.

Example output:

host: app1
  webapp  (latest: v2.1.5)
    dev          def4567  3d ago      debugging
    main         abc1234  2m ago      -
    --
    v2.1.5       fed6543  1h ago      -
    v2.1.4       cba9876  2026-04-12  -
    stale:
      webapp.oldfeature.to-be-removed   3d ago

host: app3  ERROR
  ssh: connect to host app3.example.com port 22: Connection timed out

What's in this branch

  • New subcommand Status(StatusArgs) in src/main.rs, with --host PATTERN (repeatable, glob */?, union semantics; zero matches exits non-zero).
  • Remote probe src/status_probe.sh (~120 lines bash) embedded via include_str!. Uses the same DIR_BASE env contract as check-push.sh. Longest-prefix matching against \$DIR_REPOS correctly handles dotted repo names like my.api. Distinguishes missing \$DIR_COPIES (legitimate empty, exit 0) from unreadable (stderr + exit 1).
  • Renderer in src/status.rs: grouped hierarchical layout, branches sorted alphabetically, releases sorted version-desc via a Rust port of check-push.sh's sort_version_tags_desc (handles vX.Y.Z and vYYYYQN.X.Y Q-notation). Adaptive relative time (just now / Nm/Nh/Nd ago / YYYY-MM-DD after 7d). 7-char SHA truncation. Color rules for .debugging/.stopping (red), .skipping (grey), stale section (yellow), latest annotation (green/yellow/red by health).
  • Parallel fanout via std::thread::scope, mirroring run_cycle. Output buffered and sorted by host ID for deterministic rendering.
  • SSH refactor in src/ssh.rs: extracted build_ssh_command shared by ssh_run / ssh_run_with_stdin / new ssh_run_capture. ssh_run_capture pipes all three streams and uses wait_with_output to drain stdout+stderr concurrently (avoids a deadlock latent in the naive sequential-read pattern).
  • Failed/empty hosts appear in the table as ERROR (red) or (no deployments yet) (grey) — never silently dropped.

Test plan

  • Unit tests: 24 in src/status.rs (parser + helpers: time format, glob, version sort, group, repo header)
  • Unit tests: 5 in src/ssh.rs (incl. ssh_run_capture stdout + stderr+exit-code propagation)
  • Unit tests: 2 in src/main.rs (CLI parser for --host flag)
  • Integration tests: 3 in tests/integration_status.rs against localhost (happy path, dotted repo my.api, --host zero-match)
  • Full suite: `cargo test` → 79 passed, 2 ignored (no regressions in existing 47 tests)
  • `cargo clippy --all-targets -- -D warnings` → clean
  • `cargo build --release` → clean

Design plan

The design was iterated through a structured interview before implementation (see `docs/superpowers/plans/2026-05-20-01-status-subcommand.md` for the full rationale on each decision: output format, layout, sort order, repo-name parsing, concurrency, SSH helper architecture, exit code semantics, time format, probe robustness, env-var contract, filter flags, renderer specifics, and test tiers).

🤖 Generated with Claude Code

jfding and others added 14 commits April 19, 2026 08:05
Signed-off-by: JF Ding <jfding@gmail.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Create src/status_probe.sh with TSV-per-finding output, dotted-repo
longest-prefix matching, and flag collection. Expose it via
STATUS_PROBE_SCRIPT in src/status.rs using include_str!.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Stale .to-be-removed dirs can carry .stopping/.skipping (check-push.sh:648-657);
  previously hardcoded flags='-' silently dropped them.
- Add 'local f' to collect_flags loop.
- Document probe exit code semantics in header comment.
The sequential stdout-then-stderr read in ssh_run_capture risks deadlock if
remote stderr fills the 64 KiB pipe buffer before stdout EOFs. wait_with_output
drains both pipes concurrently via internal threads.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… skew

- Add direct tests for cmp_version_tags_desc (basic, Q-notation, missing segments)
- Add tests for host_filter_matches (empty patterns, union semantics)
- Add test for format_relative_time future-mtime case
- Remove dead Debug derive on private HostOutcome enum
- Comment intent for clock-skew saturation in format_relative_time
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jfding
jfding merged commit 300039b into master May 24, 2026
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