commands: Ignore "module does not exist" errors in hugo mod init

Closes #11458
This commit is contained in:
Bjørn Erik Pedersen 2024-09-19 10:53:39 +02:00
parent c260cb28a9
commit e363964f2f
10 changed files with 92 additions and 50 deletions

View file

@ -88,6 +88,11 @@ type commonConfig struct {
fs *hugofs.Fs fs *hugofs.Fs
} }
type configKey struct {
counter int32
ignoreModulesDoesNotExists bool
}
// This is the root command. // This is the root command.
type rootCommand struct { type rootCommand struct {
Printf func(format string, v ...interface{}) Printf func(format string, v ...interface{})
@ -101,8 +106,8 @@ type rootCommand struct {
// Some, but not all commands need access to these. // Some, but not all commands need access to these.
// Some needs more than one, so keep them in a small cache. // Some needs more than one, so keep them in a small cache.
commonConfigs *lazycache.Cache[int32, *commonConfig] commonConfigs *lazycache.Cache[configKey, *commonConfig]
hugoSites *lazycache.Cache[int32, *hugolib.HugoSites] hugoSites *lazycache.Cache[configKey, *hugolib.HugoSites]
// changesFromBuild received from Hugo in watch mode. // changesFromBuild received from Hugo in watch mode.
changesFromBuild chan []identity.Identity changesFromBuild chan []identity.Identity
@ -160,17 +165,18 @@ func (r *rootCommand) Commands() []simplecobra.Commander {
return r.commands return r.commands
} }
func (r *rootCommand) ConfigFromConfig(key int32, oldConf *commonConfig) (*commonConfig, error) { func (r *rootCommand) ConfigFromConfig(key configKey, oldConf *commonConfig) (*commonConfig, error) {
cc, _, err := r.commonConfigs.GetOrCreate(key, func(key int32) (*commonConfig, error) { cc, _, err := r.commonConfigs.GetOrCreate(key, func(key configKey) (*commonConfig, error) {
fs := oldConf.fs fs := oldConf.fs
configs, err := allconfig.LoadConfig( configs, err := allconfig.LoadConfig(
allconfig.ConfigSourceDescriptor{ allconfig.ConfigSourceDescriptor{
Flags: oldConf.cfg, Flags: oldConf.cfg,
Fs: fs.Source, Fs: fs.Source,
Filename: r.cfgFile, Filename: r.cfgFile,
ConfigDir: r.cfgDir, ConfigDir: r.cfgDir,
Logger: r.logger, Logger: r.logger,
Environment: r.environment, Environment: r.environment,
IgnoreModuleDoesNotExist: key.ignoreModulesDoesNotExists,
}, },
) )
if err != nil { if err != nil {
@ -193,11 +199,11 @@ func (r *rootCommand) ConfigFromConfig(key int32, oldConf *commonConfig) (*commo
return cc, err 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 { if cfg == nil {
panic("cfg must be set") 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 var dir string
if r.source != "" { if r.source != "" {
dir, _ = filepath.Abs(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. // Load the config first to allow publishDir to be configured in config file.
configs, err := allconfig.LoadConfig( configs, err := allconfig.LoadConfig(
allconfig.ConfigSourceDescriptor{ allconfig.ConfigSourceDescriptor{
Flags: cfg, Flags: cfg,
Fs: hugofs.Os, Fs: hugofs.Os,
Filename: r.cfgFile, Filename: r.cfgFile,
ConfigDir: r.cfgDir, ConfigDir: r.cfgDir,
Environment: r.environment, Environment: r.environment,
Logger: r.logger, Logger: r.logger,
IgnoreModuleDoesNotExist: key.ignoreModulesDoesNotExists,
}, },
) )
if err != nil { 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) { 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) depsCfg := r.newDepsConfig(conf)
return hugolib.NewHugoSites(depsCfg) 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) { 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) conf, err := r.ConfigFromProvider(key, cfg)
if err != nil { if err != nil {
return nil, err return nil, err
@ -418,11 +431,11 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
r.changesFromBuild = make(chan []identity.Identity, 10) 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. // 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, MaxEntries: 1,
OnEvict: func(key int32, value *hugolib.HugoSites) { OnEvict: func(key configKey, value *hugolib.HugoSites) {
value.Close() value.Close()
runtime.GC() runtime.GC()
}, },

View file

@ -58,7 +58,7 @@ func (c *configCommand) Name() string {
} }
func (c *configCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { 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 { if err != nil {
return err return err
} }
@ -209,7 +209,7 @@ func (c *configMountsCommand) Name() string {
func (c *configMountsCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { func (c *configMountsCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error {
r := c.configCmd.r 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 { if err != nil {
return err return err
} }

View file

@ -1047,7 +1047,7 @@ func (c *hugoBuilder) loadConfig(cd *simplecobra.Commandeer, running bool) error
"fastRenderMode": c.fastRenderMode, "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 { if err != nil {
return err return err
} }
@ -1116,7 +1116,7 @@ func (c *hugoBuilder) reloadConfig() error {
if err := c.withConfE(func(conf *commonConfig) error { if err := c.withConfE(func(conf *commonConfig) error {
oldConf := conf 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 { if err != nil {
return err return err
} }

View file

@ -94,7 +94,7 @@ so this may/will change in future versions of Hugo.
applyLocalFlagsBuildConfig(cmd, r) applyLocalFlagsBuildConfig(cmd, r)
}, },
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error { 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 { if err != nil {
return err return err
} }
@ -102,7 +102,11 @@ so this may/will change in future versions of Hugo.
if len(args) >= 1 { if len(args) >= 1 {
initPath = args[0] 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{ &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") 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 { 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 { if err != nil {
return err 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") 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 { 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 { if err != nil {
return err 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 := config.New()
cfg.Set("workingDir", dir) 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 { if err != nil {
return err return err
} }
@ -284,7 +288,7 @@ Run "go help get" for more information. All flags available for "go get" is also
}) })
return nil return nil
} else { } 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 { if err != nil {
return err return err
} }
@ -313,7 +317,7 @@ func (c *modCommands) Name() string {
} }
func (c *modCommands) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error { 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 { if err != nil {
return err return err
} }

View file

@ -93,7 +93,7 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
cfg.Set("workingDir", createpath) cfg.Set("workingDir", createpath)
cfg.Set("publishDir", "public") 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 { if err != nil {
return err return err
} }
@ -136,7 +136,7 @@ according to your needs.`,
cfg := config.New() cfg := config.New()
cfg.Set("publishDir", "public") 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 { if err != nil {
return err return err
} }

View file

@ -809,7 +809,7 @@ func (c *Configs) Init() error {
} }
if len(c.Modules) == 0 { 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. // Apply default project mounts.

View file

@ -64,7 +64,7 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) {
return nil, fmt.Errorf("failed to create config from result: %w", err) 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 { if err != nil {
return nil, fmt.Errorf("failed to load modules: %w", err) return nil, fmt.Errorf("failed to load modules: %w", err)
} }
@ -116,6 +116,9 @@ type ConfigSourceDescriptor struct {
// Defaults to os.Environ if not set. // Defaults to os.Environ if not set.
Environ []string Environ []string
// If set, this will be used to ignore the module does not exist error.
IgnoreModuleDoesNotExist bool
} }
func (d ConfigSourceDescriptor) configFilenames() []string { func (d ConfigSourceDescriptor) configFilenames() []string {
@ -453,7 +456,7 @@ func (l *configLoader) loadConfigMain(d ConfigSourceDescriptor) (config.LoadConf
return res, l.ModulesConfig, err 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 bcfg := configs.LoadingInfo.BaseConfig
conf := configs.Base conf := configs.Base
workingDir := bcfg.WorkingDir workingDir := bcfg.WorkingDir
@ -487,17 +490,18 @@ func (l *configLoader) loadModules(configs *Configs) (modules.ModulesConfig, *mo
} }
modulesClient := modules.NewClient(modules.ClientConfig{ modulesClient := modules.NewClient(modules.ClientConfig{
Fs: l.Fs, Fs: l.Fs,
Logger: l.Logger, Logger: l.Logger,
Exec: ex, Exec: ex,
HookBeforeFinalize: hook, HookBeforeFinalize: hook,
WorkingDir: workingDir, WorkingDir: workingDir,
ThemesDir: themesDir, ThemesDir: themesDir,
PublishDir: publishDir, PublishDir: publishDir,
Environment: l.Environment, Environment: l.Environment,
CacheDir: conf.Caches.CacheDirModules(), CacheDir: conf.Caches.CacheDirModules(),
ModuleConfig: conf.Module, ModuleConfig: conf.Module,
IgnoreVendor: ignoreVendor, IgnoreVendor: ignoreVendor,
IgnoreModuleDoesNotExist: ignoreModuleDoesNotExist,
}) })
moduleConfig, err := modulesClient.Collect() moduleConfig, err := modulesClient.Collect()

View file

@ -742,6 +742,9 @@ type ClientConfig struct {
// This can be nil. // This can be nil.
IgnoreVendor glob.Glob IgnoreVendor glob.Glob
// Ignore any module not found errors.
IgnoreModuleDoesNotExist bool
// Absolute path to the project dir. // Absolute path to the project dir.
WorkingDir string WorkingDir string

View file

@ -699,6 +699,9 @@ func (c *collector) normalizeMounts(owner *moduleAdapter, mounts []Mount) ([]Mou
} }
func (c *collector) wrapModuleNotFound(err error) error { func (c *collector) wrapModuleNotFound(err error) error {
if c.Client.ccfg.IgnoreModuleDoesNotExist {
return nil
}
err = fmt.Errorf(err.Error()+": %w", ErrNotExist) err = fmt.Errorf(err.Error()+": %w", ErrNotExist)
if c.GoModulesFilename == "" { if c.GoModulesFilename == "" {
return err return err

View file

@ -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}