Skip to content

Commit 9810364

Browse files
mdelapenyaclaude
andauthored
fix(release): bump in-repo consumers when releasing a dependency module (#156)
* fix(release): consistency when releasing modules that update in-repo modules * fix(release): skip go mod tidy in pre-release.sh dry-run go mod tidy was running unconditionally in the per-module loop, which could mutate go.mod/go.sum (checksums, unused requires, new imports) even though portable_sed was a no-op in dry-run. Once prepare-release-pr.sh advertised dry-run as side-effect free, this became a real regression. Guard tidy with the same DRY_RUN check the version.go write already uses, so the dry-run path leaves the working tree untouched. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent a5dec42 commit 9810364

9 files changed

Lines changed: 221 additions & 435 deletions

File tree

.github/scripts/check-pre-release.sh

Lines changed: 0 additions & 82 deletions
This file was deleted.

.github/scripts/common.sh

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
# When true, commands are echoed instead of executed
1111
#
1212
# Functions:
13-
# curlGolangProxy - Trigger Go proxy to fetch module (for publishing)
14-
# execute_or_echo - Execute command or echo based on DRY_RUN setting
15-
# find_latest_tag - Find latest tag for a given module
16-
# get_modules - Get list of modules from go.work file
17-
# get_script_dir - Get directory of the calling script
18-
# portable_sed - Portable in-place sed editing
13+
# curlGolangProxy - Trigger Go proxy to fetch module (for publishing)
14+
# execute_or_echo - Execute command or echo based on DRY_RUN setting
15+
# find_latest_tag - Find latest tag for a given module
16+
# get_modules - Get list of modules from go.work file
17+
# get_modules_to_release - Expand a module to itself + transitive in-repo consumers
18+
# get_script_dir - Get directory of the calling script
19+
# portable_sed - Portable in-place sed editing
20+
# validate_git_remote - Verify origin points to docker/go-sdk
1921
#
2022
# Constants:
2123
# ROOT_DIR - Root directory of the repository
@@ -41,7 +43,18 @@ readonly BUILD_DIR="${ROOT_DIR}/.github/scripts/.build"
4143
readonly GITHUB_REPO="github.com/docker/go-sdk"
4244
readonly EXPECTED_ORIGIN_SSH="git@github.com:docker/go-sdk.git"
4345
readonly EXPECTED_ORIGIN_HTTPS="https://${GITHUB_REPO}.git"
44-
readonly DRY_RUN="${DRY_RUN:-true}"
46+
47+
# Normalize DRY_RUN: only the literal string "false" (any case) opts into a
48+
# real run. Everything else — typos like "True", "FALSE", "no", or unset —
49+
# stays in dry-run. This biases the safety default toward not making changes,
50+
# so a typo in the OFF case can't accidentally trigger a real release.
51+
# Export so the canonical value propagates to subprocess invocations.
52+
case "$(echo "${DRY_RUN:-true}" | tr '[:upper:]' '[:lower:]')" in
53+
false) DRY_RUN="false" ;;
54+
*) DRY_RUN="true" ;;
55+
esac
56+
export DRY_RUN
57+
readonly DRY_RUN
4558

4659
# This function is used to trigger the Go proxy to fetch the module.
4760
# See https://pkg.go.dev/about#adding-a-package for more details.
@@ -111,6 +124,46 @@ get_modules() {
111124
go work edit -json | jq -r '.Use[] | "\(.DiskPath | ltrimstr("./"))"' | tr '\n' ' ' && echo
112125
}
113126

127+
# Compute the set of modules that must be released together.
128+
#
129+
# When invoked without arguments, returns every module in go.work.
130+
#
131+
# When invoked with a single module name, returns that module plus every
132+
# in-repo module that requires it (transitively). This is required because
133+
# pre-release.sh rewrites the go.mod of every module that depends on the
134+
# released one, but only bumps version.go for the released module itself.
135+
# Without this expansion, consumer modules end up with rewritten go.mod
136+
# content under main while their existing tag still references the old
137+
# dependency version — leaving "main" inconsistent with the latest tag.
138+
get_modules_to_release() {
139+
local requested="${1:-}"
140+
local all_modules
141+
all_modules=$(get_modules)
142+
143+
if [[ -z "${requested}" ]]; then
144+
echo "${all_modules}"
145+
return
146+
fi
147+
148+
local to_release="${requested}"
149+
local added=1
150+
while [[ ${added} -eq 1 ]]; do
151+
added=0
152+
for m in ${all_modules}; do
153+
case " ${to_release} " in *" ${m} "*) continue ;; esac
154+
for dep in ${to_release}; do
155+
if grep -qE "${GITHUB_REPO}/${dep} v" "${ROOT_DIR}/${m}/go.mod" 2>/dev/null; then
156+
to_release="${to_release} ${m}"
157+
added=1
158+
break
159+
fi
160+
done
161+
done
162+
done
163+
164+
echo "${to_release}"
165+
}
166+
114167
# Function to find latest tag for a module
115168
find_latest_tag() {
116169
local module="$1"

.github/scripts/pre-release.sh

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ echo "${NEXT_vVERSION}" > "$(get_next_tag "${MODULE}")"
146146

147147
for m in $MODULES; do
148148
portable_sed "s|${GITHUB_REPO}/${MODULE} v[^[:space:]]*|${GITHUB_REPO}/${MODULE} v${NEXT_VERSION}|g" "${ROOT_DIR}/${m}/go.mod"
149-
# Update the go.sum file
150-
(cd "${ROOT_DIR}/${m}" && go mod tidy)
149+
# Update the go.sum file. Skip in dry-run: portable_sed was a no-op, so
150+
# go.mod is unchanged and there's nothing for tidy to reconcile — and tidy
151+
# itself can mutate go.mod/go.sum (checksums, unused requires, new imports),
152+
# which would break the dry-run "no working-tree changes" guarantee.
153+
if [[ "${DRY_RUN}" != "true" ]]; then
154+
(cd "${ROOT_DIR}/${m}" && go mod tidy)
155+
fi
151156
done

0 commit comments

Comments
 (0)