diff --git a/scripts/modowners/go.mod b/scripts/modowners/go.mod index 4c8b57c59f4..0d03fc58cc7 100644 --- a/scripts/modowners/go.mod +++ b/scripts/modowners/go.mod @@ -1,12 +1,16 @@ -module modowners +module github.com/grafana/grafana/scripts/modowners go 1.19 -require golang.org/x/mod v0.10.0 +require ( + golang.org/x/mod v0.10.0 + golang.org/x/tools v0.1.12 +) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.8.3 // indirect + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/scripts/modowners/go.sum b/scripts/modowners/go.sum index 4f12f8fe9f3..b3c29c3c58a 100644 --- a/scripts/modowners/go.sum +++ b/scripts/modowners/go.sum @@ -6,6 +6,10 @@ github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gt github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/scripts/modowners/go.txd b/scripts/modowners/go.txd deleted file mode 100644 index 982b09c6189..00000000000 --- a/scripts/modowners/go.txd +++ /dev/null @@ -1,23 +0,0 @@ -module github.com/grafana/grafana - -go 1.19 - -require ( - cloud.google.com/go/storage v1.28.1 // @backend-platform - cuelang.org/go v0.5.0 // @as-code @backend-platform - github.com/Azure/azure-sdk-for-go v65.0.0+incompatible // indirect, @delivery - github.com/Masterminds/semver v1.5.0 // @delivery @backend-platform -) - -require ( - github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect -) - -require ( - cloud.google.com/go/kms v1.4.0 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // @backend-platform - github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.9.0 // @delivery -) diff --git a/scripts/modowners/modowners.go b/scripts/modowners/modowners.go old mode 100644 new mode 100755 index 6cb59883312..76d11b2e479 --- a/scripts/modowners/modowners.go +++ b/scripts/modowners/modowners.go @@ -7,9 +7,10 @@ import ( "io" "io/fs" "log" - "os" "strings" + "os" + "golang.org/x/mod/modfile" ) @@ -69,8 +70,7 @@ func parseGoMod(fileSystem fs.FS, name string) ([]Module, error) { } // Validate that each module has an owner. -// An example CLI command is `go run dummy/modowners.go check dummy/go.txd` -// TODO: replace above example with final filepath in the end +// An example CLI command is `go run scripts/modowners/modowners.go check go.mod` func check(fileSystem fs.FS, logger *log.Logger, args []string) error { m, err := parseGoMod(fileSystem, args[0]) if err != nil { @@ -84,17 +84,19 @@ func check(fileSystem fs.FS, logger *log.Logger, args []string) error { } } if fail { - return errors.New("modfile is invalid") + return errors.New("one or more newly added dependencies do not have an assigned owner - please assign a team as an owner") } return nil } -// TODO: owners and modules may optionally take a list (modules for owners, owners for modules) -// TODO: test with go test -// Print owners. +// Print owner(s) for a given dependency. +// An example CLI command to get a list of all owners in go.mod with a count of the number of dependencies they own is `go run scripts/modowners/modowners.go owners -a -c go.mod` +// An example CLI command to get the owner for a specific dependency is `go run scripts/modowners/modowners.go owners -d cloud.google.com/go/storage@v1.30.1 go.mod`. You must use `dependency@version`, not `dependency version`. func owners(fileSystem fs.FS, logger *log.Logger, args []string) error { fs := flag.NewFlagSet("owners", flag.ExitOnError) + allOwners := fs.Bool("a", false, "print all owners in specified file") count := fs.Bool("c", false, "print count of dependencies per owner") + dep := fs.String("d", "", "name of dependency") fs.Parse(args) m, err := parseGoMod(fileSystem, fs.Arg(0)) if err != nil { @@ -102,64 +104,48 @@ func owners(fileSystem fs.FS, logger *log.Logger, args []string) error { } owners := map[string]int{} for _, mod := range m { + if len(*dep) > 0 && mod.Name == *dep { + for _, owner := range mod.Owners { + logger.Println(owner) + break + } + } if mod.Indirect == false { for _, owner := range mod.Owners { owners[owner]++ } } } - for owner, n := range owners { - if *count { - fmt.Println(owner, n) - } else { - fmt.Println(owner) + if *allOwners { + for owner, n := range owners { + if *count { + logger.Println(owner, n) + } else { + logger.Println(owner) + } } } return nil } -/* - GOAL: - 1. if no flags, print all direct dependencies - 2. if -i, print all dependencies (direct + indirect) - 3. if -o, print dependencies owned by the owner(s) listed - 4. if -i and -o, print all dependencies owned by the owner(s) listed - - print all dependencies for each owner listed in CLI after -o flag - check each dependency's owners - - if it match one of the owners in the flag/CLI, print it - if not skip - - CURRENT ISSUE: - owner flag logic not working well with indirect flag logic - not sure how to check for both flags - - mod.Owners := [bep, as-code, delivery] - flag := [gaas, delivery] -*/ - -// Print dependencies. Can specify direct / multiple owners. -// Example CLI command `go run dummy/modowners.go modules -m dummy/go.txd -o @as-code,@delivery` +// Print dependencies for a given owner. Can specify one or more owners. +// Example CLI command to list all direct dependencies owned by Delivery and Authnz `go run scripts/modowners/modowners.go modules -o @grafana/grafana-delivery,@grafana/grafana-authnz-team go.mod` func modules(fileSystem fs.FS, logger *log.Logger, args []string) error { fs := flag.NewFlagSet("modules", flag.ExitOnError) - indirect := fs.Bool("i", false, "print indirect dependencies") // NOTE: indirect is a pointer bc we dont want to lose value after changing it - modfile := fs.String("m", "go.txd", "use specified modfile") + indirect := fs.Bool("i", false, "print indirect dependencies") owner := fs.String("o", "", "one or more owners") fs.Parse(args) - m, err := parseGoMod(fileSystem, *modfile) // NOTE: give me the string that's the first positional argument; fs.Arg works only after fs.Parse + m, err := parseGoMod(fileSystem, fs.Arg(0)) if err != nil { return err } ownerFlags := strings.Split(*owner, ",") for _, mod := range m { - // If there are owner flags or modfile's dependency has an owner to compare - // Else if -i is present and current dependency is indirect - if len(*owner) > 0 && hasCommonElement(mod.Owners, ownerFlags) { - logger.Println(mod.Name) - } else if *indirect && !mod.Indirect { - logger.Println(mod.Name) + if len(*owner) == 0 || hasCommonElement(mod.Owners, ownerFlags) { + if *indirect || !mod.Indirect { + logger.Println(mod.Name) + } } } return nil @@ -177,13 +163,15 @@ func hasCommonElement(a []string, b []string) bool { } func main() { + log.SetFlags(0) + log.SetOutput(os.Stdout) if len(os.Args) < 2 { fmt.Println("usage: modowners subcommand go.mod...") os.Exit(1) } type CmdFunc func(fs.FS, *log.Logger, []string) error cmds := map[string]CmdFunc{"check": check, "owners": owners, "modules": modules} - if f, ok := cmds[os.Args[1]]; !ok { // NOTE: both f and ok are visible inside the if / else if statement, but not outside; chaining of ifs very common in go when checking errors and calling multiple funcs + if f, ok := cmds[os.Args[1]]; !ok { log.Fatal("invalid command") } else if err := f(os.DirFS("."), log.Default(), os.Args[2:]); err != nil { log.Fatal(err) diff --git a/scripts/modowners/modowners_test.go b/scripts/modowners/modowners_test.go index 26efc7c85ab..795f49bfcaf 100644 --- a/scripts/modowners/modowners_test.go +++ b/scripts/modowners/modowners_test.go @@ -69,7 +69,7 @@ func TestCheck(t *testing.T) { func TestModules(t *testing.T) { buf := &bytes.Buffer{} logger := log.New(buf, "", 0) - filesystem := fstest.MapFS{"go.txd": &fstest.MapFile{Data: []byte(` + filesystem := fstest.MapFS{"go.mod": &fstest.MapFile{Data: []byte(` require ( cloud.google.com/go/storage v1.28.1 cuelang.org/go v0.5.0 // @as-code @backend-platform @@ -78,7 +78,7 @@ func TestModules(t *testing.T) { ) `)}} - err := modules(filesystem, logger, []string{"-m", "go.txd"}) // NOTE: pass various flags, these are cmd line arguments + err := modules(filesystem, logger, []string{"go.mod"}) if err != nil { t.Error(err, buf.String()) } @@ -87,16 +87,17 @@ func TestModules(t *testing.T) { // Expected results expectedModules := []string{ - "cloud.google.com/go/storage v1.28.1", - "cuelang.org/go v0.5.0", - "github.com/Azure/azure-sdk-for-go v65.0.0+incompatible", - "github.com/Masterminds/semver v1.5.0", + "cloud.google.com/go/storage@v1.28.1", + "cuelang.org/go@v0.5.0", + "github.com/Masterminds/semver@v1.5.0", + "", } expectedResults := strings.Join(expectedModules, "\n") // Compare logs to expected results if logs != expectedResults { - t.Error(err) + t.Error(logs) + t.Error(expectedResults) } }