diff --git a/tool/cmd/kitex/args/args.go b/tool/cmd/kitex/args/args.go index 29d86f5c25..4edbcea7b4 100644 --- a/tool/cmd/kitex/args/args.go +++ b/tool/cmd/kitex/args/args.go @@ -25,6 +25,10 @@ import ( "path/filepath" "strings" + "github.com/cloudwego/kitex/tool/cmd/kitex/versions" + + "github.com/cloudwego/kitex/tool/cmd/kitex/code" + "github.com/cloudwego/kitex/tool/internal_pkg/generator" "github.com/cloudwego/kitex/tool/internal_pkg/log" "github.com/cloudwego/kitex/tool/internal_pkg/pluginmode/protoc" @@ -50,7 +54,8 @@ type ExtraFlag struct { // Arguments . type Arguments struct { generator.Config - extends []*ExtraFlag + extends []*ExtraFlag + queryVersion bool } const ( @@ -77,6 +82,8 @@ func (a *Arguments) AddExtraFlag(e *ExtraFlag) { func (a *Arguments) buildFlags(version string) *flag.FlagSet { f := flag.NewFlagSet(os.Args[0], flag.ContinueOnError) + f.BoolVar(&a.queryVersion, "version", false, + "Show the version of kitex") f.BoolVar(&a.NoFastAPI, "no-fast-api", false, "Generate codes without injecting fast method.") f.StringVar(&a.ModuleName, "module", "", @@ -90,7 +97,6 @@ func (a *Arguments) buildFlags(version string) *flag.FlagSet { "Turn on verbose mode.") f.BoolVar(&a.GenerateInvoker, "invoker", false, "Generate invoker side codes when service name is specified.") - f.StringVar(&a.IDLType, "type", "unknown", "Specify the type of IDL: 'thrift' or 'protobuf'.") f.Var(&a.Includes, "I", "Add an IDL search path for includes.") f.Var(&a.ThriftOptions, "thrift", "Specify arguments for the thrift go compiler.") f.Var(&a.Hessian2Options, "hessian2", "Specify arguments for the hessian2 codec.") @@ -175,14 +181,33 @@ func (a *Arguments) ParseArgs(version, curpath string, kitexArgs []string) (err if err = f.Parse(kitexArgs); err != nil { return err } + if a.queryVersion { + println(a.Version) + return code.ErrExitZeroInterrupted + } + if !a.NoDependencyCheck { + if ok, _ := versions.CheckVersion(); !ok { + return code.ErrVersionCheckFailed + } + } if a.StreamX { a.ThriftOptions = append(a.ThriftOptions, "streamx") } if a.Record { - a.RecordCmd = os.Args + a.RecordCmd = append([]string{"kitex"}, kitexArgs...) } log.Verbose = a.Verbose + for i, inc := range a.Includes { + if util.IsGitURL(inc) { + localGitPath, gitErr := util.RunGitCommand(inc) + if gitErr != nil { + return fmt.Errorf("failed to pull IDL from git: %s, errMsg: %s\nYou can execute 'rm -rf ~/.kitex' to clean the git cache and try again", inc, gitErr.Error()) + } + a.Includes[i] = localGitPath + } + } + // format -thrift xxx,xxx to -thrift xx -thrift xx thriftOptions := make([]string, len(a.ThriftOptions)) for i := range a.ThriftOptions { @@ -238,16 +263,11 @@ func (a *Arguments) checkIDL(files []string) error { } a.IDL = files[0] - switch a.IDLType { - case Thrift, Protobuf: - case Unknown: - if typ, ok := guessIDLType(a.IDL); ok { - a.IDLType = typ - } else { - return fmt.Errorf("can not guess an IDL type from %q (unknown suffix), please specify with the '-type' flag", a.IDL) - } - default: - return fmt.Errorf("unsupported IDL type: %s", a.IDLType) + if typ, ok := guessIDLType(a.IDL); ok { + a.IDLType = typ + } else { + return fmt.Errorf("the last parameter shoule be IDL filename (xxx.thrift or xxx.proto), for example: " + + "\"kitex -service demo idl.thrift\"") } return nil } @@ -352,19 +372,6 @@ func (a *Arguments) BuildCmd(out io.Writer) (*exec.Cmd, error) { return nil, fmt.Errorf("failed to detect current executable: %s", err.Error()) } - for i, inc := range a.Includes { - if strings.HasPrefix(inc, "git@") || strings.HasPrefix(inc, "http://") || strings.HasPrefix(inc, "https://") { - localGitPath, errMsg, gitErr := util.RunGitCommand(inc) - if gitErr != nil { - if errMsg == "" { - errMsg = gitErr.Error() - } - return nil, fmt.Errorf("failed to pull IDL from git:%s\nYou can execute 'rm -rf ~/.kitex' to clean the git cache and try again", errMsg) - } - a.Includes[i] = localGitPath - } - } - configkv := a.Config.Pack() kas := strings.Join(configkv, ",") cmd := &exec.Cmd{ diff --git a/tool/cmd/kitex/code/code.go b/tool/cmd/kitex/code/code.go new file mode 100644 index 0000000000..43ddb47c8e --- /dev/null +++ b/tool/cmd/kitex/code/code.go @@ -0,0 +1,51 @@ +package code + +import ( + "errors" + "flag" + "fmt" +) + +// ToolExitCode defines the enumeration type for tool exit codes. +type ToolExitCode int + +const ( + // ToolSuccess indicates that the tool has run successfully and returned. + ToolSuccess ToolExitCode = iota + + ToolExecuteFailed + + ToolArgsError + // ToolVersionCheckFailed indicates that the tool's version check has failed. + ToolVersionCheckFailed + ToolInterrupted + ToolInitFailed +) + +var ( + ErrExitZeroInterrupted = fmt.Errorf("os.Exit(%d)", ToolInterrupted) + ErrVersionCheckFailed = fmt.Errorf("os.Exit(%d)", ToolVersionCheckFailed) +) + +func WrapExitCode(ret int) (error, ToolExitCode) { + switch ret { + case 0: + return nil, ToolSuccess + case 1: + return fmt.Errorf("tool run failed"), ToolExecuteFailed + default: + return fmt.Errorf("unexpected return value: %d", ret), ToolExecuteFailed + } +} + +func Good(ret ToolExitCode) bool { + return ret == ToolSuccess || ret == ToolInterrupted +} + +func IsInterrupted(err error) bool { + return errors.Is(err, flag.ErrHelp) || errors.Is(err, ErrExitZeroInterrupted) +} + +func IsVersionCheckFailed(err error) bool { + return errors.Is(err, ErrVersionCheckFailed) +} diff --git a/tool/cmd/kitex/generator/generator.go b/tool/cmd/kitex/generator/generator.go new file mode 100644 index 0000000000..1b443f23ff --- /dev/null +++ b/tool/cmd/kitex/generator/generator.go @@ -0,0 +1,84 @@ +package generator + +import ( + "bytes" + "fmt" + "os" + + "github.com/cloudwego/kitex/tool/internal_pkg/thriftgo" + + "github.com/cloudwego/kitex/tool/cmd/kitex/code" + + kargs "github.com/cloudwego/kitex/tool/cmd/kitex/args" + "github.com/cloudwego/kitex/tool/internal_pkg/pluginmode/protoc" + thriftgo_plugin "github.com/cloudwego/kitex/tool/internal_pkg/pluginmode/thriftgo" + "github.com/cloudwego/kitex/tool/internal_pkg/prutal" + "github.com/cloudwego/kitex/tool/internal_pkg/util/env" + + "github.com/cloudwego/kitex" + t "github.com/cloudwego/kitex/tool/internal_pkg/thriftgo" + "github.com/cloudwego/thriftgo/plugin" +) + +// RunKitexThriftgoGen run kitex tool to generate thrift as sdk +func RunKitexThriftgoGen(wd string, plugins []plugin.SDKPlugin, kitexOsArgs ...string) (error, code.ToolExitCode) { + var args kargs.Arguments + err := args.ParseArgs(kitex.Version, wd, kitexOsArgs) + if err != nil { + return err, code.ToolArgsError + } + return t.RunKitexThriftgoGenFromArgs(wd, plugins, &args) +} + +func RunKitexTool(curpath string, osArgs []string, args *kargs.Arguments, toolVersion string) (err error, retCode code.ToolExitCode) { + // try run as thriftgo/protoc plugin process + mode := os.Getenv(kargs.EnvPluginMode) + if len(osArgs) <= 1 && mode != "" { + switch mode { + case thriftgo_plugin.PluginName: + return code.WrapExitCode(thriftgo_plugin.Run()) + case protoc.PluginName: + return code.WrapExitCode(protoc.Run()) + } + return fmt.Errorf("unknown plugin mode %s", mode), code.ToolArgsError + } + + // parse arguments + err = args.ParseArgs(toolVersion, curpath, osArgs[1:]) + if err != nil { + if code.IsInterrupted(err) { + return nil, code.ToolInterrupted + } + if code.IsVersionCheckFailed(err) { + return err, code.ToolVersionCheckFailed + } + return err, code.ToolArgsError + } + + if args.IsProtobuf() && !env.UseProtoc() { + if err = prutal.NewPrutalGen(args.Config).Process(); err != nil { + return err, code.ToolExecuteFailed + } + } + + if args.IsThrift() && !args.LocalThriftgo { + return thriftgo.RunKitexThriftgoGenFromArgs(curpath, nil, args) + } + + out := new(bytes.Buffer) + cmd, err := args.BuildCmd(out) + if err != nil { + return err, code.ToolArgsError + } + + err = kargs.ValidateCMD(cmd.Path, args.IDLType) + if err != nil { + return err, code.ToolArgsError + } + + if err = cmd.Run(); err != nil { + return err, code.ToolExecuteFailed + } + + return nil, code.ToolSuccess +} diff --git a/tool/cmd/kitex/main.go b/tool/cmd/kitex/main.go index 7321544504..f8a3fbc2f8 100644 --- a/tool/cmd/kitex/main.go +++ b/tool/cmd/kitex/main.go @@ -15,128 +15,41 @@ package main import ( - "bytes" - "errors" - "flag" "os" "path/filepath" - "strings" - "github.com/cloudwego/kitex/tool/cmd/kitex/utils" + "github.com/cloudwego/kitex/tool/cmd/kitex/code" - "github.com/cloudwego/kitex/tool/cmd/kitex/sdk" + "github.com/cloudwego/kitex/tool/cmd/kitex/utils" "github.com/cloudwego/kitex" kargs "github.com/cloudwego/kitex/tool/cmd/kitex/args" - "github.com/cloudwego/kitex/tool/cmd/kitex/versions" + "github.com/cloudwego/kitex/tool/cmd/kitex/generator" "github.com/cloudwego/kitex/tool/internal_pkg/log" - "github.com/cloudwego/kitex/tool/internal_pkg/pluginmode/protoc" - "github.com/cloudwego/kitex/tool/internal_pkg/pluginmode/thriftgo" - "github.com/cloudwego/kitex/tool/internal_pkg/prutal" - "github.com/cloudwego/kitex/tool/internal_pkg/util/env" ) -var args kargs.Arguments - -func init() { - var queryVersion bool - args.AddExtraFlag(&kargs.ExtraFlag{ - Apply: func(f *flag.FlagSet) { - f.BoolVar(&queryVersion, "version", false, - "Show the version of kitex") - }, - Check: func(a *kargs.Arguments) error { - if queryVersion { - println(a.Version) - os.Exit(0) - } - return nil - }, - }) - if err := versions.RegisterMinDepVersion( - &versions.MinDepVersion{ - RefPath: "github.com/cloudwego/kitex", - Version: "v0.11.0", - }, - ); err != nil { - log.Error(err) - os.Exit(versions.CompatibilityCheckExitCode) - } -} - func main() { - mode := os.Getenv(kargs.EnvPluginMode) - if len(os.Args) <= 1 && mode != "" { - // run as a plugin - switch mode { - case thriftgo.PluginName: - os.Exit(thriftgo.Run()) - case protoc.PluginName: - os.Exit(protoc.Run()) - } - panic(mode) - } + var args kargs.Arguments curpath, err := filepath.Abs(".") if err != nil { - log.Errorf("Get current path failed: %s", err) - os.Exit(1) - } - // run as kitex - err = args.ParseArgs(kitex.Version, curpath, os.Args[1:]) - if err != nil { - if errors.Is(err, flag.ErrHelp) { - os.Exit(0) - } - log.Error(err) - os.Exit(2) - } - if !args.NoDependencyCheck { - // check dependency compatibility between kitex cmd tool and dependency in go.mod - if err := versions.DefaultCheckDependencyAndProcess(); err != nil { - os.Exit(versions.CompatibilityCheckExitCode) - } - } - if args.IsProtobuf() && !env.UseProtoc() { - g := prutal.NewPrutalGen(args.Config) - if err := g.Process(); err != nil { - log.Errorf("%s", err) - os.Exit(1) - } - return - } - - out := new(bytes.Buffer) - cmd, err := args.BuildCmd(out) - if err != nil { - log.Warn(err) - os.Exit(1) + log.Errorf("get current path failed: %s", err.Error()) + os.Exit(int(code.ToolExecuteFailed)) } - if args.IsThrift() && !args.LocalThriftgo { - if err = sdk.InvokeThriftgoBySDK(curpath, cmd); err != nil { - // todo: optimize -use and remove error returned from thriftgo - out.WriteString(err.Error()) - } - } else { - err = kargs.ValidateCMD(cmd.Path, args.IDLType) - if err != nil { - log.Warn(err) - os.Exit(1) - } - err = cmd.Run() + // run kitex tool: + // 1. check and execute process plugin mode callback + // 2. parse user input to arguments + // 3. check dependency version ( details: versions/dependencies_v2.go) + // 4. execute code generation by choosing specific tool (thriftgo、prutal、protoc?) + err, retCode := generator.RunKitexTool(curpath, os.Args, &args, kitex.Version) + if err != nil || !code.Good(retCode) { + log.Errorf(err.Error()) + os.Exit(int(retCode)) } - - if err != nil { - if args.Use != "" { - out := strings.TrimSpace(out.String()) - if strings.HasSuffix(out, thriftgo.TheUseOptionMessage) { - goto NormalExit - } - } - log.Warn(err) - os.Exit(1) + // only execute after tool finishes code generation (not -help、-version) + if retCode != code.ToolInterrupted { + utils.OnKitexToolNormalExit(args) } -NormalExit: - utils.OnKitexToolNormalExit(args) + os.Exit(int(code.ToolSuccess)) } diff --git a/tool/cmd/kitex/sdk/kitex_sdk.go b/tool/cmd/kitex/sdk/kitex_sdk.go deleted file mode 100644 index 4411a06aab..0000000000 --- a/tool/cmd/kitex/sdk/kitex_sdk.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2024 CloudWeGo Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sdk - -import ( - "bytes" - "errors" - "flag" - "fmt" - "os/exec" - "strings" - - "github.com/cloudwego/kitex/tool/internal_pkg/log" - - "github.com/cloudwego/thriftgo/plugin" - "github.com/cloudwego/thriftgo/sdk" - - "github.com/cloudwego/kitex" - kargs "github.com/cloudwego/kitex/tool/cmd/kitex/args" - "github.com/cloudwego/kitex/tool/internal_pkg/pluginmode/thriftgo" -) - -var args kargs.Arguments - -var errExitZero = errors.New("os.Exit(0)") - -func init() { - var queryVersion bool - args.AddExtraFlag(&kargs.ExtraFlag{ - Apply: func(f *flag.FlagSet) { - f.BoolVar(&queryVersion, "version", false, - "Show the version of kitex") - }, - Check: func(a *kargs.Arguments) error { - if queryVersion { - println(a.Version) - return errExitZero - } - return nil - }, - }) -} - -func RunKitexTool(wd string, plugins []plugin.SDKPlugin, kitexArgs ...string) error { - kitexPlugin, err := GetKiteXSDKPlugin(wd, kitexArgs) - if err != nil { - if errors.Is(err, flag.ErrHelp) || errors.Is(err, errExitZero) { - return nil - } - return err - } - s := []plugin.SDKPlugin{kitexPlugin} - s = append(s, plugins...) - - return sdk.RunThriftgoAsSDK(wd, s, kitexPlugin.GetThriftgoParameters()...) -} - -func GetKiteXSDKPlugin(pwd string, rawKiteXArgs []string) (*KiteXSDKPlugin, error) { - // run as kitex - err := args.ParseArgs(kitex.Version, pwd, rawKiteXArgs) - if err != nil { - return nil, err - } - - out := new(bytes.Buffer) - cmd, err := args.BuildCmd(out) - if err != nil { - return nil, err - } - - kitexPlugin := &KiteXSDKPlugin{} - - kitexPlugin.ThriftgoParams, kitexPlugin.KitexParams, err = ParseKitexCmd(cmd) - if err != nil { - return nil, err - } - kitexPlugin.Pwd = pwd - - return kitexPlugin, nil -} - -// InvokeThriftgoBySDK is for kitex tool main.go -func InvokeThriftgoBySDK(pwd string, cmd *exec.Cmd) (err error) { - kitexPlugin := &KiteXSDKPlugin{} - - kitexPlugin.ThriftgoParams, kitexPlugin.KitexParams, err = ParseKitexCmd(cmd) - if err != nil { - return err - } - - kitexPlugin.Pwd = pwd - - l := log.DefaultLogger() // pluginmode/thriftgo/convertor.go will change the logger - defer log.SetDefaultLogger(l) // revert it back - return sdk.RunThriftgoAsSDK(pwd, - []plugin.SDKPlugin{kitexPlugin}, - kitexPlugin.GetThriftgoParameters()...) -} - -type KiteXSDKPlugin struct { - KitexParams []string - ThriftgoParams []string - Pwd string -} - -func (k *KiteXSDKPlugin) Invoke(req *plugin.Request) (res *plugin.Response) { - return thriftgo.HandleRequest(req) -} - -func (k *KiteXSDKPlugin) GetName() string { - return "kitex" -} - -func (k *KiteXSDKPlugin) GetPluginParameters() []string { - return k.KitexParams -} - -func (k *KiteXSDKPlugin) GetThriftgoParameters() []string { - return k.ThriftgoParams -} - -func ParseKitexCmd(cmd *exec.Cmd) (thriftgoParams, kitexParams []string, err error) { - cmdArgs := cmd.Args - // thriftgo -r -o kitex_gen -g go:xxx -p kitex=xxxx -p otherplugin xxx.thrift - // ignore first argument, and remove -p kitex=xxxx - - thriftgoParams = []string{} - kitexParams = []string{} - if len(cmdArgs) < 1 { - return nil, nil, fmt.Errorf("cmd args too short: %s", cmdArgs) - } - - for i := 1; i < len(cmdArgs); i++ { - arg := cmdArgs[i] - if arg == "-p" && i+1 < len(cmdArgs) { - pluginArgs := cmdArgs[i+1] - if strings.HasPrefix(pluginArgs, "kitex") { - kitexParams = strings.Split(pluginArgs, ",") - i++ - continue - } - } - thriftgoParams = append(thriftgoParams, arg) - } - return thriftgoParams, kitexParams, nil -} diff --git a/tool/cmd/kitex/utils/utils.go b/tool/cmd/kitex/utils/utils.go index f24378aefc..f0fa0ff9de 100644 --- a/tool/cmd/kitex/utils/utils.go +++ b/tool/cmd/kitex/utils/utils.go @@ -15,9 +15,6 @@ package utils import ( - "os/exec" - "strings" - kargs "github.com/cloudwego/kitex/tool/cmd/kitex/args" "github.com/cloudwego/kitex/tool/internal_pkg/log" "github.com/cloudwego/kitex/tool/internal_pkg/pluginmode/thriftgo" @@ -25,16 +22,6 @@ import ( func OnKitexToolNormalExit(args kargs.Arguments) { log.Info("Code Generation is Done!") - - if args.IDLType == "thrift" { - cmd := "go mod edit -replace github.com/apache/thrift=github.com/apache/thrift@v0.13.0" - argv := strings.Split(cmd, " ") - err := exec.Command(argv[0], argv[1:]...).Run() - if err != nil { - log.Warnf("Tips: please execute the following command to fix apache thrift lib version.\n%s", cmd) - } - } - // If hessian option is java_extension, replace *java.Object to java.Object if thriftgo.EnableJavaExtension(args.Config) { if err := thriftgo.Hessian2PatchByReplace(args.Config, ""); err != nil { diff --git a/tool/cmd/kitex/versions/dependencies.go b/tool/cmd/kitex/versions/dependencies.go index 2113d1c81a..780518987d 100644 --- a/tool/cmd/kitex/versions/dependencies.go +++ b/tool/cmd/kitex/versions/dependencies.go @@ -99,9 +99,9 @@ func defaultParseCheckResult(cr *CheckResult) (prompt string, shouldExit bool) { } var defaultPrompt = `# Kitex Cmd Tool %s is not compatible with %s %s in your go.mod -# You can upgrade %s to latest version +# You can upgrade kitex to latest version go get %s@latest -# Or upgrade %s to %s version +# Or upgrade kitex to %s version go get %s@%s # Or downgrade Kitex Cmd Tool to %s version go install github.com/cloudwego/kitex/tool/cmd/kitex@%s` diff --git a/tool/cmd/kitex/versions/dependencies_v2.go b/tool/cmd/kitex/versions/dependencies_v2.go new file mode 100644 index 0000000000..116514d633 --- /dev/null +++ b/tool/cmd/kitex/versions/dependencies_v2.go @@ -0,0 +1,92 @@ +package versions + +import ( + "fmt" + + "github.com/cloudwego/kitex" + "github.com/cloudwego/kitex/tool/internal_pkg/log" +) + +type DependencyChecker struct { + minDepRepoPath string + minDepRepoVersionStr string + promptFunc CheckerPromptFunc +} + +const ( + KitexDependencyPath = "github.com/cloudwego/kitex" + KitexDependencyVersion = "v0.11.0" +) + +var defaultDepdencyChecker = &DependencyChecker{ + minDepRepoPath: KitexDependencyPath, + minDepRepoVersionStr: KitexDependencyVersion, + promptFunc: DefaultDependencyFailedPrompt, +} + +func DefaultDependencyFailedPrompt(minDepRepoPath, currentVersion, toolVersion string) string { + return fmt.Sprintf(defaultPrompt, toolVersion, minDepRepoPath, currentVersion, + minDepRepoPath, + toolVersion, + minDepRepoPath, toolVersion, + currentVersion, + currentVersion, + ) +} + +type CheckerPromptFunc func(minDepRepoPath, currentVersion, toolVersion string) string + +func RegisterCustomDependencyChecker( + minDepRepoPath, minDepRepoVersionStr string, + promptFunc CheckerPromptFunc, +) *DependencyChecker { + return &DependencyChecker{ + minDepRepoPath: minDepRepoPath, + minDepRepoVersionStr: minDepRepoVersionStr, + promptFunc: promptFunc, + } +} + +func CheckVersion() (ok bool, err error) { + cr := defaultDepdencyChecker + + if cr.minDepRepoVersionStr == "" || cr.minDepRepoPath == "" || cr.promptFunc == nil { + return true, fmt.Errorf("global dependency checker error") + } + + defer func() { + if err != nil { + log.Warnf("[Dependency Check]: %s", err.Error()) + } + }() + minDepRepoVersion, err := newVersion(cr.minDepRepoVersionStr) + if err != nil { + return true, err + } + + // output: github.com/xxx/xxx v1.2.3 + depRepoInfo, err := runGoListCmd(cr.minDepRepoPath) + if err != nil { + return true, err + } + + currentVersionStr := parseGoListVersion(depRepoInfo) + // replace with local repository + if currentVersionStr == "" { + return true, ErrDependencyReplacedWithLocalRepo + } + + currentVersion, err := newVersion(currentVersionStr) + if err != nil { + return true, ErrDependencyVersionNotSemantic + } + + // check compatibility + if !currentVersion.greatOrEqual(minDepRepoVersion) { + prompt := cr.promptFunc(cr.minDepRepoPath, currentVersionStr, kitex.Version) + log.Infof(prompt) + return false, ErrDependencyVersionNotCompatible + } + + return true, nil +} diff --git a/tool/internal_pkg/log/log.go b/tool/internal_pkg/log/log.go index 106d5bbb9d..9eb136e510 100644 --- a/tool/internal_pkg/log/log.go +++ b/tool/internal_pkg/log/log.go @@ -18,6 +18,8 @@ import ( "fmt" "log" "os" + + "github.com/cloudwego/kitex/tool/internal_pkg/util" ) // Verbose decides whether the informatc logs should be output. @@ -48,24 +50,28 @@ func SetDefaultLogger(l Logger) { defaultLogger = l } +var errorPrefix = util.Bold + util.FgRed + "[ERROR] " + util.Reset + +var warningPrefix = util.Bold + util.FgYellow + "[WARN] " + util.Reset + // Error ... func Error(v ...interface{}) { - defaultLogger.Printf("[ERROR] %s", fmt.Sprintln(v...)) + defaultLogger.Printf("%s%s", errorPrefix, fmt.Sprintln(v...)) } // Errorf ... func Errorf(format string, v ...interface{}) { - defaultLogger.Printf("[ERROR] "+format, v...) + defaultLogger.Printf(errorPrefix+format, v...) } // Warn ... func Warn(v ...interface{}) { - defaultLogger.Printf("[WARN] %s", fmt.Sprintln(v...)) + defaultLogger.Printf("%s %s", warningPrefix, fmt.Sprintln(v...)) } // Warnf ... func Warnf(format string, v ...interface{}) { - defaultLogger.Printf("[WARN] "+format, v...) + defaultLogger.Printf(warningPrefix+format, v...) } // Info ... diff --git a/tool/internal_pkg/pluginmode/thriftgo/convertor.go b/tool/internal_pkg/pluginmode/thriftgo/convertor.go index 2891a9fd6b..40d982bbc7 100644 --- a/tool/internal_pkg/pluginmode/thriftgo/convertor.go +++ b/tool/internal_pkg/pluginmode/thriftgo/convertor.go @@ -66,6 +66,8 @@ func (c *converter) init(req *plugin.Request) error { } func (c *converter) initLogs() backend.LogFunc { + // todo fix log + // todo 单步验证 thriftgo log lf := backend.LogFunc{ Info: func(v ...interface{}) {}, Warn: func(v ...interface{}) { @@ -75,13 +77,13 @@ func (c *converter) initLogs() backend.LogFunc { c.Warnings = append(c.Warnings, warns...) }, } - if c.Config.Verbose { - lf.Info = lf.Warn - } + //if c.Config.Verbose { + // lf.Info = lf.Warn + //} - log.SetDefaultLogger(log.LoggerFunc(func(format string, a ...interface{}) { - c.Warnings = append(c.Warnings, fmt.Sprintf(format, a...)) - })) + //log.SetDefaultLogger(log.LoggerFunc(func(format string, a ...interface{}) { + // c.Warnings = append(c.Warnings, fmt.Sprintf(format, a...)) + //})) return lf } diff --git a/tool/internal_pkg/pluginmode/thriftgo/plugin.go b/tool/internal_pkg/pluginmode/thriftgo/plugin.go index 75e877aa29..35cc7bc815 100644 --- a/tool/internal_pkg/pluginmode/thriftgo/plugin.go +++ b/tool/internal_pkg/pluginmode/thriftgo/plugin.go @@ -19,6 +19,8 @@ import ( "fmt" "os" + "github.com/cloudwego/kitex/tool/internal_pkg/log" + "github.com/cloudwego/thriftgo/plugin" "github.com/cloudwego/kitex/tool/internal_pkg/generator" @@ -28,9 +30,6 @@ import ( // PluginName is the link name when the kitex binary is used as a plugin for thriftgo. const PluginName = "thrift-gen-kitex" -// TheUseOptionMessage indicates that the generating of kitex_gen is aborted due to the -use option. -const TheUseOptionMessage = "kitex_gen is not generated due to the -use option" - // Run is an entry of the plugin mode of kitex for thriftgo. // It reads a plugin request from the standard input and writes out a response. func Run() int { @@ -132,7 +131,8 @@ func HandleRequest(req *plugin.Request) *plugin.Response { if conv.Config.Use != "" { err := conv.persist(res) if err == nil { - err = errors.New(TheUseOptionMessage) + log.Info("kitex_gen is not generated due to the -use option") + return res } return conv.failResp(err) } diff --git a/tool/internal_pkg/thriftgo/thriftgo.go b/tool/internal_pkg/thriftgo/thriftgo.go new file mode 100644 index 0000000000..7881c673fc --- /dev/null +++ b/tool/internal_pkg/thriftgo/thriftgo.go @@ -0,0 +1,108 @@ +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package thriftgo + +import ( + "bytes" + "fmt" + + "github.com/cloudwego/kitex" + "github.com/cloudwego/kitex/tool/internal_pkg/pluginmode/thriftgo" + + kargs "github.com/cloudwego/kitex/tool/cmd/kitex/args" + "github.com/cloudwego/kitex/tool/cmd/kitex/code" + "github.com/cloudwego/thriftgo/plugin" + "github.com/cloudwego/thriftgo/sdk" +) + +type KitexThriftgoPlugin struct { + KitexParams []string + ThriftgoParams []string + Pwd string +} + +func (k *KitexThriftgoPlugin) Invoke(req *plugin.Request) (res *plugin.Response) { + return thriftgo.HandleRequest(req) +} + +func (k *KitexThriftgoPlugin) GetName() string { + return "kitex" +} + +func (k *KitexThriftgoPlugin) GetPluginParameters() []string { + return k.KitexParams +} + +func (k *KitexThriftgoPlugin) GetThriftgoParameters() []string { + return k.ThriftgoParams +} + +// RunKitexThriftgoGenFromArgs kitex tool to generate thrift as sdk +func RunKitexThriftgoGenFromArgs(wd string, plugins []plugin.SDKPlugin, argsParsed *kargs.Arguments) (error, code.ToolExitCode) { + if !argsParsed.IsThrift() { + return fmt.Errorf("only support thrift idl"), code.ToolArgsError + } + p, err := GetKitexThriftgoPluginFromArgs(wd, argsParsed) + if err != nil { + if code.IsInterrupted(err) { + return nil, code.ToolInterrupted + } + if code.IsVersionCheckFailed(err) { + return err, code.ToolVersionCheckFailed + } + return err, code.ToolArgsError + } + + plugins = append([]plugin.SDKPlugin{p}, plugins...) + + err = sdk.RunThriftgoAsSDK(wd, plugins, p.GetThriftgoParameters()...) + if err == nil { + return nil, code.ToolSuccess + } + return err, code.ToolExecuteFailed +} + +// GetKitexThriftgoPlugin rawKiteXArgs is -xxx -xxx not start with kitex, for outside user +func GetKitexThriftgoPlugin(pwd string, kitexOsArgs []string) (*KitexThriftgoPlugin, error) { + // run as kitex + var args kargs.Arguments + + err := args.ParseArgs(kitex.Version, pwd, kitexOsArgs) + if err != nil { + return nil, err + } + + return GetKitexThriftgoPluginFromArgs(pwd, &args) +} + +// GetKitexThriftgoPluginFromArgs +func GetKitexThriftgoPluginFromArgs(pwd string, argsParsed *kargs.Arguments) (*KitexThriftgoPlugin, error) { + out := new(bytes.Buffer) + cmd, err := argsParsed.BuildCmd(out) + if err != nil { + return nil, err + } + + thriftgoParams, kitexParams, err := ParseKitexThriftgoCmd(cmd) + if err != nil { + return nil, err + } + + return &KitexThriftgoPlugin{ + KitexParams: kitexParams, + ThriftgoParams: thriftgoParams, + Pwd: pwd, + }, nil +} diff --git a/tool/internal_pkg/thriftgo/util.go b/tool/internal_pkg/thriftgo/util.go new file mode 100644 index 0000000000..d0a2b29eb9 --- /dev/null +++ b/tool/internal_pkg/thriftgo/util.go @@ -0,0 +1,34 @@ +package thriftgo + +import ( + "fmt" + "os/exec" + "strings" +) + +// ParseKitexCmd parse KitexCmd to ThriftgoParams and KitexParams +func ParseKitexThriftgoCmd(cmd *exec.Cmd) (thriftgoParams, kitexParams []string, err error) { + cmdArgs := cmd.Args + // thriftgo -r -o kitex_gen -g go:xxx -p kitex=xxxx -p otherplugin xxx.thrift + // ignore first argument, and remove -p kitex=xxxx + + thriftgoParams = []string{} + kitexParams = []string{} + if len(cmdArgs) < 1 { + return nil, nil, fmt.Errorf("cmd args too short: %s", cmdArgs) + } + + for i := 1; i < len(cmdArgs); i++ { + arg := cmdArgs[i] + if arg == "-p" && i+1 < len(cmdArgs) { + pluginArgs := cmdArgs[i+1] + if strings.HasPrefix(pluginArgs, "kitex") { + kitexParams = strings.Split(pluginArgs, ",") + i++ + continue + } + } + thriftgoParams = append(thriftgoParams, arg) + } + return thriftgoParams, kitexParams, nil +} diff --git a/tool/internal_pkg/util/color.go b/tool/internal_pkg/util/color.go new file mode 100644 index 0000000000..2e66b41581 --- /dev/null +++ b/tool/internal_pkg/util/color.go @@ -0,0 +1,50 @@ +package util + +// Define common ANSI escape code constants +const ( + Reset = "\x1b[0m" // Reset all attributes + // Regular Colors (Foreground) + FgBlack = "\x1b[30m" + FgRed = "\x1b[31m" + FgGreen = "\x1b[32m" + FgYellow = "\x1b[33m" + FgBlue = "\x1b[34m" + FgMagenta = "\x1b[35m" + FgCyan = "\x1b[36m" + FgWhite = "\x1b[37m" + // Bright/High-Intensity Colors (Foreground) + FgBrightBlack = "\x1b[90m" // Often called Gray + FgBrightRed = "\x1b[91m" + FgBrightGreen = "\x1b[92m" + FgBrightYellow = "\x1b[93m" + FgBrightBlue = "\x1b[94m" + FgBrightMagenta = "\x1b[95m" + FgBrightCyan = "\x1b[96m" + FgBrightWhite = "\x1b[97m" + // Background Colors + BgBlack = "\x1b[40m" + BgRed = "\x1b[41m" + BgGreen = "\x1b[42m" + BgYellow = "\x1b[43m" + BgBlue = "\x1b[44m" + BgMagenta = "\x1b[45m" + BgCyan = "\x1b[46m" + BgWhite = "\x1b[47m" + // Bright/High-Intensity Background Colors + BgBrightBlack = "\x1b[100m" + BgBrightRed = "\x1b[101m" + BgBrightGreen = "\x1b[102m" + BgBrightYellow = "\x1b[103m" + BgBrightBlue = "\x1b[104m" + BgBrightMagenta = "\x1b[105m" + BgBrightCyan = "\x1b[106m" + BgBrightWhite = "\x1b[107m" + // Styles + Bold = "\x1b[1m" + Dim = "\x1b[2m" // Dim/Faint + Italic = "\x1b[3m" // Italic (not supported by all terminals) + Underline = "\x1b[4m" // Underline + Blink = "\x1b[5m" // Blink (not supported by all terminals) + Reverse = "\x1b[7m" // Reverse foreground and background colors + Hidden = "\x1b[8m" // Hidden (useful for password input) +) diff --git a/tool/internal_pkg/util/util.go b/tool/internal_pkg/util/util.go index 6ecca8b9b6..a2a2c706c8 100644 --- a/tool/internal_pkg/util/util.go +++ b/tool/internal_pkg/util/util.go @@ -158,10 +158,20 @@ func SearchGoMod(cwd string) (moduleName, path string, found bool) { return } -func RunGitCommand(gitLink string) (string, string, error) { +func IsGitURL(s string) bool { + prefixes := []string{"git@", "http://", "https://"} + for _, prefix := range prefixes { + if strings.HasPrefix(s, prefix) { + return true + } + } + return false +} + +func RunGitCommand(gitLink string) (string, error) { homeDir, err := os.UserHomeDir() if err != nil { - return "", "Failed to get home dir", err + return "", fmt.Errorf("failed to get home dir: %s", err.Error()) } cachePath := JoinPath(homeDir, ".kitex", "cache") @@ -194,21 +204,25 @@ func RunGitCommand(gitLink string) (string, string, error) { if err != nil && !os.IsExist(err) { err = os.MkdirAll(gitPath, os.ModePerm) if err != nil { - return "", "Failed to create cache directory,please check your permission for ~/.kitex/cache", err + return "", fmt.Errorf("failed to create cache directory,please check your permission for ~/.kitex/cache: %s", err.Error()) } cmdClone := exec.Command("git", "clone", pullLink, ".") cmdClone.Dir = gitPath out, gitErr := cmdClone.CombinedOutput() if gitErr != nil { - return "", string(out), gitErr + return "", fmt.Errorf("%s: %s", gitErr.Error(), string(out)) } if branch != "" { cmdCheckout := exec.Command("git", "checkout", branch) cmdCheckout.Dir = gitPath out, gitErr = cmdCheckout.CombinedOutput() - return gitPath, string(out), gitErr + if gitErr != nil { + return "", fmt.Errorf("%s: %s", gitErr.Error(), string(out)) + } else { + return gitPath, nil + } } else { - return gitPath, "", nil + return gitPath, nil } } @@ -216,10 +230,10 @@ func RunGitCommand(gitLink string) (string, string, error) { cmdPull.Dir = gitPath out, gitErr := cmdPull.CombinedOutput() if gitErr != nil { - return "", string(out), gitErr + return "", fmt.Errorf("%s: %s", gitErr.Error(), string(out)) } - return gitPath, "", nil + return gitPath, nil } // CombineOutputPath read the output and path variables and render them into the final path