1*1c12ee1eSDan Willemsen// Copyright 2019 The Go Authors. All rights reserved. 2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style 3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file. 4*1c12ee1eSDan Willemsen 5*1c12ee1eSDan Willemsen//go:build ignore 6*1c12ee1eSDan Willemsen// +build ignore 7*1c12ee1eSDan Willemsen 8*1c12ee1eSDan Willemsenpackage main 9*1c12ee1eSDan Willemsen 10*1c12ee1eSDan Willemsenimport ( 11*1c12ee1eSDan Willemsen "archive/tar" 12*1c12ee1eSDan Willemsen "archive/zip" 13*1c12ee1eSDan Willemsen "bytes" 14*1c12ee1eSDan Willemsen "compress/gzip" 15*1c12ee1eSDan Willemsen "crypto/sha256" 16*1c12ee1eSDan Willemsen "flag" 17*1c12ee1eSDan Willemsen "fmt" 18*1c12ee1eSDan Willemsen "io" 19*1c12ee1eSDan Willemsen "io/ioutil" 20*1c12ee1eSDan Willemsen "net/http" 21*1c12ee1eSDan Willemsen "os" 22*1c12ee1eSDan Willemsen "os/exec" 23*1c12ee1eSDan Willemsen "path/filepath" 24*1c12ee1eSDan Willemsen "regexp" 25*1c12ee1eSDan Willemsen "runtime" 26*1c12ee1eSDan Willemsen "strings" 27*1c12ee1eSDan Willemsen "sync" 28*1c12ee1eSDan Willemsen "testing" 29*1c12ee1eSDan Willemsen "time" 30*1c12ee1eSDan Willemsen 31*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/version" 32*1c12ee1eSDan Willemsen) 33*1c12ee1eSDan Willemsen 34*1c12ee1eSDan Willemsenvar ( 35*1c12ee1eSDan Willemsen regenerate = flag.Bool("regenerate", false, "regenerate files") 36*1c12ee1eSDan Willemsen buildRelease = flag.Bool("buildRelease", false, "build release binaries") 37*1c12ee1eSDan Willemsen 38*1c12ee1eSDan Willemsen protobufVersion = "22.0" 39*1c12ee1eSDan Willemsen protobufSHA256 = "" // ignored if protobufVersion is a git hash 40*1c12ee1eSDan Willemsen 41*1c12ee1eSDan Willemsen golangVersions = func() []string { 42*1c12ee1eSDan Willemsen var vers []string 43*1c12ee1eSDan Willemsen switch runtime.GOOS + "/" + runtime.GOARCH { 44*1c12ee1eSDan Willemsen case "darwin/arm64": 45*1c12ee1eSDan Willemsen default: 46*1c12ee1eSDan Willemsen vers = []string{"1.13.15", "1.14.15", "1.15.15"} 47*1c12ee1eSDan Willemsen } 48*1c12ee1eSDan Willemsen return append(vers, "1.16.15", "1.17.13", "1.18.10", "1.19.6") 49*1c12ee1eSDan Willemsen }() 50*1c12ee1eSDan Willemsen golangLatest = golangVersions[len(golangVersions)-1] 51*1c12ee1eSDan Willemsen 52*1c12ee1eSDan Willemsen staticcheckVersion = "2022.1.2" 53*1c12ee1eSDan Willemsen staticcheckSHA256s = map[string]string{ 54*1c12ee1eSDan Willemsen "darwin/amd64": "baa35f8fb967ee2aacad57f026e3724fbf8d9b7ad8f682f4d44b2084a96e103b", 55*1c12ee1eSDan Willemsen "darwin/arm64": "9f01a581eeea088d0a6272538360f6d84996d66ae554bfada8026fe24991daa0", 56*1c12ee1eSDan Willemsen "linux/386": "4cf74373e5d668b265d7a241b59ba7d26064f2cd6af50b77e62c2b3e2f3afb43", 57*1c12ee1eSDan Willemsen "linux/amd64": "6dbb7187e43812fa23363cdaaa90ab13544dd36e24d02e2347014e4cf265f06d", 58*1c12ee1eSDan Willemsen } 59*1c12ee1eSDan Willemsen 60*1c12ee1eSDan Willemsen // purgeTimeout determines the maximum age of unused sub-directories. 61*1c12ee1eSDan Willemsen purgeTimeout = 30 * 24 * time.Hour // 1 month 62*1c12ee1eSDan Willemsen 63*1c12ee1eSDan Willemsen // Variables initialized by mustInitDeps. 64*1c12ee1eSDan Willemsen goPath string 65*1c12ee1eSDan Willemsen modulePath string 66*1c12ee1eSDan Willemsen protobufPath string 67*1c12ee1eSDan Willemsen) 68*1c12ee1eSDan Willemsen 69*1c12ee1eSDan Willemsenfunc Test(t *testing.T) { 70*1c12ee1eSDan Willemsen mustInitDeps(t) 71*1c12ee1eSDan Willemsen mustHandleFlags(t) 72*1c12ee1eSDan Willemsen 73*1c12ee1eSDan Willemsen // Report dirt in the working tree quickly, rather than after 74*1c12ee1eSDan Willemsen // going through all the presubmits. 75*1c12ee1eSDan Willemsen // 76*1c12ee1eSDan Willemsen // Fail the test late, so we can test uncommitted changes with -failfast. 77*1c12ee1eSDan Willemsen gitDiff := mustRunCommand(t, "git", "diff", "HEAD") 78*1c12ee1eSDan Willemsen if strings.TrimSpace(gitDiff) != "" { 79*1c12ee1eSDan Willemsen fmt.Printf("WARNING: working tree contains uncommitted changes:\n%v\n", gitDiff) 80*1c12ee1eSDan Willemsen } 81*1c12ee1eSDan Willemsen gitUntracked := mustRunCommand(t, "git", "ls-files", "--others", "--exclude-standard") 82*1c12ee1eSDan Willemsen if strings.TrimSpace(gitUntracked) != "" { 83*1c12ee1eSDan Willemsen fmt.Printf("WARNING: working tree contains untracked files:\n%v\n", gitUntracked) 84*1c12ee1eSDan Willemsen } 85*1c12ee1eSDan Willemsen 86*1c12ee1eSDan Willemsen // Do the relatively fast checks up-front. 87*1c12ee1eSDan Willemsen t.Run("GeneratedGoFiles", func(t *testing.T) { 88*1c12ee1eSDan Willemsen diff := mustRunCommand(t, "go", "run", "-tags", "protolegacy", "./internal/cmd/generate-types") 89*1c12ee1eSDan Willemsen if strings.TrimSpace(diff) != "" { 90*1c12ee1eSDan Willemsen t.Fatalf("stale generated files:\n%v", diff) 91*1c12ee1eSDan Willemsen } 92*1c12ee1eSDan Willemsen diff = mustRunCommand(t, "go", "run", "-tags", "protolegacy", "./internal/cmd/generate-protos") 93*1c12ee1eSDan Willemsen if strings.TrimSpace(diff) != "" { 94*1c12ee1eSDan Willemsen t.Fatalf("stale generated files:\n%v", diff) 95*1c12ee1eSDan Willemsen } 96*1c12ee1eSDan Willemsen }) 97*1c12ee1eSDan Willemsen t.Run("FormattedGoFiles", func(t *testing.T) { 98*1c12ee1eSDan Willemsen files := strings.Split(strings.TrimSpace(mustRunCommand(t, "git", "ls-files", "*.go")), "\n") 99*1c12ee1eSDan Willemsen diff := mustRunCommand(t, append([]string{"gofmt", "-d"}, files...)...) 100*1c12ee1eSDan Willemsen if strings.TrimSpace(diff) != "" { 101*1c12ee1eSDan Willemsen t.Fatalf("unformatted source files:\n%v", diff) 102*1c12ee1eSDan Willemsen } 103*1c12ee1eSDan Willemsen }) 104*1c12ee1eSDan Willemsen t.Run("CopyrightHeaders", func(t *testing.T) { 105*1c12ee1eSDan Willemsen files := strings.Split(strings.TrimSpace(mustRunCommand(t, "git", "ls-files", "*.go", "*.proto")), "\n") 106*1c12ee1eSDan Willemsen mustHaveCopyrightHeader(t, files) 107*1c12ee1eSDan Willemsen }) 108*1c12ee1eSDan Willemsen 109*1c12ee1eSDan Willemsen var wg sync.WaitGroup 110*1c12ee1eSDan Willemsen sema := make(chan bool, (runtime.NumCPU()+1)/2) 111*1c12ee1eSDan Willemsen for i := range golangVersions { 112*1c12ee1eSDan Willemsen goVersion := golangVersions[i] 113*1c12ee1eSDan Willemsen goLabel := "Go" + goVersion 114*1c12ee1eSDan Willemsen runGo := func(label string, cmd command, args ...string) { 115*1c12ee1eSDan Willemsen wg.Add(1) 116*1c12ee1eSDan Willemsen sema <- true 117*1c12ee1eSDan Willemsen go func() { 118*1c12ee1eSDan Willemsen defer wg.Done() 119*1c12ee1eSDan Willemsen defer func() { <-sema }() 120*1c12ee1eSDan Willemsen t.Run(goLabel+"/"+label, func(t *testing.T) { 121*1c12ee1eSDan Willemsen args[0] += goVersion 122*1c12ee1eSDan Willemsen cmd.mustRun(t, args...) 123*1c12ee1eSDan Willemsen }) 124*1c12ee1eSDan Willemsen }() 125*1c12ee1eSDan Willemsen } 126*1c12ee1eSDan Willemsen 127*1c12ee1eSDan Willemsen workDir := filepath.Join(goPath, "src", modulePath) 128*1c12ee1eSDan Willemsen runGo("Normal", command{Dir: workDir}, "go", "test", "-race", "./...") 129*1c12ee1eSDan Willemsen runGo("PureGo", command{Dir: workDir}, "go", "test", "-race", "-tags", "purego", "./...") 130*1c12ee1eSDan Willemsen runGo("Reflect", command{Dir: workDir}, "go", "test", "-race", "-tags", "protoreflect", "./...") 131*1c12ee1eSDan Willemsen if goVersion == golangLatest { 132*1c12ee1eSDan Willemsen runGo("ProtoLegacy", command{Dir: workDir}, "go", "test", "-race", "-tags", "protolegacy", "./...") 133*1c12ee1eSDan Willemsen runGo("ProtocGenGo", command{Dir: "cmd/protoc-gen-go/testdata"}, "go", "test") 134*1c12ee1eSDan Willemsen runGo("Conformance", command{Dir: "internal/conformance"}, "go", "test", "-execute") 135*1c12ee1eSDan Willemsen 136*1c12ee1eSDan Willemsen // Only run the 32-bit compatibility tests for Linux; 137*1c12ee1eSDan Willemsen // avoid Darwin since 10.15 dropped support i386 code execution. 138*1c12ee1eSDan Willemsen if runtime.GOOS == "linux" { 139*1c12ee1eSDan Willemsen runGo("Arch32Bit", command{Dir: workDir, Env: append(os.Environ(), "GOARCH=386")}, "go", "test", "./...") 140*1c12ee1eSDan Willemsen } 141*1c12ee1eSDan Willemsen } 142*1c12ee1eSDan Willemsen } 143*1c12ee1eSDan Willemsen wg.Wait() 144*1c12ee1eSDan Willemsen 145*1c12ee1eSDan Willemsen t.Run("GoStaticCheck", func(t *testing.T) { 146*1c12ee1eSDan Willemsen checks := []string{ 147*1c12ee1eSDan Willemsen "all", // start with all checks enabled 148*1c12ee1eSDan Willemsen "-SA1019", // disable deprecated usage check 149*1c12ee1eSDan Willemsen "-S*", // disable code simplification checks 150*1c12ee1eSDan Willemsen "-ST*", // disable coding style checks 151*1c12ee1eSDan Willemsen "-U*", // disable unused declaration checks 152*1c12ee1eSDan Willemsen } 153*1c12ee1eSDan Willemsen out := mustRunCommand(t, "staticcheck", "-checks="+strings.Join(checks, ","), "-fail=none", "./...") 154*1c12ee1eSDan Willemsen 155*1c12ee1eSDan Willemsen // Filter out findings from certain paths. 156*1c12ee1eSDan Willemsen var findings []string 157*1c12ee1eSDan Willemsen for _, finding := range strings.Split(strings.TrimSpace(out), "\n") { 158*1c12ee1eSDan Willemsen switch { 159*1c12ee1eSDan Willemsen case strings.HasPrefix(finding, "internal/testprotos/legacy/"): 160*1c12ee1eSDan Willemsen default: 161*1c12ee1eSDan Willemsen findings = append(findings, finding) 162*1c12ee1eSDan Willemsen } 163*1c12ee1eSDan Willemsen } 164*1c12ee1eSDan Willemsen if len(findings) > 0 { 165*1c12ee1eSDan Willemsen t.Fatalf("staticcheck findings:\n%v", strings.Join(findings, "\n")) 166*1c12ee1eSDan Willemsen } 167*1c12ee1eSDan Willemsen }) 168*1c12ee1eSDan Willemsen t.Run("CommittedGitChanges", func(t *testing.T) { 169*1c12ee1eSDan Willemsen if strings.TrimSpace(gitDiff) != "" { 170*1c12ee1eSDan Willemsen t.Fatalf("uncommitted changes") 171*1c12ee1eSDan Willemsen } 172*1c12ee1eSDan Willemsen }) 173*1c12ee1eSDan Willemsen t.Run("TrackedGitFiles", func(t *testing.T) { 174*1c12ee1eSDan Willemsen if strings.TrimSpace(gitUntracked) != "" { 175*1c12ee1eSDan Willemsen t.Fatalf("untracked files") 176*1c12ee1eSDan Willemsen } 177*1c12ee1eSDan Willemsen }) 178*1c12ee1eSDan Willemsen} 179*1c12ee1eSDan Willemsen 180*1c12ee1eSDan Willemsenfunc mustInitDeps(t *testing.T) { 181*1c12ee1eSDan Willemsen check := func(err error) { 182*1c12ee1eSDan Willemsen t.Helper() 183*1c12ee1eSDan Willemsen if err != nil { 184*1c12ee1eSDan Willemsen t.Fatal(err) 185*1c12ee1eSDan Willemsen } 186*1c12ee1eSDan Willemsen } 187*1c12ee1eSDan Willemsen 188*1c12ee1eSDan Willemsen // Determine the directory to place the test directory. 189*1c12ee1eSDan Willemsen repoRoot, err := os.Getwd() 190*1c12ee1eSDan Willemsen check(err) 191*1c12ee1eSDan Willemsen testDir := filepath.Join(repoRoot, ".cache") 192*1c12ee1eSDan Willemsen check(os.MkdirAll(testDir, 0775)) 193*1c12ee1eSDan Willemsen 194*1c12ee1eSDan Willemsen // Delete the current directory if non-empty, 195*1c12ee1eSDan Willemsen // which only occurs if a dependency failed to initialize properly. 196*1c12ee1eSDan Willemsen var workingDir string 197*1c12ee1eSDan Willemsen finishedDirs := map[string]bool{} 198*1c12ee1eSDan Willemsen defer func() { 199*1c12ee1eSDan Willemsen if workingDir != "" { 200*1c12ee1eSDan Willemsen os.RemoveAll(workingDir) // best-effort 201*1c12ee1eSDan Willemsen } 202*1c12ee1eSDan Willemsen }() 203*1c12ee1eSDan Willemsen startWork := func(name string) string { 204*1c12ee1eSDan Willemsen workingDir = filepath.Join(testDir, name) 205*1c12ee1eSDan Willemsen return workingDir 206*1c12ee1eSDan Willemsen } 207*1c12ee1eSDan Willemsen finishWork := func() { 208*1c12ee1eSDan Willemsen finishedDirs[workingDir] = true 209*1c12ee1eSDan Willemsen workingDir = "" 210*1c12ee1eSDan Willemsen } 211*1c12ee1eSDan Willemsen 212*1c12ee1eSDan Willemsen // Delete other sub-directories that are no longer relevant. 213*1c12ee1eSDan Willemsen defer func() { 214*1c12ee1eSDan Willemsen now := time.Now() 215*1c12ee1eSDan Willemsen fis, _ := ioutil.ReadDir(testDir) 216*1c12ee1eSDan Willemsen for _, fi := range fis { 217*1c12ee1eSDan Willemsen dir := filepath.Join(testDir, fi.Name()) 218*1c12ee1eSDan Willemsen if finishedDirs[dir] { 219*1c12ee1eSDan Willemsen os.Chtimes(dir, now, now) // best-effort 220*1c12ee1eSDan Willemsen continue 221*1c12ee1eSDan Willemsen } 222*1c12ee1eSDan Willemsen if now.Sub(fi.ModTime()) < purgeTimeout { 223*1c12ee1eSDan Willemsen continue 224*1c12ee1eSDan Willemsen } 225*1c12ee1eSDan Willemsen fmt.Printf("delete %v\n", fi.Name()) 226*1c12ee1eSDan Willemsen os.RemoveAll(dir) // best-effort 227*1c12ee1eSDan Willemsen } 228*1c12ee1eSDan Willemsen }() 229*1c12ee1eSDan Willemsen 230*1c12ee1eSDan Willemsen // The bin directory contains symlinks to each tool by version. 231*1c12ee1eSDan Willemsen // It is safe to delete this directory and run the test script from scratch. 232*1c12ee1eSDan Willemsen binPath := startWork("bin") 233*1c12ee1eSDan Willemsen check(os.RemoveAll(binPath)) 234*1c12ee1eSDan Willemsen check(os.Mkdir(binPath, 0775)) 235*1c12ee1eSDan Willemsen check(os.Setenv("PATH", binPath+":"+os.Getenv("PATH"))) 236*1c12ee1eSDan Willemsen registerBinary := func(name, path string) { 237*1c12ee1eSDan Willemsen check(os.Symlink(path, filepath.Join(binPath, name))) 238*1c12ee1eSDan Willemsen } 239*1c12ee1eSDan Willemsen finishWork() 240*1c12ee1eSDan Willemsen 241*1c12ee1eSDan Willemsen // Download and build the protobuf toolchain. 242*1c12ee1eSDan Willemsen // We avoid downloading the pre-compiled binaries since they do not contain 243*1c12ee1eSDan Willemsen // the conformance test runner. 244*1c12ee1eSDan Willemsen protobufPath = startWork("protobuf-" + protobufVersion) 245*1c12ee1eSDan Willemsen if _, err := os.Stat(protobufPath); err != nil { 246*1c12ee1eSDan Willemsen fmt.Printf("download %v\n", filepath.Base(protobufPath)) 247*1c12ee1eSDan Willemsen checkoutVersion := protobufVersion 248*1c12ee1eSDan Willemsen if isCommit := strings.Trim(protobufVersion, "0123456789abcdef") == ""; !isCommit { 249*1c12ee1eSDan Willemsen // release tags have "v" prefix 250*1c12ee1eSDan Willemsen checkoutVersion = "v" + protobufVersion 251*1c12ee1eSDan Willemsen } 252*1c12ee1eSDan Willemsen command{Dir: testDir}.mustRun(t, "git", "clone", "https://github.com/protocolbuffers/protobuf", "protobuf-"+protobufVersion) 253*1c12ee1eSDan Willemsen command{Dir: protobufPath}.mustRun(t, "git", "checkout", checkoutVersion) 254*1c12ee1eSDan Willemsen 255*1c12ee1eSDan Willemsen fmt.Printf("build %v\n", filepath.Base(protobufPath)) 256*1c12ee1eSDan Willemsen command{Dir: protobufPath}.mustRun(t, "bazel", "build", ":protoc", "//conformance:conformance_test_runner") 257*1c12ee1eSDan Willemsen } 258*1c12ee1eSDan Willemsen check(os.Setenv("PROTOBUF_ROOT", protobufPath)) // for generate-protos 259*1c12ee1eSDan Willemsen registerBinary("conform-test-runner", filepath.Join(protobufPath, "bazel-bin", "conformance", "conformance_test_runner")) 260*1c12ee1eSDan Willemsen registerBinary("protoc", filepath.Join(protobufPath, "bazel-bin", "protoc")) 261*1c12ee1eSDan Willemsen finishWork() 262*1c12ee1eSDan Willemsen 263*1c12ee1eSDan Willemsen // Download each Go toolchain version. 264*1c12ee1eSDan Willemsen for _, v := range golangVersions { 265*1c12ee1eSDan Willemsen goDir := startWork("go" + v) 266*1c12ee1eSDan Willemsen if _, err := os.Stat(goDir); err != nil { 267*1c12ee1eSDan Willemsen fmt.Printf("download %v\n", filepath.Base(goDir)) 268*1c12ee1eSDan Willemsen url := fmt.Sprintf("https://dl.google.com/go/go%v.%v-%v.tar.gz", v, runtime.GOOS, runtime.GOARCH) 269*1c12ee1eSDan Willemsen downloadArchive(check, goDir, url, "go", "") // skip SHA256 check as we fetch over https from a trusted domain 270*1c12ee1eSDan Willemsen } 271*1c12ee1eSDan Willemsen registerBinary("go"+v, filepath.Join(goDir, "bin", "go")) 272*1c12ee1eSDan Willemsen finishWork() 273*1c12ee1eSDan Willemsen } 274*1c12ee1eSDan Willemsen registerBinary("go", filepath.Join(testDir, "go"+golangLatest, "bin", "go")) 275*1c12ee1eSDan Willemsen registerBinary("gofmt", filepath.Join(testDir, "go"+golangLatest, "bin", "gofmt")) 276*1c12ee1eSDan Willemsen 277*1c12ee1eSDan Willemsen // Download the staticcheck tool. 278*1c12ee1eSDan Willemsen checkDir := startWork("staticcheck-" + staticcheckVersion) 279*1c12ee1eSDan Willemsen if _, err := os.Stat(checkDir); err != nil { 280*1c12ee1eSDan Willemsen fmt.Printf("download %v\n", filepath.Base(checkDir)) 281*1c12ee1eSDan Willemsen url := fmt.Sprintf("https://github.com/dominikh/go-tools/releases/download/%v/staticcheck_%v_%v.tar.gz", staticcheckVersion, runtime.GOOS, runtime.GOARCH) 282*1c12ee1eSDan Willemsen downloadArchive(check, checkDir, url, "staticcheck", staticcheckSHA256s[runtime.GOOS+"/"+runtime.GOARCH]) 283*1c12ee1eSDan Willemsen } 284*1c12ee1eSDan Willemsen registerBinary("staticcheck", filepath.Join(checkDir, "staticcheck")) 285*1c12ee1eSDan Willemsen finishWork() 286*1c12ee1eSDan Willemsen 287*1c12ee1eSDan Willemsen // GitHub actions sets GOROOT, which confuses invocations of the Go toolchain. 288*1c12ee1eSDan Willemsen // Explicitly clear GOROOT, so each toolchain uses their default GOROOT. 289*1c12ee1eSDan Willemsen check(os.Unsetenv("GOROOT")) 290*1c12ee1eSDan Willemsen 291*1c12ee1eSDan Willemsen // Set a cache directory outside the test directory. 292*1c12ee1eSDan Willemsen check(os.Setenv("GOCACHE", filepath.Join(repoRoot, ".gocache"))) 293*1c12ee1eSDan Willemsen 294*1c12ee1eSDan Willemsen // Setup GOPATH for pre-module support (i.e., go1.10 and earlier). 295*1c12ee1eSDan Willemsen goPath = startWork("gopath") 296*1c12ee1eSDan Willemsen modulePath = strings.TrimSpace(command{Dir: testDir}.mustRun(t, "go", "list", "-m", "-f", "{{.Path}}")) 297*1c12ee1eSDan Willemsen check(os.RemoveAll(filepath.Join(goPath, "src"))) 298*1c12ee1eSDan Willemsen check(os.MkdirAll(filepath.Join(goPath, "src", filepath.Dir(modulePath)), 0775)) 299*1c12ee1eSDan Willemsen check(os.Symlink(repoRoot, filepath.Join(goPath, "src", modulePath))) 300*1c12ee1eSDan Willemsen command{Dir: repoRoot}.mustRun(t, "go", "mod", "tidy") 301*1c12ee1eSDan Willemsen command{Dir: repoRoot}.mustRun(t, "go", "mod", "vendor") 302*1c12ee1eSDan Willemsen check(os.Setenv("GOPATH", goPath)) 303*1c12ee1eSDan Willemsen finishWork() 304*1c12ee1eSDan Willemsen} 305*1c12ee1eSDan Willemsen 306*1c12ee1eSDan Willemsenfunc downloadFile(check func(error), dstPath, srcURL string) { 307*1c12ee1eSDan Willemsen resp, err := http.Get(srcURL) 308*1c12ee1eSDan Willemsen check(err) 309*1c12ee1eSDan Willemsen defer resp.Body.Close() 310*1c12ee1eSDan Willemsen 311*1c12ee1eSDan Willemsen check(os.MkdirAll(filepath.Dir(dstPath), 0775)) 312*1c12ee1eSDan Willemsen f, err := os.Create(dstPath) 313*1c12ee1eSDan Willemsen check(err) 314*1c12ee1eSDan Willemsen 315*1c12ee1eSDan Willemsen _, err = io.Copy(f, resp.Body) 316*1c12ee1eSDan Willemsen check(err) 317*1c12ee1eSDan Willemsen} 318*1c12ee1eSDan Willemsen 319*1c12ee1eSDan Willemsenfunc downloadArchive(check func(error), dstPath, srcURL, skipPrefix, wantSHA256 string) { 320*1c12ee1eSDan Willemsen check(os.RemoveAll(dstPath)) 321*1c12ee1eSDan Willemsen 322*1c12ee1eSDan Willemsen resp, err := http.Get(srcURL) 323*1c12ee1eSDan Willemsen check(err) 324*1c12ee1eSDan Willemsen defer resp.Body.Close() 325*1c12ee1eSDan Willemsen 326*1c12ee1eSDan Willemsen var r io.Reader = resp.Body 327*1c12ee1eSDan Willemsen if wantSHA256 != "" { 328*1c12ee1eSDan Willemsen b, err := ioutil.ReadAll(resp.Body) 329*1c12ee1eSDan Willemsen check(err) 330*1c12ee1eSDan Willemsen r = bytes.NewReader(b) 331*1c12ee1eSDan Willemsen 332*1c12ee1eSDan Willemsen if gotSHA256 := fmt.Sprintf("%x", sha256.Sum256(b)); gotSHA256 != wantSHA256 { 333*1c12ee1eSDan Willemsen check(fmt.Errorf("checksum validation error:\ngot %v\nwant %v", gotSHA256, wantSHA256)) 334*1c12ee1eSDan Willemsen } 335*1c12ee1eSDan Willemsen } 336*1c12ee1eSDan Willemsen 337*1c12ee1eSDan Willemsen zr, err := gzip.NewReader(r) 338*1c12ee1eSDan Willemsen check(err) 339*1c12ee1eSDan Willemsen 340*1c12ee1eSDan Willemsen tr := tar.NewReader(zr) 341*1c12ee1eSDan Willemsen for { 342*1c12ee1eSDan Willemsen h, err := tr.Next() 343*1c12ee1eSDan Willemsen if err == io.EOF { 344*1c12ee1eSDan Willemsen return 345*1c12ee1eSDan Willemsen } 346*1c12ee1eSDan Willemsen check(err) 347*1c12ee1eSDan Willemsen 348*1c12ee1eSDan Willemsen // Skip directories or files outside the prefix directory. 349*1c12ee1eSDan Willemsen if len(skipPrefix) > 0 { 350*1c12ee1eSDan Willemsen if !strings.HasPrefix(h.Name, skipPrefix) { 351*1c12ee1eSDan Willemsen continue 352*1c12ee1eSDan Willemsen } 353*1c12ee1eSDan Willemsen if len(h.Name) > len(skipPrefix) && h.Name[len(skipPrefix)] != '/' { 354*1c12ee1eSDan Willemsen continue 355*1c12ee1eSDan Willemsen } 356*1c12ee1eSDan Willemsen } 357*1c12ee1eSDan Willemsen 358*1c12ee1eSDan Willemsen path := strings.TrimPrefix(strings.TrimPrefix(h.Name, skipPrefix), "/") 359*1c12ee1eSDan Willemsen path = filepath.Join(dstPath, filepath.FromSlash(path)) 360*1c12ee1eSDan Willemsen mode := os.FileMode(h.Mode & 0777) 361*1c12ee1eSDan Willemsen switch h.Typeflag { 362*1c12ee1eSDan Willemsen case tar.TypeReg: 363*1c12ee1eSDan Willemsen b, err := ioutil.ReadAll(tr) 364*1c12ee1eSDan Willemsen check(err) 365*1c12ee1eSDan Willemsen check(ioutil.WriteFile(path, b, mode)) 366*1c12ee1eSDan Willemsen case tar.TypeDir: 367*1c12ee1eSDan Willemsen check(os.Mkdir(path, mode)) 368*1c12ee1eSDan Willemsen } 369*1c12ee1eSDan Willemsen } 370*1c12ee1eSDan Willemsen} 371*1c12ee1eSDan Willemsen 372*1c12ee1eSDan Willemsenfunc mustHandleFlags(t *testing.T) { 373*1c12ee1eSDan Willemsen if *regenerate { 374*1c12ee1eSDan Willemsen t.Run("Generate", func(t *testing.T) { 375*1c12ee1eSDan Willemsen fmt.Print(mustRunCommand(t, "go", "run", "-tags", "protolegacy", "./internal/cmd/generate-types", "-execute")) 376*1c12ee1eSDan Willemsen fmt.Print(mustRunCommand(t, "go", "run", "-tags", "protolegacy", "./internal/cmd/generate-protos", "-execute")) 377*1c12ee1eSDan Willemsen files := strings.Split(strings.TrimSpace(mustRunCommand(t, "git", "ls-files", "*.go")), "\n") 378*1c12ee1eSDan Willemsen mustRunCommand(t, append([]string{"gofmt", "-w"}, files...)...) 379*1c12ee1eSDan Willemsen }) 380*1c12ee1eSDan Willemsen } 381*1c12ee1eSDan Willemsen if *buildRelease { 382*1c12ee1eSDan Willemsen t.Run("BuildRelease", func(t *testing.T) { 383*1c12ee1eSDan Willemsen v := version.String() 384*1c12ee1eSDan Willemsen for _, goos := range []string{"linux", "darwin", "windows"} { 385*1c12ee1eSDan Willemsen for _, goarch := range []string{"386", "amd64", "arm64"} { 386*1c12ee1eSDan Willemsen // Avoid Darwin since 10.15 dropped support for i386. 387*1c12ee1eSDan Willemsen if goos == "darwin" && goarch == "386" { 388*1c12ee1eSDan Willemsen continue 389*1c12ee1eSDan Willemsen } 390*1c12ee1eSDan Willemsen 391*1c12ee1eSDan Willemsen binPath := filepath.Join("bin", fmt.Sprintf("protoc-gen-go.%v.%v.%v", v, goos, goarch)) 392*1c12ee1eSDan Willemsen 393*1c12ee1eSDan Willemsen // Build the binary. 394*1c12ee1eSDan Willemsen cmd := command{Env: append(os.Environ(), "GOOS="+goos, "GOARCH="+goarch)} 395*1c12ee1eSDan Willemsen cmd.mustRun(t, "go", "build", "-trimpath", "-ldflags", "-s -w -buildid=", "-o", binPath, "./cmd/protoc-gen-go") 396*1c12ee1eSDan Willemsen 397*1c12ee1eSDan Willemsen // Archive and compress the binary. 398*1c12ee1eSDan Willemsen in, err := ioutil.ReadFile(binPath) 399*1c12ee1eSDan Willemsen if err != nil { 400*1c12ee1eSDan Willemsen t.Fatal(err) 401*1c12ee1eSDan Willemsen } 402*1c12ee1eSDan Willemsen out := new(bytes.Buffer) 403*1c12ee1eSDan Willemsen suffix := "" 404*1c12ee1eSDan Willemsen comment := fmt.Sprintf("protoc-gen-go VERSION=%v GOOS=%v GOARCH=%v", v, goos, goarch) 405*1c12ee1eSDan Willemsen switch goos { 406*1c12ee1eSDan Willemsen case "windows": 407*1c12ee1eSDan Willemsen suffix = ".zip" 408*1c12ee1eSDan Willemsen zw := zip.NewWriter(out) 409*1c12ee1eSDan Willemsen zw.SetComment(comment) 410*1c12ee1eSDan Willemsen fw, _ := zw.Create("protoc-gen-go.exe") 411*1c12ee1eSDan Willemsen fw.Write(in) 412*1c12ee1eSDan Willemsen zw.Close() 413*1c12ee1eSDan Willemsen default: 414*1c12ee1eSDan Willemsen suffix = ".tar.gz" 415*1c12ee1eSDan Willemsen gz, _ := gzip.NewWriterLevel(out, gzip.BestCompression) 416*1c12ee1eSDan Willemsen gz.Comment = comment 417*1c12ee1eSDan Willemsen tw := tar.NewWriter(gz) 418*1c12ee1eSDan Willemsen tw.WriteHeader(&tar.Header{ 419*1c12ee1eSDan Willemsen Name: "protoc-gen-go", 420*1c12ee1eSDan Willemsen Mode: int64(0775), 421*1c12ee1eSDan Willemsen Size: int64(len(in)), 422*1c12ee1eSDan Willemsen }) 423*1c12ee1eSDan Willemsen tw.Write(in) 424*1c12ee1eSDan Willemsen tw.Close() 425*1c12ee1eSDan Willemsen gz.Close() 426*1c12ee1eSDan Willemsen } 427*1c12ee1eSDan Willemsen if err := ioutil.WriteFile(binPath+suffix, out.Bytes(), 0664); err != nil { 428*1c12ee1eSDan Willemsen t.Fatal(err) 429*1c12ee1eSDan Willemsen } 430*1c12ee1eSDan Willemsen } 431*1c12ee1eSDan Willemsen } 432*1c12ee1eSDan Willemsen }) 433*1c12ee1eSDan Willemsen } 434*1c12ee1eSDan Willemsen if *regenerate || *buildRelease { 435*1c12ee1eSDan Willemsen t.SkipNow() 436*1c12ee1eSDan Willemsen } 437*1c12ee1eSDan Willemsen} 438*1c12ee1eSDan Willemsen 439*1c12ee1eSDan Willemsenvar copyrightRegex = []*regexp.Regexp{ 440*1c12ee1eSDan Willemsen regexp.MustCompile(`^// Copyright \d\d\d\d The Go Authors\. All rights reserved. 441*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style 442*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file\. 443*1c12ee1eSDan Willemsen`), 444*1c12ee1eSDan Willemsen // Generated .pb.go files from main protobuf repo. 445*1c12ee1eSDan Willemsen regexp.MustCompile(`^// Protocol Buffers - Google's data interchange format 446*1c12ee1eSDan Willemsen// Copyright \d\d\d\d Google Inc\. All rights reserved\. 447*1c12ee1eSDan Willemsen`), 448*1c12ee1eSDan Willemsen} 449*1c12ee1eSDan Willemsen 450*1c12ee1eSDan Willemsenfunc mustHaveCopyrightHeader(t *testing.T, files []string) { 451*1c12ee1eSDan Willemsen var bad []string 452*1c12ee1eSDan WillemsenFile: 453*1c12ee1eSDan Willemsen for _, file := range files { 454*1c12ee1eSDan Willemsen b, err := ioutil.ReadFile(file) 455*1c12ee1eSDan Willemsen if err != nil { 456*1c12ee1eSDan Willemsen t.Fatal(err) 457*1c12ee1eSDan Willemsen } 458*1c12ee1eSDan Willemsen for _, re := range copyrightRegex { 459*1c12ee1eSDan Willemsen if loc := re.FindIndex(b); loc != nil && loc[0] == 0 { 460*1c12ee1eSDan Willemsen continue File 461*1c12ee1eSDan Willemsen } 462*1c12ee1eSDan Willemsen } 463*1c12ee1eSDan Willemsen bad = append(bad, file) 464*1c12ee1eSDan Willemsen } 465*1c12ee1eSDan Willemsen if len(bad) > 0 { 466*1c12ee1eSDan Willemsen t.Fatalf("files with missing/bad copyright headers:\n %v", strings.Join(bad, "\n ")) 467*1c12ee1eSDan Willemsen } 468*1c12ee1eSDan Willemsen} 469*1c12ee1eSDan Willemsen 470*1c12ee1eSDan Willemsentype command struct { 471*1c12ee1eSDan Willemsen Dir string 472*1c12ee1eSDan Willemsen Env []string 473*1c12ee1eSDan Willemsen} 474*1c12ee1eSDan Willemsen 475*1c12ee1eSDan Willemsenfunc (c command) mustRun(t *testing.T, args ...string) string { 476*1c12ee1eSDan Willemsen t.Helper() 477*1c12ee1eSDan Willemsen stdout := new(bytes.Buffer) 478*1c12ee1eSDan Willemsen stderr := new(bytes.Buffer) 479*1c12ee1eSDan Willemsen cmd := exec.Command(args[0], args[1:]...) 480*1c12ee1eSDan Willemsen cmd.Dir = "." 481*1c12ee1eSDan Willemsen if c.Dir != "" { 482*1c12ee1eSDan Willemsen cmd.Dir = c.Dir 483*1c12ee1eSDan Willemsen } 484*1c12ee1eSDan Willemsen cmd.Env = os.Environ() 485*1c12ee1eSDan Willemsen if c.Env != nil { 486*1c12ee1eSDan Willemsen cmd.Env = c.Env 487*1c12ee1eSDan Willemsen } 488*1c12ee1eSDan Willemsen cmd.Env = append(cmd.Env, "PWD="+cmd.Dir) 489*1c12ee1eSDan Willemsen cmd.Stdout = stdout 490*1c12ee1eSDan Willemsen cmd.Stderr = stderr 491*1c12ee1eSDan Willemsen if err := cmd.Run(); err != nil { 492*1c12ee1eSDan Willemsen t.Fatalf("executing (%v): %v\n%s%s", strings.Join(args, " "), err, stdout.String(), stderr.String()) 493*1c12ee1eSDan Willemsen } 494*1c12ee1eSDan Willemsen return stdout.String() 495*1c12ee1eSDan Willemsen} 496*1c12ee1eSDan Willemsen 497*1c12ee1eSDan Willemsenfunc mustRunCommand(t *testing.T, args ...string) string { 498*1c12ee1eSDan Willemsen t.Helper() 499*1c12ee1eSDan Willemsen return command{}.mustRun(t, args...) 500*1c12ee1eSDan Willemsen} 501