Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions .github/scripts/check-go-mod-tidy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd "${script_dir}/../.." && pwd)"
cd "${repo_root}"

tmp_dir="$(mktemp -d)"
trap 'chmod -R u+w "${tmp_dir}" >/dev/null 2>&1 || true; rm -rf "${tmp_dir}"' EXIT
tmp_root="$(mktemp -d)"
trap 'chmod -R u+w "${tmp_root}" >/dev/null 2>&1 || true; rm -rf "${tmp_root}"' EXIT
export GOMODCACHE="${tmp_root}/gomodcache"

export GOMODCACHE="${tmp_dir}/gomodcache"
export GOPROXY="https://proxy.golang.org,direct"
export GOSUMDB="sum.golang.org"
# Setting these to empty does not reliably override values from `go env -w`.
Expand All @@ -25,6 +25,15 @@ fi

mkdir -p "${GOMODCACHE}"

# Keep downloaded archives and Go toolchains reusable, but drop extracted
# module source trees between modules.
clear_extracted_module_cache() {
find "${GOMODCACHE}" \
-path "${GOMODCACHE}/cache/download" -prune -o \
-path "${GOMODCACHE}/golang.org/toolchain@*" -prune -o \
-type d -name '*@*' -prune -exec rm -rf {} +
}

echo "::group::Checking go.mod/go.sum are tidy for all modules"

# Find all go.mod files that are part of published modules.
Expand Down Expand Up @@ -65,11 +74,11 @@ for mod_file in "${go_mod_files[@]}"; do
echo "::group::Tidying ${readable_name}"
cd "${mod_dir}"

tmp_dir="$(mktemp -d)"
cp go.mod "${tmp_dir}/go.mod"
module_tmp_dir="$(mktemp -d "${tmp_root}/module.XXXXXX")"
cp go.mod "${module_tmp_dir}/go.mod"
had_go_sum=false
if [ -f "go.sum" ]; then
cp go.sum "${tmp_dir}/go.sum"
cp go.sum "${module_tmp_dir}/go.sum"
had_go_sum=true
fi

Expand All @@ -78,12 +87,12 @@ for mod_file in "${go_mod_files[@]}"; do
mod_changed=false
sum_changed=false

if ! diff -q "${tmp_dir}/go.mod" go.mod >/dev/null; then
if ! diff -q "${module_tmp_dir}/go.mod" go.mod >/dev/null; then
mod_changed=true
fi

if [ "${had_go_sum}" = true ]; then
if ! diff -q "${tmp_dir}/go.sum" go.sum >/dev/null; then
if ! diff -q "${module_tmp_dir}/go.sum" go.sum >/dev/null; then
sum_changed=true
fi
else
Expand All @@ -97,12 +106,12 @@ for mod_file in "${go_mod_files[@]}"; do
issue_modules+=("${readable_name}")
if [ "${mod_changed}" = true ]; then
echo "::error::${readable_name}/go.mod is not up-to-date. Run 'go mod tidy' in ${readable_name}."
diff -u "${tmp_dir}/go.mod" go.mod | head -n 200 || true
diff -u "${module_tmp_dir}/go.mod" go.mod | head -n 200 || true
fi
if [ "${sum_changed}" = true ]; then
echo "::error::${readable_name}/go.sum is not up-to-date. Run 'go mod tidy' in ${readable_name}."
if [ "${had_go_sum}" = true ]; then
diff -u "${tmp_dir}/go.sum" go.sum | head -n 200 || true
diff -u "${module_tmp_dir}/go.sum" go.sum | head -n 200 || true
else
echo "::error::${readable_name}/go.sum was created by go mod tidy."
fi
Expand All @@ -111,7 +120,8 @@ for mod_file in "${go_mod_files[@]}"; do
echo "${readable_name}/go.mod and go.sum are up-to-date"
fi

rm -rf "${tmp_dir}"
clear_extracted_module_cache
rm -rf "${module_tmp_dir}"
cd "${repo_root}"
echo "::endgroup::"
done
Expand Down
114 changes: 114 additions & 0 deletions .github/scripts/verify-check-go-mod-tidy-cache.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/usr/bin/env bash
set -euo pipefail

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
test_tmp_root="$(mktemp -d)"
trap 'chmod -R u+w "${test_tmp_root}" >/dev/null 2>&1 || true; rm -rf "${test_tmp_root}"' EXIT

fixture_root="${test_tmp_root}/fixture"
bin_dir="${test_tmp_root}/bin"
mkdir -p "${fixture_root}/.github/scripts" "${fixture_root}/submodule" "${bin_dir}"
cp "${script_dir}/check-go-mod-tidy.sh" "${fixture_root}/.github/scripts/check-go-mod-tidy.sh"

