Repository: gohugoio/hugo (language: go)

Issue:
regression: Panic when user does not have write access to /tmp/hugo_cache
Introduced with a2a660e. This is the third issue related to this commit; see also #9518 and #9530.

Ref: <https://discourse.gohugo.io/t/hugo-on-ubuntu-20-04-panic/37294/>

Silly example, but...

```text
drwxrwxr-x   5 games    games      4096 Feb 21 08:13 hugo_cache
```

The current user is not a member of the `games` group.

```bash
$ groups
jmooring adm cdrom sudo dip plugdev lpadmin lxd sambashare
```

Build the site with `hugo`.

> panic: runtime error: invalid memory address or nil pointer dereference

<details>

```text
[signal SIGSEGV: segmentation violation code=0x1 addr=0x50 pc=0x1c77b2c]

goroutine 1 [running]:
github.com/gohugoio/hugo/commands.(*commandeer).loadConfig.func3()
	/home/jmooring/code/hugo/commands/commandeer.go:426 +0x58c
sync.(*Once).doSlow(0x2726ab0?, 0x2248b52?)
	/snap/go/current/src/sync/once.go:68 +0xc2
sync.(*Once).Do(...)
	/snap/go/current/src/sync/once.go:59
github.com/gohugoio/hugo/commands.(*commandeer).loadConfig(0xc0003920f0)
	/home/jmooring/code/hugo/commands/commandeer.go:377 +0x8a6
github.com/gohugoio/hugo/commands.newCommandeer(0x1, 0x1, 0x0, 0xc0005a60e0, {0x27112c0?, 0xc0005a2050}, 0xc0005a24c0, {0x0, 0x0, 0x0})
	/home/jmooring/code/hugo/commands/commandeer.go:195 +0x310
github.com/gohugoio/hugo/commands.initializeConfig(0x0?, 0xfd?, 0x6b?, 0xc0006bfca0?, {0x27112c0?, 0xc0005a2050?}, 0x2d?)
	/home/jmooring/code/hugo/commands/hugo.go:126 +0x31
github.com/gohugoio/hugo/commands.(*commandsBuilder).newHugoCmd.func1(0xc000572000, {0x21f1d56?, 0x0?, 0x0?})
	/home/jmooring/code/hugo/commands/commands.go:166 +0x135
github.com/spf13/cobra.(*Command).execute(0xc000572000, {0xc00003c200, 0x0, 0x0})
	/home/jmooring/go/pkg/mod/github.com/spf13/cobra@v1.2.1/command.go:856 +0x67c
github.com/spf13/cobra.(*Command).ExecuteC(0xc000572000)
	/home/jmooring/go/pkg/mod/github.com/spf13/cobra@v1.2.1/command.go:974 +0x3b4
github.com/gohugoio/hugo/commands.Execute({0xc00003c200, 0x0, 0x0})
	/home/jmooring/code/hugo/commands/hugo.go:93 +0xb4
main.main()
	/home/jmooring/code/hugo/main.go:23 +0x57
```
</details>


Discussion / hints:
Just to be clear: All of these are error situations, right, which should produce error with a sensible message and not panic?
> All of these are error situations, right, which should produce error with a sensible message and not panic?

Correct. Prior to a2a660e they produced sensible messages.
>Prior to https://github.com/gohugoio/hugo/commit/a2a660ed1209528b075a21af84fc0ed3e7b266ef they produced sensible messages.

With a deadlock, so progress :-)

Fix patch:
diff --git a/commands/commandeer.go b/commands/commandeer.go
index 304ef7a7c64..bf42501e0ce 100644
--- a/commands/commandeer.go
+++ b/commands/commandeer.go
@@ -422,7 +422,7 @@ func (c *commandeer) loadConfig() error {
 		}
 		c.hugoSites = h
 		// TODO(bep) improve.
-		if c.buildLock == nil {
+		if c.buildLock == nil && h != nil {
 			c.buildLock = h.LockBuild
 		}
 		close(c.created)
diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go
index f1930fd7155..d2f4426d58a 100644
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -390,6 +390,9 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) {
 	}
 
 	h.Deps = sites[0].Deps
+	if h.Deps == nil {
+		return nil, initErr
+	}
 
 	// Only needed in server mode.
 	// TODO(bep) clean up the running vs watching terms


