From 00797dda0c2a9899d5bea6bfd32867ef546850a2 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 14 Jan 2025 09:46:27 +0800 Subject: [PATCH] build: add 'go mod tidy' checker --- Makefile | 4 ++ build/ci.go | 26 ++++++++++- internal/build/file.go | 102 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 internal/build/file.go diff --git a/Makefile b/Makefile index e92835b952..3b748a0a0b 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,10 @@ test: all lint: ## Run linters. $(GORUN) build/ci.go lint +#? check_tidy: Verify go.mod and go.sum by 'go mod tidy' +check_tidy: ## Run 'go mod tidy'. + $(GORUN) build/ci.go check_tidy + #? clean: Clean go cache, built executables, and the auto generated folder. clean: rm -fr build/_workspace/pkg/ $(GOBIN)/* diff --git a/build/ci.go b/build/ci.go index 3bdb52a7a3..3c0660a71a 100644 --- a/build/ci.go +++ b/build/ci.go @@ -24,9 +24,11 @@ Usage: go run build/ci.go Available commands are: + lint -- runs certain pre-selected linters + check_tidy -- verifies that everything is 'go mod tidy'-ed + install [ -arch architecture ] [ -cc compiler ] [ packages... ] -- builds packages and executables test [ -coverage ] [ packages... ] -- runs the tests - lint -- runs certain pre-selected linters importkeys -- imports signing keys from env xgo [ -alltools ] [ options ] -- cross builds according to options @@ -89,6 +91,8 @@ func main() { doTest(os.Args[2:]) case "lint": doLint(os.Args[2:]) + case "check_tidy": + doCheckTidy() case "xgo": doXgo(os.Args[2:]) default: @@ -246,6 +250,26 @@ func doTest(cmdline []string) { build.MustRun(gotest) } +// doCheckTidy assets that the Go modules files are tidied already. +func doCheckTidy() { + targets := []string{"go.mod", "go.sum"} + + hashes, err := build.HashFiles(targets) + if err != nil { + log.Fatalf("failed to hash go.mod/go.sum: %v", err) + } + build.MustRun(new(build.GoToolchain).Go("mod", "tidy")) + + tidied, err := build.HashFiles(targets) + if err != nil { + log.Fatalf("failed to rehash go.mod/go.sum: %v", err) + } + if updates := build.DiffHashes(hashes, tidied); len(updates) > 0 { + log.Fatalf("files changed on running 'go mod tidy': %v", updates) + } + fmt.Println("No untidy module files detected.") +} + // doLint runs golangci-lint on requested packages. func doLint(cmdline []string) { var ( diff --git a/internal/build/file.go b/internal/build/file.go new file mode 100644 index 0000000000..2d8c993f36 --- /dev/null +++ b/internal/build/file.go @@ -0,0 +1,102 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package build + +import ( + "crypto/sha256" + "io" + "os" + "path/filepath" + "sort" + "strings" +) + +// FileExist checks if a file exists at path. +func FileExist(path string) bool { + _, err := os.Stat(path) + if err != nil && os.IsNotExist(err) { + return false + } + return true +} + +// HashFiles iterates the provided set of files, computing the hash of each. +func HashFiles(files []string) (map[string][32]byte, error) { + res := make(map[string][32]byte) + for _, filePath := range files { + f, err := os.OpenFile(filePath, os.O_RDONLY, 0666) + if err != nil { + return nil, err + } + hasher := sha256.New() + if _, err := io.Copy(hasher, f); err != nil { + return nil, err + } + res[filePath] = [32]byte(hasher.Sum(nil)) + } + return res, nil +} + +// HashFolder iterates all files under the given directory, computing the hash +// of each. +func HashFolder(folder string, exlude []string) (map[string][32]byte, error) { + res := make(map[string][32]byte) + err := filepath.WalkDir(folder, func(path string, d os.DirEntry, _ error) error { + // Skip anything that's exluded or not a regular file + for _, skip := range exlude { + if strings.HasPrefix(path, filepath.FromSlash(skip)) { + return filepath.SkipDir + } + } + if !d.Type().IsRegular() { + return nil + } + // Regular file found, hash it + f, err := os.OpenFile(path, os.O_RDONLY, 0666) + if err != nil { + return err + } + hasher := sha256.New() + if _, err := io.Copy(hasher, f); err != nil { + return err + } + res[path] = [32]byte(hasher.Sum(nil)) + return nil + }) + if err != nil { + return nil, err + } + return res, nil +} + +// DiffHashes compares two maps of file hashes and returns the changed files. +func DiffHashes(a map[string][32]byte, b map[string][32]byte) []string { + var updates []string + + for file := range a { + if _, ok := b[file]; !ok || a[file] != b[file] { + updates = append(updates, file) + } + } + for file := range b { + if _, ok := a[file]; !ok { + updates = append(updates, file) + } + } + sort.Strings(updates) + return updates +}