modules: Make ignoreVendor a glob pattern
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 9 Sep 2020 14:51:13 +0000 (16:51 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Thu, 10 Sep 2020 06:47:05 +0000 (08:47 +0200)
Fixes #7642

commands/commands.go
commands/commands_test.go
commands/hugo.go
docs/content/en/hugo-modules/use-modules.md
hugolib/config.go
hugolib/hugo_modules_test.go
modules/client.go
modules/client_test.go
modules/collect.go

index 09e0c84553df3c71842d82a63db469af3acf79ff..ddacc7cf3ba7d2fe5b6bcedd920e8a91ed00c967 100644 (file)
@@ -273,6 +273,7 @@ func (cc *hugoBuilderCommon) handleCommonBuilderFlags(cmd *cobra.Command) {
        cmd.PersistentFlags().StringVarP(&cc.environment, "environment", "e", "", "build environment")
        cmd.PersistentFlags().StringP("themesDir", "", "", "filesystem path to themes directory")
        cmd.PersistentFlags().BoolP("ignoreVendor", "", false, "ignores any _vendor directory")
+       cmd.PersistentFlags().StringP("ignoreVendorPaths", "", "", "ignores any _vendor for module paths matching the given Glob pattern")
 }
 
 func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) {
index 3b1944891bba14b2ef411e7d2985df8de5db1243..22e9968535e745c0d537382a04ab1236f635011a 100644 (file)
@@ -151,7 +151,7 @@ func readFileFrom(c *qt.C, filename string) string {
        return string(b)
 }
 
-func TestCommandsPersistentFlags(t *testing.T) {
+func TestFlags(t *testing.T) {
        c := qt.New(t)
 
        noOpRunE := func(cmd *cobra.Command, args []string) error {
@@ -159,90 +159,103 @@ func TestCommandsPersistentFlags(t *testing.T) {
        }
 
        tests := []struct {
+               name  string
                args  []string
-               check func(command []cmder)
-       }{{[]string{"server",
-               "--config=myconfig.toml",
-               "--configDir=myconfigdir",
-               "--contentDir=mycontent",
-               "--disableKinds=page,home",
-               "--environment=testing",
-               "--configDir=myconfigdir",
-               "--layoutDir=mylayouts",
-               "--theme=mytheme",
-               "--gc",
-               "--themesDir=mythemes",
-               "--cleanDestinationDir",
-               "--navigateToChanged",
-               "--disableLiveReload",
-               "--noHTTPCache",
-               "--i18n-warnings",
-               "--destination=/tmp/mydestination",
-               "-b=https://example.com/b/",
-               "--port=1366",
-               "--renderToDisk",
-               "--source=mysource",
-               "--path-warnings",
-       }, func(commands []cmder) {
-               var sc *serverCmd
-               for _, command := range commands {
-                       if b, ok := command.(commandsBuilderGetter); ok {
-                               v := b.getCommandsBuilder().hugoBuilderCommon
-                               c.Assert(v.cfgFile, qt.Equals, "myconfig.toml")
-                               c.Assert(v.cfgDir, qt.Equals, "myconfigdir")
-                               c.Assert(v.source, qt.Equals, "mysource")
-                               c.Assert(v.baseURL, qt.Equals, "https://example.com/b/")
-                       }
-
-                       if srvCmd, ok := command.(*serverCmd); ok {
-                               sc = srvCmd
-                       }
-               }
-
-               c.Assert(sc, qt.Not(qt.IsNil))
-               c.Assert(sc.navigateToChanged, qt.Equals, true)
-               c.Assert(sc.disableLiveReload, qt.Equals, true)
-               c.Assert(sc.noHTTPCache, qt.Equals, true)
-               c.Assert(sc.renderToDisk, qt.Equals, true)
-               c.Assert(sc.serverPort, qt.Equals, 1366)
-               c.Assert(sc.environment, qt.Equals, "testing")
-
-               cfg := viper.New()
-               sc.flagsToConfig(cfg)
-               c.Assert(cfg.GetString("publishDir"), qt.Equals, "/tmp/mydestination")
-               c.Assert(cfg.GetString("contentDir"), qt.Equals, "mycontent")
-               c.Assert(cfg.GetString("layoutDir"), qt.Equals, "mylayouts")
-               c.Assert(cfg.GetStringSlice("theme"), qt.DeepEquals, []string{"mytheme"})
-               c.Assert(cfg.GetString("themesDir"), qt.Equals, "mythemes")
-               c.Assert(cfg.GetString("baseURL"), qt.Equals, "https://example.com/b/")
-
-               c.Assert(cfg.Get("disableKinds"), qt.DeepEquals, []string{"page", "home"})
-
-               c.Assert(cfg.GetBool("gc"), qt.Equals, true)
-
-               // The flag is named path-warnings
-               c.Assert(cfg.GetBool("logPathWarnings"), qt.Equals, true)
-
-               // The flag is named i18n-warnings
-               c.Assert(cfg.GetBool("logI18nWarnings"), qt.Equals, true)
-
-       }}}
+               check func(c *qt.C, cmd *serverCmd)
+       }{
+               {
+                       // https://github.com/gohugoio/hugo/issues/7642
+                       name: "ignoreVendor as bool",
+                       args: []string{"server", "--ignoreVendor"},
+                       check: func(c *qt.C, cmd *serverCmd) {
+                               cfg := viper.New()
+                               cmd.flagsToConfig(cfg)
+                               c.Assert(cfg.Get("ignoreVendor"), qt.Equals, true)
+                       },
+               },
+               {
+                       // https://github.com/gohugoio/hugo/issues/7642
+                       name: "ignoreVendorPaths",
+                       args: []string{"server", "--ignoreVendorPaths=github.com/**"},
+                       check: func(c *qt.C, cmd *serverCmd) {
+                               cfg := viper.New()
+                               cmd.flagsToConfig(cfg)
+                               c.Assert(cfg.Get("ignoreVendorPaths"), qt.Equals, "github.com/**")
+                       },
+               },
+               {
+                       name: "Persistent flags",
+                       args: []string{"server",
+                               "--config=myconfig.toml",
+                               "--configDir=myconfigdir",
+                               "--contentDir=mycontent",
+                               "--disableKinds=page,home",
+                               "--environment=testing",
+                               "--configDir=myconfigdir",
+                               "--layoutDir=mylayouts",
+                               "--theme=mytheme",
+                               "--gc",
+                               "--themesDir=mythemes",
+                               "--cleanDestinationDir",
+                               "--navigateToChanged",
+                               "--disableLiveReload",
+                               "--noHTTPCache",
+                               "--i18n-warnings",
+                               "--destination=/tmp/mydestination",
+                               "-b=https://example.com/b/",
+                               "--port=1366",
+                               "--renderToDisk",
+                               "--source=mysource",
+                               "--path-warnings",
+                       },
+                       check: func(c *qt.C, sc *serverCmd) {
+                               c.Assert(sc, qt.Not(qt.IsNil))
+                               c.Assert(sc.navigateToChanged, qt.Equals, true)
+                               c.Assert(sc.disableLiveReload, qt.Equals, true)
+                               c.Assert(sc.noHTTPCache, qt.Equals, true)
+                               c.Assert(sc.renderToDisk, qt.Equals, true)
+                               c.Assert(sc.serverPort, qt.Equals, 1366)
+                               c.Assert(sc.environment, qt.Equals, "testing")
+
+                               cfg := viper.New()
+                               sc.flagsToConfig(cfg)
+                               c.Assert(cfg.GetString("publishDir"), qt.Equals, "/tmp/mydestination")
+                               c.Assert(cfg.GetString("contentDir"), qt.Equals, "mycontent")
+                               c.Assert(cfg.GetString("layoutDir"), qt.Equals, "mylayouts")
+                               c.Assert(cfg.GetStringSlice("theme"), qt.DeepEquals, []string{"mytheme"})
+                               c.Assert(cfg.GetString("themesDir"), qt.Equals, "mythemes")
+                               c.Assert(cfg.GetString("baseURL"), qt.Equals, "https://example.com/b/")
+
+                               c.Assert(cfg.Get("disableKinds"), qt.DeepEquals, []string{"page", "home"})
+
+                               c.Assert(cfg.GetBool("gc"), qt.Equals, true)
+
+                               // The flag is named path-warnings
+                               c.Assert(cfg.GetBool("logPathWarnings"), qt.Equals, true)
+
+                               // The flag is named i18n-warnings
+                               c.Assert(cfg.GetBool("logI18nWarnings"), qt.Equals, true)
+
+                       }}}
 
        for _, test := range tests {
-               b := newCommandsBuilder()
-               root := b.addAll().build()
+               c.Run(test.name, func(c *qt.C) {
 
-               for _, c := range b.commands {
-                       if c.getCommand() == nil {
-                               continue
+                       b := newCommandsBuilder()
+                       root := b.addAll().build()
+
+                       for _, cmd := range b.commands {
+                               if cmd.getCommand() == nil {
+                                       continue
+                               }
+                               // We are only intereseted in the flag handling here.
+                               cmd.getCommand().RunE = noOpRunE
                        }
-                       // We are only intereseted in the flag handling here.
-                       c.getCommand().RunE = noOpRunE
-               }
-               rootCmd := root.getCommand()
-               rootCmd.SetArgs(test.args)
-               c.Assert(rootCmd.Execute(), qt.IsNil)
-               test.check(b.commands)
+                       rootCmd := root.getCommand()
+                       rootCmd.SetArgs(test.args)
+                       c.Assert(rootCmd.Execute(), qt.IsNil)
+                       test.check(c, b.commands[0].(*serverCmd))
+               })
        }
 
 }
index de4e3fbb2537514d0f5b7a7a60643c0092819cfb..7eaaedbc9a62f4320e4da2a3a2d2604560924831 100644 (file)
@@ -200,6 +200,7 @@ func initializeFlags(cmd *cobra.Command, cfg config.Provider) {
                "noTimes",
                "noChmod",
                "ignoreVendor",
+               "ignoreVendorPaths",
                "templateMetrics",
                "templateMetricsHints",
 
index 5f16d5675dfee90bc6a0181df010af36f7d82e74..b6fd3a0aabf80021df4a1aae1366feaa0b143ad3 100644 (file)
@@ -120,7 +120,7 @@ Note that:
 
 * You can run `hugo mod vendor` on any level in the module tree.
 * Vendoring will not store modules stored in your `themes` folder.
-* Most commands accept a `--ignoreVendor` flag, which will then run as if the none of the `_vendor` folders in the module tree existed.
+* Most commands accept a `--ignoreVendorPaths` flag, which will then not use the vendored modules in `_vendor` for the module paths matching the [Glob](https://github.com/gobwas/glob) pattern given. Note that before Hugo 0.75 this flag was named `--ignoreVendor` and was a "all or nothing". {{< new-in "0.75.0" >}}
 
 Also see the [CLI Doc](/commands/hugo_mod_vendor/).
 
index 841bd5193a3e01dc9be44e501631b0efd5383c93..cab2013ca353caae66c6fd8025005153aed930fb 100644 (file)
@@ -18,6 +18,9 @@ import (
        "path/filepath"
        "strings"
 
+       "github.com/gobwas/glob"
+       hglob "github.com/gohugoio/hugo/hugofs/glob"
+
        "github.com/gohugoio/hugo/common/loggers"
 
        "github.com/gohugoio/hugo/cache/filecache"
@@ -202,6 +205,12 @@ func LoadConfig(d ConfigSourceDescriptor, doWithConfig ...func(cfg config.Provid
                }
        }
 
+       // We made this a Glob pattern in Hugo 0.75, we don't need both.
+       if v.GetBool("ignoreVendor") {
+               helpers.Deprecated("--ignoreVendor", "--ignoreVendorPaths **", false)
+               v.Set("ignoreVendorPaths", "**")
+       }
+
        modulesConfig, err := l.loadModulesConfig(v)
        if err != nil {
                return v, configFiles, err
@@ -417,7 +426,10 @@ func (l configLoader) collectModules(modConfig modules.Config, v1 *viper.Viper,
 
        themesDir := paths.AbsPathify(l.WorkingDir, v1.GetString("themesDir"))
 
-       ignoreVendor := v1.GetBool("ignoreVendor")
+       var ignoreVendor glob.Glob
+       if s := v1.GetString("ignoreVendorPaths"); s != "" {
+               ignoreVendor, _ = hglob.GetGlob(hglob.NormalizePath(s))
+       }
 
        filecacheConfigs, err := filecache.DecodeConfig(l.Fs, v1)
        if err != nil {
index b69503021aa2460c914e90cbdf9f1a7dc67260a9..037684862031a775319e047a40dff82841dc7808 100644 (file)
@@ -126,11 +126,15 @@ baseURL = "https://example.com"
 title = "My Modular Site"
 workingDir = %q
 theme = %q
-ignoreVendor = %t
+ignoreVendorPaths = %q
 
 `
 
-               config := fmt.Sprintf(configTemplate, workingDir, m.Path(), ignoreVendor)
+               ignoreVendorPaths := ""
+               if ignoreVendor {
+                       ignoreVendorPaths = "github.com/**"
+               }
+               config := fmt.Sprintf(configTemplate, workingDir, m.Path(), ignoreVendorPaths)
 
                b := newTestSitesBuilder(t)
 
index c66311d0577c115b6d559d81cf24acdd0255dc48..914d06a4e653dd8987a84fea38f5bf939a1a0fc2 100644 (file)
@@ -605,8 +605,9 @@ type ClientConfig struct {
        // etc.
        HookBeforeFinalize func(m *ModulesConfig) error
 
-       // Ignore any _vendor directory.
-       IgnoreVendor bool
+       // Ignore any _vendor directory for module paths matching the given pattern.
+       // This can be nil.
+       IgnoreVendor glob.Glob
 
        // Absolute path to the project dir.
        WorkingDir string
@@ -618,6 +619,10 @@ type ClientConfig struct {
        ModuleConfig Config
 }
 
+func (c ClientConfig) shouldIgnoreVendor(path string) bool {
+       return c.IgnoreVendor != nil && c.IgnoreVendor.Match(path)
+}
+
 type goBinaryStatus int
 
 type goModule struct {
index 07b71c4091d0f6ed58eff09425128d1776cee0ab..d5da621d1d228970452e80d53aad36416bee0db2 100644 (file)
@@ -17,6 +17,8 @@ import (
        "bytes"
        "testing"
 
+       "github.com/gohugoio/hugo/hugofs/glob"
+
        "github.com/gohugoio/hugo/common/hugo"
 
        "github.com/gohugoio/hugo/htesting"
@@ -89,7 +91,7 @@ project github.com/gohugoio/hugoTestModules1_darwin/modh2_2_2@v1.3.0+vendor
                Fs:           hugofs.Os,
                WorkingDir:   workingDir,
                ModuleConfig: modConfig,
-               IgnoreVendor: true,
+               IgnoreVendor: globAll,
        })
 
        graphb.Reset()
@@ -101,6 +103,8 @@ project github.com/gohugoio/hugoTestModules1_darwin/modh2_2_2@v1.3.0+vendor
 
 }
 
+var globAll, _ = glob.GetGlob("**")
+
 func TestGetModlineSplitter(t *testing.T) {
 
        c := qt.New(t)
index 0ac766fb9442c3b732d8a1dee17c3b258ba32205..f87ed248429bef9dd76624ad936506a07ad426d8 100644 (file)
@@ -196,7 +196,8 @@ func (c *collector) initModules() error {
                gomods:   goModules{},
        }
 
-       if !c.ccfg.IgnoreVendor && c.isVendored(c.ccfg.WorkingDir) {
+       // If both these are true, we don't even need Go installed to build.
+       if c.ccfg.IgnoreVendor == nil && c.isVendored(c.ccfg.WorkingDir) {
                return nil
        }
 
@@ -229,7 +230,7 @@ func (c *collector) add(owner *moduleAdapter, moduleImport Import, disabled bool
        modulePath := moduleImport.Path
        var realOwner Module = owner
 
-       if !c.ccfg.IgnoreVendor {
+       if !c.ccfg.shouldIgnoreVendor(modulePath) {
                if err := c.collectModulesTXT(owner); err != nil {
                        return nil, err
                }