Skip to content

Commit

Permalink
🌱 verify go modules are in sync with upstream k/k
Browse files Browse the repository at this point in the history
This commit addresses issues were go modules aren't in sync with
upstream k/k by adding these changes:
- add `tools/cmd/gomodcheck/main.go` to:
  - Parse and compares k/k direct dependencies to controller-runtime's
    direct deps.
  - If any version diffs is found, returns a payload describing the diffs
    and exit 1.
  - The user may exclude packages by passing them as arguments.
- extend the `verify-modules` make target with `gomodcheck`.

Signed-off-by: Alexandre Mahdhaoui <alexandre.mahdhaoui@gmail.com>
  • Loading branch information
alexandremahdhaoui committed Apr 14, 2024
1 parent 45e166d commit 86ad2dc
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 1 deletion.
9 changes: 9 additions & 0 deletions .gomodcheck.ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
github.com/onsi/gomega
github.com/onsi/ginkgo/v2

k8s.io/api
k8s.io/apimachinery
k8s.io/apiextensions-apiserver
k8s.io/apiserver
k8s.io/client-go
k8s.io/component-base
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ GOLANGCI_LINT_PKG := github.com/golangci/golangci-lint/cmd/golangci-lint
$(GOLANGCI_LINT): # Build golangci-lint from tools folder.
GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(GOLANGCI_LINT_PKG) $(GOLANGCI_LINT_BIN) $(GOLANGCI_LINT_VER)

GO_MOD_CHECK_DIR := $(abspath ./tools/cmd/gomodcheck)
GO_MOD_CHECK := $(abspath $(TOOLS_BIN_DIR)/gomodcheck)
GO_MOD_CHECK_IGNORE := $(abspath ./.gomodcheck.ignore)
$(GO_MOD_CHECK): # Build gomodcheck
go build -C $(GO_MOD_CHECK_DIR) -o $(GO_MOD_CHECK)

## --------------------------------------
## Linting
## --------------------------------------
Expand Down Expand Up @@ -134,11 +140,12 @@ clean-bin: ## Remove all generated binaries.
rm -rf hack/tools/bin

.PHONY: verify-modules
verify-modules: modules ## Verify go modules are up to date
verify-modules: modules $(GO_MOD_CHECK) ## Verify go modules are up to date
@if !(git diff --quiet HEAD -- go.sum go.mod $(TOOLS_DIR)/go.mod $(TOOLS_DIR)/go.sum $(ENVTEST_DIR)/go.mod $(ENVTEST_DIR)/go.sum $(SCRATCH_ENV_DIR)/go.sum); then \
git diff; \
echo "go module files are out of date, please run 'make modules'"; exit 1; \
fi
cat $(GO_MOD_CHECK_IGNORE) | xargs $(GO_MOD_CHECK)

APIDIFF_OLD_COMMIT ?= $(shell git rev-parse origin/main)

Expand Down
119 changes: 119 additions & 0 deletions tools/cmd/gomodcheck/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package main

import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"regexp"
"strings"

"sigs.k8s.io/controller-runtime/pkg/log/zap"
)

const (
kkModUrl = "https://raw.githubusercontent.com/kubernetes/kubernetes/master/go.mod"

Check failure on line 16 in tools/cmd/gomodcheck/main.go

View workflow job for this annotation

GitHub Actions / lint

var-naming: const kkModUrl should be kkModURL (revive)

Check warning on line 16 in tools/cmd/gomodcheck/main.go

View workflow job for this annotation

GitHub Actions / lint

var-naming: const kkModUrl should be kkModURL (revive)
modFile = "./go.mod"
)

var (
cleanMods = regexp.MustCompile(`\t| *//.*`)
modDelimStart = regexp.MustCompile(`^require.*`)
modDelimEnd = ")"
)

type oosMod struct {
Name string `json:"name"`
Version string `json:"version"`
UpstreamVersion string `json:"upstreamVersion"`
}

func main() {
logger := zap.New()
excludedMods := getExcludedMods()

// 1. kk
resp, err := http.Get(kkModUrl)

Check failure on line 37 in tools/cmd/gomodcheck/main.go

View workflow job for this annotation

GitHub Actions / lint

response body must be closed (bodyclose)

Check failure on line 37 in tools/cmd/gomodcheck/main.go

View workflow job for this annotation

GitHub Actions / lint

response body must be closed (bodyclose)
if err != nil {
panic(err.Error())
}

b, err := io.ReadAll(resp.Body)
if err != nil {
panic(err.Error())
}

kkMods := parseMod(b)

// 2. go.mod
b, err = os.ReadFile(modFile)
if err != nil {
return
}

oosMods := make([]oosMod, 0)

mods := parseMod(b)
for k, v := range mods {
if _, ok := excludedMods[k]; ok {
logger.Info(fmt.Sprintf("skipped module: %s", k))
continue
}

if upstreamVersion, ok := kkMods[k]; ok && v != upstreamVersion {
oosMods = append(oosMods, oosMod{
Name: k,
Version: v,
UpstreamVersion: upstreamVersion,
})
}
}

if len(oosMods) == 0 {
fmt.Println("Success! 🎉")
os.Exit(0)
}

b, err = json.MarshalIndent(map[string]any{"outOfSyncModules": oosMods}, "", " ")
if err != nil {
panic(err)
}

fmt.Println(string(b))
os.Exit(1)
}

func parseMod(b []byte) map[string]string {
in := string(cleanMods.ReplaceAll(b, []byte("")))
out := make(map[string]string)

start := false
for _, s := range strings.Split(in, "\n") {
switch {
case modDelimStart.MatchString(s) && !start:
start = true
case s == modDelimEnd:
return out
case start:
kv := strings.SplitN(s, " ", 2)
if len(kv) < 2 {
panic(fmt.Sprintf("unexpected format for module: %q", s))
}

out[kv[0]] = kv[1]
}
}

return out
}

func getExcludedMods() map[string]any {
out := make(map[string]any)

for _, mod := range os.Args[1:] {
out[mod] = nil
}

return out
}

0 comments on commit 86ad2dc

Please sign in to comment.