diff --git a/adapter/github/protoc-gen-go/protoc-gen-go.go b/adapter/github/protoc-gen-go/protoc-gen-go.go new file mode 100644 index 0000000..1a1e308 --- /dev/null +++ b/adapter/github/protoc-gen-go/protoc-gen-go.go @@ -0,0 +1,47 @@ +package protoc_gen_go + +import ( + "context" + "fmt" + "github.com/shiv3/protoenv/adapter/github" + "path/filepath" + "strings" +) + +var ( + owner = "protocolbuffers" + repo = "protobuf-go" +) + +func GetprotocGenGoRepoGoVersions(ctx context.Context) ([]string, error) { + return github.GetVersions(ctx, owner, repo) +} + +func GetprotocGenGoRepoGoGetReleaseAssetURL(ctx context.Context, tag string, os, arch string) (string, error) { + assets, err := github.GetReleaseAssets(ctx, owner, repo, tag) + if err != nil { + return "", err + } + for _, asset := range assets { + url := asset.GetBrowserDownloadURL() + filename := filepath.Base(url) + archString := Get(os, arch) + if strings.Contains(filename, "protoc") && strings.Contains(filename, archString) { + return url, nil + } + } + return "", fmt.Errorf("Unknown version") +} + +func Get(os, arch string) string { + switch fmt.Sprintf("%s/%s", os, arch) { + case "darwin/amd64": + return "darwin.amd64" + case "linux/386": + return "linux.386" + case "linux/amd64": + return "linux.amd64" + default: + return "Unknown" + } +} diff --git a/adapter/github/protobuf.go b/adapter/github/protoc/protobuf.go similarity index 62% rename from adapter/github/protobuf.go rename to adapter/github/protoc/protobuf.go index b70ff23..191ba74 100644 --- a/adapter/github/protobuf.go +++ b/adapter/github/protoc/protobuf.go @@ -1,8 +1,9 @@ -package github +package protoc import ( "context" "fmt" + "github.com/shiv3/protoenv/adapter/github" "path/filepath" "strings" ) @@ -13,27 +14,27 @@ var ( ) func GetProtobufVersions(ctx context.Context) ([]string, error) { - return GetVersions(ctx, owner, repo) + return github.GetVersions(ctx, owner, repo) } -func GetProtobufGetReleaseAssetURL(ctx context.Context, tag string,os ,arch string) (string, error) { - assets, err := GetReleaseAssets(ctx, owner, repo, tag) +func GetProtobufGetReleaseAssetURL(ctx context.Context, tag string, os, arch string) (string, error) { + assets, err := github.GetReleaseAssets(ctx, owner, repo, tag) if err != nil { return "", err } for _, asset := range assets { url := asset.GetBrowserDownloadURL() filename := filepath.Base(url) - archString := Get(os,arch) - if strings.Contains(filename,"protoc") && strings.Contains(filename,archString) { + archString := Get(os, arch) + if strings.Contains(filename, "protoc") && strings.Contains(filename, archString) { return url, nil } } return "", fmt.Errorf("Unknown version") } -func Get(os,arch string) string { - switch fmt.Sprintf("%s/%s",os,arch) { +func Get(os, arch string) string { + switch fmt.Sprintf("%s/%s", os, arch) { case "darwin/386": return "osx-x86_64" case "darwin/amd64": diff --git a/cmd/commands/plugins/commands/list.go b/cmd/commands/plugins/commands/list.go new file mode 100644 index 0000000..ac3e2e7 --- /dev/null +++ b/cmd/commands/plugins/commands/list.go @@ -0,0 +1,31 @@ +package commands + +import ( + "fmt" + "github.com/spf13/cobra" +) + +type List struct { + plugins []string +} + +func NewList(parentCmd *cobra.Command, plugins []string) List { + list := List{ + plugins: plugins, + } + cmd := &cobra.Command{ + Use: "list", + Short: fmt.Sprintf("list plugins"), + Long: fmt.Sprintf(`list plugins`), + RunE: list.RunE, + } + parentCmd.AddCommand(cmd) + return list +} + +func (i *List) RunE(cmd *cobra.Command, args []string) error { + for _, plugin := range i.plugins { + fmt.Printf("%s\n", plugin) + } + return nil +} diff --git a/cmd/commands/plugins/commands/protoc-gen-go/commands/global.go b/cmd/commands/plugins/commands/protoc-gen-go/commands/global.go new file mode 100644 index 0000000..54b72bd --- /dev/null +++ b/cmd/commands/plugins/commands/protoc-gen-go/commands/global.go @@ -0,0 +1,62 @@ +package commands + +import ( + "fmt" + "github.com/spf13/cobra" + "os" + "path/filepath" +) + +type Global struct { + InstallDirectoryPath string + ShowVersionFormatSimple string + TargetBinaryFileName string +} + +func NewGlobal(parentCmd *cobra.Command, installDirectoryPath string, ShowVersionFormatSimple string, TargetBinaryFileName string) Global { + global := Global{ + InstallDirectoryPath: installDirectoryPath, + ShowVersionFormatSimple: ShowVersionFormatSimple, + TargetBinaryFileName: TargetBinaryFileName, + } + cmd := &cobra.Command{ + Use: "global", + Short: fmt.Sprintf("Set or show the global %s version", TargetBinaryFileName), + Long: fmt.Sprintf(`Set or show the global %s version`, TargetBinaryFileName), + RunE: global.RunE, + } + parentCmd.AddCommand(cmd) + return global +} + +func (i *Global) RunE(cmd *cobra.Command, args []string) error { + if len(args) > 0 { + version := args[0] + if err := setVersion(getVersionsPath(i.InstallDirectoryPath), getGlobalVersionFilePath(i.InstallDirectoryPath), version); err != nil { + return err + } + return setShims(GetShimsFileDir(i.InstallDirectoryPath), i.TargetBinaryFileName, i.InstallDirectoryPath, version) + } + v, err := getVersion(getGlobalVersionFilePath(i.InstallDirectoryPath)) + if err != nil { + return err + } + fmt.Printf(i.ShowVersionFormatSimple, v) + return nil +} + +func setShims(shimsDir, targetFileName, installPath, version string) error { + if _, err := os.Stat(filepath.Join(shimsDir)); os.IsNotExist(err) { + err = os.MkdirAll(shimsDir, os.ModePerm) + if err != nil { + return err + } + } + shimsPath := filepath.Join(shimsDir, targetFileName) + if _, err := os.Lstat(shimsPath); err == nil { + if err := os.Remove(shimsPath); err != nil { + return err + } + } + return nil +} diff --git a/cmd/commands/plugins/commands/protoc-gen-go/commands/init_cmd.go b/cmd/commands/plugins/commands/protoc-gen-go/commands/init_cmd.go new file mode 100644 index 0000000..0aef6d6 --- /dev/null +++ b/cmd/commands/plugins/commands/protoc-gen-go/commands/init_cmd.go @@ -0,0 +1,35 @@ +package commands + +import ( + "fmt" + "github.com/spf13/cobra" + "os" +) + +type Init struct { + InstallDirectoryPath string + TargetBinaryFileName string +} + +func NewInit(parentCmd *cobra.Command, installDirectoryPath string, TargetBinaryFileName string) Init { + init := Init{ + InstallDirectoryPath: installDirectoryPath, + TargetBinaryFileName: TargetBinaryFileName, + } + cmd := &cobra.Command{ + Use: "init", + Short: fmt.Sprintf("Set or show the global Go version", TargetBinaryFileName), + Long: fmt.Sprintf(`Set or show the global Go version`, TargetBinaryFileName), + RunE: init.RunE, + } + parentCmd.AddCommand(cmd) + return init +} + +func (i *Init) RunE(cmd *cobra.Command, args []string) error { + if _, err := os.Stat(i.InstallDirectoryPath); os.IsNotExist(err) { + return os.Mkdir(i.InstallDirectoryPath, os.ModePerm) + } + fmt.Printf(`export PATH=$PATH:%s`, GetShimsFileDir(i.InstallDirectoryPath)) + return nil +} diff --git a/cmd/commands/plugins/commands/protoc-gen-go/commands/install.go b/cmd/commands/plugins/commands/protoc-gen-go/commands/install.go new file mode 100644 index 0000000..0e1528b --- /dev/null +++ b/cmd/commands/plugins/commands/protoc-gen-go/commands/install.go @@ -0,0 +1,91 @@ +package commands + +import ( + "context" + "errors" + "fmt" + protoc_gen_go "github.com/shiv3/protoenv/adapter/github/protoc-gen-go" + "github.com/shiv3/protoenv/adapter/installer" + "os" + "path/filepath" + "runtime" + + "github.com/spf13/cobra" +) + +type Install struct { + InstallDirectoryPath string + ShowVersionFormatSimple string + TargetBinaryFileName string +} + +func NewInstall(parentCmd *cobra.Command, installDirectoryPath string, ShowVersionFormatSimple string, TargetBinaryFileName string) Install { + install := Install{ + InstallDirectoryPath: installDirectoryPath, + ShowVersionFormatSimple: ShowVersionFormatSimple, + TargetBinaryFileName: TargetBinaryFileName, + } + cmd := &cobra.Command{ + Use: "install (version)", + Short: "install specified version", + Long: `install specified version`, + RunE: install.RunE, + } + cmd.PersistentFlags().BoolP("list", "l", false, "show install list flag") + parentCmd.AddCommand(cmd) + return install +} + +func (c Install) RunE(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + //c.installOptions.ShowVersionList { + if list, err := cmd.PersistentFlags().GetBool("list"); err == nil && list { + if err := c.showVersion(ctx); err != nil { + return err + } + return nil + } + + if len(args) >= 1 { + err := c.installVersion(ctx, args[0]) + if err != nil { + return err + } + return nil + } + + return errors.New("requires a installing version or some flags") +} + +func (i Install) showVersion(ctx context.Context) error { + versions, err := protoc_gen_go.GetprotocGenGoRepoGoVersions(ctx) + if err != nil { + return err + } + for _, version := range versions { + fmt.Printf(i.ShowVersionFormatSimple, version) + } + return nil +} + +func (i Install) installVersion(ctx context.Context, version string) error { + url, err := protoc_gen_go.GetprotocGenGoRepoGoGetReleaseAssetURL(ctx, version, runtime.GOOS, runtime.GOARCH) + if err != nil { + return err + } + targetDirPath := filepath.Join(i.InstallDirectoryPath, "versions", version) + err = os.MkdirAll(targetDirPath, os.ModePerm) + if err != nil { + return err + } + filePath, err := installer.GetTargetFile(url, i.TargetBinaryFileName, targetDirPath) + if err != nil { + return err + } + fmt.Printf("installed %s %s\n", i.TargetBinaryFileName, filePath) + if err := setVersion(getVersionsPath(i.InstallDirectoryPath), getGlobalVersionFilePath(i.InstallDirectoryPath), version); err != nil { + return err + } + return nil +} diff --git a/cmd/commands/plugins/commands/protoc-gen-go/commands/local.go b/cmd/commands/plugins/commands/protoc-gen-go/commands/local.go new file mode 100644 index 0000000..6cde4d7 --- /dev/null +++ b/cmd/commands/plugins/commands/protoc-gen-go/commands/local.go @@ -0,0 +1,46 @@ +package commands + +import ( + "fmt" + "github.com/spf13/cobra" + "os" +) + +type Local struct { + InstallDirectoryPath string + ShowVersionFormatSimple string + TargetBinaryFileName string +} + +func NewLocal(parentCmd *cobra.Command, installDirectoryPath string, ShowVersionFormatSimple string, TargetBinaryFileName string) Local { + local := Local{ + InstallDirectoryPath: installDirectoryPath, + ShowVersionFormatSimple: ShowVersionFormatSimple, + TargetBinaryFileName: TargetBinaryFileName, + } + cmd := &cobra.Command{ + Use: "local", + Short: fmt.Sprintf("Set or show the local %s version", TargetBinaryFileName), + Long: fmt.Sprintf(`Set or show the local %s version`, TargetBinaryFileName), + RunE: local.RunE, + } + parentCmd.AddCommand(cmd) + return local +} + +func (i *Local) RunE(cmd *cobra.Command, args []string) error { + currentDirectory, err := os.Getwd() + if err != nil { + return err + } + localVersionFilePath := getLocalVersionFilePath(currentDirectory) + if len(args) > 0 { + return setVersion(getVersionsPath(i.InstallDirectoryPath), localVersionFilePath, args[0]) + } + if v, err := getVersion(localVersionFilePath); err != nil { + return err + } else { + fmt.Printf(i.ShowVersionFormatSimple, v) + } + return nil +} diff --git a/cmd/commands/plugins/commands/protoc-gen-go/commands/version.go b/cmd/commands/plugins/commands/protoc-gen-go/commands/version.go new file mode 100644 index 0000000..7d0387f --- /dev/null +++ b/cmd/commands/plugins/commands/protoc-gen-go/commands/version.go @@ -0,0 +1,76 @@ +package commands + +import ( + "fmt" + "github.com/spf13/cobra" + "io/ioutil" + "os" + "path/filepath" +) + +var versionFileName = ".protoc-version" + +type Version struct { + GlobalVersionFilePath string + ShowVersionFormatSimple string + TargetBinaryFileName string +} + +func NewVersion(parentCmd *cobra.Command, installDirectoryPath string, ShowVersionFormatSimple string, TargetBinaryFileName string) Version { + version := Version{ + GlobalVersionFilePath: getGlobalVersionFilePath(installDirectoryPath), + ShowVersionFormatSimple: ShowVersionFormatSimple, + TargetBinaryFileName: TargetBinaryFileName, + } + cmd := &cobra.Command{ + Use: "version", + Short: fmt.Sprintf("Show the current %s version and its origin", TargetBinaryFileName), + Long: fmt.Sprintf(`Show the current %s version and its origin`, TargetBinaryFileName), + RunE: version.RunE, + } + parentCmd.AddCommand(cmd) + return version +} + +func getLocalVersionFilePath(currentDirectory string) string { + return filepath.Join(currentDirectory, versionFileName) +} +func getGlobalVersionFilePath(installDirectoryPath string) string { + return filepath.Join(installDirectoryPath, versionFileName) +} + +func (i *Version) RunE(cmd *cobra.Command, args []string) error { + v, err := getVersion(i.GlobalVersionFilePath) + if err != nil { + return err + } + fmt.Printf(i.ShowVersionFormatSimple, v) + return nil +} + +func getVersion(versionFilePath string) (string, error) { + if _, err := os.Stat(versionFilePath); os.IsNotExist(err) { + return "", fmt.Errorf("version file not found: %w", err) + } + b, err := os.ReadFile(versionFilePath) + if err != nil { + return "", err + } + return string(b), nil +} + +func setVersion(versionsPath, versionFilePath string, version string) error { + if _, err := os.Stat(filepath.Join(versionsPath, version)); os.IsNotExist(err) { + return fmt.Errorf("goenv: version '%s' not installed", version) + } + err := ioutil.WriteFile(versionFilePath, []byte(version), 0644) + if err != nil { + return err + } + fmt.Printf("set global version: %s\n", version) + return nil +} + +func GetShimsFileDir(installFilePath string) string { + return filepath.Join(installFilePath, "shims") +} diff --git a/cmd/commands/plugins/commands/protoc-gen-go/commands/versions.go b/cmd/commands/plugins/commands/protoc-gen-go/commands/versions.go new file mode 100644 index 0000000..5914f6c --- /dev/null +++ b/cmd/commands/plugins/commands/protoc-gen-go/commands/versions.go @@ -0,0 +1,52 @@ +package commands + +import ( + "fmt" + "github.com/spf13/cobra" + "os" + "path/filepath" +) + +type Versions struct { + InstallDirectoryPath string + ShowVersionFormatSimple string + TargetBinaryFileName string +} + +func NewVersions(parentCmd *cobra.Command, installDirectoryPath string, ShowVersionFormatSimple string, TargetBinaryFileName string) Versions { + versions := Versions{ + InstallDirectoryPath: getVersionsPath(installDirectoryPath), + ShowVersionFormatSimple: ShowVersionFormatSimple, + TargetBinaryFileName: TargetBinaryFileName, + } + cmd := &cobra.Command{ + Use: "versions", + Short: fmt.Sprintf("List all %s versions available to protoenv", TargetBinaryFileName), + Long: fmt.Sprintf(`List all %s versions available to protoenv`, TargetBinaryFileName), + RunE: versions.RunE, + } + parentCmd.AddCommand(cmd) + return versions +} + +func getVersionsPath(installDirectoryPath string) string { + return filepath.Join(installDirectoryPath, "versions") +} + +func (i *Versions) RunE(cmd *cobra.Command, args []string) error { + if _, err := os.Stat(i.InstallDirectoryPath); os.IsNotExist(err) { + return fmt.Errorf("versions dir not found: %w", err) + } + dirs, err := os.ReadDir(i.InstallDirectoryPath) + if err != nil { + return err + } + + // only show local directories + for _, dir := range dirs { + if dir.IsDir() { + fmt.Printf(i.ShowVersionFormatSimple, dir.Name()) + } + } + return nil +} diff --git a/cmd/commands/plugins/commands/protoc-gen-go/protoc_gen_go_cmd.go b/cmd/commands/plugins/commands/protoc-gen-go/protoc_gen_go_cmd.go new file mode 100644 index 0000000..84d1cd2 --- /dev/null +++ b/cmd/commands/plugins/commands/protoc-gen-go/protoc_gen_go_cmd.go @@ -0,0 +1,50 @@ +package protoc_gen_go + +import ( + "fmt" + commands3 "github.com/shiv3/protoenv/cmd/commands/plugins/commands/protoc-gen-go/commands" + "github.com/spf13/cobra" +) + +type Cmd struct { + ProtocGenGoCommands ProtocGenGoCommands + CmdOptions CmdOptions +} + +type ProtocGenGoCommands struct { + init commands3.Init + install commands3.Install + local commands3.Local + global commands3.Global + version commands3.Version + versions commands3.Versions +} + +type CmdOptions struct { + ShowVersionFormatSimple string + TargetBinaryFileName string +} + +func NewProtocGenGo(cmdAlias string, parentCmd *cobra.Command) *Cmd { + showVersionFormatSimple := "%s\n" + targetBinaryFileName := "protoc" + installPath := "" + + cmd := &cobra.Command{ + Use: cmdAlias, + Short: fmt.Sprintf("%s version manager", targetBinaryFileName), + Long: fmt.Sprintf(`%s version manager`, targetBinaryFileName), + } + + parentCmd.AddCommand(cmd) + return &Cmd{ + ProtocGenGoCommands: ProtocGenGoCommands{ + init: commands3.NewInit(cmd, installPath, targetBinaryFileName), + install: commands3.NewInstall(cmd, installPath, showVersionFormatSimple, targetBinaryFileName), + local: commands3.NewLocal(cmd, installPath, showVersionFormatSimple, targetBinaryFileName), + global: commands3.NewGlobal(cmd, installPath, showVersionFormatSimple, targetBinaryFileName), + version: commands3.NewVersion(cmd, installPath, showVersionFormatSimple, targetBinaryFileName), + versions: commands3.NewVersions(cmd, installPath, showVersionFormatSimple, targetBinaryFileName), + }, + } +} diff --git a/cmd/commands/plugins/plugins_cmd.go b/cmd/commands/plugins/plugins_cmd.go new file mode 100644 index 0000000..5da7fd3 --- /dev/null +++ b/cmd/commands/plugins/plugins_cmd.go @@ -0,0 +1,46 @@ +package protoc + +import ( + "fmt" + "github.com/shiv3/protoenv/cmd/commands/plugins/commands" + protoc_gen_go "github.com/shiv3/protoenv/cmd/commands/plugins/commands/protoc-gen-go" + "github.com/spf13/cobra" +) + +type Cmd struct { + PluginsCommands PluginsCommands + CmdOptions CmdOptions +} + +type PluginsCommands struct { + list commands.List +} + +type CmdOptions struct { + ShowVersionFormatSimple string + TargetBinaryFileName string +} + +func NewPlugins(parentCmd *cobra.Command) *Cmd { + cmd := &cobra.Command{ + Use: "plugins", + Short: fmt.Sprintf("plugins version manager"), + Long: fmt.Sprintf(`plugins version manager`), + } + + plugins := map[string]interface{}{ + "protoc-gen-go": protoc_gen_go.NewProtocGenGo("protoc-gen-go", cmd), + } + + var pluginsAliases []string + for k, _ := range plugins { + pluginsAliases = append(pluginsAliases, k) + } + + parentCmd.AddCommand(cmd) + return &Cmd{ + PluginsCommands: PluginsCommands{ + list: commands.NewList(cmd, pluginsAliases), + }, + } +} diff --git a/cmd/commands/protoc/commands/install.go b/cmd/commands/protoc/commands/install.go index 664acfd..b64f314 100644 --- a/cmd/commands/protoc/commands/install.go +++ b/cmd/commands/protoc/commands/install.go @@ -4,13 +4,12 @@ import ( "context" "errors" "fmt" + "github.com/shiv3/protoenv/adapter/github/protoc" "github.com/shiv3/protoenv/adapter/installer" "os" "path/filepath" "runtime" - "github.com/shiv3/protoenv/adapter/github" - "github.com/spf13/cobra" ) @@ -60,7 +59,7 @@ func (c Install) RunE(cmd *cobra.Command, args []string) error { } func (i Install) showVersion(ctx context.Context) error { - versions, err := github.GetProtobufVersions(ctx) + versions, err := protoc.GetProtobufVersions(ctx) if err != nil { return err } @@ -71,7 +70,7 @@ func (i Install) showVersion(ctx context.Context) error { } func (i Install) installVersion(ctx context.Context, version string) error { - url, err := github.GetProtobufGetReleaseAssetURL(ctx, version, runtime.GOOS, runtime.GOARCH) + url, err := protoc.GetProtobufGetReleaseAssetURL(ctx, version, runtime.GOOS, runtime.GOARCH) if err != nil { return err } diff --git a/cmd/root_cmd.go b/cmd/root_cmd.go index 1c3b10b..5a857b1 100644 --- a/cmd/root_cmd.go +++ b/cmd/root_cmd.go @@ -2,6 +2,7 @@ package cmd import ( initcmd "github.com/shiv3/protoenv/cmd/commands/init" + protoc "github.com/shiv3/protoenv/cmd/commands/plugins" protoc2 "github.com/shiv3/protoenv/cmd/commands/protoc" "github.com/shiv3/protoenv/config" ) @@ -10,4 +11,5 @@ func init() { cfg := config.InitConfig() initcmd.NewInit(rootCmd, cfg) protoc2.NewProtoc(rootCmd, cfg.Protoc) + protoc.NewPlugins(rootCmd) } diff --git a/config/config.go b/config/config.go index 5ca70b3..75268fb 100644 --- a/config/config.go +++ b/config/config.go @@ -6,6 +6,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "os" + "path" ) var configFileName = "config.yaml" @@ -55,7 +56,7 @@ func InitConfig() Config { Debug: false, RootDirectoryPath: getDefaultConfigPath(), Protoc: ProtocConfig{ - InstallPath: fmt.Sprintf("%s/%s", getDefaultConfigPath(), "protoc"), + InstallPath: path.Join(getDefaultConfigPath(), "protoc"), }, } }