Skip to content

Commit 2fea1a2

Browse files
committed
hack/verify-go-modules.sh: check for dirty gomod and version mismatch
The script now exits with an error if go.mod and/or go.sum are dirty, or if dependencies from '*k8s.io/*' pull in different versions than the ones used in k8s.io/kubernetes. On-behalf-of: SAP robert.vasek@sap.com Signed-off-by: Robert Vasek <robert.vasek@clyso.com>
1 parent 4004244 commit 2fea1a2

File tree

1 file changed

+60
-24
lines changed

1 file changed

+60
-24
lines changed

hack/verify-go-modules.sh

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,38 @@ set -o pipefail
2626

2727
REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
2828

29-
mapfile -t DIRS < <(find "${REPO_ROOT}" -name go.mod -print0 | xargs -0 dirname)
29+
if [[ ! -f "${REPO_ROOT}/go.work" ]]; then
30+
(
31+
cd "${REPO_ROOT}"
32+
go work init
33+
go work use -r .
34+
)
35+
fi
36+
37+
# Array of all modules inside the repository, sorted in
38+
# descending topological order (dependant comes before its dependee).
39+
mapfile -t MODULE_DIRS < <(
40+
for mod in $(tsort <(
41+
# Lines in format "<Dependant> <Dependee>" are fed into `tsort` to perform the topological sort.
42+
for dir in $(go list -m -json | jq -r '.Dir'); do
43+
(
44+
cd "${dir}"
45+
# Prints the dependency graph where we extract only the direct
46+
# github.com/kcp-dev/kcp/* dependencies of the currently examined module,
47+
# and we discard the dependency version (the line is in format
48+
# "<This module> <Its dependency>@<Version>", so we split by '@' and get the
49+
# first part). We skip when no such deps are found.
50+
go mod graph \
51+
| grep -E "^$(go mod edit -json | jq -r '.Module.Path') github.com/kcp-dev/kcp/" \
52+
| cut -d'@' -f1 \
53+
|| true
54+
)
55+
done
56+
) | tac); do # We need to reverse the lines (with `tac`) to have a descending order.
57+
# We have sorted module paths. We need to convert them into their respective directory locations.
58+
go list -m -json "$mod" | jq -r '.Dir'
59+
done
60+
)
3061

3162
# list_deps lists dependencies of the supplied go.mod file (dependencies
3263
# with version "v0.0.0" are skipped). The output is a json dictionary in the
@@ -55,42 +86,40 @@ function diff_version_deps {
5586
has_deps="${1}"
5687
wants_deps="${2}"
5788
jq -s '
58-
# Extracts v<Maj>.<Min> from a semantic version string.
59-
def major_minor_semver: capture("v(?<major>\\d+)\\.(?<minor>\\d+)")
60-
| "v" + .major + "." + .minor;
61-
6289
map(to_entries) # Convert both input dicts into two separate arrays.
6390
| add # Concatenate those two arrays into a single one.
6491
| group_by(.key) # Group items by `.key`.
6592
| map( # Map each selected item into { "<Dep>": {"has": "<Version0>", "wants": "<Version1>"} }.
6693
select(
6794
# If grouping resulted in two items, it means both arrays have this dependency.
6895
length == 2
69-
# Compare the v<Maj>.<Min> of both items.
70-
and (.[0].value | major_minor_semver) != (.[1].value | major_minor_semver)
96+
# And the dependency has a version mismatch.
97+
and (.[0].value != .[1].value)
7198
)
7299
| { (.[0].key): { "has": .[0].value, "wants": .[1].value } }
73100
)
74101
| add // empty
75102
' "${has_deps}" "${wants_deps}"
76103
}
77104

78-
# print_diff_version_deps prints the output of diff_version_deps as a
79-
# human-readable multi-line text.
105+
# print_diff_version_deps prints the output of diff_version_deps (expected in stdin)
106+
# as a human-readable multi-line text.
107+
# Returns 1 if any lines were printed (i.e. errors were found).
80108
function print_diff_version_deps {
81-
jq -r "to_entries | map(\"Warning: version mismatch: has \(.key)@\(.value.has), but \(.value.wants) expected\") | join(\"\\n\")"
109+
jq -r '
110+
to_entries
111+
| map("Version mismatch: \(.key) is \(.value.has), but \(.value.wants) expected")
112+
| join("\n")
113+
'
82114
}
83115

84116
# Compares versions of dependencies in the supplied go.mod to
85117
# makes sure they are in line with the ones declared in
86-
# k8s.io/kubernetes module and prints the result.
87-
function compare_mod_versions {
118+
# k8s.io/kubernetes module.
119+
function compare_mod_versions_with_k8s_deps {
88120
gomod_file="${1}"
89-
echo "Verifying dependency versions in ${gomod_file} against ${k8s_gomod}"
90121
deps="$(list_deps ${gomod_file})"
91-
92-
diff_version_deps <(echo "${deps}") <(echo "${k8s_deps}") \
93-
| print_diff_version_deps "${gomod_file}"
122+
diff_version_deps <(echo "${deps}") <(echo "${k8s_deps}")
94123
}
95124

96125
function gomod_filepath_for {
@@ -101,20 +130,27 @@ function gomod_filepath_for {
101130
k8s_gomod="$(gomod_filepath_for k8s.io/kubernetes)"
102131
k8s_deps="$(list_deps ${k8s_gomod})"
103132

104-
for dir in "${DIRS[@]}"; do
133+
for dir in "${MODULE_DIRS[@]}"; do
105134
(
106135
cd "$dir"
107136
echo "Verifying ${dir}"
108137

109-
if ! git diff --quiet HEAD -- go.mod go.sum; then
110-
git --no-pager diff HEAD -- go.mod go.sum
111-
echo "Error: go.mod and/or go.sum in ${dir} files have been modified, inspect and commit them before continuing" 1>&2
138+
if ! go mod tidy -diff ; then
139+
echo "Error: go.mod and/or go.sum is not clean, run 'go mod tidy' before continuing" 1>&2
112140
exit 1
113141
fi
114142

115-
compare_mod_versions "${dir}/go.mod"
143+
gomod_file="${dir}/go.mod"
144+
echo "Verifying dependency versions in ${gomod_file} against ${k8s_gomod}"
145+
146+
diff="$(compare_mod_versions_with_k8s_deps ${dir}/go.mod)"
147+
if [[ -n "${diff}" ]]; then
148+
echo "${diff}" | print_diff_version_deps 1>&2
149+
echo "${diff}" | grep -q "k8s.io/" && {
150+
echo "Error: dependencies from '*k8s.io/*' must be in line with ${k8s_gomod}" 1>&2
151+
exit 1
152+
} || true
153+
echo "Continuing because no fatal errors found"
154+
fi
116155
)
117156
done
118-
119-
compare_mod_versions "$(gomod_filepath_for github.com/kcp-dev/client-go)"
120-
compare_mod_versions "$(gomod_filepath_for github.com/kcp-dev/apimachinery/v2)"

0 commit comments

Comments
 (0)