Prechádzať zdrojové kódy

feat: Add migrate (#1419)

* Add migrate

* Remove unused module

* refactor filename

* rename refactor to migrate

Co-authored-by: anqiansong <anqiansong@bytedance.com>
anqiansong 3 rokov pred
rodič
commit
9d67fc4cfb

+ 1 - 1
go.mod

@@ -29,7 +29,7 @@ require (
 	go.opentelemetry.io/otel/sdk v1.1.0
 	go.opentelemetry.io/otel/trace v1.1.0
 	go.uber.org/automaxprocs v1.4.0
-	golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b // indirect
+	golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
 	golang.org/x/sys v0.0.0-20211106132015-ebca88c72f68 // indirect
 	golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
 	google.golang.org/genproto v0.0.0-20210928142010-c7af6a1a74c9 // indirect

+ 2 - 2
go.sum

@@ -488,8 +488,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
 golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b h1:eB48h3HiRycXNy8E0Gf5e0hv7YT6Kt14L/D73G1fuwo=
-golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=

+ 17 - 0
tools/goctl/goctl.go

@@ -23,6 +23,7 @@ import (
 	"github.com/tal-tech/go-zero/tools/goctl/internal/errorx"
 	"github.com/tal-tech/go-zero/tools/goctl/internal/version"
 	"github.com/tal-tech/go-zero/tools/goctl/kube"
+	"github.com/tal-tech/go-zero/tools/goctl/migrate"
 	"github.com/tal-tech/go-zero/tools/goctl/model/mongo"
 	model "github.com/tal-tech/go-zero/tools/goctl/model/sql/command"
 	"github.com/tal-tech/go-zero/tools/goctl/plugin"
@@ -45,6 +46,22 @@ var commands = []cli.Command{
 		Usage:  "upgrade goctl to latest version",
 		Action: upgrade.Upgrade,
 	},
+	{
+		Name:        "migrate",
+		Usage:       "migrate from tal-tech to zeromicro",
+		Description: "migrate is a transition command to help users migrate their projects from tal-tech to zeromicro version",
+		Action:      migrate.Migrate,
+		Flags: []cli.Flag{
+			cli.BoolFlag{
+				Name:  "verbose, v",
+				Usage: "verbose enables extra logging",
+			},
+			cli.StringFlag{
+				Name:  "version",
+				Usage: "the target release version of github.com/zeromicro/go-zero to refactor",
+			},
+		},
+	},
 	{
 		Name:  "api",
 		Usage: "generate api related files",

+ 113 - 0
tools/goctl/migrate/migrate.go

@@ -0,0 +1,113 @@
+package migrate
+
+import (
+	"bytes"
+	"fmt"
+	"go/ast"
+	"go/format"
+	"go/parser"
+	"go/token"
+	"io/fs"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+
+	"github.com/tal-tech/go-zero/tools/goctl/util/console"
+	"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
+	"github.com/urfave/cli"
+)
+
+const zeromicroVersion = "1.3.0"
+
+var fset = token.NewFileSet()
+
+func Migrate(c *cli.Context) error {
+	verbose := c.Bool("verbose")
+	version := c.String("version")
+	if len(version) == 0 {
+		version = zeromicroVersion
+	}
+	err := editMod(version, verbose)
+	if err != nil {
+		return err
+	}
+
+	err = rewriteImport(verbose)
+	if err != nil {
+		return err
+	}
+
+	err = tidy(verbose)
+	if err != nil {
+		return err
+	}
+
+	console.Success("[OK] refactor finish, execute %q on project root to check status.", "go test -race ./...")
+	return nil
+}
+
+func rewriteImport(verbose bool) error {
+	if verbose {
+		console.Info("preparing to rewrite import ...")
+		time.Sleep(200 * time.Millisecond)
+	}
+
+	wd, err := os.Getwd()
+	if err != nil {
+		return err
+	}
+	project, err := ctx.Prepare(wd)
+	if err != nil {
+		return err
+	}
+	root := project.Dir
+	fsys := os.DirFS(root)
+	return fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
+		if !d.IsDir() {
+			return nil
+		}
+		if verbose {
+			console.Info("walking to %q", path)
+		}
+		pkgs, err := parser.ParseDir(fset, path, func(info fs.FileInfo) bool {
+			return strings.HasSuffix(info.Name(), ".go")
+		}, parser.ParseComments)
+		if err != nil {
+			return err
+		}
+
+		return rewriteFile(pkgs, verbose)
+	})
+}
+
+func rewriteFile(pkgs map[string]*ast.Package, verbose bool) error {
+	for _, pkg := range pkgs {
+		for filename, file := range pkg.Files {
+			for _, imp := range file.Imports {
+				if !strings.Contains(imp.Path.Value, deprecatedGoZeroMod) {
+					continue
+				}
+				newPath := strings.ReplaceAll(imp.Path.Value, deprecatedGoZeroMod, goZeroMod)
+				imp.EndPos = imp.End()
+				imp.Path.Value = newPath
+			}
+
+			var w = bytes.NewBuffer(nil)
+			err := format.Node(w, fset, file)
+			if err != nil {
+				return fmt.Errorf("[rewriteImport] format file %s error: %+v", filename, err)
+			}
+
+			err = ioutil.WriteFile(filename, w.Bytes(), os.ModePerm)
+			if err != nil {
+				return fmt.Errorf("[rewriteImport] write file %s error: %+v", filename, err)
+			}
+			if verbose {
+				console.Success("[OK] rewriting %q ... ", filepath.Base(filename))
+			}
+		}
+	}
+	return nil
+}

+ 95 - 0
tools/goctl/migrate/mod.go

@@ -0,0 +1,95 @@
+package migrate
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"time"
+
+	"github.com/tal-tech/go-zero/core/stringx"
+	"github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
+	"github.com/tal-tech/go-zero/tools/goctl/util/console"
+	"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
+)
+
+const deprecatedGoZeroMod = "github.com/tal-tech/go-zero"
+const goZeroMod = "github.com/zeromicro/go-zero"
+
+var errInvalidGoMod = errors.New("it's only working for go module")
+
+func editMod(version string, verbose bool) error {
+	wd, err := os.Getwd()
+	if err != nil {
+		return err
+	}
+
+	isGoMod, _ := ctx.IsGoMod(wd)
+	if !isGoMod {
+		return nil
+	}
+
+	latest, err := getLatest(goZeroMod, verbose)
+	if err != nil {
+		return err
+	}
+
+	if !stringx.Contains(latest, version) {
+		return fmt.Errorf("release version %q is not found", version)
+	}
+	mod := fmt.Sprintf("%s@%s", goZeroMod, version)
+	err = removeRequire(deprecatedGoZeroMod, verbose)
+	if err != nil {
+		return err
+	}
+	return addRequire(mod, verbose)
+}
+
+func addRequire(mod string, verbose bool) error {
+	if verbose {
+		console.Info("adding require %s ...", mod)
+		time.Sleep(200 * time.Millisecond)
+	}
+	wd, err := os.Getwd()
+	if err != nil {
+		return err
+	}
+
+	isGoMod, _ := ctx.IsGoMod(wd)
+	if !isGoMod {
+		return errInvalidGoMod
+	}
+
+	_, err = execx.Run("go mod edit -require "+mod, wd)
+	return err
+}
+
+func removeRequire(mod string, verbose bool) error {
+	if verbose {
+		console.Info("remove require %s ...", mod)
+		time.Sleep(200 * time.Millisecond)
+	}
+	wd, err := os.Getwd()
+	if err != nil {
+		return err
+	}
+	_, err = execx.Run("go mod edit -droprequire "+mod, wd)
+	return err
+}
+
+func tidy(verbose bool) error {
+	if verbose {
+		console.Info("go mod tidy ...")
+		time.Sleep(200 * time.Millisecond)
+	}
+	wd, err := os.Getwd()
+	if err != nil {
+		return err
+	}
+	isGoMod, _ := ctx.IsGoMod(wd)
+	if !isGoMod {
+		return nil
+	}
+
+	_, err = execx.Run("go mod tidy", wd)
+	return err
+}

+ 42 - 0
tools/goctl/migrate/proxy.go

@@ -0,0 +1,42 @@
+package migrate
+
+import (
+	"net/url"
+	"os"
+	"strings"
+
+	"github.com/tal-tech/go-zero/core/stringx"
+	"github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
+)
+
+var defaultProxy = "https://goproxy.cn"
+var defaultProxies = []string{defaultProxy}
+
+func goProxy() []string {
+	wd, err := os.Getwd()
+	if err != nil {
+		return defaultProxies
+	}
+
+	proxy, err := execx.Run("go env GOPROXY", wd)
+	if err != nil {
+		return defaultProxies
+	}
+	list := strings.FieldsFunc(proxy, func(r rune) bool {
+		return r == '|' || r == ','
+	})
+	var ret []string
+	for _, item := range list {
+		if len(item) == 0 {
+			continue
+		}
+		_, err = url.Parse(item)
+		if err == nil && !stringx.Contains(ret, item) {
+			ret = append(ret, item)
+		}
+	}
+	if !stringx.Contains(ret, defaultProxy) {
+		ret = append(ret, defaultProxy)
+	}
+	return ret
+}

+ 47 - 0
tools/goctl/migrate/version.go

@@ -0,0 +1,47 @@
+package migrate
+
+import (
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/tal-tech/go-zero/tools/goctl/util/console"
+)
+
+var client = http.Client{
+	Timeout: 5 * time.Second,
+}
+
+func getLatest(repo string, verbose bool) ([]string, error) {
+	proxies := goProxy()
+	for _, proxy := range proxies {
+		if verbose {
+			console.Info("use go proxy %q", proxy)
+		}
+		log := func(err error) {
+			console.Warning("get latest versions failed from proxy %q, error: %+v", proxy, err)
+		}
+		resp, err := client.Get(fmt.Sprintf("%s/%s/@v/list", proxy, repo))
+		if err != nil {
+			log(err)
+			continue
+		}
+
+		if resp.StatusCode != http.StatusOK {
+			log(fmt.Errorf("%s", resp.Status))
+			continue
+		}
+		defer resp.Body.Close()
+		data, err := ioutil.ReadAll(resp.Body)
+		if err != nil {
+			log(err)
+			continue
+		}
+		versionStr := string(data)
+		versions := strings.Fields(versionStr)
+		return versions, nil
+	}
+	return []string{}, nil
+}