Repository: gohugoio/hugo (language: go)

Issue:
Hugo mod vendor does not copy hugo.toml
<!-- Please answer these questions before submitting your issue. Thanks! -->

### What version of Hugo are you using (`hugo version`)?

<pre>
$ hugo version
hugo v0.115.1+extended darwin/arm64
</pre>

### Does this issue reproduce with the latest release?
Yes

### Issue description
[Hugo 0.110.0 changed the default config base filename to hugo](https://gohugo.io/getting-started/configuration/#hugotoml-vs-configtoml), e.g. hugo.toml. However, `hugo mod` does not seem to pick up the new filename.

### Expected behavior
hugo mod copies the file `hugo.toml` to the local module cache / vendor directory, similar to `config.toml`.

### Steps to reproduce the error
A test site has been set up that references two modules:
 - https://github.com/markdumay/hugo-mod-test-site: test site (based on https://github.com/jmooring/hugo-testing/tree/hugo-github-issue-9751) that references two modules
 - https://github.com/markdumay/hugo-mod-issue-config: test module that includes `config.toml`
 - https://github.com/markdumay/hugo-mod-issue-hugo: test module that includes `hugo.toml`

From a local machine, run the following commands (note: tested on macOS, `tree` installed with `brew`)
```bash
git clone https://github.com/markdumay/hugo-mod-test-site.git
cd hugo-mod-test-site
hugo 
hugo mod vendor
tree _vendor
```

The `_vendor` directory includes a copy of `config.toml`, but `hugo.toml` is missing.


Discussion / hints:

Fix patch:
diff --git a/commands/hugobuilder.go b/commands/hugobuilder.go
index b5cd0c2bbed..d2b43cc7707 100644
--- a/commands/hugobuilder.go
+++ b/commands/hugobuilder.go
@@ -1042,8 +1042,8 @@ func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) error {
 	if c.fastRenderMode {
 		c.withConf(func(conf *commonConfig) {
 			// Make sure we always render the home pages
-			for _, l := range conf.configs.Languages {
-				langPath := h.GetLangSubDir(l.Lang)
+			for _, l := range conf.configs.ConfigLangs() {
+				langPath := l.LanguagePrefix()
 				if langPath != "" {
 					langPath = langPath + "/"
 				}
diff --git a/config/allconfig/configlanguage.go b/config/allconfig/configlanguage.go
index be549126bd2..534276c25ba 100644
--- a/config/allconfig/configlanguage.go
+++ b/config/allconfig/configlanguage.go
@@ -41,6 +41,16 @@ func (c ConfigLanguage) LanguagesDefaultFirst() langs.Languages {
 	return c.m.LanguagesDefaultFirst
 }
 
+func (c ConfigLanguage) LanguagePrefix() string {
+	if c.DefaultContentLanguageInSubdir() && c.DefaultContentLanguage() == c.Language().Lang {
+		return c.Language().Lang
+	}
+	if !c.IsMultiLingual() || c.DefaultContentLanguage() == c.Language().Lang {
+		return ""
+	}
+	return c.Language().Lang
+}
+
 func (c ConfigLanguage) BaseURL() urls.BaseURL {
 	return c.config.C.BaseURL
 }
diff --git a/config/configProvider.go b/config/configProvider.go
index 5d6acce9f3b..94683005667 100644
--- a/config/configProvider.go
+++ b/config/configProvider.go
@@ -27,6 +27,7 @@ type AllProvider interface {
 	Language() *langs.Language
 	Languages() langs.Languages
 	LanguagesDefaultFirst() langs.Languages
+	LanguagePrefix() string
 	BaseURL() urls.BaseURL
 	BaseURLLiveReload() urls.BaseURL
 	Environment() string
diff --git a/hugolib/paths/paths.go b/hugolib/paths/paths.go
index 9e15d3ab4e3..817670dbd65 100644
--- a/hugolib/paths/paths.go
+++ b/hugolib/paths/paths.go
@@ -103,48 +103,15 @@ func (p *Paths) Lang() string {
 }
 
 func (p *Paths) GetTargetLanguageBasePath() string {
-	if len(p.Cfg.Languages()) > 1 {
+	if p.Cfg.IsMultihost() {
 		// In a multihost configuration all assets will be published below the language code.
 		return p.Lang()
 	}
 	return p.GetLanguagePrefix()
 }
 
-func (p *Paths) GetURLLanguageBasePath() string {
-	if len(p.Cfg.Languages()) > 1 {
-		return ""
-	}
-	return p.GetLanguagePrefix()
-}
-
 func (p *Paths) GetLanguagePrefix() string {
-	if len(p.Cfg.Languages()) < 2 {
-		return ""
-	}
-	defaultLang := p.Cfg.DefaultContentLanguage()
-	defaultInSubDir := p.Cfg.DefaultContentLanguageInSubdir()
-	currentLang := p.Cfg.Language().Lang
-	if currentLang == "" || (currentLang == defaultLang && !defaultInSubDir) {
-		return ""
-	}
-	return currentLang
-}
-
-// GetLangSubDir returns the given language's subdir if needed.
-func (p *Paths) GetLangSubDir(lang string) string {
-	if len(p.Cfg.Languages()) < 2 {
-		return ""
-	}
-
-	if p.Cfg.IsMultihost() {
-		return ""
-	}
-
-	if lang == "" || (lang == p.Cfg.DefaultContentLanguage() && !p.Cfg.DefaultContentLanguageInSubdir()) {
-		return ""
-	}
-
-	return lang
+	return p.Cfg.LanguagePrefix()
 }
 
 // AbsPathify creates an absolute path if given a relative path. If already
diff --git a/hugolib/site.go b/hugolib/site.go
index 7a352892990..eb6a4e344bb 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -887,21 +887,16 @@ func (s *Site) getLanguageTargetPathLang(alwaysInSubDir bool) string {
 
 // get any language code to prefix the relative permalink with.
 func (s *Site) getLanguagePermalinkLang(alwaysInSubDir bool) string {
-	if !s.h.isMultiLingual() || s.h.Conf.IsMultihost() {
+	if s.h.Conf.IsMultihost() {
 		return ""
 	}
 
-	if alwaysInSubDir {
+	if s.h.Conf.IsMultiLingual() && alwaysInSubDir {
 		return s.Language().Lang
 	}
 
-	isDefault := s.Language().Lang == s.conf.DefaultContentLanguage
-
-	if !isDefault || s.conf.DefaultContentLanguageInSubdir {
-		return s.Language().Lang
-	}
+	return s.GetLanguagePrefix()
 
-	return ""
 }
 
 func (s *Site) getTaxonomyKey(key string) string {
diff --git a/hugolib/site_new.go b/hugolib/site_new.go
index c6c5cd2a35c..61273d3c874 100644
--- a/hugolib/site_new.go
+++ b/hugolib/site_new.go
@@ -470,16 +470,11 @@ func (s *Site) IsMultiLingual() bool {
 }
 
 func (s *Site) LanguagePrefix() string {
-	conf := s.s.Conf
-	if !conf.IsMultiLingual() {
+	prefix := s.GetLanguagePrefix()
+	if prefix == "" {
 		return ""
 	}
-
-	if !conf.DefaultContentLanguageInSubdir() && s.language.Lang == conf.DefaultContentLanguage() {
-		return ""
-	}
-
-	return "/" + s.language.Lang
+	return "/" + prefix
 }
 
 // Returns the identity of this site.
diff --git a/modules/client.go b/modules/client.go
index 5d8daf92623..53ce67cb326 100644
--- a/modules/client.go
+++ b/modules/client.go
@@ -292,8 +292,10 @@ func (c *Client) Vendor() error {
 			}
 		}
 
-		// Also include any theme.toml or config.* files in the root.
+		// Also include any theme.toml or config.* or hugo.* files in the root.
 		configFiles, _ := afero.Glob(c.fs, filepath.Join(dir, "config.*"))
+		configFiles2, _ := afero.Glob(c.fs, filepath.Join(dir, "hugo.*"))
+		configFiles = append(configFiles, configFiles2...)
 		configFiles = append(configFiles, filepath.Join(dir, "theme.toml"))
 		for _, configFile := range configFiles {
 			if err := hugio.CopyFile(c.fs, configFile, filepath.Join(vendorDir, t.Path(), filepath.Base(configFile))); err != nil {


Test patch:
diff --git a/helpers/url_test.go b/helpers/url_test.go
index b51625a30bf..448756b5bdb 100644
--- a/helpers/url_test.go
+++ b/helpers/url_test.go
@@ -109,8 +109,16 @@ func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
 						"weight": 10,
 					},
 				})
+				v.Set("defaultContentLanguage", "en")
+			} else {
+				v.Set("defaultContentLanguage", lang)
+				v.Set("languages", map[string]any{
+					lang: map[string]interface{}{
+						"weight": 10,
+					},
+				})
 			}
-			v.Set("defaultContentLanguage", "en")
+
 			v.Set("defaultContentLanguageInSubdir", defaultInSubDir)
 			v.Set("baseURL", test.baseURL)
 
@@ -118,16 +126,21 @@ func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
 			if multilingual {
 				configLang = lang
 			}
+			defaultContentLanguage := lang
+			if multilingual {
+				defaultContentLanguage = "en"
+			}
+
 			p := newTestPathSpecFromCfgAndLang(v, configLang)
 
 			output := p.AbsURL(test.input, addLanguage)
 			expected := test.expected
-			if multilingual && addLanguage {
-				if !defaultInSubDir && lang == "en" {
-					expected = strings.Replace(expected, "MULTI", "", 1)
-				} else {
-					expected = strings.Replace(expected, "MULTI", lang+"/", 1)
-				}
+			if addLanguage {
+				addLanguage = defaultInSubDir && lang == defaultContentLanguage
+				addLanguage = addLanguage || (lang != defaultContentLanguage && multilingual)
+			}
+			if addLanguage {
+				expected = strings.Replace(expected, "MULTI", lang+"/", 1)
 			} else {
 				expected = strings.Replace(expected, "MULTI", "", 1)
 			}
@@ -162,8 +175,16 @@ func doTestRelURL(t testing.TB, defaultInSubDir, addLanguage, multilingual bool,
 				"weight": 10,
 			},
 		})
+		v.Set("defaultContentLanguage", "en")
+	} else {
+		v.Set("defaultContentLanguage", lang)
+		v.Set("languages", map[string]any{
+			lang: map[string]interface{}{
+				"weight": 10,
+			},
+		})
 	}
-	v.Set("defaultContentLanguage", "en")
+
 	v.Set("defaultContentLanguageInSubdir", defaultInSubDir)
 
 	tests := []struct {
@@ -209,25 +230,25 @@ func doTestRelURL(t testing.TB, defaultInSubDir, addLanguage, multilingual bool,
 	}
 
 	for i, test := range tests {
-		c.Run(fmt.Sprintf("%v/%t%t%t/%s", test, defaultInSubDir, addLanguage, multilingual, lang), func(c *qt.C) {
+		c.Run(fmt.Sprintf("%v/defaultInSubDir=%t;addLanguage=%t;multilingual=%t/%s", test, defaultInSubDir, addLanguage, multilingual, lang), func(c *qt.C) {
 
 			v.Set("baseURL", test.baseURL)
 			v.Set("canonifyURLs", test.canonify)
-			var configLang string
+			defaultContentLanguage := lang
 			if multilingual {
-				configLang = lang
+				defaultContentLanguage = "en"
 			}
-			p := newTestPathSpecFromCfgAndLang(v, configLang)
+			p := newTestPathSpecFromCfgAndLang(v, lang)
 
 			output := p.RelURL(test.input, addLanguage)
 
 			expected := test.expected
-			if multilingual && addLanguage {
-				if !defaultInSubDir && lang == "en" {
-					expected = strings.Replace(expected, "MULTI", "", 1)
-				} else {
-					expected = strings.Replace(expected, "MULTI", "/"+lang, 1)
-				}
+			if addLanguage {
+				addLanguage = defaultInSubDir && lang == defaultContentLanguage
+				addLanguage = addLanguage || (lang != defaultContentLanguage && multilingual)
+			}
+			if addLanguage {
+				expected = strings.Replace(expected, "MULTI", "/"+lang, 1)
 			} else {
 				expected = strings.Replace(expected, "MULTI", "", 1)
 			}
diff --git a/hugolib/config_test.go b/hugolib/config_test.go
index f87d8b936f3..567059e6834 100644
--- a/hugolib/config_test.go
+++ b/hugolib/config_test.go
@@ -1295,3 +1295,92 @@ Home.
 	b.Assert(b.H.Configs.LanguageConfigSlice[0].Module.Mounts, qt.HasLen, 7)
 
 }
+
+func TestDefaultContentLanguageInSubdirOnlyOneLanguage(t *testing.T) {
+
+	t.Run("One language, default in sub dir", func(t *testing.T) {
+		t.Parallel()
+
+		files := `
+-- hugo.toml --
+baseURL = "https://example.com"
+defaultContentLanguage = "en"
+defaultContentLanguageInSubdir = true
+disableKinds = ["taxonomy", "term", "page", "section"]
+-- content/foo/bar.txt --
+Foo.
+-- layouts/index.html --
+Home.
+`
+		b := NewIntegrationTestBuilder(
+			IntegrationTestConfig{
+				T:           t,
+				TxtarString: files,
+			},
+		).Build()
+
+		b.AssertFileContent("public/en/index.html", "Home.")
+		b.AssertFileContent("public/en/foo/bar.txt", "Foo.")
+	})
+
+	t.Run("Two languages, default in sub dir", func(t *testing.T) {
+		t.Parallel()
+
+		files := `
+-- hugo.toml --
+baseURL = "https://example.com"
+defaultContentLanguage = "en"
+defaultContentLanguageInSubdir = true
+disableKinds = ["taxonomy", "term", "page", "section"]
+[languages]
+[languages.en]
+title = "English Title"
+[languages.sv]
+title = "Swedish Title"
+-- content/foo/bar.txt --
+Foo.
+-- layouts/index.html --
+Home.
+`
+		b := NewIntegrationTestBuilder(
+			IntegrationTestConfig{
+				T:           t,
+				TxtarString: files,
+			},
+		).Build()
+
+		b.AssertFileContent("public/en/index.html", "Home.")
+		b.AssertFileContent("public/en/foo/bar.txt", "Foo.")
+	})
+
+	t.Run("Two languages, default in root", func(t *testing.T) {
+		t.Parallel()
+
+		files := `
+-- hugo.toml --
+baseURL = "https://example.com"
+defaultContentLanguage = "en"
+defaultContentLanguageInSubdir = false
+disableKinds = ["taxonomy", "term", "page", "section"]
+[languages]
+[languages.en]
+title = "English Title"
+[languages.sv]
+title = "Swedish Title"
+-- content/foo/bar.txt --
+Foo.
+-- layouts/index.html --
+Home.
+`
+		b := NewIntegrationTestBuilder(
+			IntegrationTestConfig{
+				T:           t,
+				TxtarString: files,
+			},
+		).Build()
+
+		b.AssertFileContent("public/index.html", "Home.")
+		b.AssertFileContent("public/foo/bar.txt", "Foo.")
+	})
+
+}
diff --git a/hugolib/pagebundler_test.go b/hugolib/pagebundler_test.go
index 51ce8252637..ebf5d628bd1 100644
--- a/hugolib/pagebundler_test.go
+++ b/hugolib/pagebundler_test.go
@@ -67,6 +67,7 @@ func TestPageBundlerSiteRegular(t *testing.T) {
 						fs, cfg := newTestBundleSources(c)
 						cfg.Set("baseURL", baseURL)
 						cfg.Set("canonifyURLs", canonify)
+						cfg.Set("defaultContentLanguageInSubdir", false)
 
 						cfg.Set("permalinks", map[string]string{
 							"a": ":sections/:filename",
diff --git a/hugolib/site_url_test.go b/hugolib/site_url_test.go
index cb69be4ca05..821c84c9513 100644
--- a/hugolib/site_url_test.go
+++ b/hugolib/site_url_test.go
@@ -38,35 +38,6 @@ var urlFakeSource = [][2]string{
 	{filepath.FromSlash("content/blue/doc2.md"), slugDoc2},
 }
 
-func TestPageCount(t *testing.T) {
-	t.Parallel()
-	c := qt.New(t)
-	cfg, fs := newTestCfg()
-	cfg.Set("uglyURLs", false)
-	cfg.Set("paginate", 10)
-	configs, err := loadTestConfigFromProvider(cfg)
-	c.Assert(err, qt.IsNil)
-
-	writeSourcesToSource(t, "", fs, urlFakeSource...)
-	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Configs: configs}, BuildCfg{})
-
-	_, err = s.Fs.WorkingDirReadOnly.Open("public/blue")
-	if err != nil {
-		t.Errorf("No indexed rendered.")
-	}
-
-	for _, pth := range []string{
-		"public/sd1/foo/index.html",
-		"public/sd2/index.html",
-		"public/sd3/index.html",
-		"public/sd4.html",
-	} {
-		if _, err := s.Fs.WorkingDirReadOnly.Open(filepath.FromSlash(pth)); err != nil {
-			t.Errorf("No alias rendered: %s", pth)
-		}
-	}
-}
-
 func TestUglyURLsPerSection(t *testing.T) {
 	t.Parallel()
 
diff --git a/hugolib/sitemap_test.go b/hugolib/sitemap_test.go
index 984943c6f1f..aae874d502f 100644
--- a/hugolib/sitemap_test.go
+++ b/hugolib/sitemap_test.go
@@ -46,6 +46,7 @@ func doTestSitemapOutput(t *testing.T, internal bool) {
 	c := qt.New(t)
 	cfg, fs := newTestCfg()
 	cfg.Set("baseURL", "http://auth/bub/")
+	cfg.Set("defaultContentLanguageInSubdir", false)
 	configs, err := loadTestConfigFromProvider(cfg)
 	c.Assert(err, qt.IsNil)
 	writeSource(t, fs, "layouts/sitemap.xml", sitemapTemplate)
diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go
index 73920bd49f6..700aa8c3d5e 100644
--- a/hugolib/testhelpers_test.go
+++ b/hugolib/testhelpers_test.go
@@ -937,8 +937,7 @@ func newTestCfgBasic() (config.Provider, *hugofs.Fs) {
 func newTestCfg(withConfig ...func(cfg config.Provider) error) (config.Provider, *hugofs.Fs) {
 	mm := afero.NewMemMapFs()
 	cfg := config.New()
-	// Default is false, but true is easier to use as default in tests
-	cfg.Set("defaultContentLanguageInSubdir", true)
+	cfg.Set("defaultContentLanguageInSubdir", false)
 	cfg.Set("publishDir", "public")
 
 	fs := hugofs.NewFromOld(hugofs.NewBaseFileDecorator(mm), cfg)
diff --git a/main_test.go b/main_test.go
index aae8a0a9ed2..6274cd9bb47 100644
--- a/main_test.go
+++ b/main_test.go
@@ -50,6 +50,7 @@ func TestUnfinished(t *testing.T) {
 
 	p := commonTestScriptsParam
 	p.Dir = "testscripts/unfinished"
+	//p.UpdateScripts = true
 
 	testscript.Run(t, p)
 }
diff --git a/testscripts/commands/mod_vendor.txt b/testscripts/commands/mod_vendor.txt
new file mode 100644
index 00000000000..8a77776b3df
--- /dev/null
+++ b/testscripts/commands/mod_vendor.txt
@@ -0,0 +1,24 @@
+dostounix golden/vendor.txt
+
+hugo mod vendor
+cmp _vendor/modules.txt golden/vendor.txt
+ls _vendor/github.com/gohugoio/hugo-mod-integrationtests/withconfigtoml
+stdout 'config.toml'
+ls _vendor/github.com/gohugoio/hugo-mod-integrationtests/withhugotoml
+stdout 'hugo.toml'
+
+
+-- hugo.toml --
+title = "Hugo Modules Test"
+[module]
+[[module.imports]]
+path="github.com/gohugoio/hugo-mod-integrationtests/withconfigtoml"
+[[module.imports]]
+path="github.com/gohugoio/hugo-mod-integrationtests/withhugotoml"
+-- go.mod --
+go 1.19
+
+module github.com/gohugoio/testmod
+-- golden/vendor.txt --
+# github.com/gohugoio/hugo-mod-integrationtests/withconfigtoml v1.0.0
+# github.com/gohugoio/hugo-mod-integrationtests/withhugotoml v1.0.0