diff --git a/pkg/goformat/runner.go b/pkg/goformat/runner.go index f87626158179..7c8ce913d724 100644 --- a/pkg/goformat/runner.go +++ b/pkg/goformat/runner.go @@ -223,8 +223,13 @@ func NewRunnerOptions(cfg *config.Config, diff, diffColored, stdin bool) (Runner return RunnerOptions{}, fmt.Errorf("get base path: %w", err) } + evaluatedBasePath, err := fsutils.EvalSymlinks(basePath) + if err != nil { + return RunnerOptions{}, fmt.Errorf("evaluate base path: %w", err) + } + opts := RunnerOptions{ - basePath: basePath, + basePath: evaluatedBasePath, generated: cfg.Formatters.Exclusions.Generated, diff: diff || diffColored, colors: diffColored, @@ -251,7 +256,12 @@ func (o RunnerOptions) MatchAnyPattern(path string) (bool, error) { return false, nil } - rel, err := filepath.Rel(o.basePath, path) + evaluatedPath, err := fsutils.EvalSymlinks(path) + if err != nil { + return false, err + } + + rel, err := filepath.Rel(o.basePath, evaluatedPath) if err != nil { return false, err } diff --git a/pkg/goformat/runner_test.go b/pkg/goformat/runner_test.go new file mode 100644 index 000000000000..40df44abd9dd --- /dev/null +++ b/pkg/goformat/runner_test.go @@ -0,0 +1,135 @@ +package goformat + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/golangci/golangci-lint/v2/pkg/config" +) + +func TestRunnerOptions_MatchAnyPattern(t *testing.T) { + testCases := []struct { + desc string + cfg *config.Config + filename string + + assertMatch assert.BoolAssertionFunc + expectedCount int + }{ + { + desc: "match", + cfg: &config.Config{ + Formatters: config.Formatters{ + Exclusions: config.FormatterExclusions{ + Paths: []string{`generated\.go`}, + }, + }, + }, + filename: "generated.go", + assertMatch: assert.True, + expectedCount: 1, + }, + { + desc: "no match", + cfg: &config.Config{ + Formatters: config.Formatters{ + Exclusions: config.FormatterExclusions{ + Paths: []string{`excluded\.go`}, + }, + }, + }, + filename: "test.go", + assertMatch: assert.False, + expectedCount: 0, + }, + { + desc: "no patterns", + cfg: &config.Config{}, + filename: "test.go", + assertMatch: assert.False, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + tmpDir := t.TempDir() + + testFile := filepath.Join(tmpDir, test.filename) + + err := os.WriteFile(testFile, []byte("package main"), 0o600) + require.NoError(t, err) + + test.cfg.SetConfigDir(tmpDir) + + opts, err := NewRunnerOptions(test.cfg, false, false, false) + require.NoError(t, err) + + match, err := opts.MatchAnyPattern(testFile) + require.NoError(t, err) + + test.assertMatch(t, match) + + require.Len(t, opts.patterns, len(test.cfg.Formatters.Exclusions.Paths)) + + if len(opts.patterns) == 0 { + assert.Empty(t, opts.excludedPathCounter) + } else { + assert.Equal(t, test.expectedCount, opts.excludedPathCounter[opts.patterns[0]]) + } + }) + } +} + +func TestRunnerOptions_MatchAnyPattern_withSymlinks(t *testing.T) { + tmpDir := t.TempDir() + + testFile := filepath.Join(tmpDir, "project", "test.go") + + realDir := filepath.Dir(testFile) + + err := os.MkdirAll(realDir, 0o755) + require.NoError(t, err) + + err = os.WriteFile(testFile, []byte("package main"), 0o600) + require.NoError(t, err) + + // Create a symlink in a completely different part of the tree. + symlink := filepath.Join(tmpDir, "somewhere", "symlink") + + err = os.MkdirAll(filepath.Dir(symlink), 0o755) + require.NoError(t, err) + + err = os.Symlink(realDir, symlink) + require.NoError(t, err) + + cfg := &config.Config{ + Formatters: config.Formatters{ + Exclusions: config.FormatterExclusions{ + // Creates a pattern that matches files in the root directory only. + // This pattern will match `test.go" but not `../../../test.go` or similar broken paths. + Paths: []string{`^[^/\\]+\.go$`}, + }, + }, + } + + cfg.SetConfigDir(realDir) + + opts, err := NewRunnerOptions(cfg, false, false, false) + require.NoError(t, err) + + match, err := opts.MatchAnyPattern(filepath.Join(symlink, "test.go")) + require.NoError(t, err) + + assert.True(t, match) + + require.NotEmpty(t, opts.patterns) + require.Len(t, opts.patterns, len(cfg.Formatters.Exclusions.Paths)) + + assert.Equal(t, 1, opts.excludedPathCounter[opts.patterns[0]]) +}