mirror of
https://github.com/go-gitea/gitea
synced 2024-08-18 21:31:02 +02:00
285 lines
6.9 KiB
Go
285 lines
6.9 KiB
Go
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
//go:build ignore
|
|
// +build ignore
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"code.gitea.io/gitea/build/codeformat"
|
|
)
|
|
|
|
// Windows has a limitation for command line arguments, the size can not exceed 32KB.
|
|
// So we have to feed the files to some tools (like gofmt/misspell) batch by batch
|
|
|
|
// We also introduce a `gitea-fmt` command, it does better import formatting than gofmt/goimports. `gitea-fmt` calls `gofmt` internally.
|
|
|
|
var optionLogVerbose bool
|
|
|
|
func logVerbose(msg string, args ...interface{}) {
|
|
if optionLogVerbose {
|
|
log.Printf(msg, args...)
|
|
}
|
|
}
|
|
|
|
func passThroughCmd(cmd string, args []string) error {
|
|
foundCmd, err := exec.LookPath(cmd)
|
|
if err != nil {
|
|
log.Fatalf("can not find cmd: %s", cmd)
|
|
}
|
|
c := exec.Cmd{
|
|
Path: foundCmd,
|
|
Args: args,
|
|
Stdin: os.Stdin,
|
|
Stdout: os.Stdout,
|
|
Stderr: os.Stderr,
|
|
}
|
|
return c.Run()
|
|
}
|
|
|
|
type fileCollector struct {
|
|
dirs []string
|
|
includePatterns []*regexp.Regexp
|
|
excludePatterns []*regexp.Regexp
|
|
batchSize int
|
|
}
|
|
|
|
func newFileCollector(fileFilter string, batchSize int) (*fileCollector, error) {
|
|
co := &fileCollector{batchSize: batchSize}
|
|
if fileFilter == "go-own" {
|
|
co.dirs = []string{
|
|
"build",
|
|
"cmd",
|
|
"contrib",
|
|
"integrations",
|
|
"models",
|
|
"modules",
|
|
"routers",
|
|
"services",
|
|
"tools",
|
|
}
|
|
co.includePatterns = append(co.includePatterns, regexp.MustCompile(`.*\.go$`))
|
|
|
|
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`.*\bbindata\.go$`))
|
|
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`integrations/gitea-repositories-meta`))
|
|
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`integrations/migration-test`))
|
|
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`modules/git/tests`))
|
|
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`models/fixtures`))
|
|
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`models/migrations/fixtures`))
|
|
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`services/gitdiff/testdata`))
|
|
}
|
|
|
|
if co.dirs == nil {
|
|
return nil, fmt.Errorf("unknown file-filter: %s", fileFilter)
|
|
}
|
|
return co, nil
|
|
}
|
|
|
|
func (fc *fileCollector) matchPatterns(path string, regexps []*regexp.Regexp) bool {
|
|
path = strings.ReplaceAll(path, "\\", "/")
|
|
for _, re := range regexps {
|
|
if re.MatchString(path) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (fc *fileCollector) collectFiles() (res [][]string, err error) {
|
|
var batch []string
|
|
for _, dir := range fc.dirs {
|
|
err = filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
|
|
include := len(fc.includePatterns) == 0 || fc.matchPatterns(path, fc.includePatterns)
|
|
exclude := fc.matchPatterns(path, fc.excludePatterns)
|
|
process := include && !exclude
|
|
if !process {
|
|
if d.IsDir() {
|
|
if exclude {
|
|
logVerbose("exclude dir %s", path)
|
|
return filepath.SkipDir
|
|
}
|
|
// for a directory, if it is not excluded explicitly, we should walk into
|
|
return nil
|
|
}
|
|
// for a file, we skip it if it shouldn't be processed
|
|
logVerbose("skip process %s", path)
|
|
return nil
|
|
}
|
|
if d.IsDir() {
|
|
// skip dir, we don't add dirs to the file list now
|
|
return nil
|
|
}
|
|
if len(batch) >= fc.batchSize {
|
|
res = append(res, batch)
|
|
batch = nil
|
|
}
|
|
batch = append(batch, path)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
res = append(res, batch)
|
|
return res, nil
|
|
}
|
|
|
|
// substArgFiles expands the {file-list} to a real file list for commands
|
|
func substArgFiles(args, files []string) []string {
|
|
for i, s := range args {
|
|
if s == "{file-list}" {
|
|
newArgs := append(args[:i], files...)
|
|
newArgs = append(newArgs, args[i+1:]...)
|
|
return newArgs
|
|
}
|
|
}
|
|
return args
|
|
}
|
|
|
|
func exitWithCmdErrors(subCmd string, subArgs []string, cmdErrors []error) {
|
|
for _, err := range cmdErrors {
|
|
if err != nil {
|
|
if exitError, ok := err.(*exec.ExitError); ok {
|
|
exitCode := exitError.ExitCode()
|
|
log.Printf("run command failed (code=%d): %s %v", exitCode, subCmd, subArgs)
|
|
os.Exit(exitCode)
|
|
} else {
|
|
log.Fatalf("run command failed (err=%s) %s %v", err, subCmd, subArgs)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func parseArgs() (mainOptions map[string]string, subCmd string, subArgs []string) {
|
|
mainOptions = map[string]string{}
|
|
for i := 1; i < len(os.Args); i++ {
|
|
arg := os.Args[i]
|
|
if arg == "" {
|
|
break
|
|
}
|
|
if arg[0] == '-' {
|
|
arg = strings.TrimPrefix(arg, "-")
|
|
arg = strings.TrimPrefix(arg, "-")
|
|
fields := strings.SplitN(arg, "=", 2)
|
|
if len(fields) == 1 {
|
|
mainOptions[fields[0]] = "1"
|
|
} else {
|
|
mainOptions[fields[0]] = fields[1]
|
|
}
|
|
} else {
|
|
subCmd = arg
|
|
subArgs = os.Args[i+1:]
|
|
break
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func showUsage() {
|
|
fmt.Printf(`Usage: %[1]s [options] {command} [arguments]
|
|
|
|
Options:
|
|
--verbose
|
|
--file-filter=go-own
|
|
--batch-size=100
|
|
|
|
Commands:
|
|
%[1]s gofmt ...
|
|
%[1]s misspell ...
|
|
|
|
Arguments:
|
|
{file-list} the file list
|
|
|
|
Example:
|
|
%[1]s gofmt -s -d {file-list}
|
|
|
|
`, "file-batch-exec")
|
|
}
|
|
|
|
func newFileCollectorFromMainOptions(mainOptions map[string]string) (fc *fileCollector, err error) {
|
|
fileFilter := mainOptions["file-filter"]
|
|
if fileFilter == "" {
|
|
fileFilter = "go-own"
|
|
}
|
|
batchSize, _ := strconv.Atoi(mainOptions["batch-size"])
|
|
if batchSize == 0 {
|
|
batchSize = 100
|
|
}
|
|
|
|
return newFileCollector(fileFilter, batchSize)
|
|
}
|
|
|
|
func containsString(a []string, s string) bool {
|
|
for _, v := range a {
|
|
if v == s {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func giteaFormatGoImports(files []string) error {
|
|
for _, file := range files {
|
|
if err := codeformat.FormatGoImports(file); err != nil {
|
|
log.Printf("failed to format go imports: %s, err=%v", file, err)
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
mainOptions, subCmd, subArgs := parseArgs()
|
|
if subCmd == "" {
|
|
showUsage()
|
|
os.Exit(1)
|
|
}
|
|
optionLogVerbose = mainOptions["verbose"] != ""
|
|
|
|
fc, err := newFileCollectorFromMainOptions(mainOptions)
|
|
if err != nil {
|
|
log.Fatalf("can not create file collector: %s", err.Error())
|
|
}
|
|
|
|
fileBatches, err := fc.collectFiles()
|
|
if err != nil {
|
|
log.Fatalf("can not collect files: %s", err.Error())
|
|
}
|
|
|
|
processed := 0
|
|
var cmdErrors []error
|
|
for _, files := range fileBatches {
|
|
if len(files) == 0 {
|
|
break
|
|
}
|
|
substArgs := substArgFiles(subArgs, files)
|
|
logVerbose("batch cmd: %s %v", subCmd, substArgs)
|
|
switch subCmd {
|
|
case "gitea-fmt":
|
|
if containsString(subArgs, "-w") {
|
|
cmdErrors = append(cmdErrors, giteaFormatGoImports(files))
|
|
}
|
|
cmdErrors = append(cmdErrors, passThroughCmd("gofmt", substArgs))
|
|
case "misspell":
|
|
cmdErrors = append(cmdErrors, passThroughCmd("misspell", substArgs))
|
|
default:
|
|
log.Fatalf("unknown cmd: %s %v", subCmd, subArgs)
|
|
}
|
|
processed += len(files)
|
|
}
|
|
|
|
logVerbose("processed %d files", processed)
|
|
exitWithCmdErrors(subCmd, subArgs, cmdErrors)
|
|
}
|