Skip to content

Commit fde86ba

Browse files
committed
resources: Refix resources.GetMatch/Match case-sensitivity bug
- add integration tests - fix not found error in `walk.go` Fix #7686 Fix #10112 - ref - #10305 - 281554e
1 parent d8aba18 commit fde86ba

File tree

3 files changed

+207
-6
lines changed

3 files changed

+207
-6
lines changed

hugofs/walk.go

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,20 @@ func (w *Walkway) Walk() error {
117117
fi = w.fi
118118
} else {
119119
info, _, err := lstatIfPossible(w.fs, w.root)
120-
if err != nil {
121-
if os.IsNotExist(err) {
122-
return nil
123-
}
124120

121+
if os.IsNotExist(err) {
122+
// recheck case-insensitive directory name
123+
info, err = w.findDirInsensitively()
124+
}
125+
126+
// On darwin or windows, info can be returned even if lettercases in path is unmatched
127+
// If using unmached cases, make them original ones and set as w.root.
128+
base := filepath.Base(w.root)
129+
if info.Name() != base {
130+
w.root = filepath.Join(filepath.Dir(w.root), base)
131+
}
132+
133+
if err != nil {
125134
if w.checkErr(w.root, err) {
126135
return nil
127136
}
@@ -147,7 +156,49 @@ func lstatIfPossible(fs afero.Fs, path string) (os.FileInfo, bool, error) {
147156
return fi, false, err
148157
}
149158

150-
// checkErr returns true if the error is handled.
159+
// case-insenstively directory search
160+
func (w *Walkway) findDirInsensitively() (os.FileInfo, error) {
161+
parent := filepath.Dir(w.root)
162+
base := filepath.Base(w.root)
163+
164+
fi, err := w.fs.Stat(parent)
165+
166+
if err != nil {
167+
return nil, err
168+
}
169+
170+
if !fi.IsDir() {
171+
return nil, fmt.Errorf("%s is not directory", parent)
172+
}
173+
174+
f, err := w.fs.Open(parent)
175+
defer f.Close()
176+
if err != nil {
177+
return nil, err
178+
}
179+
180+
names, err := f.Readdirnames(-1)
181+
182+
if err != nil {
183+
return nil, err
184+
}
185+
186+
baseLowered := strings.ToLower(base)
187+
for _, name := range names {
188+
if baseLowered == strings.ToLower(name) {
189+
p := filepath.Join(parent, name)
190+
fi, _, err := lstatIfPossible(w.fs, p)
191+
if err != nil {
192+
return nil, err
193+
}
194+
w.root = p
195+
return fi, err
196+
}
197+
}
198+
return nil, os.ErrNotExist
199+
}
200+
201+
// ceckErr returns true if the error is handled.
151202
func (w *Walkway) checkErr(filename string, err error) bool {
152203
if err == ErrPermissionSymlink {
153204
logUnsupportedSymlink(filename, w.logger)

resources/resource_factories/create/create.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ func (c *Client) GetMatch(pattern string) (resource.Resource, error) {
9393
}
9494

9595
func (c *Client) match(name, pattern string, matchFunc func(r resource.Resource) bool, firstOnly bool) (resource.Resources, error) {
96+
patternNoLower := glob.NormalizePathNoLower(pattern)
9697
pattern = glob.NormalizePath(pattern)
9798
partitions := glob.FilterGlobParts(strings.Split(pattern, "/"))
9899
if len(partitions) == 0 {
@@ -127,7 +128,7 @@ func (c *Client) match(name, pattern string, matchFunc func(r resource.Resource)
127128
return firstOnly, nil
128129
}
129130

130-
if err := hugofs.Glob(c.rs.BaseFs.Assets.Fs, pattern, handle); err != nil {
131+
if err := hugofs.Glob(c.rs.BaseFs.Assets.Fs, patternNoLower, handle); err != nil {
131132
return nil, err
132133
}
133134

tpl/resources/integration_test.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,152 @@ func TestCopyPageShouldFail(t *testing.T) {
9898
b.Assert(err, qt.IsNotNil)
9999

100100
}
101+
102+
func TestMatchPatternCaseSensitivity(t *testing.T) {
103+
t.Parallel()
104+
105+
files := `
106+
-- config.toml --
107+
baseURL = "http://example.com/blog"
108+
-- assets/DIR1/sub/x.txt --
109+
content in x.txt
110+
-- assets/dir2/SUB/y.txt --
111+
content in y.txt
112+
-- layouts/index.html --
113+
{{ with resources.GetMatch "DIR1/sub/x.txt" }}
114+
GetMatch "DIR1/sub/x.txt" | .Content => {{ .Content }}
115+
GetMatch "DIR1/sub/x.txt" | .Name => {{ .Name }}
116+
{{ end }}
117+
118+
{{ with resources.GetMatch "dir2/SUB/y.txt" }}
119+
GetMatch "dir2/SUB/y.txt" | .Content => {{ .Content }}
120+
GetMatch "dir2/SUB/y.txt" | .Name => {{ .Name }}
121+
{{ end }}
122+
123+
{{ with resources.GetMatch "dir1/sub/x.txt" }}
124+
GetMatch "dir1/sub/x.txt" | .Content => {{ .Content }}
125+
GetMatch "dir1/sub/x.txt" | .Name => {{ .Name }}
126+
{{ end }}
127+
128+
{{ with resources.GetMatch "dir2/sub/y.txt" }}
129+
GetMatch "dir2/sub/y.txt" | .Content => {{ .Content }}
130+
GetMatch "dir2/sub/y.txt" | .Name => {{ .Name }}
131+
{{ end }}
132+
133+
{{ with resources.GetMatch "DIR1/SUB/X.TXT" }}
134+
GetMatch "DIR1/SUB/X.TXT" | .Content => {{ .Content }}
135+
GetMatch "DIR1/SUB/X.TXT" | .Name => {{ .Name }}
136+
{{ end }}
137+
138+
{{ with resources.GetMatch "DIR2/SUB/Y.TXT" }}
139+
GetMatch "DIR2/SUB/Y.TXT" | .Content => {{ .Content }}
140+
GetMatch "DIR2/SUB/Y.TXT" | .Name => {{ .Name }}
141+
{{ end }}
142+
143+
{{ with resources.GetMatch "DIR1/*/x.txt" }}
144+
GetMatch "DIR1/*/x.txt" | .Content => {{ .Content }}
145+
GetMatch "DIR1/*/x.txt" | .Name => {{ .Name }}
146+
{{ end }}
147+
148+
{{ with resources.GetMatch "dir2/*/y.txt" }}
149+
GetMatch "dir2/*/y.txt" | .Content => {{ .Content }}
150+
GetMatch "dir2/*/y.txt" | .Name => {{ .Name }}
151+
{{ end }}
152+
153+
{{ with resources.GetMatch "dir1/*/x.txt" }}
154+
GetMatch "dir1/*/x.txt" | .Content => {{ .Content }}
155+
GetMatch "dir1/*/x.txt" | .Name => {{ .Name }}
156+
{{ end }}
157+
158+
{{ with resources.GetMatch "DIR2/*/Y.TXT" }}
159+
GetMatch "DIR2/*/Y.TXT" | .Content => {{ .Content }}
160+
GetMatch "DIR2/*/Y.TXT" | .Name => {{ .Name }}
161+
{{ end }}
162+
163+
{{ with resources.GetMatch "DIR1/**/x.txt" }}
164+
GetMatch "DIR1/**/x.txt" | .Content => {{ .Content }}
165+
GetMatch "DIR1/**/x.txt" | .Name => {{ .Name }}
166+
{{ end }}
167+
168+
{{ with resources.GetMatch "dir2/**/y.txt" }}
169+
GetMatch "dir2/**/y.txt" | .Content => {{ .Content }}
170+
GetMatch "dir2/**/y.txt" | .Name => {{ .Name }}
171+
{{ end }}
172+
173+
{{ with resources.GetMatch "dir1/**/x.txt" }}
174+
GetMatch "dir1/**/x.txt" | .Content => {{ .Content }}
175+
GetMatch "dir1/**/x.txt" | .Name => {{ .Name }}
176+
{{ end }}
177+
178+
{{ with resources.GetMatch "DIR2/**/Y.TXT" }}
179+
GetMatch "DIR2/**/Y.TXT" | .Content => {{ .Content }}
180+
GetMatch "DIR2/**/Y.TXT" | .Name => {{ .Name }}
181+
{{ end }}
182+
183+
{{ with resources.Match "**/*.txt" }}
184+
Match "**/*.txt" | len => {{ len . }}
185+
{{ end }}
186+
187+
{{ with resources.Match "*/*/*.txt" }}
188+
Match "*/*/*.txt" | len => {{ len . }}
189+
{{ end }}
190+
191+
`
192+
193+
b := hugolib.NewIntegrationTestBuilder(
194+
hugolib.IntegrationTestConfig{
195+
T: t,
196+
TxtarString: files,
197+
NeedsOsFS: true,
198+
}).Build()
199+
200+
want := `
201+
GetMatch "DIR1/sub/x.txt" | .Content => content in x.txt
202+
GetMatch "DIR1/sub/x.txt" | .Name => DIR1/sub/x.txt
203+
204+
GetMatch "dir2/SUB/y.txt" | .Content => content in y.txt
205+
GetMatch "dir2/SUB/y.txt" | .Name => dir2/SUB/y.txt
206+
207+
GetMatch "dir1/sub/x.txt" | .Content => content in x.txt
208+
GetMatch "dir1/sub/x.txt" | .Name => DIR1/sub/x.txt
209+
210+
GetMatch "dir2/sub/y.txt" | .Content => content in y.txt
211+
GetMatch "dir2/sub/y.txt" | .Name => dir2/SUB/y.txt
212+
213+
GetMatch "DIR1/SUB/X.TXT" | .Content => content in x.txt
214+
GetMatch "DIR1/SUB/X.TXT" | .Name => DIR1/sub/x.txt
215+
216+
GetMatch "DIR2/SUB/Y.TXT" | .Content => content in y.txt
217+
GetMatch "DIR2/SUB/Y.TXT" | .Name => dir2/SUB/y.txt
218+
219+
GetMatch "DIR1/*/x.txt" | .Content => content in x.txt
220+
GetMatch "DIR1/*/x.txt" | .Name => DIR1/sub/x.txt
221+
222+
GetMatch "dir2/*/y.txt" | .Content => content in y.txt
223+
GetMatch "dir2/*/y.txt" | .Name => dir2/SUB/y.txt
224+
225+
GetMatch "dir1/*/x.txt" | .Content => content in x.txt
226+
GetMatch "dir1/*/x.txt" | .Name => DIR1/sub/x.txt
227+
228+
GetMatch "DIR2/*/Y.TXT" | .Content => content in y.txt
229+
GetMatch "DIR2/*/Y.TXT" | .Name => dir2/SUB/y.txt
230+
231+
GetMatch "DIR1/**/x.txt" | .Content => content in x.txt
232+
GetMatch "DIR1/**/x.txt" | .Name => DIR1/sub/x.txt
233+
234+
GetMatch "dir2/**/y.txt" | .Content => content in y.txt
235+
GetMatch "dir2/**/y.txt" | .Name => dir2/SUB/y.txt
236+
237+
GetMatch "dir1/**/x.txt" | .Content => content in x.txt
238+
GetMatch "dir1/**/x.txt" | .Name => DIR1/sub/x.txt
239+
240+
GetMatch "DIR2/**/Y.TXT" | .Content => content in y.txt
241+
GetMatch "DIR2/**/Y.TXT" | .Name => dir2/SUB/y.txt
242+
243+
Match "*/*/*.txt" | len => 2
244+
Match "**/*.txt" | len => 2
245+
`
246+
247+
b.AssertFileContent("public/index.html", want)
248+
249+
}

0 commit comments

Comments
 (0)