Skip to content

zero repo-info: local (network-free) repository characterizer - #150

Merged
gnanam1990 merged 7 commits into
mainfrom
repo-info
Jun 10, 2026
Merged

zero repo-info: local (network-free) repository characterizer#150
gnanam1990 merged 7 commits into
mainfrom
repo-info

Conversation

@gnanam1990

@gnanam1990 gnanam1990 commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds zero repo-info [--json] [--cwd <dir>] — a repository characterizer that reads local git only (no network). Dep-free, additive (a brand-new command + package; nothing else changes).

Reports: file / directory / max-depth / estimated-LOC counts; per-language breakdown (LOC + file counts) with primary language; workspace type + package count; build / test / CI tooling; and bounded git-history metrics (branch, remote URL, age, contributors-90d, commit-velocity-30d).

This is the network-free residue of the upstream telemetry module — the OTEL/metrics-egress half is intentionally not ported.

Design

  • internal/repoinfo (new, stdlib-only): Collect(ctx, Options{Cwd, Now, RunGit}) (Info, error) behind an injectable RunGit (default shells local git). Streams git ls-tree -r -l -z HEAD, plus rev-parse / remote get-url origin / log / rev-list. languages.go (extension→language map) and detect.go (build/test/CI + workspace tables) are pure helpers.
  • internal/cli/repoinfo.go: runRepoInfo formats text or --json; dispatch case "repo-info" in app.go + help line.

Guarantees

  • Network-free: only the read-only local subcommands ls-tree, rev-parse, remote get-url, log, rev-list are ever invoked; manifest reads are local os.ReadFile. Enforced by a test that records every subcommand against an allowlist.
  • Robust parsing: -z (NUL-terminated, unquoted paths) so non-ASCII/special filenames aren't dropped; gitlinks (size -) skipped; DirectoryCount counts passthrough directories (expands each file's ancestors), verified against git ls-tree -r -t.
  • Correct age: first-commit via log --max-parents=0 (NOT log --reverse -1, which returns the latest commit); oldest root chosen for multi-root repos.
  • Fail-soft: non-git / no-commits → friendly message (stderr, exit 1); each history metric is omitted (not fatal) on error; a 20s context bounds runtime; --max-count bounds history walking.
  • Defaults / scope: focused field set (not the upstream 11-category config taxonomy); pure data/prose formats (json/yaml/toml/md) are excluded from the language ranking so "primary language" stays meaningful (markup-code like html/css is kept).

Test Plan

  • go build ./... / GOOS=windows GOARCH=amd64 go build ./... clean
  • go vet ./... clean; gofmt clean
  • go test ./... green (repoinfo: parse/languages/detect/age/contributors/network-free; cli: parse/format/hermetic-JSON)
  • go test -race ./internal/{repoinfo,cli}/... green
  • Smoke: zero repo-info / --json on this repo (Go primary, DirectoryCount matches git ls-tree -r -t, real age)
  • Adversarial whole-diff review: caught + fixed DirectoryCount undercount and core.quotePath filename corruption

Reviewer focus

  • Network-free invariant (allowlisted subcommands; no fetch/ls-remote).
  • -z parsing + ancestor-based DirectoryCount.
  • Age from root commit.

Summary by CodeRabbit

  • New Features

    • Added repo-info (alias: repoinfo) CLI command with help entry; supports --json and --cwd and prints text or pretty-JSON summaries.
    • Reports file/dir counts, language breakdown and LOC estimates, workspace/package hints, and optional Git metadata (branch, sanitized remote URL, repo age, contributors, commit velocity).
  • Tests

    • Added unit and end-to-end tests for argument parsing, output formatting, detection heuristics, remote-URL sanitization, and repository metrics.

git applies -1 before --reverse, so the old call returned the LATEST commit
(age always ~0). Use --max-parents=0 (root commits) and take the oldest.
…tests

