mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-24 15:28:54 +00:00
commands: Preserve non-content files in convert output
When running `hugo convert` with `--output`, copy the content tree first so non-content bundle resources are kept in the destination, then overwrite converted content files. Also avoid recursive self-copy when the output path points inside the content tree by skipping output directories during copy. Fixes #4621
This commit is contained in:
@@ -18,10 +18,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bep/simplecobra"
|
"github.com/bep/simplecobra"
|
||||||
|
"github.com/gohugoio/hugo/common/hugio"
|
||||||
"github.com/gohugoio/hugo/config"
|
"github.com/gohugoio/hugo/config"
|
||||||
"github.com/gohugoio/hugo/helpers"
|
"github.com/gohugoio/hugo/helpers"
|
||||||
"github.com/gohugoio/hugo/hugofs"
|
"github.com/gohugoio/hugo/hugofs"
|
||||||
@@ -200,6 +202,56 @@ func (c *convertCommand) convertAndSavePage(p page.Page, site *hugolib.Site, tar
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *convertCommand) copyContentDirsForOutput(pagesBackedByFile page.Pages) error {
|
||||||
|
contentDirs := make(map[string]bool)
|
||||||
|
for _, p := range pagesBackedByFile {
|
||||||
|
filename := p.File().Filename()
|
||||||
|
contentDir := strings.TrimSuffix(filename, p.File().Path())
|
||||||
|
if contentDir == filename {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
contentDirs[filepath.Clean(contentDir)] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var contentDirList []string
|
||||||
|
for contentDir := range contentDirs {
|
||||||
|
contentDirList = append(contentDirList, contentDir)
|
||||||
|
}
|
||||||
|
slices.Sort(contentDirList)
|
||||||
|
|
||||||
|
outputDirAbs, err := filepath.Abs(c.outputDir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to resolve output path %q: %w", c.outputDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, contentDir := range contentDirList {
|
||||||
|
outputContentDirAbs := filepath.Join(outputDirAbs, filepath.Base(contentDir))
|
||||||
|
|
||||||
|
skipDirs := make(map[string]bool)
|
||||||
|
relToOutputDir, err := filepath.Rel(contentDir, outputDirAbs)
|
||||||
|
if err == nil && relToOutputDir != ".." && !strings.HasPrefix(relToOutputDir, ".."+string(filepath.Separator)) {
|
||||||
|
skipDirs[filepath.Clean(outputDirAbs)] = true
|
||||||
|
}
|
||||||
|
relToOutputContentDir, err := filepath.Rel(contentDir, outputContentDirAbs)
|
||||||
|
if err == nil && relToOutputContentDir != ".." && !strings.HasPrefix(relToOutputContentDir, ".."+string(filepath.Separator)) {
|
||||||
|
skipDirs[filepath.Clean(outputContentDirAbs)] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var shouldCopy func(filename string) bool
|
||||||
|
if len(skipDirs) > 0 {
|
||||||
|
shouldCopy = func(filename string) bool {
|
||||||
|
return !skipDirs[filepath.Clean(filename)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := hugio.CopyDir(hugofs.Os, contentDir, outputContentDirAbs, shouldCopy); err != nil {
|
||||||
|
return fmt.Errorf("failed to copy %q to %q: %w", contentDir, outputContentDirAbs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *convertCommand) convertContents(format metadecoders.Format) error {
|
func (c *convertCommand) convertContents(format metadecoders.Format) error {
|
||||||
if c.outputDir == "" && !c.unsafe {
|
if c.outputDir == "" && !c.unsafe {
|
||||||
return newUserError("Unsafe operation not allowed, use --unsafe or set a different output path")
|
return newUserError("Unsafe operation not allowed, use --unsafe or set a different output path")
|
||||||
@@ -219,6 +271,12 @@ func (c *convertCommand) convertContents(format metadecoders.Format) error {
|
|||||||
pagesBackedByFile = append(pagesBackedByFile, p)
|
pagesBackedByFile = append(pagesBackedByFile, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.outputDir != "" {
|
||||||
|
if err := c.copyContentDirsForOutput(pagesBackedByFile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
site.Log.Println("processing", len(pagesBackedByFile), "content files")
|
site.Log.Println("processing", len(pagesBackedByFile), "content files")
|
||||||
for _, p := range site.AllPages() {
|
for _, p := range site.AllPages() {
|
||||||
if err := c.convertAndSavePage(p, site, format); err != nil {
|
if err := c.convertAndSavePage(p, site, format); err != nil {
|
||||||
|
|||||||
@@ -10,14 +10,21 @@ hugo convert toYAML -h
|
|||||||
stdout 'to use YAML for the front matter'
|
stdout 'to use YAML for the front matter'
|
||||||
|
|
||||||
hugo convert toJSON -o myjsoncontent
|
hugo convert toJSON -o myjsoncontent
|
||||||
stdout 'processing 3 content files'
|
stdout 'processing 4 content files'
|
||||||
grep '^{' myjsoncontent/content/mytoml.md
|
grep '^{' myjsoncontent/content/mytoml.md
|
||||||
grep '^{' myjsoncontent/content/myjson.md
|
grep '^{' myjsoncontent/content/myjson.md
|
||||||
grep '^{' myjsoncontent/content/myyaml.md
|
grep '^{' myjsoncontent/content/myyaml.md
|
||||||
|
grep '^{' myjsoncontent/content/bundle/index.md
|
||||||
|
exists myjsoncontent/content/bundle/data.txt
|
||||||
|
exists myjsoncontent/content/bundle/nested/asset.dat
|
||||||
hugo convert toYAML -o myyamlcontent
|
hugo convert toYAML -o myyamlcontent
|
||||||
stdout 'processing 3 content files'
|
stdout 'processing 4 content files'
|
||||||
|
exists myyamlcontent/content/bundle/data.txt
|
||||||
|
exists myyamlcontent/content/bundle/nested/asset.dat
|
||||||
hugo convert toTOML -o mytomlcontent
|
hugo convert toTOML -o mytomlcontent
|
||||||
stdout 'processing 3 content files'
|
stdout 'processing 4 content files'
|
||||||
|
exists mytomlcontent/content/bundle/data.txt
|
||||||
|
exists mytomlcontent/content/bundle/nested/asset.dat
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -40,3 +47,12 @@ JSON content
|
|||||||
title: YAML
|
title: YAML
|
||||||
---
|
---
|
||||||
YAML content
|
YAML content
|
||||||
|
-- content/bundle/index.md --
|
||||||
|
---
|
||||||
|
title: Bundle
|
||||||
|
---
|
||||||
|
Bundle content
|
||||||
|
-- content/bundle/data.txt --
|
||||||
|
bundle resource
|
||||||
|
-- content/bundle/nested/asset.dat --
|
||||||
|
nested resource
|
||||||
|
|||||||
Reference in New Issue
Block a user