Revert "Revert "Allow rendering static files to disk and dynamic to memory in server mode""

This reverts commit 64b7b7a897.
This commit is contained in:
Bjørn Erik Pedersen 2022-03-14 16:02:04 +01:00
parent 9e360d3844
commit 0a56f2af4e
No known key found for this signature in database
GPG key ID: 330E6E2BD4859D8F
8 changed files with 69 additions and 25 deletions

View file

@ -94,6 +94,7 @@ type commandeer struct {
languagesConfigured bool languagesConfigured bool
languages langs.Languages languages langs.Languages
doLiveReload bool doLiveReload bool
renderStaticToDisk bool
fastRenderMode bool fastRenderMode bool
showErrorInBrowser bool showErrorInBrowser bool
wasError bool wasError bool
@ -375,8 +376,9 @@ func (c *commandeer) loadConfig() error {
} }
createMemFs := config.GetBool("renderToMemory") createMemFs := config.GetBool("renderToMemory")
c.renderStaticToDisk = config.GetBool("renderStaticToDisk")
if createMemFs { if createMemFs && !c.renderStaticToDisk {
// Rendering to memoryFS, publish to Root regardless of publishDir. // Rendering to memoryFS, publish to Root regardless of publishDir.
config.Set("publishDir", "/") config.Set("publishDir", "/")
} }
@ -387,6 +389,14 @@ func (c *commandeer) loadConfig() error {
if c.destinationFs != nil { if c.destinationFs != nil {
// Need to reuse the destination on server rebuilds. // Need to reuse the destination on server rebuilds.
fs.Destination = c.destinationFs fs.Destination = c.destinationFs
} else if createMemFs && c.renderStaticToDisk {
// Writes the dynamic output on memory,
// while serve others directly from publishDir
publishDir := config.GetString("publishDir")
writableFs := afero.NewBasePathFs(afero.NewMemMapFs(), publishDir)
publicFs := afero.NewOsFs()
fs.Destination = afero.NewCopyOnWriteFs(afero.NewReadOnlyFs(publicFs), writableFs)
fs.DestinationStatic = publicFs
} else if createMemFs { } else if createMemFs {
// Hugo writes the output to memory instead of the disk. // Hugo writes the output to memory instead of the disk.
fs.Destination = new(afero.MemMapFs) fs.Destination = new(afero.MemMapFs)
@ -404,11 +414,13 @@ func (c *commandeer) loadConfig() error {
changeDetector.PrepareNew() changeDetector.PrepareNew()
fs.Destination = hugofs.NewHashingFs(fs.Destination, changeDetector) fs.Destination = hugofs.NewHashingFs(fs.Destination, changeDetector)
fs.DestinationStatic = hugofs.NewHashingFs(fs.DestinationStatic, changeDetector)
c.changeDetector = changeDetector c.changeDetector = changeDetector
} }
if c.Cfg.GetBool("logPathWarnings") { if c.Cfg.GetBool("logPathWarnings") {
fs.Destination = hugofs.NewCreateCountingFs(fs.Destination) fs.Destination = hugofs.NewCreateCountingFs(fs.Destination)
fs.DestinationStatic = hugofs.NewCreateCountingFs(fs.DestinationStatic)
} }
// To debug hard-to-find path issues. // To debug hard-to-find path issues.

View file

@ -652,6 +652,9 @@ func (c *commandeer) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint6
syncer.ChmodFilter = chmodFilter syncer.ChmodFilter = chmodFilter
syncer.SrcFs = fs syncer.SrcFs = fs
syncer.DestFs = c.Fs.Destination syncer.DestFs = c.Fs.Destination
if c.renderStaticToDisk {
syncer.DestFs = c.Fs.DestinationStatic
}
// Now that we are using a unionFs for the static directories // Now that we are using a unionFs for the static directories
// We can effectively clean the publishDir on initial sync // We can effectively clean the publishDir on initial sync
syncer.Delete = c.Cfg.GetBool("cleanDestinationDir") syncer.Delete = c.Cfg.GetBool("cleanDestinationDir")

View file

@ -53,6 +53,7 @@ type serverCmd struct {
disableLiveReload bool disableLiveReload bool
navigateToChanged bool navigateToChanged bool
renderToDisk bool renderToDisk bool
renderStaticToDisk bool
serverAppend bool serverAppend bool
serverInterface string serverInterface string
serverPort int serverPort int
@ -109,6 +110,7 @@ of a second, you will be able to save and see your changes nearly instantly.`,
cc.cmd.Flags().BoolVar(&cc.renderToDisk, "renderToDisk", false, "render to Destination path (default is render to memory & serve from there)") cc.cmd.Flags().BoolVar(&cc.renderToDisk, "renderToDisk", false, "render to Destination path (default is render to memory & serve from there)")
cc.cmd.Flags().BoolVar(&cc.disableFastRender, "disableFastRender", false, "enables full re-renders on changes") cc.cmd.Flags().BoolVar(&cc.disableFastRender, "disableFastRender", false, "enables full re-renders on changes")
cc.cmd.Flags().BoolVar(&cc.disableBrowserError, "disableBrowserError", false, "do not show build errors in the browser") cc.cmd.Flags().BoolVar(&cc.disableBrowserError, "disableBrowserError", false, "do not show build errors in the browser")
cc.cmd.Flags().BoolVar(&cc.renderStaticToDisk, "renderStaticToDisk", false, "render static files to disk but dynamic files render to memory.")
cc.cmd.Flags().String("memstats", "", "log memory usage to this file") cc.cmd.Flags().String("memstats", "", "log memory usage to this file")
cc.cmd.Flags().String("meminterval", "100ms", "interval to poll memory usage (requires --memstats), valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".") cc.cmd.Flags().String("meminterval", "100ms", "interval to poll memory usage (requires --memstats), valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".")
@ -147,6 +149,7 @@ func (sc *serverCmd) server(cmd *cobra.Command, args []string) error {
cfgInit := func(c *commandeer) (rerr error) { cfgInit := func(c *commandeer) (rerr error) {
c.Set("renderToMemory", !sc.renderToDisk) c.Set("renderToMemory", !sc.renderToDisk)
c.Set("renderStaticToDisk", sc.renderStaticToDisk)
if cmd.Flags().Changed("navigateToChanged") { if cmd.Flags().Changed("navigateToChanged") {
c.Set("navigateToChanged", sc.navigateToChanged) c.Set("navigateToChanged", sc.navigateToChanged)
} }
@ -340,6 +343,8 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
if i == 0 { if i == 0 {
if f.s.renderToDisk { if f.s.renderToDisk {
jww.FEEDBACK.Println("Serving pages from " + absPublishDir) jww.FEEDBACK.Println("Serving pages from " + absPublishDir)
} else if f.s.renderStaticToDisk {
jww.FEEDBACK.Println("Serving pages from memory and static files from " + absPublishDir)
} else { } else {
jww.FEEDBACK.Println("Serving pages from memory") jww.FEEDBACK.Println("Serving pages from memory")
} }

View file

@ -56,6 +56,9 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
syncer.ChmodFilter = chmodFilter syncer.ChmodFilter = chmodFilter
syncer.SrcFs = sourceFs.Fs syncer.SrcFs = sourceFs.Fs
syncer.DestFs = c.Fs.Destination syncer.DestFs = c.Fs.Destination
if c.renderStaticToDisk {
syncer.DestFs = c.Fs.DestinationStatic
}
// prevent spamming the log on changes // prevent spamming the log on changes
logger := helpers.NewDistinctErrorLogger() logger := helpers.NewDistinctErrorLogger()
@ -101,7 +104,11 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
toRemove := filepath.Join(publishDir, relPath) toRemove := filepath.Join(publishDir, relPath)
logger.Println("File no longer exists in static dir, removing", toRemove) logger.Println("File no longer exists in static dir, removing", toRemove)
if c.renderStaticToDisk {
_ = c.Fs.DestinationStatic.RemoveAll(toRemove)
} else {
_ = c.Fs.Destination.RemoveAll(toRemove) _ = c.Fs.Destination.RemoveAll(toRemove)
}
} else if err == nil { } else if err == nil {
// If file still exists, sync it // If file still exists, sync it
logger.Println("Syncing", relPath, "to", publishDir) logger.Println("Syncing", relPath, "to", publishDir)

View file

@ -35,6 +35,9 @@ type Fs struct {
// Destination is Hugo's destination file system. // Destination is Hugo's destination file system.
Destination afero.Fs Destination afero.Fs
// Destination used for `renderStaticToDisk`
DestinationStatic afero.Fs
// Os is an OS file system. // Os is an OS file system.
// NOTE: Field is currently unused. // NOTE: Field is currently unused.
Os afero.Fs Os afero.Fs
@ -71,6 +74,7 @@ func newFs(base afero.Fs, cfg config.Provider) *Fs {
return &Fs{ return &Fs{
Source: base, Source: base,
Destination: base, Destination: base,
DestinationStatic: base,
Os: &afero.OsFs{}, Os: &afero.OsFs{},
WorkingDir: getWorkingDirFs(base, cfg), WorkingDir: getWorkingDirFs(base, cfg),
} }

View file

@ -71,6 +71,9 @@ type BaseFs struct {
// A read-only filesystem starting from the project workDir. // A read-only filesystem starting from the project workDir.
WorkDir afero.Fs WorkDir afero.Fs
// The filesystem used for renderStaticToDisk.
PublishFsStatic afero.Fs
theBigFs *filesystemsCollector theBigFs *filesystemsCollector
// Locks. // Locks.
@ -438,6 +441,7 @@ func NewBase(p *paths.Paths, logger loggers.Logger, options ...func(*BaseFs) err
publishFs := hugofs.NewBaseFileDecorator(afero.NewBasePathFs(fs.Destination, p.AbsPublishDir)) publishFs := hugofs.NewBaseFileDecorator(afero.NewBasePathFs(fs.Destination, p.AbsPublishDir))
sourceFs := hugofs.NewBaseFileDecorator(afero.NewBasePathFs(fs.Source, p.WorkingDir)) sourceFs := hugofs.NewBaseFileDecorator(afero.NewBasePathFs(fs.Source, p.WorkingDir))
publishFsStatic := afero.NewBasePathFs(fs.Source, p.AbsPublishDir)
// Same as sourceFs, but no decoration. This is what's used by os.ReadDir etc. // Same as sourceFs, but no decoration. This is what's used by os.ReadDir etc.
workDir := afero.NewBasePathFs(afero.NewReadOnlyFs(fs.Source), p.WorkingDir) workDir := afero.NewBasePathFs(afero.NewReadOnlyFs(fs.Source), p.WorkingDir)
@ -446,6 +450,7 @@ func NewBase(p *paths.Paths, logger loggers.Logger, options ...func(*BaseFs) err
SourceFs: sourceFs, SourceFs: sourceFs,
WorkDir: workDir, WorkDir: workDir,
PublishFs: publishFs, PublishFs: publishFs,
PublishFsStatic: publishFsStatic,
buildMu: lockedfile.MutexAt(filepath.Join(p.WorkingDir, lockFileBuild)), buildMu: lockedfile.MutexAt(filepath.Join(p.WorkingDir, lockFileBuild)),
} }

View file

@ -35,7 +35,8 @@ func newPagesProcessor(h *HugoSites, sp *source.SourceSpec) *pagesProcessor {
procs[s.Lang()] = &sitePagesProcessor{ procs[s.Lang()] = &sitePagesProcessor{
m: s.pageMap, m: s.pageMap,
errorSender: s.h, errorSender: s.h,
itemChan: make(chan any, config.GetNumWorkerMultiplier()*2), itemChan: make(chan interface{}, config.GetNumWorkerMultiplier()*2),
renderStaticToDisk: h.Cfg.GetBool("renderStaticToDisk"),
} }
} }
return &pagesProcessor{ return &pagesProcessor{
@ -118,6 +119,8 @@ type sitePagesProcessor struct {
ctx context.Context ctx context.Context
itemChan chan any itemChan chan any
itemGroup *errgroup.Group itemGroup *errgroup.Group
renderStaticToDisk bool
} }
func (p *sitePagesProcessor) Process(item any) error { func (p *sitePagesProcessor) Process(item any) error {
@ -162,7 +165,12 @@ func (p *sitePagesProcessor) copyFile(fim hugofs.FileMetaInfo) error {
defer f.Close() defer f.Close()
return s.publish(&s.PathSpec.ProcessingStats.Files, target, f) fs := s.PublishFs
if p.renderStaticToDisk {
fs = s.PublishFsStatic
}
return s.publish(&s.PathSpec.ProcessingStats.Files, target, f, fs)
} }
func (p *sitePagesProcessor) doProcess(item any) error { func (p *sitePagesProcessor) doProcess(item any) error {

View file

@ -1829,10 +1829,10 @@ func (s *Site) lookupTemplate(layouts ...string) (tpl.Template, bool) {
return nil, false return nil, false
} }
func (s *Site) publish(statCounter *uint64, path string, r io.Reader) (err error) { func (s *Site) publish(statCounter *uint64, path string, r io.Reader, fs afero.Fs) (err error) {
s.PathSpec.ProcessingStats.Incr(statCounter) s.PathSpec.ProcessingStats.Incr(statCounter)
return helpers.WriteToDisk(filepath.Clean(path), r, s.BaseFs.PublishFs) return helpers.WriteToDisk(filepath.Clean(path), r, fs)
} }
func (s *Site) kindFromFileInfoOrSections(fi *fileInfo, sections []string) string { func (s *Site) kindFromFileInfoOrSections(fi *fileInfo, sections []string) string {