Skip to content

Commit 8c73523

Browse files
authored
appveyor, build, internal: ci.go cleanups, add package dep checker (#30696)
1 parent 236147b commit 8c73523

File tree

3 files changed

+156
-112
lines changed

3 files changed

+156
-112
lines changed

appveyor.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ for:
2424
- image: Ubuntu
2525
build_script:
2626
- go run build/ci.go lint
27-
- go run build/ci.go generate -verify
27+
- go run build/ci.go check_tidy
28+
- go run build/ci.go check_generate
29+
- go run build/ci.go check_baddeps
2830
- go run build/ci.go install -dlgo
2931
test_script:
3032
- go run build/ci.go test -dlgo -short

build/ci.go

+78-110
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,14 @@ Usage: go run build/ci.go <command> <command flags/arguments>
2424
2525
Available commands are:
2626
27-
install [ -arch architecture ] [ -cc compiler ] [ packages... ] -- builds packages and executables
28-
test [ -coverage ] [ packages... ] -- runs the tests
29-
lint -- runs certain pre-selected linters
27+
lint -- runs certain pre-selected linters
28+
check_tidy -- verifies that everything is 'go mod tidy'-ed
29+
check_generate -- verifies that everything is 'go generate'-ed
30+
check_baddeps -- verifies that certain dependencies are avoided
31+
32+
install [ -arch architecture ] [ -cc compiler ] [ packages... ] -- builds packages and executables
33+
test [ -coverage ] [ packages... ] -- runs the tests
34+
3035
archive [ -arch architecture ] [ -type zip|tar ] [ -signer key-envvar ] [ -signify key-envvar ] [ -upload dest ] -- archives build artifacts
3136
importkeys -- imports signing keys from env
3237
debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package
@@ -39,11 +44,9 @@ package main
3944

4045
import (
4146
"bytes"
42-
"crypto/sha256"
4347
"encoding/base64"
4448
"flag"
4549
"fmt"
46-
"io"
4750
"log"
4851
"os"
4952
"os/exec"
@@ -156,6 +159,12 @@ func main() {
156159
doTest(os.Args[2:])
157160
case "lint":
158161
doLint(os.Args[2:])
162+
case "check_tidy":
163+
doCheckTidy()
164+
case "check_generate":
165+
doCheckGenerate()
166+
case "check_baddeps":
167+
doCheckBadDeps()
159168
case "archive":
160169
doArchive(os.Args[2:])
161170
case "dockerx":
@@ -168,8 +177,6 @@ func main() {
168177
doPurge(os.Args[2:])
169178
case "sanitycheck":
170179
doSanityCheck()
171-
case "generate":
172-
doGenerate()
173180
default:
174181
log.Fatal("unknown command ", os.Args[1])
175182
}
@@ -348,130 +355,93 @@ func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string {
348355
return filepath.Join(cachedir, base)
349356
}
350357

351-
// hashAllSourceFiles iterates all files under the top-level project directory
352-
// computing the hash of each file (excluding files within the tests
353-
// subrepo)
354-
func hashAllSourceFiles() (map[string][32]byte, error) {
355-
res := make(map[string][32]byte)
356-
err := filepath.WalkDir(".", func(path string, d os.DirEntry, err error) error {
357-
if strings.HasPrefix(path, filepath.FromSlash("tests/testdata")) {
358-
return filepath.SkipDir
359-
}
360-
if !d.Type().IsRegular() {
361-
return nil
362-
}
363-
// open the file and hash it
364-
f, err := os.OpenFile(path, os.O_RDONLY, 0666)
365-
if err != nil {
366-
return err
367-
}
368-
hasher := sha256.New()
369-
if _, err := io.Copy(hasher, f); err != nil {
370-
return err
371-
}
372-
res[path] = [32]byte(hasher.Sum(nil))
373-
return nil
374-
})
375-
if err != nil {
376-
return nil, err
377-
}
378-
return res, nil
379-
}
358+
// doCheckTidy assets that the Go modules files are tidied already.
359+
func doCheckTidy() {
360+
targets := []string{"go.mod", "go.sum"}
380361

381-
// hashSourceFiles iterates the provided set of filepaths (relative to the top-level geth project directory)
382-
// computing the hash of each file.
383-
func hashSourceFiles(files []string) (map[string][32]byte, error) {
384-
res := make(map[string][32]byte)
385-
for _, filePath := range files {
386-
f, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
387-
if err != nil {
388-
return nil, err
389-
}
390-
hasher := sha256.New()
391-
if _, err := io.Copy(hasher, f); err != nil {
392-
return nil, err
393-
}
394-
res[filePath] = [32]byte(hasher.Sum(nil))
395-
}
396-
return res, nil
397-
}
398-
399-
// compareHashedFilesets compares two maps (key is relative file path to top-level geth directory, value is its hash)
400-
// and returns the list of file paths whose hashes differed.
401-
func compareHashedFilesets(preHashes map[string][32]byte, postHashes map[string][32]byte) []string {
402-
updates := []string{}
403-
for path, postHash := range postHashes {
404-
preHash, ok := preHashes[path]
405-
if !ok || preHash != postHash {
406-
updates = append(updates, path)
407-
}
362+
hashes, err := build.HashFiles(targets)
363+
if err != nil {
364+
log.Fatalf("failed to hash go.mod/go.sum: %v", err)
408365
}
409-
return updates
410-
}
366+
build.MustRun(new(build.GoToolchain).Go("mod", "tidy"))
411367

412-
// doGoModTidy runs 'go mod tidy' and asserts that go.sum/go.mod do not change
413-
// as a result.
414-
func doGoModTidy() {
415-
targetFiles := []string{"go.mod", "go.sum"}
416-
preHashes, err := hashSourceFiles(targetFiles)
368+
tidied, err := build.HashFiles(targets)
417369
if err != nil {
418-
log.Fatal("failed to hash go.mod/go.sum", "err", err)
419-
}
420-
tc := new(build.GoToolchain)
421-
c := tc.Go("mod", "tidy")
422-
build.MustRun(c)
423-
postHashes, err := hashSourceFiles(targetFiles)
424-
updates := compareHashedFilesets(preHashes, postHashes)
425-
for _, updatedFile := range updates {
426-
fmt.Fprintf(os.Stderr, "changed file %s\n", updatedFile)
370+
log.Fatalf("failed to rehash go.mod/go.sum: %v", err)
427371
}
428-
if len(updates) != 0 {
429-
log.Fatal("go.sum and/or go.mod were updated by running 'go mod tidy'")
372+
if updates := build.DiffHashes(hashes, tidied); len(updates) > 0 {
373+
log.Fatalf("files changed on running 'go mod tidy': %v", updates)
430374
}
375+
fmt.Println("No untidy module files detected.")
431376
}
432377

433-
// doGenerate ensures that re-generating generated files does not cause
434-
// any mutations in the source file tree: i.e. all generated files were
435-
// updated and committed. Any stale generated files are updated.
436-
func doGenerate() {
378+
// doCheckGenerate ensures that re-generating generated files does not cause
379+
// any mutations in the source file tree.
380+
func doCheckGenerate() {
437381
var (
438-
tc = new(build.GoToolchain)
439382
cachedir = flag.String("cachedir", "./build/cache", "directory for caching binaries.")
440-
verify = flag.Bool("verify", false, "check whether any files are changed by go generate")
441383
)
384+
// Compute the origin hashes of all the files
385+
var hashes map[string][32]byte
442386

443-
protocPath := downloadProtoc(*cachedir)
444-
protocGenGoPath := downloadProtocGenGo(*cachedir)
445-
446-
var preHashes map[string][32]byte
447-
if *verify {
448-
var err error
449-
preHashes, err = hashAllSourceFiles()
450-
if err != nil {
451-
log.Fatal("failed to compute map of source hashes", "err", err)
452-
}
387+
var err error
388+
hashes, err = build.HashFolder(".", []string{"tests/testdata", "build/cache"})
389+
if err != nil {
390+
log.Fatal("Error computing hashes", "err", err)
453391
}
454-
455-
c := tc.Go("generate", "./...")
392+
// Run any go generate steps we might be missing
393+
var (
394+
protocPath = downloadProtoc(*cachedir)
395+
protocGenGoPath = downloadProtocGenGo(*cachedir)
396+
)
397+
c := new(build.GoToolchain).Go("generate", "./...")
456398
pathList := []string{filepath.Join(protocPath, "bin"), protocGenGoPath, os.Getenv("PATH")}
457399
c.Env = append(c.Env, "PATH="+strings.Join(pathList, string(os.PathListSeparator)))
458400
build.MustRun(c)
459401

460-
if !*verify {
461-
return
462-
}
463-
// Check if files were changed.
464-
postHashes, err := hashAllSourceFiles()
402+
// Check if generate file hashes have changed
403+
generated, err := build.HashFolder(".", []string{"tests/testdata", "build/cache"})
465404
if err != nil {
466-
log.Fatal("error computing source tree file hashes", "err", err)
405+
log.Fatalf("Error re-computing hashes: %v", err)
467406
}
468-
updates := compareHashedFilesets(preHashes, postHashes)
469-
for _, updatedFile := range updates {
470-
fmt.Fprintf(os.Stderr, "changed file %s\n", updatedFile)
407+
updates := build.DiffHashes(hashes, generated)
408+
for _, file := range updates {
409+
log.Printf("File changed: %s", file)
471410
}
472411
if len(updates) != 0 {
473412
log.Fatal("One or more generated files were updated by running 'go generate ./...'")
474413
}
414+
fmt.Println("No stale files detected.")
415+
}
416+
417+
// doCheckBadDeps verifies whether certain unintended dependencies between some
418+
// packages leak into the codebase due to a refactor. This is not an exhaustive
419+
// list, rather something we build up over time at sensitive places.
420+
func doCheckBadDeps() {
421+
baddeps := [][2]string{
422+
// Rawdb tends to be a dumping ground for db utils, sometimes leaking the db itself
423+
{"github.com/ethereum/go-ethereum/core/rawdb", "github.com/ethereum/go-ethereum/ethdb/leveldb"},
424+
{"github.com/ethereum/go-ethereum/core/rawdb", "github.com/ethereum/go-ethereum/ethdb/pebbledb"},
425+
}
426+
tc := new(build.GoToolchain)
427+
428+
var failed bool
429+
for _, rule := range baddeps {
430+
out, err := tc.Go("list", "-deps", rule[0]).CombinedOutput()
431+
if err != nil {
432+
log.Fatalf("Failed to list '%s' dependencies: %v", rule[0], err)
433+
}
434+
for _, line := range strings.Split(string(out), "\n") {
435+
if strings.TrimSpace(line) == rule[1] {
436+
log.Printf("Found bad dependency '%s' -> '%s'", rule[0], rule[1])
437+
failed = true
438+
}
439+
}
440+
}
441+
if failed {
442+
log.Fatalf("Bad dependencies detected.")
443+
}
444+
fmt.Println("No bad dependencies detected.")
475445
}
476446

477447
// doLint runs golangci-lint on requested packages.
@@ -488,8 +458,6 @@ func doLint(cmdline []string) {
488458
linter := downloadLinter(*cachedir)
489459
lflags := []string{"run", "--config", ".golangci.yml"}
490460
build.MustRunCommandWithOutput(linter, append(lflags, packages...)...)
491-
492-
doGoModTidy()
493461
fmt.Println("You have achieved perfection.")
494462
}
495463

internal/build/file.go

+75-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,14 @@
1616

1717
package build
1818

19-
import "os"
19+
import (
20+
"crypto/sha256"
21+
"io"
22+
"os"
23+
"path/filepath"
24+
"sort"
25+
"strings"
26+
)
2027

2128
// FileExist checks if a file exists at path.
2229
func FileExist(path string) bool {
@@ -26,3 +33,70 @@ func FileExist(path string) bool {
2633
}
2734
return true
2835
}
36+
37+
// HashFiles iterates the provided set of files, computing the hash of each.
38+
func HashFiles(files []string) (map[string][32]byte, error) {
39+
res := make(map[string][32]byte)
40+
for _, filePath := range files {
41+
f, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
42+
if err != nil {
43+
return nil, err
44+
}
45+
hasher := sha256.New()
46+
if _, err := io.Copy(hasher, f); err != nil {
47+
return nil, err
48+
}
49+
res[filePath] = [32]byte(hasher.Sum(nil))
50+
}
51+
return res, nil
52+
}
53+
54+
// HashFolder iterates all files under the given directory, computing the hash
55+
// of each.
56+
func HashFolder(folder string, exlude []string) (map[string][32]byte, error) {
57+
res := make(map[string][32]byte)
58+
err := filepath.WalkDir(folder, func(path string, d os.DirEntry, _ error) error {
59+
// Skip anything that's exluded or not a regular file
60+
for _, skip := range exlude {
61+
if strings.HasPrefix(path, filepath.FromSlash(skip)) {
62+
return filepath.SkipDir
63+
}
64+
}
65+
if !d.Type().IsRegular() {
66+
return nil
67+
}
68+
// Regular file found, hash it
69+
f, err := os.OpenFile(path, os.O_RDONLY, 0666)
70+
if err != nil {
71+
return err
72+
}
73+
hasher := sha256.New()
74+
if _, err := io.Copy(hasher, f); err != nil {
75+
return err
76+
}
77+
res[path] = [32]byte(hasher.Sum(nil))
78+
return nil
79+
})
80+
if err != nil {
81+
return nil, err
82+
}
83+
return res, nil
84+
}
85+
86+
// DiffHashes compares two maps of file hashes and returns the changed files.
87+
func DiffHashes(a map[string][32]byte, b map[string][32]byte) []string {
88+
var updates []string
89+
90+
for file := range a {
91+
if _, ok := b[file]; !ok || a[file] != b[file] {
92+
updates = append(updates, file)
93+
}
94+
}
95+
for file := range b {
96+
if _, ok := a[file]; !ok {
97+
updates = append(updates, file)
98+
}
99+
}
100+
sort.Strings(updates)
101+
return updates
102+
}

0 commit comments

Comments
 (0)