From cf717f98b719527fc68cc45e728f8933bfa488cb Mon Sep 17 00:00:00 2001 From: sksizer Date: Tue, 7 Apr 2026 11:22:01 -0500 Subject: [PATCH] feat(scripts): add colored, repo-labeled logging to update scripts Output from parallel cargo_update_all runs is now buffered per repo and replayed in labeled blocks, so it's always clear which downstream project a given log line (or PR URL) belongs to. cargo_update.sh tags every line with a [repo] prefix via REPO_LABEL and surfaces the PR URL with the repo name. --- scripts/cargo_update.sh | 62 +++++++++++++++++--------- scripts/cargo_update_all.sh | 86 +++++++++++++++++++++++++++++-------- 2 files changed, 111 insertions(+), 37 deletions(-) diff --git a/scripts/cargo_update.sh b/scripts/cargo_update.sh index 652ec70..6d6e776 100755 --- a/scripts/cargo_update.sh +++ b/scripts/cargo_update.sh @@ -4,6 +4,22 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROMPT_DIR="${SCRIPT_DIR}/cargo_update" +# --- Colors (disabled when not a tty or NO_COLOR is set) -------------------- +if [[ -t 1 && -z "${NO_COLOR:-}" ]]; then + C_RESET=$'\033[0m'; C_BOLD=$'\033[1m'; C_DIM=$'\033[2m' + C_RED=$'\033[31m'; C_GREEN=$'\033[32m'; C_YELLOW=$'\033[33m' + C_BLUE=$'\033[34m'; C_MAGENTA=$'\033[35m'; C_CYAN=$'\033[36m' +else + C_RESET=; C_BOLD=; C_DIM=; C_RED=; C_GREEN=; C_YELLOW=; C_BLUE=; C_MAGENTA=; C_CYAN= +fi + +# REPO_LABEL can be set by the caller (e.g. cargo_update_all.sh) to prefix logs +LABEL="${REPO_LABEL:-cargo_update}" +log() { echo "${C_CYAN}[${LABEL}]${C_RESET} $*"; } +info() { echo "${C_BLUE}[${LABEL}]${C_RESET} $*"; } +warn() { echo "${C_YELLOW}[${LABEL}]${C_RESET} $*" >&2; } +error() { echo "${C_RED}[${LABEL}] ERROR:${C_RESET} $*" >&2; } + # --- Locate a JS package runner (pnpm dlx preferred, npx fallback) ---------- find_runner() { if command -v pnpm &>/dev/null; then @@ -11,7 +27,7 @@ find_runner() { elif command -v npx &>/dev/null; then echo "npx" else - echo "Error: neither pnpm nor npx found. Install one of them first." >&2 + error "neither pnpm nor npx found. Install one of them first." exit 1 fi } @@ -72,36 +88,42 @@ open_url() { ALLOWED_TOOLS="Read Edit Write Bash" # --- Execute or dry-run ----------------------------------------------------- +print_config() { + info "Runner: ${C_BOLD}${RUNNER}${C_RESET}" + info "Target: ${C_BOLD}${TARGET_DIR}${C_RESET}" + info "Prompt length: ${C_BOLD}${#PROMPT}${C_RESET} chars" + info "Allowed tools: ${C_DIM}${ALLOWED_TOOLS}${C_RESET}" +} + if [[ "$EXECUTE" == true ]]; then - echo "Using runner: ${RUNNER}" - echo "Target: ${TARGET_DIR}" - echo "Prompt length: ${#PROMPT} chars" - echo "Allowed tools: ${ALLOWED_TOOLS}" - echo "---" + log "${C_GREEN}${C_BOLD}▶ EXECUTE${C_RESET} updating ${C_MAGENTA}${LABEL}${C_RESET}" + print_config + log "${C_DIM}──── claude output ────${C_RESET}" cd "$TARGET_DIR" OUTPUT="$(echo "${PROMPT}" | ${RUNNER} @anthropic-ai/claude-code --print \ --allowed-tools ${ALLOWED_TOOLS})" 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)" if [[ -n "$PR_URL" ]]; then - echo "Opening PR: ${PR_URL}" + 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" + else + log "${C_YELLOW}No PR URL detected in output for ${LABEL}${C_RESET}" fi else - echo "=== DRY RUN ===" - echo "" + log "${C_YELLOW}${C_BOLD}=== DRY RUN ===${C_RESET} (${C_MAGENTA}${LABEL}${C_RESET})" + echo echo "${PROMPT}" - echo "---" - echo "Runner: ${RUNNER}" - echo "Target: ${TARGET_DIR}" - echo "Prompt length: ${#PROMPT} chars" - echo "Allowed tools: ${ALLOWED_TOOLS}" - echo "" - echo "Would run:" - echo " cd ${TARGET_DIR}" - echo " echo \"\${PROMPT}\" | ${RUNNER} @anthropic-ai/claude-code --print --allowed-tools ${ALLOWED_TOOLS}" - echo "" - echo "Pass --execute to run this against Claude Code." + log "${C_DIM}────────────────${C_RESET}" + print_config + echo + log "Would run:" + echo " ${C_DIM}cd ${TARGET_DIR}${C_RESET}" + echo " ${C_DIM}echo \"\${PROMPT}\" | ${RUNNER} @anthropic-ai/claude-code --print --allowed-tools ${ALLOWED_TOOLS}${C_RESET}" + echo + log "Pass ${C_BOLD}--execute${C_RESET} to run this against Claude Code." fi diff --git a/scripts/cargo_update_all.sh b/scripts/cargo_update_all.sh index ece89a3..97061d6 100755 --- a/scripts/cargo_update_all.sh +++ b/scripts/cargo_update_all.sh @@ -5,8 +5,27 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DOWNSTREAM_FILE="${SCRIPT_DIR}/downstream.txt" CARGO_UPDATE="${SCRIPT_DIR}/cargo_update.sh" +# --- Colors (disabled when not a tty or NO_COLOR is set) -------------------- +if [[ -t 1 && -z "${NO_COLOR:-}" ]]; then + C_RESET=$'\033[0m' + C_BOLD=$'\033[1m' + C_DIM=$'\033[2m' + C_RED=$'\033[31m' + C_GREEN=$'\033[32m' + C_YELLOW=$'\033[33m' + C_BLUE=$'\033[34m' + C_MAGENTA=$'\033[35m' + C_CYAN=$'\033[36m' +else + C_RESET=; C_BOLD=; C_DIM=; C_RED=; C_GREEN=; C_YELLOW=; C_BLUE=; C_MAGENTA=; C_CYAN= +fi + +log() { echo "${C_CYAN}[update-all]${C_RESET} $*"; } +warn() { echo "${C_YELLOW}[update-all]${C_RESET} $*" >&2; } +error() { echo "${C_RED}[update-all] ERROR:${C_RESET} $*" >&2; } + if [[ ! -f "$DOWNSTREAM_FILE" ]]; then - echo "Error: ${DOWNSTREAM_FILE} not found" >&2 + error "${DOWNSTREAM_FILE} not found" exit 1 fi @@ -15,46 +34,79 @@ ARGS=("$@") # Create a shared tmp directory for all clones WORK_DIR="$(mktemp -d)" -echo "Clone directory: ${WORK_DIR}" +log "Clone directory: ${C_DIM}${WORK_DIR}${C_RESET}" +if [[ ${#ARGS[@]} -gt 0 ]]; then + log "Forwarding args: ${C_BOLD}${ARGS[*]}${C_RESET}" +else + log "Mode: ${C_YELLOW}dry-run${C_RESET} (pass --execute to apply)" +fi -PIDS=() +# Collect repos first so we can report a plan REPOS=() -CLONE_DIRS=() - while IFS= read -r repo_url; do - # Skip empty lines and comments [[ -z "$repo_url" || "$repo_url" == \#* ]] && continue + REPOS+=("$repo_url") +done < "$DOWNSTREAM_FILE" +log "Found ${C_BOLD}${#REPOS[@]}${C_RESET} downstream repo(s):" +for r in "${REPOS[@]}"; do + echo " ${C_DIM}•${C_RESET} $r" +done + +PIDS=() +CLONE_DIRS=() +REPO_NAMES=() + +for repo_url in "${REPOS[@]}"; do # Derive repo name from URL (e.g. https://github.com/sksizer/rust-dir-aspect/ -> rust-dir-aspect) - REPO_NAME="$(basename "${repo_url%/}")" + REPO_NAME="$(basename "${repo_url%/}" .git)" CLONE_PATH="${WORK_DIR}/${REPO_NAME}" + LOG_FILE="${WORK_DIR}/${REPO_NAME}.log" - echo "Cloning: ${repo_url} -> ${CLONE_PATH}" + log "${C_MAGENTA}▶ ${REPO_NAME}${C_RESET} — cloning ${C_DIM}${repo_url}${C_RESET}" ( - git clone --quiet "$repo_url" "$CLONE_PATH" - bash "$CARGO_UPDATE" ${ARGS[@]+"${ARGS[@]}"} "$CLONE_PATH" + { + echo "${C_BOLD}${C_MAGENTA}=== ${REPO_NAME} ===${C_RESET}" + echo "${C_CYAN}[${REPO_NAME}]${C_RESET} repo: ${repo_url}" + echo "${C_CYAN}[${REPO_NAME}]${C_RESET} clone path: ${CLONE_PATH}" + git clone --quiet "$repo_url" "$CLONE_PATH" + echo "${C_CYAN}[${REPO_NAME}]${C_RESET} running cargo_update.sh ${ARGS[*]:-}" + REPO_LABEL="$REPO_NAME" bash "$CARGO_UPDATE" ${ARGS[@]+"${ARGS[@]}"} "$CLONE_PATH" + } >"$LOG_FILE" 2>&1 ) & PIDS+=($!) - REPOS+=("$repo_url") + REPO_NAMES+=("$REPO_NAME") CLONE_DIRS+=("$CLONE_PATH") -done < "$DOWNSTREAM_FILE" +done # Wait for all and report results FAILED=0 for i in "${!PIDS[@]}"; do + NAME="${REPO_NAMES[$i]}" + URL="${REPOS[$i]}" + LOG_FILE="${WORK_DIR}/${NAME}.log" if wait "${PIDS[$i]}"; then - echo "Done: ${REPOS[$i]}" + echo + echo "${C_GREEN}${C_BOLD}✔ ${NAME}${C_RESET} ${C_DIM}(${URL})${C_RESET}" + [[ -f "$LOG_FILE" ]] && sed "s/^/ ${C_DIM}│${C_RESET} /" "$LOG_FILE" else - echo "FAILED: ${REPOS[$i]}" >&2 + echo + echo "${C_RED}${C_BOLD}✘ ${NAME} FAILED${C_RESET} ${C_DIM}(${URL})${C_RESET}" >&2 + [[ -f "$LOG_FILE" ]] && sed "s/^/ ${C_RED}│${C_RESET} /" "$LOG_FILE" >&2 FAILED=$((FAILED + 1)) fi done -echo "---" -echo "Finished: $((${#PIDS[@]} - FAILED))/${#PIDS[@]} succeeded" +echo +echo "${C_BOLD}────────────────────────────────────────${C_RESET}" +if [[ $FAILED -eq 0 ]]; then + log "${C_GREEN}All ${#PIDS[@]} repo(s) succeeded${C_RESET}" +else + log "${C_YELLOW}$((${#PIDS[@]} - FAILED))/${#PIDS[@]} succeeded, ${C_RED}${FAILED} failed${C_RESET}" +fi # Clean up clones -echo "Cleaning up: ${WORK_DIR}" +log "Cleaning up: ${C_DIM}${WORK_DIR}${C_RESET}" rm -rf "$WORK_DIR" exit $FAILED