Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V3: Fix to use the version of goa specified in go.mod file, when 'goa gen'/'goa example' #2182

Merged
merged 12 commits into from
Jul 10, 2019
57 changes: 54 additions & 3 deletions cmd/goa/gen.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package main

import (
"bufio"
"fmt"
"go/parser"
"go/token"
"io"
"io/ioutil"
"os"
"os/exec"
Expand Down Expand Up @@ -141,12 +143,61 @@ func (g *Generator) Write(debug bool) error {
return err
}

const goModEnvKey = "GOMOD"

func findGoMod() string {
env := os.Getenv(goModEnvKey)
if _, err := exec.LookPath("go"); err != nil {
return env
}
mod, err := exec.Command("go", "env", goModEnvKey).Output()
if err != nil {
return env
}
return strings.TrimSpace(string(mod))
}

func (g *Generator) goaPackage() (string, error) {
goaPkg := "goa.design/goa"
if g.DesignVersion < 3 {
return goaPkg, nil
}
goaPkg = fmt.Sprintf("goa.design/goa/v%d", g.DesignVersion)
path := findGoMod()
if _, err := os.Stat(path); err != nil {
return goaPkg, nil
}
fp, err := os.Open(path)
if err != nil {
return "", err
}
defer fp.Close()
return parseGoModGoaPackage(goaPkg, fp)
}

var reMod = regexp.MustCompile(`^\s*(?:require )?\s*(goa\.design/goa/v\d+?)\s+([^\/]\S+?)\s*(?:\/\/.+)?$`)

func parseGoModGoaPackage(pkg string, r io.Reader) (string, error) {
s := bufio.NewScanner(r)
for s.Scan() {
match := reMod.FindStringSubmatch(s.Text())
if len(match) == 3 && match[1] == pkg {
return match[1] + "@" + match[2], nil
}
}
if err := s.Err(); err != nil {
return "", fmt.Errorf("scan error, %v", err)
}
return pkg, nil
}

// Compile compiles the generator.
func (g *Generator) Compile() error {
goaPkg := "goa.design/goa"
if g.DesignVersion > 2 {
goaPkg = fmt.Sprintf("goa.design/goa/v%d", g.DesignVersion)
goaPkg, err := g.goaPackage()
if err != nil {
return err
}
fmt.Println("go get ", goaPkg)
if err := g.runGoCmd("get", goaPkg); err != nil {
return err
}
Expand Down
210 changes: 210 additions & 0 deletions cmd/goa/gen_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package main

import (
"reflect"
"strings"
"testing"
)

func TestGenerator_goaPackage(t *testing.T) {
cases := []struct {
Name string
Version int
Expected string
}{
{
Name: "specify v2",
Version: 2,
Expected: "goa.design/goa",
},
{
Name: "specify v3, but go.mod file does not exist",
Version: 3,
Expected: "goa.design/goa/v3",
},
}
for _, c := range cases {
t.Run(c.Name, func(t *testing.T) {
pkg, err := (&Generator{
DesignVersion: c.Version,
}).goaPackage()
if err != nil {
t.Fatalf("unexpected error, %v", err)
}
if pkg != c.Expected {
t.Errorf("expected %v, got %v", c.Expected, pkg)
}
})
}
}

func TestParseGoModGoaPackage(t *testing.T) {
cases := []struct {
Name string
Mod string
Package string
Expected string
}{
{
Name: "simple require",
Mod: "require goa.design/goa/v3 v3.0.3-0.20190704022140-85024ebc66dc",
Package: "goa.design/goa/v3",
Expected: "goa.design/goa/[email protected]",
},
{
Name: "in require block",
Mod: `module calc
go 1.12
require (
github.com/ikawaha/kagome v1.0.0 // indirect
goa.design/goa/v3 v3.0.3-0.20190704022140-85024ebc66dc
goa.design/plugins/v3 v3.0.1
)
`,
Package: "goa.design/goa/v3",
Expected: "goa.design/goa/[email protected]",
},
{
Name: "not found",
Mod: `module calc
go 1.12
require (
github.com/ikawaha/kagome v1.0.0 // indirect
goa.design/plugins/v3 v3.0.1
)
`,
Package: "goa.design/goa/v3",
Expected: "goa.design/goa/v3",
},
{
Name: "with replace",
Mod: `module calc
go 1.12
replace goa.design/goa/v3 => ../../../goa.design/goa
require (
github.com/ikawaha/kagome v1.0.0 // indirect
goa.design/goa/v3 v3.0.3-0.20190704022140-85024ebc66dc
goa.design/plugins/v3 v3.0.1
)
`,
Package: "goa.design/goa/v3",
Expected: "goa.design/goa/[email protected]",
},
{
Name: "with comment",
Mod: `module calc
go 1.12
replace goa.design/goa/v3 => ../../../goa.design/goa
require (
github.com/ikawaha/kagome v1.0.0 // indirect
goa.design/goa/v3 v3.0.3-0.20190704022140-85024ebc66dc // indirect // comment
goa.design/plugins/v3 v3.0.1
)
`,
Package: "goa.design/goa/v3",
Expected: "goa.design/goa/[email protected]",
},
{
Name: "require with comment",
Mod: " require goa.design/goa/v3 v3.0.3-0.20190704022140-85024ebc66dc// indirect // comment",
Package: "goa.design/goa/v3",
Expected: "goa.design/goa/[email protected]",
},
{
Name: "comment out",
Mod: `module calc
go 1.12
replace goa.design/goa/v3 => ../../../goa.design/goa
require (
github.com/ikawaha/kagome v1.0.0 // indirect
// goa.design/goa/v3 v3.0.3-0.20190704022140-85024ebc66dc
goa.design/plugins/v3 v3.0.1
)
`,
Package: "goa.design/goa/v3",
Expected: "goa.design/goa/v3",
},
{
Name: "without version",
Mod: " goa.design/goa/v3//v3.0.3-0.20190704022140-85024ebc66dc",
Package: "goa.design/goa/v3",
Expected: "goa.design/goa/v3",
},
{
Name: "different version",
Mod: "goa.design/goa/v2 v2.0.3-0.20190704022140-85024ebc66dc // comment",
Package: "goa.design/goa/v3",
Expected: "goa.design/goa/v3",
},
}
for _, c := range cases {
t.Run(c.Name, func(t *testing.T) {
pkg, err := parseGoModGoaPackage("goa.design/goa/v3", strings.NewReader(c.Mod))
if err != nil {
t.Fatalf("unexpected error, %v", err)
}
if pkg != c.Expected {
t.Errorf("expected %v, got %v", c.Expected, pkg)
}
})
}
}

func TestModPatternRegexp(t *testing.T) {
cases := []struct {
Name string
Input string
Expected []string
}{
{
Name: "goa v3 release version",
Input: "goa.design/goa/v3 v3.0.2",
Expected: []string{"goa.design/goa/v3 v3.0.2", "goa.design/goa/v3", "v3.0.2"},
},
{
Name: "goa v3 commit number",
Input: "goa.design/goa/v3 02c84c9b39d845611bfe6e8a96465d698ea374b1",
Expected: []string{"goa.design/goa/v3 02c84c9b39d845611bfe6e8a96465d698ea374b1", "goa.design/goa/v3", "02c84c9b39d845611bfe6e8a96465d698ea374b1"},
},
{
Name: "goa v3 with comment",
Input: "goa.design/goa/v3 v3.0.2 //comment1 //comment2 //comment3",
Expected: []string{"goa.design/goa/v3 v3.0.2 //comment1 //comment2 //comment3", "goa.design/goa/v3", "v3.0.2"},
},
{
Name: "goa v3 with require and comment",
Input: "require goa.design/goa/v3 v3.0.2 //comment",
Expected: []string{"require goa.design/goa/v3 v3.0.2 //comment", "goa.design/goa/v3", "v3.0.2"},
},
{
Name: "goa v2",
Input: "goa.design/goa/v2 v2.0.0",
Expected: []string{"goa.design/goa/v2 v2.0.0", "goa.design/goa/v2", "v2.0.0"},
},
{
Name: "goa v123",
Input: " goa.design/goa/v123 v123.4.5 ",
Expected: []string{" goa.design/goa/v123 v123.4.5 ", "goa.design/goa/v123", "v123.4.5"},
},
{
Name: "without version",
Input: "goa.design/goa/v3",
Expected: nil,
},
{
Name: "comment out",
Input: "// goa.design/goa/v3 v3.0.0",
Expected: nil,
},
{
Name: "irrelevant package",
Input: "github.com/ikawaha/kagome v1.10.0",
Expected: nil,
},
}
for _, c := range cases {
if got := reMod.FindStringSubmatch(c.Input); !reflect.DeepEqual(c.Expected, got) {
t.Errorf("expected %+v, got %+v", c.Expected, got)
}
}
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/manveru/gobdd v0.0.0-20131210092515-f1a17fdd710b // indirect
github.com/pkg/errors v0.8.1
github.com/sergi/go-diff v1.0.0
github.com/sirkon/goproxy v1.4.0
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops

github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
github.com/zach-klippenstein/goregen v0.0.0-20160303162051-795b5e3961ea
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c
Expand Down