hugo

Unnamed repository; edit this file 'description' to name the repository.

git clone git://git.shimmy1996.com/hugo.git
commit bdfbcf6f4b4ab53a617ab76f72e8aa28da6067de
parent b60e9279ab95030828eb4f822be96250284c4d8d
Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Date:   Sun, 14 Feb 2021 19:24:13 +0100

modules: Add config option modules.vendorClosest

Fixes #8235
Fixes #8242

Diffstat:
Mdocs/content/en/hugo-modules/configuration.md | 3+++
Mhugofs/glob/glob.go | 10+++++++++-
Mmodules/client_test.go | 42+++++++++++++++++++++++++++++++++++-------
Mmodules/collect.go | 11++++++++++-
Mmodules/config.go | 7+++++++
5 files changed, 64 insertions(+), 9 deletions(-)
diff --git a/docs/content/en/hugo-modules/configuration.md b/docs/content/en/hugo-modules/configuration.md
@@ -29,6 +29,9 @@ replacements = ""
 noVendor {{< new-in "0.75.0" >}}
 : A optional Glob pattern matching module paths to skip when vendoring, e.g. "github.com/**"
 
+vendorClosest {{< new-in "0.81.0" >}}
+: When enabled, we will pick the vendored module closest to the module using it. The default behaviour is to pick the first. Note that there can still be only one dependency of a given module path, so once it is in use it cannot be redefined.
+
 proxy
 : Defines the proxy server to use to download remote modules. Default is `direct`, which means "git clone" and similar.
 
diff --git a/hugofs/glob/glob.go b/hugofs/glob/glob.go
@@ -33,6 +33,14 @@ var (
 	globMu    sync.RWMutex
 )
 
+type caseInsensitiveGlob struct {
+	g glob.Glob
+}
+
+func (g caseInsensitiveGlob) Match(s string) bool {
+	return g.g.Match(strings.ToLower(s))
+
+}
 func GetGlob(pattern string) (glob.Glob, error) {
 	var eg globErr
 
@@ -46,7 +54,7 @@ func GetGlob(pattern string) (glob.Glob, error) {
 
 	var err error
 	g, err := glob.Compile(strings.ToLower(pattern), '/')
-	eg = globErr{g, err}
+	eg = globErr{caseInsensitiveGlob{g: g}, err}
 
 	globMu.Lock()
 	globCache[pattern] = eg
diff --git a/modules/client_test.go b/modules/client_test.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
+	"sync/atomic"
 	"testing"
 
 	"github.com/gohugoio/hugo/hugofs/glob"
@@ -32,15 +33,18 @@ import (
 func TestClient(t *testing.T) {
 	modName := "hugo-modules-basic-test"
 	modPath := "github.com/gohugoio/tests/" + modName
+	defaultImport := "modh2_2"
 	expect := `github.com/gohugoio/tests/hugo-modules-basic-test github.com/gohugoio/hugoTestModules1_darwin/modh2_2@v1.4.0
 github.com/gohugoio/hugoTestModules1_darwin/modh2_2@v1.4.0 github.com/gohugoio/hugoTestModules1_darwin/modh2_2_1v@v1.3.0
 github.com/gohugoio/hugoTestModules1_darwin/modh2_2@v1.4.0 github.com/gohugoio/hugoTestModules1_darwin/modh2_2_2@v1.3.0
 `
 
 	c := qt.New(t)
+	var clientID uint64 // we increment this to get each test in its own directory.
 
-	newClient := func(c *qt.C, withConfig func(cfg *ClientConfig)) (*Client, func()) {
-		workingDir, clean, err := htesting.CreateTempDir(hugofs.Os, modName)
+	newClient := func(c *qt.C, withConfig func(cfg *ClientConfig), imp string) (*Client, func()) {
+		atomic.AddUint64(&clientID, uint64(1))
+		workingDir, clean, err := htesting.CreateTempDir(hugofs.Os, fmt.Sprintf("%s-%d", modName, clientID))
 		c.Assert(err, qt.IsNil)
 		themesDir := filepath.Join(workingDir, "themes")
 		err = os.Mkdir(themesDir, 0777)
@@ -53,7 +57,7 @@ github.com/gohugoio/hugoTestModules1_darwin/modh2_2@v1.4.0 github.com/gohugoio/h
 		}
 
 		withConfig(&ccfg)
-		ccfg.ModuleConfig.Imports = []Import{{Path: "github.com/gohugoio/hugoTestModules1_darwin/modh2_2"}}
+		ccfg.ModuleConfig.Imports = []Import{{Path: "github.com/gohugoio/hugoTestModules1_darwin/" + imp}}
 		client := NewClient(ccfg)
 
 		return client, clean
@@ -62,7 +66,7 @@ github.com/gohugoio/hugoTestModules1_darwin/modh2_2@v1.4.0 github.com/gohugoio/h
 	c.Run("All", func(c *qt.C) {
 		client, clean := newClient(c, func(cfg *ClientConfig) {
 			cfg.ModuleConfig = DefaultModuleConfig
-		})
+		}, defaultImport)
 		defer clean()
 
 		// Test Init
@@ -103,7 +107,7 @@ project github.com/gohugoio/hugoTestModules1_darwin/modh2_2_2@v1.3.0+vendor
 			c, func(cfg *ClientConfig) {
 				cfg.ModuleConfig = DefaultModuleConfig
 				cfg.IgnoreVendor = globAll
-			})
+			}, defaultImport)
 		defer clean()
 
 		c.Assert(client.Init(modPath), qt.IsNil)
@@ -122,7 +126,7 @@ project github.com/gohugoio/hugoTestModules1_darwin/modh2_2_2@v1.3.0+vendor
 		client, clean := newClient(
 			c, func(cfg *ClientConfig) {
 				cfg.ModuleConfig = mcfg
-			})
+			}, defaultImport)
 		defer clean()
 
 		c.Assert(client.Init(modPath), qt.IsNil)
