8000 ghx/CHANGELOG.md at main · lolay/ghx · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
190 lines (176 loc) · 11 KB

CHANGELOG.md

File metadata and controls

190 lines (176 loc) · 11 KB

Changelog

All notable changes to ghx are recorded here. Format follows Keep a Changelog.


[Unreleased]

Added

  • ghb <ORG>, a read-only branches report shipped beside ghx: every repo's default branch (flagged when it isn't main) and every other branch, classified as merged, stale, open-pr or active from the GitHub API, with org totals. New config keys stale-days, expected-default and include-archived; --format json for piping. A rate limit pauses it, with a countdown under the table (or a note line while it lists repos), until the limit lifts. See specs/branches-report.md.
  • Man pages for ghx and ghb, installed by the Homebrew formula and shipped in every release archive under manpages/.

Changed

  • A GitHub rate limit while listing an org's repos now says which limit and when to retry, rather than echoing the request.

[v0.3.0] - 2026-10-14

Changed

  • A config value of the wrong type (concurrency = "8"), or a float for an integer key, stops the run with an error naming the file and key, where it was silently ignored or truncated. An unknown config key gets a warning and is ignored.
  • ghx --help and command errors are styled with fang: a titled usage block, examples, and an error message with a pointer to --help. Piped output, and any output under NO_COLOR, has no escape codes. The exit code is still 0 on success and 1 on any error.
  • An existing clone that had nothing new is counted as up to date, so pulled counts only repos that took in new commits.
  • A clone fetched only for local changes names the first three changed files (local changes: app.py, data.csv (+2 more)).
  • Clone and pull get a 60-minute limit (other git commands keep 5 minutes) and give up on a connection that stays under 1000 bytes a second for 60 seconds, so a stalled transfer fails in a minute instead of holding a worker.
  • A clone or pull that fails on a dropped connection, a DNS or timeout error, a rate limit or a server error is tried once more after 2 seconds. "Repository not found" and authentication failures are never retried.
  • A failed repo's detail is git's last fatal: or error: line, not its progress output and hint: blocks.
  • Every existing clone is fetched on every run, so every remote branch is backed up whatever state the working tree is in. fetched only, with the reason, replaces skipped (local changes) for a clone whose working tree couldn't be updated. Wikis are synced the same way.
  • The summary marks each kind of result with a glyph beside its words: ✗ failed, ⇣ fetched only, ∅ skipped (empty). A "Skipped (empty)" section lists empty repos, and a "Notes" section lists clones and pulls that carry a detail (a branch fallback, a reset after a force-push, a rename switch).
  • Status marks say how much of a repo came down: ✓ cloned, ↓ pulled (new commits), – up to date, ⇣ fetched only (partly downloaded), ⚠ skipped, ∅ empty, ✗ failed. The live table's closing footer counts one class per mark (60 repos · ✓ 3 cloned · ↓ 12 pulled · – 40 up to date · ⇣ 2 fetched only · ⚠ 1 skipped · ∅ 1 empty · ✗ 1 failed · 1m12s), showing a class only when it has repos.
  • On a terminal, the live repo table replaces the progress bar: fixed columns that never shift, a row per repo that scrolls into the terminal's history as it finishes, the repos in flight and a progress line in the footer, and the totals at the close (wrapped between counts when the line is too long), followed by the lists of repos that need a look. Piped or redirected output keeps the static table and the full summary, with no escape codes.
  • The repo table's Visibility column reads archived for an archived repo (it wins over private), and the separate Archived column is gone. A narrow terminal now drops Last Pushed, Default Branch and Language, in that order.

Added

  • ghx completion <shell> prints a shell completion script, and ghx man a man page. An organization named help, completion or man needs -- before it (ghx -- help <PATH>).

Fixed

  • --delete deletes an archived repo's wiki along with the repo, and a removed repo's wiki moves to DELETED/ (or is deleted) with it, instead of being left in the output directory, never synced again.
  • A removed repo moved into DELETED/ no longer replaces an earlier clone of the same name there, and any work in it: the second one gets a UTC timestamp suffix (DELETED/x.20261014T013134Z), and its wiki takes the same name.
  • An archived repo's clone is moved into ARCHIVED/ whenever it is found outside it, not only on the run after the repo was archived: a mirror with no .ghx.json no longer gets a second clone in ARCHIVED/, and a move that failed is tried again. An unarchived repo's clone is moved back out of ARCHIVED/ (moved (unarchived)) and synced there, instead of being cloned again beside a stale copy. A wiki moves with its repo.
  • A clone is moved or deleted only when its origin URL names the repo; otherwise it is left alone and reported as skipped (origin mismatch) with the URL found. A clone is never moved onto an existing directory, which used to be deleted first: both are left in place and reported as conflict.
  • A repo left out by --include, --exclude or --type is no longer moved into ARCHIVED/, where no sync would reach it.
  • A repo whose HEAD names a branch that doesn't exist, which GitHub reports for an empty repo or one whose pushes never made main, is cloned and checked out on main, else master, else its newest branch, instead of failing.
  • An empty repo is skipped (skipped (empty)) and its empty clone removed, instead of failing on every run.
  • A force-pushed default branch is followed when the clone has no unpushed work, with the old tip kept under refs/ghx/forced/<branch>/<timestamp>, instead of being skipped on every run. That includes a force-push an earlier fetch already brought in (such as the failed pull --ff-only of earlier versions), found through the reflog of origin/<branch>, and the fetched-only count of local commits leaves out the ones the force-push dropped.
  • A default branch renamed on GitHub is switched to when nothing would be lost.
  • A clone on a feature branch or a detached HEAD is fetched, and reported as fetched only with the reason, instead of failing its pull.
  • A big clone is no longer killed at 5 minutes.
  • A wiki that fails to clone for a reason other than "not found" is reported as failed wiki with git's message, not as not found.

