reviewdog

package module
v0.0.0-...-9f51c1b Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2016 License: MIT Imports: 12 Imported by: 0

README ΒΆ

reviewdog - A code review dog who keeps your codebase healthy

Gitter LICENSE Go Report Card releases

reviewdog logo

CI Status
Travis CI Travis Build Status
CircleCI CircleCI
drone.io drone.io Build Status

"reviewdog" provides a way to post review comments to code hosting service, such as GitHub, automatically by integrating with any linter tools with ease. It uses any output of lint tools and post them as a comment if the file and line are in diff of patches to review.

reviewdog also supports run in local environment to filter output of lint tools by diff.

design doc

Automatic code review (sample PR)

sample-comment.png

Local run

reviewdog-local-demo.gif

Installation

Get the binary release (recommended way)

or

go get -u github.com/haya14busa/reviewdog/cmd/reviewdog

Usage

'errorformat'

reviewdog accepts any compiler or linter result from stdin and parses it with scan-f like 'errorformat', which is the port of Vim's errorformat feature.

For example, if the result format is {file}:{line number}:{column number}: {message}, errorformat should be %f:%l:%c: %m and you can pass it as -efm arguments.

$ golint ./...
comment_iowriter.go:11:6: exported type CommentWriter should have comment or be unexported
$ golint ./... | reviewdog -efm="%f:%l:%c: %m" -diff="git diff master"
name description
%f file name
%l line number
%c column number
%m error message
%% the single '%' character
... ...

Please see haya14busa/errorformat and :h errorformat if you want to deal with more complex output. 'errorformat' can handle more complex output like multi-line error message.

By this 'errorformat' feature, reviewdog can support any tools output with ease.

Available pre-defined 'errorformat'

But, you don't have to write 'errorformat' in many cases. reviewdog supports pre-defined errorformat for major compiler or linter tools.

You can find available errorformat name by reviewdog -list and you can use it with -f={name}.

$ reviewdog -list
golint          linter for Go source code                                       - https://github.com/golang/lint
govet           Vet examines Go source code and reports suspicious problems     - https://golang.org/cmd/vet/
sbt             the interactive build tool                                      - http://www.scala-sbt.org/
...
$ golint ./... | reviewdog -f=golint -diff="git diff master"

You can add supported pre-defined 'errorformat' by contributing to haya14busa/errorformat

checkstyle format

reviewdog also accepts checkstyle XML format as well. If the linter supports checkstyle format as a report format, you can us -f=checkstyle instead of using 'errorformat'.

# Local
$ eslint -f checkstyle . | reviewdog -f=checkstyle -diff="git diff"

# CI (overwrite tool name which is shown in review comment by -name arg)
$ eslint -f checkstyle . | reviewdog -f=checkstyle -name="eslint" -ci="circle-ci"

Also, if you want to pass other Json/XML/etc... format to reviewdog, you can write a converter.

$ <linter> | <convert-to-checkstyle> | reviewdog -f=checkstyle -name="<linter>" -ci="circle-ci"

Run locally

reviewdog can find new introduced warnings or error by filtering linter results using diff. You can pass diff comamnd as -diff arg, like -diff="git diff", -diff="git diff master", etc... when you use reviewdog in local environment.

Working with GitHub and CI services

reviewdog can intergrate with CI service and post review comments to report results automatically as well!

Supported code hosting (or code review) service

At the time of writing, reviewdog supports GitHub and GitHub Enterprise as a code hosting service for posting comments.

It may support Gerrit, Bitbucket, or other services later.

reviewdog requires GitHub Personal API Access token as an environment variable (REVIEWDOG_GITHUB_API_TOKEN) to post comments to GitHub or GitHub Enterprise. Go to https://github.com/settings/tokens and generate new API token. Check repo for private repositories or public_repo for public repositories. Export the token environment variable by secure way, depending on CI services.

For GitHub Enterprise, please set API endpoint by environment variable.

