modules: Add config option modules.vendorClosest
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sun, 14 Feb 2021 18:24:13 +0000 (19:24 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 16 Feb 2021 15:30:01 +0000 (16:30 +0100)
Fixes #8235
Fixes #8242

docs/content/en/hugo-modules/configuration.md
hugofs/glob/glob.go
modules/client_test.go
modules/collect.go
modules/config.go

index 5405e4e51e1b9527da6739dee8e61a15f1241e2e..f82902ebbff234817a4fa1f9b6b7ad422adcb0b6 100644 (file)
@@ -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.
 
index 5d2d3d5e5d8e8ee8bff7bf710599263da4fccee5..57115ddfa763dae33392116ebb6229e7470bfe27 100644 (file)
@@ -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
index c7a07fb87c48b7db24a674b59000dc3a9a889ee9..f801af07de211453fac9084414a39e97fe37a8df 100644 (file)
@@ -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)
index 4de51258b1e9a5bf454e772d8c7583e615b621ba..db79f434e1bcb08b7100cdacbc400fcac91b8975 100644 (file)
@@ -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),
index 106becc60916a016d70f35851f0d5b21792ffbb6..1e3e3c79804e15f1cfec13bf2118a5488e014954 100644 (file)
@@ -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