forked from Luzifer/envrun
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
137 lines (114 loc) · 3.59 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package main
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
log "github.com/sirupsen/logrus"
"github.com/Luzifer/rconfig"
)
var (
cfg = struct {
CleanEnv bool `flag:"clean" default:"false" description:"Do not pass current environment to child process"`
EncryptionMethod string `flag:"encryption" default:"openssl-md5" description:"Encryption method used for encrypted env-file (Available: gpg-symmetric, openssl-md5, openssl-sha256)"`
EnvFile string `flag:"env-file" default:".env" description:"Location of the environment file"`
LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"`
PasswordFile string `flag:"password-file" default:"" description:"Read encryption key from file"`
Password string `flag:"password,p" default:"" env:"PASSWORD" description:"Password to decrypt environment file"`
Silent bool `flag:"q" default:"false" description:"Suppress informational messages from envrun (DEPRECATED, use --log-level=warn)"`
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
}{}
version = "dev"
)
func init() {
if err := rconfig.ParseAndValidate(&cfg); err != nil {
log.Fatalf("Unable to parse commandline options: %s", err)
}
if cfg.VersionAndExit {
fmt.Printf("envrun %s\n", version)
os.Exit(0)
}
if cfg.Silent && cfg.LogLevel == "info" {
// Migration of deprecated flag
cfg.LogLevel = "warn"
}
if l, err := log.ParseLevel(cfg.LogLevel); err != nil {
log.WithError(err).Fatal("Unable to parse log level")
} else {
log.SetLevel(l)
}
}
func envListToMap(list []string) map[string]string {
out := map[string]string{}
for _, entry := range list {
if len(entry) == 0 || entry[0] == '#' {
continue
}
parts := strings.SplitN(entry, "=", 2)
out[parts[0]] = parts[1]
}
return out
}
func envMapToList(envMap map[string]string) []string {
out := []string{}
for k, v := range envMap {
out = append(out, k+"="+v)
}
return out
}
func main() {
if cfg.Password == "" && cfg.PasswordFile != "" {
if _, err := os.Stat(cfg.PasswordFile); err == nil {
data, err := ioutil.ReadFile(cfg.PasswordFile)
if err != nil {
log.WithError(err).Fatal("Unable to read password from file")
}
cfg.Password = strings.TrimSpace(string(data))
}
}
dec, err := decryptMethodFromName(cfg.EncryptionMethod)
if err != nil {
log.WithError(err).Fatal("Could not load decrypt method")
}
pairs, err := loadEnvFromFile(cfg.EnvFile, cfg.Password, dec)
if err != nil {
log.WithError(err).Fatal("Could not load env file")
}
var childenv = envListToMap(os.Environ())
if cfg.CleanEnv {
childenv = map[string]string{}
}
for k, v := range pairs {
childenv[k] = v
}
c := exec.Command(rconfig.Args()[1], rconfig.Args()[2:]...)
c.Env = envMapToList(childenv)
c.Stdout = os.Stdout
c.Stderr = os.Stderr
c.Stdin = os.Stdin
err = c.Run()
switch err.(type) {
case nil:
log.Info("Process exitted with code 0")
os.Exit(0)
case *exec.ExitError:
log.Error("Unclean exit with exit-code != 0")
os.Exit(1)
default:
log.WithError(err).Error("An unknown error occurred")
os.Exit(2)
}
}
func loadEnvFromFile(filename, passphrase string, decrypt decryptMethod) (map[string]string, error) {
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("Could not read env-file: %s", err)
}
if passphrase != "" {
if body, err = decrypt(body, passphrase); err != nil {
return nil, fmt.Errorf("Could not decrypt env-file: %s", err)
}
}
return envListToMap(strings.Split(string(body), "\n")), nil
}