summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml5
-rw-r--r--.github/workflows/issue_reviver.yml2
-rw-r--r--Makefile5
-rw-r--r--tools/bazel.mk1
-rw-r--r--tools/github/BUILD15
-rw-r--r--tools/github/main.go162
-rw-r--r--tools/github/nogo/BUILD16
-rw-r--r--tools/github/nogo/nogo.go126
-rw-r--r--tools/github/reviver/BUILD27
-rw-r--r--tools/github/reviver/github.go (renamed from tools/issue_reviver/github/github.go)38
-rw-r--r--tools/github/reviver/github_test.go (renamed from tools/issue_reviver/github/github_test.go)2
-rw-r--r--tools/github/reviver/reviver.go (renamed from tools/issue_reviver/reviver/reviver.go)0
-rw-r--r--tools/github/reviver/reviver_test.go (renamed from tools/issue_reviver/reviver/reviver_test.go)0
-rw-r--r--tools/issue_reviver/BUILD13
-rw-r--r--tools/issue_reviver/github/BUILD25
-rw-r--r--tools/issue_reviver/main.go100
-rw-r--r--tools/issue_reviver/reviver/BUILD18
-rw-r--r--tools/nogo/build.go3
-rw-r--r--tools/nogo/matchers.go24
-rw-r--r--tools/nogo/util/BUILD9
-rw-r--r--tools/nogo/util/util.go85
21 files changed, 489 insertions, 187 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1e1677ab5..e28e46352 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -21,3 +21,8 @@ jobs:
restore-keys: |
${{ runner.os }}-bazel-
- run: make
+ - run: make build OPTIONS="--build_tag_filters nogo" TARGETS="//..."
+ - run: make run TARGETS="//tools/github" ARGS="-path=bazel-bin/ nogo"
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_REPOSITORY: ${{ github.repository }}
diff --git a/.github/workflows/issue_reviver.yml b/.github/workflows/issue_reviver.yml
index 2b399a3f2..c53185620 100644
--- a/.github/workflows/issue_reviver.yml
+++ b/.github/workflows/issue_reviver.yml
@@ -9,7 +9,7 @@ jobs:
steps:
- uses: actions/checkout@v2
if: github.repository == 'google/gvisor'
- - run: make run TARGETS="//tools/issue_reviver"
+ - run: make run TARGETS="//tools/github" ARGS="revive"
if: github.repository == 'google/gvisor'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/Makefile b/Makefile
index 1a42ec9cd..e36a3baed 100644
--- a/Makefile
+++ b/Makefile
@@ -379,3 +379,8 @@ configure: ## Configures a single runtime. Requires sudo. Typically called from
test-runtime: ## A convenient wrapper around test that provides the runtime argument. Target must still be provided.
@$(call submake,test OPTIONS="$(OPTIONS) --test_arg=--runtime=$(RUNTIME)")
.PHONY: test-runtime
+
+nogo: ## Surfaces all nogo findings.
+ @$(call submake,build OPTIONS="--build_tag_filters nogo" TARGETS="//...")
+ @$(call submake,run TARGETS="//tools/github" ARGS="-path=$(BUILD_ROOT) -dry-run nogo")
+.PHONY: nogo
diff --git a/tools/bazel.mk b/tools/bazel.mk
index 4235c36ca..5e129b2ed 100644
--- a/tools/bazel.mk
+++ b/tools/bazel.mk
@@ -19,6 +19,7 @@ SHELL=/bin/bash -o pipefail
BRANCH_NAME := $(shell (git branch --show-current 2>/dev/null || \
git rev-parse --abbrev-ref HEAD 2>/dev/null) | \
xargs -n 1 basename 2>/dev/null)
+BUILD_ROOT := $(CURDIR)/bazel-bin/
# Bazel container configuration (see below).
USER ?= gvisor
diff --git a/tools/github/BUILD b/tools/github/BUILD
new file mode 100644
index 000000000..aad088d13
--- /dev/null
+++ b/tools/github/BUILD
@@ -0,0 +1,15 @@
+load("//tools:defs.bzl", "go_binary")
+
+package(licenses = ["notice"])
+
+go_binary(
+ name = "github",
+ srcs = ["main.go"],
+ nogo = False,
+ deps = [
+ "//tools/github/nogo",
+ "//tools/github/reviver",
+ "@com_github_google_go_github_v28//github:go_default_library",
+ "@org_golang_x_oauth2//:go_default_library",
+ ],
+)
diff --git a/tools/github/main.go b/tools/github/main.go
new file mode 100644
index 000000000..7a74dc033
--- /dev/null
+++ b/tools/github/main.go
@@ -0,0 +1,162 @@
+// Copyright 2019 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Binary github is the entry point for GitHub utilities.
+package main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "strings"
+
+ "github.com/google/go-github/github"
+ "golang.org/x/oauth2"
+ "gvisor.dev/gvisor/tools/github/nogo"
+ "gvisor.dev/gvisor/tools/github/reviver"
+)
+
+var (
+ owner string
+ repo string
+ tokenFile string
+ path string
+ commit string
+ dryRun bool
+)
+
+// Keep the options simple for now. Supports only a single path and repo.
+func init() {
+ flag.StringVar(&owner, "owner", "", "GitHub project org/owner (required, except nogo dry-run)")
+ flag.StringVar(&repo, "repo", "", "GitHub repo (required, except nogo dry-run)")
+ flag.StringVar(&tokenFile, "oauth-token-file", "", "file containing the GitHub token (or GITHUB_TOKEN is set)")
+ flag.StringVar(&path, "path", ".", "path to scan (required for revive and nogo)")
+ flag.StringVar(&commit, "commit", "", "commit to associated (required for nogo, except dry-run)")
+ flag.BoolVar(&dryRun, "dry-run", false, "just print changes to be made")
+}
+
+func main() {
+ // Set defaults from the environment.
+ repository := os.Getenv("GITHUB_REPOSITORY")
+ if parts := strings.SplitN(repository, "/", 2); len(parts) == 2 {
+ owner = parts[0]
+ repo = parts[1]
+ }
+
+ // Parse flags.
+ flag.Usage = func() {
+ fmt.Fprintf(flag.CommandLine.Output(), "usage: %s [options] <command>\n", os.Args[0])
+ fmt.Fprintf(flag.CommandLine.Output(), "commands: revive, nogo\n")
+ flag.PrintDefaults()
+ }
+ flag.Parse()
+ args := flag.Args()
+ if len(args) != 1 {
+ fmt.Fprintf(flag.CommandLine.Output(), "extra arguments: %s\n", strings.Join(args[1:], ", "))
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ // Check for mandatory parameters.
+ command := args[0]
+ if len(owner) == 0 && (command != "nogo" || !dryRun) {
+ fmt.Fprintln(flag.CommandLine.Output(), "missing --owner option.")
+ flag.Usage()
+ os.Exit(1)
+ }
+ if len(repo) == 0 && (command != "nogo" || !dryRun) {
+ fmt.Fprintln(flag.CommandLine.Output(), "missing --repo option.")
+ flag.Usage()
+ os.Exit(1)
+ }
+ if len(path) == 0 {
+ fmt.Fprintln(flag.CommandLine.Output(), "missing --path option.")
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ // The access token may be passed as a file so it doesn't show up in
+ // command line arguments. It also may be provided through the
+ // environment to faciliate use through GitHub's CI system.
+ token := os.Getenv("GITHUB_TOKEN")
+ if len(tokenFile) != 0 {
+ bytes, err := ioutil.ReadFile(tokenFile)
+ if err != nil {
+ fmt.Println(err.Error())
+ os.Exit(1)
+ }
+ token = string(bytes)
+ }
+ var client *github.Client
+ if len(token) == 0 {
+ // Client is unauthenticated.
+ client = github.NewClient(nil)
+ } else {
+ // Using the above token.
+ ts := oauth2.StaticTokenSource(
+ &oauth2.Token{AccessToken: token},
+ )
+ tc := oauth2.NewClient(context.Background(), ts)
+ client = github.NewClient(tc)
+ }
+
+ switch command {
+ case "revive":
+ // Load existing GitHub bugs.
+ bugger, err := reviver.NewGitHubBugger(client, owner, repo, dryRun)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error getting github issues: %v\n", err)
+ os.Exit(1)
+ }
+ // Scan the provided path.
+ rev := reviver.New([]string{path}, []reviver.Bugger{bugger})
+ if errs := rev.Run(); len(errs) > 0 {
+ fmt.Fprintf(os.Stderr, "Encountered %d errors:\n", len(errs))
+ for _, err := range errs {
+ fmt.Fprintf(os.Stderr, "\t%v\n", err)
+ }
+ os.Exit(1)
+ }
+ case "nogo":
+ // Did we get a commit? Try to extract one.
+ if len(commit) == 0 && !dryRun {
+ cmd := exec.Command("git", "rev-parse", "HEAD")
+ revBytes, err := cmd.Output()
+ if err != nil {
+ fmt.Fprintf(flag.CommandLine.Output(), "missing --commit option, unable to infer: %v\n", err)
+ flag.Usage()
+ os.Exit(1)
+ }
+ commit = strings.TrimSpace(string(revBytes))
+ }
+ // Scan all findings.
+ poster := nogo.NewFindingsPoster(client, owner, repo, commit, dryRun)
+ if err := poster.Walk(path); err != nil {
+ fmt.Fprintln(os.Stderr, "Error finding nogo findings:", err)
+ os.Exit(1)
+ }
+ // Post to GitHub.
+ if err := poster.Post(); err != nil {
+ fmt.Fprintln(os.Stderr, "Error posting nogo findings:", err)
+ }
+ default:
+ // Not a known command.
+ fmt.Fprintf(flag.CommandLine.Output(), "unknown command: %s\n", command)
+ flag.Usage()
+ os.Exit(1)
+ }
+}
diff --git a/tools/github/nogo/BUILD b/tools/github/nogo/BUILD
new file mode 100644
index 000000000..0633eaf19
--- /dev/null
+++ b/tools/github/nogo/BUILD
@@ -0,0 +1,16 @@
+load("//tools:defs.bzl", "go_library")
+
+package(licenses = ["notice"])
+
+go_library(
+ name = "nogo",
+ srcs = ["nogo.go"],
+ nogo = False,
+ visibility = [
+ "//tools/github:__subpackages__",
+ ],
+ deps = [
+ "//tools/nogo/util",
+ "@com_github_google_go_github_v28//github:go_default_library",
+ ],
+)
diff --git a/tools/github/nogo/nogo.go b/tools/github/nogo/nogo.go
new file mode 100644
index 000000000..b70dfe63b
--- /dev/null
+++ b/tools/github/nogo/nogo.go
@@ -0,0 +1,126 @@
+// Copyright 2019 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package nogo provides nogo-related utilities.
+package nogo
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "github.com/google/go-github/github"
+ "gvisor.dev/gvisor/tools/nogo/util"
+)
+
+// FindingsPoster is a simple wrapper around the GitHub api.
+type FindingsPoster struct {
+ owner string
+ repo string
+ commit string
+ dryRun bool
+ startTime time.Time
+
+ findings map[util.Finding]struct{}
+ client *github.Client
+}
+
+// NewFindingsPoster returns a object that can post findings.
+func NewFindingsPoster(client *github.Client, owner, repo, commit string, dryRun bool) *FindingsPoster {
+ return &FindingsPoster{
+ owner: owner,
+ repo: repo,
+ commit: commit,
+ dryRun: dryRun,
+ startTime: time.Now(),
+ findings: make(map[util.Finding]struct{}),
+ client: client,
+ }
+}
+
+// Walk walks the given path tree for findings files.
+func (p *FindingsPoster) Walk(path string) error {
+ return filepath.Walk(path, func(filename string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ // Skip any directories or files not ending in .findings.
+ if !strings.HasSuffix(filename, ".findings") || info.IsDir() {
+ return nil
+ }
+ findings, err := util.ExtractFindingsFromFile(filename)
+ if err != nil {
+ return err
+ }
+ // Add all findings to the list. We use a map to ensure
+ // that each finding is unique.
+ for _, finding := range findings {
+ p.findings[finding] = struct{}{}
+ }
+ return nil
+ })
+}
+
+// Post posts all results to the GitHub API as a check run.
+func (p *FindingsPoster) Post() error {
+ // Just show results?
+ if p.dryRun {
+ for finding, _ := range p.findings {
+ // Pretty print, so that this is useful for debugging.
+ fmt.Printf("%s: (%s+%d) %s\n", finding.Category, finding.Path, finding.Line, finding.Message)
+ }
+ return nil
+ }
+
+ // Construct the message.
+ title := "nogo"
+ count := len(p.findings)
+ status := "completed"
+ conclusion := "success"
+ if count > 0 {
+ conclusion = "failure" // Contains errors.
+ }
+ summary := fmt.Sprintf("%d findings.", count)
+ opts := github.CreateCheckRunOptions{
+ Name: title,
+ HeadSHA: p.commit,
+ Status: &status,
+ Conclusion: &conclusion,
+ StartedAt: &github.Timestamp{p.startTime},
+ CompletedAt: &github.Timestamp{time.Now()},
+ Output: &github.CheckRunOutput{
+ Title: &title,
+ Summary: &summary,
+ AnnotationsCount: &count,
+ },
+ }
+ annotationLevel := "failure" // Always.
+ for finding, _ := range p.findings {
+ opts.Output.Annotations = append(opts.Output.Annotations, &github.CheckRunAnnotation{
+ Path: &finding.Path,
+ StartLine: &finding.Line,
+ EndLine: &finding.Line,
+ Message: &finding.Message,
+ Title: &finding.Category,
+ AnnotationLevel: &annotationLevel,
+ })
+ }
+
+ // Post to GitHub.
+ _, _, err := p.client.Checks.CreateCheckRun(context.Background(), p.owner, p.repo, opts)
+ return err
+}
diff --git a/tools/github/reviver/BUILD b/tools/github/reviver/BUILD
new file mode 100644
index 000000000..7d78480a7
--- /dev/null
+++ b/tools/github/reviver/BUILD
@@ -0,0 +1,27 @@
+load("//tools:defs.bzl", "go_library", "go_test")
+
+package(licenses = ["notice"])
+
+go_library(
+ name = "reviver",
+ srcs = [
+ "github.go",
+ "reviver.go",
+ ],
+ nogo = False,
+ visibility = [
+ "//tools/github:__subpackages__",
+ ],
+ deps = ["@com_github_google_go_github_v28//github:go_default_library"],
+)
+
+go_test(
+ name = "reviver_test",
+ size = "small",
+ srcs = [
+ "github_test.go",
+ "reviver_test.go",
+ ],
+ library = ":reviver",
+ nogo = False,
+)
diff --git a/tools/issue_reviver/github/github.go b/tools/github/reviver/github.go
index 8ffd7e606..a95df0fb6 100644
--- a/tools/issue_reviver/github/github.go
+++ b/tools/github/reviver/github.go
@@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// Package github implements reviver.Bugger interface on top of Github issues.
-package github
+package reviver
import (
"context"
@@ -23,12 +22,10 @@ import (
"time"
"github.com/google/go-github/github"
- "golang.org/x/oauth2"
- "gvisor.dev/gvisor/tools/issue_reviver/reviver"
)
-// Bugger implements reviver.Bugger interface for github issues.
-type Bugger struct {
+// GitHubBugger implements Bugger interface for github issues.
+type GitHubBugger struct {
owner string
repo string
dryRun bool
@@ -37,36 +34,25 @@ type Bugger struct {
issues map[int]*github.Issue
}
-// NewBugger creates a new Bugger.
-func NewBugger(token, owner, repo string, dryRun bool) (*Bugger, error) {
- b := &Bugger{
+// NewGitHubBugger creates a new GitHubBugger.
+func NewGitHubBugger(client *github.Client, owner, repo string, dryRun bool) (*GitHubBugger, error) {
+ b := &GitHubBugger{
owner: owner,
repo: repo,
dryRun: dryRun,
issues: map[int]*github.Issue{},
+ client: client,
}
- if err := b.load(token); err != nil {
+ if err := b.load(); err != nil {
return nil, err
}
return b, nil
}
-func (b *Bugger) load(token string) error {
- ctx := context.Background()
- if len(token) == 0 {
- fmt.Print("No OAUTH token provided, using unauthenticated account.\n")
- b.client = github.NewClient(nil)
- } else {
- ts := oauth2.StaticTokenSource(
- &oauth2.Token{AccessToken: token},
- )
- tc := oauth2.NewClient(ctx, ts)
- b.client = github.NewClient(tc)
- }
-
+func (b *GitHubBugger) load() error {
err := processAllPages(func(listOpts github.ListOptions) (*github.Response, error) {
opts := &github.IssueListByRepoOptions{State: "open", ListOptions: listOpts}
- tmps, resp, err := b.client.Issues.ListByRepo(ctx, b.owner, b.repo, opts)
+ tmps, resp, err := b.client.Issues.ListByRepo(context.Background(), b.owner, b.repo, opts)
if err != nil {
return resp, err
}
@@ -83,8 +69,8 @@ func (b *Bugger) load(token string) error {
return nil
}
-// Activate implements reviver.Bugger.
-func (b *Bugger) Activate(todo *reviver.Todo) (bool, error) {
+// Activate implements Bugger.Activate.
+func (b *GitHubBugger) Activate(todo *Todo) (bool, error) {
id, err := parseIssueNo(todo.Issue)
if err != nil {
return true, err
diff --git a/tools/issue_reviver/github/github_test.go b/tools/github/reviver/github_test.go
index a78b230ef..5df7e3624 100644
--- a/tools/issue_reviver/github/github_test.go
+++ b/tools/github/reviver/github_test.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package github
+package reviver
import (
"testing"
diff --git a/tools/issue_reviver/reviver/reviver.go b/tools/github/reviver/reviver.go
index 2af7f0d59..2af7f0d59 100644
--- a/tools/issue_reviver/reviver/reviver.go
+++ b/tools/github/reviver/reviver.go
diff --git a/tools/issue_reviver/reviver/reviver_test.go b/tools/github/reviver/reviver_test.go
index a9fb1f9f1..a9fb1f9f1 100644
--- a/tools/issue_reviver/reviver/reviver_test.go
+++ b/tools/github/reviver/reviver_test.go
diff --git a/tools/issue_reviver/BUILD b/tools/issue_reviver/BUILD
deleted file mode 100644
index 35b0111ca..000000000
--- a/tools/issue_reviver/BUILD
+++ /dev/null
@@ -1,13 +0,0 @@
-load("//tools:defs.bzl", "go_binary")
-
-package(licenses = ["notice"])
-
-go_binary(
- name = "issue_reviver",
- srcs = ["main.go"],
- nogo = False,
- deps = [
- "//tools/issue_reviver/github",
- "//tools/issue_reviver/reviver",
- ],
-)
diff --git a/tools/issue_reviver/github/BUILD b/tools/issue_reviver/github/BUILD
deleted file mode 100644
index 555abd296..000000000
--- a/tools/issue_reviver/github/BUILD
+++ /dev/null
@@ -1,25 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "github",
- srcs = ["github.go"],
- nogo = False,
- visibility = [
- "//tools/issue_reviver:__subpackages__",
- ],
- deps = [
- "//tools/issue_reviver/reviver",
- "@com_github_google_go_github_v28//github:go_default_library",
- "@org_golang_x_oauth2//:go_default_library",
- ],
-)
-
-go_test(
- name = "github_test",
- size = "small",
- srcs = ["github_test.go"],
- library = ":github",
- nogo = False,
-)
diff --git a/tools/issue_reviver/main.go b/tools/issue_reviver/main.go
deleted file mode 100644
index 47c796b8a..000000000
--- a/tools/issue_reviver/main.go
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2019 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package main is the entry point for issue_reviver.
-package main
-
-import (
- "flag"
- "fmt"
- "io/ioutil"
- "os"
- "strings"
-
- "gvisor.dev/gvisor/tools/issue_reviver/github"
- "gvisor.dev/gvisor/tools/issue_reviver/reviver"
-)
-
-var (
- owner string
- repo string
- tokenFile string
- path string
- dryRun bool
-)
-
-// Keep the options simple for now. Supports only a single path and repo.
-func init() {
- flag.StringVar(&owner, "owner", "", "Github project org/owner to look for issues")
- flag.StringVar(&repo, "repo", "", "Github repo to look for issues")
- flag.StringVar(&tokenFile, "oauth-token-file", "", "Path to file containing the OAUTH token to be used as credential to github")
- flag.StringVar(&path, "path", ".", "Path to scan for TODOs")
- flag.BoolVar(&dryRun, "dry-run", false, "If set to true, no changes are made to issues")
-}
-
-func main() {
- // Set defaults from the environment.
- repository := os.Getenv("GITHUB_REPOSITORY")
- if parts := strings.SplitN(repository, "/", 2); len(parts) == 2 {
- owner = parts[0]
- repo = parts[1]
- }
-
- // Parse flags.
- flag.Parse()
-
- // Check for mandatory parameters.
- if len(owner) == 0 {
- fmt.Println("missing --owner option.")
- flag.Usage()
- os.Exit(1)
- }
- if len(repo) == 0 {
- fmt.Println("missing --repo option.")
- flag.Usage()
- os.Exit(1)
- }
- if len(path) == 0 {
- fmt.Println("missing --path option.")
- flag.Usage()
- os.Exit(1)
- }
-
- // The access token may be passed as a file so it doesn't show up in
- // command line arguments. It also may be provided through the
- // environment to faciliate use through GitHub's CI system.
- token := os.Getenv("GITHUB_TOKEN")
- if len(tokenFile) != 0 {
- bytes, err := ioutil.ReadFile(tokenFile)
- if err != nil {
- fmt.Println(err.Error())
- os.Exit(1)
- }
- token = string(bytes)
- }
-
- bugger, err := github.NewBugger(token, owner, repo, dryRun)
- if err != nil {
- fmt.Fprintln(os.Stderr, "Error getting github issues:", err)
- os.Exit(1)
- }
- rev := reviver.New([]string{path}, []reviver.Bugger{bugger})
- if errs := rev.Run(); len(errs) > 0 {
- fmt.Fprintf(os.Stderr, "Encountered %d errors:\n", len(errs))
- for _, err := range errs {
- fmt.Fprintf(os.Stderr, "\t%v\n", err)
- }
- os.Exit(1)
- }
-}
diff --git a/tools/issue_reviver/reviver/BUILD b/tools/issue_reviver/reviver/BUILD
deleted file mode 100644
index d262932bd..000000000
--- a/tools/issue_reviver/reviver/BUILD
+++ /dev/null
@@ -1,18 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "reviver",
- srcs = ["reviver.go"],
- visibility = [
- "//tools/issue_reviver:__subpackages__",
- ],
-)
-
-go_test(
- name = "reviver_test",
- size = "small",
- srcs = ["reviver_test.go"],
- library = ":reviver",
-)
diff --git a/tools/nogo/build.go b/tools/nogo/build.go
index 37947b5c3..39c2ae418 100644
--- a/tools/nogo/build.go
+++ b/tools/nogo/build.go
@@ -26,6 +26,9 @@ var (
// and should not have any special prefix applied.
internalPrefix = fmt.Sprintf("^")
+ // internalDefault is applied when no paths are provided.
+ internalDefault = fmt.Sprintf("%s/.*", notPath("external"))
+
// externalPrefix is external workspace packages.
externalPrefix = "^external/"
)
diff --git a/tools/nogo/matchers.go b/tools/nogo/matchers.go
index 57a250501..5c39be630 100644
--- a/tools/nogo/matchers.go
+++ b/tools/nogo/matchers.go
@@ -16,7 +16,6 @@ package nogo
import (
"go/token"
- "path/filepath"
"regexp"
"strings"
@@ -44,11 +43,30 @@ type pathRegexps struct {
func buildRegexps(prefix string, args ...string) []*regexp.Regexp {
result := make([]*regexp.Regexp, 0, len(args))
for _, arg := range args {
- result = append(result, regexp.MustCompile(filepath.Join(prefix, arg)))
+ result = append(result, regexp.MustCompile(prefix+arg))
}
return result
}
+// notPath works around the lack of backtracking.
+//
+// It is used to construct a regular expression for non-matching components.
+func notPath(name string) string {
+ sb := strings.Builder{}
+ sb.WriteString("(")
+ for i := range name {
+ if i > 0 {
+ sb.WriteString("|")
+ }
+ sb.WriteString(name[:i])
+ sb.WriteString("[^")
+ sb.WriteByte(name[i])
+ sb.WriteString("/][^/]*")
+ }
+ sb.WriteString(")")
+ return sb.String()
+}
+
// ShouldReport implements matcher.ShouldReport.
func (p *pathRegexps) ShouldReport(d analysis.Diagnostic, fs *token.FileSet) bool {
fullPos := fs.Position(d.Pos).String()
@@ -79,7 +97,7 @@ func externalExcluded(paths ...string) *pathRegexps {
// internalMatches returns a path matcher for internal packages.
func internalMatches() *pathRegexps {
return &pathRegexps{
- expr: buildRegexps(internalPrefix, ".*"),
+ expr: buildRegexps(internalPrefix, internalDefault),
include: true,
}
}
diff --git a/tools/nogo/util/BUILD b/tools/nogo/util/BUILD
new file mode 100644
index 000000000..7ab340b51
--- /dev/null
+++ b/tools/nogo/util/BUILD
@@ -0,0 +1,9 @@
+load("//tools:defs.bzl", "go_library")
+
+package(licenses = ["notice"])
+
+go_library(
+ name = "util",
+ srcs = ["util.go"],
+ visibility = ["//visibility:public"],
+)
diff --git a/tools/nogo/util/util.go b/tools/nogo/util/util.go
new file mode 100644
index 000000000..919fec799
--- /dev/null
+++ b/tools/nogo/util/util.go
@@ -0,0 +1,85 @@
+// Copyright 2019 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package util contains nogo-related utilities.
+package util
+
+import (
+ "fmt"
+ "io/ioutil"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+// findingRegexp is used to parse findings.
+var findingRegexp = regexp.MustCompile(`([a-zA-Z0-9_\/\.-]+): (-|([a-zA-Z0-9_\/\.-]+):([0-9]+)(:([0-9]+))?): (.*)`)
+
+const (
+ categoryIndex = 1
+ fullPathAndLineIndex = 2
+ fullPathIndex = 3
+ lineIndex = 4
+ messageIndex = 7
+)
+
+// Finding is a single finding.
+type Finding struct {
+ Category string
+ Path string
+ Line int
+ Message string
+}
+
+// ExtractFindingsFromFile loads findings from a file.
+func ExtractFindingsFromFile(filename string) ([]Finding, error) {
+ content, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return nil, err
+ }
+ return ExtractFindingsFromBytes(content)
+}
+
+// ExtractFindingsFromBytes loads findings from bytes.
+func ExtractFindingsFromBytes(content []byte) (findings []Finding, err error) {
+ lines := strings.Split(string(content), "\n")
+ for _, singleLine := range lines {
+ // Skip blank lines.
+ singleLine = strings.TrimSpace(singleLine)
+ if singleLine == "" {
+ continue
+ }
+ m := findingRegexp.FindStringSubmatch(singleLine)
+ if m == nil {
+ // We shouldn't see findings like this.
+ return findings, fmt.Errorf("poorly formated line: %v", singleLine)
+ }
+ if m[fullPathAndLineIndex] == "-" {
+ continue // No source file available.
+ }
+ // Cleanup the message.
+ message := m[messageIndex]
+ message = strings.Replace(message, " → ", "\n → ", -1)
+ message = strings.Replace(message, " or ", "\n or ", -1)
+ // Construct a new annotation.
+ lineNumber, _ := strconv.ParseUint(m[lineIndex], 10, 32)
+ findings = append(findings, Finding{
+ Category: m[categoryIndex],
+ Path: m[fullPathIndex],
+ Line: int(lineNumber),
+ Message: message,
+ })
+ }
+ return findings, nil
+}