diff --git a/modules/client.go b/modules/client.go index 09adeab906b..5a2fb450ae9 100644 --- a/modules/client.go +++ b/modules/client.go @@ -717,12 +717,13 @@ func (c *Client) runGo( return nil } - stderr := new(bytes.Buffer) + stdErrBuf := new(bytes.Buffer) + stdOutBuf := new(bytes.Buffer) argsv := collections.StringSliceToInterfaceSlice(args) argsv = append(argsv, hexec.WithEnviron(c.environ)) - argsv = append(argsv, hexec.WithStderr(goOutputReplacerWriter{w: io.MultiWriter(stderr, os.Stderr)})) - argsv = append(argsv, hexec.WithStdout(stdout)) + argsv = append(argsv, hexec.WithStderr(goOutputReplacerWriter{w: io.MultiWriter(stdErrBuf, os.Stderr)})) + argsv = append(argsv, hexec.WithStdout(goOutputReplacerWriter{w: io.MultiWriter(stdOutBuf, stdout)})) argsv = append(argsv, hexec.WithDir(c.ccfg.WorkingDir)) argsv = append(argsv, hexec.WithContext(ctx)) @@ -737,7 +738,15 @@ func (c *Client) runGo( return nil } - if strings.Contains(stderr.String(), "invalid version: unknown revision") { + var stdOutMap map[string]any + err2 := json.Unmarshal(stdOutBuf.Bytes(), &stdOutMap) + if err2 == nil { + if stdOutMap["Error"] != "" { + return fmt.Errorf("%s", stdOutMap["Error"]) + } + } + + if strings.Contains(stdErrBuf.String(), "invalid version: unknown revision") { // See https://github.com/gohugoio/hugo/issues/6825 c.logger.Println(`An unknown revision most likely means that someone has deleted the remote ref (e.g. with a force push to GitHub). To resolve this, you need to manually edit your go.mod file and replace the version for the module in question with a valid ref. @@ -755,12 +764,12 @@ If you then run 'hugo mod graph' it should resolve itself to the most recent ver } // Too old Go version - if strings.Contains(stderr.String(), "flag provided but not defined") { + if strings.Contains(stdErrBuf.String(), "flag provided but not defined") { c.goBinaryStatus = goBinaryStatusTooOld return nil } - return fmt.Errorf("go command failed: %s", stderr) + return fmt.Errorf("go command failed: %s", stdErrBuf.String()) } diff --git a/modules/collect.go b/modules/collect.go index 6ff37aa63a0..3ce45a4939c 100644 --- a/modules/collect.go +++ b/modules/collect.go @@ -371,6 +371,14 @@ func (c *collector) addAndRecurse(owner *moduleAdapter) error { continue } + // Validate the combination of module path and version. + if moduleImport.Version != "" { + err := module.Check(moduleImport.Path, moduleImport.Version) + if err != nil { + return err + } + } + // Prevent cyclic references. if v := c.isPathSeen(moduleImport.Path, owner); v != nil && v != owner { continue diff --git a/modules/modules_integration_test.go b/modules/modules_integration_test.go index 6f09a35e0a8..0317d8539fc 100644 --- a/modules/modules_integration_test.go +++ b/modules/modules_integration_test.go @@ -14,8 +14,10 @@ package modules_test import ( + "strings" "testing" + qt "github.com/frankban/quicktest" "github.com/gohugoio/hugo/hugolib" ) @@ -48,3 +50,49 @@ Deps: {{ range hugo.Deps}}{{ printf "%s@%s" .Path .Version }}|{{ end }}$ b.AssertFileContent("public/blog/music/autumn-leaves/index.html", "Autumn Leaves is a popular jazz standard") // v0.2.0 b.AssertFileContent("public/v1/blog/music/autumn-leaves/index.html", "Lorem markdownum, placidi peremptis") // v0.1.0 } + +// Issue 14010 +func TestModuleImportErrors(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +[[module.imports]] +PATH +VERSION +` + f := strings.NewReplacer("PATH", "", "VERSION", "").Replace(files) + b, err := hugolib.TestE(t, f) + b.Assert(err, qt.IsNotNil) + b.Assert(err, qt.ErrorMatches, `^failed to load modules: module "" not found.*`) + + f = strings.NewReplacer("PATH", "path = 'foo'", "VERSION", "").Replace(files) + b, err = hugolib.TestE(t, f) + b.Assert(err, qt.IsNotNil) + b.Assert(err, qt.ErrorMatches, `^failed to load modules: module "foo" not found.*`) + + f = strings.NewReplacer("PATH", "path = 'foo'", "VERSION", "version = 'badSemVer'").Replace(files) + b, err = hugolib.TestE(t, f) + b.Assert(err, qt.IsNotNil) + b.Assert(err, qt.ErrorMatches, `failed to load modules: malformed module path "foo": missing dot in first path element`) + + f = strings.NewReplacer("PATH", "path = 'foo.bar'", "VERSION", "version = 'badSemVer'").Replace(files) + b, err = hugolib.TestE(t, f) + b.Assert(err, qt.IsNotNil) + b.Assert(err, qt.ErrorMatches, `failed to load modules: foo.bar@badSemVer: invalid version: not a semantic version`) + + f = strings.NewReplacer("PATH", "path = 'foo.bar'", "VERSION", "version = 'v6.7.42'").Replace(files) + b, err = hugolib.TestE(t, f) + b.Assert(err, qt.IsNotNil) + b.Assert(err, qt.ErrorMatches, `failed to load modules: foo.bar@v6.7.42: invalid version: should be v0 or v1, not v6`) + + f = strings.NewReplacer("PATH", "path = 'foo.bar/v2'", "VERSION", "version = 'v6.7.42'").Replace(files) + b, err = hugolib.TestE(t, f) + b.Assert(err, qt.IsNotNil) + b.Assert(err, qt.ErrorMatches, `failed to load modules: foo.bar/v2@v6.7.42: invalid version: should be v2, not v6`) + + f = strings.NewReplacer("PATH", "path = 'github.com/bep/hugo-mod-misc/dummy-content/v99'", "VERSION", "version = 'v99.0.0'").Replace(files) + b, err = hugolib.TestE(t, f) + b.Assert(err, qt.IsNotNil) + b.Assert(err, qt.ErrorMatches, `failed to load modules: failed to download module github.com/bep/hugo-mod-misc/dummy-content/v99@v99.0.0: github.com/bep/hugo-mod-misc/dummy-content/v99@v99.0.0: invalid version: unknown revision dummy-content/v99.0.0`) +} diff --git a/testscripts/commands/mod_vendor__versions.txt b/testscripts/commands/mod_vendor__versions.txt index 10a8a0367ab..3d06c09d654 100644 --- a/testscripts/commands/mod_vendor__versions.txt +++ b/testscripts/commands/mod_vendor__versions.txt @@ -3,15 +3,15 @@ dostounix golden/vendor.txt hugo mod vendor cmp _vendor/modules.txt golden/vendor.txt lsr _vendor -stdout 'github.com/bep/hugo-mod-misc/dummy-content@%3C%3Dv0.1.0/config.toml' +stdout 'github.com/bep/hugo-mod-misc/dummy-content@v0.1.0/config.toml' stdout 'github.com/bep/hugo-mod-misc/dummy-content@v0.2.0/config.toml' stdout 'github.com/bep/hugo-mod-misc/dummy-content@v0.2.0/content/blog/music/all-of-me/index.md' stdout 'github.com/bep/hugo-mod-misc/dummy-content@v0.2.0/content/blog/music/blue-bossa/index.md' -stdout 'github.com/bep/hugo-mod-misc/dummy-content@%3C%3Dv0.1.0/content/blog/music/all-of-me/index.md' -! stdout 'github.com/bep/hugo-mod-misc/dummy-content@%3C%3Dv0.1.0/content/blog/music/blue-bossa/index.md' # not mounted +stdout 'github.com/bep/hugo-mod-misc/dummy-content@v0.1.0/content/blog/music/all-of-me/index.md' +! stdout 'github.com/bep/hugo-mod-misc/dummy-content@v0.1.0/content/blog/music/blue-bossa/index.md' # not mounted hugo mod graph -stdout 'project github.com/bep/hugo-mod-misc/dummy-content@v0.2.0' +stdout 'project github.com/bep/hugo-mod-misc/dummy-content@v0.2.0' stdout 'project github.com/bep/hugo-mod-misc/dummy-content@v0.1.0' hugo config mounts @@ -24,7 +24,7 @@ path = "github.com/bep/hugo-mod-misc/dummy-content" version = "v0.2.0" [[module.imports]] path = "github.com/bep/hugo-mod-misc/dummy-content" -version = "<=v0.1.0" +version = "v0.1.0" [[module.imports.mounts]] source = "content/blog/music/all-of-me" target = "content/all" @@ -35,7 +35,7 @@ Deps: {{ range hugo.Deps}}{{ printf "%s@%s" .Path .Version }}|{{ end }}$ -- golden/vendor.txt -- # github.com/bep/hugo-mod-misc/dummy-content@v0.2.0 v0.2.0 -# github.com/bep/hugo-mod-misc/dummy-content@%3C%3Dv0.1.0 v0.1.0 +# github.com/bep/hugo-mod-misc/dummy-content@v0.1.0 v0.1.0 -- golden/mounts.json -- { "path": "project", @@ -92,7 +92,7 @@ Deps: {{ range hugo.Deps}}{{ printf "%s@%s" .Path .Version }}|{{ end }}$ "version": "v0.1.0", "time": "0001-01-01T00:00:00Z", "owner": "project", - "dir": "$WORK/_vendor/github.com/bep/hugo-mod-misc/dummy-content@%3C%3Dv0.1.0/", + "dir": "$WORK/_vendor/github.com/bep/hugo-mod-misc/dummy-content@v0.1.0/", "mounts": [ { "source": "content/blog/music/all-of-me",