txgruppi / run

Compare 77ab6c8 ... +1 ... 78992f3

No flags found

Use flags to group coverage reports by test type, project and/or folders.
Then setup custom commit statuses and notifications for each flag.

e.g., #unittest #integration

#production #enterprise

#frontend #backend

Learn more about Codecov Flags here.

Showing 1 of 5 files from the diff.
Other files ignored by Codecov
go.mod has changed.
VERSION has changed.
README.md has changed.
go.sum has changed.

@@ -1,11 +1,13 @@
Loading
1 1
package cli
2 2
3 3
import (
4 +
	"bytes"
4 5
	"io/ioutil"
5 6
	"os"
6 7
	"os/exec"
7 8
	"time"
8 9
10 +
	"github.com/joho/godotenv"
9 11
	"github.com/txgruppi/run/build"
10 12
	"github.com/txgruppi/run/text"
11 13
	"github.com/txgruppi/run/valuesloader"
@@ -72,8 +74,24 @@
Loading
72 74
			Usage:  "The ARN or name of a secret with a JSON encoded value",
73 75
			EnvVar: "RUN_AWS_SECRET_ARN",
74 76
		},
77 +
		cli.StringFlag{
78 +
			Name:   "env-file",
79 +
			Usage:  "A dotenv file template to be rendered and added to the environment",
80 +
			EnvVar: "RUN_ENV_FILE",
81 +
		},
82 +
		cli.StringFlag{
83 +
			Name:   "env-output-var",
84 +
			Usage:  "Create a environment variable with the contents of the output file",
85 +
			EnvVar: "RUN_ENV_OUTPUT_VAR",
86 +
		},
75 87
	}
