From ef2652a64e6ae98b6f3e9c7ccf39ec1d730b7c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vlado=20Paji=C4=87?= Date: Fri, 7 Mar 2025 23:11:43 +0100 Subject: [PATCH 1/2] feat: add source dir --- action.yml | 8 ++++++- docs/github_action.md | 13 ++++++++++ main.go | 6 +++++ pkg/testcoverage/check.go | 2 +- pkg/testcoverage/check_test.go | 32 ++++++++++++------------- pkg/testcoverage/config.go | 2 +- pkg/testcoverage/coverage/cover.go | 26 ++++++++------------ pkg/testcoverage/coverage/cover_test.go | 20 ++++++++-------- 8 files changed, 64 insertions(+), 45 deletions(-) diff --git a/action.yml b/action.yml index 4dea6bf2..f6895d5f 100644 --- a/action.yml +++ b/action.yml @@ -8,7 +8,12 @@ inputs: required: false default: "" type: string - + source-dir: + description: Sets relative path to source files. + required: false + default: "" + type: string + # Individual properties profile: description: Path to the coverage profile file. Overrides value from configuration. @@ -132,6 +137,7 @@ runs: args: - --config=${{ inputs.config || '''''' }} - --profile=${{ inputs.profile || '''''' }} + - --source-dir=${{ inputs.source-dir || '''''' }} - --github-action-output=true - --threshold-file=${{ inputs.threshold-file }} - --threshold-package=${{ inputs.threshold-package }} diff --git a/docs/github_action.md b/docs/github_action.md index 93ef95d6..7c54eff6 100644 --- a/docs/github_action.md +++ b/docs/github_action.md @@ -38,6 +38,19 @@ Alternatively, if you don't need advanced configuration options from a config fi Note: When using a config file alongside action properties, specifying these parameters will override the corresponding values in the config file. +## Source Directory + +Some projects, such as monorepos with multiple projects under the root directory, may require specifying the path to a project's source. +In such cases, the `source-dir` property can be used to specify the source files location relative to the root directory. + +```yml + - name: check test coverage + uses: vladopajic/go-test-coverage@v2 + with: + config: ./.testcoverage.yml + source-dir: ./some_project +``` + ## Liberal Coverage Check The `go-test-coverage` GitHub Action can be configured to report the current test coverage without enforcing specific thresholds. To enable this functionality in your GitHub workflow, include the `continue-on-error: true` property in the job step configuration. This ensures that the workflow proceeds even if the coverage check fails. diff --git a/main.go b/main.go index 50ddb39a..7a685a29 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,7 @@ 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"` @@ -56,6 +57,7 @@ func newArgs() args { ConfigPath: ciDefaultString, Profile: ciDefaultString, LocalPrefix: ciDefaultString, + SourceDir: ciDefaultString, GithubActionOutput: false, ThresholdFile: ciDefaultInt, ThresholdPackage: ciDefaultInt, @@ -102,6 +104,10 @@ func (a *args) overrideConfig(cfg testcoverage.Config) (testcoverage.Config, err cfg.LocalPrefixDeprecated = a.LocalPrefix } + if !isCIDefaultString(a.SourceDir) { + cfg.SourceDir = a.SourceDir + } + if !isCIDefaultInt(a.ThresholdFile) { cfg.Threshold.File = a.ThresholdFile } diff --git a/pkg/testcoverage/check.go b/pkg/testcoverage/check.go index a272734d..c563a2b2 100644 --- a/pkg/testcoverage/check.go +++ b/pkg/testcoverage/check.go @@ -74,7 +74,7 @@ func GenerateCoverageStats(cfg Config) ([]coverage.Stats, error) { return coverage.GenerateCoverageStats(coverage.Config{ //nolint:wrapcheck // err wrapped above Profiles: strings.Split(cfg.Profile, ","), ExcludePaths: cfg.Exclude.Paths, - RootDir: cfg.RootDir, + SourceDir: cfg.SourceDir, }) } diff --git a/pkg/testcoverage/check_test.go b/pkg/testcoverage/check_test.go index 1773c726..eb96ae5c 100644 --- a/pkg/testcoverage/check_test.go +++ b/pkg/testcoverage/check_test.go @@ -22,8 +22,8 @@ const ( breakdownOK = testdataDir + testdata.BreakdownOK breakdownNOK = testdataDir + testdata.BreakdownNOK - prefix = "github.com/vladopajic/go-test-coverage/v2" - rootDir = "../../" + prefix = "github.com/vladopajic/go-test-coverage/v2" + sourceDir = "../../" ) func TestCheck(t *testing.T) { @@ -60,7 +60,7 @@ func TestCheck(t *testing.T) { t.Parallel() buf := &bytes.Buffer{} - cfg := Config{Profile: profileOK, Threshold: Threshold{Total: 65}, RootDir: rootDir} + cfg := Config{Profile: profileOK, Threshold: Threshold{Total: 65}, SourceDir: sourceDir} pass := Check(buf, cfg) assert.True(t, pass) assertGithubActionErrorsCount(t, buf.String(), 0) @@ -79,7 +79,7 @@ func TestCheck(t *testing.T) { Exclude: Exclude{ Paths: []string{`cdn\.go$`, `github\.go$`, `cover\.go$`, `check\.go$`, `path\.go$`}, }, - RootDir: rootDir, + SourceDir: sourceDir, } pass := Check(buf, cfg) assert.True(t, pass) @@ -92,7 +92,7 @@ func TestCheck(t *testing.T) { t.Parallel() buf := &bytes.Buffer{} - cfg := Config{Profile: profileOK, Threshold: Threshold{Total: 100}, RootDir: rootDir} + cfg := Config{Profile: profileOK, Threshold: Threshold{Total: 100}, SourceDir: sourceDir} pass := Check(buf, cfg) assert.False(t, pass) assertGithubActionErrorsCount(t, buf.String(), 0) @@ -113,7 +113,7 @@ func TestCheck(t *testing.T) { Profile: profileOK, Threshold: Threshold{File: 100}, Override: []Override{{Threshold: 10, Path: "^pkg"}}, - RootDir: rootDir, + SourceDir: sourceDir, } pass := Check(buf, cfg) assert.True(t, pass) @@ -131,7 +131,7 @@ func TestCheck(t *testing.T) { Profile: profileOK, Threshold: Threshold{File: 10}, Override: []Override{{Threshold: 100, Path: "^pkg"}}, - RootDir: rootDir, + SourceDir: sourceDir, } pass := Check(buf, cfg) assert.False(t, pass) @@ -153,7 +153,7 @@ func TestCheck(t *testing.T) { Profile: profileOK, Threshold: Threshold{File: 70}, Override: []Override{{Threshold: 60, Path: "pkg/testcoverage/badgestorer/github.go"}}, - RootDir: rootDir, + SourceDir: sourceDir, } pass := Check(buf, cfg) assert.True(t, pass) @@ -171,7 +171,7 @@ func TestCheck(t *testing.T) { Profile: profileOK, Threshold: Threshold{File: 70}, Override: []Override{{Threshold: 80, Path: "pkg/testcoverage/badgestorer/github.go"}}, - RootDir: rootDir, + SourceDir: sourceDir, } pass := Check(buf, cfg) assert.False(t, pass) @@ -195,7 +195,7 @@ func TestCheck(t *testing.T) { Badge: Badge{ FileName: t.TempDir(), // should failed because this is dir }, - RootDir: rootDir, + SourceDir: sourceDir, } pass := Check(buf, cfg) assert.False(t, pass) @@ -209,7 +209,7 @@ func TestCheck(t *testing.T) { cfg := Config{ Profile: profileOK, BreakdownFileName: t.TempDir(), // should failed because this is dir - RootDir: rootDir, + SourceDir: sourceDir, } pass := Check(buf, cfg) assert.False(t, pass) @@ -223,7 +223,7 @@ func TestCheck(t *testing.T) { cfg := Config{ Profile: profileOK, BreakdownFileName: t.TempDir() + "/breakdown.testcoverage", - RootDir: rootDir, + SourceDir: sourceDir, } pass := Check(buf, cfg) assert.True(t, pass) @@ -246,7 +246,7 @@ func TestCheck(t *testing.T) { Diff: Diff{ BaseBreakdownFileName: t.TempDir(), // should failed because this is dir }, - RootDir: rootDir, + SourceDir: sourceDir, } pass := Check(buf, cfg) assert.False(t, pass) @@ -268,7 +268,7 @@ func TestCheckNoParallel(t *testing.T) { Profile: profileOK, GithubActionOutput: true, Threshold: Threshold{Total: 100}, - RootDir: rootDir, + SourceDir: sourceDir, } pass := Check(buf, cfg) assert.False(t, pass) @@ -283,7 +283,7 @@ func TestCheckNoParallel(t *testing.T) { Profile: profileOK, GithubActionOutput: true, Threshold: Threshold{Total: 10}, - RootDir: rootDir, + SourceDir: sourceDir, } pass := Check(buf, cfg) assert.True(t, pass) @@ -302,7 +302,7 @@ func TestCheckNoParallel(t *testing.T) { Profile: profileOK, GithubActionOutput: true, Threshold: Threshold{Total: 100}, - RootDir: rootDir, + SourceDir: sourceDir, } pass := Check(buf, cfg) assert.False(t, pass) diff --git a/pkg/testcoverage/config.go b/pkg/testcoverage/config.go index acc6e816..ae8b09a5 100644 --- a/pkg/testcoverage/config.go +++ b/pkg/testcoverage/config.go @@ -24,6 +24,7 @@ var ( type Config struct { Profile string `yaml:"profile"` LocalPrefixDeprecated string `yaml:"-"` + SourceDir string `yaml:"-"` Threshold Threshold `yaml:"threshold"` Override []Override `yaml:"override,omitempty"` Exclude Exclude `yaml:"exclude"` @@ -31,7 +32,6 @@ type Config struct { GithubActionOutput bool `yaml:"github-action-output"` Diff Diff `yaml:"diff"` Badge Badge `yaml:"-"` - RootDir string `yaml:"-"` } type Threshold struct { diff --git a/pkg/testcoverage/coverage/cover.go b/pkg/testcoverage/coverage/cover.go index a771c4f2..9a7ed149 100644 --- a/pkg/testcoverage/coverage/cover.go +++ b/pkg/testcoverage/coverage/cover.go @@ -21,7 +21,7 @@ const IgnoreText = "coverage-ignore" type Config struct { Profiles []string ExcludePaths []string - RootDir string + SourceDir string } func GenerateCoverageStats(cfg Config) ([]Stats, error) { @@ -30,7 +30,7 @@ func GenerateCoverageStats(cfg Config) ([]Stats, error) { return nil, fmt.Errorf("parsing profiles: %w", err) } - files, err := findFiles(profiles, cfg.RootDir) + files, err := findFiles(profiles, cfg.SourceDir) if err != nil { return nil, err } @@ -121,17 +121,7 @@ func findFiles(profiles []*cover.Profile, rootDir string) (map[string]fileInfo, return result, nil } -func defaultRootDir(rootDir string) string { - if rootDir == "" { - rootDir = "." - } - - return rootDir -} - func findFileCreator(rootDir string) func(file string) (string, string, bool) { - rootDir = defaultRootDir(rootDir) - cache := make(map[string]*build.Package) findBuildImport := func(file string) (string, string, bool) { dir, file := filepath.Split(file) @@ -180,6 +170,13 @@ func findFileCreator(rootDir string) func(file string) (string, string, bool) { func listAllFiles(rootDir string) []fileInfo { files := make([]fileInfo, 0) + makeName := func(file string) string { + name, _ := strings.CutPrefix(file, rootDir) + name = path.NormalizeForTool(name) + + return name + } + //nolint:errcheck // error ignored because there is fallback mechanism for finding files filepath.Walk(rootDir, func(file string, info os.FileInfo, err error) error { if err != nil { // coverage-ignore @@ -189,12 +186,9 @@ func listAllFiles(rootDir string) []fileInfo { if !info.IsDir() && strings.HasSuffix(file, ".go") && !strings.HasSuffix(file, "_test.go") { - name, _ := strings.CutPrefix(file, rootDir) - name = path.NormalizeForTool(name) - files = append(files, fileInfo{ path: file, - name: name, + name: makeName(file), }) } diff --git a/pkg/testcoverage/coverage/cover_test.go b/pkg/testcoverage/coverage/cover_test.go index 87fb1e6b..61c55f5a 100644 --- a/pkg/testcoverage/coverage/cover_test.go +++ b/pkg/testcoverage/coverage/cover_test.go @@ -25,7 +25,7 @@ const ( prefix = "github.com/vladopajic/go-test-coverage/v2" coverFilename = "pkg/testcoverage/coverage/cover.go" - rootDir = "../../../" + sourceDir = "../../../" ) func Test_GenerateCoverageStats(t *testing.T) { @@ -42,16 +42,16 @@ func Test_GenerateCoverageStats(t *testing.T) { // should get error parsing invalid profile file stats, err = GenerateCoverageStats(Config{ - Profiles: []string{profileNOK}, - RootDir: rootDir, + Profiles: []string{profileNOK}, + SourceDir: sourceDir, }) assert.Error(t, err) assert.Empty(t, stats) // should be okay to read valid profile stats1, err := GenerateCoverageStats(Config{ - Profiles: []string{profileOK}, - RootDir: rootDir, + Profiles: []string{profileOK}, + SourceDir: sourceDir, }) assert.NoError(t, err) assert.NotEmpty(t, stats1) @@ -60,7 +60,7 @@ func Test_GenerateCoverageStats(t *testing.T) { stats2, err := GenerateCoverageStats(Config{ Profiles: []string{profileOK}, ExcludePaths: []string{`cover\.go$`}, - RootDir: rootDir, + SourceDir: sourceDir, }) assert.NoError(t, err) assert.NotEmpty(t, stats2) @@ -69,8 +69,8 @@ func Test_GenerateCoverageStats(t *testing.T) { // should have total coverage because of second profile stats3, err := GenerateCoverageStats(Config{ - Profiles: []string{profileOK, profileOKFull}, - RootDir: rootDir, + Profiles: []string{profileOK, profileOKFull}, + SourceDir: sourceDir, }) assert.NoError(t, err) assert.NotEmpty(t, stats3) @@ -78,8 +78,8 @@ func Test_GenerateCoverageStats(t *testing.T) { // should not have `badge/generate.go` in statistics because it has no statements stats4, err := GenerateCoverageStats(Config{ - Profiles: []string{profileOKNoStatements}, - RootDir: rootDir, + Profiles: []string{profileOKNoStatements}, + SourceDir: sourceDir, }) assert.NoError(t, err) assert.Len(t, stats4, 1) From 5e3021b8f911a5c5b40c9583ea9c0359062eeb14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vlado=20Paji=C4=87?= Date: Fri, 7 Mar 2025 23:17:38 +0100 Subject: [PATCH 2/2] default root dir --- pkg/testcoverage/coverage/cover.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/testcoverage/coverage/cover.go b/pkg/testcoverage/coverage/cover.go index 9a7ed149..3710ba67 100644 --- a/pkg/testcoverage/coverage/cover.go +++ b/pkg/testcoverage/coverage/cover.go @@ -145,6 +145,7 @@ func findFileCreator(rootDir string) func(file string) (string, string, bool) { return file, noPrefixName, err == nil } + rootDir = defaultRootDir(rootDir) prefix := findModuleDirective(rootDir) files := listAllFiles(rootDir) findWalk := func(file, prefix string) (string, string, bool) { @@ -167,6 +168,14 @@ func findFileCreator(rootDir string) func(file string) (string, string, bool) { } } +func defaultRootDir(rootDir string) string { + if rootDir == "" { + rootDir = "." + } + + return rootDir +} + func listAllFiles(rootDir string) []fileInfo { files := make([]fileInfo, 0)