Repository: gohugoio/hugo (language: go)

Issue:
Add a --noBuildLock flag
For local development, the [Kubernetes project](https://kubernetes.io/) runs Hugo in a container, with the website source mounted in read-only (there is a tmpfs that Hugo renders to).

Recent Hugo releases try to lock the source directory, which fails because of the read-only mount. I'd welcome a mechanism to bypass the lock file creation for the within-a-container case; we know it's actually safe to have Hugo try to run inside the container and to run outside it as well.


Discussion / hints:
You're not the first asking for something similar, so I guess we should do that. If someone is quick with a PR, I may get it in the 0.97 release...

Fix patch:
diff --git a/commands/commands.go b/commands/commands.go
index aee6a72841a..b81b867f9a0 100644
--- a/commands/commands.go
+++ b/commands/commands.go
@@ -305,6 +305,7 @@ func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) {
 	cmd.Flags().BoolP("forceSyncStatic", "", false, "copy all files when static is changed.")
 	cmd.Flags().BoolP("noTimes", "", false, "don't sync modification time of files")
 	cmd.Flags().BoolP("noChmod", "", false, "don't sync permission mode of files")
+	cmd.Flags().BoolP("noBuildLock", "", false, "don't create .hugo_build.lock file")
 	cmd.Flags().BoolP("printI18nWarnings", "", false, "print missing translations")
 	cmd.Flags().BoolP("printPathWarnings", "", false, "print warnings on duplicate target paths etc.")
 	cmd.Flags().BoolP("printUnusedTemplates", "", false, "print warnings on unused templates.")
diff --git a/commands/hugo.go b/commands/hugo.go
index 8dfd4b4bd56..94111e92d01 100644
--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -200,6 +200,7 @@ func initializeFlags(cmd *cobra.Command, cfg config.Provider) {
 		"forceSyncStatic",
 		"noTimes",
 		"noChmod",
+		"noBuildLock",
 		"ignoreVendorPaths",
 		"templateMetrics",
 		"templateMetricsHints",
diff --git a/hugolib/filesystems/basefs.go b/hugolib/filesystems/basefs.go
index 2447246d636..a380857cddd 100644
--- a/hugolib/filesystems/basefs.go
+++ b/hugolib/filesystems/basefs.go
@@ -76,18 +76,24 @@ type BaseFs struct {
 	theBigFs *filesystemsCollector
 
 	// Locks.
-	buildMu      *lockedfile.Mutex // <project>/.hugo_build.lock
-	buildMuTests sync.Mutex        // Used in tests.
+	buildMu Lockable // <project>/.hugo_build.lock
+}
+
+type Lockable interface {
+	Lock() (unlock func(), err error)
+}
+
+type fakeLockfileMutex struct {
+	mu sync.Mutex
+}
+
+func (f *fakeLockfileMutex) Lock() (func(), error) {
+	f.mu.Lock()
+	return func() { f.mu.Unlock() }, nil
 }
 
 // Tries to acquire a build lock.
 func (fs *BaseFs) LockBuild() (unlock func(), err error) {
-	if htesting.IsTest {
-		fs.buildMuTests.Lock()
-		return func() {
-			fs.buildMuTests.Unlock()
-		}, nil
-	}
 	return fs.buildMu.Lock()
 }
 
@@ -445,12 +451,19 @@ func NewBase(p *paths.Paths, logger loggers.Logger, options ...func(*BaseFs) err
 	sourceFs := hugofs.NewBaseFileDecorator(afero.NewBasePathFs(fs.Source, p.WorkingDir))
 	publishFsStatic := fs.PublishDirStatic
 
+	var buildMu Lockable
+	if p.Cfg.GetBool("noBuildLock") || htesting.IsTest {
+		buildMu = &fakeLockfileMutex{}
+	} else {
+		buildMu = lockedfile.MutexAt(filepath.Join(p.WorkingDir, lockFileBuild))
+	}
+
 	b := &BaseFs{
 		SourceFs:        sourceFs,
 		WorkDir:         fs.WorkingDirReadOnly,
 		PublishFs:       publishFs,
 		PublishFsStatic: publishFsStatic,
-		buildMu:         lockedfile.MutexAt(filepath.Join(p.WorkingDir, lockFileBuild)),
+		buildMu:         buildMu,
 	}
 
 	for _, opt := range options {


Test patch: