2021-11-17 15:34:35 +03:00
// 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
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 [ ] string , 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 ) )
}
2021-11-27 14:59:51 +03:00
cmdErrors = append ( cmdErrors , passThroughCmd ( "gofmt" , substArgs ) )
2021-11-17 15:34:35 +03:00
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 )
}