envstack 0.7.1__tar.gz → 0.7.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {envstack-0.7.1/lib/envstack.egg-info → envstack-0.7.2}/PKG-INFO +76 -11
- {envstack-0.7.1 → envstack-0.7.2}/README.md +75 -10
- {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/__init__.py +1 -1
- {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/cli.py +2 -3
- {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/env.py +36 -29
- {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/util.py +63 -4
- {envstack-0.7.1 → envstack-0.7.2/lib/envstack.egg-info}/PKG-INFO +76 -11
- {envstack-0.7.1 → envstack-0.7.2}/setup.py +1 -1
- {envstack-0.7.1 → envstack-0.7.2}/tests/test_cmds.py +5 -3
- {envstack-0.7.1 → envstack-0.7.2}/tests/test_util.py +2 -1
- {envstack-0.7.1 → envstack-0.7.2}/LICENSE +0 -0
- {envstack-0.7.1 → envstack-0.7.2}/dist.json +0 -0
- {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/config.py +0 -0
- {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/exceptions.py +0 -0
- {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/logger.py +0 -0
- {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/path.py +0 -0
- {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/wrapper.py +0 -0
- {envstack-0.7.1 → envstack-0.7.2}/lib/envstack.egg-info/SOURCES.txt +0 -0
- {envstack-0.7.1 → envstack-0.7.2}/lib/envstack.egg-info/dependency_links.txt +0 -0
- {envstack-0.7.1 → envstack-0.7.2}/lib/envstack.egg-info/entry_points.txt +0 -0
- {envstack-0.7.1 → envstack-0.7.2}/lib/envstack.egg-info/not-zip-safe +0 -0
- {envstack-0.7.1 → envstack-0.7.2}/lib/envstack.egg-info/requires.txt +0 -0
- {envstack-0.7.1 → envstack-0.7.2}/lib/envstack.egg-info/top_level.txt +0 -0
- {envstack-0.7.1 → envstack-0.7.2}/setup.cfg +0 -0
- {envstack-0.7.1 → envstack-0.7.2}/tests/test_env.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: envstack
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Stacked environment variable management system
|
|
5
5
|
Home-page: http://github.com/rsgalloway/envstack
|
|
6
6
|
Author: Ryan Galloway
|
|
@@ -34,6 +34,7 @@ Environment variable management system.
|
|
|
34
34
|
| Variable expansion modifiers | Supports bash-like variable expansion modifiers, allowing you to set default values for variables and override them in the environment or by higher scope stacks. |
|
|
35
35
|
| Platform-specific variables | Stacks can have platform-specific variables and values. This allows you to define different values for variables based on the platform. |
|
|
36
36
|
| Variable references | Variables can reference other variables, allowing for more flexibility and dynamic value assignment. |
|
|
37
|
+
| Multi-line values | Supports variables with multi-line values. |
|
|
37
38
|
| Includes | Stack files can include other stacks, making it easy to reuse and combine different stacks. |
|
|
38
39
|
| Python API | Provides a Python API that allows you to initialize and work with environment stacks programmatically. Easily initialize pre-defined environments with Python scripts, tools, and wrappers. |
|
|
39
40
|
| Running commands | Allows you to run command line executables inside an environment stack, providing a convenient way to execute commands with a pre-defined environment. |
|
|
@@ -127,6 +128,43 @@ scope to lower scope, left to right):
|
|
|
127
128
|
$ envstack [STACK [STACK ...]]
|
|
128
129
|
```
|
|
129
130
|
|
|
131
|
+
#### Executing Scripts
|
|
132
|
+
|
|
133
|
+
Environment stack files are also executable scripts that can be run directly:
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
$ ./env/test.env
|
|
138
|
+
DEPLOY_ROOT=${ROOT}/${STACK}
|
|
139
|
+
ENV=${STACK}
|
|
140
|
+
ENVPATH=${DEPLOY_ROOT}/env:${ROOT}/prod/env
|
|
141
|
+
HELLO=${HELLO:=world}
|
|
142
|
+
LOG_LEVEL=DEBUG
|
|
143
|
+
PATH=${DEPLOY_ROOT}/bin:${ROOT}/prod/bin:${PATH}
|
|
144
|
+
PYTHONPATH=${DEPLOY_ROOT}/lib/python:${ROOT}/prod/lib/python:${PYTHONPATH}
|
|
145
|
+
ROOT=/mnt/pipe
|
|
146
|
+
STACK=test
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Run commands inside a specific environment stack file:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
$ ./env/test.env -- <command>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
For example:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
$ ./env/hello.env -- echo {HELLO}
|
|
159
|
+
world
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Export a specific environment stack file:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
$ ./env/hello.env --export
|
|
166
|
+
```
|
|
167
|
+
|
|
130
168
|
## Setting Values
|
|
131
169
|
|
|
132
170
|
Envstack uses bash-like variable expansion modifiers. Setting `$VAR` to a fixed
|
|
@@ -180,40 +218,67 @@ goodbye
|
|
|
180
218
|
Several example or starter stacks are available in the [env folder of the
|
|
181
219
|
envstack repo](https://github.com/rsgalloway/envstack/tree/master/env).
|
|
182
220
|
|
|
183
|
-
To create a blank environment stack, create a new
|
|
184
|
-
|
|
221
|
+
To create a blank environment stack, create a new envstack file and declare some
|
|
222
|
+
variables.
|
|
223
|
+
|
|
224
|
+
Create a new stack file called "foobar.env" and make it executable (note: at
|
|
225
|
+
least one VAR needs to be defined under all):
|
|
185
226
|
|
|
186
|
-
|
|
227
|
+
#### foobar.env
|
|
187
228
|
|
|
188
229
|
```yaml
|
|
230
|
+
#!/usr/bin/env envstack
|
|
231
|
+
|
|
189
232
|
all: &default
|
|
190
233
|
FOO: bar
|
|
191
234
|
BAR: ${FOO}
|
|
235
|
+
darwin:
|
|
236
|
+
<<: *default
|
|
237
|
+
linux:
|
|
238
|
+
<<: *default
|
|
239
|
+
windows:
|
|
240
|
+
<<: *default
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Make it executable
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
$ chmod +x ./foobar.env
|
|
192
247
|
```
|
|
193
248
|
|
|
194
|
-
To see the resolved environment for the `
|
|
249
|
+
To see the resolved environment for the `foobar` stack, run:
|
|
195
250
|
|
|
196
251
|
```bash
|
|
197
|
-
$ envstack
|
|
252
|
+
$ envstack foobar
|
|
198
253
|
FOO=bar
|
|
199
254
|
BAR=$FOO
|
|
200
255
|
```
|
|
201
256
|
|
|
257
|
+
or execute it directly:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
$ ./foobar.env
|
|
261
|
+
```
|
|
262
|
+
|
|
202
263
|
To see resolved values:
|
|
203
264
|
|
|
204
265
|
```bash
|
|
205
|
-
$
|
|
266
|
+
$ ./foobar.env -r
|
|
206
267
|
FOO=bar
|
|
207
268
|
BAR=bar
|
|
208
269
|
```
|
|
209
270
|
|
|
210
|
-
|
|
271
|
+
#### More Details
|
|
272
|
+
|
|
273
|
+
Variables can be platform specific:
|
|
211
274
|
|
|
212
275
|
```yaml
|
|
276
|
+
darwin:
|
|
277
|
+
<<: *default
|
|
278
|
+
HELLO: olleh
|
|
213
279
|
linux:
|
|
214
280
|
<<: *default
|
|
215
281
|
HELLO: world
|
|
216
|
-
|
|
217
282
|
windows:
|
|
218
283
|
<<: *default
|
|
219
284
|
HELLO: goodbye
|
|
@@ -365,7 +430,7 @@ environment stack that sets a value for `${PYEXE}`:
|
|
|
365
430
|
#### hello.env
|
|
366
431
|
```yaml
|
|
367
432
|
all: &default
|
|
368
|
-
PYEXE: python
|
|
433
|
+
PYEXE: /usr/bin/python
|
|
369
434
|
```
|
|
370
435
|
|
|
371
436
|
#### bin/hello
|
|
@@ -434,7 +499,7 @@ The following environment variables are used to help manage functionality:
|
|
|
434
499
|
|------|-------------|
|
|
435
500
|
| ENVPATH | Colon-separated paths to search for stack files |
|
|
436
501
|
| IGNORE_MISSING | Ignore missing stack files when resolving environments |
|
|
437
|
-
| STACK | Name of the current stack |
|
|
502
|
+
| STACK | Name of the current environment stack |
|
|
438
503
|
|
|
439
504
|
# Tests
|
|
440
505
|
|
|
@@ -11,6 +11,7 @@ Environment variable management system.
|
|
|
11
11
|
| Variable expansion modifiers | Supports bash-like variable expansion modifiers, allowing you to set default values for variables and override them in the environment or by higher scope stacks. |
|
|
12
12
|
| Platform-specific variables | Stacks can have platform-specific variables and values. This allows you to define different values for variables based on the platform. |
|
|
13
13
|
| Variable references | Variables can reference other variables, allowing for more flexibility and dynamic value assignment. |
|
|
14
|
+
| Multi-line values | Supports variables with multi-line values. |
|
|
14
15
|
| Includes | Stack files can include other stacks, making it easy to reuse and combine different stacks. |
|
|
15
16
|
| Python API | Provides a Python API that allows you to initialize and work with environment stacks programmatically. Easily initialize pre-defined environments with Python scripts, tools, and wrappers. |
|
|
16
17
|
| Running commands | Allows you to run command line executables inside an environment stack, providing a convenient way to execute commands with a pre-defined environment. |
|
|
@@ -104,6 +105,43 @@ scope to lower scope, left to right):
|
|
|
104
105
|
$ envstack [STACK [STACK ...]]
|
|
105
106
|
```
|
|
106
107
|
|
|
108
|
+
#### Executing Scripts
|
|
109
|
+
|
|
110
|
+
Environment stack files are also executable scripts that can be run directly:
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
$ ./env/test.env
|
|
115
|
+
DEPLOY_ROOT=${ROOT}/${STACK}
|
|
116
|
+
ENV=${STACK}
|
|
117
|
+
ENVPATH=${DEPLOY_ROOT}/env:${ROOT}/prod/env
|
|
118
|
+
HELLO=${HELLO:=world}
|
|
119
|
+
LOG_LEVEL=DEBUG
|
|
120
|
+
PATH=${DEPLOY_ROOT}/bin:${ROOT}/prod/bin:${PATH}
|
|
121
|
+
PYTHONPATH=${DEPLOY_ROOT}/lib/python:${ROOT}/prod/lib/python:${PYTHONPATH}
|
|
122
|
+
ROOT=/mnt/pipe
|
|
123
|
+
STACK=test
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Run commands inside a specific environment stack file:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
$ ./env/test.env -- <command>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
For example:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
$ ./env/hello.env -- echo {HELLO}
|
|
136
|
+
world
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Export a specific environment stack file:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
$ ./env/hello.env --export
|
|
143
|
+
```
|
|
144
|
+
|
|
107
145
|
## Setting Values
|
|
108
146
|
|
|
109
147
|
Envstack uses bash-like variable expansion modifiers. Setting `$VAR` to a fixed
|
|
@@ -157,40 +195,67 @@ goodbye
|
|
|
157
195
|
Several example or starter stacks are available in the [env folder of the
|
|
158
196
|
envstack repo](https://github.com/rsgalloway/envstack/tree/master/env).
|
|
159
197
|
|
|
160
|
-
To create a blank environment stack, create a new
|
|
161
|
-
|
|
198
|
+
To create a blank environment stack, create a new envstack file and declare some
|
|
199
|
+
variables.
|
|
200
|
+
|
|
201
|
+
Create a new stack file called "foobar.env" and make it executable (note: at
|
|
202
|
+
least one VAR needs to be defined under all):
|
|
162
203
|
|
|
163
|
-
|
|
204
|
+
#### foobar.env
|
|
164
205
|
|
|
165
206
|
```yaml
|
|
207
|
+
#!/usr/bin/env envstack
|
|
208
|
+
|
|
166
209
|
all: &default
|
|
167
210
|
FOO: bar
|
|
168
211
|
BAR: ${FOO}
|
|
212
|
+
darwin:
|
|
213
|
+
<<: *default
|
|
214
|
+
linux:
|
|
215
|
+
<<: *default
|
|
216
|
+
windows:
|
|
217
|
+
<<: *default
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Make it executable
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
$ chmod +x ./foobar.env
|
|
169
224
|
```
|
|
170
225
|
|
|
171
|
-
To see the resolved environment for the `
|
|
226
|
+
To see the resolved environment for the `foobar` stack, run:
|
|
172
227
|
|
|
173
228
|
```bash
|
|
174
|
-
$ envstack
|
|
229
|
+
$ envstack foobar
|
|
175
230
|
FOO=bar
|
|
176
231
|
BAR=$FOO
|
|
177
232
|
```
|
|
178
233
|
|
|
234
|
+
or execute it directly:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
$ ./foobar.env
|
|
238
|
+
```
|
|
239
|
+
|
|
179
240
|
To see resolved values:
|
|
180
241
|
|
|
181
242
|
```bash
|
|
182
|
-
$
|
|
243
|
+
$ ./foobar.env -r
|
|
183
244
|
FOO=bar
|
|
184
245
|
BAR=bar
|
|
185
246
|
```
|
|
186
247
|
|
|
187
|
-
|
|
248
|
+
#### More Details
|
|
249
|
+
|
|
250
|
+
Variables can be platform specific:
|
|
188
251
|
|
|
189
252
|
```yaml
|
|
253
|
+
darwin:
|
|
254
|
+
<<: *default
|
|
255
|
+
HELLO: olleh
|
|
190
256
|
linux:
|
|
191
257
|
<<: *default
|
|
192
258
|
HELLO: world
|
|
193
|
-
|
|
194
259
|
windows:
|
|
195
260
|
<<: *default
|
|
196
261
|
HELLO: goodbye
|
|
@@ -342,7 +407,7 @@ environment stack that sets a value for `${PYEXE}`:
|
|
|
342
407
|
#### hello.env
|
|
343
408
|
```yaml
|
|
344
409
|
all: &default
|
|
345
|
-
PYEXE: python
|
|
410
|
+
PYEXE: /usr/bin/python
|
|
346
411
|
```
|
|
347
412
|
|
|
348
413
|
#### bin/hello
|
|
@@ -411,7 +476,7 @@ The following environment variables are used to help manage functionality:
|
|
|
411
476
|
|------|-------------|
|
|
412
477
|
| ENVPATH | Colon-separated paths to search for stack files |
|
|
413
478
|
| IGNORE_MISSING | Ignore missing stack files when resolving environments |
|
|
414
|
-
| STACK | Name of the current stack |
|
|
479
|
+
| STACK | Name of the current environment stack |
|
|
415
480
|
|
|
416
481
|
# Tests
|
|
417
482
|
|
|
@@ -145,12 +145,11 @@ def main():
|
|
|
145
145
|
if command:
|
|
146
146
|
return run_command(command, args.namespace)
|
|
147
147
|
elif args.resolve is not None:
|
|
148
|
-
if len(args.resolve) == 0:
|
|
149
|
-
args.resolve = load_environ(args.namespace).keys()
|
|
150
148
|
resolved = resolve_environ(
|
|
151
149
|
load_environ(args.namespace, platform=args.platform)
|
|
152
150
|
)
|
|
153
|
-
|
|
151
|
+
keys = args.resolve or resolved.keys()
|
|
152
|
+
for key in sorted(keys):
|
|
154
153
|
val = resolved.get(key)
|
|
155
154
|
print(f"{key}={val}")
|
|
156
155
|
elif args.trace is not None:
|
|
@@ -106,7 +106,7 @@ class EnvVar(string.Template, str):
|
|
|
106
106
|
try:
|
|
107
107
|
val = EnvVar(self.safe_substitute(env))
|
|
108
108
|
except RuntimeError as err:
|
|
109
|
-
if "maximum recursion
|
|
109
|
+
if "maximum recursion" in str(err):
|
|
110
110
|
raise CyclicalReference(self.template)
|
|
111
111
|
else:
|
|
112
112
|
raise InvalidSyntax(err)
|
|
@@ -287,7 +287,9 @@ def get_sources(
|
|
|
287
287
|
:raises TemplateNotFound: if a file is not found in ENVPATH or scope.
|
|
288
288
|
:returns: list of Source objects.
|
|
289
289
|
"""
|
|
290
|
-
|
|
290
|
+
|
|
291
|
+
# TODO: smarter file caching (issue #26)
|
|
292
|
+
# clear_file_cache()
|
|
291
293
|
|
|
292
294
|
# set default scope to the current working directory
|
|
293
295
|
scope = Path(scope or os.getcwd()).resolve()
|
|
@@ -396,39 +398,53 @@ def clear(
|
|
|
396
398
|
"""
|
|
397
399
|
|
|
398
400
|
env = load_environ(name, scope=scope)
|
|
401
|
+
|
|
402
|
+
# track the environment variables to export
|
|
399
403
|
export_list = list()
|
|
400
|
-
|
|
404
|
+
|
|
405
|
+
# restricted environment variables
|
|
406
|
+
restricted = [
|
|
407
|
+
"PATH",
|
|
408
|
+
"PYTHONPATH",
|
|
409
|
+
"ENVPATH",
|
|
410
|
+
"PS1",
|
|
411
|
+
"PWD",
|
|
412
|
+
"PROMPT",
|
|
413
|
+
]
|
|
414
|
+
|
|
415
|
+
# get the name of the shell
|
|
416
|
+
shell_name = os.path.basename(shell)
|
|
401
417
|
|
|
402
418
|
for key in env:
|
|
403
419
|
if key not in os.environ:
|
|
404
420
|
continue
|
|
405
421
|
old_key = f"_ES_OLD_{key}"
|
|
406
422
|
old_val = os.environ.get(old_key)
|
|
407
|
-
if
|
|
423
|
+
if shell_name in ["bash", "sh", "zsh"]:
|
|
408
424
|
if old_val:
|
|
409
425
|
export_list.append("export %s=%s" % (key, old_val))
|
|
410
426
|
export_list.append("unset %s" % (old_key))
|
|
411
427
|
elif key not in restricted:
|
|
412
428
|
export_list.append(f"unset {key}")
|
|
413
|
-
elif
|
|
429
|
+
elif shell_name == "tcsh":
|
|
414
430
|
if old_val:
|
|
415
431
|
export_list.append(f"setenv {key} {old_val}")
|
|
416
432
|
export_list.append(f"unsetenv {old_key}")
|
|
417
433
|
elif key not in restricted:
|
|
418
434
|
export_list.append(f"unsetenv {key}")
|
|
419
|
-
elif
|
|
435
|
+
elif shell_name == "cmd":
|
|
420
436
|
if old_val:
|
|
421
437
|
export_list.append(f"set {key}={old_val}")
|
|
422
438
|
export_list.append(f"set {old_key}=")
|
|
423
439
|
elif key not in restricted:
|
|
424
440
|
export_list.append(f"set {key}=")
|
|
425
|
-
elif
|
|
441
|
+
elif shell_name == "pwsh":
|
|
426
442
|
if old_val:
|
|
427
443
|
export_list.append(f"$env:{key}='{old_val}'")
|
|
428
444
|
export_list.append(f"Remove-Item Env:{old_key}")
|
|
429
445
|
elif key not in restricted:
|
|
430
446
|
export_list.append(f"Remove-Item Env:{key}")
|
|
431
|
-
elif
|
|
447
|
+
elif shell_name == "unknown":
|
|
432
448
|
raise Exception("unknown shell")
|
|
433
449
|
|
|
434
450
|
export_list.sort()
|
|
@@ -460,29 +476,32 @@ def export(
|
|
|
460
476
|
# track the environment variables to export
|
|
461
477
|
export_list = list()
|
|
462
478
|
|
|
479
|
+
# get the name of the shell
|
|
480
|
+
shell_name = os.path.basename(shell)
|
|
481
|
+
|
|
463
482
|
for key, val in resolved_env.items():
|
|
464
483
|
old_key = f"_ES_OLD_{key}"
|
|
465
484
|
old_val = os.environ.get(key)
|
|
466
485
|
if key == "PATH" and not val:
|
|
467
486
|
logger.log.warning("PATH is empty")
|
|
468
487
|
continue
|
|
469
|
-
if
|
|
488
|
+
if shell_name in ["bash", "sh", "zsh"]:
|
|
470
489
|
export_list.append(f"export {key}={val}")
|
|
471
490
|
if old_val:
|
|
472
491
|
export_list.append(f"export {old_key}={old_val}")
|
|
473
|
-
elif
|
|
492
|
+
elif shell_name == "tcsh":
|
|
474
493
|
export_list.append(f'setenv {key}:"{val}"')
|
|
475
494
|
if old_val:
|
|
476
495
|
export_list.append(f'setenv {old_key}:"{old_val}"')
|
|
477
|
-
elif
|
|
496
|
+
elif shell_name == "cmd":
|
|
478
497
|
export_list.append(f'set {key}="{val}"')
|
|
479
498
|
if old_val:
|
|
480
499
|
export_list.append(f'set {old_key}="{old_val}"')
|
|
481
|
-
elif
|
|
500
|
+
elif shell_name == "pwsh":
|
|
482
501
|
export_list.append(f'$env:{key}="{val}"')
|
|
483
502
|
if old_val:
|
|
484
503
|
export_list.append(f'$env:{old_key}="{old_val}"')
|
|
485
|
-
elif
|
|
504
|
+
elif shell_name == "unknown":
|
|
486
505
|
raise Exception("unknown shell")
|
|
487
506
|
|
|
488
507
|
export_list.sort()
|
|
@@ -652,24 +671,12 @@ def load_file(path: str):
|
|
|
652
671
|
if path in load_file_cache:
|
|
653
672
|
return load_file_cache[path]
|
|
654
673
|
|
|
655
|
-
data = {}
|
|
656
|
-
|
|
657
674
|
if not os.path.exists(path):
|
|
658
|
-
return
|
|
675
|
+
return {}
|
|
659
676
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
try:
|
|
664
|
-
data.update(yaml.safe_load(stream))
|
|
665
|
-
except (TypeError, yaml.YAMLError) as exc:
|
|
666
|
-
logger.log.error(exc)
|
|
667
|
-
raise InvalidSource(path)
|
|
668
|
-
except yaml.parser.ParserError as err:
|
|
669
|
-
logger.log.error(err)
|
|
670
|
-
raise InvalidSource(path)
|
|
671
|
-
|
|
672
|
-
load_file_cache[path] = data
|
|
677
|
+
else:
|
|
678
|
+
data = util.validate_yaml(path)
|
|
679
|
+
load_file_cache[path] = data
|
|
673
680
|
|
|
674
681
|
return data
|
|
675
682
|
|
|
@@ -160,15 +160,15 @@ def get_paths_from_var(
|
|
|
160
160
|
def get_stack_name(name: str = config.DEFAULT_NAMESPACE):
|
|
161
161
|
"""
|
|
162
162
|
Returns the stack name as a string. The stack name is always the last
|
|
163
|
-
element in the stack list.
|
|
163
|
+
element in the stack list or the basename of the envstack file.
|
|
164
164
|
|
|
165
165
|
:param name: The input name, can be a string, tuple, or list.
|
|
166
166
|
:return: The stack name as a string.
|
|
167
167
|
"""
|
|
168
|
+
if isinstance(name, (tuple, list)):
|
|
169
|
+
name = str(name[-1]) if name else config.DEFAULT_NAMESPACE
|
|
168
170
|
if isinstance(name, str):
|
|
169
|
-
return name
|
|
170
|
-
elif isinstance(name, (tuple, list)):
|
|
171
|
-
return str(name[-1]) if name else ""
|
|
171
|
+
return os.path.splitext(os.path.basename(name))[0]
|
|
172
172
|
else:
|
|
173
173
|
raise ValueError("Invalid input type. Expected string, tuple, or list.")
|
|
174
174
|
|
|
@@ -344,3 +344,62 @@ def findenv(var_name):
|
|
|
344
344
|
paths.add(path)
|
|
345
345
|
|
|
346
346
|
return sorted(list(paths))
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def print_error(file_path: str, e: Exception):
|
|
350
|
+
"""
|
|
351
|
+
Prints the problematic line and a few surrounding lines for context.
|
|
352
|
+
|
|
353
|
+
:param file_path: Path to the file.
|
|
354
|
+
:param e: The exception.
|
|
355
|
+
"""
|
|
356
|
+
try:
|
|
357
|
+
with open(file_path, "r") as file:
|
|
358
|
+
lines = file.readlines()
|
|
359
|
+
if hasattr(e, "problem_mark") and e.problem_mark:
|
|
360
|
+
line_num = e.problem_mark.line - 1
|
|
361
|
+
# problematic line and a few surrounding lines for context
|
|
362
|
+
start = max(0, line_num - 1)
|
|
363
|
+
end = min(len(lines), line_num + 2)
|
|
364
|
+
for i in range(start, end):
|
|
365
|
+
prefix = ">> " if i == line_num else " "
|
|
366
|
+
print(f"{prefix}{i + 1}: {lines[i].rstrip()}")
|
|
367
|
+
except Exception as ex:
|
|
368
|
+
print("read error:", ex)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def validate_yaml(file_path: str):
|
|
372
|
+
"""
|
|
373
|
+
Loads a YAML file and prints helpful error hints if invalid.
|
|
374
|
+
|
|
375
|
+
:param file_path: Path to the YAML file to validate.
|
|
376
|
+
"""
|
|
377
|
+
import yaml
|
|
378
|
+
|
|
379
|
+
required_keys = {"all", "darwin", "linux", "windows"}
|
|
380
|
+
|
|
381
|
+
try:
|
|
382
|
+
with open(file_path, "r") as stream:
|
|
383
|
+
data = yaml.safe_load(stream.read())
|
|
384
|
+
|
|
385
|
+
if not isinstance(data, dict):
|
|
386
|
+
raise yaml.YAMLError("invalid data structure")
|
|
387
|
+
|
|
388
|
+
missing_keys = required_keys - data.keys()
|
|
389
|
+
if missing_keys:
|
|
390
|
+
raise yaml.YAMLError(f"missing keys: {', '.join(sorted(missing_keys))}")
|
|
391
|
+
|
|
392
|
+
return data
|
|
393
|
+
|
|
394
|
+
except yaml.YAMLError as e:
|
|
395
|
+
if hasattr(e, "problem_mark") and e.problem_mark:
|
|
396
|
+
mark = e.problem_mark
|
|
397
|
+
print(f' File "{file_path}" line {mark.line}, column {mark.column}:')
|
|
398
|
+
print_error(file_path, e)
|
|
399
|
+
if hasattr(e, "problem") and e.problem:
|
|
400
|
+
print(f"SyntaxError: {e.problem}")
|
|
401
|
+
else:
|
|
402
|
+
print(f' File "{file_path}":')
|
|
403
|
+
print(f"SyntaxError: {e}")
|
|
404
|
+
|
|
405
|
+
return {}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: envstack
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Stacked environment variable management system
|
|
5
5
|
Home-page: http://github.com/rsgalloway/envstack
|
|
6
6
|
Author: Ryan Galloway
|
|
@@ -34,6 +34,7 @@ Environment variable management system.
|
|
|
34
34
|
| Variable expansion modifiers | Supports bash-like variable expansion modifiers, allowing you to set default values for variables and override them in the environment or by higher scope stacks. |
|
|
35
35
|
| Platform-specific variables | Stacks can have platform-specific variables and values. This allows you to define different values for variables based on the platform. |
|
|
36
36
|
| Variable references | Variables can reference other variables, allowing for more flexibility and dynamic value assignment. |
|
|
37
|
+
| Multi-line values | Supports variables with multi-line values. |
|
|
37
38
|
| Includes | Stack files can include other stacks, making it easy to reuse and combine different stacks. |
|
|
38
39
|
| Python API | Provides a Python API that allows you to initialize and work with environment stacks programmatically. Easily initialize pre-defined environments with Python scripts, tools, and wrappers. |
|
|
39
40
|
| Running commands | Allows you to run command line executables inside an environment stack, providing a convenient way to execute commands with a pre-defined environment. |
|
|
@@ -127,6 +128,43 @@ scope to lower scope, left to right):
|
|
|
127
128
|
$ envstack [STACK [STACK ...]]
|
|
128
129
|
```
|
|
129
130
|
|
|
131
|
+
#### Executing Scripts
|
|
132
|
+
|
|
133
|
+
Environment stack files are also executable scripts that can be run directly:
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
$ ./env/test.env
|
|
138
|
+
DEPLOY_ROOT=${ROOT}/${STACK}
|
|
139
|
+
ENV=${STACK}
|
|
140
|
+
ENVPATH=${DEPLOY_ROOT}/env:${ROOT}/prod/env
|
|
141
|
+
HELLO=${HELLO:=world}
|
|
142
|
+
LOG_LEVEL=DEBUG
|
|
143
|
+
PATH=${DEPLOY_ROOT}/bin:${ROOT}/prod/bin:${PATH}
|
|
144
|
+
PYTHONPATH=${DEPLOY_ROOT}/lib/python:${ROOT}/prod/lib/python:${PYTHONPATH}
|
|
145
|
+
ROOT=/mnt/pipe
|
|
146
|
+
STACK=test
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Run commands inside a specific environment stack file:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
$ ./env/test.env -- <command>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
For example:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
$ ./env/hello.env -- echo {HELLO}
|
|
159
|
+
world
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Export a specific environment stack file:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
$ ./env/hello.env --export
|
|
166
|
+
```
|
|
167
|
+
|
|
130
168
|
## Setting Values
|
|
131
169
|
|
|
132
170
|
Envstack uses bash-like variable expansion modifiers. Setting `$VAR` to a fixed
|
|
@@ -180,40 +218,67 @@ goodbye
|
|
|
180
218
|
Several example or starter stacks are available in the [env folder of the
|
|
181
219
|
envstack repo](https://github.com/rsgalloway/envstack/tree/master/env).
|
|
182
220
|
|
|
183
|
-
To create a blank environment stack, create a new
|
|
184
|
-
|
|
221
|
+
To create a blank environment stack, create a new envstack file and declare some
|
|
222
|
+
variables.
|
|
223
|
+
|
|
224
|
+
Create a new stack file called "foobar.env" and make it executable (note: at
|
|
225
|
+
least one VAR needs to be defined under all):
|
|
185
226
|
|
|
186
|
-
|
|
227
|
+
#### foobar.env
|
|
187
228
|
|
|
188
229
|
```yaml
|
|
230
|
+
#!/usr/bin/env envstack
|
|
231
|
+
|
|
189
232
|
all: &default
|
|
190
233
|
FOO: bar
|
|
191
234
|
BAR: ${FOO}
|
|
235
|
+
darwin:
|
|
236
|
+
<<: *default
|
|
237
|
+
linux:
|
|
238
|
+
<<: *default
|
|
239
|
+
windows:
|
|
240
|
+
<<: *default
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Make it executable
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
$ chmod +x ./foobar.env
|
|
192
247
|
```
|
|
193
248
|
|
|
194
|
-
To see the resolved environment for the `
|
|
249
|
+
To see the resolved environment for the `foobar` stack, run:
|
|
195
250
|
|
|
196
251
|
```bash
|
|
197
|
-
$ envstack
|
|
252
|
+
$ envstack foobar
|
|
198
253
|
FOO=bar
|
|
199
254
|
BAR=$FOO
|
|
200
255
|
```
|
|
201
256
|
|
|
257
|
+
or execute it directly:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
$ ./foobar.env
|
|
261
|
+
```
|
|
262
|
+
|
|
202
263
|
To see resolved values:
|
|
203
264
|
|
|
204
265
|
```bash
|
|
205
|
-
$
|
|
266
|
+
$ ./foobar.env -r
|
|
206
267
|
FOO=bar
|
|
207
268
|
BAR=bar
|
|
208
269
|
```
|
|
209
270
|
|
|
210
|
-
|
|
271
|
+
#### More Details
|
|
272
|
+
|
|
273
|
+
Variables can be platform specific:
|
|
211
274
|
|
|
212
275
|
```yaml
|
|
276
|
+
darwin:
|
|
277
|
+
<<: *default
|
|
278
|
+
HELLO: olleh
|
|
213
279
|
linux:
|
|
214
280
|
<<: *default
|
|
215
281
|
HELLO: world
|
|
216
|
-
|
|
217
282
|
windows:
|
|
218
283
|
<<: *default
|
|
219
284
|
HELLO: goodbye
|
|
@@ -365,7 +430,7 @@ environment stack that sets a value for `${PYEXE}`:
|
|
|
365
430
|
#### hello.env
|
|
366
431
|
```yaml
|
|
367
432
|
all: &default
|
|
368
|
-
PYEXE: python
|
|
433
|
+
PYEXE: /usr/bin/python
|
|
369
434
|
```
|
|
370
435
|
|
|
371
436
|
#### bin/hello
|
|
@@ -434,7 +499,7 @@ The following environment variables are used to help manage functionality:
|
|
|
434
499
|
|------|-------------|
|
|
435
500
|
| ENVPATH | Colon-separated paths to search for stack files |
|
|
436
501
|
| IGNORE_MISSING | Ignore missing stack files when resolving environments |
|
|
437
|
-
| STACK | Name of the current stack |
|
|
502
|
+
| STACK | Name of the current environment stack |
|
|
438
503
|
|
|
439
504
|
# Tests
|
|
440
505
|
|
|
@@ -40,7 +40,7 @@ with open(os.path.join(here, "README.md")) as f:
|
|
|
40
40
|
|
|
41
41
|
setup(
|
|
42
42
|
name="envstack",
|
|
43
|
-
version="0.7.
|
|
43
|
+
version="0.7.2",
|
|
44
44
|
description="Stacked environment variable management system",
|
|
45
45
|
long_description=long_description,
|
|
46
46
|
long_description_content_type="text/markdown",
|
|
@@ -123,7 +123,7 @@ LOG_LEVEL=${LOG_LEVEL:=INFO}
|
|
|
123
123
|
NUMBER_LIST=[1, 2, 3]
|
|
124
124
|
PATH=${DEPLOY_ROOT}/bin:${PATH}
|
|
125
125
|
PYTHONPATH=${DEPLOY_ROOT}/lib/python:${PYTHONPATH}
|
|
126
|
-
ROOT
|
|
126
|
+
ROOT=${HOME}/.local/pipe
|
|
127
127
|
STACK=thing
|
|
128
128
|
"""
|
|
129
129
|
command = "%s thing" % self.envstack_bin
|
|
@@ -208,8 +208,10 @@ STACK=foobar
|
|
|
208
208
|
self.assertEqual(output, expected_output)
|
|
209
209
|
|
|
210
210
|
def test_thing(self):
|
|
211
|
-
|
|
212
|
-
|
|
211
|
+
home = os.getenv("HOME")
|
|
212
|
+
deploy_root = f"{home}/.local/pipe/prod" # linux only for now
|
|
213
|
+
expected_output = f"""CHAR_LIST=['a', 'b', 'c', 'goodbye']
|
|
214
|
+
DEPLOY_ROOT={deploy_root}
|
|
213
215
|
HELLO=goodbye
|
|
214
216
|
"""
|
|
215
217
|
command = "%s thing -r DEPLOY_ROOT HELLO CHAR_LIST" % self.envstack_bin
|
|
@@ -36,6 +36,7 @@ Contains unit tests for the util.py module.
|
|
|
36
36
|
import os
|
|
37
37
|
import unittest
|
|
38
38
|
|
|
39
|
+
from envstack import config
|
|
39
40
|
from envstack.exceptions import CyclicalReference
|
|
40
41
|
from envstack.util import encode, evaluate_modifiers, get_stack_name, safe_eval
|
|
41
42
|
|
|
@@ -127,7 +128,7 @@ class TestUtils(unittest.TestCase):
|
|
|
127
128
|
def test_get_stack_name_empty(self):
|
|
128
129
|
name = []
|
|
129
130
|
result = get_stack_name(name)
|
|
130
|
-
self.assertEqual(result,
|
|
131
|
+
self.assertEqual(result, config.DEFAULT_NAMESPACE)
|
|
131
132
|
|
|
132
133
|
def test_get_stack_name_invalid_type(self):
|
|
133
134
|
name = 123
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|