Skip to content

Commit a8a12db

Browse files
authored
fix: fmt command with symlinks (#6187)
1 parent 7bcb236 commit a8a12db

File tree

3 files changed

+160
-22
lines changed

3 files changed

+160
-22
lines changed

pkg/commands/fmt.go

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,11 @@ func (c *fmtCommand) preRunE(_ *cobra.Command, _ []string) error {
113113
}
114114

115115
func (c *fmtCommand) execute(_ *cobra.Command, args []string) error {
116-
paths, err := cleanArgs(args)
117-
if err != nil {
118-
return fmt.Errorf("failed to clean arguments: %w", err)
119-
}
116+
paths := cleanArgs(args)
120117

121118
c.log.Infof("Formatting Go files...")
122119

123-
err = c.runner.Run(paths)
120+
err := c.runner.Run(paths)
124121
if err != nil {
125122
return fmt.Errorf("failed to process files: %w", err)
126123
}
@@ -134,25 +131,15 @@ func (c *fmtCommand) persistentPostRun(_ *cobra.Command, _ []string) {
134131
}
135132
}
136133

137-
func cleanArgs(args []string) ([]string, error) {
134+
func cleanArgs(args []string) []string {
138135
if len(args) == 0 {
139-
abs, err := filepath.Abs(".")
140-
if err != nil {
141-
return nil, err
142-
}
143-
144-
return []string{abs}, nil
136+
return []string{"."}
145137
}
146138

147139
var expanded []string
148140
for _, arg := range args {
149-
abs, err := filepath.Abs(strings.ReplaceAll(arg, "...", ""))
150-
if err != nil {
151-
return nil, err
152-
}
153-
154-
expanded = append(expanded, abs)
141+
expanded = append(expanded, filepath.Clean(strings.ReplaceAll(arg, "...", "")))
155142
}
156143

157-
return expanded, nil
144+
return expanded
158145
}

pkg/goformat/runner.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,14 @@ func NewRunnerOptions(cfg *config.Config, diff, diffColored, stdin bool) (Runner
223223
return RunnerOptions{}, fmt.Errorf("get base path: %w", err)
224224
}
225225

226+
// Required to be consistent with `RunnerOptions.MatchAnyPattern`.
227+
absBasePath, err := filepath.Abs(basePath)
228+
if err != nil {
229+
return RunnerOptions{}, err
230+
}
231+
226232
opts := RunnerOptions{
227-
basePath: basePath,
233+
basePath: absBasePath,
228234
generated: cfg.Formatters.Exclusions.Generated,
229235
diff: diff || diffColored,
230236
colors: diffColored,
@@ -251,7 +257,12 @@ func (o RunnerOptions) MatchAnyPattern(path string) (bool, error) {
251257
return false, nil
252258
}
253259

254-
rel, err := filepath.Rel(o.basePath, path)
260+
abs, err := filepath.Abs(path)
261+
if err != nil {
262+
return false, err
263+
}
264+
265+
rel, err := filepath.Rel(o.basePath, abs)
255266
if err != nil {
256267
return false, err
257268
}
@@ -272,7 +283,7 @@ func skipDir(name string) bool {
272283
return true
273284

274285
default:
275-
return strings.HasPrefix(name, ".")
286+
return strings.HasPrefix(name, ".") && name != "."
276287
}
277288
}
278289

pkg/goformat/runner_test.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package goformat
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/golangci/golangci-lint/v2/pkg/config"
12+
)
13+
14+
func TestRunnerOptions_MatchAnyPattern(t *testing.T) {
15+
testCases := []struct {
16+
desc string
17+
cfg *config.Config
18+
filename string
19+
20+
assertMatch assert.BoolAssertionFunc
21+
expectedCount int
22+
}{
23+
{
24+
desc: "match",
25+
cfg: &config.Config{
26+
Formatters: config.Formatters{
27+
Exclusions: config.FormatterExclusions{
28+
Paths: []string{`generated\.go`},
29+
},
30+
},
31+
},
32+
filename: "generated.go",
33+
assertMatch: assert.True,
34+
expectedCount: 1,
35+
},
36+
{
37+
desc: "no match",
38+
cfg: &config.Config{
39+
Formatters: config.Formatters{
40+
Exclusions: config.FormatterExclusions{
41+
Paths: []string{`excluded\.go`},
42+
},
43+
},
44+
},
45+
filename: "test.go",
46+
assertMatch: assert.False,
47+
expectedCount: 0,
48+
},
49+
{
50+
desc: "no patterns",
51+
cfg: &config.Config{},
52+
filename: "test.go",
53+
assertMatch: assert.False,
54+
},
55+
}
56+
57+
for _, test := range testCases {
58+
t.Run(test.desc, func(t *testing.T) {
59+
t.Parallel()
60+
61+
tmpDir := t.TempDir()
62+
63+
testFile := filepath.Join(tmpDir, test.filename)
64+
65+
err := os.WriteFile(testFile, []byte("package main"), 0o600)
66+
require.NoError(t, err)
67+
68+
test.cfg.SetConfigDir(tmpDir)
69+
70+
opts, err := NewRunnerOptions(test.cfg, false, false, false)
71+
require.NoError(t, err)
72+
73+
match, err := opts.MatchAnyPattern(testFile)
74+
require.NoError(t, err)
75+
76+
test.assertMatch(t, match)
77+
78+
require.Len(t, opts.patterns, len(test.cfg.Formatters.Exclusions.Paths))
79+
80+
if len(opts.patterns) == 0 {
81+
assert.Empty(t, opts.excludedPathCounter)
82+
} else {
83+
assert.Equal(t, test.expectedCount, opts.excludedPathCounter[opts.patterns[0]])
84+
}
85+
})
86+
}
87+
}
88+
89+
// File structure:
90+
//
91+
// tmp
92+
// ├── project (`realDir`)
93+
// │ ├── .golangci.yml
94+
// │ └── test.go
95+
// └── somewhere
96+
// └── symlink (to "project")
97+
func TestRunnerOptions_MatchAnyPattern_withSymlinks(t *testing.T) {
98+
tmpDir := t.TempDir()
99+
100+
testFile := filepath.Join(tmpDir, "project", "test.go")
101+
102+
realDir := filepath.Dir(testFile)
103+
104+
err := os.MkdirAll(realDir, 0o755)
105+
require.NoError(t, err)
106+
107+
err = os.WriteFile(testFile, []byte("package main"), 0o600)
108+
require.NoError(t, err)
109+
110+
symlink := filepath.Join(tmpDir, "somewhere", "symlink")
111+
112+
err = os.MkdirAll(filepath.Dir(symlink), 0o755)
113+
require.NoError(t, err)
114+
115+
err = os.Symlink(realDir, symlink)
116+
require.NoError(t, err)
117+
118+
cfg := &config.Config{
119+
Formatters: config.Formatters{
120+
Exclusions: config.FormatterExclusions{
121+
Paths: []string{`^[^/\\]+\.go$`},
122+
},
123+
},
124+
}
125+
126+
cfg.SetConfigDir(symlink)
127+
128+
opts, err := NewRunnerOptions(cfg, false, false, false)
129+
require.NoError(t, err)
130+
131+
match, err := opts.MatchAnyPattern(filepath.Join(symlink, "test.go"))
132+
require.NoError(t, err)
133+
134+
assert.True(t, match)
135+
136+
require.NotEmpty(t, opts.patterns)
137+
require.Len(t, opts.patterns, len(cfg.Formatters.Exclusions.Paths))
138+
139+
assert.Equal(t, 1, opts.excludedPathCounter[opts.patterns[0]])
140+
}

0 commit comments

Comments
 (0)