Move the mount duplicate filter to the modules package

Also simplify the mount validation logic. There are plenty of ways a user can create mount configs that behaves oddly.
This commit is contained in:
Bjørn Erik Pedersen
2019-07-31 08:21:17 +02:00
parent edf9f0a354
commit 4b6c5eba30
6 changed files with 154 additions and 123 deletions

View File

@@ -66,7 +66,6 @@ const (
// level imports to start out.
func NewClient(cfg ClientConfig) *Client {
fs := cfg.Fs
n := filepath.Join(cfg.WorkingDir, goModFilename)
goModEnabled, _ := afero.Exists(fs, n)
var goModFilename string
@@ -97,9 +96,7 @@ func NewClient(cfg ClientConfig) *Client {
return &Client{
fs: fs,
ignoreVendor: cfg.IgnoreVendor,
workingDir: cfg.WorkingDir,
themesDir: cfg.ThemesDir,
ccfg: cfg,
logger: logger,
moduleConfig: mcfg,
environ: env,
@@ -111,14 +108,7 @@ type Client struct {
fs afero.Fs
logger *loggers.Logger
// Ignore any _vendor directory.
ignoreVendor bool
// Absolute path to the project dir.
workingDir string
// Absolute path to the project's themes dir.
themesDir string
ccfg ClientConfig
// The top level module config
moduleConfig Config
@@ -194,7 +184,7 @@ func (c *Client) Tidy() error {
// meaning that if the top-level module is vendored, that will be the full
// set of dependencies.
func (c *Client) Vendor() error {
vendorDir := filepath.Join(c.workingDir, vendord)
vendorDir := filepath.Join(c.ccfg.WorkingDir, vendord)
if err := c.rmVendorDir(vendorDir); err != nil {
return err
}
@@ -284,7 +274,7 @@ func (c *Client) Init(path string) error {
return errors.Wrap(err, "failed to init modules")
}
c.GoModulesFilename = filepath.Join(c.workingDir, goModFilename)
c.GoModulesFilename = filepath.Join(c.ccfg.WorkingDir, goModFilename)
return nil
}
@@ -335,7 +325,7 @@ func (c *Client) rewriteGoMod(name string, isGoMod map[string]bool) error {
return err
}
if data != nil {
if err := afero.WriteFile(c.fs, filepath.Join(c.workingDir, name), data, 0666); err != nil {
if err := afero.WriteFile(c.fs, filepath.Join(c.ccfg.WorkingDir, name), data, 0666); err != nil {
return err
}
}
@@ -352,7 +342,7 @@ func (c *Client) rewriteGoModRewrite(name string, isGoMod map[string]bool) ([]by
modlineSplitter := getModlineSplitter(name == goModFilename)
b := &bytes.Buffer{}
f, err := c.fs.Open(filepath.Join(c.workingDir, name))
f, err := c.fs.Open(filepath.Join(c.ccfg.WorkingDir, name))
if err != nil {
if os.IsNotExist(err) {
// It's been deleted.
@@ -424,7 +414,7 @@ func (c *Client) runGo(
cmd := exec.CommandContext(ctx, "go", args...)
cmd.Env = c.environ
cmd.Dir = c.workingDir
cmd.Dir = c.ccfg.WorkingDir
cmd.Stdout = stdout
cmd.Stderr = io.MultiWriter(stderr, os.Stderr)
@@ -482,11 +472,22 @@ func (c *Client) tidy(mods Modules, goModOnly bool) error {
// ClientConfig configures the module Client.
type ClientConfig struct {
Fs afero.Fs
Logger *loggers.Logger
Fs afero.Fs
Logger *loggers.Logger
// If set, it will be run before we do any duplicate checks for modules
// etc.
HookBeforeFinalize func(m *ModulesConfig) error
// Ignore any _vendor directory.
IgnoreVendor bool
WorkingDir string
ThemesDir string // Absolute directory path
// Absolute path to the project dir.
WorkingDir string
// Absolute path to the project's themes dir.
ThemesDir string
CacheDir string // Module cache
ModuleConfig Config
}

View File

@@ -20,6 +20,8 @@ import (
"path/filepath"
"strings"
"github.com/gohugoio/hugo/common/loggers"
"github.com/spf13/cast"
"github.com/gohugoio/hugo/common/maps"
@@ -62,8 +64,25 @@ func CreateProjectModule(cfg config.Provider) (Module, error) {
func (h *Client) Collect() (ModulesConfig, error) {
mc, coll := h.collect(true)
return mc, coll.err
if coll.err != nil {
return mc, coll.err
}
if err := (&mc).setActiveMods(h.logger); err != nil {
return mc, err
}
if h.ccfg.HookBeforeFinalize != nil {
if err := h.ccfg.HookBeforeFinalize(&mc); err != nil {
return mc, err
}
}
if err := (&mc).finalize(h.logger); err != nil {
return mc, err
}
return mc, nil
}
func (h *Client) collect(tidy bool) (ModulesConfig, *collector) {
@@ -83,20 +102,8 @@ func (h *Client) collect(tidy bool) (ModulesConfig, *collector) {
}
}
// TODO(bep) consider --ignoreVendor vs removing from go.mod
var activeMods Modules
for _, mod := range c.modules {
if !mod.Config().HugoVersion.IsValid() {
h.logger.WARN.Printf(`Module %q is not compatible with this Hugo version; run "hugo mod graph" for more information.`, mod.Path())
}
if !mod.Disabled() {
activeMods = append(activeMods, mod)
}
}
return ModulesConfig{
AllModules: c.modules,
ActiveModules: activeMods,
GoModulesFilename: c.GoModulesFilename,
}, c
@@ -113,6 +120,43 @@ type ModulesConfig struct {
GoModulesFilename string
}
func (m *ModulesConfig) setActiveMods(logger *loggers.Logger) error {
var activeMods Modules
for _, mod := range m.AllModules {
if !mod.Config().HugoVersion.IsValid() {
logger.WARN.Printf(`Module %q is not compatible with this Hugo version; run "hugo mod graph" for more information.`, mod.Path())
}
if !mod.Disabled() {
activeMods = append(activeMods, mod)
}
}
m.ActiveModules = activeMods
return nil
}
func (m *ModulesConfig) finalize(logger *loggers.Logger) error {
for _, mod := range m.AllModules {
m := mod.(*moduleAdapter)
m.mounts = filterUnwantedMounts(m.mounts)
}
return nil
}
func filterUnwantedMounts(mounts []Mount) []Mount {
// Remove duplicates
seen := make(map[Mount]bool)
tmp := mounts[:0]
for _, m := range mounts {
if !seen[m] {
tmp = append(tmp, m)
}
seen[m] = true
}
return tmp
}
type collected struct {
// Pick the first and prevent circular loops.
seen map[string]bool
@@ -177,7 +221,7 @@ func (c *collector) add(owner *moduleAdapter, moduleImport Import, disabled bool
modulePath := moduleImport.Path
var realOwner Module = owner
if !c.ignoreVendor {
if !c.ccfg.IgnoreVendor {
if err := c.collectModulesTXT(owner); err != nil {
return nil, err
}
@@ -223,10 +267,10 @@ func (c *collector) add(owner *moduleAdapter, moduleImport Import, disabled bool
// Fall back to /themes/<mymodule>
if moduleDir == "" {
moduleDir = filepath.Join(c.themesDir, modulePath)
moduleDir = filepath.Join(c.ccfg.ThemesDir, modulePath)
if found, _ := afero.Exists(c.fs, moduleDir); !found {
c.err = c.wrapModuleNotFound(errors.Errorf(`module %q not found; either add it as a Hugo Module or store it in %q.`, modulePath, c.themesDir))
c.err = c.wrapModuleNotFound(errors.Errorf(`module %q not found; either add it as a Hugo Module or store it in %q.`, modulePath, c.ccfg.ThemesDir))
return nil, nil
}
}
@@ -427,7 +471,7 @@ func (c *collector) collect() {
return
}
projectMod := createProjectModule(c.gomods.GetMain(), c.workingDir, c.moduleConfig)
projectMod := createProjectModule(c.gomods.GetMain(), c.ccfg.WorkingDir, c.moduleConfig)
if err := c.addAndRecurse(projectMod, false); err != nil {
c.err = err

View File

@@ -36,3 +36,19 @@ func TestPathKey(t *testing.T) {
}
}
func TestFilterUnwantedMounts(t *testing.T) {
mounts := []Mount{
Mount{Source: "a", Target: "b", Lang: "en"},
Mount{Source: "a", Target: "b", Lang: "en"},
Mount{Source: "b", Target: "c", Lang: "en"},
}
filtered := filterUnwantedMounts(mounts)
assert := require.New(t)
assert.Len(filtered, 2)
assert.Equal([]Mount{Mount{Source: "a", Target: "b", Lang: "en"}, Mount{Source: "b", Target: "c", Lang: "en"}}, filtered)
}

View File

@@ -15,7 +15,6 @@ package modules
import (
"fmt"
"path"
"path/filepath"
"strings"
@@ -174,18 +173,7 @@ func ApplyProjectConfigDefaults(cfg config.Provider, mod Module) error {
// Prepend the mounts from configuration.
mounts = append(moda.mounts, mounts...)
// Remove duplicates
seen := make(map[string]bool)
tmp := mounts[:0]
for _, m := range mounts {
key := path.Join(m.Lang, m.Source, m.Target)
if !seen[key] {
tmp = append(tmp, m)
}
seen[key] = true
}
moda.mounts = tmp
moda.mounts = mounts
return nil
}