From e363964f2fcbeabe4264eaa9d7f803c9989ad096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 19 Sep 2024 10:53:39 +0200 Subject: [PATCH] commands: Ignore "module does not exist" errors in hugo mod init Closes #11458 --- commands/commandeer.go | 59 +++++++++++++++++++------------ commands/config.go | 4 +-- commands/hugobuilder.go | 4 +-- commands/mod.go | 18 ++++++---- commands/new.go | 4 +-- config/allconfig/allconfig.go | 2 +- config/allconfig/load.go | 30 +++++++++------- modules/client.go | 3 ++ modules/collect.go | 3 ++ testscripts/commands/mod_init.txt | 15 ++++++++ 10 files changed, 92 insertions(+), 50 deletions(-) create mode 100644 testscripts/commands/mod_init.txt diff --git a/commands/commandeer.go b/commands/commandeer.go index f18a95bb9..e8cde2114 100644 --- a/commands/commandeer.go +++ b/commands/commandeer.go @@ -88,6 +88,11 @@ type commonConfig struct { fs *hugofs.Fs } +type configKey struct { + counter int32 + ignoreModulesDoesNotExists bool +} + // This is the root command. type rootCommand struct { Printf func(format string, v ...interface{}) @@ -101,8 +106,8 @@ type rootCommand struct { // Some, but not all commands need access to these. // Some needs more than one, so keep them in a small cache. - commonConfigs *lazycache.Cache[int32, *commonConfig] - hugoSites *lazycache.Cache[int32, *hugolib.HugoSites] + commonConfigs *lazycache.Cache[configKey, *commonConfig] + hugoSites *lazycache.Cache[configKey, *hugolib.HugoSites] // changesFromBuild received from Hugo in watch mode. changesFromBuild chan []identity.Identity @@ -160,17 +165,18 @@ func (r *rootCommand) Commands() []simplecobra.Commander { return r.commands } -func (r *rootCommand) ConfigFromConfig(key int32, oldConf *commonConfig) (*commonConfig, error) { - cc, _, err := r.commonConfigs.GetOrCreate(key, func(key int32) (*commonConfig, error) { +func (r *rootCommand) ConfigFromConfig(key configKey, oldConf *commonConfig) (*commonConfig, error) { + cc, _, err := r.commonConfigs.GetOrCreate(key, func(key configKey) (*commonConfig, error) { fs := oldConf.fs configs, err := allconfig.LoadConfig( allconfig.ConfigSourceDescriptor{ - Flags: oldConf.cfg, - Fs: fs.Source, - Filename: r.cfgFile, - ConfigDir: r.cfgDir, - Logger: r.logger, - Environment: r.environment, + Flags: oldConf.cfg, + Fs: fs.Source, + Filename: r.cfgFile, + ConfigDir: r.cfgDir, + Logger: r.logger, + Environment: r.environment, + IgnoreModuleDoesNotExist: key.ignoreModulesDoesNotExists, }, ) if err != nil { @@ -193,11 +199,11 @@ func (r *rootCommand) ConfigFromConfig(key int32, oldConf *commonConfig) (*commo return cc, err } -func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commonConfig, error) { +func (r *rootCommand) ConfigFromProvider(key configKey, cfg config.Provider) (*commonConfig, error) { if cfg == nil { panic("cfg must be set") } - cc, _, err := r.commonConfigs.GetOrCreate(key, func(key int32) (*commonConfig, error) { + cc, _, err := r.commonConfigs.GetOrCreate(key, func(key configKey) (*commonConfig, error) { var dir string if r.source != "" { dir, _ = filepath.Abs(r.source) @@ -220,12 +226,13 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo // Load the config first to allow publishDir to be configured in config file. configs, err := allconfig.LoadConfig( allconfig.ConfigSourceDescriptor{ - Flags: cfg, - Fs: hugofs.Os, - Filename: r.cfgFile, - ConfigDir: r.cfgDir, - Environment: r.environment, - Logger: r.logger, + Flags: cfg, + Fs: hugofs.Os, + Filename: r.cfgFile, + ConfigDir: r.cfgDir, + Environment: r.environment, + Logger: r.logger, + IgnoreModuleDoesNotExist: key.ignoreModulesDoesNotExists, }, ) if err != nil { @@ -307,7 +314,8 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo } func (r *rootCommand) HugFromConfig(conf *commonConfig) (*hugolib.HugoSites, error) { - h, _, err := r.hugoSites.GetOrCreate(r.configVersionID.Load(), func(key int32) (*hugolib.HugoSites, error) { + k := configKey{counter: r.configVersionID.Load()} + h, _, err := r.hugoSites.GetOrCreate(k, func(key configKey) (*hugolib.HugoSites, error) { depsCfg := r.newDepsConfig(conf) return hugolib.NewHugoSites(depsCfg) }) @@ -315,7 +323,12 @@ func (r *rootCommand) HugFromConfig(conf *commonConfig) (*hugolib.HugoSites, err } func (r *rootCommand) Hugo(cfg config.Provider) (*hugolib.HugoSites, error) { - h, _, err := r.hugoSites.GetOrCreate(r.configVersionID.Load(), func(key int32) (*hugolib.HugoSites, error) { + return r.getOrCreateHugo(cfg, false) +} + +func (r *rootCommand) getOrCreateHugo(cfg config.Provider, ignoreModuleDoesNotExist bool) (*hugolib.HugoSites, error) { + k := configKey{counter: r.configVersionID.Load(), ignoreModulesDoesNotExists: ignoreModuleDoesNotExist} + h, _, err := r.hugoSites.GetOrCreate(k, func(key configKey) (*hugolib.HugoSites, error) { conf, err := r.ConfigFromProvider(key, cfg) if err != nil { return nil, err @@ -418,11 +431,11 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error { r.changesFromBuild = make(chan []identity.Identity, 10) - r.commonConfigs = lazycache.New(lazycache.Options[int32, *commonConfig]{MaxEntries: 5}) + r.commonConfigs = lazycache.New(lazycache.Options[configKey, *commonConfig]{MaxEntries: 5}) // We don't want to keep stale HugoSites in memory longer than needed. - r.hugoSites = lazycache.New(lazycache.Options[int32, *hugolib.HugoSites]{ + r.hugoSites = lazycache.New(lazycache.Options[configKey, *hugolib.HugoSites]{ MaxEntries: 1, - OnEvict: func(key int32, value *hugolib.HugoSites) { + OnEvict: func(key configKey, value *hugolib.HugoSites) { value.Close() runtime.GC() }, diff --git a/commands/config.go b/commands/config.go index adf6bbe2b..c3d08ae22 100644 --- a/commands/config.go +++ b/commands/config.go @@ -58,7 +58,7 @@ func (c *configCommand) Name() string { } func (c *configCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { - conf, err := c.r.ConfigFromProvider(c.r.configVersionID.Load(), flagsToCfg(cd, nil)) + conf, err := c.r.ConfigFromProvider(configKey{counter: c.r.configVersionID.Load()}, flagsToCfg(cd, nil)) if err != nil { return err } @@ -209,7 +209,7 @@ func (c *configMountsCommand) Name() string { func (c *configMountsCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { r := c.configCmd.r - conf, err := r.ConfigFromProvider(r.configVersionID.Load(), flagsToCfg(cd, nil)) + conf, err := r.ConfigFromProvider(configKey{counter: c.r.configVersionID.Load()}, flagsToCfg(cd, nil)) if err != nil { return err } diff --git a/commands/hugobuilder.go b/commands/hugobuilder.go index d15d47326..42bf68a37 100644 --- a/commands/hugobuilder.go +++ b/commands/hugobuilder.go @@ -1047,7 +1047,7 @@ func (c *hugoBuilder) loadConfig(cd *simplecobra.Commandeer, running bool) error "fastRenderMode": c.fastRenderMode, }) - conf, err := c.r.ConfigFromProvider(c.r.configVersionID.Load(), flagsToCfg(cd, cfg)) + conf, err := c.r.ConfigFromProvider(configKey{counter: c.r.configVersionID.Load()}, flagsToCfg(cd, cfg)) if err != nil { return err } @@ -1116,7 +1116,7 @@ func (c *hugoBuilder) reloadConfig() error { if err := c.withConfE(func(conf *commonConfig) error { oldConf := conf - newConf, err := c.r.ConfigFromConfig(c.r.configVersionID.Load(), conf) + newConf, err := c.r.ConfigFromConfig(configKey{counter: c.r.configVersionID.Load()}, conf) if err != nil { return err } diff --git a/commands/mod.go b/commands/mod.go index 0fa83e5c0..a9c8c459d 100644 --- a/commands/mod.go +++ b/commands/mod.go @@ -94,7 +94,7 @@ so this may/will change in future versions of Hugo. applyLocalFlagsBuildConfig(cmd, r) }, run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { - h, err := r.Hugo(flagsToCfg(cd, nil)) + h, err := r.getOrCreateHugo(flagsToCfg(cd, nil), true) if err != nil { return err } @@ -102,7 +102,11 @@ so this may/will change in future versions of Hugo. if len(args) >= 1 { initPath = args[0] } - return h.Configs.ModulesClient.Init(initPath) + c := h.Configs.ModulesClient + if err := c.Init(initPath); err != nil { + return err + } + return nil }, }, &simpleCommand{ @@ -115,7 +119,7 @@ so this may/will change in future versions of Hugo. cmd.Flags().BoolVarP(&clean, "clean", "", false, "delete module cache for dependencies that fail verification") }, run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { - conf, err := r.ConfigFromProvider(r.configVersionID.Load(), flagsToCfg(cd, nil)) + conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Load()}, flagsToCfg(cd, nil)) if err != nil { return err } @@ -135,7 +139,7 @@ Note that for vendored modules, that is the version listed and not the one from cmd.Flags().BoolVarP(&clean, "clean", "", false, "delete module cache for dependencies that fail verification") }, run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { - conf, err := r.ConfigFromProvider(r.configVersionID.Load(), flagsToCfg(cd, nil)) + conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Load()}, flagsToCfg(cd, nil)) if err != nil { return err } @@ -271,7 +275,7 @@ Run "go help get" for more information. All flags available for "go get" is also cfg := config.New() cfg.Set("workingDir", dir) - conf, err := r.ConfigFromProvider(r.configVersionID.Add(1), flagsToCfg(cd, cfg)) + conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Add(1)}, flagsToCfg(cd, cfg)) if err != nil { return err } @@ -284,7 +288,7 @@ Run "go help get" for more information. All flags available for "go get" is also }) return nil } else { - conf, err := r.ConfigFromProvider(r.configVersionID.Load(), flagsToCfg(cd, nil)) + conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Load()}, flagsToCfg(cd, nil)) if err != nil { return err } @@ -313,7 +317,7 @@ func (c *modCommands) Name() string { } func (c *modCommands) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { - _, err := c.r.ConfigFromProvider(c.r.configVersionID.Load(), nil) + _, err := c.r.ConfigFromProvider(configKey{counter: c.r.configVersionID.Load()}, nil) if err != nil { return err } diff --git a/commands/new.go b/commands/new.go index 4bad0059f..f6bb09e00 100644 --- a/commands/new.go +++ b/commands/new.go @@ -93,7 +93,7 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`, cfg.Set("workingDir", createpath) cfg.Set("publishDir", "public") - conf, err := r.ConfigFromProvider(r.configVersionID.Load(), flagsToCfg(cd, cfg)) + conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Load()}, flagsToCfg(cd, cfg)) if err != nil { return err } @@ -136,7 +136,7 @@ according to your needs.`, cfg := config.New() cfg.Set("publishDir", "public") - conf, err := r.ConfigFromProvider(r.configVersionID.Load(), flagsToCfg(cd, cfg)) + conf, err := r.ConfigFromProvider(configKey{counter: r.configVersionID.Load()}, flagsToCfg(cd, cfg)) if err != nil { return err } diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go index f6dfa7260..35517ece2 100644 --- a/config/allconfig/allconfig.go +++ b/config/allconfig/allconfig.go @@ -809,7 +809,7 @@ func (c *Configs) Init() error { } if len(c.Modules) == 0 { - return errors.New("no modules loaded (ned at least the main module)") + return errors.New("no modules loaded (need at least the main module)") } // Apply default project mounts. diff --git a/config/allconfig/load.go b/config/allconfig/load.go index 84419cb2e..16e2019cf 100644 --- a/config/allconfig/load.go +++ b/config/allconfig/load.go @@ -64,7 +64,7 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) { return nil, fmt.Errorf("failed to create config from result: %w", err) } - moduleConfig, modulesClient, err := l.loadModules(configs) + moduleConfig, modulesClient, err := l.loadModules(configs, d.IgnoreModuleDoesNotExist) if err != nil { return nil, fmt.Errorf("failed to load modules: %w", err) } @@ -116,6 +116,9 @@ type ConfigSourceDescriptor struct { // Defaults to os.Environ if not set. Environ []string + + // If set, this will be used to ignore the module does not exist error. + IgnoreModuleDoesNotExist bool } func (d ConfigSourceDescriptor) configFilenames() []string { @@ -453,7 +456,7 @@ func (l *configLoader) loadConfigMain(d ConfigSourceDescriptor) (config.LoadConf return res, l.ModulesConfig, err } -func (l *configLoader) loadModules(configs *Configs) (modules.ModulesConfig, *modules.Client, error) { +func (l *configLoader) loadModules(configs *Configs, ignoreModuleDoesNotExist bool) (modules.ModulesConfig, *modules.Client, error) { bcfg := configs.LoadingInfo.BaseConfig conf := configs.Base workingDir := bcfg.WorkingDir @@ -487,17 +490,18 @@ func (l *configLoader) loadModules(configs *Configs) (modules.ModulesConfig, *mo } modulesClient := modules.NewClient(modules.ClientConfig{ - Fs: l.Fs, - Logger: l.Logger, - Exec: ex, - HookBeforeFinalize: hook, - WorkingDir: workingDir, - ThemesDir: themesDir, - PublishDir: publishDir, - Environment: l.Environment, - CacheDir: conf.Caches.CacheDirModules(), - ModuleConfig: conf.Module, - IgnoreVendor: ignoreVendor, + Fs: l.Fs, + Logger: l.Logger, + Exec: ex, + HookBeforeFinalize: hook, + WorkingDir: workingDir, + ThemesDir: themesDir, + PublishDir: publishDir, + Environment: l.Environment, + CacheDir: conf.Caches.CacheDirModules(), + ModuleConfig: conf.Module, + IgnoreVendor: ignoreVendor, + IgnoreModuleDoesNotExist: ignoreModuleDoesNotExist, }) moduleConfig, err := modulesClient.Collect() diff --git a/modules/client.go b/modules/client.go index dce40d2db..70b3ac485 100644 --- a/modules/client.go +++ b/modules/client.go @@ -742,6 +742,9 @@ type ClientConfig struct { // This can be nil. IgnoreVendor glob.Glob + // Ignore any module not found errors. + IgnoreModuleDoesNotExist bool + // Absolute path to the project dir. WorkingDir string diff --git a/modules/collect.go b/modules/collect.go index cfb6d9249..4eb12a1a6 100644 --- a/modules/collect.go +++ b/modules/collect.go @@ -699,6 +699,9 @@ func (c *collector) normalizeMounts(owner *moduleAdapter, mounts []Mount) ([]Mou } func (c *collector) wrapModuleNotFound(err error) error { + if c.Client.ccfg.IgnoreModuleDoesNotExist { + return nil + } err = fmt.Errorf(err.Error()+": %w", ErrNotExist) if c.GoModulesFilename == "" { return err diff --git a/testscripts/commands/mod_init.txt b/testscripts/commands/mod_init.txt new file mode 100644 index 000000000..c09e71a23 --- /dev/null +++ b/testscripts/commands/mod_init.txt @@ -0,0 +1,15 @@ +# Test the hugo init command. +dostounix golden/go.mod.testsubmod + +hugo mod init testsubmod +cmpenv go.mod $WORK/golden/go.mod.testsubmod + +-- hugo.toml -- +title = "Hugo Modules Test" +[module] +[[module.imports]] +path="github.com/bep/empty-hugo-module" +-- golden/go.mod.testsubmod -- +module testsubmod + +go ${GOVERSION} \ No newline at end of file