-
Notifications
You must be signed in to change notification settings - Fork 419
Improve go mod checks with hack/verify-go-modules.sh #3373
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -26,7 +26,38 @@ set -o pipefail | |||||||||||||
|
||||||||||||||
REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) | ||||||||||||||
|
||||||||||||||
mapfile -t DIRS < <(find "${REPO_ROOT}" -name go.mod -print0 | xargs -0 dirname) | ||||||||||||||
if [[ ! -f "${REPO_ROOT}/go.work" ]]; then | ||||||||||||||
( | ||||||||||||||
cd "${REPO_ROOT}" | ||||||||||||||
go work init | ||||||||||||||
go work use -r . | ||||||||||||||
) | ||||||||||||||
fi | ||||||||||||||
|
||||||||||||||
# Array of all modules inside the repository, sorted in | ||||||||||||||
# descending topological order (dependant comes before its dependee). | ||||||||||||||
mapfile -t MODULE_DIRS < <( | ||||||||||||||
for mod in $(tsort <( | ||||||||||||||
# Lines in format "<Dependant> <Dependee>" are fed into `tsort` to perform the topological sort. | ||||||||||||||
for dir in $(go list -m -json | jq -r '.Dir'); do | ||||||||||||||
( | ||||||||||||||
cd "${dir}" | ||||||||||||||
# Prints the dependency graph where we extract only the direct | ||||||||||||||
# github.com/kcp-dev/kcp/* dependencies of the currently examined module, | ||||||||||||||
# and we discard the dependency version (the line is in format | ||||||||||||||
# "<This module> <Its dependency>@<Version>", so we split by '@' and get the | ||||||||||||||
# first part). We skip when no such deps are found. | ||||||||||||||
go mod graph \ | ||||||||||||||
| grep -E "^$(go mod edit -json | jq -r '.Module.Path') github.com/kcp-dev/kcp/" \ | ||||||||||||||
| cut -d'@' -f1 \ | ||||||||||||||
|| true | ||||||||||||||
) | ||||||||||||||
done | ||||||||||||||
) | tac); do # We need to reverse the lines (with `tac`) to have a descending order. | ||||||||||||||
# We have sorted module paths. We need to convert them into their respective directory locations. | ||||||||||||||
go list -m -json "$mod" | jq -r '.Dir' | ||||||||||||||
done | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
# list_deps lists dependencies of the supplied go.mod file (dependencies | ||||||||||||||
# with version "v0.0.0" are skipped). The output is a json dictionary in the | ||||||||||||||
|
@@ -55,42 +86,40 @@ function diff_version_deps { | |||||||||||||
has_deps="${1}" | ||||||||||||||
wants_deps="${2}" | ||||||||||||||
jq -s ' | ||||||||||||||
# Extracts v<Maj>.<Min> from a semantic version string. | ||||||||||||||
def major_minor_semver: capture("v(?<major>\\d+)\\.(?<minor>\\d+)") | ||||||||||||||
| "v" + .major + "." + .minor; | ||||||||||||||
|
||||||||||||||
map(to_entries) # Convert both input dicts into two separate arrays. | ||||||||||||||
| add # Concatenate those two arrays into a single one. | ||||||||||||||
| group_by(.key) # Group items by `.key`. | ||||||||||||||
| map( # Map each selected item into { "<Dep>": {"has": "<Version0>", "wants": "<Version1>"} }. | ||||||||||||||
select( | ||||||||||||||
# If grouping resulted in two items, it means both arrays have this dependency. | ||||||||||||||
length == 2 | ||||||||||||||
# Compare the v<Maj>.<Min> of both items. | ||||||||||||||
and (.[0].value | major_minor_semver) != (.[1].value | major_minor_semver) | ||||||||||||||
# And the dependency has a version mismatch. | ||||||||||||||
and (.[0].value != .[1].value) | ||||||||||||||
) | ||||||||||||||
| { (.[0].key): { "has": .[0].value, "wants": .[1].value } } | ||||||||||||||
) | ||||||||||||||
| add // empty | ||||||||||||||
' "${has_deps}" "${wants_deps}" | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
# print_diff_version_deps prints the output of diff_version_deps as a | ||||||||||||||
# human-readable multi-line text. | ||||||||||||||
# print_diff_version_deps prints the output of diff_version_deps (expected in stdin) | ||||||||||||||
# as a human-readable multi-line text. | ||||||||||||||
# Returns 1 if any lines were printed (i.e. errors were found). | ||||||||||||||
function print_diff_version_deps { | ||||||||||||||
jq -r "to_entries | map(\"Warning: version mismatch: has \(.key)@\(.value.has), but \(.value.wants) expected\") | join(\"\\n\")" | ||||||||||||||
jq -r ' | ||||||||||||||
to_entries | ||||||||||||||
| map("Version mismatch: \(.key) is \(.value.has), but \(.value.wants) expected") | ||||||||||||||
| join("\n") | ||||||||||||||
' | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
# Compares versions of dependencies in the supplied go.mod to | ||||||||||||||
# makes sure they are in line with the ones declared in | ||||||||||||||
# k8s.io/kubernetes module and prints the result. | ||||||||||||||
function compare_mod_versions { | ||||||||||||||
# k8s.io/kubernetes module. | ||||||||||||||
function compare_mod_versions_with_k8s_deps { | ||||||||||||||
gomod_file="${1}" | ||||||||||||||
echo "Verifying dependency versions in ${gomod_file} against ${k8s_gomod}" | ||||||||||||||
deps="$(list_deps ${gomod_file})" | ||||||||||||||
|
||||||||||||||
diff_version_deps <(echo "${deps}") <(echo "${k8s_deps}") \ | ||||||||||||||
| print_diff_version_deps "${gomod_file}" | ||||||||||||||
diff_version_deps <(echo "${deps}") <(echo "${k8s_deps}") | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
function gomod_filepath_for { | ||||||||||||||
|
@@ -101,20 +130,27 @@ function gomod_filepath_for { | |||||||||||||
k8s_gomod="$(gomod_filepath_for k8s.io/kubernetes)" | ||||||||||||||
k8s_deps="$(list_deps ${k8s_gomod})" | ||||||||||||||
|
||||||||||||||
for dir in "${DIRS[@]}"; do | ||||||||||||||
for dir in "${MODULE_DIRS[@]}"; do | ||||||||||||||
( | ||||||||||||||
cd "$dir" | ||||||||||||||
echo "Verifying ${dir}" | ||||||||||||||
|
||||||||||||||
if ! git diff --quiet HEAD -- go.mod go.sum; then | ||||||||||||||
git --no-pager diff HEAD -- go.mod go.sum | ||||||||||||||
echo "Error: go.mod and/or go.sum in ${dir} files have been modified, inspect and commit them before continuing" 1>&2 | ||||||||||||||
if ! go mod tidy -diff ; then | ||||||||||||||
echo "Error: go.mod and/or go.sum is not clean, run 'go mod tidy' before continuing" 1>&2 | ||||||||||||||
exit 1 | ||||||||||||||
fi | ||||||||||||||
|
||||||||||||||
compare_mod_versions "${dir}/go.mod" | ||||||||||||||
gomod_file="${dir}/go.mod" | ||||||||||||||
echo "Verifying dependency versions in ${gomod_file} against ${k8s_gomod}" | ||||||||||||||
|
||||||||||||||
diff="$(compare_mod_versions_with_k8s_deps ${dir}/go.mod)" | ||||||||||||||
if [[ -n "${diff}" ]]; then | ||||||||||||||
echo "${diff}" | print_diff_version_deps 1>&2 | ||||||||||||||
echo "${diff}" | grep -q "k8s.io/" && { | ||||||||||||||
echo "Error: dependencies from '*k8s.io/*' must be in line with ${k8s_gomod}" 1>&2 | ||||||||||||||
exit 1 | ||||||||||||||
} || true | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Personally I'd prefer the script to not error but to continue going through the go.mods There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Sorry like this - the |
||||||||||||||
echo "Continuing because no fatal errors found" | ||||||||||||||
fi | ||||||||||||||
) | ||||||||||||||
done | ||||||||||||||
|
||||||||||||||
compare_mod_versions "$(gomod_filepath_for github.com/kcp-dev/client-go)" | ||||||||||||||
compare_mod_versions "$(gomod_filepath_for github.com/kcp-dev/apimachinery/v2)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just as a visual separator in the output - I felt like the mismatches were going missing a bit in the output