Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/gohugoio/hugo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2022-04-08 16:15:26 +0300
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2022-04-10 14:49:31 +0300
commit30c2e54c25f6c3a942080f30be49712adda27586 (patch)
tree6805e432456f92623db0b1c862879cee54693481 /hugolib
parent3117e5859509e909298153972ab6f700af577f92 (diff)
Replace all usage of CopyOnWriteFs with OverlayFs
Fixes #9761
Diffstat (limited to 'hugolib')
-rw-r--r--hugolib/content_factory.go1
-rw-r--r--hugolib/datafiles_test.go32
-rw-r--r--hugolib/filesystems/basefs.go315
-rw-r--r--hugolib/hugo_modules_test.go2
-rw-r--r--hugolib/paths/paths.go6
5 files changed, 186 insertions, 170 deletions
diff --git a/hugolib/content_factory.go b/hugolib/content_factory.go
index bf16a9821..bea98894d 100644
--- a/hugolib/content_factory.go
+++ b/hugolib/content_factory.go
@@ -112,6 +112,7 @@ func (f ContentFactory) SectionFromFilename(filename string) (string, error) {
func (f ContentFactory) CreateContentPlaceHolder(filename string) (string, error) {
filename = filepath.Clean(filename)
_, abs, err := f.h.AbsProjectContentDir(filename)
+
if err != nil {
return "", err
}
diff --git a/hugolib/datafiles_test.go b/hugolib/datafiles_test.go
index 6cbe7bbc6..a6bcae944 100644
--- a/hugolib/datafiles_test.go
+++ b/hugolib/datafiles_test.go
@@ -27,6 +27,38 @@ import (
qt "github.com/frankban/quicktest"
)
+func TestDataFromTheme(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- config.toml --
+[module]
+[[module.imports]]
+path = "mytheme"
+-- data/a.toml --
+d1 = "d1main"
+d2 = "d2main"
+-- themes/mytheme/data/a.toml --
+d1 = "d1theme"
+d2 = "d2theme"
+d3 = "d3theme"
+-- layouts/index.html --
+d1: {{ site.Data.a.d1 }}|d2: {{ site.Data.a.d2 }}|d3: {{ site.Data.a.d3 }}
+
+`
+
+ b := NewIntegrationTestBuilder(
+ IntegrationTestConfig{
+ T: t,
+ TxtarString: files,
+ },
+ ).Build()
+
+ b.AssertFileContent("public/index.html", `
+d1: d1main|d2: d2main|d3: d3theme
+ `)
+}
+
func TestDataDir(t *testing.T) {
t.Parallel()
equivDataDirs := make([]dataDir, 3)
diff --git a/hugolib/filesystems/basefs.go b/hugolib/filesystems/basefs.go
index 693dd8575..d02f8c624 100644
--- a/hugolib/filesystems/basefs.go
+++ b/hugolib/filesystems/basefs.go
@@ -24,6 +24,7 @@ import (
"strings"
"sync"
+ "github.com/bep/overlayfs"
"github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/hugofs/glob"
@@ -145,12 +146,14 @@ func (b *BaseFs) AbsProjectContentDir(filename string) (string, string, error) {
if !meta.IsProject {
continue
}
+
if isAbs {
if strings.HasPrefix(filename, meta.Filename) {
return strings.TrimPrefix(filename, meta.Filename), filename, nil
}
} else {
- contentDir := strings.TrimPrefix(strings.TrimPrefix(meta.Filename, meta.BaseDir), filePathSeparator)
+ contentDir := strings.TrimPrefix(strings.TrimPrefix(meta.Filename, meta.BaseDir), filePathSeparator) + filePathSeparator
+
if strings.HasPrefix(filename, contentDir) {
relFilename := strings.TrimPrefix(filename, contentDir)
absFilename := filepath.Join(meta.Filename, relFilename)
@@ -163,14 +166,14 @@ func (b *BaseFs) AbsProjectContentDir(filename string) (string, string, error) {
if !isAbs {
// A filename on the form "posts/mypage.md", put it inside
// the first content folder, usually <workDir>/content.
- // Pick the last project dir (which is probably the most important one).
- contentDirs := b.SourceFilesystems.Content.Dirs
- for i := len(contentDirs) - 1; i >= 0; i-- {
- meta := contentDirs[i].Meta()
+ // Pick the first project dir (which is probably the most important one).
+ for _, dir := range b.SourceFilesystems.Content.Dirs {
+ meta := dir.Meta()
if meta.IsProject {
return filename, filepath.Join(meta.Filename, filename), nil
}
}
+
}
return "", "", errors.Errorf("could not determine content directory for %q", filename)
@@ -260,10 +263,16 @@ type SourceFilesystem struct {
// The order is content, static and then assets.
// TODO(bep) check usage
func (s SourceFilesystems) ContentStaticAssetFs(lang string) afero.Fs {
- staticFs := s.StaticFs(lang)
+ return overlayfs.New(
+ overlayfs.Options{
+ Fss: []afero.Fs{
+ s.Content.Fs,
+ s.StaticFs(lang),
+ s.Assets.Fs,
+ },
+ },
+ )
- base := afero.NewCopyOnWriteFs(s.Assets.Fs, staticFs)
- return afero.NewCopyOnWriteFs(base, s.Content.Fs)
}
// StaticFs returns the static filesystem for the given language.
@@ -491,7 +500,6 @@ func (b *sourceFilesystemsBuilder) newSourceFilesystem(name string, fs afero.Fs,
func (b *sourceFilesystemsBuilder) Build() (*SourceFilesystems, error) {
if b.theBigFs == nil {
-
theBigFs, err := b.createMainOverlayFs(b.p)
if err != nil {
return nil, errors.Wrap(err, "create main fs")
@@ -510,8 +518,6 @@ func (b *sourceFilesystemsBuilder) Build() (*SourceFilesystems, error) {
return b.newSourceFilesystem(componentID, afero.NewBasePathFs(b.theBigFs.overlayMounts, componentID), dirs)
}
- b.theBigFs.finalizeDirs()
-
b.result.Archetypes = createView(files.ComponentFolderArchetypes)
b.result.Layouts = createView(files.ComponentFolderLayouts)
b.result.Assets = createView(files.ComponentFolderAssets)
@@ -566,9 +572,12 @@ func (b *sourceFilesystemsBuilder) Build() (*SourceFilesystems, error) {
}
func (b *sourceFilesystemsBuilder) createMainOverlayFs(p *paths.Paths) (*filesystemsCollector, error) {
- var staticFsMap map[string]afero.Fs
+ var staticFsMap map[string]*overlayfs.OverlayFs
if b.p.Cfg.GetBool("multihost") {
- staticFsMap = make(map[string]afero.Fs)
+ staticFsMap = make(map[string]*overlayfs.OverlayFs)
+ for _, l := range b.p.Languages {
+ staticFsMap[l.Lang] = overlayfs.New(overlayfs.Options{})
+ }
}
collector := &filesystemsCollector{
@@ -576,35 +585,33 @@ func (b *sourceFilesystemsBuilder) createMainOverlayFs(p *paths.Paths) (*filesys
sourceModules: hugofs.NewNoSymlinkFs(b.sourceFs, b.logger, false),
overlayDirs: make(map[string][]hugofs.FileMetaInfo),
staticPerLanguage: staticFsMap,
+
+ overlayMounts: overlayfs.New(overlayfs.Options{}),
+ overlayMountsContent: overlayfs.New(overlayfs.Options{DirsMerger: hugofs.LanguageDirsMerger}),
+ overlayMountsStatic: overlayfs.New(overlayfs.Options{DirsMerger: hugofs.LanguageDirsMerger}),
+ overlayFull: overlayfs.New(overlayfs.Options{}),
+ overlayResources: overlayfs.New(overlayfs.Options{FirstWritable: true}),
}
mods := p.AllModules
- if len(mods) == 0 {
- return collector, nil
- }
-
- modsReversed := make([]mountsDescriptor, len(mods))
+ mounts := make([]mountsDescriptor, len(mods))
- // The theme components are ordered from left to right.
- // We need to revert it to get the
- // overlay logic below working as expected, with the project on top.
- j := 0
- for i := len(mods) - 1; i >= 0; i-- {
+ for i := 0; i < len(mods); i++ {
mod := mods[i]
dir := mod.Dir()
isMainProject := mod.Owner() == nil
- modsReversed[j] = mountsDescriptor{
+ mounts[i] = mountsDescriptor{
Module: mod,
dir: dir,
isMainProject: isMainProject,
- ordinal: j,
+ ordinal: i,
}
- j++
+
}
- err := b.createOverlayFs(collector, modsReversed)
+ err := b.createOverlayFs(collector, mounts)
return collector, err
}
@@ -617,137 +624,143 @@ func (b *sourceFilesystemsBuilder) isStaticMount(mnt modules.Mount) bool {
return strings.HasPrefix(mnt.Target, files.ComponentFolderStatic)
}
-func (b *sourceFilesystemsBuilder) createModFs(
+func (b *sourceFilesystemsBuilder) createOverlayFs(
collector *filesystemsCollector,
- md mountsDescriptor) error {
- var (
- fromTo []hugofs.RootMapping
- fromToContent []hugofs.RootMapping
- fromToStatic []hugofs.RootMapping
- )
+ mounts []mountsDescriptor) error {
- absPathify := func(path string) (string, string) {
- if filepath.IsAbs(path) {
- return "", path
+ if len(mounts) == 0 {
+ appendNopIfEmpty := func(ofs *overlayfs.OverlayFs) *overlayfs.OverlayFs {
+ if ofs.NumFilesystems() > 0 {
+ return ofs
+ }
+ return ofs.Append(hugofs.NoOpFs)
}
- return md.dir, hpaths.AbsPathify(md.dir, path)
- }
+ collector.overlayMounts = appendNopIfEmpty(collector.overlayMounts)
+ collector.overlayMountsContent = appendNopIfEmpty(collector.overlayMountsContent)
+ collector.overlayMountsStatic = appendNopIfEmpty(collector.overlayMountsStatic)
+ collector.overlayFull = appendNopIfEmpty(collector.overlayFull)
+ collector.overlayResources = appendNopIfEmpty(collector.overlayResources)
- for i, mount := range md.Mounts() {
-
- // Add more weight to early mounts.
- // When two mounts contain the same filename,
- // the first entry wins.
- mountWeight := (10 + md.ordinal) * (len(md.Mounts()) - i)
+ return nil
+ }
- inclusionFilter, err := glob.NewFilenameFilter(
- types.ToStringSlicePreserveString(mount.IncludeFiles),
- types.ToStringSlicePreserveString(mount.ExcludeFiles),
+ for _, md := range mounts {
+ var (
+ fromTo []hugofs.RootMapping
+ fromToContent []hugofs.RootMapping
+ fromToStatic []hugofs.RootMapping
)
- if err != nil {
- return err
- }
- base, filename := absPathify(mount.Source)
-
- rm := hugofs.RootMapping{
- From: mount.Target,
- To: filename,
- ToBasedir: base,
- Module: md.Module.Path(),
- IsProject: md.isMainProject,
- Meta: &hugofs.FileMeta{
- Watch: md.Watch(),
- Weight: mountWeight,
- Classifier: files.ContentClassContent,
- InclusionFilter: inclusionFilter,
- },
+ absPathify := func(path string) (string, string) {
+ if filepath.IsAbs(path) {
+ return "", path
+ }
+ return md.dir, hpaths.AbsPathify(md.dir, path)
}
- isContentMount := b.isContentMount(mount)
+ for i, mount := range md.Mounts() {
- lang := mount.Lang
- if lang == "" && isContentMount {
- lang = b.p.DefaultContentLanguage
- }
+ // Add more weight to early mounts.
+ // When two mounts contain the same filename,
+ // the first entry wins.
+ mountWeight := (10 + md.ordinal) * (len(md.Mounts()) - i)
- rm.Meta.Lang = lang
+ inclusionFilter, err := glob.NewFilenameFilter(
+ types.ToStringSlicePreserveString(mount.IncludeFiles),
+ types.ToStringSlicePreserveString(mount.ExcludeFiles),
+ )
+ if err != nil {
+ return err
+ }
- if isContentMount {
- fromToContent = append(fromToContent, rm)
- } else if b.isStaticMount(mount) {
- fromToStatic = append(fromToStatic, rm)
- } else {
- fromTo = append(fromTo, rm)
+ base, filename := absPathify(mount.Source)
+
+ rm := hugofs.RootMapping{
+ From: mount.Target,
+ To: filename,
+ ToBasedir: base,
+ Module: md.Module.Path(),
+ IsProject: md.isMainProject,
+ Meta: &hugofs.FileMeta{
+ Watch: md.Watch(),
+ Weight: mountWeight,
+ Classifier: files.ContentClassContent,
+ InclusionFilter: inclusionFilter,
+ },
+ }
+
+ isContentMount := b.isContentMount(mount)
+
+ lang := mount.Lang
+ if lang == "" && isContentMount {
+ lang = b.p.DefaultContentLanguage
+ }
+
+ rm.Meta.Lang = lang
+
+ if isContentMount {
+ fromToContent = append(fromToContent, rm)
+ } else if b.isStaticMount(mount) {
+ fromToStatic = append(fromToStatic, rm)
+ } else {
+ fromTo = append(fromTo, rm)
+ }
}
- }
- modBase := collector.sourceProject
- if !md.isMainProject {
- modBase = collector.sourceModules
- }
- sourceStatic := hugofs.NewNoSymlinkFs(modBase, b.logger, true)
+ modBase := collector.sourceProject
+ if !md.isMainProject {
+ modBase = collector.sourceModules
+ }
+ sourceStatic := hugofs.NewNoSymlinkFs(modBase, b.logger, true)
- rmfs, err := hugofs.NewRootMappingFs(modBase, fromTo...)
- if err != nil {
- return err
- }
- rmfsContent, err := hugofs.NewRootMappingFs(modBase, fromToContent...)
- if err != nil {
- return err
- }
- rmfsStatic, err := hugofs.NewRootMappingFs(sourceStatic, fromToStatic...)
- if err != nil {
- return err
- }
+ rmfs, err := hugofs.NewRootMappingFs(modBase, fromTo...)
+ if err != nil {
+ return err
+ }
+ rmfsContent, err := hugofs.NewRootMappingFs(modBase, fromToContent...)
+ if err != nil {
+ return err
+ }
+ rmfsStatic, err := hugofs.NewRootMappingFs(sourceStatic, fromToStatic...)
+ if err != nil {
+ return err
+ }
- // We need to keep the ordered list of directories for watching and
- // some special merge operations (data, i18n).
- collector.addDirs(rmfs)
- collector.addDirs(rmfsContent)
- collector.addDirs(rmfsStatic)
+ // We need to keep the ordered list of directories for watching and
+ // some special merge operations (data, i18n).
+ collector.addDirs(rmfs)
+ collector.addDirs(rmfsContent)
+ collector.addDirs(rmfsStatic)
- if collector.staticPerLanguage != nil {
- for _, l := range b.p.Languages {
- lang := l.Lang
+ if collector.staticPerLanguage != nil {
+ for _, l := range b.p.Languages {
+ lang := l.Lang
- lfs := rmfsStatic.Filter(func(rm hugofs.RootMapping) bool {
- rlang := rm.Meta.Lang
- return rlang == "" || rlang == lang
- })
+ lfs := rmfsStatic.Filter(func(rm hugofs.RootMapping) bool {
+ rlang := rm.Meta.Lang
+ return rlang == "" || rlang == lang
+ })
- bfs := afero.NewBasePathFs(lfs, files.ComponentFolderStatic)
+ bfs := afero.NewBasePathFs(lfs, files.ComponentFolderStatic)
+ collector.staticPerLanguage[lang] = collector.staticPerLanguage[lang].Append(bfs)
- sfs, found := collector.staticPerLanguage[lang]
- if found {
- collector.staticPerLanguage[lang] = afero.NewCopyOnWriteFs(sfs, bfs)
- } else {
- collector.staticPerLanguage[lang] = bfs
}
}
- }
- getResourcesDir := func() string {
- if md.isMainProject {
- return b.p.AbsResourcesDir
+ getResourcesDir := func() string {
+ if md.isMainProject {
+ return b.p.AbsResourcesDir
+ }
+ _, filename := absPathify(files.FolderResources)
+ return filename
}
- _, filename := absPathify(files.FolderResources)
- return filename
- }
- if collector.overlayMounts == nil {
- collector.overlayMounts = rmfs
- collector.overlayMountsContent = rmfsContent
- collector.overlayMountsStatic = rmfsStatic
- collector.overlayFull = afero.NewBasePathFs(modBase, md.dir)
- collector.overlayResources = afero.NewBasePathFs(modBase, getResourcesDir())
- } else {
+ collector.overlayMounts = collector.overlayMounts.Append(rmfs)
+ collector.overlayMountsContent = collector.overlayMountsContent.Append(rmfsContent)
+ collector.overlayMountsStatic = collector.overlayMountsStatic.Append(rmfsStatic)
+ collector.overlayFull = collector.overlayFull.Append(afero.NewBasePathFs(modBase, md.dir))
+ collector.overlayResources = collector.overlayResources.Append(afero.NewBasePathFs(modBase, getResourcesDir()))
- collector.overlayMounts = afero.NewCopyOnWriteFs(collector.overlayMounts, rmfs)
- collector.overlayMountsContent = hugofs.NewLanguageCompositeFs(collector.overlayMountsContent, rmfsContent)
- collector.overlayMountsStatic = hugofs.NewLanguageCompositeFs(collector.overlayMountsStatic, rmfsStatic)
- collector.overlayFull = afero.NewCopyOnWriteFs(collector.overlayFull, afero.NewBasePathFs(modBase, md.dir))
- collector.overlayResources = afero.NewCopyOnWriteFs(collector.overlayResources, afero.NewBasePathFs(modBase, getResourcesDir()))
}
return nil
@@ -777,18 +790,18 @@ type filesystemsCollector struct {
sourceProject afero.Fs // Source for project folders
sourceModules afero.Fs // Source for modules/themes
- overlayMounts afero.Fs
- overlayMountsContent afero.Fs
- overlayMountsStatic afero.Fs
- overlayFull afero.Fs
- overlayResources afero.Fs
+ overlayMounts *overlayfs.OverlayFs
+ overlayMountsContent *overlayfs.OverlayFs
+ overlayMountsStatic *overlayfs.OverlayFs
+ overlayFull *overlayfs.OverlayFs
+ overlayResources *overlayfs.OverlayFs
// Maps component type (layouts, static, content etc.) an ordered list of
// directories representing the overlay filesystems above.
overlayDirs map[string][]hugofs.FileMetaInfo
// Set if in multihost mode
- staticPerLanguage map[string]afero.Fs
+ staticPerLanguage map[string]*overlayfs.OverlayFs
finalizerInit sync.Once
}
@@ -807,15 +820,6 @@ func (c *filesystemsCollector) addDir(rfs *hugofs.RootMappingFs, componentFolder
}
}
-func (c *filesystemsCollector) finalizeDirs() {
- c.finalizerInit.Do(func() {
- // Order the directories from top to bottom (project, theme a, theme ...).
- for _, dirs := range c.overlayDirs {
- c.reverseFis(dirs)
- }
- })
-}
-
func (c *filesystemsCollector) reverseFis(fis []hugofs.FileMetaInfo) {
for i := len(fis)/2 - 1; i >= 0; i-- {
opp := len(fis) - 1 - i
@@ -829,20 +833,3 @@ type mountsDescriptor struct {
isMainProject bool
ordinal int
}
-
-func (b *sourceFilesystemsBuilder) createOverlayFs(collector *filesystemsCollector, mounts []mountsDescriptor) error {
- if len(mounts) == 0 {
- return nil
- }
-
- err := b.createModFs(collector, mounts[0])
- if err != nil {
- return err
- }
-
- if len(mounts) == 1 {
- return nil
- }
-
- return b.createOverlayFs(collector, mounts[1:])
-}
diff --git a/hugolib/hugo_modules_test.go b/hugolib/hugo_modules_test.go
index 358286495..aca3f157c 100644
--- a/hugolib/hugo_modules_test.go
+++ b/hugolib/hugo_modules_test.go
@@ -777,6 +777,8 @@ weight = 2
}
}
+ c.Logf("Checking %d:%d %q", i, j, id)
+
statCheck(componentFs, fmt.Sprintf("realsym%s", id), true)
statCheck(componentFs, fmt.Sprintf("real/datasym%s.toml", id), false)
diff --git a/hugolib/paths/paths.go b/hugolib/paths/paths.go
index f6e7b1a76..9d5716e16 100644
--- a/hugolib/paths/paths.go
+++ b/hugolib/paths/paths.go
@@ -53,9 +53,6 @@ type Paths struct {
// pagination path handling
PaginatePath string
- // TODO1 check usage
- PublishDir string
-
// When in multihost mode, this returns a list of base paths below PublishDir
// for each language.
MultihostTargetBasePaths []string
@@ -185,9 +182,6 @@ func New(fs *hugofs.Fs, cfg config.Provider) (*Paths, error) {
p.ModulesClient = cfg.Get("modulesClient").(*modules.Client)
}
- // TODO(bep) remove this, eventually
- p.PublishDir = absPublishDir
-
return p, nil
}