A Weird Hugo Bug I Found

site-meta programming technology hugo

Recently, I was writing this post climate politics future about, well, vertical farming, and I had some serious difficulties getting the leaf bundle to actually show up in the final output.

In particular, when generated statically, the folder (originally named 01-vertical-farming) would not show up in static site output or when first running a hugo -D serve. However, running hugo -D serve, and then changing the filename or writing to one of the internal files would cause it to appear with the live-reload technology used by hugo. Now, in lieu of trying to fix hugo (written in Go, which, well, fuck dealing with that), I poked around and figured out - after examining lots of source code and being very frustrated - that changing the name of the directory to not contain slug-oriented characters (the -) prevented the issue.

In my exploration of the source code, I pulled down the repositories and compared version 0.97.0 (git ref release-0.97.0) to version 0.100.2 (git ref release-0.100.2), and found an interesting little set of lines in the diff of commands/commandeer.go

@@ -395,23 +420,23 @@ func (c *commandeer) loadConfig() error {
        c.fsCreate.Do(func() {
-               fs := hugofs.NewFrom(sourceFs, config)
+               // Assume both source and destination are using same filesystem.
+               fs := hugofs.NewFromSourceAndDestination(sourceFs, sourceFs, config)
                if c.publishDirFs != nil {
                        // Need to reuse the destination on server rebuilds.
                        fs.PublishDir = c.publishDirFs
                        fs.PublishDirServer = c.publishDirServerFs
                } else {
-                       publishDir := config.GetString("publishDir")
-                       publishDirStatic := config.GetString("publishDirStatic")
-                       workingDir := config.GetString("workingDir")
-                       absPublishDir := paths.AbsPathify(workingDir, publishDir)
-                       absPublishDirStatic := paths.AbsPathify(workingDir, publishDirStatic)
                        if c.renderStaticToDisk {
-                               // Writes the dynamic output oton memory,
+                               publishDirStatic := config.GetString("publishDirStatic")
+                               workingDir := config.GetString("workingDir")
+                               absPublishDirStatic := paths.AbsPathify(workingDir, publishDirStatic)
+                               fs = hugofs.NewFromSourceAndDestination(sourceFs, afero.NewMemMapFs(), config)
+                               // Writes the dynamic output to memory,
                                // while serve others directly from /public on disk.
-                               dynamicFs := afero.NewMemMapFs()
+                               dynamicFs := fs.PublishDir
                                staticFs := afero.NewBasePathFs(afero.NewOsFs(), absPublishDirStatic)

In particular passing the source directory to both the source and destination in one case is suspicious to me.

This is just my thoughts though and I don’t know the hugo codebase well enough to really say.

Note that in the process of this, it has become harder to make this thing actually work and making it not fail constantly, and now, about 30 minutes later it seems to magically be fixed???

This is very strange and feels, now, more like some kind of strange disk write cache bug or something similar. I’m not even sure what to think about this.

Eventually I made it work by pinning to Hugo v0.96, and changed back all my file names to be sensible nice --separated items :)