Add staticchecks to bazel builds (#13298)

* Update staticcheck to latest

* Add static checks while ignoring for third party / external stuff

* Added a hack to keep go mod happy.

* disable SA2002

* Pin go mod tidy checker image to golang:1.20-alpine
This commit is contained in:
Preston Van Loon
2023-12-07 23:42:55 -06:00
committed by GitHub
parent cee38660c7
commit f537a98fcd
13 changed files with 455 additions and 69 deletions

View File

@@ -0,0 +1,27 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary")
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
# gazelle:exclude hack.go
go_library(
name = "go_default_library",
srcs = [
"config.go",
"config_exclusion.go",
"main.go",
],
importpath = "github.com/prysmaticlabs/prysm/v4/tools/nogo_config",
visibility = ["//visibility:private"],
)
go_binary(
name = "nogo_config",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = ["config_exclusion_test.go"],
embed = [":go_default_library"],
)

View File

@@ -0,0 +1,13 @@
package main
// These Config types are copied from bazelbuild/rules_go.
// License: Apache 2.0
// https://github.com/bazelbuild/rules_go/blob/c90a11ad8dc5f3f9d633f0556b22c90af1b01116/go/tools/builders/generate_nogo_main.go#L193
type Configs map[string]Config
type Config struct {
Description string
OnlyFiles map[string]string `json:"only_files"`
ExcludeFiles map[string]string `json:"exclude_files"`
AnalyzerFlags map[string]string `json:"analyzer_flags"`
}

View File

@@ -0,0 +1,17 @@
package main
// AddExclusion adds an exclusion to the Configs, if they do not exist already.
func (c Configs) AddExclusion(check string, exclusions []string) {
for _, e := range exclusions {
if cc, ok := c[check]; !ok {
c[check] = Config{
ExcludeFiles: make(map[string]string),
}
} else if cc.ExcludeFiles == nil {
cc.ExcludeFiles = make(map[string]string)
}
if _, ok := c[check].ExcludeFiles[e]; !ok {
c[check].ExcludeFiles[e] = exclusionMessage
}
}
}

View File

@@ -0,0 +1,29 @@
package main
import "testing"
func TestAddExclusion(t *testing.T) {
cfg := Configs{
"foo": Config{},
}
cfg.AddExclusion("sa0000", []string{"foo.go", "bar.go"})
if len(cfg["sa0000"].ExcludeFiles) != 2 {
t.Errorf("Expected 2 exclusions, got %d", len(cfg["sa0000"].ExcludeFiles))
}
if cfg["sa0000"].ExcludeFiles["foo.go"] != exclusionMessage {
t.Errorf("Expected exclusion message, got %s", cfg["sa0000"].ExcludeFiles["foo.go"])
}
if cfg["sa0000"].ExcludeFiles["bar.go"] != exclusionMessage {
t.Errorf("Expected exclusion message, got %s", cfg["sa0000"].ExcludeFiles["bar.go"])
}
cfg.AddExclusion("sa0000", []string{"foo.go", "baz.go"})
if len(cfg["sa0000"].ExcludeFiles) != 3 {
t.Errorf("Expected 3 exclusions, got %d", len(cfg["sa0000"].ExcludeFiles))
}
if cfg["sa0000"].ExcludeFiles["baz.go"] != exclusionMessage {
t.Errorf("Expected exclusion message, got %s", cfg["sa0000"].ExcludeFiles["baz.go"])
}
}

75
tools/nogo_config/def.bzl Normal file
View File

@@ -0,0 +1,75 @@
"""
Easily add exclusions to nogo_config.json. The tool also allows for hand written entries in the
input file to be preserved.
Example usage:
nogo_config_exclude(
name = "nogo_config_with_excludes",
input = "nogo_config.json",
exclude_files = [
"third_party/.*",
".*_test\\.go",
],
checks = [
"ifferr",
"sa0000",
"neverbuild",
],
)
nogo(
name = "nogo",
config = ":nogo_config_with_excludes",
...
)
"""
def _nogo_config_exclude_impl(ctx):
input_file = ctx.attr.input.files.to_list()[0].path
output_file = ctx.outputs.out.path
exclude_files = ctx.attr.exclude_files
checks = ctx.attr.checks
ctx.actions.run(
executable = ctx.executable.tool,
inputs = ctx.attr.input.files,
outputs = [ctx.outputs.out],
arguments = [
"--input=%s" % input_file,
"--output=%s" % output_file,
"--checks=%s" % ",".join(checks),
"--exclude_files=%s" % ",".join(exclude_files),
"--silent",
],
progress_message = "Generating nogo_config.json with exclusions.",
)
nogo_config_exclude = rule(
implementation = _nogo_config_exclude_impl,
attrs = {
"input": attr.label(
mandatory = True,
allow_single_file = True,
doc = "The input nogo_config.json file.",
),
"exclude_files": attr.string_list(
mandatory = False,
doc = "A list of regexes to exclude from the input file.",
),
"checks": attr.string_list(
mandatory = True,
doc = "A list of checks to exclude.",
),
"tool": attr.label(
executable = True,
cfg = "exec",
default = Label("@prysm//tools/nogo_config:nogo_config"),
doc = "The nogo config exclusion tool.",
),
},
doc = "Generate a nogo_config.json file with exclusions.",
outputs = {
"out": "nogo_config.generated.json",
},
)

View File

@@ -0,0 +1,5 @@
package main
import (
_ "honnef.co/go/tools/staticcheck" // Hack to keep go mod tidy happy. This dep is needed by bazel tooling.
)

94
tools/nogo_config/main.go Normal file
View File

@@ -0,0 +1,94 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"os"
"strings"
)
const exclusionMessage = "Excluded by @prysm//tools/nogo_config tool"
var (
defaultExclusions = []string{
"external/.*",
}
)
var (
input = flag.String("input", "", "(required) input file")
output = flag.String("output", "", "(required) output file")
checks = flag.String("checks", "", "(required) comma separated list of checks to exclude")
exclusions = flag.String("exclude_files", strings.Join(defaultExclusions, ","), "exclusions file")
silent = flag.Bool("silent", false, "disable logging")
)
func main() {
flag.Parse()
if *input == "" || *output == "" {
fmt.Println("Error: input and output must be specified. Review the help text.")
flag.Usage()
return
}
if *checks == "" {
fmt.Println("Error: checks must be specified. Review the help text.")
flag.Usage()
return
}
e := defaultExclusions
if *exclusions != "" {
e = strings.Split(*exclusions, ",")
}
f, err := os.Open(*input)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer func() {
err := f.Close()
if err != nil {
panic(err)
}
}()
data, err := io.ReadAll(f)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
var c Configs
if err := json.Unmarshal(data, &c); err != nil {
fmt.Printf("Error: %v\n", err)
return
}
for _, check := range strings.Split(*checks, ",") {
c.AddExclusion(strings.TrimSpace(check), e)
}
out, err := os.Create(*output)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer func() {
err := out.Close()
if err != nil {
panic(err)
}
}()
if err := json.NewEncoder(out).Encode(c); err != nil {
fmt.Printf("Error: %v\n", err)
return
}
if !*silent {
fmt.Printf("Wrote %v\n", *output)
}
}