github Dicklesworthstone/repo_updater v1.7.0
ru v1.7.0

3 hours ago

ru now needs Bash 4.4 or later and stops at startup on older Bash. Commit publication now works with the stock Git on Ubuntu 22.04 and 24.04; in v1.6.0 every publication failed there. Commands that read the repository list, discover GitHub issues and PRs, or scan dependencies now fail when they cannot read everything, instead of acting on a partial result. ru dep-update keeps only the updates ru has checked and tested itself, and agent-sweep --with-release applies the release plan instead of only tagging HEAD. See the upgrade notes.

Changed

  • Bash 4.4 is the minimum. ru uses namerefs and expands arrays that may be empty under set -u, which Bash before 4.4 cannot run, but the startup check accepted any Bash 4: on 4.2 and 4.3, ru list and ru config stopped with "ARGS[@]: unbound variable". Bash 3.2 through 4.3 now get the upgrade advice and exit 3 before any command starts (7ffe91a).
  • A repository list that cannot be read stops the command. Before, a repos.d list that failed to load was skipped, and sync, status and the other fleet commands carried on with the repos already read. Now sync, status, list, ai-sync, dep-update, review, the fork commands and commit-sweep exit 3 without acting on a partial list (JSON results say config_read_failed), as they do when a list path exists but is not a readable regular file; ru doctor reports the configuration as unreadable, and a repos.d that can be searched but not listed is still read. sync and review also exit 3 when they cannot save their checkpoint (aad594d, 175f6c9, 6118847, 1c8b412).
  • Review discovery reads the whole backlog or fails. ru review follows every issue, pull request and label page before ranking; a missing gh or jq, a GraphQL error, or a page that cannot be finished now exits 3 ("Review discovery failed; no complete backlog is available") instead of a warning and a partial list. Security and critical labels add 150 points (was 50) and keep an item CRITICAL after the recent-review penalty, and labels match whole names, so non-critical or security-review-not-needed are ordinary labels. --max-repos is applied after ranking and keeps every matching item of each selected repository (26979a3, f956268, a2e8edd, 41bf7d7).
  • Dependency scans fail closed. Discovery runs npm outdated, cargo outdated --workspace --root-deps-only, go list -m -u and composer outdated. A missing tool (including cargo-outdated), a failed command, malformed output, or a manager without an adapter (Yarn, pnpm, pip, pipenv, Bundler, Maven, Gradle) now fails that repository with a typed error, also under --dry-run, instead of reading as "no outdated dependencies"; npm's answer is refused beside a yarn.lock or pnpm-lock.yaml. Cargo discovery covers the direct dependencies of every workspace member, and each record names its crate_name (5abfa02, 3019f63, 44c323f).
  • ru dep-update keeps only updates it verified itself (GH #19). Each requested dependency is updated in its own clone: ru reads the manifest and lockfile before and after, installs the locked environment, checks the installed version, runs the tests itself, and only then adds one commit to the original branch. A no-op, a wrong version, an inconsistent lock, an unrelated change or a failing test keeps that attempt out while earlier verified updates stay; each dependency is reported as updated, skipped, failed, reverted or unverified, and a partial run exits nonzero. The test command follows the managers that still have updates (for example npm test && cargo test --workspace), and an update with no detected command and no --test-cmd fails before any AI session starts (2643e01, 1f414e8, 5161000, af23f85).
  • Automatic Rust test detection runs cargo test --workspace, so a root package or a restricted workspace.default-members list no longer leaves out the member that changed. This is the command quality gates and dep-update pick for Cargo; an explicit --test-cmd still overrides it (44c323f).
  • agent-sweep --with-release applies the release plan. Before, it tagged HEAD as it was: the plan's tag was ignored (v1.1.0 became 1.1.0), version files and the changelog were never edited, checks never ran, and a missing gh still reported a GitHub release. Now ru edits the version files and changelog, commits them as chore(release): <tag>, runs the requested tests, lint and secrets checks on a clean checkout of that commit, pushes the branch and an annotated tag to the scanned receivers, and creates the GitHub release with --verify-tag; a failed check publishes nothing, and a rerun continues from .git/ru-release/<tag>.json. A plan with "version": null means no release instead of failing the sweep, a changelog or asset path through a symbolic link is refused, and signature files (.minisig, .sig, .asc) are exempt from the checksums check (ca988b6, a3c7e4d).
  • Structured output covers every command. fork-sync printed no JSON, fork-status printed nothing when there was no configuration or no repository, and init, add, remove and import printed nothing under --json. Each now prints one envelope with _meta.exit_code, and ru robot-docs schemas has data schemas for these commands and for fork-clean and commit-sweep (6aea115, 30efb09, d86ad6f).
  • The test suite runs on macOS (beads bd-ivf6), and the review and pin fixtures match the stricter checks above (4daa226, 58e8067).

Fixed

  • Commit publication failed on Git 2.34 and 2.43, the stock git on Ubuntu 22.04 and 24.04. ru bound a push to the scanned receiver by passing an empty -c remote.<name>.url= and then the receiver; those Git versions take the empty value as one more URL instead of clearing the list, so the bind check refused and every publication (inspect, fetch and push, as used by ai-sync, dep-update, review --apply, commit-sweep and agent-sweep) failed with "Cannot bind transport to the scanned publication destination". This was in v1.6.0 too. ru now binds a fresh remote name whose only URL is the receiver, copies the named remote's other settings (fetch refspecs, proxy, push options) onto it, and still passes the named remote to the repository's own pre-push hook. Measured before this fix: Git 2.34.1 and 2.43.0 failed and Git 2.56.0 worked; Apple's Git was not tested (f1f0c26).
  • A branch or tag pin could restore a tracked file and report success. When the pinned ref did not exist, git checkout treated a name such as file1.txt as a path, restored that file and dropped its uncommitted bytes. ru now checks out only a local branch, a remote branch (as a tracking branch) or a tag, and anything else fails and leaves the clone and its files in place. A detached clone whose configured pin changed now moves to the new ref and is counted as updated (before: "No upstream", counted as a conflict) (a909d95, a2e8edd).
  • ru --dry-run ai-sync ran for real, and several documented flags were rejected. Options placed before a command reached only review and agent-sweep, so ai-sync and dep-update never saw a leading --dry-run; every option now reaches its command wherever it appears. config --set KEY=VALUE, import --stars/--org=/--user=/--limit=, agent-sweep --show-denylist, --debug and -d exited 4 and now work, agent-sweep -j4 sets the sweep's own parallelism, and review --repos=PATTERN filters instead of being ignored. ru import --stars, --org=NAME and --user=NAME list each source with gh before writing anything, import nothing and exit 3 if a listing fails, and are no longer capped at gh's default of 30 repos (da26e99).
  • On macOS, ru commands could hang under pipe-memory pressure. When macOS is short of kernel pipe memory it hands out 512-byte pipes, and Bash 5.1 or later blocks writing a longer heredoc or here-string into one. The agent-sweep prompts were read that way at startup, so every ru command could hang, and about 170 other places could hang their own command; ru now has no heredocs, and stdin longer than 511 bytes comes from a separate writer process (e6254b7, 9af35e9).
  • On macOS with GNU sed first on PATH, ru config --set failed for an existing key. ru ran sed -i '' whenever OSTYPE was darwin; GNU sed (for example Homebrew's gnu-sed with gnubin) reads '' as its script, exits 2 and leaves the file unchanged. This was in v1.6.0 too. ru now writes the edited copy beside the config file, keeping its mode, and renames it over the file, so the same code runs with either sed on every OS (e230f38).
  • On macOS, temporary files ignored TMPDIR. ru's temp-file helpers called mktemp without a template, which macOS answers with the per-user temp directory. They now use ${TMPDIR:-/tmp}/ru.XXXXXXXXXX, so files land in the chosen TMPDIR, and a TMPDIR that does not exist fails (e26f511).
  • Under Bash 4.4, a second review or agent-sweep refused to start, and session names kept non-ASCII letters. Before Bash 5, a subshell started inside an EXIT trap never runs its own EXIT trap, so a lock guard file survived and every later run reported "Another review is running" until the file was removed by hand; the guard is now removed explicitly. Bash 4.4 also matched ranges such as [a-zA-Z0-9] by locale collation, so a UTF-8 repo name kept "é" in its agent-sweep session name; ru now sets globasciiranges (8d93108, 091b97f).
  • ru agent-sweep --json reported invalid_structured_output and exited 3 when every repository succeeded. ntm kill prints "Killed session ''" on stdout, and that line landed next to the JSON summary. ru now discards ntm's stdout when it kills or interrupts a session (e6cd23d).
  • Some commands exited 0 after a failure. ru status --json exited 0 after a failed fetch when jq was absent, fork-sync --push reported a fork as synced when the push to origin failed, and ru add or ru remove with an invalid spec logged an error and exited 0. Now status exits 1, the fork is counted as failed and the run exits 1, and an invalid spec exits 4 (or 1 beside repos that were added or removed) (825ceb2, 6aea115, 30efb09).
  • --format toon fell back to JSON even with toon_rust installed. ru needed a toon.sh helper that no project ships, and toon_rust's binary has been toon (before v0.2, tru). ru now calls the installed toon or tru itself; a helper that defines toon_encode still takes precedence, and with no encoder ru warns and prints JSON as before (22bfe55).

Upgrade notes

  • Bash: ru requires Bash 4.4 or later. On Bash 3.2 or 4.0 through 4.3 it prints "ru: Bash >= 4.4 is required (found: ...)" with install advice (on macOS, a Homebrew offer) and exits 3 before running any command.
  • Exit codes: these used to exit 0 and now fail: a command whose repository list cannot be read (3); ru review when discovery cannot finish or gh or jq is missing (3); ru dep-update for a repository whose scan fails or whose manager has no adapter, also under --dry-run, and for an update with no test command; a dep-update run where only some updates were verified; ru add or ru remove with an invalid spec (4, or 1 beside successes); fork-sync --push with a failed push (1); and ru status --json after a failed fetch without jq (1). Scripts that relied on the old success should read the reported reason.
  • dep-update JSON: deps_updated now counts updates that were verified, tested and added to the original branch; in v1.6.0 it was set to the number of outdated dependencies found. That number is now deps_outdated_before, and per-dependency results are in dependencies.
  • Repository filters: an agent-sweep --repos, review --repos or ai-sync --include/--exclude pattern containing *, ? or [ is now a glob that must match the whole repo name, owner/repo, spec or path; before, it was compared as literal text. Plain text still matches as a substring.
  • Release plans: agent-sweep --with-release now edits and commits version files and the changelog, uses the plan's tag, and refuses a tag that does not name the version or an unknown strategy (before, treated as auto). The secrets check now scans every file of the release commit; without gitleaks or detect-secrets, its heuristic patterns (token=, password:) can flag benign text and fail the release.

Known limitations

  • On Ubuntu 22.04 (jq 1.6), ru commit-sweep may refuse valid plans with "Commit 1 has no files". Ubuntu 24.04 (jq 1.7) and macOS are unaffected.
  • ru dep-update discovers updates only for npm, Cargo, Go and Composer; repositories using Yarn, pnpm, pip, pipenv, Bundler, Maven or Gradle fail with unsupported_scan. Automatic verification covers narrow layouts (for example one root npm project with a v2/v3 package-lock.json, or Cargo lockfile updates within the existing requirements), installs run with npm lifecycle scripts and Composer scripts and plugins disabled, and Go and Composer updates are tested only against fixtures. GH #19 stays open for this.
  • agent-sweep --with-release's GitHub release, ci and checksums steps were tested against a mocked gh, not a live GitHub release.

Testing

  • Mac gate: 93 of 98 test files green; 1921 passed, 17 failed, 3 skipped. The 5 red files have environment or load causes: robot_docs (Python schema validators not installed), session_driver (dep-update runs timing out at --timeout=10 under heavy load), dep_evidence (the local build wrapper), lifecycle (a test mock's pipe race under load), ntm_driver (ps blocked by the test sandbox).

Installation

curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/repo_updater/main/install.sh | bash

Verify

echo "$(curl -fsSL https://github.com/Dicklesworthstone/repo_updater/releases/download/v1.7.0/ru.sha256)  ru" | sha256sum -c -

SHA256 checksums for ru and install.sh are in checksums.txt. Built from 748299a (tag v1.7.0) and published from a gated clone with gh; no GitHub Actions.

Don't miss a new repo_updater release

NewReleases is sending notifications on new releases.