-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathcmd.go
201 lines (171 loc) · 4.95 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
package myexec
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"github.com/httprunner/funplugin/fungo"
"github.com/pkg/errors"
)
var (
logger = fungo.Logger
PYPI_INDEX_URL = os.Getenv("PYPI_INDEX_URL")
PATH = os.Getenv("PATH")
)
var python3Executable string = "python3" // system default python3
func isPython3(python string) bool {
out, err := Command(python, "--version").Output()
if err != nil {
return false
}
if strings.HasPrefix(string(out), "Python 3") {
return true
}
return false
}
// EnsurePython3Venv ensures python3 venv with specified packages
// venv should be directory path of target venv
func EnsurePython3Venv(venv string, packages ...string) (python3 string, err error) {
// priority: specified > $HOME/.hrp/venv
if venv == "" {
home, err := os.UserHomeDir()
if err != nil {
return "", errors.Wrap(err, "get user home dir failed")
}
venv = filepath.Join(home, ".hrp", "venv")
}
python3, err = ensurePython3Venv(venv, packages...)
if err != nil {
return "", err
}
python3Executable = python3
logger.Info("set python3 executable path",
"Python3Executable", python3Executable)
return python3, nil
}
func ExecPython3Command(cmdName string, args ...string) error {
args = append([]string{"-m", cmdName}, args...)
return RunCommand(python3Executable, args...)
}
func AssertPythonPackage(python3 string, pkgName, pkgVersion string) error {
out, err := Command(
python3, "-c", fmt.Sprintf("\"import %s; print(%s.__version__)\"", pkgName, pkgName),
).Output()
if err != nil {
return fmt.Errorf("python package %s not found", pkgName)
}
// do not check version if pkgVersion is empty
if pkgVersion == "" {
logger.Info("python package is ready", "name", pkgName)
return nil
}
// check package version equality
version := strings.TrimSpace(string(out))
if strings.TrimLeft(version, "v") != strings.TrimLeft(pkgVersion, "v") {
return fmt.Errorf("python package %s version %s not matched, please upgrade to %s",
pkgName, version, pkgVersion)
}
logger.Info("python package is ready", "name", pkgName, "version", pkgVersion)
return nil
}
func InstallPythonPackage(python3 string, pkg string) (err error) {
var pkgName, pkgVersion string
if strings.Contains(pkg, "==") {
// specify package version
// funppy==0.5.0
pkgInfo := strings.Split(pkg, "==")
pkgName = pkgInfo[0]
pkgVersion = pkgInfo[1]
} else {
// package version not specified, install the latest by default
// funppy
pkgName = pkg
}
// check if package installed and version matched
err = AssertPythonPackage(python3, pkgName, pkgVersion)
if err == nil {
return nil
}
// check if pip available
err = RunCommand(python3, "-m", "pip", "--version")
if err != nil {
logger.Warn("pip is not available")
return errors.Wrap(err, "pip is not available")
}
logger.Info("installing python package", "pkgName",
pkgName, "pkgVersion", pkgVersion)
// install package
pypiIndexURL := PYPI_INDEX_URL
if pypiIndexURL == "" {
pypiIndexURL = "https://pypi.org/simple" // default
}
err = RunCommand(python3, "-m", "pip", "install", pkg, "--upgrade",
"--index-url", pypiIndexURL,
"--quiet", "--disable-pip-version-check")
if err != nil {
return errors.Wrap(err, "pip install package failed")
}
return AssertPythonPackage(python3, pkgName, pkgVersion)
}
func RunShell(shellString string) (exitCode int, err error) {
cmd := initShellExec(shellString)
logger.Info("exec shell string", "content", cmd.String())
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Start()
if err != nil {
return 1, errors.Wrap(err, "start running command failed")
}
// wait command done and get exit code
err = cmd.Wait()
if err != nil {
exitErr, ok := err.(*exec.ExitError)
if !ok {
return 1, errors.Wrap(err, "get command exit code failed")
}
// got failed command exit code
exitCode := exitErr.ExitCode()
logger.Error("exec command failed", "exitCode", exitCode, "error", err)
return exitCode, err
}
return 0, nil
}
func RunCommand(cmdName string, args ...string) error {
cmd := Command(cmdName, args...)
logger.Info("run command", "cmd", cmd.String())
// add cmd dir path to $PATH
if cmdDir := filepath.Dir(cmdName); cmdDir != "" {
var path string
if runtime.GOOS == "windows" {
path = fmt.Sprintf("%s;%s", cmdDir, PATH)
} else {
path = fmt.Sprintf("%s:%s", cmdDir, PATH)
}
if err := os.Setenv("PATH", path); err != nil {
logger.Error("set env $PATH failed", "error", err)
return err
}
}
_, err := RunShell(cmd.String())
return err
}
func ExecCommandInDir(cmd *exec.Cmd, dir string) error {
logger.Info("exec command", "cmd", cmd.String(), "dir", dir)
cmd.Dir = dir
// print stderr output
var stderr bytes.Buffer
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
stderrStr := stderr.String()
logger.Error("exec command failed",
"error", err, "stderr", stderrStr)
if stderrStr != "" {
err = errors.Wrap(err, stderrStr)
}
return err
}
return nil
}