-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathmultienv_step_runner.go
155 lines (137 loc) · 3.27 KB
/
multienv_step_runner.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
package runtime
import (
"errors"
"fmt"
"strings"
"github.com/runatlantis/atlantis/server/core/config/valid"
"github.com/runatlantis/atlantis/server/events/command"
)
// EnvStepRunner set environment variables.
type MultiEnvStepRunner struct {
RunStepRunner *RunStepRunner
}
// Run runs the multienv step command.
// The command must return a json string containing the array of name-value pairs that are being added as extra environment variables
func (r *MultiEnvStepRunner) Run(
ctx command.ProjectContext,
shell *valid.CommandShell,
command string,
path string,
envs map[string]string,
postProcessOutput valid.PostProcessRunOutputOption,
) (string, error) {
res, err := r.RunStepRunner.Run(ctx, shell, command, path, envs, false, valid.PostProcessRunOutputShow)
if err != nil {
return "", err
}
var sb strings.Builder
if len(res) == 0 {
sb.WriteString("No dynamic environment variable added")
} else {
sb.WriteString("Dynamic environment variables added:\n")
vars, err := parseMultienvLine(res)
if err != nil {
return "", fmt.Errorf("Invalid environment variable definition: %s (%w)", res, err)
}
for i := 0; i < len(vars); i += 2 {
key := vars[i]
envs[key] = vars[i+1]
sb.WriteString(key)
sb.WriteRune('\n')
}
}
switch postProcessOutput {
case valid.PostProcessRunOutputHide:
return "", nil
case valid.PostProcessRunOutputShow:
return sb.String(), nil
default:
return sb.String(), nil
}
}
func parseMultienvLine(in string) ([]string, error) {
in = strings.TrimSpace(in)
if in == "" {
return nil, nil
}
if len(in) < 3 {
return nil, errors.New("invalid syntax") // TODO
}
var res []string
var inValue, dquoted, squoted, escaped bool
var i int
for j, r := range in {
if !inValue {
if r == '=' {
inValue = true
res = append(res, in[i:j])
i = j + 1
}
if r == ' ' || r == '\t' {
return nil, errInvalidKeySyntax
}
if r == ',' && len(res) > 0 {
i = j + 1
}
continue
}
if r == '"' && !squoted {
if j == i && !dquoted { // value is double quoted
dquoted = true
i = j + 1
} else if dquoted && in[j-1] != '\\' {
res = append(res, unescape(in[i:j], escaped))
i = j + 1
dquoted = false
inValue = false
} else if in[j-1] != '\\' {
return nil, errMisquoted
} else if in[j-1] == '\\' {
escaped = true
}
continue
}
if r == '\'' && !dquoted {
if j == i && !squoted { // value is double quoted
squoted = true
i = j + 1
} else if squoted && in[j-1] != '\\' {
res = append(res, in[i:j])
i = j + 1
squoted = false
inValue = false
}
continue
}
if r == ',' && !dquoted && !squoted && inValue {
res = append(res, in[i:j])
i = j + 1
inValue = false
}
}
if i < len(in) {
if !inValue {
return nil, errRemaining
}
res = append(res, unescape(in[i:], escaped))
inValue = false
}
if dquoted || squoted {
return nil, errMisquoted
}
if inValue {
return nil, errRemaining
}
return res, nil
}
func unescape(s string, escaped bool) string {
if escaped {
return strings.ReplaceAll(strings.ReplaceAll(s, `\\`, `\`), `\"`, `"`)
}
return s
}
var (
errInvalidKeySyntax = errors.New("invalid key syntax")
errMisquoted = errors.New("misquoted")
errRemaining = errors.New("remaining unparsed data")
)