1
1
mirror of https://github.com/mcuadros/ascode synced 2024-11-22 17:02:03 +01:00

starlark/module: os, added command function

This commit is contained in:
Máximo Cuadros 2020-04-09 04:25:52 +02:00
parent e2a245a645
commit 4d4f1058d3
No known key found for this signature in database
GPG Key ID: 17A5DFEDC735AE4B
5 changed files with 127 additions and 1 deletions

1
go.mod

@ -8,6 +8,7 @@ require (
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/go-git/go-git/v5 v5.0.0
github.com/gobs/args v0.0.0-20180315064131-86002b4df18c
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
github.com/hashicorp/go-hclog v0.11.0
github.com/hashicorp/go-plugin v1.0.1-0.20190610192547-a1bc61569a26

2
go.sum

@ -211,6 +211,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gobs/args v0.0.0-20180315064131-86002b4df18c h1:3r/O0iUDMwVJx8XCrjcUvfmfbVP3poiT+1dLyYzx8+w=
github.com/gobs/args v0.0.0-20180315064131-86002b4df18c/go.mod h1:ZpqkpUmnBz2Jz7hMGSPRbHtYC82FP/IZ1Y7A2riYH0s=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=

@ -1,10 +1,13 @@
package os
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"sync"
gobs "github.com/gobs/args"
"go.starlark.net/starlark"
"go.starlark.net/starlarkstruct"
)
@ -26,6 +29,7 @@ const (
removeAllFuncName = "remove_all"
renameFuncName = "rename"
tempDirFuncName = "temp_dir"
commandFuncName = "command"
)
var (
@ -57,6 +61,7 @@ func LoadModule() (starlark.StringDict, error) {
removeAllFuncName: starlark.NewBuiltin(mkdirFuncName, RemoveAll),
renameFuncName: starlark.NewBuiltin(renameFuncName, Rename),
tempDirFuncName: starlark.NewBuiltin(tempDirFuncName, TempDir),
commandFuncName: starlark.NewBuiltin(commandFuncName, Command),
},
},
}
@ -335,3 +340,97 @@ func Rename(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, k
func TempDir(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
return starlark.String(os.TempDir()), nil
}
// Command runs the command and returns its standard output.
//
// outline: os
// functions:
// command(command, shell?, dir?, combined?, env?)
// runs the command and returns its standard output. If the exit code
// it different to zero, an error is triggered.
// params:
// shell bool
// if True execute the command inside of a shell.
// dir string
// working directory of the command.
// combined bool
// if True returns combined standard output and standard error.
// env list
// specifies the environment of the process, each value of the list
// should follow the pattern "key=value".
func Command(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var (
command string
env *starlark.List
dir string
combined bool
shell bool
)
err := starlark.UnpackArgs(renameFuncName, args, kwargs,
"command", &command,
"env?", &env,
"dir?", &dir,
"combined?", &combined,
"shell?", &shell,
)
if err != nil {
return nil, err
}
if shell {
command = fmt.Sprintf("sh -c %q", command)
}
cmdArgs := gobs.GetArgs(command)
bin, err := exec.LookPath(cmdArgs[0])
if err != nil {
return nil, err
}
environment, err := unpackListArg(renameFuncName, "env", env)
if err != nil {
return nil, err
}
cmd := &exec.Cmd{
Path: bin,
Args: cmdArgs,
Env: append(os.Environ(), environment...),
Dir: dir,
}
var output []byte
if combined {
output, err = cmd.CombinedOutput()
} else {
output, err = cmd.Output()
}
if len(output) >= 1 && output[len(output)-1] == '\n' {
output = output[:len(output)-1]
}
return starlark.String(output), err
}
func unpackListArg(fnName, argName string, l *starlark.List) ([]string, error) {
if l == nil {
return []string{}, nil
}
output := make([]string, l.Len())
for i := 0; i < l.Len(); i++ {
s, ok := l.Index(i).(starlark.String)
if ok {
output[i] = s.GoString()
continue
}
return nil, fmt.Errorf("%s: parameter %q expected string at index %d", fnName, argName, i)
}
return output, nil
}

@ -17,6 +17,9 @@ func TestFile(t *testing.T) {
}
resolve.AllowFloat = true
resolve.AllowGlobalReassign = true
resolve.AllowLambda = true
thread := &starlark.Thread{Load: testdata.NewLoader(LoadModule, ModuleName)}
starlarktest.SetReporter(thread, t)

@ -34,3 +34,24 @@ os.remove_all("bar")
def deleteNotExistant(): os.remove("foo")
assert.fails(deleteNotExistant, "remove foo: no such file or directory")
# test command
temp = os.temp_dir() + "/example-dir"
os.mkdir_all(temp + "/foo/bar", 0o755)
os.chdir(temp)
assert.eq(os.command("ls -1"), "foo")
# test command dir
assert.eq(os.command("ls -1", dir="foo"), "bar")
# test command shell and env
assert.eq(os.command("echo $FOO", shell=True, env=["FOO=foo"]), "foo")
# test command combined
assert.ne(os.command("not-exists || true", shell=True, combined=True), "")
# test command error
assert.fails(lambda: os.command("not-exists"), "executable file not found")
os.remove_all(temp)