From 8c69492de8a75aa5da15e0c221a871c0ee2cebe6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 19:05:33 +0000 Subject: [PATCH 1/7] Initial plan From f5610036b9206cef35b79e5fd923c3d038ca17aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 19:11:09 +0000 Subject: [PATCH 2/7] Update bump-go.sh to handle missing toolchain directive - Add early trap setup to avoid exit code issues - Handle missing toolchain directive gracefully with `|| true` - Add logic to detect when toolchain is expected to be missing - Add informative messages about missing toolchain - Implement smart toolchain handling: - Skip toolchain when go version matches latest (redundant) - Add toolchain when go version is older than latest - Update toolchain when it exists but is outdated Co-authored-by: williammartin <1611510+williammartin@users.noreply.github.com> --- .github/workflows/scripts/bump-go.sh | 49 ++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/.github/workflows/scripts/bump-go.sh b/.github/workflows/scripts/bump-go.sh index f0762f3d3..853a26de8 100755 --- a/.github/workflows/scripts/bump-go.sh +++ b/.github/workflows/scripts/bump-go.sh @@ -49,23 +49,39 @@ echo " → toolchain : $TOOLCHAIN_VERSION" # ---- Prepare Git branch --------------------------------------------------- CURRENT_GO_DIRECTIVE=$(grep -E '^go ' "$GO_MOD" | cut -d ' ' -f2) -CURRENT_TOOLCHAIN_DIRECTIVE=$(grep -E '^toolchain ' "$GO_MOD" | cut -d ' ' -f2) - -if [[ "$CURRENT_GO_DIRECTIVE" = "$GO_DIRECTIVE_VERSION" && \ - "$CURRENT_TOOLCHAIN_DIRECTIVE" = "go$TOOLCHAIN_VERSION" ]]; then - echo "Already on latest Go version: $CURRENT_GO_DIRECTIVE (toolchain: $CURRENT_TOOLCHAIN_DIRECTIVE)" - exit 0 -fi +CURRENT_TOOLCHAIN_DIRECTIVE=$(grep -E '^toolchain ' "$GO_MOD" | cut -d ' ' -f2 || true) BRANCH="bump-go-$TOOLCHAIN_VERSION" +BRANCH_CREATED=0 + +# Set up cleanup trap early (before any potential exits) cleanup() { - git checkout - >/dev/null 2>&1 || true - git branch -D "$BRANCH" >/dev/null 2>&1 || true + if [[ $BRANCH_CREATED -eq 1 ]]; then + git checkout - >/dev/null 2>&1 || true + git branch -D "$BRANCH" >/dev/null 2>&1 || true + fi } trap cleanup EXIT +# Check if we're already up to date +# Note: toolchain directive may be missing when go directive == latest toolchain. +# This is expected behavior - `go mod tidy` removes the toolchain line when +# the minimum Go version matches the latest toolchain, as it's redundant. +if [[ "$CURRENT_GO_DIRECTIVE" = "$GO_DIRECTIVE_VERSION" ]]; then + if [[ -z "$CURRENT_TOOLCHAIN_DIRECTIVE" ]]; then + # No toolchain directive present - this is expected when go version == latest + echo "Already on latest Go version: $CURRENT_GO_DIRECTIVE" + echo " → Note: No toolchain directive (expected when go version matches latest toolchain)" + exit 0 + elif [[ "$CURRENT_TOOLCHAIN_DIRECTIVE" = "go$TOOLCHAIN_VERSION" ]]; then + echo "Already on latest Go version: $CURRENT_GO_DIRECTIVE (toolchain: $CURRENT_TOOLCHAIN_DIRECTIVE)" + exit 0 + fi +fi + echo "Creating branch $BRANCH" git switch -c "$BRANCH" >/dev/null 2>&1 +BRANCH_CREATED=1 # ---- Patch go.mod ----------------------------------------------------------- if [[ "$CURRENT_GO_DIRECTIVE" != "$GO_DIRECTIVE_VERSION" ]]; then @@ -73,7 +89,20 @@ if [[ "$CURRENT_GO_DIRECTIVE" != "$GO_DIRECTIVE_VERSION" ]]; then echo " • go directive $CURRENT_GO_DIRECTIVE → $GO_DIRECTIVE_VERSION" fi -if [[ "$CURRENT_TOOLCHAIN_DIRECTIVE" != "go$TOOLCHAIN_VERSION" ]]; then +# Handle toolchain directive - may need to add, update, or skip +if [[ -z "$CURRENT_TOOLCHAIN_DIRECTIVE" ]]; then + # No toolchain directive exists + if [[ "$GO_DIRECTIVE_VERSION" = "$(cut -d. -f1-2 <<< "$TOOLCHAIN_VERSION").0" ]]; then + # go directive matches latest toolchain - toolchain line is redundant + echo " • toolchain directive not needed (go version matches latest toolchain)" + else + # go directive is older than latest toolchain - add toolchain directive after go line + sed -Ei.bak "/^go [0-9]+\.[0-9]+/a\\ +toolchain go$TOOLCHAIN_VERSION" "$GO_MOD" + echo " • toolchain directive added: go$TOOLCHAIN_VERSION" + fi +elif [[ "$CURRENT_TOOLCHAIN_DIRECTIVE" != "go$TOOLCHAIN_VERSION" ]]; then + # Toolchain directive exists but needs updating sed -Ei.bak "s/^toolchain go[0-9]+\.[0-9]+\.[0-9]+.*$/toolchain go$TOOLCHAIN_VERSION/" "$GO_MOD" echo " • toolchain $CURRENT_TOOLCHAIN_DIRECTIVE → go$TOOLCHAIN_VERSION" fi From 653ffc23ca27a82d3e3a4be9b7800e2aefeca4ec Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 19:13:09 +0000 Subject: [PATCH 3/7] Improve version comparison to handle both X.Y.0 and X.Y.Z formats - Compare major.minor versions instead of exact string match - Preserve go directive if already at latest major.minor - Handle cases where go.mod has full patch version (e.g., 1.25.6) - Update PR body to use actual go directive from modified go.mod - Add detailed comments explaining version handling logic Co-authored-by: williammartin <1611510+williammartin@users.noreply.github.com> --- .github/workflows/scripts/bump-go.sh | 45 ++++++++++++++++++---------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/.github/workflows/scripts/bump-go.sh b/.github/workflows/scripts/bump-go.sh index 853a26de8..a49497939 100755 --- a/.github/workflows/scripts/bump-go.sh +++ b/.github/workflows/scripts/bump-go.sh @@ -40,17 +40,19 @@ echo "Fetching latest stable Go version…" LATEST_JSON=$(curl -fsSL https://go.dev/dl/?mode=json | jq -c '[.[] | select(.stable==true)][0]') FULL_VERSION=$(jq -r '.version' <<< "$LATEST_JSON") # e.g. go1.23.4 TOOLCHAIN_VERSION="${FULL_VERSION#go}" # e.g. 1.23.4 -# `go mod tidy` will always add `.0` if there is no minor version -# so let's just ensure .0 is suffixed to the go directive. -GO_DIRECTIVE_VERSION="$(cut -d. -f1-2 <<< "$TOOLCHAIN_VERSION").0" -echo " → go : $GO_DIRECTIVE_VERSION" -echo " → toolchain : $TOOLCHAIN_VERSION" +# The go directive can be either X.Y.0 (minor version) or X.Y.Z (patch version) +# We accept both forms as "latest" if they match the toolchain's major.minor +LATEST_MAJOR_MINOR="$(cut -d. -f1-2 <<< "$TOOLCHAIN_VERSION")" + +echo " → latest toolchain : $TOOLCHAIN_VERSION" # ---- Prepare Git branch --------------------------------------------------- CURRENT_GO_DIRECTIVE=$(grep -E '^go ' "$GO_MOD" | cut -d ' ' -f2) CURRENT_TOOLCHAIN_DIRECTIVE=$(grep -E '^toolchain ' "$GO_MOD" | cut -d ' ' -f2 || true) +CURRENT_MAJOR_MINOR="$(cut -d. -f1-2 <<< "$CURRENT_GO_DIRECTIVE")" + BRANCH="bump-go-$TOOLCHAIN_VERSION" BRANCH_CREATED=0 @@ -64,19 +66,21 @@ cleanup() { trap cleanup EXIT # Check if we're already up to date -# Note: toolchain directive may be missing when go directive == latest toolchain. +# Note: toolchain directive may be missing when go directive >= latest toolchain. # This is expected behavior - `go mod tidy` removes the toolchain line when -# the minimum Go version matches the latest toolchain, as it's redundant. -if [[ "$CURRENT_GO_DIRECTIVE" = "$GO_DIRECTIVE_VERSION" ]]; then +# the minimum Go version matches or exceeds the latest toolchain, as it's redundant. +if [[ "$CURRENT_MAJOR_MINOR" = "$LATEST_MAJOR_MINOR" ]]; then + # Current go directive is at the same major.minor as latest if [[ -z "$CURRENT_TOOLCHAIN_DIRECTIVE" ]]; then - # No toolchain directive present - this is expected when go version == latest - echo "Already on latest Go version: $CURRENT_GO_DIRECTIVE" + # No toolchain directive present - this is expected when go version >= latest + echo "Already on latest Go version: $CURRENT_GO_DIRECTIVE (latest toolchain: $TOOLCHAIN_VERSION)" echo " → Note: No toolchain directive (expected when go version matches latest toolchain)" exit 0 elif [[ "$CURRENT_TOOLCHAIN_DIRECTIVE" = "go$TOOLCHAIN_VERSION" ]]; then echo "Already on latest Go version: $CURRENT_GO_DIRECTIVE (toolchain: $CURRENT_TOOLCHAIN_DIRECTIVE)" exit 0 fi + # Current go directive is latest but toolchain is outdated - continue to update toolchain fi echo "Creating branch $BRANCH" @@ -84,16 +88,22 @@ git switch -c "$BRANCH" >/dev/null 2>&1 BRANCH_CREATED=1 # ---- Patch go.mod ----------------------------------------------------------- -if [[ "$CURRENT_GO_DIRECTIVE" != "$GO_DIRECTIVE_VERSION" ]]; then - sed -Ei.bak "s/^go [0-9]+\.[0-9]+.*$/go $GO_DIRECTIVE_VERSION/" "$GO_MOD" - echo " • go directive $CURRENT_GO_DIRECTIVE → $GO_DIRECTIVE_VERSION" +# Only update go directive if we're not already at the latest major.minor version +if [[ "$CURRENT_MAJOR_MINOR" != "$LATEST_MAJOR_MINOR" ]]; then + # Bump to the latest major.minor.0 (preserves the convention of X.Y.0 for go directive) + NEW_GO_DIRECTIVE="$LATEST_MAJOR_MINOR.0" + sed -Ei.bak "s/^go [0-9]+\.[0-9]+.*$/go $NEW_GO_DIRECTIVE/" "$GO_MOD" + echo " • go directive $CURRENT_GO_DIRECTIVE → $NEW_GO_DIRECTIVE" + # After updating, the current go directive is now the new one for toolchain logic + CURRENT_GO_DIRECTIVE="$NEW_GO_DIRECTIVE" fi # Handle toolchain directive - may need to add, update, or skip if [[ -z "$CURRENT_TOOLCHAIN_DIRECTIVE" ]]; then # No toolchain directive exists - if [[ "$GO_DIRECTIVE_VERSION" = "$(cut -d. -f1-2 <<< "$TOOLCHAIN_VERSION").0" ]]; then - # go directive matches latest toolchain - toolchain line is redundant + CURRENT_MAJOR_MINOR="$(cut -d. -f1-2 <<< "$CURRENT_GO_DIRECTIVE")" + if [[ "$CURRENT_MAJOR_MINOR" = "$LATEST_MAJOR_MINOR" ]]; then + # go directive is at latest major.minor - toolchain line is redundant echo " • toolchain directive not needed (go version matches latest toolchain)" else # go directive is older than latest toolchain - add toolchain directive after go line @@ -139,10 +149,13 @@ if [[ $APPLY -eq 0 ]]; then fi # ---- Push & PR -------------------------------------------------------------- +# Get the actual go directive from the updated go.mod +FINAL_GO_DIRECTIVE=$(grep -E '^go ' "$GO_MOD" | cut -d ' ' -f2) + PR_BODY=$(cat < Date: Thu, 29 Jan 2026 19:16:20 +0000 Subject: [PATCH 4/7] Address code review comments - Clarify comment to say "matches" instead of "matches or exceeds" - Update PR body to accurately reflect final go.mod state - Show actual toolchain if present, or note it's not specified - Provide clear explanation when toolchain is omitted Co-authored-by: williammartin <1611510+williammartin@users.noreply.github.com> --- .github/workflows/scripts/bump-go.sh | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/scripts/bump-go.sh b/.github/workflows/scripts/bump-go.sh index a49497939..24b995b4e 100755 --- a/.github/workflows/scripts/bump-go.sh +++ b/.github/workflows/scripts/bump-go.sh @@ -66,13 +66,13 @@ cleanup() { trap cleanup EXIT # Check if we're already up to date -# Note: toolchain directive may be missing when go directive >= latest toolchain. +# Note: toolchain directive may be missing when go directive matches latest toolchain. # This is expected behavior - `go mod tidy` removes the toolchain line when -# the minimum Go version matches or exceeds the latest toolchain, as it's redundant. +# the minimum Go version matches the latest toolchain, as it's redundant. if [[ "$CURRENT_MAJOR_MINOR" = "$LATEST_MAJOR_MINOR" ]]; then # Current go directive is at the same major.minor as latest if [[ -z "$CURRENT_TOOLCHAIN_DIRECTIVE" ]]; then - # No toolchain directive present - this is expected when go version >= latest + # No toolchain directive present - this is expected when go version matches latest echo "Already on latest Go version: $CURRENT_GO_DIRECTIVE (latest toolchain: $TOOLCHAIN_VERSION)" echo " → Note: No toolchain directive (expected when go version matches latest toolchain)" exit 0 @@ -151,14 +151,25 @@ fi # ---- Push & PR -------------------------------------------------------------- # Get the actual go directive from the updated go.mod FINAL_GO_DIRECTIVE=$(grep -E '^go ' "$GO_MOD" | cut -d ' ' -f2) +FINAL_TOOLCHAIN_DIRECTIVE=$(grep -E '^toolchain ' "$GO_MOD" | cut -d ' ' -f2 || true) -PR_BODY=$(cat < Date: Thu, 29 Jan 2026 19:44:36 +0000 Subject: [PATCH 5/7] Rewrite script to use go mod edit instead of grep/sed Replace manual parsing and editing with go mod edit: - Use 'go mod edit -json' to read current go and toolchain directives - Use 'go mod edit -go' to update go directive - Use 'go mod edit -toolchain' to update toolchain directive - Remove manual sed/grep parsing and .bak file handling - More reliable and maintainable than custom text manipulation Co-authored-by: williammartin <1611510+williammartin@users.noreply.github.com> --- .github/workflows/scripts/bump-go.sh | 34 ++++++++++++++++------------ 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/.github/workflows/scripts/bump-go.sh b/.github/workflows/scripts/bump-go.sh index 24b995b4e..eadeb8843 100755 --- a/.github/workflows/scripts/bump-go.sh +++ b/.github/workflows/scripts/bump-go.sh @@ -47,9 +47,14 @@ LATEST_MAJOR_MINOR="$(cut -d. -f1-2 <<< "$TOOLCHAIN_VERSION")" echo " → latest toolchain : $TOOLCHAIN_VERSION" -# ---- Prepare Git branch --------------------------------------------------- -CURRENT_GO_DIRECTIVE=$(grep -E '^go ' "$GO_MOD" | cut -d ' ' -f2) -CURRENT_TOOLCHAIN_DIRECTIVE=$(grep -E '^toolchain ' "$GO_MOD" | cut -d ' ' -f2 || true) +# ---- Read current go.mod state using go mod edit ---------------------------- +GO_MOD_JSON=$(go mod edit -json "$GO_MOD") +CURRENT_GO_DIRECTIVE=$(jq -r '.Go // ""' <<< "$GO_MOD_JSON") +CURRENT_TOOLCHAIN_DIRECTIVE=$(jq -r '.Toolchain // ""' <<< "$GO_MOD_JSON") + +# Handle empty values from jq +[[ "$CURRENT_GO_DIRECTIVE" == "null" || -z "$CURRENT_GO_DIRECTIVE" ]] && CURRENT_GO_DIRECTIVE="" +[[ "$CURRENT_TOOLCHAIN_DIRECTIVE" == "null" || -z "$CURRENT_TOOLCHAIN_DIRECTIVE" ]] && CURRENT_TOOLCHAIN_DIRECTIVE="" CURRENT_MAJOR_MINOR="$(cut -d. -f1-2 <<< "$CURRENT_GO_DIRECTIVE")" @@ -87,12 +92,12 @@ echo "Creating branch $BRANCH" git switch -c "$BRANCH" >/dev/null 2>&1 BRANCH_CREATED=1 -# ---- Patch go.mod ----------------------------------------------------------- +# ---- Patch go.mod using go mod edit ----------------------------------------- # Only update go directive if we're not already at the latest major.minor version if [[ "$CURRENT_MAJOR_MINOR" != "$LATEST_MAJOR_MINOR" ]]; then # Bump to the latest major.minor.0 (preserves the convention of X.Y.0 for go directive) NEW_GO_DIRECTIVE="$LATEST_MAJOR_MINOR.0" - sed -Ei.bak "s/^go [0-9]+\.[0-9]+.*$/go $NEW_GO_DIRECTIVE/" "$GO_MOD" + go mod edit -go="$NEW_GO_DIRECTIVE" "$GO_MOD" echo " • go directive $CURRENT_GO_DIRECTIVE → $NEW_GO_DIRECTIVE" # After updating, the current go directive is now the new one for toolchain logic CURRENT_GO_DIRECTIVE="$NEW_GO_DIRECTIVE" @@ -106,19 +111,16 @@ if [[ -z "$CURRENT_TOOLCHAIN_DIRECTIVE" ]]; then # go directive is at latest major.minor - toolchain line is redundant echo " • toolchain directive not needed (go version matches latest toolchain)" else - # go directive is older than latest toolchain - add toolchain directive after go line - sed -Ei.bak "/^go [0-9]+\.[0-9]+/a\\ -toolchain go$TOOLCHAIN_VERSION" "$GO_MOD" + # go directive is older than latest toolchain - add toolchain directive + go mod edit -toolchain="go$TOOLCHAIN_VERSION" "$GO_MOD" echo " • toolchain directive added: go$TOOLCHAIN_VERSION" fi elif [[ "$CURRENT_TOOLCHAIN_DIRECTIVE" != "go$TOOLCHAIN_VERSION" ]]; then # Toolchain directive exists but needs updating - sed -Ei.bak "s/^toolchain go[0-9]+\.[0-9]+\.[0-9]+.*$/toolchain go$TOOLCHAIN_VERSION/" "$GO_MOD" + go mod edit -toolchain="go$TOOLCHAIN_VERSION" "$GO_MOD" echo " • toolchain $CURRENT_TOOLCHAIN_DIRECTIVE → go$TOOLCHAIN_VERSION" fi -rm -f "$GO_MOD.bak" - git add "$GO_MOD" # ---- Commit ----------------------------------------------------------------- @@ -149,9 +151,13 @@ if [[ $APPLY -eq 0 ]]; then fi # ---- Push & PR -------------------------------------------------------------- -# Get the actual go directive from the updated go.mod -FINAL_GO_DIRECTIVE=$(grep -E '^go ' "$GO_MOD" | cut -d ' ' -f2) -FINAL_TOOLCHAIN_DIRECTIVE=$(grep -E '^toolchain ' "$GO_MOD" | cut -d ' ' -f2 || true) +# Get the actual go directive from the updated go.mod using go mod edit +FINAL_GO_MOD_JSON=$(go mod edit -json "$GO_MOD") +FINAL_GO_DIRECTIVE=$(jq -r '.Go // ""' <<< "$FINAL_GO_MOD_JSON") +FINAL_TOOLCHAIN_DIRECTIVE=$(jq -r '.Toolchain // ""' <<< "$FINAL_GO_MOD_JSON") + +# Handle empty/null values +[[ "$FINAL_TOOLCHAIN_DIRECTIVE" == "null" || -z "$FINAL_TOOLCHAIN_DIRECTIVE" ]] && FINAL_TOOLCHAIN_DIRECTIVE="" if [[ -n "$FINAL_TOOLCHAIN_DIRECTIVE" ]]; then PR_BODY=$(cat < Date: Thu, 29 Jan 2026 19:54:42 +0000 Subject: [PATCH 6/7] Remove unnecessary null checks in jq output handling The jq -r flag with // "" operator already returns empty string for null/missing values, never the literal string "null". The checks for == "null" are unnecessary. Co-authored-by: babakks <36728931+babakks@users.noreply.github.com> --- .github/workflows/scripts/bump-go.sh | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/scripts/bump-go.sh b/.github/workflows/scripts/bump-go.sh index eadeb8843..d67412499 100755 --- a/.github/workflows/scripts/bump-go.sh +++ b/.github/workflows/scripts/bump-go.sh @@ -52,10 +52,6 @@ GO_MOD_JSON=$(go mod edit -json "$GO_MOD") CURRENT_GO_DIRECTIVE=$(jq -r '.Go // ""' <<< "$GO_MOD_JSON") CURRENT_TOOLCHAIN_DIRECTIVE=$(jq -r '.Toolchain // ""' <<< "$GO_MOD_JSON") -# Handle empty values from jq -[[ "$CURRENT_GO_DIRECTIVE" == "null" || -z "$CURRENT_GO_DIRECTIVE" ]] && CURRENT_GO_DIRECTIVE="" -[[ "$CURRENT_TOOLCHAIN_DIRECTIVE" == "null" || -z "$CURRENT_TOOLCHAIN_DIRECTIVE" ]] && CURRENT_TOOLCHAIN_DIRECTIVE="" - CURRENT_MAJOR_MINOR="$(cut -d. -f1-2 <<< "$CURRENT_GO_DIRECTIVE")" BRANCH="bump-go-$TOOLCHAIN_VERSION" @@ -156,9 +152,6 @@ FINAL_GO_MOD_JSON=$(go mod edit -json "$GO_MOD") FINAL_GO_DIRECTIVE=$(jq -r '.Go // ""' <<< "$FINAL_GO_MOD_JSON") FINAL_TOOLCHAIN_DIRECTIVE=$(jq -r '.Toolchain // ""' <<< "$FINAL_GO_MOD_JSON") -# Handle empty/null values -[[ "$FINAL_TOOLCHAIN_DIRECTIVE" == "null" || -z "$FINAL_TOOLCHAIN_DIRECTIVE" ]] && FINAL_TOOLCHAIN_DIRECTIVE="" - if [[ -n "$FINAL_TOOLCHAIN_DIRECTIVE" ]]; then PR_BODY=$(cat < Date: Wed, 13 May 2026 13:25:39 +0200 Subject: [PATCH 7/7] Simplify bump-go.sh toolchain logic Address review feedback: always set both go and toolchain directives via go mod edit, then let go mod tidy normalize. This eliminates complex conditional toolchain handling. Additional fixes: - Add go mod tidy after edits to reconcile dependencies - Commit go.sum alongside go.mod - Filter PR search to open PRs only (--state open) - Use GITHUB_REPOSITORY for repo instead of hardcoding - Use git diff to detect no-op bumps post-tidy - Read go.mod state via go mod edit -json instead of grep Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/scripts/bump-go.sh | 124 +++++++++++---------------- 1 file changed, 49 insertions(+), 75 deletions(-) diff --git a/.github/workflows/scripts/bump-go.sh b/.github/workflows/scripts/bump-go.sh index d67412499..16dd346e8 100755 --- a/.github/workflows/scripts/bump-go.sh +++ b/.github/workflows/scripts/bump-go.sh @@ -1,15 +1,15 @@ #!/usr/bin/env bash # -# bump-go.sh — Update go.mod `go` directive and toolchain to latest stable Go release. +# bump-go.sh -- Update go.mod `go` directive and toolchain to latest stable Go release. # # Usage: # ./bump-go.sh [--apply|-a] # -# By default the script runs in *dry‑run* mode: it creates a local branch, +# By default the script runs in *dry-run* mode: it creates a local branch, # commits the version bump, shows the exact patch, **checks for an existing PR** # with the same title, and exits. Nothing is pushed. The temporary branch is # deleted automatically on exit, so your working tree stays clean. Pass -# --apply (or -a) to push the branch and open a new PR *only if one doesn’t +# --apply (or -a) to push the branch and open a new PR *only if one doesn't # already exist*. # ----------------------------------------------------------------------------- set -euo pipefail @@ -35,29 +35,32 @@ done [[ -z "$GO_MOD" ]] && usage [[ -f "$GO_MOD" ]] || { echo "Error: '$GO_MOD' not found" >&2; exit 1; } +REPO="cli/cli" +MODULE_DIR=$(dirname "$GO_MOD") +GO_SUM="$MODULE_DIR/go.sum" + # ---- Discover latest stable Go release -------------------------------------- -echo "Fetching latest stable Go version…" +echo "Fetching latest stable Go version..." LATEST_JSON=$(curl -fsSL https://go.dev/dl/?mode=json | jq -c '[.[] | select(.stable==true)][0]') FULL_VERSION=$(jq -r '.version' <<< "$LATEST_JSON") # e.g. go1.23.4 TOOLCHAIN_VERSION="${FULL_VERSION#go}" # e.g. 1.23.4 +GO_DIRECTIVE_VERSION="$(cut -d. -f1-2 <<< "$TOOLCHAIN_VERSION").0" -# The go directive can be either X.Y.0 (minor version) or X.Y.Z (patch version) -# We accept both forms as "latest" if they match the toolchain's major.minor -LATEST_MAJOR_MINOR="$(cut -d. -f1-2 <<< "$TOOLCHAIN_VERSION")" - -echo " → latest toolchain : $TOOLCHAIN_VERSION" +echo " → go directive : $GO_DIRECTIVE_VERSION" +echo " → toolchain : go$TOOLCHAIN_VERSION" # ---- Read current go.mod state using go mod edit ---------------------------- GO_MOD_JSON=$(go mod edit -json "$GO_MOD") CURRENT_GO_DIRECTIVE=$(jq -r '.Go // ""' <<< "$GO_MOD_JSON") -CURRENT_TOOLCHAIN_DIRECTIVE=$(jq -r '.Toolchain // ""' <<< "$GO_MOD_JSON") +CURRENT_TOOLCHAIN=$(jq -r '.Toolchain // ""' <<< "$GO_MOD_JSON") -CURRENT_MAJOR_MINOR="$(cut -d. -f1-2 <<< "$CURRENT_GO_DIRECTIVE")" +echo " → current go : $CURRENT_GO_DIRECTIVE" +echo " → current tc : ${CURRENT_TOOLCHAIN:-(none)}" +# ---- Prepare Git branch ----------------------------------------------------- BRANCH="bump-go-$TOOLCHAIN_VERSION" BRANCH_CREATED=0 -# Set up cleanup trap early (before any potential exits) cleanup() { if [[ $BRANCH_CREATED -eq 1 ]]; then git checkout - >/dev/null 2>&1 || true @@ -66,58 +69,32 @@ cleanup() { } trap cleanup EXIT -# Check if we're already up to date -# Note: toolchain directive may be missing when go directive matches latest toolchain. -# This is expected behavior - `go mod tidy` removes the toolchain line when -# the minimum Go version matches the latest toolchain, as it's redundant. -if [[ "$CURRENT_MAJOR_MINOR" = "$LATEST_MAJOR_MINOR" ]]; then - # Current go directive is at the same major.minor as latest - if [[ -z "$CURRENT_TOOLCHAIN_DIRECTIVE" ]]; then - # No toolchain directive present - this is expected when go version matches latest - echo "Already on latest Go version: $CURRENT_GO_DIRECTIVE (latest toolchain: $TOOLCHAIN_VERSION)" - echo " → Note: No toolchain directive (expected when go version matches latest toolchain)" - exit 0 - elif [[ "$CURRENT_TOOLCHAIN_DIRECTIVE" = "go$TOOLCHAIN_VERSION" ]]; then - echo "Already on latest Go version: $CURRENT_GO_DIRECTIVE (toolchain: $CURRENT_TOOLCHAIN_DIRECTIVE)" - exit 0 - fi - # Current go directive is latest but toolchain is outdated - continue to update toolchain -fi - echo "Creating branch $BRANCH" git switch -c "$BRANCH" >/dev/null 2>&1 BRANCH_CREATED=1 -# ---- Patch go.mod using go mod edit ----------------------------------------- -# Only update go directive if we're not already at the latest major.minor version -if [[ "$CURRENT_MAJOR_MINOR" != "$LATEST_MAJOR_MINOR" ]]; then - # Bump to the latest major.minor.0 (preserves the convention of X.Y.0 for go directive) - NEW_GO_DIRECTIVE="$LATEST_MAJOR_MINOR.0" - go mod edit -go="$NEW_GO_DIRECTIVE" "$GO_MOD" - echo " • go directive $CURRENT_GO_DIRECTIVE → $NEW_GO_DIRECTIVE" - # After updating, the current go directive is now the new one for toolchain logic - CURRENT_GO_DIRECTIVE="$NEW_GO_DIRECTIVE" -fi +# ---- Patch go.mod ----------------------------------------------------------- +# Always set both directives and let `go mod tidy` normalize. +# When the go directive version matches the toolchain version, tidy will remove +# the toolchain line because it is redundant -- this is expected Go behavior. +go mod edit -go="$GO_DIRECTIVE_VERSION" -toolchain="go$TOOLCHAIN_VERSION" "$GO_MOD" +echo " • set go directive → $GO_DIRECTIVE_VERSION" +echo " • set toolchain → go$TOOLCHAIN_VERSION" -# Handle toolchain directive - may need to add, update, or skip -if [[ -z "$CURRENT_TOOLCHAIN_DIRECTIVE" ]]; then - # No toolchain directive exists - CURRENT_MAJOR_MINOR="$(cut -d. -f1-2 <<< "$CURRENT_GO_DIRECTIVE")" - if [[ "$CURRENT_MAJOR_MINOR" = "$LATEST_MAJOR_MINOR" ]]; then - # go directive is at latest major.minor - toolchain line is redundant - echo " • toolchain directive not needed (go version matches latest toolchain)" - else - # go directive is older than latest toolchain - add toolchain directive - go mod edit -toolchain="go$TOOLCHAIN_VERSION" "$GO_MOD" - echo " • toolchain directive added: go$TOOLCHAIN_VERSION" - fi -elif [[ "$CURRENT_TOOLCHAIN_DIRECTIVE" != "go$TOOLCHAIN_VERSION" ]]; then - # Toolchain directive exists but needs updating - go mod edit -toolchain="go$TOOLCHAIN_VERSION" "$GO_MOD" - echo " • toolchain $CURRENT_TOOLCHAIN_DIRECTIVE → go$TOOLCHAIN_VERSION" +# Let go mod tidy reconcile dependencies and normalize directives. +echo " • running go mod tidy..." +pushd "$MODULE_DIR" > /dev/null +go mod tidy +popd > /dev/null + +# ---- Check if anything actually changed ------------------------------------- +if git diff --quiet -- "$GO_MOD" "$GO_SUM" 2>/dev/null; then + echo "Already on latest Go version -- no changes needed." + exit 0 fi git add "$GO_MOD" +[[ -f "$GO_SUM" ]] && git add "$GO_SUM" # ---- Commit ----------------------------------------------------------------- COMMIT_MSG="Bump Go to $TOOLCHAIN_VERSION" @@ -127,48 +104,45 @@ COMMIT_HASH=$(git rev-parse --short HEAD) PR_TITLE="$COMMIT_MSG" # ---- Check for existing PR -------------------------------------------------- -existing_pr=$(gh search prs --repo cli/cli --match title "$PR_TITLE" --json title --jq "map(select(.title == \"$PR_TITLE\") | .title) | length > 0") +existing_pr=$(gh search prs --repo "$REPO" --state open --match title "$PR_TITLE" \ + --json title --jq "map(select(.title == \"$PR_TITLE\") | .title) | length > 0") if [[ "$existing_pr" == "true" ]]; then echo "Found an existing open PR titled '$PR_TITLE'. Skipping push/PR creation." if [[ $APPLY -eq 0 ]]; then - echo -e "\n=== DRY‑RUN DIFF (commit $COMMIT_HASH):\n" + echo -e "\n=== DRY-RUN DIFF (commit $COMMIT_HASH):\n" git --no-pager show --color "$COMMIT_HASH" fi exit 0 fi -# ---- Dry‑run handling ------------------------------------------------------- +# ---- Dry-run handling ------------------------------------------------------- if [[ $APPLY -eq 0 ]]; then - echo -e "\n=== DRY‑RUN DIFF (commit $COMMIT_HASH):\n" + echo -e "\n=== DRY-RUN DIFF (commit $COMMIT_HASH):\n" git --no-pager show --color "$COMMIT_HASH" echo -e "\nIf --apply were provided, script would continue with:\n git push -u origin $BRANCH\n gh pr create --title \"$PR_TITLE\" --body \n" exit 0 fi # ---- Push & PR -------------------------------------------------------------- -# Get the actual go directive from the updated go.mod using go mod edit FINAL_GO_MOD_JSON=$(go mod edit -json "$GO_MOD") -FINAL_GO_DIRECTIVE=$(jq -r '.Go // ""' <<< "$FINAL_GO_MOD_JSON") -FINAL_TOOLCHAIN_DIRECTIVE=$(jq -r '.Toolchain // ""' <<< "$FINAL_GO_MOD_JSON") +FINAL_GO=$(jq -r '.Go // ""' <<< "$FINAL_GO_MOD_JSON") +FINAL_TC=$(jq -r '.Toolchain // ""' <<< "$FINAL_GO_MOD_JSON") -if [[ -n "$FINAL_TOOLCHAIN_DIRECTIVE" ]]; then - PR_BODY=$(cat <