export GITHUB_API="https://example.githubenterprise.com/api/v3"
Supported CI services
Name Pull Request from the same repository Pull Request from forked repository
Travis CI β­• ❌
CircleCI β­• ❌ (but possible with insecure way)
drone.io (OSS) v0.4 β­• β­•
common (Your managed CI server like Jenkins) β­• β­•

reviewdog can run in CI services which supports Pull Request build and secret environment variable feature for security reason.

But, for the Pull Request from forked repository, most CI services restrict secret environment variable for security reason, to avoid leak of secret data with malicious Pull Request. (Travis CI, CircleCI)

Travis CI

Store GitHub API token by travis encryption keys.

$ gem install travis
$ travis encrypt REVIEWDOG_GITHUB_API_TOKEN=<your token> --add env.global

Example:

env:
  global:
    secure: xxxxxxxxxxxxx

install:
  - go get github.com/haya14busa/reviewdog/cmd/reviewdog

script:
  - >-
    golint ./... | reviewdog -f=golint -ci=travis
Circle CI

Store GitHub API token in Environment variables - CircleCI

Circle CI do not build by pull-request hook by default, so please turn on "Only build pull requests" in Advanced option in Circle CI (changelog).

Pull Requests from fork repo cannot set environment variable for security reason. However, if you want to run fork PR builds for private repositories or want to run reviewdog for forked PR to OSS project, CircleCI have an option to enable it. Unsafe fork PR builds

I thinks it's not a big problem if GitHub API token has limited scope (e.g. only public_repo), but if you enables Unsafe fork PR builds to run reviewdog for fork PR, please use it at your own risk.

circle.yml sample

machine:
  environment:
    REVIEWDOG_VERSION: 0.9.2

dependencies:
  override:
    - curl -fSL https://github.com/haya14busa/reviewdog/releases/download/$REVIEWDOG_VERSION/reviewdog_linux_amd64 -o reviewdog && chmod +x ./reviewdog

test:
  override:
    - >-
      go tool vet -all -shadowstrict . 2>&1 | ./reviewdog -f=govet -ci="circle-ci"
drone.io

Store GitHub API token in environment variable. Secrets Β· Drone

Install 'drone' cli command http://readme.drone.io/devs/cli/ and setup configuration.

echo '.drone.sec.yaml' >> .gitignore

.drone.sec.yaml

environment:
  REVIEWDOG_GITHUB_API_TOKEN: <your token>

.drone.yaml example

build:
  lint:
    image: golang
    environment:
      - REVIEWDOG_GITHUB_API_TOKEN=$$REVIEWDOG_GITHUB_API_TOKEN
    commands:
      - go get github.com/haya14busa/reviewdog/cmd/reviewdog
      - |
        go tool vet -all -shadowstrict . 2>&1 | reviewdog -f=govet -ci=droneio
    when:
      event: pull_request

Finally, run drone secure to encrypt the .drone.sec.yaml file and generate a .drone.sec file

$ drone secure --repo {github-user-name}/{repo-name} --in .drone.sec.yaml

drone.io supports encrypted environment variable for fork Pull Request build in secure way, but you have to read document carefully http://readme.drone.io/usage/secrets/ not to expose secret data unexpectedly.

Common (Jenkins, local, etc...)

You can use reviewdog to post review comments anywhere with following environment variables.