76 -
	app.Action = func(c *cli.Context) error {
88 +
	app.Action = func(c *cli.Context) (err error) {
89 +
		var envData []byte
90 +
		var envTokens []*text.Token
91 +
		var inputRender, envRender []byte
92 +
		var envSlice []string
93 +
		var vl *valuesloader.ValuesLoader
94 +
77 95
		input := c.String("input")
78 96
		output := c.String("output")
79 97
		delay := c.Int("delay")
@@ -82,71 +100,93 @@
Loading
82 100
			time.Sleep(time.Duration(delay) * time.Second)
83 101
		}
84 102
85 -
		if input != "" && output != "" {
86 -
			data, err := ioutil.ReadFile(input)
103 +
		envLoader, err := valuesloader.EnvironmentLoader()
104 +
		if err != nil {
105 +
			return newExitError(err, 4)
106 +
		}
107 +
		loaderFuncs := []valuesloader.ValueLoaderFunc{envLoader}
108 +
109 +
		if value := c.String("json"); value != "" {
110 +
			loader, err := valuesloader.JSONLoader([]byte(value))
87 111
			if err != nil {
88 -
				return newExitError(err, 1)
112 +
				return newExitError(err, 5)
89 113
			}
114 +
			loaderFuncs = append(loaderFuncs, loader)
115 +
		}
90 116
91 -
			envLoader, err := valuesloader.EnvironmentLoader()
117 +
		if value := c.String("remote-json"); value != "" {
118 +
			loader, err := valuesloader.RemoteJSONLoader(value)
92 119
			if err != nil {
93 -
				return newExitError(err, 4)
120 +
				return newExitError(err, 6)
94 121
			}
95 -
			loaderFuncs := []valuesloader.ValueLoaderFunc{envLoader}
122 +
			loaderFuncs = append(loaderFuncs, loader)
123 +
		}
96 124
97 -
			if value := c.String("json"); value != "" {
98 -
				loader, err := valuesloader.JSONLoader([]byte(value))
99 -
				if err != nil {
100 -
					return newExitError(err, 5)
101 -
				}
102 -
				loaderFuncs = append(loaderFuncs, loader)
125 +
		if value := c.String("json-file"); value != "" {
126 +
			loader, err := valuesloader.JSONFileLoader(value)
127 +
			if err != nil {
128 +
				return newExitError(err, 7)
103 129
			}
130 +
			loaderFuncs = append(loaderFuncs, loader)
131 +
		}
104 132
105 -
			if value := c.String("remote-json"); value != "" {
106 -
				loader, err := valuesloader.RemoteJSONLoader(value)
107 -
				if err != nil {
108 -
					return newExitError(err, 6)
109 -
				}
110 -
				loaderFuncs = append(loaderFuncs, loader)
133 +
		if value := c.String("aws-secret-arn"); value != "" {
134 +
			loader, err := valuesloader.AWSSecretsManagerLoader(value)
135 +
			if err != nil {
136 +
				return newExitError(err, 8)
111 137
			}
138 +
			loaderFuncs = append(loaderFuncs, loader)
139 +
		}
112 140
113 -
			if value := c.String("json-file"); value != "" {
114 -
				loader, err := valuesloader.JSONFileLoader(value)
115 -
				if err != nil {
116 -
					return newExitError(err, 7)
117 -
				}
118 -
				loaderFuncs = append(loaderFuncs, loader)
141 +
		vl, err = valuesloader.New(loaderFuncs...)
142 +
		if err != nil {
143 +
			return newExitError(err, 2)
144 +
		}
145 +
146 +
		if input != "" {
147 +
			inputData, err := ioutil.ReadFile(input)
148 +
			if err != nil {
149 +
				return newExitError(err, 1)
119 150
			}
120 151
121 -
			if value := c.String("aws-secret-arn"); value != "" {
122 -
				loader, err := valuesloader.AWSSecretsManagerLoader(value)
152 +
			inputTokens := text.Tokens(inputData)
153 +
			inputRender, err = render(inputData, inputTokens, vl)
154 +
			if err != nil {
155 +
				newExitError(err, 10)
156 +
			}
157 +
158 +
			if output != "" {
159 +
				err = ioutil.WriteFile(output, inputRender, 0777)
123 160
				if err != nil {
124 -
					return newExitError(err, 8)
161 +
					return newExitError(err, 3)
125 162
				}
126 -
				loaderFuncs = append(loaderFuncs, loader)
127 163
			}
164 +
		}
128 165
129 -
			vl, err := valuesloader.New(loaderFuncs...)
166 +
		if c.String("env-file") != "" {
167 +
			envData, err = ioutil.ReadFile(c.String("env-file"))
130 168
			if err != nil {
131 -
				return newExitError(err, 2)
169 +
				return newExitError(err, 9)
132 170
			}
133 171
134 -
			tokens := text.Tokens(data)
135 -
136 -
		TokensLoop:
137 -
			for _, token := range tokens {
138 -
				for _, key := range token.Keys {
139 -
					if value, ok := vl.Lookup(key); ok {
140 -
						data = text.Replace(data, token.Raw, value)
141 -
						continue TokensLoop
142 -
					}
143 -
				}
144 -
				data = text.Replace(data, token.Raw, "")
172 +
			envTokens = text.Tokens(envData)
173 +
			envRender, err = render(envData, envTokens, vl)
174 +
			if err != nil {
175 +
				newExitError(err, 11)
145 176
			}
146 177
147 -
			err = ioutil.WriteFile(output, data, 0777)
178 +
			envSlice, err = environ(envRender)
148 179
			if err != nil {
149 -
				return newExitError(err, 3)
180 +
				return newExitError(err, 12)
181 +
			}
182 +
		}
183 +
184 +
		if c.String("env-output-var") != "" && inputRender != nil {
185 +
			pair := c.String("env-output-var") + "=" + string(inputRender)
186 +
			if envSlice == nil {
187 +
				envSlice = []string{pair}
188 +
			} else {
189 +
				envSlice = append(envSlice, pair)
150 190
			}
151 191
		}
152 192
@@ -161,13 +201,48 @@
Loading
161 201
		cmd.Stdin = os.Stdin
162 202
		cmd.Stdout = app.Writer
163 203
		cmd.Stderr = cli.ErrWriter
204 +
		if envSlice != nil {
205 +
			cmd.Env = envSlice
206 +
		}
164 207
165 208
		return cmd.Run()
166 209
	}
167 210
168 211
	return app
169 212
}
170 213
214 +
func render(in []byte, tks []*text.Token, vl *valuesloader.ValuesLoader) ([]byte, error) {
215 +
	out := make([]byte, len(in))
216 +
	copy(out, in)
217 +
218 +
TokensLoop:
219 +
	for _, token := range tks {
220 +
		for _, key := range token.Keys {
221 +
			if value, ok := vl.Lookup(key); ok {
222 +
				out = text.Replace(out, token.Raw, value)
223 +
				continue TokensLoop
224 +
			}
225 +
		}
226 +
		out = text.Replace(out, token.Raw, "")
227 +
	}
228 +
	return out, nil
229 +
}
230 +
231 +
func environ(envData []byte) ([]string, error) {
232 +
	r := bytes.NewReader(envData)
233 +
	em, err := godotenv.Parse(r)
234 +
	if err != nil {
235 +
		return nil, err
236 +
	}
237 +
238 +
	out := os.Environ()
239 +
	for k, v := range em {
240 +
		out = append(out, k+"="+v)
241 +
	}
242 +
243 +
	return out, nil
244 +
}
245 +
171 246
func newExitError(err error, code int) error {
172 247
	return cli.NewExitError(err.Error(), code)
173 248
}

Everything is accounted for!

No changes detected that need to be reviewed.
What changes does Codecov check for?
Lines, not adjusted in diff, that have changed coverage data.
Files that introduced coverage data that had none before.
Files that have missing coverage data that once were tracked.
Files Coverage
text 92.31%
valuesloader 70.73%
cache/cache.go 100.00%
cli/app.go -13.49% 70.00%
Project Totals (6 files) 75.63%
Loading