<commit_before>package main

import (
	"crypto/tls"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"

	"golang.org/x/net/context" // "context"

	"golang.org/x/oauth2"

	"github.com/google/go-github/github"
	"github.com/haya14busa/errorformat/fmts"
	"github.com/haya14busa/reviewdog"
	"github.com/haya14busa/reviewdog/project"
	"github.com/mattn/go-shellwords"
)

const usageMessage = "" +
	`Usage:	reviewdog [flags]
	reviewdog accepts any compiler or linter results from stdin and filters
	them by diff for review. reviewdog also can posts the results as a comment to
	GitHub if you use reviewdog in CI service.
`

type option struct {
	diffCmd   string
	diffStrip int
	efms      strslice
	f         string // errorformat name
	list      bool   // list supported errorformat name
	name      string // tool name which is used in comment
	ci        string
	conf      string
}

// flags doc
const (
	diffCmdDoc   = `diff command (e.g. "git diff"). diff flag is ignored if you pass "ci" flag`
	diffStripDoc = "strip NUM leading components from diff file names (equivalent to `patch -p`) (default is 1 for git diff)"
	efmsDoc      = `list of errorformat (https://github.com/haya14busa/errorformat)`
	fDoc         = `format name (run -list to see supported format name) for input. It's also used as tool name in review comment if -name is empty`
	listDoc      = `list supported pre-defined format names which can be used as -f arg`
	nameDoc      = `tool name in review comment. -f is used as tool name if -name is empty`
	ciDoc        = `CI service (supported travis, circle-ci, droneio(OSS 0.4), common)

	GitHub/GitHub Enterprise:
		You need to set REVIEWDOG_GITHUB_API_TOKEN environment variable.
		Go to https://github.com/settings/tokens and create new Personal access token with repo scope.

		For GitHub Enterprise:
			export GITHUB_API="https://example.githubenterprise.com/api/v3"

		if you want to skip verifing SSL (please use this at your own risk)
			export REVIEWDOG_INSECURE_SKIP_VERIFY=true

	"common" requires following environment variables
		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)
`
	confDoc = `config file path`
)

var opt = &option{}

func init() {
	flag.StringVar(&opt.diffCmd, "diff", "", diffCmdDoc)
	flag.IntVar(&opt.diffStrip, "strip", 1, diffStripDoc)
	flag.Var(&opt.efms, "efm", efmsDoc)
	flag.StringVar(&opt.f, "f", "", fDoc)
	flag.BoolVar(&opt.list, "list", false, listDoc)
	flag.StringVar(&opt.name, "name", "", nameDoc)
	flag.StringVar(&opt.ci, "ci", "", ciDoc)
	flag.StringVar(&opt.conf, "conf", "reviewdog.yml", confDoc)
}

