mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
tool: generate owners for modules in go.mod
(#69583)
* test: add test for getFiles * fix: fix getFiles test to take in a module name (not modfile name) * move functionality unrelated to getFiles into main func * attempt to getFiles with ParseDir, empty map returned; TODO: restore modowners_generation_script_test.go * test * Revert "test" This reverts commit2b519f3725
. * Revert "attempt to getFiles with ParseDir, empty map returned; TODO: restore modowners_generation_script_test.go" This reverts commiteb95247487
. * attempt to getFiles with ParseDir, empty map returned; TODO: restore modowners_generation_script_test.go * post-pairing with daniel, can access imports in a file * clean up comments for readability * try to return map of importName: files that import the improt * refactor: change getFiles to accept single import name and return list of files that import it * add log to see importPath and importName * hasImport working * start modowners script & add comments for hasImport * fix modules() and uncomment main * start script to add team names to go.mod, currently can access a map of the import and name * 💩 * calculate root directory to point to correct go.mod * chore: delete unnecessary files * chore: uncomment tests * chore: remove unnecessary comments, update documentation comments with correct cli commands * fix: revert changes in go.mod and go.sum * where is my dependency flag value?? * fix: owners function now can list all owners (with counts) or list a specific owner for a given dependency * fix: change fmt.Println to logger.Println for owners func * partial fix: modules now only prints dependencies owned by given team. -i functionality still not working properly * fix: fix TestModules, modules * chore: update check error message to specify user needs to assign owner to new dependency * fix: adjust punctuation in error string * fix: clean up comments in modowners * chore: remove note in modowners_test
This commit is contained in:
parent
72c92ca187
commit
36264a095f
@ -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
|
||||
)
|
||||
|
@ -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=
|
||||
|
@ -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
|
||||
)
|
78
scripts/modowners/modowners.go
Normal file → Executable file
78
scripts/modowners/modowners.go
Normal file → Executable file
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user