From 1eac994ddb64196b43ecd74cd7b4ffafa69e6317 Mon Sep 17 00:00:00 2001 From: Ken Tobias <634380+l1a@users.noreply.github.com> Date: Wed, 12 Aug 2026 19:57:55 -0700 Subject: [PATCH 1/2] Make the pre-PR gate answerable; open-pr now pushes just pr ended in a bare read, so only a human at a terminal could answer it -- a script or agent blocked on a stdin that would never answer, or died without saying why, and that reads as the gate refusing the change. It now accepts PR_CONFIRM, an interactive stdin, or piped input under a timeout, and names PR_CONFIRM when it cannot be answered. Not a bypass: every path still requires an explicit y. just open-pr did not push, so on a never-pushed branch it printed "Gate passed" and then failed because gh pr create had no remote branch to open from. It now pushes only when there is no upstream -- pushing unconditionally would silently publish existing commits on a branch that already has one. pre-push still runs just check, so the push is inside the gate rather than around it. Both are rusticprofile's 0.0.21 and 0.2.12, which retch never received. Assisted-By: Claude Opus 5 --- Cargo.toml | 2 +- Justfile | 44 ++++++++++++++++++++++++++++++++++++++++++-- NOTES.md | 29 ++++++++++++++++++++++++++++- docs/retch.1 | 2 +- 4 files changed, 72 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 56f312e..d59ff1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ [package] name = "retch-cli" -version = "0.6.20" +version = "0.6.21" edition = "2021" authors = ["Ken Tobias"] description = "A fast, feature-rich system information fetcher written in Rust (similar to fastfetch or neofetch)" diff --git a/Justfile b/Justfile index 579baa9..475fbca 100644 --- a/Justfile +++ b/Justfile @@ -382,8 +382,32 @@ pr: echo " [ ] GitHub wiki cloned and updated (Configuration-and-Theming.md, Workspace-Architecture.md)" echo " [ ] Upstream tldr page updated / docs/retch.md synced (if CLI flags changed)" echo "" - echo -n "All manual items confirmed? [y/N] " - read -r CONFIRM + # A bare `read` makes this gate unanswerable by anything that is not a human at a + # terminal: a script, CI job or agent either blocks on a stdin that will never answer or + # dies without saying why -- and that failure reads as the gate refusing the change rather + # than asking a question nobody could hear. Three sources of an answer, in order: + # + # 1. PR_CONFIRM in the environment -- the explicit answer for a non-interactive caller. + # It is NOT a bypass: setting it is the same act of confirmation as typing y, just + # recorded where a script can supply it. Answer it AFTER checking each item. + # 2. An interactive stdin -- a human, prompted exactly as before. + # 3. Neither, so read whatever was piped in, bounded by a timeout. `echo y | just pr` + # keeps working, and a stdin that never answers costs ten seconds rather than hanging. + # + # The failure names PR_CONFIRM, because a gate that cannot be satisfied from the context it + # failed in is a wall rather than a gate. + if [ -n "${PR_CONFIRM:-}" ]; then + CONFIRM="$PR_CONFIRM" + echo "All manual items confirmed? [y/N] $CONFIRM (answered by PR_CONFIRM)" + elif [ -t 0 ]; then + echo -n "All manual items confirmed? [y/N] " + read -r CONFIRM + else + echo -n "All manual items confirmed? [y/N] " + read -r -t 10 CONFIRM || CONFIRM="" + echo "$CONFIRM" + [ -n "$CONFIRM" ] || { echo -e "${RED}Aborted.${NC} No terminal to confirm the checklist on, and nothing on stdin. Re-run with PR_CONFIRM=y once each item above is actually checked."; exit 1; } + fi [ "$CONFIRM" = "y" ] || [ "$CONFIRM" = "Y" ] \ || { echo -e "${RED}Aborted.${NC} Complete the checklist first."; exit 1; } @@ -395,6 +419,22 @@ open-pr *ARGS: #!/usr/bin/env bash set -euo pipefail just pr + + # Push the branch if it has no upstream yet. Without this, on a never-pushed branch + # `gh pr create` has no remote branch to open a PR from and fails non-interactively -- + # AFTER the gate has printed "Gate passed", which reads as the gate refusing a change it + # had just approved. + # + # Deliberately ONLY when there is no upstream. Pushing unconditionally would make this + # recipe silently publish existing commits on a branch that already has one -- a different + # and more surprising act than "put this branch where gh can see it". + if ! git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' >/dev/null 2>&1; then + BRANCH="$(git rev-parse --abbrev-ref HEAD)" + [ "$BRANCH" != HEAD ] || { echo "detached HEAD -- check out a branch first" >&2; exit 1; } + echo "no upstream for $BRANCH -- pushing it so gh has a remote branch to open from" + # pre-push runs `just check`, so this cannot publish a branch the gate would refuse. + git push -u origin "$BRANCH" + fi gh pr create "$@" # Generate a flamegraph for execution profiling (requires perf on Linux or dtrace on macOS) diff --git a/NOTES.md b/NOTES.md index 1d8af02..775eafa 100644 --- a/NOTES.md +++ b/NOTES.md @@ -96,7 +96,34 @@ The `retch-sysinfo` crate can be used independently as a library for cross-platf --- -## Current State (v0.6.20) +## Current State (v0.6.21) +- **v0.6.21 — two gates that could not be satisfied from the situation they failed in** + (tooling only; no runtime change, `retch-sysinfo` unchanged at `0.1.53`). Both were hit by hand + while landing v0.6.20, and both are fixes `rusticprofile` already had. + - **`just pr` ended in a bare `read`, so nothing but a human at a terminal could answer it.** A + script, CI job or agent either blocked on a stdin that would never answer or died without + saying why — and that failure reads as *the gate refusing the change*, not as a question + nobody could hear. It now takes its answer from `PR_CONFIRM`, from an interactive stdin, or + from piped input under a ten-second bound, and the failure message **names `PR_CONFIRM`**. + It is not a bypass: all four paths still require an explicit `y`, so this widens *who can + answer*, not *what counts as an answer* — and the checklist must still be answered after + each item is actually checked. + - **`just open-pr` did not push, so on a never-pushed branch it printed "Gate passed" and then + failed.** `gh pr create` had no remote branch to open from. The observable result was a + command that announced the gate passing and then exited non-zero, which reads as the gate + rejecting work it had just approved. It now pushes **only when there is no upstream** — + pushing unconditionally would silently publish existing commits on a branch that already has + one, a different and more surprising act. `pre-push` still runs `just check`, so this cannot + publish a branch the gate would refuse; the push is inside the gate, not around it. + - **Both were verified on this PR itself**, which is the only honest test for the second one: + the branch had no upstream when `open-pr` ran, and that condition cannot be reproduced after + the fact. + - *Why these existed at all:* they are `rusticprofile`'s `0.0.21` and `0.2.12`, which retch + never received — the same cross-repo staleness that left the nushell completion path wrong + here for months and that v0.6.20's `standard-check` now guards for the install family. The + `pr`/`open-pr` triad is **not** yet covered by that standard; `templates/justfile-common.just` + records it as out of scope, because these recipes legitimately differ per repo. + - `retch-cli` → 0.6.21. Patch bump. - **v0.6.20 — nushell completions went where Windows nushell never looks; the install helpers become a checked cross-repo standard** (tooling only; no runtime behavior change, `retch-sysinfo` unchanged at `0.1.53`). diff --git a/docs/retch.1 b/docs/retch.1 index aa52067..873aadb 100644 --- a/docs/retch.1 +++ b/docs/retch.1 @@ -1,4 +1,4 @@ -.TH "RETCH" "1" "August 2026" "retch 0.6.20" "System Information Fetcher" +.TH "RETCH" "1" "August 2026" "retch 0.6.21" "System Information Fetcher" .SH "NAME" .PP From d5fa9839b63ff5a6de6015255647ef817dec4d01 Mon Sep 17 00:00:00 2001 From: Ken Tobias <634380+l1a@users.noreply.github.com> Date: Wed, 12 Aug 2026 19:59:13 -0700 Subject: [PATCH 2/2] Commit the Cargo.lock version bump Assisted-By: Claude Opus 5 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 316f95b..ee46153 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1551,7 +1551,7 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "retch-cli" -version = "0.6.20" +version = "0.6.21" dependencies = [ "anyhow", "base64",