diff --git a/CHANGELOG.md b/CHANGELOG.md index 0066fc92..c45fece0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Lint: use golangci-lint. [4619](https://github.com/beego/beego/pull/4619) - Chore: format code. [4615](https://github.com/beego/beego/pull/4615) +- Env: non-empty GOBIN & GOPATH. [4613](https://github.com/beego/beego/pull/4613) - Chore: update dependencies. [4611](https://github.com/beego/beego/pull/4611) - Update orm_test.go/TestInsertOrUpdate with table-driven. [4609](https://github.com/beego/beego/pull/4609) - Add: Resp() method for web.Controller. [4588](https://github.com/beego/beego/pull/4588) diff --git a/core/config/env/env.go b/core/config/env/env.go index beb91138..d745362a 100644 --- a/core/config/env/env.go +++ b/core/config/env/env.go @@ -18,7 +18,10 @@ package env import ( "fmt" + "go/build" + "io/ioutil" "os" + "path/filepath" "strings" "sync" ) @@ -30,12 +33,14 @@ func init() { splits := strings.Split(e, "=") env.Store(splits[0], os.Getenv(splits[0])) } + env.Store("GOBIN", GetGOBIN()) // set non-empty GOBIN when initialization + env.Store("GOPATH", GetGOPATH()) // set non-empty GOPATH when initialization } // Get returns a value for a given key. // If the key does not exist, the default value will be returned. func Get(key string, defVal string) string { - if val,ok:= env.Load(key);ok { + if val, ok := env.Load(key); ok { return val.(string) } return defVal @@ -44,7 +49,7 @@ func Get(key string, defVal string) string { // MustGet returns a value by key. // If the key does not exist, it will return an error. func MustGet(key string) (string, error) { - if val,ok := env.Load(key); ok{ + if val, ok := env.Load(key); ok { return val.(string), nil } return "", fmt.Errorf("no env variable with %s", key) @@ -82,3 +87,91 @@ func GetAll() map[string]string { }) return envs } + +// envFile returns the name of the Go environment configuration file. +// Copy from https://github.com/golang/go/blob/c4f2a9788a7be04daf931ac54382fbe2cb754938/src/cmd/go/internal/cfg/cfg.go#L150-L166 +func envFile() (string, error) { + if file := os.Getenv("GOENV"); file != "" { + if file == "off" { + return "", fmt.Errorf("GOENV=off") + } + return file, nil + } + dir, err := os.UserConfigDir() + if err != nil { + return "", err + } + if dir == "" { + return "", fmt.Errorf("missing user-config dir") + } + return filepath.Join(dir, "go", "env"), nil +} + +// GetRuntimeEnv returns the value of runtime environment variable, +// that is set by running following command: `go env -w key=value`. +func GetRuntimeEnv(key string) (string, error) { + file, err := envFile() + if err != nil { + return "", err + } + if file == "" { + return "", fmt.Errorf("missing runtime env file") + } + + var runtimeEnv string + data, err := ioutil.ReadFile(file) + if err != nil { + return "", err + } + envStrings := strings.Split(string(data), "\n") + for _, envItem := range envStrings { + envItem = strings.TrimSuffix(envItem, "\r") + envKeyValue := strings.Split(envItem, "=") + if len(envKeyValue) == 2 && strings.TrimSpace(envKeyValue[0]) == key { + runtimeEnv = strings.TrimSpace(envKeyValue[1]) + } + } + return runtimeEnv, nil +} + +// GetGOBIN returns GOBIN environment variable as a string. +// It will NOT be an empty string. +func GetGOBIN() string { + // The one set by user explicitly by `export GOBIN=/path` or `env GOBIN=/path command` + gobin := strings.TrimSpace(Get("GOBIN", "")) + if gobin == "" { + var err error + // The one set by user by running `go env -w GOBIN=/path` + gobin, err = GetRuntimeEnv("GOBIN") + if err != nil { + // The default one that Golang uses + return filepath.Join(build.Default.GOPATH, "bin") + } + if gobin == "" { + return filepath.Join(build.Default.GOPATH, "bin") + } + return gobin + } + return gobin +} + +// GetGOPATH returns GOPATH environment variable as a string. +// It will NOT be an empty string. +func GetGOPATH() string { + // The one set by user explicitly by `export GOPATH=/path` or `env GOPATH=/path command` + gopath := strings.TrimSpace(Get("GOPATH", "")) + if gopath == "" { + var err error + // The one set by user by running `go env -w GOPATH=/path` + gopath, err = GetRuntimeEnv("GOPATH") + if err != nil { + // The default one that Golang uses + return build.Default.GOPATH + } + if gopath == "" { + return build.Default.GOPATH + } + return gopath + } + return gopath +} diff --git a/core/config/env/env_test.go b/core/config/env/env_test.go index 3f1d4dba..1cd88147 100644 --- a/core/config/env/env_test.go +++ b/core/config/env/env_test.go @@ -15,7 +15,9 @@ package env import ( + "go/build" "os" + "path/filepath" "testing" ) @@ -73,3 +75,41 @@ func TestEnvGetAll(t *testing.T) { t.Error("expected environment not empty.") } } + +func TestEnvFile(t *testing.T) { + envFile, err := envFile() + if err != nil { + t.Errorf("expected to get env file without error, but got %s.", err) + } + if envFile == "" { + t.Error("expected to get valid env file, but got empty string.") + } +} + +func TestGetGOBIN(t *testing.T) { + customGOBIN := filepath.Join("path", "to", "gobin") + Set("GOBIN", customGOBIN) + if gobin := GetGOBIN(); gobin != Get("GOBIN", "") { + t.Errorf("expected GOBIN environment variable equals to %s, but got %s.", customGOBIN, gobin) + } + + Set("GOBIN", "") + defaultGOBIN := filepath.Join(build.Default.GOPATH, "bin") + if gobin := GetGOBIN(); gobin != defaultGOBIN { + t.Errorf("expected GOBIN environment variable equals to %s, but got %s.", defaultGOBIN, gobin) + } +} + +func TestGetGOPATH(t *testing.T) { + customGOPATH := filepath.Join("path", "to", "gopath") + Set("GOPATH", customGOPATH) + if goPath := GetGOPATH(); goPath != Get("GOPATH", "") { + t.Errorf("expected GOPATH environment variable equals to %s, but got %s.", customGOPATH, goPath) + } + + Set("GOPATH", "") + defaultGOPATH := build.Default.GOPATH + if goPath := GetGOPATH(); goPath != defaultGOPATH { + t.Errorf("expected GOPATH environment variable equals to %s, but got %s.", defaultGOPATH, goPath) + } +}