mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-24 07:18:54 +00:00
create: Fix non-deterministic conflict detection in hugo new content
The contentInclusionFilter used strings.Contains to match filenames against the target path. Because strings.Contains is a substring check, a directory entry like "content/about" matches "content/about.md", causing unrelated files to be pulled into the mini-build. Whether the conflict was then detected depended on whether the filesystem walker delivered a directory entry or a full file path. Also adds an upfront check for the directory-conflict case, since a corrected filter alone would allow about.md to be created alongside an existing about/ directory. Closes #12602 Closes #12786 Closes #14112 Closes #14769 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
committed by
Bjørn Erik Pedersen
parent
547ab29ced
commit
6436deb3e1
+17
-5
@@ -162,9 +162,8 @@ func (b *contentBuilder) buildDir() error {
|
||||
if !b.dirMap.siteUsed {
|
||||
// We don't need to build everything.
|
||||
contentInclusionFilter = hglob.NewFilenameFilterForInclusionFunc(func(filename string) bool {
|
||||
filename = strings.TrimPrefix(filename, string(os.PathSeparator))
|
||||
for _, cn := range contentTargetFilenames {
|
||||
if strings.Contains(cn, filename) {
|
||||
if strings.HasSuffix(cn, filename) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -219,6 +218,9 @@ func (b *contentBuilder) buildDir() error {
|
||||
func (b *contentBuilder) buildFile() (string, error) {
|
||||
contentPlaceholderAbsFilename, err := b.cf.CreateContentPlaceHolder(b.targetPath, b.force)
|
||||
if err != nil {
|
||||
if fi, serr := b.sourceFs.Stat(contentPlaceholderAbsFilename); serr == nil && !fi.IsDir() {
|
||||
return "", errTargetConflict(contentPlaceholderAbsFilename)
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -231,11 +233,17 @@ func (b *contentBuilder) buildFile() (string, error) {
|
||||
if !usesSite {
|
||||
// We don't need to build everything.
|
||||
contentInclusionFilter = hglob.NewFilenameFilterForInclusionFunc(func(filename string) bool {
|
||||
filename = strings.TrimPrefix(filename, string(os.PathSeparator))
|
||||
return strings.Contains(contentPlaceholderAbsFilename, filename)
|
||||
return strings.HasSuffix(contentPlaceholderAbsFilename, filename)
|
||||
})
|
||||
}
|
||||
|
||||
// If a directory with the target's name (sans extension) exists, this file
|
||||
// would produce a URL conflict with the existing section or leaf bundle.
|
||||
targetDir := strings.TrimSuffix(contentPlaceholderAbsFilename, filepath.Ext(contentPlaceholderAbsFilename))
|
||||
if fi, err := b.sourceFs.Stat(targetDir); err == nil && fi.IsDir() {
|
||||
return "", errTargetConflict(contentPlaceholderAbsFilename)
|
||||
}
|
||||
|
||||
if err := b.h.Build(hugolib.BuildCfg{NoBuildLock: true, SkipRender: true, ContentInclusionFilter: contentInclusionFilter}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -268,10 +276,14 @@ func (b *contentBuilder) setArcheTypeFilenameToUse(ext string) {
|
||||
}
|
||||
}
|
||||
|
||||
func errTargetConflict(path string) error {
|
||||
return fmt.Errorf("no page found for %q; the target path conflicts with existing content", path)
|
||||
}
|
||||
|
||||
func (b *contentBuilder) applyArcheType(contentFilename string, archetypeFi hugofs.FileMetaInfo) error {
|
||||
p := b.h.GetContentPage(contentFilename)
|
||||
if p == nil {
|
||||
return fmt.Errorf("no page found for %q; if a file with the same name but different case already exists, please use a different filename or remove the existing file", contentFilename)
|
||||
return errTargetConflict(contentFilename)
|
||||
}
|
||||
|
||||
f, err := b.sourceFs.Create(contentFilename)
|
||||
|
||||
@@ -5,8 +5,8 @@ hugo new content --kind post post/first-post.md
|
||||
grep 'draft = true' content/post/first-post.md
|
||||
|
||||
# Issue 12599
|
||||
cd $WORK
|
||||
|
||||
cd $WORK
|
||||
hugo new project --format toml --force issue-12599
|
||||
cp hugo.toml issue-12599/hugo.toml
|
||||
cd issue-12599
|
||||
@@ -16,6 +16,33 @@ grep 'DATE _2099-12-31_' public/s1/p1/index.html
|
||||
grep 'SLUG _p1_' public/s1/p1/index.html
|
||||
grep 'TITLE _2099 12 31 P1_' public/s1/p1/index.html
|
||||
|
||||
# Issue 12602
|
||||
|
||||
cd $WORK
|
||||
hugo new project issue-12602
|
||||
cd issue-12602
|
||||
hugo new content about/_index.md
|
||||
! hugo new content about.md
|
||||
stderr 'the target path conflicts with existing content'
|
||||
|
||||
# Issue 12786
|
||||
|
||||
cd $WORK
|
||||
hugo new project issue-12786
|
||||
cd issue-12786
|
||||
hugo new content content/p1/index.md
|
||||
! hugo new content content/p1/p2.md
|
||||
stderr 'the target path conflicts with existing content'
|
||||
|
||||
# Issue 14112
|
||||
|
||||
cd $WORK
|
||||
hugo new project issue-14112
|
||||
cd issue-14112
|
||||
hugo new content P1.md
|
||||
! hugo new content p1.md
|
||||
stderr 'the target path conflicts with existing content'
|
||||
|
||||
-- hugo.toml --
|
||||
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
|
||||
[frontmatter]
|
||||
|
||||
Reference in New Issue
Block a user