Initial QSfera import
This commit is contained in:
+62
@@ -0,0 +1,62 @@
|
||||
# Env Util
|
||||
|
||||
Provide some commonly system or go ENV util functions.
|
||||
|
||||
## Install
|
||||
|
||||
```shell
|
||||
go get github.com/gookit/goutil/envutil
|
||||
```
|
||||
|
||||
## Go docs
|
||||
|
||||
- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/envutil)
|
||||
|
||||
## Functions API
|
||||
|
||||
> **Note**: doc by run `go doc ./envutil`
|
||||
|
||||
```go
|
||||
func Environ() map[string]string
|
||||
func GetBool(name string, def ...bool) bool
|
||||
func GetInt(name string, def ...int) int
|
||||
func Getenv(name string, def ...string) string
|
||||
func HasShellEnv(shell string) bool
|
||||
func IsConsole(out io.Writer) bool
|
||||
func IsGithubActions() bool
|
||||
func IsLinux() bool
|
||||
func IsMSys() bool
|
||||
func IsMac() bool
|
||||
func IsSupport256Color() bool
|
||||
func IsSupportColor() bool
|
||||
func IsSupportTrueColor() bool
|
||||
func IsTerminal(fd uintptr) bool
|
||||
func IsWSL() bool
|
||||
func IsWin() bool
|
||||
func IsWindows() bool
|
||||
func ParseEnvValue(val string) string
|
||||
func ParseValue(val string) (newVal string)
|
||||
func SetEnvs(mp map[string]string)
|
||||
func StdIsTerminal() bool
|
||||
func VarParse(val string) string
|
||||
func VarReplace(s string) string
|
||||
```
|
||||
|
||||
## Code Check & Testing
|
||||
|
||||
```bash
|
||||
gofmt -w -l ./
|
||||
golint ./...
|
||||
```
|
||||
|
||||
**Testing**:
|
||||
|
||||
```shell
|
||||
go test -v ./envutil/...
|
||||
```
|
||||
|
||||
**Test limit by regexp**:
|
||||
|
||||
```shell
|
||||
go test -v -run ^TestSetByKeys ./envutil/...
|
||||
```
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
package envutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gookit/goutil/internal/comfunc"
|
||||
)
|
||||
|
||||
// DefaultEnvFile default file name
|
||||
const DefaultEnvFile = ".env"
|
||||
|
||||
// Dotenv load and parse dotenv files
|
||||
type Dotenv struct {
|
||||
// Files dot env file paths, allow multi files.
|
||||
// - filename support simple glob pattern. eg: ".env.*"
|
||||
//
|
||||
// default: [".env"]
|
||||
Files []string
|
||||
// BaseDir base dir for join files path
|
||||
//
|
||||
// default is workdir
|
||||
BaseDir string
|
||||
// UpperKey change key to upper on set ENV. default: true
|
||||
UpperKey bool
|
||||
// IgnoreNotExist only load exists.
|
||||
//
|
||||
// - default: false - will return error if not exists
|
||||
IgnoreNotExist bool
|
||||
// LoadFirstExist only load first exists env file on Files
|
||||
LoadFirstExist bool
|
||||
|
||||
loadFiles []string
|
||||
loadData map[string]string
|
||||
}
|
||||
|
||||
// NewDotenv create a new dotenv config
|
||||
func NewDotenv() *Dotenv {
|
||||
return &Dotenv{
|
||||
UpperKey: true,
|
||||
Files: []string{DefaultEnvFile},
|
||||
// init fields
|
||||
loadData: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// LoadAndInit load dotenv files and parse to os.Environ
|
||||
func (c *Dotenv) LoadAndInit() error {
|
||||
return c.doLoadFiles(c.Files)
|
||||
}
|
||||
|
||||
// LoadFiles append load dotenv files
|
||||
// - filename support simple glob pattern. eg: ".env.*"
|
||||
func (c *Dotenv) LoadFiles(files ...string) error {
|
||||
return c.doLoadFiles(files)
|
||||
}
|
||||
|
||||
// LoadText load dotenv contents and parse to os Env
|
||||
func (c *Dotenv) LoadText(contents string) error {
|
||||
return c.parseAndSetEnv(contents)
|
||||
}
|
||||
|
||||
// do load dotenv files
|
||||
func (c *Dotenv) doLoadFiles(files []string) error {
|
||||
var filePath string
|
||||
for _, file := range files {
|
||||
filePath = strings.TrimSpace(file)
|
||||
if c.BaseDir != "" && !filepath.IsAbs(file) {
|
||||
filePath = filepath.Join(c.BaseDir, file)
|
||||
}
|
||||
|
||||
// load and parse to ENV
|
||||
if err := c.loadFile(filePath); err != nil {
|
||||
return err
|
||||
}
|
||||
if c.LoadFirstExist && len(c.loadFiles) > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Dotenv) loadFile(filePath string) error {
|
||||
// filename support simple glob pattern.
|
||||
if strings.ContainsRune(filePath, '*') {
|
||||
matches, err := filepath.Glob(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, matchFile := range matches {
|
||||
if err = c.parseFile(matchFile); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Load single file
|
||||
return c.parseFile(filePath)
|
||||
}
|
||||
|
||||
// parseFile load single file and parse to ENV
|
||||
func (c *Dotenv) parseFile(filePath string) error {
|
||||
contents, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
// IgnoreNotExist: skip non-existent files
|
||||
if c.IgnoreNotExist && os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.parseAndSetEnv(string(contents))
|
||||
if err == nil {
|
||||
c.loadFiles = append(c.loadFiles, filePath)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Dotenv) parseAndSetEnv(contents string) error {
|
||||
// Parse ENV lines
|
||||
envMp, err := comfunc.ParseEnvLines(contents, comfunc.ParseEnvLineOption{
|
||||
SkipOnErrorLine: true,
|
||||
})
|
||||
|
||||
// Set to ENV
|
||||
for key, val := range envMp {
|
||||
key = strings.ToUpper(key)
|
||||
c.loadData[key] = val
|
||||
_ = os.Setenv(key, val)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// UnloadEnv remove loaded dotenv data from os.Environ
|
||||
func (c *Dotenv) UnloadEnv() bool {
|
||||
if len(c.loadData) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for key := range c.loadData {
|
||||
_ = os.Unsetenv(key)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// LoadedData get loaded dotenv data map
|
||||
func (c *Dotenv) LoadedData() map[string]string {
|
||||
return c.loadData
|
||||
}
|
||||
|
||||
// LoadedFiles get loaded dotenv files
|
||||
func (c *Dotenv) LoadedFiles() []string {
|
||||
return c.loadFiles
|
||||
}
|
||||
|
||||
// Reset unload all loaded ENV and reset data
|
||||
func (c *Dotenv) Reset() {
|
||||
c.UnloadEnv()
|
||||
c.loadFiles = nil
|
||||
c.loadData = make(map[string]string)
|
||||
}
|
||||
|
||||
//
|
||||
// region standard dotenv instance
|
||||
//
|
||||
|
||||
var stdEnv = NewDotenv()
|
||||
|
||||
// StdDotenv get standard dotenv instance
|
||||
func StdDotenv() *Dotenv { return stdEnv }
|
||||
|
||||
// DotenvLoad load dotenv file and parse to ENV
|
||||
func DotenvLoad(fns ...func(cfg *Dotenv)) error {
|
||||
for _, fn := range fns {
|
||||
fn(stdEnv)
|
||||
}
|
||||
return stdEnv.LoadAndInit()
|
||||
}
|
||||
|
||||
// LoadEnvFiles load dotenv files and parse to ENV
|
||||
func LoadEnvFiles(baseDir string, files ...string) error {
|
||||
return DotenvLoad(func(cfg *Dotenv) {
|
||||
cfg.BaseDir = baseDir
|
||||
if len(files) > 0 {
|
||||
cfg.Files = files
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// LoadedEnvFiles get loaded dotenv files
|
||||
func LoadedEnvFiles() []string {
|
||||
return stdEnv.LoadedFiles()
|
||||
}
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
// Package envutil provide some commonly ENV util functions.
|
||||
package envutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gookit/goutil/internal/comfunc"
|
||||
"github.com/gookit/goutil/internal/varexpr"
|
||||
)
|
||||
|
||||
// ValueGetter Env value provider func.
|
||||
//
|
||||
// TIPS: you can custom provide data.
|
||||
var ValueGetter = os.Getenv
|
||||
|
||||
// VarReplace replaces ${var} or $var in the string according to the values.
|
||||
//
|
||||
// is alias of the os.ExpandEnv()
|
||||
func VarReplace(s string) string { return os.ExpandEnv(s) }
|
||||
|
||||
// ParseOrErr parse ENV var value from input string, support default value.
|
||||
//
|
||||
// Diff with the ParseValue, this support return error.
|
||||
//
|
||||
// With error format: ${VAR_NAME | ?error}
|
||||
func ParseOrErr(val string) (string, error) {
|
||||
return varexpr.Parse(val)
|
||||
}
|
||||
|
||||
// ParseValue parse ENV var value from input string, support default value.
|
||||
//
|
||||
// Format:
|
||||
//
|
||||
// ${var_name} Only var name
|
||||
// ${var_name | default} With default value
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// envutil.ParseValue("${ APP_NAME }")
|
||||
// envutil.ParseValue("${ APP_ENV | dev }")
|
||||
func ParseValue(val string) string {
|
||||
return varexpr.SafeParse(val)
|
||||
}
|
||||
|
||||
// VarParse alias of the ParseValue
|
||||
func VarParse(val string) string { return varexpr.SafeParse(val) }
|
||||
|
||||
// ParseEnvValue alias of the ParseValue
|
||||
func ParseEnvValue(val string) string { return varexpr.SafeParse(val) }
|
||||
|
||||
// SplitText2map parse ENV text to map. Can use to parse .env file contents.
|
||||
func SplitText2map(text string) map[string]string {
|
||||
envMp, _ := comfunc.ParseEnvLines(text, comfunc.ParseEnvLineOption{
|
||||
SkipOnErrorLine: true,
|
||||
})
|
||||
return envMp
|
||||
}
|
||||
|
||||
// SplitLineToKv parse ENV line to k-v. eg: 'DEBUG=true' => ['DEBUG', 'true']
|
||||
func SplitLineToKv(line string) (string, string) {
|
||||
if line = strings.TrimSpace(line); line == "" {
|
||||
return "", ""
|
||||
}
|
||||
return comfunc.SplitLineToKv(line, "=")
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
package envutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gookit/goutil/internal/comfunc"
|
||||
"github.com/gookit/goutil/strutil"
|
||||
"github.com/gookit/goutil/x/basefn"
|
||||
)
|
||||
|
||||
// HasEnv check ENV key exists
|
||||
func HasEnv(name string) bool {
|
||||
_, ok := os.LookupEnv(name)
|
||||
return ok
|
||||
}
|
||||
|
||||
// Getenv get ENV value by key name, can with default value
|
||||
func Getenv(name string, def ...string) string {
|
||||
val := os.Getenv(name)
|
||||
if val == "" && len(def) > 0 {
|
||||
val = def[0]
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// MustGet get ENV value by key name, if not exists or empty, will panic
|
||||
func MustGet(name string) string {
|
||||
if val := os.Getenv(name); val != "" {
|
||||
return val
|
||||
}
|
||||
panic("ENV key '" + name + "' not exists")
|
||||
}
|
||||
|
||||
// GetInt get int ENV value by key name, can with default value
|
||||
func GetInt(name string, def ...int) int {
|
||||
if val := os.Getenv(name); val != "" {
|
||||
return strutil.QuietInt(val)
|
||||
}
|
||||
return basefn.FirstOr(def, 0)
|
||||
}
|
||||
|
||||
// GetBool get bool ENV value by key name, can with default value
|
||||
func GetBool(name string, def ...bool) bool {
|
||||
if val := os.Getenv(name); val != "" {
|
||||
return strutil.QuietBool(val)
|
||||
}
|
||||
return basefn.FirstOr(def, false)
|
||||
}
|
||||
|
||||
// GetOne get one not empty ENV value by input names.
|
||||
func GetOne(names []string, defVal ...string) string {
|
||||
for _, name := range names {
|
||||
if val := os.Getenv(name); val != "" {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
if len(defVal) > 0 {
|
||||
return defVal[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetMulti ENV values by input names.
|
||||
func GetMulti(names ...string) map[string]string {
|
||||
valMap := make(map[string]string, len(names))
|
||||
|
||||
for _, name := range names {
|
||||
if val := os.Getenv(name); val != "" {
|
||||
valMap[name] = val
|
||||
}
|
||||
}
|
||||
return valMap
|
||||
}
|
||||
|
||||
// OnExist check ENV value by key name, will call fn on value exists(not-empty)
|
||||
func OnExist(name string, fn func(val string)) bool {
|
||||
if val := os.Getenv(name); val != "" {
|
||||
fn(val)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// EnvPaths get and split $PATH to []string
|
||||
func EnvPaths() []string {
|
||||
return filepath.SplitList(os.Getenv("PATH"))
|
||||
}
|
||||
|
||||
// EnvMap like os.Environ, but will returns key-value map[string]string data.
|
||||
func EnvMap() map[string]string { return comfunc.Environ() }
|
||||
|
||||
// Environ like os.Environ, but will returns key-value map[string]string data.
|
||||
func Environ() map[string]string { return comfunc.Environ() }
|
||||
|
||||
// SearchEnvKeys values by given keywords
|
||||
func SearchEnvKeys(keywords string) map[string]string {
|
||||
return SearchEnv(keywords, false)
|
||||
}
|
||||
|
||||
// SearchEnv values by given keywords
|
||||
func SearchEnv(keywords string, matchValue bool) map[string]string {
|
||||
founded := make(map[string]string)
|
||||
|
||||
for name, val := range comfunc.Environ() {
|
||||
if strutil.IContains(name, keywords) {
|
||||
founded[name] = val
|
||||
} else if matchValue && strutil.IContains(val, keywords) {
|
||||
founded[name] = val
|
||||
}
|
||||
}
|
||||
|
||||
return founded
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
package envutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/gookit/goutil/internal/comfunc"
|
||||
"github.com/gookit/goutil/sysutil"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// IsWin system. linux windows darwin
|
||||
func IsWin() bool {
|
||||
return runtime.GOOS == "windows"
|
||||
}
|
||||
|
||||
// IsWindows system. alias of IsWin
|
||||
func IsWindows() bool {
|
||||
return runtime.GOOS == "windows"
|
||||
}
|
||||
|
||||
// IsMac system
|
||||
func IsMac() bool {
|
||||
return runtime.GOOS == "darwin"
|
||||
}
|
||||
|
||||
// IsLinux system
|
||||
func IsLinux() bool {
|
||||
return runtime.GOOS == "linux"
|
||||
}
|
||||
|
||||
// IsMSys msys(MINGW64) env. alias of the sysutil.IsMSys()
|
||||
func IsMSys() bool {
|
||||
return sysutil.IsMSys()
|
||||
}
|
||||
|
||||
// IsTerminal isatty check
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// envutil.IsTerminal(os.Stdout.Fd())
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
// return isatty.IsTerminal(fd) // "github.com/mattn/go-isatty"
|
||||
return term.IsTerminal(int(fd))
|
||||
}
|
||||
|
||||
// StdIsTerminal os.Stdout is terminal
|
||||
func StdIsTerminal() bool {
|
||||
return IsTerminal(os.Stdout.Fd())
|
||||
}
|
||||
|
||||
// IsConsole check out is console env. alias of the sysutil.IsConsole()
|
||||
func IsConsole(out io.Writer) bool {
|
||||
return sysutil.IsConsole(out)
|
||||
}
|
||||
|
||||
// HasShellEnv has shell env check.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// HasShellEnv("sh")
|
||||
// HasShellEnv("bash")
|
||||
func HasShellEnv(shell string) bool {
|
||||
return comfunc.HasShellEnv(shell)
|
||||
}
|
||||
|
||||
// Support color:
|
||||
//
|
||||
// "TERM=xterm"
|
||||
// "TERM=xterm-vt220"
|
||||
// "TERM=xterm-256color"
|
||||
// "TERM=screen-256color"
|
||||
// "TERM=tmux-256color"
|
||||
// "TERM=rxvt-unicode-256color"
|
||||
//
|
||||
// Don't support color:
|
||||
//
|
||||
// "TERM=cygwin"
|
||||
var specialColorTerms = map[string]bool{
|
||||
"alacritty": true,
|
||||
}
|
||||
|
||||
// IsSupportColor check current console is support color.
|
||||
//
|
||||
// Supported:
|
||||
//
|
||||
// linux, mac, or Windows's ConEmu, Cmder, putty, git-bash.exe
|
||||
//
|
||||
// Not support:
|
||||
//
|
||||
// windows cmd.exe, powerShell.exe
|
||||
func IsSupportColor() bool {
|
||||
envTerm := os.Getenv("TERM")
|
||||
if strings.Contains(envTerm, "xterm") {
|
||||
return true
|
||||
}
|
||||
|
||||
// it's special color term
|
||||
if _, ok := specialColorTerms[envTerm]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
// like on ConEmu software, e.g "ConEmuANSI=ON"
|
||||
if os.Getenv("ConEmuANSI") == "ON" {
|
||||
return true
|
||||
}
|
||||
|
||||
// like on ConEmu software, e.g "ANSICON=189x2000 (189x43)"
|
||||
if os.Getenv("ANSICON") != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
// up: if support 256-color, can also support basic color.
|
||||
return IsSupport256Color()
|
||||
}
|
||||
|
||||
// IsSupport256Color render
|
||||
func IsSupport256Color() bool {
|
||||
// "TERM=xterm-256color"
|
||||
// "TERM=screen-256color"
|
||||
// "TERM=tmux-256color"
|
||||
// "TERM=rxvt-unicode-256color"
|
||||
supported := strings.Contains(os.Getenv("TERM"), "256color")
|
||||
if !supported {
|
||||
// up: if support true-color, can also support 256-color.
|
||||
supported = IsSupportTrueColor()
|
||||
}
|
||||
|
||||
return supported
|
||||
}
|
||||
|
||||
// IsSupportTrueColor render. IsSupportRGBColor
|
||||
func IsSupportTrueColor() bool {
|
||||
// "COLORTERM=truecolor"
|
||||
return strings.Contains(os.Getenv("COLORTERM"), "truecolor")
|
||||
}
|
||||
|
||||
// IsGithubActions env
|
||||
func IsGithubActions() bool {
|
||||
return os.Getenv("GITHUB_ACTIONS") == "true"
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package envutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/gookit/goutil/internal/comfunc"
|
||||
)
|
||||
|
||||
// SetEnvMap set multi ENV(string-map) to os
|
||||
func SetEnvMap(mp map[string]string) {
|
||||
for key, value := range mp {
|
||||
_ = os.Setenv(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// SetEnvs set multi k-v ENV pairs to os
|
||||
func SetEnvs(kvPairs ...string) {
|
||||
if len(kvPairs)%2 == 1 {
|
||||
panic("envutil.SetEnvs: odd argument count")
|
||||
}
|
||||
|
||||
for i := 0; i < len(kvPairs); i += 2 {
|
||||
_ = os.Setenv(kvPairs[i], kvPairs[i+1])
|
||||
}
|
||||
}
|
||||
|
||||
// UnsetEnvs from os
|
||||
func UnsetEnvs(keys ...string) {
|
||||
for _, key := range keys {
|
||||
_ = os.Unsetenv(key)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadText parse multiline text to ENV. Can use to load .env file contents.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// envutil.LoadText(fsutil.ReadFile(".env"))
|
||||
func LoadText(text string) {
|
||||
envMp := SplitText2map(text)
|
||||
for key, value := range envMp {
|
||||
_ = os.Setenv(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadString set line to ENV. e.g.: "KEY=VALUE"
|
||||
func LoadString(line string) bool {
|
||||
k, v := comfunc.SplitLineToKv(line, "=")
|
||||
if len(k) > 0 {
|
||||
return os.Setenv(k, v) == nil
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user