diff options
author | Stefan Reinauer <reinauer@chromium.org> | 2015-10-21 13:12:51 -0700 |
---|---|---|
committer | Stefan Reinauer <stefan.reinauer@coreboot.org> | 2015-10-23 01:47:39 +0200 |
commit | 1ecf252c38dd64dc83c0851abbce03da72d9509d (patch) | |
tree | 88a9ee482299ca1ec6a1f3852bee4df1edef2172 /util | |
parent | 33fb4cf0ffb01be8bcb6b488872c87eb50e7d77f (diff) |
MAINTAINERS: Add script to test database and find maintainers
This utility should make it easier to complete and maintain
the database of coreboot subsystem maintainers (MAINTAINERS
file)
This will need a bit of tender love and care to print information
in an easily machine readable output for the build system, but its
a first start to query the maintainers database.
Build with:
$ go build util/scripts/maintainers.go
Find a maintainer for a set of files with:
$ ./maintainers Makefile Makefile.inc
Makefile is in subsystem BUILD SYSTEM
Maintainers: [Patrick Georgi <patrick@georgi-clan.de>]
Makefile.inc is in subsystem BUILD SYSTEM
Maintainers: [Patrick Georgi <patrick@georgi-clan.de>]
Check the maintainer database with:
$ ./maintainers
.gitignore has no subsystem defined in MAINTAINERS
.gitmodules has no subsystem defined in MAINTAINERS
.gitreview has no subsystem defined in MAINTAINERS
3rdparty/arm-trusted-firmware has no subsystem defined in MAINTAINERS
3rdparty/blobs has no subsystem defined in MAINTAINERS
3rdparty/vboot has no subsystem defined in MAINTAINERS
COPYING has no subsystem defined in MAINTAINERS
Documentation/AMD-S3.txt has no subsystem defined in MAINTAINERS
Documentation/CorebootBuildingGuide.tex has no subsystem defined in MAINTAINERS
Documentation/Doxyfile.coreboot has no subsystem defined in MAINTAINERS
[..]
Change-Id: I49c43911971152b0e4d626ccdeb33c088e362695
Signed-off-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Reviewed-on: http://review.coreboot.org/12119
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
Diffstat (limited to 'util')
-rw-r--r-- | util/scripts/maintainers.go | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/util/scripts/maintainers.go b/util/scripts/maintainers.go new file mode 100644 index 0000000000..03c7709c73 --- /dev/null +++ b/util/scripts/maintainers.go @@ -0,0 +1,280 @@ +/* + * Copyright 2015 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +package main + +import ( + "bufio" + "fmt" + "log" + "os" + "os/exec" + "path/filepath" +) + +type subsystem struct { + name string + maintainer []string + file []string +} + +var subsystems []subsystem + +func get_git_files() ([]string, error) { + var files []string + + /* Read in list of all files in the git repository */ + cmd := exec.Command("git", "ls-files") + out, err := cmd.StdoutPipe() + if err != nil { + log.Fatalf("git ls-files failed: %v", err) + return files, err + } + if err := cmd.Start(); err != nil { + log.Fatalf("Could not start %v: %v", cmd, err) + return files, err + } + + r := bufio.NewScanner(out) + for r.Scan() { + /* Cut out leading tab */ + files = append(files, r.Text()) + } + + cmd.Wait() + + return files, nil +} + +func get_maintainers() ([]string, error) { + var maintainers []string + + /* Read in all maintainers */ + file, err := os.Open("MAINTAINERS") + if err != nil { + log.Fatalf("Can't open MAINTAINERS file: %v", err) + log.Fatalf("Are you running from the top-level directory?") + return maintainers, err + } + defer file.Close() + + keep := false + s := bufio.NewScanner(file) + for s.Scan() { + /* Are we in the "data" section and have a non-empty line? */ + if keep && s.Text() != "" { + maintainers = append(maintainers, s.Text()) + } + /* Skip everything before the delimiter */ + if s.Text() == "\t\t-----------------------------------" { + keep = true + } + } + + return maintainers, nil +} + +func build_maintainers(maintainers []string) { + var current *subsystem + for _, line := range maintainers { + if line[1] != ':' { + /* Create new subsystem entry */ + var tmp subsystem + subsystems = append(subsystems, tmp) + current = &subsystems[len(subsystems)-1] + current.name = line + } else { + switch line[0] { + case 'R': + case 'M': + { + /* Add subsystem maintainer */ + current.maintainer = + append(current.maintainer, + line[3:len(line)]) + break + } + case 'S': + { + break + } + case 'L': + { + break + } + case 'T': + { + break + } + case 'F': + { + // add files + current.file = + append(current.file, + line[3:len(line)]) + break + } + default: + { + fmt.Println("No such specifier: ", line) + break + } + } + } + } +} + +func print_maintainers() { + for _, subsystem := range subsystems { + fmt.Println(subsystem.name) + fmt.Println(" ", subsystem.maintainer) + fmt.Println(" ", subsystem.file) + } +} + +func match_file(fname string, files []string) (bool, error) { + var matched bool + var err error + + for _, file := range files { + /* Direct match */ + matched, err = filepath.Match(file, fname) + if err != nil { + return false, err + } + if matched { + return true, nil + } + + /* There are three cases that match_file can handle: + * + * dirname/filename + * dirname/* + * dirname/ + * + * The first case is an exact match, the second case is a + * direct match of everything in that directory, and the third + * is a direct match of everything in that directory and its + * subdirectories. + * + * The first two cases are handled above, the code below is + * only for that latter case, so if file doesn't end in /, + * skip to the next file. + */ + if file[len(file)-1] != '/' { + continue + } + + /* Remove / because we add it again below */ + file = file[:len(file)-1] + + /* Maximum tree depth, as calculated by + * $(( `git ls-files | tr -d "[a-z][A-Z][0-9]\-\_\." | \ + * sort -u | tail -1 | wc -c` - 1 )) + * 11 + */ + max_depth := 11 + + for i := 0; i < max_depth; i++ { + /* Subdirectory match */ + file += "/*" + + if matched, err = filepath.Match(file, fname); err != nil { + return false, err + } + if matched { + return true, nil + } + + } + } + return false, nil +} + +func find_maintainer(fname string) { + for _, subsystem := range subsystems { + matched, err := match_file(fname, subsystem.file) + if err != nil { + log.Fatalf("match_file failed: %v", err) + return + } + if matched && subsystem.name != "THE REST" { + fmt.Println(fname, "is in subsystem", + subsystem.name) + fmt.Println("Maintainers: ", subsystem.maintainer) + return + } + } + fmt.Println(fname, "has no subsystem defined in MAINTAINERS") +} + +func find_unmaintained(fname string) { + for _, subsystem := range subsystems { + matched, err := match_file(fname, subsystem.file) + if err != nil { + log.Fatalf("match_file failed: %v", err) + return + } + if matched && subsystem.name != "THE REST" { + fmt.Println(fname, "is in subsystem", + subsystem.name) + return + } + } + fmt.Println(fname, "has no subsystem defined in MAINTAINERS") +} + +func main() { + var files []string + var maint bool + var debug bool + var err error + + args := os.Args[1:] + if len(args) == 0 { + /* get the filenames */ + files, err = get_git_files() + if err != nil { + log.Fatalf("Oops.") + return + } + maint = false + } else { + files = args + maint = true + } + + maintainers, err := get_maintainers() + if err != nil { + log.Fatalf("Oops.") + return + } + + /* build subsystem database */ + build_maintainers(maintainers) + + if debug { + print_maintainers() + } + + if maint { + /* Find maintainers for each file */ + for _, file := range files { + find_maintainer(file) + } + } else { + for _, file := range files { + find_unmaintained(file) + } + } +} |