diff --git a/.github/.testcoverage-local.yml b/.github/.testcoverage-local.yml index 8ce0151..0232684 100644 --- a/.github/.testcoverage-local.yml +++ b/.github/.testcoverage-local.yml @@ -11,4 +11,5 @@ override: threshold: 66 exclude: paths: - - main\.go$ \ No newline at end of file + - main\.go$ + - main_config\.go$ \ No newline at end of file diff --git a/.github/.testcoverage.yml b/.github/.testcoverage.yml index a511531..b78e284 100644 --- a/.github/.testcoverage.yml +++ b/.github/.testcoverage.yml @@ -6,4 +6,5 @@ threshold: total: 100 exclude: paths: - - main\.go$ \ No newline at end of file + - main\.go$ + - main_config\.go$ \ No newline at end of file diff --git a/.github/workflows/action-test.yml b/.github/workflows/action-test.yml index e105537..3ee892d 100644 --- a/.github/workflows/action-test.yml +++ b/.github/workflows/action-test.yml @@ -125,4 +125,22 @@ jobs: config: ./.github/workflows/testdata/total100.yml threshold-file: 0 threshold-package: 0 - threshold-total: 0 \ No newline at end of file + threshold-total: 0 + + ## Test 6 + + - name: "test: debug output" + uses: ./ + id: test-6 + continue-on-error: true + with: + profile: unexistant-profile.out + debug: true + threshold-file: 0 + threshold-package: 0 + threshold-total: 100 + + - name: "check: test should have failed" + if: steps.test-4.outcome != 'failure' + shell: bash + run: echo "Previous step should have failed" && exit 1 \ No newline at end of file diff --git a/Makefile b/Makefile index 53ad7f3..9c49640 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ test: # Runs test coverage check .PHONY: check-coverage check-coverage: test - go run ./main.go --config=./.github/.testcoverage-local.yml + go run ./ --config=./.github/.testcoverage-local.yml # View coverage profile .PHONY: view-coverage diff --git a/action.yml b/action.yml index e422084..009157f 100644 --- a/action.yml +++ b/action.yml @@ -13,6 +13,11 @@ inputs: required: false default: "" type: string + debug: + description: Prints additional debugging output when running action. + required: false + default: false + type: boolean # Individual properties profile: @@ -138,6 +143,7 @@ runs: - --config=${{ inputs.config || '''''' }} - --profile=${{ inputs.profile || '''''' }} - --source-dir=${{ inputs.source-dir || '''''' }} + - --debug=${{ inputs.debug }} - --github-action-output=true - --threshold-file=${{ inputs.threshold-file }} - --threshold-package=${{ inputs.threshold-package }} diff --git a/go.mod b/go.mod index 48cfb8e..935470a 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/google/go-github/v56 v56.0.0 github.com/johannesboyne/gofakes3 v0.0.0-20230914150226-f005f5cc03aa github.com/narqo/go-badge v0.0.0-20230821190521-c9a75c019a59 + github.com/rs/zerolog v1.34.0 github.com/stretchr/testify v1.10.0 golang.org/x/tools v0.26.0 gopkg.in/yaml.v3 v3.0.1 @@ -22,11 +23,14 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/kr/pretty v0.3.1 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500 // indirect golang.org/x/image v0.18.0 // indirect + golang.org/x/sys v0.26.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 5b576e2..9e062d3 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,12 @@ github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oy github.com/aws/aws-sdk-go v1.44.256/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go v1.49.4 h1:qiXsqEeLLhdLgUIyfr5ot+N/dGPWALmtM1SetRmbUlY= github.com/aws/aws-sdk-go v1.49.4/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -31,6 +33,11 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/narqo/go-badge v0.0.0-20230821190521-c9a75c019a59 h1:kbREB9muGo4sHLoZJD/E/IV8yK3Y15eEA9mYi/ztRsk= github.com/narqo/go-badge v0.0.0-20230821190521-c9a75c019a59/go.mod h1:m9BzkaxwU4IfPQi9ko23cmuFltayFe8iS0dlRlnEWiM= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -40,6 +47,9 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500 h1:WnNuhiq+FOY3jNj6JXFT+eLN3CQ/oPIsDPRanvwsmbI= @@ -77,9 +87,14 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/main.go b/main.go index 2d3d918..8ce8f3b 100644 --- a/main.go +++ b/main.go @@ -1,14 +1,11 @@ package main import ( - "errors" "fmt" "os" - "strings" - - "github.com/alexflint/go-arg" "github.com/vladopajic/go-test-coverage/v2/pkg/testcoverage" + "github.com/vladopajic/go-test-coverage/v2/pkg/testcoverage/logger" ) const ( @@ -16,153 +13,7 @@ const ( Name = "go-test-coverage" ) -const ( - // default value of string variables passed by CI - ciDefaultString = `''` - // default value of int variables passed by CI - ciDefaultInt = -1 -) - -type args struct { - ConfigPath string `arg:"-c,--config"` - Profile string `arg:"-p,--profile" help:"path to coverage profile"` - LocalPrefix string `arg:"-l,--local-prefix"` // deprecated - SourceDir string `arg:"-s,--source-dir"` - GithubActionOutput bool `arg:"-o,--github-action-output"` - ThresholdFile int `arg:"-f,--threshold-file"` - ThresholdPackage int `arg:"-k,--threshold-package"` - ThresholdTotal int `arg:"-t,--threshold-total"` - - BreakdownFileName string `arg:"--breakdown-file-name"` - DiffBaseBreakdownFileName string `arg:"--diff-base-breakdown-file-name"` - - BadgeFileName string `arg:"-b,--badge-file-name"` - - CDNKey string `arg:"--cdn-key"` - CDNSecret string `arg:"--cdn-secret"` - CDNRegion string `arg:"--cdn-region"` - CDNEndpoint string `arg:"--cdn-endpoint"` - CDNFileName string `arg:"--cdn-file-name"` - CDNBucketName string `arg:"--cdn-bucket-name"` - CDNForcePathStyle bool `arg:"--cdn-force-path-style"` - - GitToken string `arg:"--git-token"` - GitRepository string `arg:"--git-repository"` - GitBranch string `arg:"--git-branch"` - GitFileName string `arg:"--git-file-name"` -} - -func newArgs() args { - return args{ - ConfigPath: ciDefaultString, - Profile: ciDefaultString, - LocalPrefix: ciDefaultString, - SourceDir: ciDefaultString, - GithubActionOutput: false, - ThresholdFile: ciDefaultInt, - ThresholdPackage: ciDefaultInt, - ThresholdTotal: ciDefaultInt, - - BreakdownFileName: ciDefaultString, - DiffBaseBreakdownFileName: ciDefaultString, - - // Badge - BadgeFileName: ciDefaultString, - - // CDN - CDNKey: ciDefaultString, - CDNSecret: ciDefaultString, - CDNRegion: ciDefaultString, - CDNEndpoint: ciDefaultString, - CDNFileName: ciDefaultString, - CDNBucketName: ciDefaultString, - CDNForcePathStyle: false, - - // Git - GitToken: ciDefaultString, - GitRepository: ciDefaultString, - GitBranch: ciDefaultString, - GitFileName: ciDefaultString, - } -} - -func (*args) Version() string { - return Name + " " + Version -} - -//nolint:cyclop,maintidx,mnd,funlen // relax -func (a *args) overrideConfig(cfg testcoverage.Config) (testcoverage.Config, error) { - if !isCIDefaultString(a.Profile) { - cfg.Profile = a.Profile - } - - if a.GithubActionOutput { - cfg.GithubActionOutput = true - } - - if !isCIDefaultString(a.LocalPrefix) { - cfg.LocalPrefixDeprecated = a.LocalPrefix - } - - if !isCIDefaultString(a.SourceDir) { - cfg.SourceDir = a.SourceDir - } - - if !isCIDefaultInt(a.ThresholdFile) { - cfg.Threshold.File = a.ThresholdFile - } - - if !isCIDefaultInt(a.ThresholdPackage) { - cfg.Threshold.Package = a.ThresholdPackage - } - - if !isCIDefaultInt(a.ThresholdTotal) { - cfg.Threshold.Total = a.ThresholdTotal - } - - if !isCIDefaultString(a.BreakdownFileName) { - cfg.BreakdownFileName = a.BreakdownFileName - } - - if !isCIDefaultString(a.DiffBaseBreakdownFileName) { - cfg.Diff.BaseBreakdownFileName = a.DiffBaseBreakdownFileName - } - - if !isCIDefaultString(a.BadgeFileName) { - cfg.Badge.FileName = a.BadgeFileName - } - - if !isCIDefaultString(a.CDNSecret) { - cfg.Badge.CDN.Secret = a.CDNSecret - cfg.Badge.CDN.Key = escapeCiDefaultString(a.CDNKey) - cfg.Badge.CDN.Region = escapeCiDefaultString(a.CDNRegion) - cfg.Badge.CDN.FileName = escapeCiDefaultString(a.CDNFileName) - cfg.Badge.CDN.BucketName = escapeCiDefaultString(a.CDNBucketName) - cfg.Badge.CDN.ForcePathStyle = a.CDNForcePathStyle - - if !isCIDefaultString(a.CDNEndpoint) { - cfg.Badge.CDN.Endpoint = a.CDNEndpoint - } - } - - if !isCIDefaultString(a.GitToken) { - cfg.Badge.Git.Token = a.GitToken - cfg.Badge.Git.Branch = escapeCiDefaultString(a.GitBranch) - cfg.Badge.Git.FileName = escapeCiDefaultString(a.GitFileName) - - parts := strings.Split(escapeCiDefaultString(a.GitRepository), "/") - if len(parts) != 2 { - return cfg, errors.New("--git-repository flag should have format {owner}/{repository}") - } - - cfg.Badge.Git.Owner = parts[0] - cfg.Badge.Git.Repository = parts[1] - } - - return cfg, nil -} - -//nolint:forbidigo // relax +//nolint:forbidigo,wsl // relax func main() { cfg, err := readConfig() if err != nil { @@ -170,48 +21,18 @@ func main() { os.Exit(1) } - pass := testcoverage.Check(os.Stdout, cfg) - if !pass { - os.Exit(1) - } -} - -func readConfig() (testcoverage.Config, error) { - cmdArgs := newArgs() - arg.MustParse(&cmdArgs) + logger.Init() - cfg := testcoverage.Config{} - - // Load config from file - if !isCIDefaultString(cmdArgs.ConfigPath) { - err := testcoverage.ConfigFromFile(&cfg, cmdArgs.ConfigPath) - if err != nil { - return testcoverage.Config{}, fmt.Errorf("failed loading config from file: %w", err) - } - } - - // Override config with values from args - cfg, err := cmdArgs.overrideConfig(cfg) + pass, err := testcoverage.Check(os.Stdout, cfg) if err != nil { - return testcoverage.Config{}, fmt.Errorf("argument is not valid: %w", err) - } - - // Validate config - if err := cfg.Validate(); err != nil { - return testcoverage.Config{}, fmt.Errorf("config file is not valid: %w", err) + fmt.Println("Running coverage check failed.") + if cfg.GithubActionOutput { + fmt.Printf("Please set `debug: true` input to see detailed output.") + } else { + fmt.Println("Please use `--debug=true` flag to see detailed output.") + } } - - return cfg, nil -} - -func isCIDefaultString(v string) bool { return v == ciDefaultString } - -func isCIDefaultInt(v int) bool { return v == ciDefaultInt } - -func escapeCiDefaultString(v string) string { - if v == ciDefaultString { - return "" + if !pass || err != nil { + os.Exit(1) } - - return v } diff --git a/main_config.go b/main_config.go new file mode 100644 index 0000000..c1b5707 --- /dev/null +++ b/main_config.go @@ -0,0 +1,203 @@ +package main + +import ( + "errors" + "fmt" + "strings" + + "github.com/alexflint/go-arg" + + "github.com/vladopajic/go-test-coverage/v2/pkg/testcoverage" +) + +const ( + // default value of string variables passed by CI + ciDefaultString = `''` + // default value of int variables passed by CI + ciDefaultInt = -1 +) + +type args struct { + ConfigPath string `arg:"-c,--config"` + Profile string `arg:"-p,--profile" help:"path to coverage profile"` + Debug bool `arg:"-d,--debug"` + LocalPrefix string `arg:"-l,--local-prefix"` // deprecated + SourceDir string `arg:"-s,--source-dir"` + GithubActionOutput bool `arg:"-o,--github-action-output"` + ThresholdFile int `arg:"-f,--threshold-file"` + ThresholdPackage int `arg:"-k,--threshold-package"` + ThresholdTotal int `arg:"-t,--threshold-total"` + + BreakdownFileName string `arg:"--breakdown-file-name"` + DiffBaseBreakdownFileName string `arg:"--diff-base-breakdown-file-name"` + + BadgeFileName string `arg:"-b,--badge-file-name"` + + CDNKey string `arg:"--cdn-key"` + CDNSecret string `arg:"--cdn-secret"` + CDNRegion string `arg:"--cdn-region"` + CDNEndpoint string `arg:"--cdn-endpoint"` + CDNFileName string `arg:"--cdn-file-name"` + CDNBucketName string `arg:"--cdn-bucket-name"` + CDNForcePathStyle bool `arg:"--cdn-force-path-style"` + + GitToken string `arg:"--git-token"` + GitRepository string `arg:"--git-repository"` + GitBranch string `arg:"--git-branch"` + GitFileName string `arg:"--git-file-name"` +} + +func newArgs() args { + return args{ + ConfigPath: ciDefaultString, + Profile: ciDefaultString, + Debug: false, + LocalPrefix: ciDefaultString, + SourceDir: ciDefaultString, + GithubActionOutput: false, + ThresholdFile: ciDefaultInt, + ThresholdPackage: ciDefaultInt, + ThresholdTotal: ciDefaultInt, + + BreakdownFileName: ciDefaultString, + DiffBaseBreakdownFileName: ciDefaultString, + + // Badge + BadgeFileName: ciDefaultString, + + // CDN + CDNKey: ciDefaultString, + CDNSecret: ciDefaultString, + CDNRegion: ciDefaultString, + CDNEndpoint: ciDefaultString, + CDNFileName: ciDefaultString, + CDNBucketName: ciDefaultString, + CDNForcePathStyle: false, + + // Git + GitToken: ciDefaultString, + GitRepository: ciDefaultString, + GitBranch: ciDefaultString, + GitFileName: ciDefaultString, + } +} + +func (*args) Version() string { + return Name + " " + Version +} + +//nolint:cyclop,maintidx,mnd,funlen // relax +func (a *args) overrideConfig(cfg testcoverage.Config) (testcoverage.Config, error) { + if !isCIDefaultString(a.Profile) { + cfg.Profile = a.Profile + } + + if a.Debug { + cfg.Debug = true + } + + if a.GithubActionOutput { + cfg.GithubActionOutput = true + } + + if !isCIDefaultString(a.LocalPrefix) { + cfg.LocalPrefixDeprecated = a.LocalPrefix + } + + if !isCIDefaultString(a.SourceDir) { + cfg.SourceDir = a.SourceDir + } + + if !isCIDefaultInt(a.ThresholdFile) { + cfg.Threshold.File = a.ThresholdFile + } + + if !isCIDefaultInt(a.ThresholdPackage) { + cfg.Threshold.Package = a.ThresholdPackage + } + + if !isCIDefaultInt(a.ThresholdTotal) { + cfg.Threshold.Total = a.ThresholdTotal + } + + if !isCIDefaultString(a.BreakdownFileName) { + cfg.BreakdownFileName = a.BreakdownFileName + } + + if !isCIDefaultString(a.DiffBaseBreakdownFileName) { + cfg.Diff.BaseBreakdownFileName = a.DiffBaseBreakdownFileName + } + + if !isCIDefaultString(a.BadgeFileName) { + cfg.Badge.FileName = a.BadgeFileName + } + + if !isCIDefaultString(a.CDNSecret) { + cfg.Badge.CDN.Secret = a.CDNSecret + cfg.Badge.CDN.Key = escapeCiDefaultString(a.CDNKey) + cfg.Badge.CDN.Region = escapeCiDefaultString(a.CDNRegion) + cfg.Badge.CDN.FileName = escapeCiDefaultString(a.CDNFileName) + cfg.Badge.CDN.BucketName = escapeCiDefaultString(a.CDNBucketName) + cfg.Badge.CDN.ForcePathStyle = a.CDNForcePathStyle + + if !isCIDefaultString(a.CDNEndpoint) { + cfg.Badge.CDN.Endpoint = a.CDNEndpoint + } + } + + if !isCIDefaultString(a.GitToken) { + cfg.Badge.Git.Token = a.GitToken + cfg.Badge.Git.Branch = escapeCiDefaultString(a.GitBranch) + cfg.Badge.Git.FileName = escapeCiDefaultString(a.GitFileName) + + parts := strings.Split(escapeCiDefaultString(a.GitRepository), "/") + if len(parts) != 2 { + return cfg, errors.New("--git-repository flag should have format {owner}/{repository}") + } + + cfg.Badge.Git.Owner = parts[0] + cfg.Badge.Git.Repository = parts[1] + } + + return cfg, nil +} + +func readConfig() (testcoverage.Config, error) { + cmdArgs := newArgs() + arg.MustParse(&cmdArgs) + + cfg := testcoverage.Config{} + + // Load config from file + if !isCIDefaultString(cmdArgs.ConfigPath) { + err := testcoverage.ConfigFromFile(&cfg, cmdArgs.ConfigPath) + if err != nil { + return testcoverage.Config{}, fmt.Errorf("failed loading config from file: %w", err) + } + } + + // Override config with values from args + cfg, err := cmdArgs.overrideConfig(cfg) + if err != nil { + return testcoverage.Config{}, fmt.Errorf("argument is not valid: %w", err) + } + + // Validate config + if err := cfg.Validate(); err != nil { + return testcoverage.Config{}, fmt.Errorf("config file is not valid: %w", err) + } + + return cfg, nil +} + +func isCIDefaultString(v string) bool { return v == ciDefaultString } + +func isCIDefaultInt(v int) bool { return v == ciDefaultInt } + +func escapeCiDefaultString(v string) string { + if v == ciDefaultString { + return "" + } + + return v +} diff --git a/pkg/testcoverage/check.go b/pkg/testcoverage/check.go index c563a2b..be01885 100644 --- a/pkg/testcoverage/check.go +++ b/pkg/testcoverage/check.go @@ -9,25 +9,45 @@ import ( "strings" "github.com/vladopajic/go-test-coverage/v2/pkg/testcoverage/coverage" + "github.com/vladopajic/go-test-coverage/v2/pkg/testcoverage/logger" ) -func Check(w io.Writer, cfg Config) bool { +//nolint:maintidx // relax +func Check(wout io.Writer, cfg Config) (bool, error) { + buffer := &bytes.Buffer{} + w := bufio.NewWriter(buffer) + //nolint:errcheck // relax + defer func() { + if cfg.Debug { + wout.Write(logger.Bytes()) + wout.Write([]byte("-------------------------\n\n")) + } + + w.Flush() + wout.Write(buffer.Bytes()) + }() + + handleErr := func(err error, msg string) (bool, error) { + logger.L.Error().Err(err).Msg(msg) + return false, fmt.Errorf("%s: %w", msg, err) + } + + logger.L.Info().Msg("running check...") + logger.L.Info().Any("config", cfg).Msg("using configuration") + currentStats, err := GenerateCoverageStats(cfg) if err != nil { - fmt.Fprintf(w, "failed to generate coverage statistics: %v\n", err) - return false + return handleErr(err, "failed to generate coverage statistics") } err = saveCoverageBreakdown(cfg, currentStats) if err != nil { - fmt.Fprintf(w, "failed to save coverage breakdown: %v\n", err) - return false + return handleErr(err, "failed to save coverage breakdown") } baseStats, err := loadBaseCoverageBreakdown(cfg) if err != nil { - fmt.Fprintf(w, "failed to load base coverage breakdown: %v\n", err) - return false + return handleErr(err, "failed to load base coverage breakdown") } result := Analyze(cfg, currentStats, baseStats) @@ -39,8 +59,7 @@ func Check(w io.Writer, cfg Config) bool { err = SetGithubActionOutput(result, report) if err != nil { - fmt.Fprintf(w, "failed setting github action output: %v\n", err) - return false + return handleErr(err, "failed setting github action output") } if cfg.LocalPrefixDeprecated != "" { // coverage-ignore @@ -51,11 +70,10 @@ func Check(w io.Writer, cfg Config) bool { err = generateAndSaveBadge(w, cfg, result.TotalStats.CoveredPercentage()) if err != nil { - fmt.Fprintf(w, "failed to generate and save badge: %v\n", err) - return false + return handleErr(err, "failed to generate and save badge") } - return result.Pass() + return result.Pass(), nil } func reportForHuman(w io.Writer, result AnalyzeResult) string { diff --git a/pkg/testcoverage/check_test.go b/pkg/testcoverage/check_test.go index 3a099a8..1d39758 100644 --- a/pkg/testcoverage/check_test.go +++ b/pkg/testcoverage/check_test.go @@ -10,6 +10,7 @@ import ( . "github.com/vladopajic/go-test-coverage/v2/pkg/testcoverage" "github.com/vladopajic/go-test-coverage/v2/pkg/testcoverage/coverage" + "github.com/vladopajic/go-test-coverage/v2/pkg/testcoverage/logger" "github.com/vladopajic/go-test-coverage/v2/pkg/testcoverage/path" "github.com/vladopajic/go-test-coverage/v2/pkg/testcoverage/testdata" ) @@ -36,8 +37,9 @@ func TestCheck(t *testing.T) { t.Parallel() buf := &bytes.Buffer{} - pass := Check(buf, Config{}) + pass, err := Check(buf, Config{}) assert.False(t, pass) + assert.Error(t, err) assertGithubActionErrorsCount(t, buf.String(), 0) assertHumanReport(t, buf.String(), 0, 0) assertNoUncoveredLinesInfo(t, buf.String()) @@ -48,8 +50,9 @@ func TestCheck(t *testing.T) { buf := &bytes.Buffer{} cfg := Config{Profile: profileNOK, Threshold: Threshold{Total: 65}} - pass := Check(buf, cfg) + pass, err := Check(buf, cfg) assert.False(t, pass) + assert.Error(t, err) assertGithubActionErrorsCount(t, buf.String(), 0) assertHumanReport(t, buf.String(), 0, 0) assertNoUncoveredLinesInfo(t, buf.String()) @@ -60,8 +63,9 @@ func TestCheck(t *testing.T) { buf := &bytes.Buffer{} cfg := Config{Profile: profileOK, Threshold: Threshold{Total: 65}, SourceDir: sourceDir} - pass := Check(buf, cfg) + pass, err := Check(buf, cfg) assert.True(t, pass) + assert.NoError(t, err) assertGithubActionErrorsCount(t, buf.String(), 0) assertHumanReport(t, buf.String(), 1, 0) assertNoFileNames(t, buf.String(), prefix) @@ -80,8 +84,9 @@ func TestCheck(t *testing.T) { }, SourceDir: sourceDir, } - pass := Check(buf, cfg) + pass, err := Check(buf, cfg) assert.True(t, pass) + assert.NoError(t, err) assertGithubActionErrorsCount(t, buf.String(), 0) assertHumanReport(t, buf.String(), 1, 0) assertNoUncoveredLinesInfo(t, buf.String()) @@ -92,8 +97,9 @@ func TestCheck(t *testing.T) { buf := &bytes.Buffer{} cfg := Config{Profile: profileOK, Threshold: Threshold{Total: 100}, SourceDir: sourceDir} - pass := Check(buf, cfg) + pass, err := Check(buf, cfg) assert.False(t, pass) + assert.NoError(t, err) assertGithubActionErrorsCount(t, buf.String(), 0) assertHumanReport(t, buf.String(), 0, 1) assertHasUncoveredLinesInfo(t, buf.String(), []string{ @@ -114,8 +120,9 @@ func TestCheck(t *testing.T) { Override: []Override{{Threshold: 10, Path: "^pkg"}}, SourceDir: sourceDir, } - pass := Check(buf, cfg) + pass, err := Check(buf, cfg) assert.True(t, pass) + assert.NoError(t, err) assertGithubActionErrorsCount(t, buf.String(), 0) assertHumanReport(t, buf.String(), 2, 0) assertNoFileNames(t, buf.String(), prefix) @@ -132,8 +139,9 @@ func TestCheck(t *testing.T) { Override: []Override{{Threshold: 100, Path: "^pkg"}}, SourceDir: sourceDir, } - pass := Check(buf, cfg) + pass, err := Check(buf, cfg) assert.False(t, pass) + assert.NoError(t, err) assertGithubActionErrorsCount(t, buf.String(), 0) assertHumanReport(t, buf.String(), 0, 2) assertHasUncoveredLinesInfo(t, buf.String(), []string{ @@ -154,8 +162,9 @@ func TestCheck(t *testing.T) { Override: []Override{{Threshold: 60, Path: "pkg/testcoverage/badgestorer/github.go"}}, SourceDir: sourceDir, } - pass := Check(buf, cfg) + pass, err := Check(buf, cfg) assert.True(t, pass) + assert.NoError(t, err) assertGithubActionErrorsCount(t, buf.String(), 0) assertHumanReport(t, buf.String(), 1, 0) assertNoFileNames(t, buf.String(), prefix) @@ -172,8 +181,9 @@ func TestCheck(t *testing.T) { Override: []Override{{Threshold: 80, Path: "pkg/testcoverage/badgestorer/github.go"}}, SourceDir: sourceDir, } - pass := Check(buf, cfg) + pass, err := Check(buf, cfg) assert.False(t, pass) + assert.NoError(t, err) assertGithubActionErrorsCount(t, buf.String(), 0) assertHumanReport(t, buf.String(), 0, 1) assert.GreaterOrEqual(t, strings.Count(buf.String(), prefix), 0) @@ -196,9 +206,10 @@ func TestCheck(t *testing.T) { }, SourceDir: sourceDir, } - pass := Check(buf, cfg) + pass, err := Check(buf, cfg) assert.False(t, pass) - assertFailedToSaveBadge(t, buf.String()) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to generate and save badge") }) t.Run("valid profile - fail invalid breakdown file", func(t *testing.T) { @@ -210,9 +221,10 @@ func TestCheck(t *testing.T) { BreakdownFileName: t.TempDir(), // should failed because this is dir SourceDir: sourceDir, } - pass := Check(buf, cfg) + pass, err := Check(buf, cfg) assert.False(t, pass) - assert.Contains(t, buf.String(), "failed to save coverage breakdown") + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to save coverage breakdown") }) t.Run("valid profile - valid breakdown file", func(t *testing.T) { @@ -224,8 +236,9 @@ func TestCheck(t *testing.T) { BreakdownFileName: t.TempDir() + "/breakdown.testcoverage", SourceDir: sourceDir, } - pass := Check(buf, cfg) + pass, err := Check(buf, cfg) assert.True(t, pass) + assert.NoError(t, err) contentBytes, err := os.ReadFile(cfg.BreakdownFileName) assert.NoError(t, err) @@ -247,13 +260,14 @@ func TestCheck(t *testing.T) { }, SourceDir: sourceDir, } - pass := Check(buf, cfg) + pass, err := Check(buf, cfg) assert.False(t, pass) - assert.Contains(t, buf.String(), "failed to load base coverage breakdown") + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to load base coverage breakdown") }) } -// must not be parallel because it uses env +//nolint:paralleltest // must not be parallel because it uses env func TestCheckNoParallel(t *testing.T) { if testing.Short() { return @@ -269,8 +283,9 @@ func TestCheckNoParallel(t *testing.T) { Threshold: Threshold{Total: 100}, SourceDir: sourceDir, } - pass := Check(buf, cfg) + pass, err := Check(buf, cfg) assert.False(t, pass) + assert.Error(t, err) }) t.Run("ok pass; with github output file", func(t *testing.T) { @@ -284,8 +299,9 @@ func TestCheckNoParallel(t *testing.T) { Threshold: Threshold{Total: 10}, SourceDir: sourceDir, } - pass := Check(buf, cfg) + pass, err := Check(buf, cfg) assert.True(t, pass) + assert.NoError(t, err) assertGithubActionErrorsCount(t, buf.String(), 0) assertHumanReport(t, buf.String(), 1, 0) assertGithubOutputValues(t, testFile) @@ -303,13 +319,33 @@ func TestCheckNoParallel(t *testing.T) { Threshold: Threshold{Total: 100}, SourceDir: sourceDir, } - pass := Check(buf, cfg) + pass, err := Check(buf, cfg) assert.False(t, pass) + assert.NoError(t, err) assertGithubActionErrorsCount(t, buf.String(), 1) assertHumanReport(t, buf.String(), 0, 1) assertGithubOutputValues(t, testFile) assertHasUncoveredLinesInfo(t, buf.String(), []string{}) }) + + t.Run("logger has output", func(t *testing.T) { + logger.Init() + defer logger.Destruct() + + buf := &bytes.Buffer{} + cfg := Config{ + Profile: profileOK, + Threshold: Threshold{Total: 65}, + SourceDir: sourceDir, + Debug: true, + } + pass, err := Check(buf, cfg) + assert.True(t, pass) + assert.NoError(t, err) + + assert.NotEmpty(t, logger.Bytes()) + assert.Contains(t, buf.String(), string(logger.Bytes())) + }) } func Test_Analyze(t *testing.T) { diff --git a/pkg/testcoverage/config.go b/pkg/testcoverage/config.go index ae8b09a..7a89fc9 100644 --- a/pkg/testcoverage/config.go +++ b/pkg/testcoverage/config.go @@ -23,6 +23,7 @@ var ( type Config struct { Profile string `yaml:"profile"` + Debug bool `yaml:"-"` LocalPrefixDeprecated string `yaml:"-"` SourceDir string `yaml:"-"` Threshold Threshold `yaml:"threshold"` diff --git a/pkg/testcoverage/coverage/module.go b/pkg/testcoverage/coverage/module.go index 2d4ec8c..a27158b 100644 --- a/pkg/testcoverage/coverage/module.go +++ b/pkg/testcoverage/coverage/module.go @@ -2,23 +2,23 @@ package coverage import ( "bufio" - "fmt" "os" "path/filepath" "strings" + + "github.com/vladopajic/go-test-coverage/v2/pkg/testcoverage/logger" ) -//nolint:forbidigo // relax func findModuleDirective(rootDir string) string { goModFile := findGoModFile(rootDir) if goModFile == "" { - fmt.Printf("could not find go.mod file in root dir: %s\n", rootDir) + logger.L.Warn().Str("dir", rootDir).Msg("could not find go.mod file in root dir") return "" } module := readModuleDirective(goModFile) if module == "" { // coverage-ignore - fmt.Println("`module` directive not found") + logger.L.Warn().Msg("`module` directive not found") } return module diff --git a/pkg/testcoverage/helpers_test.go b/pkg/testcoverage/helpers_test.go index 2d50c25..206682a 100644 --- a/pkg/testcoverage/helpers_test.go +++ b/pkg/testcoverage/helpers_test.go @@ -195,12 +195,6 @@ func assertGithubActionErrorsCount(t *testing.T, content string, count int) { assert.Equal(t, count, strings.Count(content, "::error")) } -func assertFailedToSaveBadge(t *testing.T, content string) { - t.Helper() - - assert.Contains(t, content, "failed to generate and save badge") -} - func assertPrefix(t *testing.T, result AnalyzeResult, prefix string, has bool) { t.Helper() diff --git a/pkg/testcoverage/logger/logger.go b/pkg/testcoverage/logger/logger.go new file mode 100644 index 0000000..5d14e87 --- /dev/null +++ b/pkg/testcoverage/logger/logger.go @@ -0,0 +1,26 @@ +package logger + +import ( + "bytes" + + "github.com/rs/zerolog" +) + +//nolint:gochecknoglobals // relax +var ( + buffer bytes.Buffer + L zerolog.Logger +) + +func Init() { // coverage-ignore + L = zerolog.New(&buffer).With().Logger() +} + +func Destruct() { + L = zerolog.Logger{} + buffer = bytes.Buffer{} +} + +func Bytes() []byte { + return buffer.Bytes() +}