Feature: Add goctl env (#1557)
parent
842656aa90
commit
daa98f5a27
@ -0,0 +1,112 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/env"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/protoc"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/protocgengo"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/protocgengogrpc"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/util/console"
|
||||
)
|
||||
|
||||
type bin struct {
|
||||
name string
|
||||
exists bool
|
||||
get func(cacheDir string) (string, error)
|
||||
}
|
||||
|
||||
var bins = []bin{
|
||||
{
|
||||
name: "protoc",
|
||||
exists: protoc.Exists(),
|
||||
get: protoc.Install,
|
||||
},
|
||||
{
|
||||
name: "protoc-gen-go",
|
||||
exists: protocgengo.Exists(),
|
||||
get: protocgengo.Install,
|
||||
},
|
||||
{
|
||||
name: "protoc-gen-go-grpc",
|
||||
exists: protocgengogrpc.Exists(),
|
||||
get: protocgengogrpc.Install,
|
||||
},
|
||||
}
|
||||
|
||||
func Check(ctx *cli.Context) error {
|
||||
install := ctx.Bool("install")
|
||||
force := ctx.Bool("force")
|
||||
return check(install, force)
|
||||
}
|
||||
|
||||
func check(install, force bool) error {
|
||||
var pending = true
|
||||
console.Info("[goctl-env]: preparing to check env")
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
console.Error("%+v", p)
|
||||
return
|
||||
}
|
||||
if pending {
|
||||
console.Success("\n[goctl-env]: congratulations! your goctl environment is ready!")
|
||||
} else {
|
||||
console.Error(`
|
||||
[goctl-env]: check env finish, some dependencies is not found in PATH, you can execute
|
||||
command 'goctl env check --install' or 'goctl env install' to install it, for details,
|
||||
please see 'goctl env check --help' or 'goctl env install --help'`)
|
||||
}
|
||||
}()
|
||||
for _, e := range bins {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
console.Info("")
|
||||
console.Info("[goctl-env]: looking up %q", e.name)
|
||||
if e.exists {
|
||||
console.Success("[goctl-env]: %q is installed", e.name)
|
||||
continue
|
||||
}
|
||||
console.Warning("[goctl-env]: %q is not found in PATH", e.name)
|
||||
if install {
|
||||
install := func() {
|
||||
console.Info("[goctl-env]: preparing to install %q", e.name)
|
||||
path, err := e.get(env.Get(env.GoctlCache))
|
||||
if err != nil {
|
||||
console.Error("[goctl-env]: an error interrupted the installation: %+v", err)
|
||||
pending = false
|
||||
} else {
|
||||
console.Success("[goctl-env]: %q is already installed in %q", e.name, path)
|
||||
}
|
||||
}
|
||||
if force {
|
||||
install()
|
||||
continue
|
||||
}
|
||||
console.Info("[goctl-env]: do you want to install %q [y: YES, n: No]", e.name)
|
||||
for {
|
||||
var in string
|
||||
fmt.Scanln(&in)
|
||||
var brk bool
|
||||
switch {
|
||||
case strings.EqualFold(in, "y"):
|
||||
install()
|
||||
brk = true
|
||||
case strings.EqualFold(in, "n"):
|
||||
pending = false
|
||||
console.Info("[goctl-env]: %q installation is ignored", e.name)
|
||||
brk = true
|
||||
default:
|
||||
console.Error("[goctl-env]: invalid input, input 'y' for yes, 'n' for no")
|
||||
}
|
||||
if brk {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pending = false
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/env"
|
||||
)
|
||||
|
||||
func Action(c *cli.Context) error {
|
||||
write := c.StringSlice("write")
|
||||
if len(write) > 0 {
|
||||
return env.WriteEnv(write)
|
||||
}
|
||||
fmt.Println(env.Print())
|
||||
return nil
|
||||
}
|
@ -0,0 +1,208 @@
|
||||
package sortedmap
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/zeromicro/go-zero/tools/goctl/util/stringx"
|
||||
)
|
||||
|
||||
var ErrInvalidKVExpression = errors.New(`invalid key-value expression`)
|
||||
var ErrInvalidKVS = errors.New("the length of kv must be a even number")
|
||||
|
||||
type KV []interface{}
|
||||
|
||||
type SortedMap struct {
|
||||
kv *list.List
|
||||
keys map[interface{}]*list.Element
|
||||
}
|
||||
|
||||
func New() *SortedMap {
|
||||
return &SortedMap{
|
||||
kv: list.New(),
|
||||
keys: make(map[interface{}]*list.Element),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *SortedMap) SetExpression(expression string) (key interface{}, value interface{}, err error) {
|
||||
idx := strings.Index(expression, "=")
|
||||
if idx == -1 {
|
||||
return "", "", ErrInvalidKVExpression
|
||||
}
|
||||
key = expression[:idx]
|
||||
if len(expression) == idx {
|
||||
value = ""
|
||||
} else {
|
||||
value = expression[idx+1:]
|
||||
}
|
||||
if keys, ok := key.(string); ok && stringx.ContainsWhiteSpace(keys) {
|
||||
return "", "", ErrInvalidKVExpression
|
||||
}
|
||||
if values, ok := value.(string); ok && stringx.ContainsWhiteSpace(values) {
|
||||
return "", "", ErrInvalidKVExpression
|
||||
}
|
||||
if len(key.(string)) == 0 {
|
||||
return "", "", ErrInvalidKVExpression
|
||||
}
|
||||
|
||||
m.SetKV(key, value)
|
||||
return
|
||||
}
|
||||
|
||||
func (m *SortedMap) SetKV(key, value interface{}) {
|
||||
e, ok := m.keys[key]
|
||||
if !ok {
|
||||
e = m.kv.PushBack(KV{
|
||||
key, value,
|
||||
})
|
||||
} else {
|
||||
e.Value.(KV)[1] = value
|
||||
}
|
||||
m.keys[key] = e
|
||||
}
|
||||
|
||||
func (m *SortedMap) Set(kv KV) error {
|
||||
if len(kv) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(kv)%2 != 0 {
|
||||
return ErrInvalidKVS
|
||||
}
|
||||
for idx := 0; idx < len(kv); idx += 2 {
|
||||
m.SetKV(kv[idx], kv[idx+1])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SortedMap) Get(key interface{}) (interface{}, bool) {
|
||||
e, ok := m.keys[key]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return e.Value.(KV)[1], true
|
||||
}
|
||||
|
||||
func (m *SortedMap) GetOr(key interface{}, dft interface{}) interface{} {
|
||||
e, ok := m.keys[key]
|
||||
if !ok {
|
||||
return dft
|
||||
}
|
||||
return e.Value.(KV)[1]
|
||||
}
|
||||
|
||||
func (m *SortedMap) GetString(key interface{}) (string, bool) {
|
||||
value, ok := m.Get(key)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
vs, ok := value.(string)
|
||||
return vs, ok
|
||||
}
|
||||
|
||||
func (m *SortedMap) GetStringOr(key interface{}, dft string) string {
|
||||
value, ok := m.GetString(key)
|
||||
if !ok {
|
||||
return dft
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func (m *SortedMap) HasKey(key interface{}) bool {
|
||||
_, ok := m.keys[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (m *SortedMap) HasValue(value interface{}) bool {
|
||||
var contains bool
|
||||
m.RangeIf(func(key, v interface{}) bool {
|
||||
if value == v {
|
||||
contains = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return contains
|
||||
}
|
||||
|
||||
func (m *SortedMap) Keys() []interface{} {
|
||||
keys := make([]interface{}, 0)
|
||||
next := m.kv.Front()
|
||||
for next != nil {
|
||||
keys = append(keys, next.Value.(KV)[0])
|
||||
next = next.Next()
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (m *SortedMap) Values() []interface{} {
|
||||
keys := m.Keys()
|
||||
values := make([]interface{}, len(keys))
|
||||
for idx, key := range keys {
|
||||
values[idx] = m.keys[key].Value.(KV)[1]
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
func (m *SortedMap) Range(iterator func(key, value interface{})) {
|
||||
next := m.kv.Front()
|
||||
for next != nil {
|
||||
value := next.Value.(KV)
|
||||
iterator(value[0], value[1])
|
||||
next = next.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *SortedMap) RangeIf(iterator func(key, value interface{}) bool) {
|
||||
next := m.kv.Front()
|
||||
for next != nil {
|
||||
value := next.Value.(KV)
|
||||
loop := iterator(value[0], value[1])
|
||||
if !loop {
|
||||
return
|
||||
}
|
||||
next = next.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *SortedMap) Remove(key interface{}) (value interface{}, ok bool) {
|
||||
v, ok := m.keys[key]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
value = v.Value.(KV)[1]
|
||||
ok = true
|
||||
m.kv.Remove(v)
|
||||
delete(m.keys, key)
|
||||
return
|
||||
}
|
||||
|
||||
func (m *SortedMap) Insert(sm *SortedMap) {
|
||||
sm.Range(func(key, value interface{}) {
|
||||
m.SetKV(key, value)
|
||||
})
|
||||
}
|
||||
|
||||
func (m *SortedMap) Copy() *SortedMap {
|
||||
sm := New()
|
||||
m.Range(func(key, value interface{}) {
|
||||
sm.SetKV(key, value)
|
||||
})
|
||||
return sm
|
||||
}
|
||||
|
||||
func (m *SortedMap) Format() []string {
|
||||
var format = make([]string, 0)
|
||||
m.Range(func(key, value interface{}) {
|
||||
format = append(format, fmt.Sprintf("%s=%s", key, value))
|
||||
})
|
||||
return format
|
||||
}
|
||||
|
||||
func (m *SortedMap) Reset() {
|
||||
m.kv.Init()
|
||||
for key := range m.keys {
|
||||
delete(m.keys, key)
|
||||
}
|
||||
}
|
@ -0,0 +1,235 @@
|
||||
package sortedmap
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_SortedMap(t *testing.T) {
|
||||
sm := New()
|
||||
t.Run("SetExpression", func(t *testing.T) {
|
||||
_, _, err := sm.SetExpression("")
|
||||
assert.ErrorIs(t, err, ErrInvalidKVExpression)
|
||||
_, _, err = sm.SetExpression("foo")
|
||||
assert.ErrorIs(t, err, ErrInvalidKVExpression)
|
||||
_, _, err = sm.SetExpression("foo= ")
|
||||
assert.ErrorIs(t, err, ErrInvalidKVExpression)
|
||||
_, _, err = sm.SetExpression(" foo=")
|
||||
assert.ErrorIs(t, err, ErrInvalidKVExpression)
|
||||
_, _, err = sm.SetExpression("foo =")
|
||||
assert.ErrorIs(t, err, ErrInvalidKVExpression)
|
||||
_, _, err = sm.SetExpression("=")
|
||||
assert.ErrorIs(t, err, ErrInvalidKVExpression)
|
||||
_, _, err = sm.SetExpression("=bar")
|
||||
assert.ErrorIs(t, err, ErrInvalidKVExpression)
|
||||
key, value, err := sm.SetExpression("foo=bar")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "foo", key)
|
||||
assert.Equal(t, "bar", value)
|
||||
key, value, err = sm.SetExpression("foo=")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, value, sm.GetOr(key, ""))
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("SetKV", func(t *testing.T) {
|
||||
sm.SetKV("foo", "bar")
|
||||
assert.Equal(t, "bar", sm.GetOr("foo", ""))
|
||||
sm.SetKV("foo", "bar-changed")
|
||||
assert.Equal(t, "bar-changed", sm.GetOr("foo", ""))
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("Set", func(t *testing.T) {
|
||||
err := sm.Set(KV{})
|
||||
assert.Nil(t, err)
|
||||
err = sm.Set(KV{"foo"})
|
||||
assert.ErrorIs(t, ErrInvalidKVS, err)
|
||||
err = sm.Set(KV{"foo", "bar", "bar", "foo"})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "bar", sm.GetOr("foo", ""))
|
||||
assert.Equal(t, "foo", sm.GetOr("bar", ""))
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("Get", func(t *testing.T) {
|
||||
_, ok := sm.Get("foo")
|
||||
assert.False(t, ok)
|
||||
sm.SetKV("foo", "bar")
|
||||
value, ok := sm.Get("foo")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "bar", value)
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("GetString", func(t *testing.T) {
|
||||
_, ok := sm.GetString("foo")
|
||||
assert.False(t, ok)
|
||||
sm.SetKV("foo", "bar")
|
||||
value, ok := sm.GetString("foo")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "bar", value)
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("GetStringOr", func(t *testing.T) {
|
||||
value := sm.GetStringOr("foo", "bar")
|
||||
assert.Equal(t, "bar", value)
|
||||
sm.SetKV("foo", "foo")
|
||||
value = sm.GetStringOr("foo", "bar")
|
||||
assert.Equal(t, "foo", value)
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("GetOr", func(t *testing.T) {
|
||||
value := sm.GetOr("foo", "bar")
|
||||
assert.Equal(t, "bar", value)
|
||||
sm.SetKV("foo", "foo")
|
||||
value = sm.GetOr("foo", "bar")
|
||||
assert.Equal(t, "foo", value)
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("HasKey", func(t *testing.T) {
|
||||
ok := sm.HasKey("foo")
|
||||
assert.False(t, ok)
|
||||
sm.SetKV("foo", "")
|
||||
assert.True(t, sm.HasKey("foo"))
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("HasValue", func(t *testing.T) {
|
||||
assert.False(t, sm.HasValue("bar"))
|
||||
sm.SetKV("foo", "bar")
|
||||
assert.True(t, sm.HasValue("bar"))
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("Keys", func(t *testing.T) {
|
||||
keys := sm.Keys()
|
||||
assert.Equal(t, 0, len(keys))
|
||||
expected := []string{"foo1", "foo2", "foo3"}
|
||||
for _, key := range expected {
|
||||
sm.SetKV(key, "")
|
||||
}
|
||||
keys = sm.Keys()
|
||||
var actual []string
|
||||
for _, key := range keys {
|
||||
actual = append(actual, key.(string))
|
||||
}
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("Values", func(t *testing.T) {
|
||||
values := sm.Values()
|
||||
assert.Equal(t, 0, len(values))
|
||||
expected := []string{"foo1", "foo2", "foo3"}
|
||||
for _, key := range expected {
|
||||
sm.SetKV(key, key)
|
||||
}
|
||||
values = sm.Values()
|
||||
var actual []string
|
||||
for _, value := range values {
|
||||
actual = append(actual, value.(string))
|
||||
}
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("Range", func(t *testing.T) {
|
||||
var keys, values []string
|
||||
sm.Range(func(key, value interface{}) {
|
||||
keys = append(keys, key.(string))
|
||||
values = append(values, value.(string))
|
||||
})
|
||||
assert.Len(t, keys, 0)
|
||||
assert.Len(t, values, 0)
|
||||
|
||||
expected := []string{"foo1", "foo2", "foo3"}
|
||||
for _, key := range expected {
|
||||
sm.SetKV(key, key)
|
||||
}
|
||||
sm.Range(func(key, value interface{}) {
|
||||
keys = append(keys, key.(string))
|
||||
values = append(values, value.(string))
|
||||
})
|
||||
assert.Equal(t, expected, keys)
|
||||
assert.Equal(t, expected, values)
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("RangeIf", func(t *testing.T) {
|
||||
var keys, values []string
|
||||
sm.RangeIf(func(key, value interface{}) bool {
|
||||
keys = append(keys, key.(string))
|
||||
values = append(values, value.(string))
|
||||
return true
|
||||
})
|
||||
assert.Len(t, keys, 0)
|
||||
assert.Len(t, values, 0)
|
||||
|
||||
expected := []string{"foo1", "foo2", "foo3"}
|
||||
for _, key := range expected {
|
||||
sm.SetKV(key, key)
|
||||
}
|
||||
sm.RangeIf(func(key, value interface{}) bool {
|
||||
keys = append(keys, key.(string))
|
||||
values = append(values, value.(string))
|
||||
if key.(string) == "foo1" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
assert.Equal(t, []string{"foo1"}, keys)
|
||||
assert.Equal(t, []string{"foo1"}, values)
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("Remove", func(t *testing.T) {
|
||||
_, ok := sm.Remove("foo")
|
||||
assert.False(t, ok)
|
||||
sm.SetKV("foo", "bar")
|
||||
value, ok := sm.Remove("foo")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "bar", value)
|
||||
assert.False(t, sm.HasKey("foo"))
|
||||
assert.False(t, sm.HasValue("bar"))
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("Insert", func(t *testing.T) {
|
||||
data := New()
|
||||
data.SetKV("foo", "bar")
|
||||
sm.SetKV("foo1", "bar1")
|
||||
sm.Insert(data)
|
||||
assert.True(t, sm.HasKey("foo"))
|
||||
assert.True(t, sm.HasValue("bar"))
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("Copy", func(t *testing.T) {
|
||||
sm.SetKV("foo", "bar")
|
||||
data := sm.Copy()
|
||||
assert.True(t, data.HasKey("foo"))
|
||||
assert.True(t, data.HasValue("bar"))
|
||||
sm.SetKV("foo", "bar1")
|
||||
assert.True(t, data.HasKey("foo"))
|
||||
assert.True(t, data.HasValue("bar"))
|
||||
sm.Reset()
|
||||
})
|
||||
|
||||
t.Run("Format", func(t *testing.T) {
|
||||
format := sm.Format()
|
||||
assert.Equal(t, []string{}, format)
|
||||
sm.SetKV("foo1", "bar1")
|
||||
sm.SetKV("foo2", "bar2")
|
||||
sm.SetKV("foo3", "")
|
||||
format = sm.Format()
|
||||
assert.Equal(t, []string{"foo1=bar1", "foo2=bar2", "foo3="}, format)
|
||||
sm.Reset()
|
||||
})
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package downloader
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func Download(url string, filename string) error {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = io.Copy(f, resp.Body)
|
||||
return err
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/zeromicro/go-zero/tools/goctl/internal/version"
|
||||
sortedmap "github.com/zeromicro/go-zero/tools/goctl/pkg/collection"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/protoc"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/protocgengo"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/protocgengogrpc"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
|
||||
)
|
||||
|
||||
var goctlEnv *sortedmap.SortedMap
|
||||
|
||||
const (
|
||||
GoctlOS = "GOCTL_OS"
|
||||
GoctlArch = "GOCTL_ARCH"
|
||||
GoctlHome = "GOCTL_HOME"
|
||||
GoctlDebug = "GOCTL_DEBUG"
|
||||
GoctlCache = "GOCTL_CACHE"
|
||||
GoctlVersion = "GOCTL_VERSION"
|
||||
ProtocVersion = "PROTOC_VERSION"
|
||||
ProtocGenGoVersion = "PROTOC_GEN_GO_VERSION"
|
||||
ProtocGenGoGRPCVersion = "PROTO_GEN_GO_GRPC_VERSION"
|
||||
|
||||
envFileDir = "env"
|
||||
)
|
||||
|
||||
// init initializes the goctl environment variables, the environment variables of the function are set in order,
|
||||
// please do not change the logic order of the code.
|
||||
func init() {
|
||||
defaultGoctlHome, err := pathx.GetDefaultGoctlHome()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
goctlEnv = sortedmap.New()
|
||||
goctlEnv.SetKV(GoctlOS, runtime.GOOS)
|
||||
goctlEnv.SetKV(GoctlArch, runtime.GOARCH)
|
||||
existsEnv := readEnv(defaultGoctlHome)
|
||||
if existsEnv != nil {
|
||||
goctlHome, ok := existsEnv.GetString(GoctlHome)
|
||||
if ok && len(goctlHome) > 0 {
|
||||
goctlEnv.SetKV(GoctlHome, goctlHome)
|
||||
}
|
||||
if debug := existsEnv.GetOr(GoctlDebug, "").(string); debug != "" {
|
||||
if strings.EqualFold(debug, "true") || strings.EqualFold(debug, "false") {
|
||||
goctlEnv.SetKV(GoctlDebug, debug)
|
||||
}
|
||||
}
|
||||
if value := existsEnv.GetStringOr(GoctlCache, ""); value != "" {
|
||||
goctlEnv.SetKV(GoctlCache, value)
|
||||
}
|
||||
}
|
||||
if !goctlEnv.HasKey(GoctlHome) {
|
||||
goctlEnv.SetKV(GoctlHome, defaultGoctlHome)
|
||||
}
|
||||
if !goctlEnv.HasKey(GoctlDebug) {
|
||||
goctlEnv.SetKV(GoctlDebug, "False")
|
||||
}
|
||||
|
||||
if !goctlEnv.HasKey(GoctlCache) {
|
||||
cacheDir, _ := pathx.GetCacheDir()
|
||||
goctlEnv.SetKV(GoctlCache, cacheDir)
|
||||
}
|
||||
|
||||
goctlEnv.SetKV(GoctlVersion, version.BuildVersion)
|
||||
protocVer, _ := protoc.Version()
|
||||
goctlEnv.SetKV(ProtocVersion, protocVer)
|
||||
|
||||
protocGenGoVer, _ := protocgengo.Version()
|
||||
goctlEnv.SetKV(ProtocGenGoVersion, protocGenGoVer)
|
||||
|
||||
protocGenGoGrpcVer, _ := protocgengogrpc.Version()
|
||||
goctlEnv.SetKV(ProtocGenGoGRPCVersion, protocGenGoGrpcVer)
|
||||
}
|
||||
|
||||
func Print() string {
|
||||
return strings.Join(goctlEnv.Format(), "\n")
|
||||
}
|
||||
|
||||
func Get(key string) string {
|
||||
return GetOr(key, "")
|
||||
}
|
||||
|
||||
func GetOr(key string, def string) string {
|
||||
return goctlEnv.GetStringOr(key, def)
|
||||
}
|
||||
|
||||
func readEnv(goctlHome string) *sortedmap.SortedMap {
|
||||
envFile := filepath.Join(goctlHome, envFileDir)
|
||||
data, err := ioutil.ReadFile(envFile)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
dataStr := string(data)
|
||||
lines := strings.Split(dataStr, "\n")
|
||||
sm := sortedmap.New()
|
||||
for _, line := range lines {
|
||||
_, _, err = sm.SetExpression(line)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return sm
|
||||
}
|
||||
|
||||
func WriteEnv(kv []string) error {
|
||||
defaultGoctlHome, err := pathx.GetDefaultGoctlHome()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
data := sortedmap.New()
|
||||
for _, e := range kv {
|
||||
_, _, err := data.SetExpression(e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
data.RangeIf(func(key, value interface{}) bool {
|
||||
switch key.(string) {
|
||||
case GoctlHome, GoctlCache:
|
||||
path := value.(string)
|
||||
if !pathx.FileExists(path) {
|
||||
err = fmt.Errorf("[writeEnv]: path %q is not exists", path)
|
||||
return false
|
||||
}
|
||||
}
|
||||
if goctlEnv.HasKey(key) {
|
||||
goctlEnv.SetKV(key, value)
|
||||
return true
|
||||
} else {
|
||||
err = fmt.Errorf("[writeEnv]: invalid key: %v", key)
|
||||
return false
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
envFile := filepath.Join(defaultGoctlHome, envFileDir)
|
||||
return ioutil.WriteFile(envFile, []byte(strings.Join(goctlEnv.Format(), "\n")), 0777)
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package goctl
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/golang"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/util/console"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/vars"
|
||||
)
|
||||
|
||||
func Install(cacheDir, name string, installFn func(dest string) (string, error)) (string, error) {
|
||||
goBin := golang.GoBin()
|
||||
cacheFile := filepath.Join(cacheDir, name)
|
||||
binFile := filepath.Join(goBin, name)
|
||||
|
||||
goos := runtime.GOOS
|
||||
if goos == vars.OsWindows {
|
||||
cacheFile = cacheFile + ".exe"
|
||||
binFile = binFile + ".exe"
|
||||
}
|
||||
// read cache.
|
||||
err := pathx.Copy(cacheFile, binFile)
|
||||
if err == nil {
|
||||
console.Info("%q installed from cache", name)
|
||||
return binFile, nil
|
||||
}
|
||||
|
||||
binFile, err = installFn(binFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// write cache.
|
||||
err = pathx.Copy(binFile, cacheFile)
|
||||
if err != nil {
|
||||
console.Warning("write cache error: %+v", err)
|
||||
}
|
||||
return binFile, nil
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
|
||||
)
|
||||
|
||||
// GoBin returns a path of GOBIN.
|
||||
func GoBin() string {
|
||||
def := build.Default
|
||||
goroot := os.Getenv("GOROOT")
|
||||
bin := filepath.Join(goroot, "bin")
|
||||
if !pathx.FileExists(bin) {
|
||||
gopath := os.Getenv("GOPATH")
|
||||
bin = filepath.Join(gopath, "bin")
|
||||
}
|
||||
if !pathx.FileExists(bin) {
|
||||
bin = os.Getenv("GOBIN")
|
||||
}
|
||||
if !pathx.FileExists(bin) {
|
||||
bin = filepath.Join(def.GOPATH, "bin")
|
||||
}
|
||||
return bin
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func Install(git string) error {
|
||||
cmd := exec.Command("go", "install", git)
|
||||
env := os.Environ()
|
||||
env = append(env, "GO111MODULE=on", "GOPROXY=https://goproxy.cn,direct")
|
||||
cmd.Env = env
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
return err
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package protoc
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/downloader"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/goctl"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/rpc/execx"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/util/env"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/util/zipx"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/vars"
|
||||
)
|
||||
|
||||
var url = map[string]string{
|
||||
"linux_32": "https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protoc-3.19.4-linux-x86_32.zip",
|
||||
"linux_64": "https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protoc-3.19.4-linux-x86_64.zip",
|
||||
"darwin": "https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protoc-3.19.4-osx-x86_64.zip",
|
||||
"windows_32": "https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protoc-3.19.4-win32.zip",
|
||||
"windows_64": "https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protoc-3.19.4-win64.zip",
|
||||
}
|
||||
|
||||
const (
|
||||
Name = "protoc"
|
||||
ZipFileName = Name + ".zip"
|
||||
)
|
||||
|
||||
func Install(cacheDir string) (string, error) {
|
||||
return goctl.Install(cacheDir, Name, func(dest string) (string, error) {
|
||||
goos := runtime.GOOS
|
||||
tempFile := filepath.Join(os.TempDir(), ZipFileName)
|
||||
bit := 32 << (^uint(0) >> 63)
|
||||
var downloadUrl string
|
||||
switch goos {
|
||||
case vars.OsMac:
|
||||
downloadUrl = url[vars.OsMac]
|
||||
case vars.OsWindows:
|
||||
downloadUrl = url[fmt.Sprintf("%s_%d", vars.OsWindows, bit)]
|
||||
case vars.OsLinux:
|
||||
downloadUrl = url[fmt.Sprintf("%s_%d", vars.OsLinux, bit)]
|
||||
default:
|
||||
return "", fmt.Errorf("unsupport OS: %q", goos)
|
||||
}
|
||||
|
||||
err := downloader.Download(downloadUrl, tempFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return dest, zipx.Unpacking(tempFile, filepath.Dir(dest), func(f *zip.File) bool {
|
||||
return filepath.Base(f.Name) == filepath.Base(dest)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Exists() bool {
|
||||
_, err := env.LookUpProtoc()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func Version() (string, error) {
|
||||
path, err := env.LookUpProtoc()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
version, err := execx.Run(path+" --version", "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fields := strings.Fields(version)
|
||||
if len(fields) > 1 {
|
||||
return fields[1], nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package protocgengo
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/goctl"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/golang"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/rpc/execx"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/util/env"
|
||||
)
|
||||
|
||||
const (
|
||||
Name = "protoc-gen-go"
|
||||
url = "google.golang.org/protobuf/cmd/protoc-gen-go@latest"
|
||||
)
|
||||
|
||||
func Install(cacheDir string) (string, error) {
|
||||
return goctl.Install(cacheDir, Name, func(dest string) (string, error) {
|
||||
err := golang.Install(url)
|
||||
return dest, err
|
||||
})
|
||||
}
|
||||
|
||||
func Exists() bool {
|
||||
_, err := env.LookUpProtocGenGo()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Version is used to get the version of the protoc-gen-go plugin. For older versions, protoc-gen-go does not support
|
||||
// version fetching, so if protoc-gen-go --version is executed, it will cause the process to block, so it is controlled
|
||||
// by a timer to prevent the older version process from blocking.
|
||||
func Version() (string, error) {
|
||||
path, err := env.LookUpProtocGenGo()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
versionC := make(chan string)
|
||||
go func(c chan string) {
|
||||
version, _ := execx.Run(path+" --version", "")
|
||||
fields := strings.Fields(version)
|
||||
if len(fields) > 1 {
|
||||
c <- fields[1]
|
||||
}
|
||||
}(versionC)
|
||||
t := time.NewTimer(time.Second)
|
||||
select {
|
||||
case <-t.C:
|
||||
return "", nil
|
||||
case version := <-versionC:
|
||||
return version, nil
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package protocgengogrpc
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/goctl"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/golang"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/rpc/execx"
|
||||
"github.com/zeromicro/go-zero/tools/goctl/util/env"
|
||||
)
|
||||
|
||||
const (
|
||||
Name = "protoc-gen-go-grpc"
|
||||
url = "google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest"
|
||||
)
|
||||
|
||||
func Install(cacheDir string) (string, error) {
|
||||
return goctl.Install(cacheDir, Name, func(dest string) (string, error) {
|
||||
err := golang.Install(url)
|
||||
return dest, err
|
||||
})
|
||||
}
|
||||
|
||||
func Exists() bool {
|
||||
_, err := env.LookUpProtocGenGoGrpc()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Version is used to get the version of the protoc-gen-go-grpc plugin.
|
||||
func Version() (string, error) {
|
||||
path, err := env.LookUpProtocGenGoGrpc()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
version, err := execx.Run(path+" --version", "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fields := strings.Fields(version)
|
||||
if len(fields) > 1 {
|
||||
return fields[1], nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package zipx
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
|
||||
)
|
||||
|
||||
func Unpacking(name, destPath string, mapper func(f *zip.File) bool) error {
|
||||
r, err := zip.OpenReader(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
for _, file := range r.File {
|
||||
ok := mapper(file)
|
||||
if ok {
|
||||
err = fileCopy(file, destPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func fileCopy(file *zip.File, destPath string) error {
|
||||
rc, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rc.Close()
|
||||
filename := filepath.Join(destPath, filepath.Base(file.Name))
|
||||
dir := filepath.Dir(filename)
|
||||
err = pathx.MkdirIfNotExist(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer w.Close()
|
||||
_, err = io.Copy(w, rc)
|
||||
return err
|
||||
}
|
Loading…
Reference in New Issue