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.
Files changed (25) hide show
  1. {envstack-0.7.1/lib/envstack.egg-info → envstack-0.7.2}/PKG-INFO +76 -11
  2. {envstack-0.7.1 → envstack-0.7.2}/README.md +75 -10
  3. {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/__init__.py +1 -1
  4. {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/cli.py +2 -3
  5. {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/env.py +36 -29
  6. {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/util.py +63 -4
  7. {envstack-0.7.1 → envstack-0.7.2/lib/envstack.egg-info}/PKG-INFO +76 -11
  8. {envstack-0.7.1 → envstack-0.7.2}/setup.py +1 -1
  9. {envstack-0.7.1 → envstack-0.7.2}/tests/test_cmds.py +5 -3
  10. {envstack-0.7.1 → envstack-0.7.2}/tests/test_util.py +2 -1
  11. {envstack-0.7.1 → envstack-0.7.2}/LICENSE +0 -0
  12. {envstack-0.7.1 → envstack-0.7.2}/dist.json +0 -0
  13. {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/config.py +0 -0
  14. {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/exceptions.py +0 -0
  15. {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/logger.py +0 -0
  16. {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/path.py +0 -0
  17. {envstack-0.7.1 → envstack-0.7.2}/lib/envstack/wrapper.py +0 -0
  18. {envstack-0.7.1 → envstack-0.7.2}/lib/envstack.egg-info/SOURCES.txt +0 -0
  19. {envstack-0.7.1 → envstack-0.7.2}/lib/envstack.egg-info/dependency_links.txt +0 -0
  20. {envstack-0.7.1 → envstack-0.7.2}/lib/envstack.egg-info/entry_points.txt +0 -0
  21. {envstack-0.7.1 → envstack-0.7.2}/lib/envstack.egg-info/not-zip-safe +0 -0
  22. {envstack-0.7.1 → envstack-0.7.2}/lib/envstack.egg-info/requires.txt +0 -0
  23. {envstack-0.7.1 → envstack-0.7.2}/lib/envstack.egg-info/top_level.txt +0 -0
  24. {envstack-0.7.1 → envstack-0.7.2}/setup.cfg +0 -0
  25. {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.1
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 namespaced .env file and
184
- declare some variables.
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
- For example `foo.env` (the stack name is "foo"):
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 `foo` environment stack, run:
249
+ To see the resolved environment for the `foobar` stack, run:
195
250
 
196
251
  ```bash
197
- $ envstack foo
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
- $ envstack foo -r
266
+ $ ./foobar.env -r
206
267
  FOO=bar
207
268
  BAR=bar
208
269
  ```
209
270
 
210
- Variables can be platform specific (but always inherit from `all`):
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 namespaced .env file and
161
- declare some variables.
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
- For example `foo.env` (the stack name is "foo"):
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 `foo` environment stack, run:
226
+ To see the resolved environment for the `foobar` stack, run:
172
227
 
173
228
  ```bash
174
- $ envstack foo
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
- $ envstack foo -r
243
+ $ ./foobar.env -r
183
244
  FOO=bar
184
245
  BAR=bar
185
246
  ```
186
247
 
187
- Variables can be platform specific (but always inherit from `all`):
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
 
@@ -34,6 +34,6 @@ Stacked environment variable management system.
34
34
  """
35
35
 
36
36
  __prog__ = "envstack"
37
- __version__ = "0.7.1"
37
+ __version__ = "0.7.2"
38
38
 
39
39
  from envstack.env import clear, init, revert, save
@@ -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
- for key in sorted(args.resolve):
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 depth exceeded" in str(err):
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
- clear_file_cache()
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
- restricted = ["PATH", "PS1", "PWD", "PROMPT"]
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 shell in ["bash", "sh", "zsh"]:
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 shell == "tcsh":
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 shell == "cmd":
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 shell == "pwsh":
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 shell == "unknown":
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 shell in ["bash", "sh", "zsh"]:
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 shell == "tcsh":
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 shell == "cmd":
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 shell == "pwsh":
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 shell == "unknown":
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 data
675
+ return {}
659
676
 
660
- import yaml
661
-
662
- with open(path) as stream:
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.1
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 namespaced .env file and
184
- declare some variables.
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
- For example `foo.env` (the stack name is "foo"):
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 `foo` environment stack, run:
249
+ To see the resolved environment for the `foobar` stack, run:
195
250
 
196
251
  ```bash
197
- $ envstack foo
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
- $ envstack foo -r
266
+ $ ./foobar.env -r
206
267
  FOO=bar
207
268
  BAR=bar
208
269
  ```
209
270
 
210
- Variables can be platform specific (but always inherit from `all`):
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.1",
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=/var/tmp/pipe
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
- expected_output = """CHAR_LIST=['a', 'b', 'c', 'goodbye']
212
- DEPLOY_ROOT=/var/tmp/pipe/prod
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