Skip to content

Commit 86d9ed7

Browse files
authored
Merge pull request #34 from nxtcoder17/feat/support-run-targets-with-env
Feat/support run targets with env [Github PR](#34)
2 parents 3968a84 + f50fb16 commit 86d9ed7

15 files changed

+296
-176
lines changed

Runfile.yml

+9-5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ tasks:
2727
env:
2828
pattern:
2929
default: ""
30+
only_failing:
31+
default: false
3032
watch:
3133
enable: true
3234
dir:
@@ -35,11 +37,13 @@ tasks:
3537
- .go
3638
cmd:
3739
- |+
38-
if [ -z "$pattern" ]; then
39-
go test -json ./parser/... | gotestfmt
40-
else
41-
go test -json ./parser/... -run "$pattern" | gotestfmt
42-
fi
40+
pattern_args=""
41+
[ -n "$pattern" ] && pattern_args="-run '$pattern'"
42+
43+
testfmt_args=""
44+
[ "$only_failing" = "true" ] && testfmt_args="--hide successful-tests"
45+
46+
go test -json ./parser/... $pattern_args | gotestfmt $testfmt_args
4347
4448
test:only-failing:
4549
cmd:

cmd/run/completions/run.fish

+7-28
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,14 @@
11
# run fish shell completion
22
set PROGNAME run
33

4-
function __runfile_list_targets --description 'fetches all runnable tasks'
5-
for i in (commandline -opc)
6-
if contains -- $i help h
7-
return 1
8-
end
9-
end
10-
11-
$PROGNAME --list 2>&1 | read -lz rawOutput
12-
13-
# RETURN on non-zero exit code (in case of errors)
14-
if test $status -ne 0
15-
return
16-
end
17-
18-
set -l output (echo $rawOutput | string split0)
19-
20-
if test $output
21-
if test "$DEBUG" = "true"
22-
echo "$output" > /tmp/test.txt
23-
end
24-
echo $output
25-
end
26-
27-
return 0
4+
# list_targets fetches all targets, with all flags provided on the cli
5+
function list_targets
6+
eval $PROGNAME --list
7+
# eval (commandline -b) --list
288
end
299

30-
complete -c $PROGNAME -d "runs target with given name" -xa "(__runfile_list_targets)"
10+
complete -c $PROGNAME -d "runs named task" -xa '(list_targets)'
11+
complete -c $PROGNAME -l help -s h -d 'show help'
12+
complete -c $PROGNAME -l list -s l -d 'list all tasks'
3113
complete -c $PROGNAME -rF -l file -s f -d 'runs targets from this runfile'
32-
# complete -c $PROGNAME -n '__runfile_list_targets' -f -l help -s h -d 'show help'
33-
# complete -c $PROGNAME -n '__runfile_list_targets' -f -l help -s h -d 'show help'
34-
# complete -r -c $PROGNAME -n '__runfile_list_targets' -a 'help h' -d 'Shows a list of commands or help for one command'
3514

cmd/run/main.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func main() {
8282
&cli.BoolFlag{
8383
Name: "list",
8484
Value: false,
85-
Aliases: []string{"ls"},
85+
Aliases: []string{"l"},
8686
},
8787

8888
&cli.BoolFlag{
@@ -112,13 +112,15 @@ func main() {
112112
Commands: []*cli.Command{
113113
{
114114
Name: "shell:completion",
115+
Usage: "<bash|zsh|fish|ps>",
115116
Suggest: true,
116117
Action: func(ctx context.Context, c *cli.Command) error {
117-
if c.NArg() != 2 {
118+
fmt.Printf("args: (%d)\n", c.NArg())
119+
if c.NArg() != 1 {
118120
return fmt.Errorf("needs argument one of [bash,zsh,fish,ps]")
119121
}
120122

121-
switch c.Args().Slice()[1] {
123+
switch c.Args().First() {
122124
case "fish":
123125
fmt.Fprint(c.Writer, shellCompletionFISH)
124126
case "bash":

errors/errors.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ func (e *Error) resolveTaskName() string {
4646

4747
// Error implements error.
4848
func (e *Error) Error() string {
49-
return e.err.Error()
49+
if e.err != nil {
50+
return e.err.Error()
51+
}
52+
return ""
5053
// return fmt.Sprintf("%v {%#v}", e.err, e.kv)
5154
}
5255

parser/parse-command.go

+13-6
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"github.com/nxtcoder17/runfile/types"
99
)
1010

11-
func parseCommand(prf *types.ParsedRunfile, command any) (*types.ParsedCommandJson, error) {
11+
func parseCommand(ctx types.Context, prf *types.ParsedRunfile, taskEnv map[string]string, command any) (*types.ParsedCommandJson, error) {
1212
ferr := func(err error) error {
1313
return errors.ErrTaskInvalidCommand.Wrap(err).KV("command", command)
1414
}
@@ -30,14 +30,25 @@ func parseCommand(prf *types.ParsedRunfile, command any) (*types.ParsedCommandJs
3030
return nil, ferr(err)
3131
}
3232

33+
parsedEnv, err := parseEnvVars(ctx, cj.Env, evaluationParams{
34+
Env: taskEnv,
35+
})
36+
if err != nil {
37+
return nil, ferr(err)
38+
}
39+
3340
pcj := types.ParsedCommandJson{
34-
Env: cj.Env,
41+
Env: parsedEnv,
3542
}
3643

3744
switch {
3845
case cj.Run != nil:
3946
{
4047
pcj.Run = cj.Run
48+
if _, ok := prf.Tasks[*cj.Run]; !ok {
49+
err := errors.ErrTaskNotFound.Wrap(fmt.Errorf("run target, not found")).KV("command", command, "run-target", cj.Run)
50+
return nil, err
51+
}
4152
}
4253
case cj.Command != nil:
4354
{
@@ -49,10 +60,6 @@ func parseCommand(prf *types.ParsedRunfile, command any) (*types.ParsedCommandJs
4960
}
5061
}
5162

52-
if _, ok := prf.Tasks[*cj.Run]; !ok {
53-
return nil, errors.ErrTaskNotFound.Wrap(fmt.Errorf("run target, not found")).KV("command", command, "run-target", cj.Run)
54-
}
55-
5663
return &pcj, nil
5764
}
5865
default:

parser/parse-command_test.go

+96-6
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,39 @@
11
package parser
22

33
import (
4+
"context"
5+
"fmt"
46
"reflect"
57
"testing"
68

9+
"github.com/nxtcoder17/go.pkgs/log"
10+
fn "github.com/nxtcoder17/runfile/functions"
711
"github.com/nxtcoder17/runfile/types"
812
)
913

14+
func testParseCommandJsonEqual(t *testing.T, got, want *types.ParsedCommandJson) {
15+
if got == nil && want != nil || got != nil && want == nil {
16+
t.Errorf("parseCommand(),\n[.command] \n\tgot = %v\n\twant = %v", got, want)
17+
return
18+
}
19+
20+
// t.Log("first", first, "err", err, "secondErr", secondErr, "condition", secondErr != (err != nil))
21+
22+
if !reflect.DeepEqual(got.Command, want.Command) {
23+
t.Errorf("parseCommand(),\n[.command] \n\tgot = %v\n\twant = %v", fn.DefaultIfNil(got.Command, ""), fn.DefaultIfNil(want.Command, ""))
24+
return
25+
}
26+
27+
if fmt.Sprint(got.Env) != fmt.Sprint(want.Env) {
28+
t.Errorf("parseCommand(),\n[.env] \n\tgot = %+v\n\twant = %+v", got.Env, want.Env)
29+
return
30+
}
31+
}
32+
1033
func Test_parseCommand(t *testing.T) {
1134
type args struct {
1235
prf *types.ParsedRunfile
36+
taskEnv map[string]string
1337
command any
1438
}
1539
tests := []struct {
@@ -18,18 +42,84 @@ func Test_parseCommand(t *testing.T) {
1842
want *types.ParsedCommandJson
1943
wantErr bool
2044
}{
21-
// TODO: Add test cases.
45+
{
46+
name: "1. must pass with only command",
47+
args: args{
48+
prf: &types.ParsedRunfile{},
49+
taskEnv: map[string]string{},
50+
command: "echo hi hello",
51+
},
52+
want: &types.ParsedCommandJson{
53+
Command: fn.New("echo hi hello"),
54+
Run: nil,
55+
Env: map[string]string{},
56+
If: nil,
57+
},
58+
wantErr: false,
59+
},
60+
{
61+
name: "2. must fail with only run command with run target not found",
62+
args: args{
63+
prf: &types.ParsedRunfile{},
64+
taskEnv: map[string]string{},
65+
command: map[string]any{
66+
"run": "build",
67+
},
68+
},
69+
want: nil,
70+
wantErr: true,
71+
},
72+
{
73+
name: "3. must pass with run command, and target exists in runfile tasks",
74+
args: args{
75+
prf: &types.ParsedRunfile{
76+
Tasks: map[string]types.Task{
77+
"build": {
78+
Commands: []any{
79+
"echo from build",
80+
},
81+
},
82+
},
83+
},
84+
taskEnv: map[string]string{},
85+
command: map[string]any{
86+
"run": "build",
87+
"env": map[string]string{
88+
"k1": "v1",
89+
},
90+
},
91+
},
92+
want: &types.ParsedCommandJson{
93+
Command: nil,
94+
Run: fn.New("build"),
95+
Env: map[string]string{
96+
"k1": "v1",
97+
},
98+
If: nil,
99+
},
100+
wantErr: false,
101+
},
22102
}
23-
for _, tt := range tests {
103+
104+
for i := range tests {
105+
tt := tests[i]
24106
t.Run(tt.name, func(t *testing.T) {
25-
got, err := parseCommand(tt.args.prf, tt.args.command)
26-
if (err != nil) != tt.wantErr {
107+
ctx := types.Context{
108+
Context: context.TODO(),
109+
Logger: log.New(),
110+
}
111+
112+
got, err := parseCommand(ctx, tt.args.prf, tt.args.taskEnv, tt.args.command)
113+
if tt.wantErr != (err != nil) {
27114
t.Errorf("parseCommand() error = %v, wantErr %v", err, tt.wantErr)
28115
return
29116
}
30-
if !reflect.DeepEqual(got, tt.want) {
31-
t.Errorf("parseCommand() = %v, want %v", got, tt.want)
117+
118+
if tt.wantErr {
119+
return
32120
}
121+
122+
testParseCommandJsonEqual(t, got, tt.want)
33123
})
34124
}
35125
}

parser/parse-env.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -102,18 +102,20 @@ func parseEnvVars(ctx types.Context, ev types.EnvVar, params evaluationParams) (
102102
case specials.Sh != nil:
103103
{
104104
*specials.Sh = strings.TrimSpace(*specials.Sh)
105-
value := new(bytes.Buffer)
106105
cmd := exec.CommandContext(ctx, "sh", "-c", *specials.Sh)
107106
cmd.Env = fn.ToEnviron(params.Env)
108-
cmd.Stdout = value
107+
108+
stdoutB := new(bytes.Buffer)
109+
cmd.Stdout = stdoutB
110+
109111
stderrB := new(bytes.Buffer)
110112
cmd.Stderr = stderrB
111113
if err := cmd.Run(); err != nil {
112114
return nil, errors.ErrEvalEnvVarSh.WithCtx(ctx).WrapStr(stderrB.String()).KV()
113115
// return nil, errors.ErrEvalEnvVarSh.WithCtx(ctx).KV(attr...)
114116
}
115117

116-
env[k] = strings.TrimSpace(value.String())
118+
env[k] = strings.TrimSpace(stdoutB.String())
117119
}
118120
default:
119121
{

parser/parse-task.go

+1-6
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func ParseTask(ctx types.Context, prf *types.ParsedRunfile, task types.Task) (*t
102102

103103
commands := make([]types.ParsedCommandJson, 0, len(task.Commands))
104104
for i := range task.Commands {
105-
c2, err := parseCommand(prf, task.Commands[i])
105+
c2, err := parseCommand(ctx, prf, taskEnv, task.Commands[i])
106106
if err != nil {
107107
return nil, err
108108
}
@@ -116,11 +116,6 @@ func ParseTask(ctx types.Context, prf *types.ParsedRunfile, task types.Task) (*t
116116
watch.Dirs[i] = filepath.Join(*task.Dir, watch.Dirs[i])
117117
}
118118
}
119-
// for i := range watch.ExcludeDirs {
120-
// if !isAbsPath(watch.ExcludeDirs[i]) {
121-
// watch.ExcludeDirs[i] = filepath.Join(*task.Dir, watch.ExcludeDirs[i])
122-
// }
123-
// }
124119
}
125120

126121
return &types.ParsedTask{

0 commit comments

Comments
 (0)