[v0.2.0] - 2026-10-13

Security

  • Hardening: ghx no longer puts the token in HTTPS clone URLs. It gives git the token through git's environment for the length of each clone or pull (an http.extraheader scoped to the clone URL's host), so the token is in no command line, no clone URL and no .git/config, and never reaches a credential helper. ghx never writes credentials to disk.
  • A sync cleans the origin URLs of clones made by older versions, which held the token: in each repository it pulls, and in every clone directly under the output directory, DELETED/ and ARCHIVED/. Rotate a token that older versions used, since copies of those clones may still hold it.
  • The token and its header value are masked in git failure messages and GitHub API errors.

Added

  • ghx --version (and -v) prints the build version as ghx <release>+<sha> (<build code>), for example ghx 0.1.0+abc1234 (42), with .dirty after the sha for a build from a tree with uncommitted changes. make build stamps it; a plain go build or go install prints ghx dev.
  • Release pipeline: the Release workflow (release.yml) bumps version.txt and the changelog, commits and tags vX.Y.Z; the tag starts the Publish workflow (publish.yml), which builds six archives and checksums.txt with goreleaser, publishes the GitHub release, then updates lolay/homebrew-tap and lolay/scoop-bucket, in that order. make snapshot builds the same archives locally without publishing, make release-check validates .goreleaser.yaml (also a CI job), and the Danger targets publish-formula, publish-scoop and release refuse to run without a CONFIRM_* variable. The procedure, secrets and first-release checklist are in specs/releasing.md.
  • Install with brew install lolay/tap/ghx or scoop install lolay/ghx, or download an archive from GitHub Releases, once the first release is out.
  • version.txt holds the last released version (0.1.0 until the first release), and make bump LEVEL=patch|minor|major runs scripts/bump_version.py to bump it and move these entries under the new version's heading.
  • Makefile with the standard targets (make help lists them; make ci is the pre-push gate), documented in Makefile.md.
  • Unit tests for configuration loading and precedence, repository filtering, the .ghx.json manifest (checked byte for byte against a file written by the Python implementation) and terminal output; see specs/testing.md.
  • CONTRIBUTING.md, SECURITY.md, AGENTS.md and .github/CODEOWNERS.
  • .go-version pins the Go version used for development.

Changed

  • The binary is now named ghx (it was ghx-go), built from cmd/ghx. Update any script or alias that calls ghx-go.
  • The Go module path is now github.com/lolay/ghx (it was github.com/garyrudolph/ghx), so go install github.com/lolay/ghx/cmd/ghx@latest works.
  • The README is restructured, with install, authentication and configuration (config file paths on macOS, Linux and Windows) and a CLI reference that matches ghx --help. It states the real Go requirement: 1.26.2 or newer.

Fixed

  • gofmt drift in internal/config/resolve.go.
  • Repository names that can't be a directory are skipped and reported as skipped (unusable name) instead of failing mid-sync: on every system a name that isn't a single directory name (a .. in an edited .ghx.json could otherwise move or delete a directory outside the output directory), and on Windows device names such as CON or NUL and names ending in a dot or space.
  • --deleted-dir and --archived-dir must now be directories inside the output directory; . would have deleted each repo it moved.
  • git is looked up once, before anything is asked of GitHub, with a clear error when it's missing or older than 2.31, the minimum ghx now needs. A git found only in the current directory is not run.
  • A repository whose git status fails is reported as failed instead of being pulled.
  • On Windows, git commands run with core.longpaths on, and moves and deletes wait briefly for a file another program holds open.
  • On macOS and Linux, a git that ghx stops (Ctrl-C, or the 5-minute timeout) is interrupted rather than killed, so it can remove a half-made clone.
  • The progress bar is drawn only on a terminal, so piped output holds no cursor escapes.
0