func usage() {
	fmt.Fprintln(os.Stderr, usageMessage)
	fmt.Fprintln(os.Stderr, "Flags:")
	flag.PrintDefaults()
	os.Exit(2)
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if err := run(os.Stdin, os.Stdout, opt); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run(r io.Reader, w io.Writer, opt *option) error {
	ctx := context.Background()

	if opt.list {
		return runList(w)
	}

	// assume it's project based run when both -efm ane -f are not specified
	isProject := len(opt.efms) == 0 && opt.f == ""

	var cs reviewdog.CommentService
	var ds reviewdog.DiffService

	if isProject {
		cs = reviewdog.NewUnifiedCommentWriter(w)
	} else {
		cs = reviewdog.NewRawCommentWriter(w)
	}

	if opt.ci != "" {
		if os.Getenv("REVIEWDOG_GITHUB_API_TOKEN") != "" {
			gs, isPR, err := githubService(ctx, opt.ci)
			if err != nil {
				return err
			}
			if !isPR {
				fmt.Fprintf(os.Stderr, "this is not PullRequest build. CI: %v\n", opt.ci)
				return nil
			}
			cs = reviewdog.MultiCommentService(gs, cs)
			ds = gs
		} else {
			fmt.Fprintf(os.Stderr, "REVIEWDOG_GITHUB_API_TOKEN is not set\n")
			return nil
		}
	} else {
		// local
		d, err := diffService(opt.diffCmd, opt.diffStrip)
		if err != nil {
			return err
		}
		ds = d
	}

	if isProject {
		b, err := ioutil.ReadFile(opt.conf)
		if err != nil {
			return fmt.Errorf("fail to open config: %v", err)
		}
		conf, err := project.Parse(b)
		if err != nil {
			return fmt.Errorf("config is invalid: %v", err)
		}
		return project.Run(ctx, conf, cs, ds)
	}

	p, err := reviewdog.NewParser(&reviewdog.ParserOpt{FormatName: opt.f, Errorformat: opt.efms})
	if err != nil {
		return fmt.Errorf("fail to create parser. use either -f or -efm: %v", err)
	}

	// tool name
	name := opt.name
	if name == "" && opt.f != "" {
		name = opt.f
	}

	app := reviewdog.NewReviewdog(name, p, cs, ds)
	if err := app.Run(r); err != nil {
		return err
	}
	return nil
}

func runList(w io.Writer) error {
	tabw := tabwriter.NewWriter(w, 0, 8, 0, '\t', 0)
	for _, f := range sortedFmts(fmts.DefinedFmts()) {
		fmt.Fprintf(tabw, "%s\t%s\t- %s\n", f.Name, f.Description, f.URL)
	}
	fmt.Fprintf(tabw, "%s\t%s\t- %s\n", "checkstyle", "checkstyle XML format", "http://checkstyle.sourceforge.net/")
	return tabw.Flush()
}

type byFmtName []*fmts.Fmt

func (p byFmtName) Len() int           { return len(p) }
func (p byFmtName) Less(i, j int) bool { return p[i].Name < p[j].Name }
func (p byFmtName) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func sortedFmts(fs fmts.Fmts) []*fmts.Fmt {
	r := make([]*fmts.Fmt, 0, len(fs))
	for _, f := range fs {
		r = append(r, f)
	}<commit_msg>fix gosimple warning<commit_after>package main

import (
	"crypto/tls"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"text/tabwriter"

	"golang.org/x/net/context" // "context"

	"golang.org/x/oauth2"

	"github.com/google/go-github/github"
	"github.com/haya14busa/errorformat/fmts"
	"github.com/haya14busa/reviewdog"
	"github.com/haya14busa/reviewdog/project"
	"github.com/mattn/go-shellwords"
)

const usageMessage = "" +
	`Usage:	reviewdog [flags]
	reviewdog accepts any compiler or linter results from stdin and filters
	them by diff for review. reviewdog also can posts the results as a comment to
	GitHub if you use reviewdog in CI service.
`

type option struct {
	diffCmd   string
	diffStrip int
	efms      strslice
	f         string // errorformat name
	list      bool   // list supported errorformat name
	name      string // tool name which is used in comment
	ci        string
	conf      string
}

// flags doc
const (
	diffCmdDoc   = `diff command (e.g. "git diff"). diff flag is ignored if you pass "ci" flag`
	diffStripDoc = "strip NUM leading components from diff file names (equivalent to `patch -p`) (default is 1 for git diff)"
	efmsDoc      = `list of errorformat (https://github.com/haya14busa/errorformat)`
	fDoc         = `format name (run -list to see supported format name) for input. It's also used as tool name in review comment if -name is empty`
	listDoc      = `list supported pre-defined format names which can be used as -f arg`
	nameDoc      = `tool name in review comment. -f is used as tool name if -name is empty`
	ciDoc        = `CI service (supported travis, circle-ci, droneio(OSS 0.4), common)

	GitHub/GitHub Enterprise:
		You need to set REVIEWDOG_GITHUB_API_TOKEN environment variable.
		Go to https://github.com/settings/tokens and create new Personal access token with repo scope.

		For GitHub Enterprise:
			export GITHUB_API="https://example.githubenterprise.com/api/v3"

		if you want to skip verifing SSL (please use this at your own risk)
			export REVIEWDOG_INSECURE_SKIP_VERIFY=true

	"common" requires following environment variables
		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)
`
	confDoc = `config file path`
)

var opt = &option{}

func init() {
	flag.StringVar(&opt.diffCmd, "diff", "", diffCmdDoc)
	flag.IntVar(&opt.diffStrip, "strip", 1, diffStripDoc)
	flag.Var(&opt.efms, "efm", efmsDoc)
	flag.StringVar(&opt.f, "f", "", fDoc)
	flag.BoolVar(&opt.list, "list", false, listDoc)
	flag.StringVar(&opt.name, "name", "", nameDoc)
	flag.StringVar(&opt.ci, "ci", "", ciDoc)
	flag.StringVar(&opt.conf, "conf", "reviewdog.yml", confDoc)
}

func usage() {
	fmt.Fprintln(os.Stderr, usageMessage)
	fmt.Fprintln(os.Stderr, "Flags:")
	flag.PrintDefaults()
	os.Exit(2)
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if err := run(os.Stdin, os.Stdout, opt); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run(r io.Reader, w io.Writer, opt *option) error {
	ctx := context.Background()

	if opt.list {
		return runList(w)
	}

	// assume it's project based run when both -efm ane -f are not specified
	isProject := len(opt.efms) == 0 && opt.f == ""

	var cs reviewdog.CommentService
	var ds reviewdog.DiffService

	if isProject {
		cs = reviewdog.NewUnifiedCommentWriter(w)
	} else {
		cs = reviewdog.NewRawCommentWriter(w)
	}

	if opt.ci != "" {
		if os.Getenv("REVIEWDOG_GITHUB_API_TOKEN") != "" {
			gs, isPR, err := githubService(ctx, opt.ci)
			if err != nil {
				return err
			}
			if !isPR {
				fmt.Fprintf(os.Stderr, "this is not PullRequest build. CI: %v\n", opt.ci)
				return nil
			}
			cs = reviewdog.MultiCommentService(gs, cs)
			ds = gs
		} else {
			fmt.Fprintf(os.Stderr, "REVIEWDOG_GITHUB_API_TOKEN is not set\n")
			return nil
		}
	} else {
		// local
		d, err := diffService(opt.diffCmd, opt.diffStrip)
		if err != nil {
			return err
		}
		ds = d
	}

	if isProject {
		b, err := ioutil.ReadFile(opt.conf)
		if err != nil {
			return fmt.Errorf("fail to open config: %v", err)
		}
		conf, err := project.Parse(b)
		if err != nil {
			return fmt.Errorf("config is invalid: %v", err)
		}
		return project.Run(ctx, conf, cs, ds)
	}

	p, err := reviewdog.NewParser(&reviewdog.ParserOpt{FormatName: opt.f, Errorformat: opt.efms})
	if err != nil {
		return fmt.Errorf("fail to create parser. use either -f or -efm: %v", err)
	}

	// tool name
	name := opt.name
	if name == "" && opt.f != "" {
		name = opt.f
	}

	return reviewdog.NewReviewdog(name, p, cs, ds).Run(r)
}

func runList(w io.Writer) error {
	tabw := tabwriter.NewWriter(w, 0, 8, 0, '\t', 0)
	for _, f := range sortedFmts(fmts.DefinedFmts()) {
		fmt.Fprintf(tabw, "%s\t%s\t- %s\n", f.Name, f.Description, f.URL)
	}
	fmt.Fprintf(tabw, "%s\t%s\t- %s\n", "checkstyle", "checkstyle XML format", "http://checkstyle.sourceforge.net/")
	return tabw.Flush()
}

type byFmtName []*fmts.Fmt

func (p byFmtName) Len() int           { return len(p) }
func (p byFmtName) Less(i, j int) bool { return p[i].Name < p[j].Name }
func (p byFmtName) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func sortedFmts(fs fmts.Fmts) []*fmts.Fmt {
	r := make([]*fmts.Fmt, 0, len(fs))
	for _, f := range fs {
		r = append(r, f)
	}
{'fraction_non_alphanumeric': 0.14083865086599817, 'fraction_numerical': 0.005651777575205105, 'mean_word_length': 3.420225624496374, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 10, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '880259', 'n_tokens_mistral': 4394, 'n_tokens_neox': 4154, 'n_words': 1159}