envstack 0.9.2__tar.gz → 0.9.3__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.9.2/lib/envstack.egg-info → envstack-0.9.3}/PKG-INFO +9 -7
- {envstack-0.9.2 → envstack-0.9.3}/README.md +8 -6
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack/__init__.py +1 -1
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack/cli.py +23 -4
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack/encrypt.py +7 -7
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack/env.py +6 -2
- envstack-0.9.3/lib/envstack/envshell.py +124 -0
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack/node.py +1 -1
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack/util.py +0 -5
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack/wrapper.py +1 -1
- {envstack-0.9.2 → envstack-0.9.3/lib/envstack.egg-info}/PKG-INFO +9 -7
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack.egg-info/SOURCES.txt +1 -0
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack.egg-info/entry_points.txt +1 -0
- {envstack-0.9.2 → envstack-0.9.3}/setup.py +2 -1
- {envstack-0.9.2 → envstack-0.9.3}/tests/test_cmds.py +42 -30
- {envstack-0.9.2 → envstack-0.9.3}/tests/test_env.py +9 -9
- {envstack-0.9.2 → envstack-0.9.3}/tests/test_node.py +1 -1
- {envstack-0.9.2 → envstack-0.9.3}/LICENSE +0 -0
- {envstack-0.9.2 → envstack-0.9.3}/dist.json +0 -0
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack/config.py +0 -0
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack/exceptions.py +0 -0
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack/logger.py +0 -0
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack/path.py +0 -0
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack.egg-info/dependency_links.txt +0 -0
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack.egg-info/not-zip-safe +0 -0
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack.egg-info/requires.txt +0 -0
- {envstack-0.9.2 → envstack-0.9.3}/lib/envstack.egg-info/top_level.txt +0 -0
- {envstack-0.9.2 → envstack-0.9.3}/setup.cfg +0 -0
- {envstack-0.9.2 → envstack-0.9.3}/tests/test_encrypt.py +0 -0
- {envstack-0.9.2 → envstack-0.9.3}/tests/test_util.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: envstack
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.3
|
|
4
4
|
Summary: Stacked environment variable management system
|
|
5
5
|
Home-page: http://github.com/rsgalloway/envstack
|
|
6
6
|
Author: Ryan Galloway
|
|
@@ -112,7 +112,9 @@ STACK=default
|
|
|
112
112
|
If you are not seeing the above output, make sure the `default.env` stack file
|
|
113
113
|
is in `${ENVPATH}` or the current working directory.
|
|
114
114
|
|
|
115
|
-
> NOTE: The name of the current stack will always be stored in `${STACK}
|
|
115
|
+
> NOTE: The name of the current stack will always be stored in `${STACK}`.
|
|
116
|
+
|
|
117
|
+
ENV is the tier, STACK is the namespace.
|
|
116
118
|
|
|
117
119
|
Environments can be combined, or stacked, in order of priority (variables
|
|
118
120
|
defined in stacks flow from higher scope to lower scope, left to right):
|
|
@@ -371,21 +373,21 @@ Then use `keys.env` to encrypt any other environment files:
|
|
|
371
373
|
$ ./keys.env -- envstack -eo encrypted.env
|
|
372
374
|
```
|
|
373
375
|
|
|
374
|
-
To decrypt,
|
|
376
|
+
To decrypt, run the command inside the `keys` environment again:
|
|
375
377
|
|
|
376
378
|
```bash
|
|
377
|
-
$ envstack
|
|
379
|
+
$ ./keys.env -- envstack encrypted -r HELLO
|
|
378
380
|
HELLO=world
|
|
379
381
|
```
|
|
380
382
|
|
|
381
|
-
Or
|
|
383
|
+
Or add `keys` to the env stack:
|
|
382
384
|
|
|
383
385
|
```bash
|
|
384
|
-
$
|
|
386
|
+
$ envstack keys encrypted -r HELLO
|
|
385
387
|
HELLO=world
|
|
386
388
|
```
|
|
387
389
|
|
|
388
|
-
Or include `keys
|
|
390
|
+
Or automatically include `keys`:
|
|
389
391
|
|
|
390
392
|
```yaml
|
|
391
393
|
include: [keys]
|
|
@@ -88,7 +88,9 @@ STACK=default
|
|
|
88
88
|
If you are not seeing the above output, make sure the `default.env` stack file
|
|
89
89
|
is in `${ENVPATH}` or the current working directory.
|
|
90
90
|
|
|
91
|
-
> NOTE: The name of the current stack will always be stored in `${STACK}
|
|
91
|
+
> NOTE: The name of the current stack will always be stored in `${STACK}`.
|
|
92
|
+
|
|
93
|
+
ENV is the tier, STACK is the namespace.
|
|
92
94
|
|
|
93
95
|
Environments can be combined, or stacked, in order of priority (variables
|
|
94
96
|
defined in stacks flow from higher scope to lower scope, left to right):
|
|
@@ -347,21 +349,21 @@ Then use `keys.env` to encrypt any other environment files:
|
|
|
347
349
|
$ ./keys.env -- envstack -eo encrypted.env
|
|
348
350
|
```
|
|
349
351
|
|
|
350
|
-
To decrypt,
|
|
352
|
+
To decrypt, run the command inside the `keys` environment again:
|
|
351
353
|
|
|
352
354
|
```bash
|
|
353
|
-
$ envstack
|
|
355
|
+
$ ./keys.env -- envstack encrypted -r HELLO
|
|
354
356
|
HELLO=world
|
|
355
357
|
```
|
|
356
358
|
|
|
357
|
-
Or
|
|
359
|
+
Or add `keys` to the env stack:
|
|
358
360
|
|
|
359
361
|
```bash
|
|
360
|
-
$
|
|
362
|
+
$ envstack keys encrypted -r HELLO
|
|
361
363
|
HELLO=world
|
|
362
364
|
```
|
|
363
365
|
|
|
364
|
-
Or include `keys
|
|
366
|
+
Or automatically include `keys`:
|
|
365
367
|
|
|
366
368
|
```yaml
|
|
367
369
|
include: [keys]
|
|
@@ -37,6 +37,7 @@ import argparse
|
|
|
37
37
|
import re
|
|
38
38
|
import sys
|
|
39
39
|
import traceback
|
|
40
|
+
from typing import List
|
|
40
41
|
|
|
41
42
|
from envstack import __version__, config
|
|
42
43
|
from envstack.env import (
|
|
@@ -147,20 +148,24 @@ def parse_args():
|
|
|
147
148
|
action="version",
|
|
148
149
|
version=f"envstack {__version__}",
|
|
149
150
|
)
|
|
150
|
-
|
|
151
|
-
group.add_argument(
|
|
151
|
+
parser.add_argument(
|
|
152
152
|
"namespace",
|
|
153
153
|
metavar="STACK",
|
|
154
154
|
nargs="*",
|
|
155
155
|
default=[config.DEFAULT_NAMESPACE],
|
|
156
156
|
help="the environment stacks to use",
|
|
157
157
|
)
|
|
158
|
-
|
|
158
|
+
parser.add_argument(
|
|
159
159
|
"-b",
|
|
160
160
|
"--bare",
|
|
161
161
|
action="store_true",
|
|
162
162
|
help="create a bare environment",
|
|
163
163
|
)
|
|
164
|
+
parser.add_argument(
|
|
165
|
+
"--shell",
|
|
166
|
+
action="store_true",
|
|
167
|
+
help="drop into a shell with the environment loaded",
|
|
168
|
+
)
|
|
164
169
|
encrypt_group = parser.add_argument_group("encryption options")
|
|
165
170
|
encrypt_group.add_argument(
|
|
166
171
|
"-e",
|
|
@@ -254,9 +259,20 @@ def parse_args():
|
|
|
254
259
|
return args, args_after_dash
|
|
255
260
|
|
|
256
261
|
|
|
262
|
+
def envshell(namespace: List[str] = None):
|
|
263
|
+
"""Run a shell in the given environment stack."""
|
|
264
|
+
from .envshell import EnvshellWrapper
|
|
265
|
+
|
|
266
|
+
print("\U0001F680 Launching envshell... CTRL+D to exit")
|
|
267
|
+
|
|
268
|
+
name = (namespace or [config.DEFAULT_NAMESPACE])[:]
|
|
269
|
+
envshell = EnvshellWrapper(name)
|
|
270
|
+
return envshell.launch()
|
|
271
|
+
|
|
272
|
+
|
|
257
273
|
def whichenv():
|
|
258
274
|
"""Entry point for the whichenv command line tool. Finds {VAR}s."""
|
|
259
|
-
from
|
|
275
|
+
from .util import findenv
|
|
260
276
|
|
|
261
277
|
if len(sys.argv) != 2:
|
|
262
278
|
print("Usage: whichenv [VAR]")
|
|
@@ -276,6 +292,9 @@ def main():
|
|
|
276
292
|
if command:
|
|
277
293
|
return run_command(command, args.namespace)
|
|
278
294
|
|
|
295
|
+
elif args.shell:
|
|
296
|
+
return envshell(args.namespace)
|
|
297
|
+
|
|
279
298
|
elif args.keygen:
|
|
280
299
|
from envstack.encrypt import generate_keys
|
|
281
300
|
|
|
@@ -43,6 +43,7 @@ from envstack.logger import log
|
|
|
43
43
|
|
|
44
44
|
# cryptography and _rust dependency may not be available everywhere
|
|
45
45
|
# ImportError: DLL load failed while importing _rust: Module not found.
|
|
46
|
+
Fernet = None
|
|
46
47
|
try:
|
|
47
48
|
import cryptography.exceptions
|
|
48
49
|
from cryptography.fernet import Fernet, InvalidToken
|
|
@@ -50,7 +51,6 @@ try:
|
|
|
50
51
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
|
51
52
|
except ImportError as err:
|
|
52
53
|
log.debug("cryptography module not available: %s", err)
|
|
53
|
-
Fernet = None
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
class Base64Encryptor(object):
|
|
@@ -78,7 +78,7 @@ class FernetEncryptor(object):
|
|
|
78
78
|
KEY_VAR_NAME = "ENVSTACK_FERNET_KEY"
|
|
79
79
|
|
|
80
80
|
def __init__(self, key: str = None, env: dict = os.environ):
|
|
81
|
-
if key:
|
|
81
|
+
if key and Fernet:
|
|
82
82
|
self.key = Fernet(key)
|
|
83
83
|
else:
|
|
84
84
|
self.key = self.get_key(env)
|
|
@@ -90,7 +90,8 @@ class FernetEncryptor(object):
|
|
|
90
90
|
key = Fernet.generate_key()
|
|
91
91
|
return key.decode()
|
|
92
92
|
else:
|
|
93
|
-
log.
|
|
93
|
+
log.debug("Fernet encryption not available")
|
|
94
|
+
return ""
|
|
94
95
|
|
|
95
96
|
def get_key(self, env: dict = os.environ):
|
|
96
97
|
"""Load the encryption key from the environment `env`.
|
|
@@ -99,7 +100,7 @@ class FernetEncryptor(object):
|
|
|
99
100
|
:return: encryption key.
|
|
100
101
|
"""
|
|
101
102
|
key = env.get(self.KEY_VAR_NAME)
|
|
102
|
-
if key:
|
|
103
|
+
if key and Fernet:
|
|
103
104
|
return Fernet(key)
|
|
104
105
|
return key
|
|
105
106
|
|
|
@@ -313,12 +314,11 @@ def generate_keys():
|
|
|
313
314
|
|
|
314
315
|
:returns: Dictionary containing Fernet and AES-GCM keys.
|
|
315
316
|
"""
|
|
316
|
-
from envstack.node import Base64Node
|
|
317
317
|
|
|
318
318
|
symmetric_key = AESGCMEncryptor.generate_key()
|
|
319
319
|
fernet_key = FernetEncryptor.generate_key()
|
|
320
320
|
|
|
321
321
|
return {
|
|
322
|
-
AESGCMEncryptor.KEY_VAR_NAME:
|
|
323
|
-
FernetEncryptor.KEY_VAR_NAME:
|
|
322
|
+
AESGCMEncryptor.KEY_VAR_NAME: symmetric_key,
|
|
323
|
+
FernetEncryptor.KEY_VAR_NAME: fernet_key,
|
|
324
324
|
}
|
|
@@ -36,6 +36,7 @@ Contains functions and classes for processing scoped .env files.
|
|
|
36
36
|
import os
|
|
37
37
|
import re
|
|
38
38
|
import string
|
|
39
|
+
from collections import defaultdict
|
|
39
40
|
from pathlib import Path
|
|
40
41
|
|
|
41
42
|
import yaml # noqa
|
|
@@ -77,7 +78,7 @@ class Source(object):
|
|
|
77
78
|
:param path: path to .env file.
|
|
78
79
|
"""
|
|
79
80
|
self.path = path
|
|
80
|
-
self.data =
|
|
81
|
+
self.data = defaultdict(dict)
|
|
81
82
|
|
|
82
83
|
def __eq__(self, other):
|
|
83
84
|
if not isinstance(other, Source):
|
|
@@ -126,7 +127,10 @@ class Source(object):
|
|
|
126
127
|
|
|
127
128
|
def write(self, filepath: str = None):
|
|
128
129
|
"""Writes the source data to the .env file."""
|
|
129
|
-
|
|
130
|
+
try:
|
|
131
|
+
util.dump_yaml(filepath or self.path, self.data)
|
|
132
|
+
except Exception as err:
|
|
133
|
+
logger.log.exception("Failed to write %s: %s", filepath or self.path, err)
|
|
130
134
|
|
|
131
135
|
|
|
132
136
|
class EnvVar(string.Template, str):
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2024-2025, Ryan Galloway (ryan@rsgalloway.com)
|
|
4
|
+
#
|
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
|
7
|
+
#
|
|
8
|
+
# - Redistributions of source code must retain the above copyright notice,
|
|
9
|
+
# this list of conditions and the following disclaimer.
|
|
10
|
+
#
|
|
11
|
+
# - Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
# and/or other materials provided with the distribution.
|
|
14
|
+
#
|
|
15
|
+
# - Neither the name of the software nor the names of its contributors
|
|
16
|
+
# may be used to endorse or promote products derived from this software
|
|
17
|
+
# without specific prior written permission.
|
|
18
|
+
#
|
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
22
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
23
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
24
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
25
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
26
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
27
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
28
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
29
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
30
|
+
#
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
Contains envshell wrapper class.
|
|
34
|
+
|
|
35
|
+
Goal: drop the user into an interactive shell *without* sourcing their usual
|
|
36
|
+
shell rc files, so prompt vars (PS1/PROMPT) set by envstack can survive.
|
|
37
|
+
|
|
38
|
+
Notes:
|
|
39
|
+
- You can override the detected shell with ENVSTACK_SHELL.
|
|
40
|
+
Examples:
|
|
41
|
+
ENVSTACK_SHELL=/bin/zsh envstack --shell
|
|
42
|
+
ENVSTACK_SHELL=pwsh envstack --shell
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
import os
|
|
46
|
+
from pathlib import Path
|
|
47
|
+
from typing import List
|
|
48
|
+
|
|
49
|
+
from . import config
|
|
50
|
+
from .wrapper import Wrapper
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _basename(p: str) -> str:
|
|
54
|
+
"""Return the lowercase basename of a path, robustly."""
|
|
55
|
+
try:
|
|
56
|
+
return Path(p).name.lower()
|
|
57
|
+
except Exception:
|
|
58
|
+
return os.path.basename(p).lower()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _detect_shell_argv() -> List[str]:
|
|
62
|
+
"""
|
|
63
|
+
Return argv list for a *clean* interactive shell.
|
|
64
|
+
"""
|
|
65
|
+
shell = os.environ.get("ENVSTACK_SHELL", config.SHELL)
|
|
66
|
+
|
|
67
|
+
if os.name == "nt":
|
|
68
|
+
# Prefer COMSPEC if it looks like cmd.exe; otherwise allow pwsh/powershell.
|
|
69
|
+
comspec = os.environ.get("COMSPEC", "cmd.exe")
|
|
70
|
+
|
|
71
|
+
# If user explicitly asked for pwsh/powershell, honor it.
|
|
72
|
+
base = _basename(shell)
|
|
73
|
+
if base in ("pwsh", "pwsh.exe"):
|
|
74
|
+
return [shell, "-NoExit", "-NoProfile"]
|
|
75
|
+
if base in ("powershell", "powershell.exe"):
|
|
76
|
+
return [shell, "-NoExit", "-NoProfile"]
|
|
77
|
+
|
|
78
|
+
# Otherwise use cmd.exe, with /K to keep it open.
|
|
79
|
+
# (Even if config.SHELL returned "cmd", use COMSPEC so we get the real path.)
|
|
80
|
+
return [comspec, "/K"]
|
|
81
|
+
|
|
82
|
+
# POSIX shells
|
|
83
|
+
base = _basename(shell)
|
|
84
|
+
|
|
85
|
+
# bash: skip /etc/profile, ~/.bash_profile, ~/.bashrc, but stay interactive
|
|
86
|
+
if base == "bash":
|
|
87
|
+
return [shell, "--noprofile", "--norc", "-i"]
|
|
88
|
+
|
|
89
|
+
# zsh: -f skips zshrcs; -i for interactive
|
|
90
|
+
if base == "zsh":
|
|
91
|
+
return [shell, "-f", "-i"]
|
|
92
|
+
|
|
93
|
+
# tcsh/csh: -f skips rc; interactive by default when attached to a tty
|
|
94
|
+
if base in ("tcsh", "csh"):
|
|
95
|
+
return [shell, "-f"]
|
|
96
|
+
|
|
97
|
+
# fish: --no-config skips config.fish; interactive by default
|
|
98
|
+
if base == "fish":
|
|
99
|
+
return [shell, "--no-config"]
|
|
100
|
+
|
|
101
|
+
# Fallback: try interactive flag if common; otherwise just exec the shell
|
|
102
|
+
# (Most shells become interactive when connected to a tty anyway.)
|
|
103
|
+
return [shell, "-i"]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class EnvshellWrapper(Wrapper):
|
|
107
|
+
"""A wrapper that spawns an interactive shell with the environment set."""
|
|
108
|
+
|
|
109
|
+
def __init__(self, *args, **kwargs):
|
|
110
|
+
super(EnvshellWrapper, self).__init__(*args, **kwargs)
|
|
111
|
+
self.shell = False # exec the shell directly
|
|
112
|
+
|
|
113
|
+
def executable(self):
|
|
114
|
+
"""
|
|
115
|
+
Kept for interface compatibility. The actual argv is produced in
|
|
116
|
+
get_subprocess_command().
|
|
117
|
+
"""
|
|
118
|
+
return ""
|
|
119
|
+
|
|
120
|
+
def get_subprocess_command(self, env):
|
|
121
|
+
"""
|
|
122
|
+
Override to return argv list for subprocess.Popen(..., shell=False).
|
|
123
|
+
"""
|
|
124
|
+
return _detect_shell_argv()
|
|
@@ -328,7 +328,7 @@ class CustomDumper(yaml.SafeDumper):
|
|
|
328
328
|
node.style = '"%s"' % node.value
|
|
329
329
|
elif node.value and node.value[-1] == ":":
|
|
330
330
|
node.style = '"%s"' % node.value
|
|
331
|
-
elif ": " in node.value:
|
|
331
|
+
elif ": " in str(node.value):
|
|
332
332
|
node.style = '"%s"' % node.value
|
|
333
333
|
|
|
334
334
|
def quote_vars(self, node: yaml.Node):
|
|
@@ -604,17 +604,12 @@ def validate_yaml(file_path: str):
|
|
|
604
604
|
|
|
605
605
|
:param file_path: Path to the YAML file to validate.
|
|
606
606
|
"""
|
|
607
|
-
required_keys = {"all", "darwin", "linux", "windows"}
|
|
608
|
-
|
|
609
607
|
try:
|
|
610
608
|
with open(file_path, "r") as stream:
|
|
611
609
|
data = yaml.safe_load(stream.read())
|
|
612
610
|
# data = yaml.load(stream.read(), Loader=CustomLoader)
|
|
613
611
|
if not isinstance(data, dict):
|
|
614
612
|
raise yaml.YAMLError("invalid data structure")
|
|
615
|
-
missing_keys = required_keys - data.keys()
|
|
616
|
-
if missing_keys:
|
|
617
|
-
raise yaml.YAMLError(f"missing keys: {', '.join(sorted(missing_keys))}")
|
|
618
613
|
return data
|
|
619
614
|
except OSError as e:
|
|
620
615
|
print(e)
|
|
@@ -128,7 +128,7 @@ class Wrapper(object):
|
|
|
128
128
|
traceback.print_exc()
|
|
129
129
|
exitcode = 1
|
|
130
130
|
else:
|
|
131
|
-
stdout, stderr = process.communicate()
|
|
131
|
+
stdout, stderr = process.communicate() # dead code, kept for reference
|
|
132
132
|
while stdout and stderr:
|
|
133
133
|
self.log.info(stdout)
|
|
134
134
|
self.log.error(stderr)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: envstack
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.3
|
|
4
4
|
Summary: Stacked environment variable management system
|
|
5
5
|
Home-page: http://github.com/rsgalloway/envstack
|
|
6
6
|
Author: Ryan Galloway
|
|
@@ -112,7 +112,9 @@ STACK=default
|
|
|
112
112
|
If you are not seeing the above output, make sure the `default.env` stack file
|
|
113
113
|
is in `${ENVPATH}` or the current working directory.
|
|
114
114
|
|
|
115
|
-
> NOTE: The name of the current stack will always be stored in `${STACK}
|
|
115
|
+
> NOTE: The name of the current stack will always be stored in `${STACK}`.
|
|
116
|
+
|
|
117
|
+
ENV is the tier, STACK is the namespace.
|
|
116
118
|
|
|
117
119
|
Environments can be combined, or stacked, in order of priority (variables
|
|
118
120
|
defined in stacks flow from higher scope to lower scope, left to right):
|
|
@@ -371,21 +373,21 @@ Then use `keys.env` to encrypt any other environment files:
|
|
|
371
373
|
$ ./keys.env -- envstack -eo encrypted.env
|
|
372
374
|
```
|
|
373
375
|
|
|
374
|
-
To decrypt,
|
|
376
|
+
To decrypt, run the command inside the `keys` environment again:
|
|
375
377
|
|
|
376
378
|
```bash
|
|
377
|
-
$ envstack
|
|
379
|
+
$ ./keys.env -- envstack encrypted -r HELLO
|
|
378
380
|
HELLO=world
|
|
379
381
|
```
|
|
380
382
|
|
|
381
|
-
Or
|
|
383
|
+
Or add `keys` to the env stack:
|
|
382
384
|
|
|
383
385
|
```bash
|
|
384
|
-
$
|
|
386
|
+
$ envstack keys encrypted -r HELLO
|
|
385
387
|
HELLO=world
|
|
386
388
|
```
|
|
387
389
|
|
|
388
|
-
Or include `keys
|
|
390
|
+
Or automatically include `keys`:
|
|
389
391
|
|
|
390
392
|
```yaml
|
|
391
393
|
include: [keys]
|
|
@@ -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.9.
|
|
43
|
+
version="0.9.3",
|
|
44
44
|
description="Stacked environment variable management system",
|
|
45
45
|
long_description=long_description,
|
|
46
46
|
long_description_content_type="text/markdown",
|
|
@@ -69,6 +69,7 @@ setup(
|
|
|
69
69
|
entry_points={
|
|
70
70
|
"console_scripts": [
|
|
71
71
|
"envstack=envstack.cli:main",
|
|
72
|
+
"envshell=envstack.cli:envshell",
|
|
72
73
|
"whichenv=envstack.cli:whichenv",
|
|
73
74
|
],
|
|
74
75
|
},
|
|
@@ -73,7 +73,7 @@ class TestUnresolved(unittest.TestCase):
|
|
|
73
73
|
envpath = os.path.join(os.path.dirname(__file__), "..", "env")
|
|
74
74
|
self.root = {
|
|
75
75
|
"linux": "/mnt/pipe",
|
|
76
|
-
"win32": "
|
|
76
|
+
"win32": "//tools/pipe",
|
|
77
77
|
"darwin": "/Volumes/pipe",
|
|
78
78
|
}.get(sys.platform)
|
|
79
79
|
os.environ["ENVPATH"] = envpath
|
|
@@ -86,6 +86,7 @@ ENVPATH=${DEPLOY_ROOT}/env:${ENVPATH}
|
|
|
86
86
|
HELLO=${HELLO:=world}
|
|
87
87
|
LOG_LEVEL=${LOG_LEVEL:=INFO}
|
|
88
88
|
PATH=${DEPLOY_ROOT}/bin:${PATH}
|
|
89
|
+
PS1=\[\e[32m\](${ENV})\[\e[0m\] \w\$
|
|
89
90
|
PYTHONPATH=${DEPLOY_ROOT}/lib/python:${PYTHONPATH}
|
|
90
91
|
ROOT=%s
|
|
91
92
|
STACK=default
|
|
@@ -104,6 +105,7 @@ ENVPATH=${ROOT}/dev/env:${ROOT}/prod/env:${ENVPATH}
|
|
|
104
105
|
HELLO=${HELLO:=world}
|
|
105
106
|
LOG_LEVEL=DEBUG
|
|
106
107
|
PATH=${ROOT}/dev/bin:${ROOT}/prod/bin:${PATH}
|
|
108
|
+
PS1=\[\e[32m\](${ENV})\[\e[0m\] \w\$
|
|
107
109
|
PYTHONPATH=${ROOT}/dev/lib/python:${ROOT}/prod/lib/python:${PYTHONPATH}
|
|
108
110
|
ROOT=%s
|
|
109
111
|
STACK=dev
|
|
@@ -122,6 +124,7 @@ ENVPATH=${DEPLOY_ROOT}/env:${ENVPATH}
|
|
|
122
124
|
HELLO=${HELLO:=world}
|
|
123
125
|
LOG_LEVEL=INFO
|
|
124
126
|
PATH=${DEPLOY_ROOT}/bin:${PATH}
|
|
127
|
+
PS1=\[\e[32m\](${ENV})\[\e[0m\] \w\$
|
|
125
128
|
PYTHONPATH=${DEPLOY_ROOT}/lib/python:${PYTHONPATH}
|
|
126
129
|
ROOT=%s
|
|
127
130
|
STACK=distman
|
|
@@ -140,6 +143,7 @@ ENVPATH=${ROOT}/dev/env:${ROOT}/prod/env:${ENVPATH}
|
|
|
140
143
|
HELLO=${HELLO:=world}
|
|
141
144
|
LOG_LEVEL=${LOG_LEVEL:=INFO}
|
|
142
145
|
PATH=${ROOT}/dev/bin:${ROOT}/prod/bin:${PATH}
|
|
146
|
+
PS1=\[\e[33m\](${STACK})\[\e[0m\] \w\$
|
|
143
147
|
PYEXE=/usr/bin/python
|
|
144
148
|
PYTHONPATH=${ROOT}/dev/lib/python:${ROOT}/prod/lib/python:${PYTHONPATH}
|
|
145
149
|
ROOT=%s
|
|
@@ -163,6 +167,7 @@ INT=5
|
|
|
163
167
|
LOG_LEVEL=${LOG_LEVEL:=INFO}
|
|
164
168
|
NUMBER_LIST=[1, 2, 3]
|
|
165
169
|
PATH=${DEPLOY_ROOT}/bin:${PATH}
|
|
170
|
+
PS1=\[\e[32m\](${ENV})\[\e[0m\] \w\$
|
|
166
171
|
PYTHONPATH=${DEPLOY_ROOT}/lib/python:${PYTHONPATH}
|
|
167
172
|
ROOT=${HOME}/.local/pipe
|
|
168
173
|
STACK=thing
|
|
@@ -182,7 +187,7 @@ class TestEncrypt(unittest.TestCase):
|
|
|
182
187
|
envpath = os.path.join(os.path.dirname(__file__), "..", "env")
|
|
183
188
|
self.root = {
|
|
184
189
|
"linux": "/mnt/pipe",
|
|
185
|
-
"win32": "
|
|
190
|
+
"win32": "//tools/pipe",
|
|
186
191
|
"darwin": "/Volumes/pipe",
|
|
187
192
|
}.get(sys.platform)
|
|
188
193
|
os.environ["ENVPATH"] = envpath
|
|
@@ -203,6 +208,7 @@ ENVPATH=JHtERVBMT1lfUk9PVH0vZW52OiR7RU5WUEFUSH0=
|
|
|
203
208
|
HELLO=JHtIRUxMTzo9d29ybGR9
|
|
204
209
|
LOG_LEVEL=JHtMT0dfTEVWRUw6PUlORk99
|
|
205
210
|
PATH=JHtERVBMT1lfUk9PVH0vYmluOiR7UEFUSH0=
|
|
211
|
+
PS1=XFtcZVszMm1cXSgke0VOVn0pXFtcZVswbVxdIFx3XCQg
|
|
206
212
|
PYTHONPATH=JHtERVBMT1lfUk9PVH0vbGliL3B5dGhvbjoke1BZVEhPTlBBVEh9
|
|
207
213
|
ROOT=L21udC9waXBl
|
|
208
214
|
STACK=ZGVmYXVsdA==
|
|
@@ -251,6 +257,7 @@ ENVPATH=JHtST09UfS9kZXYvZW52OiR7Uk9PVH0vcHJvZC9lbnY6JHtFTlZQQVRIfQ==
|
|
|
251
257
|
HELLO=JHtIRUxMTzo9d29ybGR9
|
|
252
258
|
LOG_LEVEL=REVCVUc=
|
|
253
259
|
PATH=JHtST09UfS9kZXYvYmluOiR7Uk9PVH0vcHJvZC9iaW46JHtQQVRIfQ==
|
|
260
|
+
PS1=XFtcZVszMm1cXSgke0VOVn0pXFtcZVswbVxdIFx3XCQg
|
|
254
261
|
PYTHONPATH=JHtST09UfS9kZXYvbGliL3B5dGhvbjoke1JPT1R9L3Byb2QvbGliL3B5dGhvbjoke1BZVEhPTlBBVEh9
|
|
255
262
|
ROOT=L21udC9waXBl
|
|
256
263
|
STACK=ZGV2
|
|
@@ -277,7 +284,7 @@ class TestResolved(unittest.TestCase):
|
|
|
277
284
|
envpath = os.path.join(os.path.dirname(__file__), "..", "env")
|
|
278
285
|
self.root = {
|
|
279
286
|
"linux": "/mnt/pipe",
|
|
280
|
-
"win32": "
|
|
287
|
+
"win32": "//tools/pipe",
|
|
281
288
|
"darwin": "/Volumes/pipe",
|
|
282
289
|
}.get(sys.platform)
|
|
283
290
|
os.environ["ENVPATH"] = envpath
|
|
@@ -321,14 +328,14 @@ STACK=distman
|
|
|
321
328
|
output = subprocess.check_output(command, shell=True, universal_newlines=True)
|
|
322
329
|
self.assertEqual(output, expected_output)
|
|
323
330
|
|
|
324
|
-
def
|
|
325
|
-
expected_output = f"""DEPLOY_ROOT={self.root}/
|
|
331
|
+
def test_project(self):
|
|
332
|
+
expected_output = f"""DEPLOY_ROOT={self.root}/project
|
|
326
333
|
HELLO=world
|
|
327
334
|
ROOT={self.root}
|
|
328
|
-
STACK=
|
|
335
|
+
STACK=project
|
|
329
336
|
"""
|
|
330
337
|
command = (
|
|
331
|
-
"ENV=blah ROOT=/var/tmp %s
|
|
338
|
+
"ENV=blah ROOT=/var/tmp %s project -r DEPLOY_ROOT HELLO ROOT STACK"
|
|
332
339
|
% self.envstack_bin
|
|
333
340
|
)
|
|
334
341
|
output = subprocess.check_output(command, shell=True, universal_newlines=True)
|
|
@@ -341,7 +348,7 @@ ROOT={self.root}
|
|
|
341
348
|
STACK=foobar
|
|
342
349
|
"""
|
|
343
350
|
command = (
|
|
344
|
-
"ENV=blah ROOT=/var/tmp %s
|
|
351
|
+
"ENV=blah ROOT=/var/tmp %s project foobar -r DEPLOY_ROOT HELLO ROOT STACK"
|
|
345
352
|
% self.envstack_bin
|
|
346
353
|
)
|
|
347
354
|
output = subprocess.check_output(command, shell=True, universal_newlines=True)
|
|
@@ -372,7 +379,7 @@ class TestBake(unittest.TestCase):
|
|
|
372
379
|
envpath = os.path.join(os.path.dirname(__file__), "..", "env")
|
|
373
380
|
self.root = {
|
|
374
381
|
"linux": "/mnt/pipe",
|
|
375
|
-
"win32": "
|
|
382
|
+
"win32": "//tools/pipe",
|
|
376
383
|
"darwin": "/Volumes/pipe",
|
|
377
384
|
}.get(sys.platform)
|
|
378
385
|
os.environ["ENVPATH"] = envpath
|
|
@@ -401,13 +408,16 @@ all: &all
|
|
|
401
408
|
PYTHONPATH: ${DEPLOY_ROOT}/lib/python:${PYTHONPATH}
|
|
402
409
|
darwin:
|
|
403
410
|
<<: *all
|
|
411
|
+
PS1: \[\e[32m\](${ENV})\[\e[0m\] \w\$
|
|
404
412
|
ROOT: /Volumes/pipe
|
|
405
413
|
linux:
|
|
406
414
|
<<: *all
|
|
415
|
+
PS1: \[\e[32m\](${ENV})\[\e[0m\] \w\$
|
|
407
416
|
ROOT: /mnt/pipe
|
|
408
417
|
windows:
|
|
409
418
|
<<: *all
|
|
410
|
-
|
|
419
|
+
PROMPT: $E[32m(${ENV})$E[0m $P$G
|
|
420
|
+
ROOT: //tools/pipe
|
|
411
421
|
"""
|
|
412
422
|
output = subprocess.check_output(
|
|
413
423
|
command,
|
|
@@ -418,7 +428,7 @@ windows:
|
|
|
418
428
|
|
|
419
429
|
def test_dev(self):
|
|
420
430
|
"""Tests baking the dev stack."""
|
|
421
|
-
command = make_command(self.envstack_bin, self.filename, "dev", "-d 1")
|
|
431
|
+
command = make_command(self.envstack_bin, self.filename, "dev", "-d", "1")
|
|
422
432
|
expected_output = """#!/usr/bin/env envstack
|
|
423
433
|
include: [default]
|
|
424
434
|
all: &all
|
|
@@ -489,13 +499,16 @@ all: &all
|
|
|
489
499
|
PYTHONPATH: !encrypt JHtERVBMT1lfUk9PVH0vbGliL3B5dGhvbjoke1BZVEhPTlBBVEh9
|
|
490
500
|
darwin:
|
|
491
501
|
<<: *all
|
|
502
|
+
PS1: !encrypt XFtcZVszMm1cXSgke0VOVn0pXFtcZVswbVxdIFx3XCQg
|
|
492
503
|
ROOT: !encrypt L1ZvbHVtZXMvcGlwZQ==
|
|
493
504
|
linux:
|
|
494
505
|
<<: *all
|
|
506
|
+
PS1: !encrypt XFtcZVszMm1cXSgke0VOVn0pXFtcZVswbVxdIFx3XCQg
|
|
495
507
|
ROOT: !encrypt L21udC9waXBl
|
|
496
508
|
windows:
|
|
497
509
|
<<: *all
|
|
498
|
-
|
|
510
|
+
PROMPT: !encrypt JEVbMzJtKCR7RU5WfSkkRVswbSAkUCRH
|
|
511
|
+
ROOT: !encrypt Ly90b29scy9waXBl
|
|
499
512
|
"""
|
|
500
513
|
output = subprocess.check_output(
|
|
501
514
|
command,
|
|
@@ -568,7 +581,7 @@ class TestCommands(unittest.TestCase):
|
|
|
568
581
|
envpath = os.path.join(os.path.dirname(__file__), "..", "env")
|
|
569
582
|
self.root = {
|
|
570
583
|
"linux": "/mnt/pipe",
|
|
571
|
-
"win32": "
|
|
584
|
+
"win32": "//tools/pipe",
|
|
572
585
|
"darwin": "/Volumes/pipe",
|
|
573
586
|
}.get(sys.platform)
|
|
574
587
|
os.environ["ENVPATH"] = envpath
|
|
@@ -602,18 +615,18 @@ class TestCommands(unittest.TestCase):
|
|
|
602
615
|
)
|
|
603
616
|
self.assertEqual(output, expected_output)
|
|
604
617
|
|
|
605
|
-
def
|
|
606
|
-
"""Tests the
|
|
607
|
-
command = "%s
|
|
608
|
-
expected_output = f"{self.root}/
|
|
618
|
+
def test_project_echo_deploy_root(self):
|
|
619
|
+
"""Tests the project stack with an echo command."""
|
|
620
|
+
command = "%s project -- echo {DEPLOY_ROOT}" % self.envstack_bin
|
|
621
|
+
expected_output = f"{self.root}/project\n"
|
|
609
622
|
output = subprocess.check_output(
|
|
610
623
|
command, start_new_session=True, shell=True, universal_newlines=True
|
|
611
624
|
)
|
|
612
625
|
self.assertEqual(output, expected_output)
|
|
613
626
|
|
|
614
|
-
def
|
|
615
|
-
"""Tests the
|
|
616
|
-
command = "%s
|
|
627
|
+
def test_project_echo_deploy_root_foobar(self):
|
|
628
|
+
"""Tests the project stack with an echo command."""
|
|
629
|
+
command = "%s project foobar -- echo {DEPLOY_ROOT}" % self.envstack_bin
|
|
617
630
|
expected_output = f"{self.root}/foobar\n"
|
|
618
631
|
output = subprocess.check_output(
|
|
619
632
|
command, start_new_session=True, shell=True, universal_newlines=True
|
|
@@ -632,7 +645,7 @@ class TestSet(unittest.TestCase):
|
|
|
632
645
|
envpath = os.path.join(os.path.dirname(__file__), "..", "env")
|
|
633
646
|
self.root = {
|
|
634
647
|
"linux": "/mnt/pipe",
|
|
635
|
-
"win32": "
|
|
648
|
+
"win32": "//tools/pipe",
|
|
636
649
|
"darwin": "/Volumes/pipe",
|
|
637
650
|
}.get(sys.platform)
|
|
638
651
|
os.environ["ENVPATH"] = envpath
|
|
@@ -799,7 +812,7 @@ class TestDistman(unittest.TestCase):
|
|
|
799
812
|
envpath = os.path.join(os.path.dirname(__file__), "..", "env")
|
|
800
813
|
self.root = {
|
|
801
814
|
"linux": "/mnt/pipe",
|
|
802
|
-
"win32": "
|
|
815
|
+
"win32": "//tools/pipe",
|
|
803
816
|
"darwin": "/Volumes/pipe",
|
|
804
817
|
}.get(sys.platform)
|
|
805
818
|
os.environ["ENVPATH"] = envpath
|
|
@@ -823,15 +836,15 @@ class TestDistman(unittest.TestCase):
|
|
|
823
836
|
self.assertEqual(output, expected_output)
|
|
824
837
|
|
|
825
838
|
def test_test_deploy_root(self):
|
|
826
|
-
command = "ENV=invalid %s
|
|
827
|
-
expected_output = f"{self.root}/
|
|
839
|
+
command = "ENV=invalid %s project -- %s" % (self.envstack_bin, self.python_cmd)
|
|
840
|
+
expected_output = f"{self.root}/project\n"
|
|
828
841
|
output = subprocess.check_output(
|
|
829
842
|
command, start_new_session=True, shell=True, universal_newlines=True
|
|
830
843
|
)
|
|
831
844
|
self.assertEqual(output, expected_output)
|
|
832
845
|
|
|
833
846
|
def test_foobar_deploy_root(self):
|
|
834
|
-
command = "ENV=invalid %s
|
|
847
|
+
command = "ENV=invalid %s project foobar -- %s" % (
|
|
835
848
|
self.envstack_bin,
|
|
836
849
|
self.python_cmd,
|
|
837
850
|
)
|
|
@@ -922,7 +935,7 @@ class TestIssues(unittest.TestCase):
|
|
|
922
935
|
env/dev.env:
|
|
923
936
|
ENVPATH: ${ROOT}/dev/env:${ROOT}/prod/env:${ENVPATH}
|
|
924
937
|
|
|
925
|
-
env/
|
|
938
|
+
env/project.env:
|
|
926
939
|
ENVPATH: ${ROOT}/${STACK}/env:${ROOT}/prod/env
|
|
927
940
|
"""
|
|
928
941
|
from envstack.env import Env
|
|
@@ -950,11 +963,10 @@ class TestIssues(unittest.TestCase):
|
|
|
950
963
|
)
|
|
951
964
|
self.assertEqual(output, expected_output)
|
|
952
965
|
|
|
953
|
-
#
|
|
954
|
-
command = "%s
|
|
966
|
+
# project.env does use STACK in ENVPATH and should include our test env
|
|
967
|
+
command = "%s project test_issue_55 --sources" % self.envstack_bin
|
|
955
968
|
expected_output = f"""{self.root}/prod/env/default.env
|
|
956
|
-
{self.root}/prod/env/
|
|
957
|
-
{self.root}/prod/env/test.env
|
|
969
|
+
{self.root}/prod/env/project.env
|
|
958
970
|
{self.root}/test_issue_55/env/test_issue_55.env
|
|
959
971
|
"""
|
|
960
972
|
output = subprocess.check_output(
|
|
@@ -176,7 +176,7 @@ class TestEnv(unittest.TestCase):
|
|
|
176
176
|
env = load_environ("thing")
|
|
177
177
|
baked = env.bake()
|
|
178
178
|
for k, v in env.items():
|
|
179
|
-
if k == "STACK":
|
|
179
|
+
if k == "STACK" or k == "PS1": # skip
|
|
180
180
|
continue
|
|
181
181
|
self.assertEqual(baked[k], v)
|
|
182
182
|
|
|
@@ -189,7 +189,7 @@ class TestEnv(unittest.TestCase):
|
|
|
189
189
|
env1.write(self.filename, depth=0)
|
|
190
190
|
env2 = load_environ(self.filename)
|
|
191
191
|
for k, v in env1.items():
|
|
192
|
-
if k == "STACK":
|
|
192
|
+
if k == "STACK" or k == "PS1": # skip
|
|
193
193
|
continue
|
|
194
194
|
self.assertEqual(env2[k], v)
|
|
195
195
|
|
|
@@ -342,8 +342,8 @@ class TestInit(unittest.TestCase):
|
|
|
342
342
|
self.assertEqual(diffs["removed"], {})
|
|
343
343
|
self.assertEqual(diffs["unchanged"], original_env)
|
|
344
344
|
|
|
345
|
-
def
|
|
346
|
-
"""Tests init with custom
|
|
345
|
+
def test_init_zzz_project(self):
|
|
346
|
+
"""Tests init with custom project stack."""
|
|
347
347
|
envpath = os.path.join(os.path.dirname(__file__), "..", "env")
|
|
348
348
|
os.environ["ENVPATH"] = envpath
|
|
349
349
|
os.environ["HELLO"] = "goodbye"
|
|
@@ -351,7 +351,7 @@ class TestInit(unittest.TestCase):
|
|
|
351
351
|
original_env = os.environ.copy()
|
|
352
352
|
sys_path = sys.path.copy()
|
|
353
353
|
|
|
354
|
-
envstack.init("
|
|
354
|
+
envstack.init("project", "custom", ignore_missing=True)
|
|
355
355
|
self.assertEqual(os.getenv("ENV"), "custom")
|
|
356
356
|
self.assertEqual(os.getenv("STACK"), "custom")
|
|
357
357
|
self.assertEqual(os.getenv("DEPLOY_ROOT"), f"{self.root}/custom")
|
|
@@ -667,7 +667,7 @@ class TestBakeEnviron(unittest.TestCase):
|
|
|
667
667
|
|
|
668
668
|
# compare the values
|
|
669
669
|
for key, value in env.items():
|
|
670
|
-
if key == "STACK": # skip
|
|
670
|
+
if key == "STACK" or key == "PS1": # skip
|
|
671
671
|
continue
|
|
672
672
|
self.assertEqual(baked[key], value)
|
|
673
673
|
|
|
@@ -695,7 +695,7 @@ class TestBakeEnviron(unittest.TestCase):
|
|
|
695
695
|
|
|
696
696
|
# compare the values
|
|
697
697
|
for key, value in env.items():
|
|
698
|
-
if key == "STACK":
|
|
698
|
+
if key == "STACK" or key == "PS1": # skip
|
|
699
699
|
continue
|
|
700
700
|
self.assertEqual(baked2_reloaded[key], value)
|
|
701
701
|
|
|
@@ -1062,9 +1062,9 @@ class TestIssues(unittest.TestCase):
|
|
|
1062
1062
|
# FIXME: why is this necessary? (think it's caching seen stacks)
|
|
1063
1063
|
envstack.revert()
|
|
1064
1064
|
|
|
1065
|
-
# load our
|
|
1065
|
+
# load our project env file by loading the "project" env first, which
|
|
1066
1066
|
# does use STACK in ENVPATH
|
|
1067
|
-
env2 = load_environ(["
|
|
1067
|
+
env2 = load_environ(["project", "test_issue_55"])
|
|
1068
1068
|
|
|
1069
1069
|
# last env file should be our test env file
|
|
1070
1070
|
self.assertEqual(str(env2.sources[-1].path), test_env_file)
|
|
@@ -172,7 +172,7 @@ class TestTemplate(unittest.TestCase):
|
|
|
172
172
|
class TestCustomLoader(unittest.TestCase):
|
|
173
173
|
def test_construct_mapping(self):
|
|
174
174
|
"""test the CustomLoader construct_mapping method"""
|
|
175
|
-
envfile = os.path.join(envpath, "
|
|
175
|
+
envfile = os.path.join(envpath, "project.env")
|
|
176
176
|
loader = CustomLoader(open(envfile, "r"))
|
|
177
177
|
node = yaml.MappingNode("tag", [])
|
|
178
178
|
mapping = loader.construct_mapping(node)
|
|
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
|