go/internal/mage/mage.go (134 lines of code) (raw):

// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. package mage import ( "bytes" _ "embed" "fmt" "os" "strings" "time" "github.com/princjef/mageutil/bintool" "github.com/princjef/mageutil/shellcmd" ) //go:embed .golangci.yml var golangci string //go:embed package.gotxt var packageDoc string var ( golines = bintool.Must(bintool.NewGo( "github.com/segmentio/golines", "v0.12.2", )) linter = bintool.Must(bintool.New( "golangci-lint{{.BinExt}}", "1.64.5", "https://github.com/golangci/golangci-lint/releases/download/v{{.Version}}/golangci-lint-{{.Version}}-{{.GOOS}}-{{.GOARCH}}{{.ArchiveExt}}", )) documenter = bintool.Must(bintool.New( "gomarkdoc{{.BinExt}}", "1.1.0", "https://github.com/princjef/gomarkdoc/releases/download/v{{.Version}}/gomarkdoc_{{.Version}}_{{.GOOS}}_{{.GOARCH}}{{.ArchiveExt}}", )) ) // Format formats the code. func Format() error { if err := golines.Ensure(); err != nil { return err } return golines.Command("-m 80 --no-reformat-tags -w .").Run() } // Lint lints the code. func Lint() error { if err := linter.Ensure(); err != nil { return err } done, err := tmpFile(".golangci.yml", golangci) if err != nil { return err } defer done() return linter.Command(`run`).Run() } // Doc generates documents for the code. func Doc() error { if err := documenter.Ensure(); err != nil { return err } done, err := tmpFile("package.gotxt", packageDoc) if err != nil { return err } defer done() return documenter.Command( `--template-file package=package.gotxt --output '{{.Dir}}/API.md' ./...`, ).Run() } // Tester generates the test commands from various parameters. type Tester struct { Clean, Race bool CoverPkg []string Timeout time.Duration } func (t Tester) Run() error { test := `go test -coverprofile=coverage.out -covermode=atomic` if t.Race { test += ` -race` } if t.Timeout > 0 { test += ` -timeout=` + t.Timeout.String() } if len(t.CoverPkg) > 0 { test += ` -coverpkg="` + strings.Join(t.CoverPkg, `/...,`) + `/..."` } test += " ./..." var cmds []shellcmd.Command if t.Clean { cmds = append(cmds, `go clean -testcache`) } cmds = append(cmds, shellcmd.Command(test)) return shellcmd.RunAll(cmds...) } // Test runs the unit tests. func Test() error { return Tester{ Race: true, Timeout: 10 * time.Second, }.Run() } // TestClean runs the unit tests with no test cache. func TestClean() error { return Tester{ Clean: true, Race: true, Timeout: 10 * time.Second, }.Run() } // CI runs format, lint, doc, and test. func CI() error { if err := Format(); err != nil { return err } if err := Lint(); err != nil { return err } if err := Doc(); err != nil { return err } return Test() } // Verify that no thrashing occurred. func Verify() error { // Check git status for any modified files. modified, err := shellcmd.Command(`git ls-files -mz`).Output() if err != nil { return err } if len(modified) > 0 { files := bytes.Split(modified, []byte{0}) return fmt.Errorf( `found modified files - %s`, bytes.Join(files[:len(files)-1], []byte(", ")), ) } return nil } // CIVerify runs CI and verifies no thrashing occurred. func CIVerify() error { if err := CI(); err != nil { return err } return Verify() } func tmpFile(name, contents string) (func(), error) { if err := os.WriteFile(name, []byte(contents), 0o600); err != nil { return nil, err } return func() { os.Remove(name) }, nil }