Skip to content
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

build: add 'go mod tidy' checker #797

Merged
merged 1 commit into from
Jan 15, 2025
Merged
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
build: add 'go mod tidy' checker
gzliudan committed Jan 15, 2025
commit 00797dda0c2a9899d5bea6bfd32867ef546850a2
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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)/*
26 changes: 25 additions & 1 deletion build/ci.go
Original file line number Diff line number Diff line change
@@ -24,9 +24,11 @@ Usage: go run build/ci.go <command> <command flags/arguments>
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 (
102 changes: 102 additions & 0 deletions internal/build/file.go
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

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
}