2021-11-17 15:34:35 +03:00
// Copyright 2021 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2021-11-17 15:34:35 +03:00
//go: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.
2022-10-16 11:11:17 +03:00
// So we have to feed the files to some tools (like gofmt) batch by batch
2021-11-17 15:34:35 +03:00
2022-01-13 05:58:46 +03:00
// We also introduce a `gitea-fmt` command, it does better import formatting than gofmt/goimports. `gitea-fmt` calls `gofmt` internally.
2021-11-17 15:34:35 +03:00
var optionLogVerbose bool
2023-07-04 21:36:08 +03:00
func logVerbose ( msg string , args ... any ) {
2021-11-17 15:34:35 +03:00
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 ,
2022-03-16 05:50:26 +03:00
Args : append ( [ ] string { cmd } , args ... ) ,
2021-11-17 15:34:35 +03:00
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" ,
2022-09-02 22:18:23 +03:00
"tests" ,
2021-11-17 15:34:35 +03:00
"models" ,
"modules" ,
"routers" ,
"services" ,
}
co . includePatterns = append ( co . includePatterns , regexp . MustCompile ( ` .*\.go$ ` ) )
co . excludePatterns = append ( co . excludePatterns , regexp . MustCompile ( ` .*\bbindata\.go$ ` ) )
2022-09-02 22:18:23 +03:00
co . excludePatterns = append ( co . excludePatterns , regexp . MustCompile ( ` tests/gitea-repositories-meta ` ) )
co . excludePatterns = append ( co . excludePatterns , regexp . MustCompile ( ` tests/integration/migration-test ` ) )
2021-11-17 15:34:35 +03:00
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
2022-01-20 20:46:10 +03:00
func substArgFiles ( args , files [ ] string ) [ ] string {
2021-11-17 15:34:35 +03:00
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 ...
Arguments :
{ file - list } the file list
Example :
% [ 1 ] s gofmt - s - d { file - list }
` , "file-batch-exec" )
}
2022-10-15 23:10:25 +03:00
func getGoVersion ( ) string {
goModFile , err := os . ReadFile ( "go.mod" )
if err != nil {
log . Fatalf ( ` Faild to read "go.mod": %v ` , err )
os . Exit ( 1 )
}
goModVersionRegex := regexp . MustCompile ( ` go \d+\.\d+ ` )
goModVersionLine := goModVersionRegex . Find ( goModFile )
return string ( goModVersionLine [ 3 : ] )
}
2021-11-17 15:34:35 +03:00
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
}
2022-10-16 11:11:17 +03:00
func giteaFormatGoImports ( files [ ] string , doWriteFile bool ) error {
2021-11-17 15:34:35 +03:00
for _ , file := range files {
2022-10-16 11:11:17 +03:00
if err := codeformat . FormatGoImports ( file , doWriteFile ) ; err != nil {
2021-11-17 15:34:35 +03:00
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" :
2022-02-06 18:44:30 +03:00
if containsString ( subArgs , "-d" ) {
log . Print ( "the -d option is not supported by gitea-fmt" )
2021-11-17 15:34:35 +03:00
}
2022-10-16 11:11:17 +03:00
cmdErrors = append ( cmdErrors , giteaFormatGoImports ( files , containsString ( subArgs , "-w" ) ) )
2022-10-15 23:10:25 +03:00
cmdErrors = append ( cmdErrors , passThroughCmd ( "go" , append ( [ ] string { "run" , os . Getenv ( "GOFUMPT_PACKAGE" ) , "-extra" , "-lang" , getGoVersion ( ) } , substArgs ... ) ) )
2021-11-17 15:34:35 +03:00
default :
log . Fatalf ( "unknown cmd: %s %v" , subCmd , subArgs )
}
processed += len ( files )
}
logVerbose ( "processed %d files" , processed )
exitWithCmdErrors ( subCmd , subArgs , cmdErrors )
}