cat >"${fixture_root}/go.mod" <<'EOF'
module example.com/root

go 1.21
EOF

cat >"${fixture_root}/submodule/go.mod" <<'EOF'
module example.com/submodule

go 1.21
EOF

cat >"${bin_dir}/go" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

if [ "$#" -ne 2 ] || [ "$1" != "mod" ] || [ "$2" != "tidy" ]; then
echo "unexpected go invocation: $*" >&2
exit 1
fi
previous_cache=""
if [ -z "${GOMODCACHE:-}" ]; then
echo "GOMODCACHE was not set for go mod tidy" >&2
exit 1
fi

if [ -s "${TIDY_TEST_CACHE_LOG}" ]; then
previous_cache="$(tail -n 1 "${TIDY_TEST_CACHE_LOG}")"
extracted_source="${previous_cache}/example.com/dependency@v1.0.0/source.go"
download_archive="${previous_cache}/cache/download/example.com/dependency/@v/v1.0.0.zip"
toolchain_marker="${previous_cache}/golang.org/toolchain@go1.25.0.linux-amd64/bin/go"
if [ -e "${extracted_source}" ]; then
echo "previous module's extracted cache was not removed: ${extracted_source}" >&2
exit 1
fi
if [ ! -e "${download_archive}" ]; then
echo "download archive was not retained for reuse: ${download_archive}" >&2
exit 1
fi
if [ ! -e "${toolchain_marker}" ]; then
echo "downloaded Go toolchain was not retained for reuse: ${toolchain_marker}" >&2
exit 1
fi
fi

printf '%s\n' "${GOMODCACHE}" >>"${TIDY_TEST_CACHE_LOG}"
mkdir -p "${GOMODCACHE}/example.com/dependency@v1.0.0"
mkdir -p "${GOMODCACHE}/cache/download/example.com/dependency/@v"
touch "${GOMODCACHE}/example.com/dependency@v1.0.0/source.go"
touch "${GOMODCACHE}/cache/download/example.com/dependency/@v/v1.0.0.zip"
mkdir -p "${GOMODCACHE}/golang.org/toolchain@go1.25.0.linux-amd64/bin"
touch "${GOMODCACHE}/golang.org/toolchain@go1.25.0.linux-amd64/bin/go"

if [ "${TIDY_TEST_FAIL_SECOND:-false}" = true ] && [ -n "${previous_cache}" ]; then
echo "simulated go mod tidy failure" >&2
exit 23
fi
EOF
chmod +x "${bin_dir}/go"

export PATH="${bin_dir}:${PATH}"
export TIDY_TEST_CACHE_LOG="${test_tmp_root}/cache-paths.log"
bash "${fixture_root}/.github/scripts/check-go-mod-tidy.sh" >/dev/null

cache_count=0
while IFS= read -r cache_path; do
if [ -e "${cache_path%/gomodcache}" ]; then
echo "module cache was not removed after tidy: ${cache_path}" >&2
exit 1
fi
cache_count=$((cache_count + 1))
done <"${TIDY_TEST_CACHE_LOG}"

if [ "${cache_count}" -ne 2 ]; then
echo "expected two go mod tidy invocations, got ${cache_count}" >&2
exit 1
fi

failure_cache_log="${test_tmp_root}/failure-cache-paths.log"
export TIDY_TEST_CACHE_LOG="${failure_cache_log}"
export TIDY_TEST_FAIL_SECOND=true
failure_status=0
bash "${fixture_root}/.github/scripts/check-go-mod-tidy.sh" >/dev/null 2>&1 || failure_status=$?
if [ "${failure_status}" -ne 23 ]; then
echo "expected tidy exit status 23, got ${failure_status}" >&2
exit 1
fi

failure_count=0
while IFS= read -r cache_path; do
if [ -e "${cache_path%/gomodcache}" ]; then
echo "module cache was not removed after tidy failed: ${cache_path}" >&2
exit 1
fi
failure_count=$((failure_count + 1))
done <"${failure_cache_log}"
if [ "${failure_count}" -ne 2 ]; then
echo "expected two go mod tidy invocations before simulated failure, got ${failure_count}" >&2
exit 1
fi

echo "check-go-mod-tidy cache cleanup test passed"
3 changes: 3 additions & 0 deletions .github/workflows/prc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ jobs:
with:
go-version: "${{ env.ROOT_GO_VERSION }}"
cache: false
- name: Verify go mod tidy cache cleanup
shell: bash
run: bash .github/scripts/verify-check-go-mod-tidy-cache.sh
- name: Check go.mod and go.sum are tidy
shell: bash
run: bash .github/scripts/check-go-mod-tidy.sh
Expand Down
Loading