Adversarial-review fixes:
- DirectoryCount now counts every directory on the path to a file (expand each
  file's ancestors), so directories holding only subdirectories are included.
  git ls-tree -r lists blobs only, so the previous parent-of-file count
  undercounted. Verified against 'git ls-tree -r -t | awk $2==tree'.
- ls-tree now uses -z (NUL-terminated, unquoted paths) instead of newline
  scanning, so non-ASCII/special filenames (which git C-quotes by default) are
  parsed correctly instead of being silently dropped. Drops the bufio scanner.
- Tests: assert DirectoryCount/MaxDepth; add an age-from-oldest-root-commit test
  (would catch the old log --reverse -1 bug); make the CLI JSON test hermetic
  via a temp git repo.
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Zero automated PR review

Verdict: No blockers found

Blockers

  • None found.

Validation

  • [pass] Diff hygiene: git diff --check
  • [pass] Tests: go test ./...
  • [pass] Build: go run ./cmd/zero-release build
  • [pass] Smoke build: go run ./cmd/zero-release smoke

Scope

Head: 8e186ef490d4
Changed files (8): internal/cli/app.go, internal/cli/repoinfo.go, internal/cli/repoinfo_test.go, internal/repoinfo/detect.go, internal/repoinfo/detect_test.go, internal/repoinfo/languages.go, internal/repoinfo/repoinfo.go, internal/repoinfo/repoinfo_test.go

This deterministic review checks validation status and basic diff hygiene. A human reviewer still owns product judgment and design quality.

@coderabbitai

coderabbitai Bot commented Jun 9, 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: 723bf8a7-1c04-40ac-93ed-710dd5b57155

📥 Commits

Reviewing files that changed from the base of the PR and between 625a3fa and 8e186ef.

📒 Files selected for processing (1)
  • internal/cli/app.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/cli/app.go

Walkthrough

Implements a new zero repo-info CLI command and a repoinfo package that scans the local Git HEAD tree to report file/dir counts, per-language LOC/file stats, detected build/test/CI/workflow markers, inferred workspace type, and optional Git-derived metrics. Supports JSON output and custom working directories.

Changes

Repository Info Command

Layer / File(s) Summary
Detection foundations: language mappings and repository markers
internal/repoinfo/languages.go, internal/repoinfo/detect.go, internal/repoinfo/detect_test.go
Language-to-extension mapping and detection tables for build tools, test frameworks, CI/CD systems, and workspace markers are established. Helper functions map file paths to CI system names and extract sorted unique sets. Unit tests validate extension mappings, CI detection, detection table presence, and sorted-set behavior.
Repository collection library: metrics, metadata, and tests
internal/repoinfo/repoinfo.go, internal/repoinfo/repoinfo_test.go
Core Collect function scans Git file trees via ls-tree, derives language LOC and file counts, detects tools and workspaces, and gathers optional Git metadata (branch, remote, age, contributors, velocity) via read-only Git commands with soft-failure semantics. Comprehensive tests validate metrics collection, contributor de-duplication, soft-failure behavior on command errors, age calculation from oldest root commit, remote sanitization, and enforcement of allowed read-only git subcommands.
CLI command: argument parsing, formatting, and wiring
internal/cli/repoinfo.go, internal/cli/repoinfo_test.go, internal/cli/app.go
Command entry point parses --json, -C/--cwd, and help flags, resolves workspace root, calls Collect, and outputs either JSON or formatted text with file/language/tool/Git metadata sections. Integration into the main dispatcher wires repo-info and repoinfo aliases. CLI tests validate parsing and end-to-end behavior against a hermetic temporary git repository, including remote credential sanitization.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CLI as zero (runRepoInfo)
  participant Collect as repoinfo.Collect
  participant Git as git
  participant Formatter as formatRepoInfo
  User->>CLI: zero repo-info [--json|-C/--cwd]
  CLI->>Collect: Collect(ctx, opts)
  Collect->>Git: ls-tree -r -z HEAD
  Git-->>Collect: NUL-separated entries
  Collect->>Git: rev-parse, remote get-url, logs (oldest/90d/30d)
  Collect-->>CLI: repoinfo.Info
  CLI->>Formatter: format (json/text)
  Formatter-->>User: stdout
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • anandh8x
  • Vasanthdev2004
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main addition: a new repo-info command that characterizes repositories using only local git operations without network access.
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.

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

✨ 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 repo-info

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

@Vasanthdev2004 Vasanthdev2004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Verdict: request changes.

Finding:

  • P1 internal/repoinfo/repoinfo.go:200: zero repo-info stores and returns git remote get-url origin verbatim, and internal/cli/repoinfo.go:120 prints it verbatim. Git remote URLs can include credentials, for example https://x-access-token:ghp_...@github.com/org/repo.git or token-as-user forms. That would leak secrets in both text output and --json via remoteURL. Please sanitize the remote before storing/outputting it, or omit userinfo entirely. Add tests for both text and JSON output so credentials never appear.

Validation run locally on the PR worktree:

  • git diff --check 2ea36cf138e559af947b58e360d8f0d8fc8bdf80...HEAD
  • go test ./internal/repoinfo ./internal/cli
  • go test ./...

All validation passed, but the remote URL credential exposure should be fixed before merge.

Addresses #150 review (@Vasanthdev2004 P1): a git remote can embed secrets
(e.g. https://x-access-token:ghp_...@github.com/o/r.git), which would leak in
both text and --json output. sanitizeRemoteURL strips userinfo from URL forms
and the leading user@ from scp-like forms. Tests: unit table for the sanitizer,
Collect-level strip, and end-to-end CLI text + JSON assertions that the
credential never appears.
@gnanam1990

Copy link
Copy Markdown
Collaborator Author

@Vasanthdev2004 Fixed in 625a3fa. The remote URL is now sanitized before it's stored or printed: sanitizeRemoteURL strips the user:password@ userinfo from URL forms (e.g. https://x-access-token:ghp_…@github.com/o/r.githttps://github.com/o/r.git) and the leading user@ from scp-like forms. So no credentials reach remoteURL in either text or --json.

Tests added:

  • TestSanitizeRemoteURL — table over https-with-token, user:pass, ssh, scp-like, clean, empty.
  • TestCollectStripsRemoteCredentials — Collect-level strip.
  • end-to-end: the CLI JSON and text tests now use a temp repo whose origin embeds ghp_TESTSECRET and assert the secret never appears in either output.

Full suite + -race + Windows build green.

@Vasanthdev2004 Vasanthdev2004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Requesting changes.

The repo-info implementation is additive in intent, but this branch is not additive against current main: it removes the notification feature that already exists on main.

Concrete regression:

  • internal/notify/notify.go and internal/notify/notify_test.go are deleted.
  • internal/cli/exec.go drops completion notification emission and execNotifyMode.
  • internal/cli/exec_parse.go drops --notify / --no-notify parsing and validation.
  • internal/config/types.go / resolver.go drop NotifyConfig resolution.
  • internal/tui/model.go, options.go, and run.go drop notifier setup, focus reporting, completion notifications, and awaiting-input notifications.
  • internal/cli/app.go also removes the --notify / --no-notify help text and stops passing resolved.Notify into TUI options.

This looks like branch drift from an older base, not part of the repo-info feature. Please rebase/merge latest main and restore the notification files/wiring, leaving only the repo-info command/package changes in this PR.

CI is green because the tests for the removed feature are also deleted, so this needs to be fixed before merge.

@gnanam1990

Copy link
Copy Markdown
Collaborator Author

@Vasanthdev2004 Both addressed:

  1. Credential leak (P1): fixed in 625a3fasanitizeRemoteURL strips userinfo before storing/printing (URL forms and scp-like); unit + Collect + end-to-end text/JSON tests assert the token never appears.
  2. Notify 'deletion' (branch drift): correct diagnosis — repo-info was cut from main before Sound / completion notifier: terminal bell + OSC-9 on turn completion & awaiting-input #149 merged. Merged latest main in 8e186ef; the branch now contains the notify feature and the diff is purely additive (only the repo-info package + cron... no, repo-info command). go test ./... + -race + Windows build green.

@gnanam1990

Copy link
Copy Markdown
Collaborator Author

@Vasanthdev2004 both points are addressed (pushed at 8e186ef):

  1. Credential leak (P1): sanitizeRemoteURL (repoinfo.go:256) strips userinfo from URL forms (url.Parse → clear u.User) and the leading user@ from scp-like user@host:path. Tests prove it: unit cases for x-access-token:ghp_secret@github.com, user:pass@gitlab.com, and ssh://git@…, plus end-to-end tests asserting ghp_TESTSECRET never appears in either text or --json output (repoinfo_test.go:199, repoinfo_test.go cli:60/76/111).

  2. Branch drift / notify removal: merged origin/main in (8e186ef); internal/notify is present and git diff --stat origin/main repo-info is now additions-only (8 files, +1027, no deletions) — the apparent notify removal was old-base drift, now gone.

build / tests / Windows green. Re-requesting review.

@Vasanthdev2004 Vasanthdev2004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Verdict: approve. I re-reviewed the latest head after the merge from main. The previous blockers are addressed: notification files/wiring are no longer removed, and remote URLs are sanitized before text/JSON repo-info output. Validation in an isolated worktree passed: git diff --check origin/main...HEAD, go test ./internal/repoinfo ./internal/cli, go build ./cmd/zero, go test ./..., and go run ./cmd/zero repo-info --json. No blockers found.

@gnanam1990
gnanam1990 merged commit 1506621 into main Jun 10, 2026
6 checks passed
@Vasanthdev2004
Vasanthdev2004 deleted the repo-info branch June 28, 2026 08:27
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.

2 participants