@@ -135,13 +139,37 @@ project github.com/gohugoio/hugoTestModules1_darwin/modh2_2_2@v1.3.0+vendor
 		c.Assert(graphb.String(), qt.Equals, expect)
 	})
 
+	c.Run("VendorClosest", func(c *qt.C) {
+		mcfg := DefaultModuleConfig
+		mcfg.VendorClosest = true
+
+		client, clean := newClient(
+			c, func(cfg *ClientConfig) {
+				cfg.ModuleConfig = mcfg
+				s := "github.com/gohugoio/hugoTestModules1_darwin/modh1_1v"
+				g, _ := glob.GetGlob(s)
+				cfg.IgnoreVendor = g
+			}, "modh1v")
+		defer clean()
+
+		c.Assert(client.Init(modPath), qt.IsNil)
+		_, err := client.Collect()
+		c.Assert(err, qt.IsNil)
+		c.Assert(client.Vendor(), qt.IsNil)
+
+		var graphb bytes.Buffer
+		c.Assert(client.Graph(&graphb), qt.IsNil)
+
+		c.Assert(graphb.String(), qt.Contains, "github.com/gohugoio/hugoTestModules1_darwin/modh1_1v@v1.3.0 github.com/gohugoio/hugoTestModules1_darwin/modh1_1_1v@v1.1.0+vendor")
+	})
+
 	// https://github.com/gohugoio/hugo/issues/7908
 	c.Run("createThemeDirname", func(c *qt.C) {
 		mcfg := DefaultModuleConfig
 		client, clean := newClient(
 			c, func(cfg *ClientConfig) {
 				cfg.ModuleConfig = mcfg
-			})
+			}, defaultImport)
 		defer clean()
 
 		dirname, err := client.createThemeDirname("foo", false)
diff --git a/modules/collect.go b/modules/collect.go
@@ -531,7 +531,16 @@ func (c *collector) collectModulesTXT(owner Module) error {
 			return errors.Errorf("invalid modules list: %q", filename)
 		}
 		path := parts[0]
-		if _, found := c.vendored[path]; !found {
+
+		shouldAdd := c.Client.moduleConfig.VendorClosest
+
+		if !shouldAdd {
+			if _, found := c.vendored[path]; !found {
+				shouldAdd = true
+			}
+		}
+
+		if shouldAdd {
 			c.vendored[path] = vendoredModule{
 				Owner:   owner,
 				Dir:     filepath.Join(vendorDir, path),
diff --git a/modules/config.go b/modules/config.go
@@ -279,6 +279,13 @@ type Config struct {
 	// "github.com/**".
 	NoVendor string
 
+	// When enabled, we will pick the vendored module closest to the module
+	// using it.
+	// The default behaviour is to pick the first.
+	// Note that there can still be only one dependency of a given module path,
+	// so once it is in use it cannot be redefined.
+	VendorClosest bool
+
 	Replacements    []string
 	replacementsMap map[string]string