/
cmd.go
205 lines (168 loc) · 4.89 KB
/
cmd.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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package cmd
import (
"os"
"os/exec"
"path/filepath"
"runtime"
"github.com/ActiveState/cli/internal/errs"
"github.com/ActiveState/cli/internal/locale"
"github.com/ActiveState/cli/internal/logging"
"github.com/ActiveState/cli/internal/osutils"
"github.com/ActiveState/cli/internal/output"
"github.com/ActiveState/cli/internal/subshell/sscommon"
"github.com/ActiveState/cli/pkg/project"
)
var escaper *osutils.ShellEscape
func init() {
escaper = osutils.NewBatchEscaper()
}
// SubShell covers the subshell.SubShell interface, reference that for documentation
type SubShell struct {
binary string
rcFile *os.File
cmd *exec.Cmd
env map[string]string
errs chan error
}
const Name string = "cmd"
// Shell - see subshell.SubShell
func (v *SubShell) Shell() string {
return Name
}
// Binary - see subshell.SubShell
func (v *SubShell) Binary() string {
return v.binary
}
// SetBinary - see subshell.SubShell
func (v *SubShell) SetBinary(binary string) {
v.binary = binary
}
// WriteUserEnv - see subshell.SubShell
func (v *SubShell) WriteUserEnv(cfg sscommon.Configurable, env map[string]string, envType sscommon.RcIdentification, userScope bool) error {
cmdEnv := NewCmdEnv(userScope)
// Clean up old entries
oldEnv := cfg.GetStringMap(envType.Key)
for k, v := range oldEnv {
if err := cmdEnv.unset(k, v.(string)); err != nil {
return err
}
}
// Store new entries
err := cfg.Set(envType.Key, env)
if err != nil {
return errs.Wrap(err, "Could not set env infomation in config")
}
for k, v := range env {
value := v
if k == "PATH" {
path, err := cmdEnv.Get("PATH")
if err != nil {
return err
}
if path != "" {
path = ";" + path
}
value = v + path
}
// Set key/value in the user environment
err := cmdEnv.Set(k, value)
if err != nil {
return err
}
}
if err := osutils.PropagateEnv(); err != nil {
return errs.Wrap(err, "Sending OS signal to update environment failed.")
}
return nil
}
func (v *SubShell) CleanUserEnv(cfg sscommon.Configurable, envType sscommon.RcIdentification, userScope bool) error {
cmdEnv := NewCmdEnv(userScope)
// Clean up old entries
oldEnv := cfg.GetStringMap(envType.Key)
for k, v := range oldEnv {
val, ok := v.(string)
if !ok {
logging.Debug("Invalid non-string value in environment mapping")
continue
}
if err := cmdEnv.unset(k, val); err != nil {
return err
}
}
if err := osutils.PropagateEnv(); err != nil {
return errs.Wrap(err, "Sending OS signal to update environment failed.")
}
return nil
}
func (v *SubShell) RemoveLegacyInstallPath(_ sscommon.Configurable) error {
return nil
}
func (v *SubShell) WriteCompletionScript(completionScript string) error {
return locale.NewError("err_writecompletions_notsupported", "{{.V0}} does not support completions.", v.Shell())
}
func (v *SubShell) RcFile() (string, error) {
return "", locale.NewError("err_cmd_rcile", "cmd does not support RC files")
}
func (v *SubShell) EnsureRcFileExists() error {
// Windows does not use RC files
return nil
}
// SetupShellRcFile - subshell.SubShell
func (v *SubShell) SetupShellRcFile(targetDir string, env map[string]string, namespace *project.Namespaced, cfg sscommon.Configurable) error {
env = sscommon.EscapeEnv(env)
return sscommon.SetupShellRcFile(filepath.Join(targetDir, "shell.bat"), "config_global.bat", env, namespace, cfg)
}
// SetEnv - see subshell.SetEnv
func (v *SubShell) SetEnv(env map[string]string) error {
v.env = env
return nil
}
// Quote - see subshell.Quote
func (v *SubShell) Quote(value string) string {
return escaper.Quote(value)
}
// Activate - see subshell.SubShell
func (v *SubShell) Activate(prj *project.Project, cfg sscommon.Configurable, out output.Outputer) error {
var shellArgs []string
var directEnv []string
if prj != nil {
env := sscommon.EscapeEnv(v.env)
var err error
if v.rcFile, err = sscommon.SetupProjectRcFile(prj, "config.bat", ".bat", env, out, cfg, false); err != nil {
return err
}
shellArgs = append(shellArgs, "/K", v.rcFile.Name())
} else {
directEnv = sscommon.EnvSlice(v.env)
}
cmd := sscommon.NewCommand("cmd", shellArgs, directEnv)
v.errs = sscommon.Start(cmd)
v.cmd = cmd
return nil
}
// Errors returns a channel for receiving errors related to active behavior
func (v *SubShell) Errors() <-chan error {
return v.errs
}
// Deactivate - see subshell.SubShell
func (v *SubShell) Deactivate() error {
if !v.IsActive() {
return nil
}
if err := sscommon.Stop(v.cmd); err != nil {
return err
}
v.cmd = nil
return nil
}
// Run - see subshell.SubShell
func (v *SubShell) Run(filename string, args ...string) error {
return sscommon.RunFuncByBinary(v.Binary())(osutils.EnvMapToSlice(v.env), filename, args...)
}
// IsActive - see subshell.SubShell
func (v *SubShell) IsActive() bool {
return v.cmd != nil && (v.cmd.ProcessState == nil || !v.cmd.ProcessState.Exited())
}
func (v *SubShell) IsAvailable() bool {
return runtime.GOOS == "windows"
}