Test patch:
diff --git a/commands/commands_test.go b/commands/commands_test.go
index b4fb8962134..b89e317c25d 100644
--- a/commands/commands_test.go
+++ b/commands/commands_test.go
@@ -329,7 +329,7 @@ type testSiteConfig struct {
 	contentDir string
 }
 
-func createSimpleTestSite(t *testing.T, cfg testSiteConfig) (string, func(), error) {
+func createSimpleTestSite(t testing.TB, cfg testSiteConfig) (string, func(), error) {
 	d, clean, e := htesting.CreateTempDir(hugofs.Os, "hugo-cli")
 	if e != nil {
 		return "", nil, e
@@ -392,12 +392,12 @@ Environment: {{ hugo.Environment }}
 	return d, clean, nil
 }
 
-func writeFile(t *testing.T, filename, content string) {
+func writeFile(t testing.TB, filename, content string) {
 	must(t, os.MkdirAll(filepath.Dir(filename), os.FileMode(0755)))
 	must(t, ioutil.WriteFile(filename, []byte(content), os.FileMode(0755)))
 }
 
-func must(t *testing.T, err error) {
+func must(t testing.TB, err error) {
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/commands/server_test.go b/commands/server_test.go
index 05d21a5165b..562fd498c70 100644
--- a/commands/server_test.go
+++ b/commands/server_test.go
@@ -29,12 +29,33 @@ import (
 )
 
 func TestServer(t *testing.T) {
-	if isWindowsCI() {
-		// TODO(bep) not sure why server tests have started to fail on the Windows CI server.
-		t.Skip("Skip server test on appveyor")
-	}
 	c := qt.New(t)
-	dir, clean, err := createSimpleTestSite(t, testSiteConfig{})
+
+	homeContent, err := runServerTestAndGetHome(c, "")
+
+	c.Assert(err, qt.IsNil)
+	c.Assert(homeContent, qt.Contains, "List: Hugo Commands")
+	c.Assert(homeContent, qt.Contains, "Environment: development")
+}
+
+// Issue 9518
+func TestServerPanicOnConfigError(t *testing.T) {
+	c := qt.New(t)
+
+	config := `
+[markup]
+[markup.highlight]
+linenos='table'
+`
+
+	_, err := runServerTestAndGetHome(c, config)
+
+	c.Assert(err, qt.IsNotNil)
+	c.Assert(err.Error(), qt.Contains, "cannot parse 'Highlight.LineNos' as bool:")
+}
+
+func runServerTestAndGetHome(c *qt.C, config string) (string, error) {
+	dir, clean, err := createSimpleTestSite(c, testSiteConfig{configTOML: config})
 	defer clean()
 	c.Assert(err, qt.IsNil)
 
@@ -45,6 +66,7 @@ func TestServer(t *testing.T) {
 		os.RemoveAll(dir)
 	}()
 
+	errors := make(chan error)
 	stop := make(chan bool)
 
 	b := newCommandsBuilder()
@@ -54,25 +76,30 @@ func TestServer(t *testing.T) {
 	cmd.SetArgs([]string{"-s=" + dir, fmt.Sprintf("-p=%d", port)})
 
 	go func() {
-		_, err = cmd.ExecuteC()
-		c.Assert(err, qt.IsNil)
+		_, err := cmd.ExecuteC()
+		if err != nil {
+			errors <- err
+		}
 	}()
 
+	select {
 	// There is no way to know exactly when the server is ready for connections.
 	// We could improve by something like https://golang.org/pkg/net/http/httptest/#Server
 	// But for now, let us sleep and pray!
-	time.Sleep(2 * time.Second)
+	case <-time.After(2 * time.Second):
+	case err := <-errors:
+		return "", err
+	}
 
 	resp, err := http.Get("http://localhost:1331/")
 	c.Assert(err, qt.IsNil)
 	defer resp.Body.Close()
 	homeContent := helpers.ReaderToString(resp.Body)
 
-	c.Assert(homeContent, qt.Contains, "List: Hugo Commands")
-	c.Assert(homeContent, qt.Contains, "Environment: development")
-
 	// Stop the server.
 	stop <- true
+
+	return homeContent, nil
 }
 
 func TestFixURL(t *testing.T) {