name description
CI_PULL_REQUEST Pull Request number (e.g. 14)
CI_COMMIT SHA1 for the current build
CI_REPO_OWNER repository owner (e.g. "haya14busa" for https://github.com/haya14busa/reviewdog)
CI_REPO_NAME repository name (e.g. "reviewdog" for https://github.com/haya14busa/reviewdog)
REVIEWDOG_GITHUB_API_TOKEN GitHub Personal API Access token
$ export CI_PULL_REQUEST=14
$ export CI_REPO_OWNER=haya14busa
$ export CI_REPO_NAME=reviewdog
$ export CI_COMMIT=$(git rev-parse HEAD)
$ export REVIEWDOG_GITHUB_API_TOKEN="<your token>"
$ golint ./... | reviewdog -f=golint -ci=common

🐦 Author

haya14busa (https://github.com/haya14busa)

Documentation ΒΆ

Index ΒΆ

Examples ΒΆ

Constants ΒΆ

This section is empty.

Variables ΒΆ

This section is empty.

Functions ΒΆ

This section is empty.

Types ΒΆ

type AddedLine ΒΆ

type AddedLine struct {
	Path     string // path to new file
	Lnum     int    // the line number in the new file
	LnumDiff int    // the line number of the diff (Same as Lnumdiff of diff.Line)
	Content  string // line content
}

AddedLine represents added line in diff.

type CheckResult ΒΆ

type CheckResult struct {
	Path    string   // relative file path
	Lnum    int      // line number
	Col     int      // column number (1 <tab> == 1 character column)
	Message string   // error message
	Lines   []string // Original error lines (often one line)
}

CheckResult represents a checked result of static analysis tools. :h error-file-format

type CheckStyleError ΒΆ

type CheckStyleError struct {
	Column   int    `xml:"column,attr,omitempty"`
	Line     int    `xml:"line,attr"`
	Message  string `xml:"message,attr"`
	Severity string `xml:"severity,attr,omitempty"`
	Source   string `xml:"source,attr,omitempty"`
}

CheckStyleError represents <error line="1" column="10" severity="error" message="msg" source="src" />

type CheckStyleFile ΒΆ

type CheckStyleFile struct {
	Name   string             `xml:"name,attr"`
	Errors []*CheckStyleError `xml:"error"`
}

CheckStyleFile represents <file name="fname"><error ... />...</file>

type CheckStyleParser ΒΆ

type CheckStyleParser struct{}

CheckStyleParser is checkstyle parser.

func (*CheckStyleParser) Parse ΒΆ

func (p *CheckStyleParser) Parse(r io.Reader) ([]*CheckResult, error)

type CheckStyleResult ΒΆ

type CheckStyleResult struct {
	XMLName xml.Name          `xml:"checkstyle"`
	Version string            `xml:"version,attr"`
	Files   []*CheckStyleFile `xml:"file,omitempty"`
}

CheckStyleResult represents checkstyle XML result. <?xml version="1.0" encoding="utf-8"?><checkstyle version="4.3"><file ...></file>...</checkstyle>

References:

type Comment ΒΆ

type Comment struct {
	*CheckResult
	Body     string
	LnumDiff int
	ToolName string
}

Comment represents a reported result as a comment.

type CommentService ΒΆ

type CommentService interface {
	Post(*Comment) error
}

CommentService is an interface which posts Comment.

type CommentWriter ΒΆ

type CommentWriter struct {
	// contains filtered or unexported fields
}

func NewCommentWriter ΒΆ

func NewCommentWriter(w io.Writer) *CommentWriter

func (*CommentWriter) Post ΒΆ

func (s *CommentWriter) Post(c *Comment) error

type DiffCmd ΒΆ

type DiffCmd struct {
	// contains filtered or unexported fields
}

func (*DiffCmd) Diff ΒΆ

func (d *DiffCmd) Diff() ([]byte, error)

func (*DiffCmd) Strip ΒΆ

func (d *DiffCmd) Strip() int

type DiffService ΒΆ

type DiffService interface {
	Diff() ([]byte, error)
	Strip() int
}

DiffService is an interface which get diff.

func NewDiffCmd ΒΆ

func NewDiffCmd(cmd *exec.Cmd, strip int) DiffService

func NewDiffString ΒΆ

func NewDiffString(diff string, strip int) DiffService

type DiffString ΒΆ

type DiffString struct {
	// contains filtered or unexported fields
}

func (*DiffString) Diff ΒΆ

func (d *DiffString) Diff() ([]byte, error)

func (*DiffString) Strip ΒΆ

func (d *DiffString) Strip() int

type ErrorformatParser ΒΆ

type ErrorformatParser struct {
	// contains filtered or unexported fields
}

ErrorformatParser is errorformat parser.

func (*ErrorformatParser) Parse ΒΆ

func (p *ErrorformatParser) Parse(r io.Reader) ([]*CheckResult, error)

type GitHubPullRequest ΒΆ

type GitHubPullRequest struct {
	// contains filtered or unexported fields
}

GitHubPullRequest is a comment and diff service for GitHub PullRequest.

API:

https://developer.github.com/v3/pulls/comments/#create-a-comment
POST /repos/:owner/:repo/pulls/:number/comments

func NewGitHubPullReqest ΒΆ

func NewGitHubPullReqest(cli *github.Client, owner, repo string, pr int, sha string) *GitHubPullRequest

NewGitHubPullReqest returns a new GitHubPullRequest service.

func (*GitHubPullRequest) Diff ΒΆ

func (g *GitHubPullRequest) Diff() ([]byte, error)

Diff returns a diff of PullRequest. It runs `git diff` locally instead of diff_url of GitHub Pull Request because diff of diff_url is not suited for comment API in a sense that diff of diff_url is equivalent to `git diff --no-renames`, we want diff which is equivalent to `git diff --find-renames`.

func (*GitHubPullRequest) Flash ΒΆ

func (g *GitHubPullRequest) Flash() error

Flash posts comments which has not been posted yet.

func (*GitHubPullRequest) ListPostComments ΒΆ

func (g *GitHubPullRequest) ListPostComments() []*Comment

ListPostComments lists comments to post.

func (*GitHubPullRequest) Post ΒΆ

func (g *GitHubPullRequest) Post(c *Comment) error

Post accepts a comment and holds it. Flash method actually posts comments to GitHub in parallel.

func (*GitHubPullRequest) Strip ΒΆ

func (g *GitHubPullRequest) Strip() int

Strip returns 1 as a strip of git diff.

type Parser ΒΆ

type Parser interface {
	Parse(r io.Reader) ([]*CheckResult, error)
}

Parser is an interface which parses compilers, linters, or any tools results.

func NewCheckStyleParser ΒΆ

func NewCheckStyleParser() Parser

NewCheckStyleParser returns a new CheckStyleParser.

func NewErrorformatParser ΒΆ

func NewErrorformatParser(efm *errorformat.Errorformat) Parser

NewErrorformatParser returns a new ErrorformatParser.

type Reviewdog ΒΆ

type Reviewdog struct {
	// contains filtered or unexported fields
}

Reviewdog represents review dog application which parses result of compiler or linter, get diff and filter the results by diff, and report filterd results.

Example ΒΆ
difftext := `diff --git a/golint.old.go b/golint.new.go
index 34cacb9..a727dd3 100644
--- a/golint.old.go
+++ b/golint.new.go
@@ -2,6 +2,12 @@ package test
 
 var V int
 
+var NewError1 int
+
 // invalid func comment
 func F() {
 }
+
+// invalid func comment2
+func F2() {
+}
`
lintresult := `golint.new.go:3:5: exported var V should have comment or be unexported
golint.new.go:5:5: exported var NewError1 should have comment or be unexported
golint.new.go:7:1: comment on exported function F should be of the form "F ..."
golint.new.go:11:1: comment on exported function F2 should be of the form "F2 ..."
`
efm, _ := errorformat.NewErrorformat([]string{`%f:%l:%c: %m`})
p := NewErrorformatParser(efm)
c := NewCommentWriter(os.Stdout)
d := NewDiffString(difftext, 1)
app := NewReviewdog("tool name", p, c, d)
app.Run(strings.NewReader(lintresult))
Output:

golint.new.go:5:5: exported var NewError1 should have comment or be unexported
golint.new.go:11:1: comment on exported function F2 should be of the form "F2 ..."

func NewReviewdog ΒΆ

func NewReviewdog(toolname string, p Parser, c CommentService, d DiffService) *Reviewdog

NewReviewdog returns a new Reviewdog.

func (*Reviewdog) Run ΒΆ

func (w *Reviewdog) Run(r io.Reader) error

Run runs Reviewdog application.

Directories ΒΆ

Path Synopsis
cmd
Package diff provides a utility to parse unified diff.
Package diff provides a utility to parse unified diff.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL