From a9aca1ae49a91520bd4f05e852edf638fdcfd32b Mon Sep 17 00:00:00 2001 From: sksizer Date: Tue, 7 Apr 2026 13:11:44 -0500 Subject: [PATCH] chore: update project from upstream template MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Performed the following: - rust-toolchain.toml: switched from nightly to stable - scripts/lib/pr_prompt.sh: new shared PR prompt helper library - scripts/bring_up_to_date.sh: refactored to use shared pr_prompt.sh - scripts/bring_up_to_date_all.sh: refactored to use shared pr_prompt.sh, added log collection - scripts/cargo_update.sh: refactored to use shared pr_prompt.sh - scripts/cargo_update_all.sh: refactored to use shared pr_prompt.sh, added log collection - scripts/template_backport.sh: refactored to use shared pr_prompt.sh - scripts/template_backport_all.sh: refactored to use shared pr_prompt.sh, added log collection Did not bring over the following because of token scope limitation: - .github/workflows/ci.yml (nightly→stable) — requires `workflow` OAuth scope --- rust-toolchain.toml | 2 +- scripts/bring_up_to_date.sh | 18 +++------- scripts/bring_up_to_date_all.sh | 24 +++++++++++-- scripts/cargo_update.sh | 18 +++------- scripts/cargo_update_all.sh | 18 ++++++++++ scripts/lib/pr_prompt.sh | 58 ++++++++++++++++++++++++++++++++ scripts/template_backport.sh | 17 +++------- scripts/template_backport_all.sh | 16 +++++++++ 8 files changed, 130 insertions(+), 41 deletions(-) create mode 100644 scripts/lib/pr_prompt.sh diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5d56faf..292fe49 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "nightly" +channel = "stable" diff --git a/scripts/bring_up_to_date.sh b/scripts/bring_up_to_date.sh index 0721f6e..884c355 100755 --- a/scripts/bring_up_to_date.sh +++ b/scripts/bring_up_to_date.sh @@ -66,15 +66,9 @@ compose_prompt() { PROMPT="$(compose_prompt)" -# --- Open a URL in the default browser (best-effort, never fails the script) -open_url() { - local url="$1" - case "$(uname -s)" in - Darwin) open "$url" ;; - Linux) xdg-open "$url" ;; - MINGW*|MSYS*|CYGWIN*) cmd.exe /c start "$url" ;; - esac 2>/dev/null || true -} +# --- Shared PR prompt helpers ---------------------------------------------- +# shellcheck source=lib/pr_prompt.sh +source "${SCRIPT_DIR}/lib/pr_prompt.sh" # --- Allowed tools ---------------------------------------------------------- # Read/Edit/Write for file changes, Bash for just commands and git, WebFetch for fetching upstream @@ -92,11 +86,9 @@ if [[ "$EXECUTE" == true ]]; then --allowed-tools ${ALLOWED_TOOLS})" echo "$OUTPUT" - # Try to extract a PR URL from the output and open it - PR_URL="$(echo "$OUTPUT" | grep -oE 'https://github\.com/[^ ]+/pull/[0-9]+' | head -1 || true)" + PR_URL="$(pr_prompt_extract_url "$OUTPUT")" if [[ -n "$PR_URL" ]]; then - echo "Opening PR: ${PR_URL}" - open_url "$PR_URL" + pr_prompt_finalize "$PR_URL" fi else echo "=== DRY RUN ===" diff --git a/scripts/bring_up_to_date_all.sh b/scripts/bring_up_to_date_all.sh index c9d9bd3..ce94df0 100755 --- a/scripts/bring_up_to_date_all.sh +++ b/scripts/bring_up_to_date_all.sh @@ -5,6 +5,11 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DOWNSTREAM_FILE="${SCRIPT_DIR}/downstream.txt" BRING_UP="${SCRIPT_DIR}/bring_up_to_date.sh" +# shellcheck source=lib/pr_prompt.sh +source "${SCRIPT_DIR}/lib/pr_prompt.sh" +# Children must not prompt — only this driver does, once, at the end. +export PR_PROMPT_SUPPRESS=1 + if [[ ! -f "$DOWNSTREAM_FILE" ]]; then echo "Error: ${DOWNSTREAM_FILE} not found" >&2 exit 1 @@ -20,6 +25,7 @@ echo "Clone directory: ${WORK_DIR}" PIDS=() REPOS=() CLONE_DIRS=() +LOG_FILES=() while IFS= read -r repo_url; do # Skip empty lines and comments @@ -28,15 +34,19 @@ while IFS= read -r repo_url; do # Derive repo name from URL (e.g. https://github.com/sksizer/rust-dir-aspect/ -> rust-dir-aspect) REPO_NAME="$(basename "${repo_url%/}")" CLONE_PATH="${WORK_DIR}/${REPO_NAME}" + LOG_FILE="${WORK_DIR}/${REPO_NAME}.log" echo "Cloning: ${repo_url} -> ${CLONE_PATH}" ( - git clone --quiet "$repo_url" "$CLONE_PATH" - bash "$BRING_UP" ${ARGS[@]+"${ARGS[@]}"} "$CLONE_PATH" + { + git clone --quiet "$repo_url" "$CLONE_PATH" + bash "$BRING_UP" ${ARGS[@]+"${ARGS[@]}"} "$CLONE_PATH" + } 2>&1 | tee "$LOG_FILE" ) & PIDS+=($!) REPOS+=("$repo_url") CLONE_DIRS+=("$CLONE_PATH") + LOG_FILES+=("$LOG_FILE") done < "$DOWNSTREAM_FILE" # Wait for all and report results @@ -53,6 +63,16 @@ done echo "---" echo "Finished: $((${#PIDS[@]} - FAILED))/${#PIDS[@]} succeeded" +# Collect PR URLs from each per-repo log and prompt once at the end. +PR_URLS=() +for LOG_FILE in "${LOG_FILES[@]}"; do + [[ -f "$LOG_FILE" ]] || continue + URL="$(pr_prompt_extract_url "$(cat "$LOG_FILE")")" + [[ -n "$URL" ]] && PR_URLS+=("$URL") +done +unset PR_PROMPT_SUPPRESS +pr_prompt_finalize "${PR_URLS[@]}" + # Clean up clones echo "Cleaning up: ${WORK_DIR}" rm -rf "$WORK_DIR" diff --git a/scripts/cargo_update.sh b/scripts/cargo_update.sh index 6d6e776..8bacb98 100755 --- a/scripts/cargo_update.sh +++ b/scripts/cargo_update.sh @@ -74,15 +74,9 @@ compose_prompt() { PROMPT="$(compose_prompt)" -# --- Open a URL in the default browser (best-effort, never fails the script) -open_url() { - local url="$1" - case "$(uname -s)" in - Darwin) open "$url" ;; - Linux) xdg-open "$url" ;; - MINGW*|MSYS*|CYGWIN*) cmd.exe /c start "$url" ;; - esac 2>/dev/null || true -} +# --- Shared PR prompt helpers ---------------------------------------------- +# shellcheck source=lib/pr_prompt.sh +source "${SCRIPT_DIR}/lib/pr_prompt.sh" # --- Allowed tools ---------------------------------------------------------- ALLOWED_TOOLS="Read Edit Write Bash" @@ -105,12 +99,10 @@ if [[ "$EXECUTE" == true ]]; then echo "$OUTPUT" log "${C_DIM}──── end output ────${C_RESET}" - # Try to extract a PR URL from the output and open it - PR_URL="$(echo "$OUTPUT" | grep -oE 'https://github\.com/[^ ]+/pull/[0-9]+' | head -1 || true)" + PR_URL="$(pr_prompt_extract_url "$OUTPUT")" if [[ -n "$PR_URL" ]]; then log "${C_GREEN}✔ PR for ${C_MAGENTA}${LABEL}${C_GREEN}:${C_RESET} ${C_BOLD}${PR_URL}${C_RESET}" - log "Opening in browser…" - open_url "$PR_URL" + pr_prompt_finalize "$PR_URL" else log "${C_YELLOW}No PR URL detected in output for ${LABEL}${C_RESET}" fi diff --git a/scripts/cargo_update_all.sh b/scripts/cargo_update_all.sh index 97061d6..05a762d 100755 --- a/scripts/cargo_update_all.sh +++ b/scripts/cargo_update_all.sh @@ -5,6 +5,11 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DOWNSTREAM_FILE="${SCRIPT_DIR}/downstream.txt" CARGO_UPDATE="${SCRIPT_DIR}/cargo_update.sh" +# shellcheck source=lib/pr_prompt.sh +source "${SCRIPT_DIR}/lib/pr_prompt.sh" +# Children must not prompt — only this driver does, once, at the end. +export PR_PROMPT_SUPPRESS=1 + # --- Colors (disabled when not a tty or NO_COLOR is set) -------------------- if [[ -t 1 && -z "${NO_COLOR:-}" ]]; then C_RESET=$'\033[0m' @@ -105,6 +110,19 @@ else log "${C_YELLOW}$((${#PIDS[@]} - FAILED))/${#PIDS[@]} succeeded, ${C_RED}${FAILED} failed${C_RESET}" fi +# Collect PR URLs from every per-repo log so we can prompt once at the end. +PR_URLS=() +for i in "${!PIDS[@]}"; do + LOG_FILE="${WORK_DIR}/${REPO_NAMES[$i]}.log" + [[ -f "$LOG_FILE" ]] || continue + URL="$(pr_prompt_extract_url "$(cat "$LOG_FILE")")" + [[ -n "$URL" ]] && PR_URLS+=("$URL") +done + +# Unset suppression so the prompt actually fires for THIS (top-level) script. +unset PR_PROMPT_SUPPRESS +pr_prompt_finalize "${PR_URLS[@]}" + # Clean up clones log "Cleaning up: ${C_DIM}${WORK_DIR}${C_RESET}" rm -rf "$WORK_DIR" diff --git a/scripts/lib/pr_prompt.sh b/scripts/lib/pr_prompt.sh new file mode 100644 index 0000000..069a02c --- /dev/null +++ b/scripts/lib/pr_prompt.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# Shared helpers for collecting PR URLs and prompting the user whether to open +# them at the end of a script run. Source this file — don't execute it. +# +# Environment: +# PR_PROMPT_SUPPRESS=1 — disable the interactive prompt entirely. Parent +# drivers (e.g. *_all.sh) set this when invoking +# single-repo scripts so only the outermost script +# asks. Also honored by CI via: PR_PROMPT_SUPPRESS=1 + +# Open a URL in the default browser (best-effort, never fails the caller). +pr_prompt__open_url() { + local url="$1" + case "$(uname -s)" in + Darwin) open "$url" ;; + Linux) xdg-open "$url" ;; + MINGW*|MSYS*|CYGWIN*) cmd.exe /c start "$url" ;; + esac 2>/dev/null || true +} + +# Extract the first GitHub PR URL from a blob of text. Prints nothing if none. +pr_prompt_extract_url() { + grep -oE 'https://github\.com/[^[:space:]]+/pull/[0-9]+' <<<"$1" | head -1 || true +} + +# Given a list of PR URLs (one per argument), show them and — if stdin is a +# TTY and PR_PROMPT_SUPPRESS is unset — ask whether to open them all now. +# No-op if no URLs are passed. +pr_prompt_finalize() { + local urls=("$@") + [[ ${#urls[@]} -eq 0 ]] && return 0 + + echo + echo "PR URL(s):" + for url in "${urls[@]}"; do + echo " $url" + done + + if [[ -n "${PR_PROMPT_SUPPRESS:-}" ]]; then + return 0 + fi + if [[ ! -t 0 || ! -t 1 ]]; then + echo "(non-interactive; skipping open prompt)" + return 0 + fi + + echo + local reply="" + read -r -p "Open $( [[ ${#urls[@]} -gt 1 ]] && echo "all ${#urls[@]} PRs" || echo "PR" ) in browser now? [y/N] " reply || return 0 + case "$reply" in + y|Y|yes|YES) + for url in "${urls[@]}"; do + pr_prompt__open_url "$url" + done + ;; + *) ;; + esac +} diff --git a/scripts/template_backport.sh b/scripts/template_backport.sh index 29e935f..b5f0f9a 100755 --- a/scripts/template_backport.sh +++ b/scripts/template_backport.sh @@ -56,15 +56,9 @@ compose_prompt() { } PROMPT="$(compose_prompt)" -# --- Open a URL in the default browser (best-effort) ----------------------- -open_url() { - local url="$1" - case "$(uname -s)" in - Darwin) open "$url" ;; - Linux) xdg-open "$url" ;; - MINGW*|MSYS*|CYGWIN*) cmd.exe /c start "$url" ;; - esac 2>/dev/null || true -} +# --- Shared PR prompt helpers ---------------------------------------------- +# shellcheck source=lib/pr_prompt.sh +source "${SCRIPT_DIR}/lib/pr_prompt.sh" # --- Allowed tools ---------------------------------------------------------- # Backport mode: Claude must edit files, run checks, and create a PR. @@ -91,10 +85,9 @@ if [[ "$EXECUTE" == true ]]; then --allowed-tools ${ALLOWED_TOOLS})" echo "$OUTPUT" - PR_URL="$(echo "$OUTPUT" | grep -oE 'https://github\.com/[^ ]+/pull/[0-9]+' | head -1 || true)" + PR_URL="$(pr_prompt_extract_url "$OUTPUT")" if [[ -n "$PR_URL" ]]; then - echo "Opening PR: ${PR_URL}" - open_url "$PR_URL" + pr_prompt_finalize "$PR_URL" else echo "No PR opened (no candidates, or run was a no-op)." fi diff --git a/scripts/template_backport_all.sh b/scripts/template_backport_all.sh index 8d1499b..65e08bd 100755 --- a/scripts/template_backport_all.sh +++ b/scripts/template_backport_all.sh @@ -5,6 +5,11 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DOWNSTREAM_FILE="${SCRIPT_DIR}/downstream.txt" TEMPLATE_BACKPORT="${SCRIPT_DIR}/template_backport.sh" +# shellcheck source=lib/pr_prompt.sh +source "${SCRIPT_DIR}/lib/pr_prompt.sh" +# Children must not prompt — only this driver does, once, at the end. +export PR_PROMPT_SUPPRESS=1 + if [[ ! -f "$DOWNSTREAM_FILE" ]]; then echo "Error: ${DOWNSTREAM_FILE} not found" >&2 exit 1 @@ -57,4 +62,15 @@ echo echo "---" echo "Finished: $((${#PIDS[@]} - FAILED))/${#PIDS[@]} succeeded" echo "Work dir left at: ${WORK_DIR}" + +# Collect PR URLs from each per-repo log and prompt once at the end. +PR_URLS=() +for LOG_FILE in "${LOG_FILES[@]}"; do + [[ -f "$LOG_FILE" ]] || continue + URL="$(pr_prompt_extract_url "$(cat "$LOG_FILE")")" + [[ -n "$URL" ]] && PR_URLS+=("$URL") +done +unset PR_PROMPT_SUPPRESS +pr_prompt_finalize "${PR_URLS[@]}" + exit $FAILED