devopsdriver 0.1.32__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.
- devopsdriver-0.1.32/LICENSE +24 -0
- devopsdriver-0.1.32/PKG-INFO +70 -0
- devopsdriver-0.1.32/README.md +20 -0
- devopsdriver-0.1.32/devopsdriver/__init__.py +5 -0
- devopsdriver-0.1.32/devopsdriver/settings.py +328 -0
- devopsdriver-0.1.32/devopsdriver.egg-info/PKG-INFO +70 -0
- devopsdriver-0.1.32/devopsdriver.egg-info/SOURCES.txt +11 -0
- devopsdriver-0.1.32/devopsdriver.egg-info/dependency_links.txt +1 -0
- devopsdriver-0.1.32/devopsdriver.egg-info/requires.txt +1 -0
- devopsdriver-0.1.32/devopsdriver.egg-info/top_level.txt +1 -0
- devopsdriver-0.1.32/pyproject.toml +40 -0
- devopsdriver-0.1.32/setup.cfg +4 -0
- devopsdriver-0.1.32/tests/test_settings.py +299 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
|
2
|
+
|
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
4
|
+
distribute this software, either in source code form or as a compiled
|
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
|
6
|
+
means.
|
|
7
|
+
|
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
|
9
|
+
of this software dedicate any and all copyright interest in the
|
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
|
11
|
+
of the public at large and to the detriment of our heirs and
|
|
12
|
+
successors. We intend this dedication to be an overt act of
|
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
|
14
|
+
software under copyright law.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
For more information, please refer to <https://unlicense.org>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: devopsdriver
|
|
3
|
+
Version: 0.1.32
|
|
4
|
+
Summary: DevOps tools
|
|
5
|
+
Author-email: Marc Page <marcallenpage@gmail.com>
|
|
6
|
+
License: This is free and unencumbered software released into the public domain.
|
|
7
|
+
|
|
8
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
9
|
+
distribute this software, either in source code form or as a compiled
|
|
10
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
|
11
|
+
means.
|
|
12
|
+
|
|
13
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
|
14
|
+
of this software dedicate any and all copyright interest in the
|
|
15
|
+
software to the public domain. We make this dedication for the benefit
|
|
16
|
+
of the public at large and to the detriment of our heirs and
|
|
17
|
+
successors. We intend this dedication to be an overt act of
|
|
18
|
+
relinquishment in perpetuity of all present and future rights to this
|
|
19
|
+
software under copyright law.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
22
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
23
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
24
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
25
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
26
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
27
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
28
|
+
|
|
29
|
+
For more information, please refer to <https://unlicense.org>
|
|
30
|
+
|
|
31
|
+
Project-URL: Homepage, https://github.com/marcpage/devops-driver
|
|
32
|
+
Project-URL: Documentation, https://github.com/marcpage/devops-driver
|
|
33
|
+
Project-URL: Repository, https://github.com/marcpage/devops-driver.git
|
|
34
|
+
Project-URL: Issues, https://github.com/marcpage/devops-driver/issues
|
|
35
|
+
Project-URL: Changelog, https://github.com/marcpage/devops-driver
|
|
36
|
+
Keywords: azure,devops,jira,confluence,email,pipelines,tools
|
|
37
|
+
Classifier: Development Status :: 1 - Planning
|
|
38
|
+
Classifier: Environment :: Console
|
|
39
|
+
Classifier: Intended Audience :: Developers
|
|
40
|
+
Classifier: Programming Language :: Python
|
|
41
|
+
Classifier: Programming Language :: Python :: 3
|
|
42
|
+
Classifier: License :: OSI Approved :: The Unlicense (Unlicense)
|
|
43
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
44
|
+
Classifier: Topic :: Utilities
|
|
45
|
+
Classifier: Topic :: Software Development
|
|
46
|
+
Requires-Python: >=3.12
|
|
47
|
+
Description-Content-Type: text/markdown
|
|
48
|
+
License-File: LICENSE
|
|
49
|
+
Requires-Dist: PyYAML==6.0.1
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
[](https://github.com/marcpage/devops-driver?tab=Unlicense-1-ov-file#readme)
|
|
53
|
+
[](https://github.com/marcpage/devops-driver/commits)
|
|
54
|
+
[](https://github.com/marcpage/devops-driver/commits)
|
|
55
|
+
[](https://github.com/marcpage/devops-driver)
|
|
56
|
+
[](https://github.com/marcpage/devops-driver)
|
|
57
|
+
|
|
58
|
+
[](https://github.com/marcpage/devops-driver/actions/workflows/pr.yml)
|
|
59
|
+
[](https://github.com/marcpage/devops-driver/blob/main/Makefile#L4)
|
|
60
|
+
[](https://github.com/marcpage/devops-driver/issues)
|
|
61
|
+
[](https://github.com/marcpage/devops-driver/pulls)
|
|
62
|
+
[](https://github.com/marcpage/devops-driver/graphs/contributors)
|
|
63
|
+
[](http://makeapullrequest.com)
|
|
64
|
+
|
|
65
|
+
[](https://github.com/marcpage?tab=followers)
|
|
66
|
+
[](https://github.com/marcpage/devops-driver/watchers)
|
|
67
|
+
|
|
68
|
+
# devops-driver
|
|
69
|
+
|
|
70
|
+
Devops-driver is a set of tools to help streamline developer's experience. It is a collection of tools to help gain insights into various processes.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+

|
|
2
|
+
[](https://github.com/marcpage/devops-driver?tab=Unlicense-1-ov-file#readme)
|
|
3
|
+
[](https://github.com/marcpage/devops-driver/commits)
|
|
4
|
+
[](https://github.com/marcpage/devops-driver/commits)
|
|
5
|
+
[](https://github.com/marcpage/devops-driver)
|
|
6
|
+
[](https://github.com/marcpage/devops-driver)
|
|
7
|
+
|
|
8
|
+
[](https://github.com/marcpage/devops-driver/actions/workflows/pr.yml)
|
|
9
|
+
[](https://github.com/marcpage/devops-driver/blob/main/Makefile#L4)
|
|
10
|
+
[](https://github.com/marcpage/devops-driver/issues)
|
|
11
|
+
[](https://github.com/marcpage/devops-driver/pulls)
|
|
12
|
+
[](https://github.com/marcpage/devops-driver/graphs/contributors)
|
|
13
|
+
[](http://makeapullrequest.com)
|
|
14
|
+
|
|
15
|
+
[](https://github.com/marcpage?tab=followers)
|
|
16
|
+
[](https://github.com/marcpage/devops-driver/watchers)
|
|
17
|
+
|
|
18
|
+
# devops-driver
|
|
19
|
+
|
|
20
|
+
Devops-driver is a set of tools to help streamline developer's experience. It is a collection of tools to help gain insights into various processes.
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
""" Settings that can be in files, environment, or on the command line
|
|
5
|
+
|
|
6
|
+
Settings can be set on the command line, environment, in the code, or in files.
|
|
7
|
+
|
|
8
|
+
Priority order:
|
|
9
|
+
- in code
|
|
10
|
+
- command line
|
|
11
|
+
- environment
|
|
12
|
+
- files
|
|
13
|
+
|
|
14
|
+
Files can be JSON or YAML.
|
|
15
|
+
They must have .json, .yml, or .yaml extension.
|
|
16
|
+
Files can be named after the file passed in (__file__) or "devopsdriver".
|
|
17
|
+
All files named after the file passed in have priority over "devopsdriver".
|
|
18
|
+
This allows for shared settings for multiple scripts as well as specific settings.
|
|
19
|
+
Files can be in an OS-specific preferences location, a series of specified directories,
|
|
20
|
+
or next to the file passed in (__file__).
|
|
21
|
+
This allows for secrets, keys, and tokens to be stored on the machine and not in the repo.
|
|
22
|
+
|
|
23
|
+
The OS specific directories are:
|
|
24
|
+
- macOS: ~/Library/Preferences/
|
|
25
|
+
- Windows: %APPDATA%/
|
|
26
|
+
- Linux: ~/.devopsdriver/
|
|
27
|
+
|
|
28
|
+
You can have environment variable substitutions in the values in the files.
|
|
29
|
+
For instance, you can specify:
|
|
30
|
+
|
|
31
|
+
output: ${home}/reports
|
|
32
|
+
|
|
33
|
+
The `${home}` will be replaced with the value of the HOME environment variable, if it exists.
|
|
34
|
+
If the environment variable does not exist, no change is made.
|
|
35
|
+
|
|
36
|
+
Use case 1: Secrets not in repo
|
|
37
|
+
tokens, passwords, etc can be stored in <pref>/devopsdriver.yml
|
|
38
|
+
This will allow for all scripts to access those secrets
|
|
39
|
+
but they are not in the repo.
|
|
40
|
+
For pipeline runs these secrets can be passed on the command line or in the environment.
|
|
41
|
+
|
|
42
|
+
Use case 2: common settings among scripts
|
|
43
|
+
Store your common settings in devopsdriver.yml next to your script.
|
|
44
|
+
All scripts in this directory will have access to these settings.
|
|
45
|
+
For instance, say you want all emails sent from the same person.
|
|
46
|
+
You could set 'email: me@domain.com' in devopsdriver.html.
|
|
47
|
+
You could also, store report paths, emails, groups, holidays, etc.
|
|
48
|
+
Any data all scripts may want to have access to.
|
|
49
|
+
|
|
50
|
+
Use case 3: configurable settings
|
|
51
|
+
Any settings in your script that you may want to configure.
|
|
52
|
+
If your script is `cool_script.py` then put the settings in `cool_script.yml` next to it.
|
|
53
|
+
This could be colors, emails, repos, queries, whatever.
|
|
54
|
+
|
|
55
|
+
Use case 4: override secrets for specific script
|
|
56
|
+
Overriding secrets stored in <pref>/devopsdrive.yml for specific scripts.
|
|
57
|
+
If your script is `cool_script.py` save them to <pref>cool_script.yml.
|
|
58
|
+
|
|
59
|
+
*Note*: You can override a specific setting in a sub-dictionary.
|
|
60
|
+
For instance, say <pref>/devopsdrive.yml:
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
api:
|
|
64
|
+
user: johndoe
|
|
65
|
+
password: Setec Astronomy
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
You could override this for `cool_script.py` be adding <pref>cool_script.yml:
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
api:
|
|
72
|
+
user: janedoe
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
johndoe and janedoe share the same password, so you just need to update the `user`
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
from json import load
|
|
80
|
+
from os.path import dirname, basename, splitext, join
|
|
81
|
+
from os import environ as os_environ, makedirs as os_makedirs
|
|
82
|
+
from re import compile as regex
|
|
83
|
+
from platform import system as os_system
|
|
84
|
+
from sys import argv as sys_argv
|
|
85
|
+
|
|
86
|
+
from yaml import safe_load
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# for testing
|
|
90
|
+
ENVIRON = os_environ
|
|
91
|
+
ARGV = sys_argv
|
|
92
|
+
SYSTEM = os_system
|
|
93
|
+
MAKEDIRS = os_makedirs
|
|
94
|
+
SHARED = "devopsdriver"
|
|
95
|
+
PRINT = print
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def load_json(path: str) -> dict:
|
|
99
|
+
"""Load a dictionary from a JSON file
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
path (str): Path to JSON file
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
dict: The contents as a dictionary, or empty dictioanry if unable to load the file
|
|
106
|
+
"""
|
|
107
|
+
try:
|
|
108
|
+
with open(path, "r", encoding="utf-8") as file:
|
|
109
|
+
return load(file)
|
|
110
|
+
|
|
111
|
+
except FileNotFoundError:
|
|
112
|
+
return {}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def load_yaml(path: str) -> dict:
|
|
116
|
+
"""Load a dictionary from a JSON file
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
path (str): Path to JSON file
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
dict: The contents as a dictionary, or empty dictioanry if unable to load the file
|
|
123
|
+
"""
|
|
124
|
+
try:
|
|
125
|
+
with open(path, "r", encoding="utf-8") as file:
|
|
126
|
+
return safe_load(file)
|
|
127
|
+
|
|
128
|
+
except FileNotFoundError:
|
|
129
|
+
return {}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class Settings:
|
|
133
|
+
"""Settings object"""
|
|
134
|
+
|
|
135
|
+
FORMATS = ((".yml", load_yaml), (".yaml", load_yaml), (".json", load_json))
|
|
136
|
+
DEFAULT_PREF_DIR = "Linux"
|
|
137
|
+
PREF_DIR = {
|
|
138
|
+
"Darwin": join(ENVIRON.get("HOME", ""), "Library", "Preferences"),
|
|
139
|
+
"Windows": join(ENVIRON.get("APPDATA", "")),
|
|
140
|
+
"Linux": join(ENVIRON.get("HOME", ""), ".devopsdriver"),
|
|
141
|
+
}
|
|
142
|
+
ENV_VAR_PATTERN = regex(r"\${(\S+)}")
|
|
143
|
+
|
|
144
|
+
def __init__(self, file: str, *directories, **settings):
|
|
145
|
+
"""Create a settings object using a file, directories to search, and settings overrides
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
file (str): The basename to use and a directory to search. pass __file__
|
|
149
|
+
"""
|
|
150
|
+
self.overrides = settings
|
|
151
|
+
directories = [dirname(file), *directories, Settings.__preferences_dir()]
|
|
152
|
+
search_info = Settings.__all_paths(file, directories)
|
|
153
|
+
self.search_files = [join(d, n + e) for e, n, d, _ in search_info]
|
|
154
|
+
self.settings = Settings.__find_all_settings(search_info)
|
|
155
|
+
self.opts = {}
|
|
156
|
+
self.environ = {}
|
|
157
|
+
|
|
158
|
+
def cli(self, key: str, name: str = None):
|
|
159
|
+
"""Sets a command line switch to map to a settings value.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
key (str): The settings key it maps to, dotted for inside dictionary
|
|
163
|
+
name (str): Name of the command line switch, eg '-p' or '--path'
|
|
164
|
+
If name is not specified, the key is a settings value
|
|
165
|
+
to lookup up the mappings for keys to switches
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
Settings: Returns self so you can chain calls
|
|
169
|
+
"""
|
|
170
|
+
if name is None:
|
|
171
|
+
for setting_key, env_name in self.settings.get(key, {}).items():
|
|
172
|
+
self.opts[setting_key] = env_name
|
|
173
|
+
return self
|
|
174
|
+
|
|
175
|
+
self.opts[key] = name
|
|
176
|
+
return self
|
|
177
|
+
|
|
178
|
+
def env(self, key: str, name: str = None):
|
|
179
|
+
"""Sets an environment variable to map to a settings value.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
key (str): The settings key it maps to, dotted for inside dictionary
|
|
183
|
+
name (str): Name of the environment variable
|
|
184
|
+
If name is not specified, the key is a settings value
|
|
185
|
+
to lookup up the mappings for keys to environment names
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
Settings: Returns self so you can chain calls
|
|
189
|
+
"""
|
|
190
|
+
if name is None:
|
|
191
|
+
for setting_key, cli_name in self.settings.get(key, {}).items():
|
|
192
|
+
self.environ[setting_key] = cli_name
|
|
193
|
+
return self
|
|
194
|
+
|
|
195
|
+
self.environ[key] = name
|
|
196
|
+
return self
|
|
197
|
+
|
|
198
|
+
@staticmethod
|
|
199
|
+
def __patch_instance(key: str) -> str:
|
|
200
|
+
for env_key, value in ENVIRON.items():
|
|
201
|
+
if env_key.lower() == key.lower():
|
|
202
|
+
return value
|
|
203
|
+
|
|
204
|
+
return "${" + key + "}"
|
|
205
|
+
|
|
206
|
+
@staticmethod
|
|
207
|
+
def __patch(value: any) -> any:
|
|
208
|
+
if isinstance(value, str):
|
|
209
|
+
return Settings.ENV_VAR_PATTERN.sub(
|
|
210
|
+
lambda m: Settings.__patch_instance(m.group(1)), value
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
return value
|
|
214
|
+
|
|
215
|
+
def __lookup(self, key: str, check: bool, default: any = None) -> any:
|
|
216
|
+
# Settings passed in override everything
|
|
217
|
+
if key in self.overrides:
|
|
218
|
+
return True if check else self.overrides[key]
|
|
219
|
+
|
|
220
|
+
# Settings on the command line take next precedence
|
|
221
|
+
if key in self.opts:
|
|
222
|
+
for nth, name in enumerate(ARGV[1:]):
|
|
223
|
+
if name.lower() == self.opts[key].lower() and nth + 2 < len(ARGV):
|
|
224
|
+
return True if check else ARGV[nth + 2]
|
|
225
|
+
|
|
226
|
+
# Settings in the environment are next
|
|
227
|
+
if key in self.environ:
|
|
228
|
+
for e_key in ENVIRON:
|
|
229
|
+
if e_key.lower() == self.environ[key].lower():
|
|
230
|
+
return True if check else ENVIRON[e_key]
|
|
231
|
+
|
|
232
|
+
# Last check the files for settings
|
|
233
|
+
keys = key.split(".")
|
|
234
|
+
level = self.settings
|
|
235
|
+
|
|
236
|
+
for key_part in keys[:-1]:
|
|
237
|
+
level = level.get(key_part, {})
|
|
238
|
+
|
|
239
|
+
if check:
|
|
240
|
+
return keys[-1] in level
|
|
241
|
+
|
|
242
|
+
return Settings.__patch(level.get(keys[-1], default))
|
|
243
|
+
|
|
244
|
+
def get(self, key: str, default: any = None) -> any:
|
|
245
|
+
"""Dictionary-like get
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
key (str): The key to load, dotted format
|
|
249
|
+
default (any, optional): The value to return if there is no value. Defaults to None.
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
any: The value or `default` if not found
|
|
253
|
+
"""
|
|
254
|
+
return self.__lookup(key, check=False, default=default)
|
|
255
|
+
|
|
256
|
+
def has(self, key: str) -> bool:
|
|
257
|
+
"""Check if the key exists in any of the environments
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
key (str): The dotted key
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
bool: True if the key exists
|
|
264
|
+
"""
|
|
265
|
+
return self.__lookup(key, check=True)
|
|
266
|
+
|
|
267
|
+
def __getitem__(self, key: str) -> any:
|
|
268
|
+
if not self.has(key):
|
|
269
|
+
raise KeyError(key)
|
|
270
|
+
|
|
271
|
+
return self.get(key)
|
|
272
|
+
|
|
273
|
+
@staticmethod
|
|
274
|
+
def __preferences_dir() -> str:
|
|
275
|
+
default_dir = Settings.PREF_DIR[Settings.DEFAULT_PREF_DIR]
|
|
276
|
+
directory = Settings.PREF_DIR.get(SYSTEM(), default_dir)
|
|
277
|
+
MAKEDIRS(directory, exist_ok=True)
|
|
278
|
+
return directory
|
|
279
|
+
|
|
280
|
+
@staticmethod
|
|
281
|
+
def __merge(base: dict, new: dict):
|
|
282
|
+
"""Add new information to an existing dictionary, if it doesn't already exist.
|
|
283
|
+
If new has keys that base doesn't, they are added.
|
|
284
|
+
If new has a key that base does and they both are dictionaries, they are merged.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
base (dict): The is the existing values to add to
|
|
288
|
+
new (dict): The new values to possible add
|
|
289
|
+
"""
|
|
290
|
+
for key in new:
|
|
291
|
+
if key not in base:
|
|
292
|
+
base[key] = new[key]
|
|
293
|
+
continue
|
|
294
|
+
|
|
295
|
+
if isinstance(base[key], dict) and isinstance(new[key], dict):
|
|
296
|
+
Settings.__merge(base[key], new[key])
|
|
297
|
+
|
|
298
|
+
@staticmethod
|
|
299
|
+
def __all_paths(file: str, directories: list[str]) -> list[tuple]:
|
|
300
|
+
names = [splitext(basename(file))[0], SHARED]
|
|
301
|
+
return [
|
|
302
|
+
(e, n, d, f)
|
|
303
|
+
for n in names
|
|
304
|
+
for d in directories
|
|
305
|
+
for e, f in Settings.FORMATS
|
|
306
|
+
]
|
|
307
|
+
|
|
308
|
+
@staticmethod
|
|
309
|
+
def __find_all_settings(search_info: list[tuple]) -> dict:
|
|
310
|
+
settings = {}
|
|
311
|
+
|
|
312
|
+
for extension, name, directory, loader in search_info:
|
|
313
|
+
contents = loader(join(directory, name + extension))
|
|
314
|
+
Settings.__merge(settings, contents)
|
|
315
|
+
|
|
316
|
+
return settings
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def main() -> None:
|
|
320
|
+
"""Get settings values"""
|
|
321
|
+
settings = Settings(__file__, dirname(dirname(__file__)))
|
|
322
|
+
|
|
323
|
+
for arg in ARGV[1:]:
|
|
324
|
+
PRINT(settings.get(arg))
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
if __name__ == "__main__":
|
|
328
|
+
main()
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: devopsdriver
|
|
3
|
+
Version: 0.1.32
|
|
4
|
+
Summary: DevOps tools
|
|
5
|
+
Author-email: Marc Page <marcallenpage@gmail.com>
|
|
6
|
+
License: This is free and unencumbered software released into the public domain.
|
|
7
|
+
|
|
8
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
9
|
+
distribute this software, either in source code form or as a compiled
|
|
10
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
|
11
|
+
means.
|
|
12
|
+
|
|
13
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
|
14
|
+
of this software dedicate any and all copyright interest in the
|
|
15
|
+
software to the public domain. We make this dedication for the benefit
|
|
16
|
+
of the public at large and to the detriment of our heirs and
|
|
17
|
+
successors. We intend this dedication to be an overt act of
|
|
18
|
+
relinquishment in perpetuity of all present and future rights to this
|
|
19
|
+
software under copyright law.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
22
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
23
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
24
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
25
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
26
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
27
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
28
|
+
|
|
29
|
+
For more information, please refer to <https://unlicense.org>
|
|
30
|
+
|
|
31
|
+
Project-URL: Homepage, https://github.com/marcpage/devops-driver
|
|
32
|
+
Project-URL: Documentation, https://github.com/marcpage/devops-driver
|
|
33
|
+
Project-URL: Repository, https://github.com/marcpage/devops-driver.git
|
|
34
|
+
Project-URL: Issues, https://github.com/marcpage/devops-driver/issues
|
|
35
|
+
Project-URL: Changelog, https://github.com/marcpage/devops-driver
|
|
36
|
+
Keywords: azure,devops,jira,confluence,email,pipelines,tools
|
|
37
|
+
Classifier: Development Status :: 1 - Planning
|
|
38
|
+
Classifier: Environment :: Console
|
|
39
|
+
Classifier: Intended Audience :: Developers
|
|
40
|
+
Classifier: Programming Language :: Python
|
|
41
|
+
Classifier: Programming Language :: Python :: 3
|
|
42
|
+
Classifier: License :: OSI Approved :: The Unlicense (Unlicense)
|
|
43
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
44
|
+
Classifier: Topic :: Utilities
|
|
45
|
+
Classifier: Topic :: Software Development
|
|
46
|
+
Requires-Python: >=3.12
|
|
47
|
+
Description-Content-Type: text/markdown
|
|
48
|
+
License-File: LICENSE
|
|
49
|
+
Requires-Dist: PyYAML==6.0.1
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
[](https://github.com/marcpage/devops-driver?tab=Unlicense-1-ov-file#readme)
|
|
53
|
+
[](https://github.com/marcpage/devops-driver/commits)
|
|
54
|
+
[](https://github.com/marcpage/devops-driver/commits)
|
|
55
|
+
[](https://github.com/marcpage/devops-driver)
|
|
56
|
+
[](https://github.com/marcpage/devops-driver)
|
|
57
|
+
|
|
58
|
+
[](https://github.com/marcpage/devops-driver/actions/workflows/pr.yml)
|
|
59
|
+
[](https://github.com/marcpage/devops-driver/blob/main/Makefile#L4)
|
|
60
|
+
[](https://github.com/marcpage/devops-driver/issues)
|
|
61
|
+
[](https://github.com/marcpage/devops-driver/pulls)
|
|
62
|
+
[](https://github.com/marcpage/devops-driver/graphs/contributors)
|
|
63
|
+
[](http://makeapullrequest.com)
|
|
64
|
+
|
|
65
|
+
[](https://github.com/marcpage?tab=followers)
|
|
66
|
+
[](https://github.com/marcpage/devops-driver/watchers)
|
|
67
|
+
|
|
68
|
+
# devops-driver
|
|
69
|
+
|
|
70
|
+
Devops-driver is a set of tools to help streamline developer's experience. It is a collection of tools to help gain insights into various processes.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
devopsdriver/__init__.py
|
|
5
|
+
devopsdriver/settings.py
|
|
6
|
+
devopsdriver.egg-info/PKG-INFO
|
|
7
|
+
devopsdriver.egg-info/SOURCES.txt
|
|
8
|
+
devopsdriver.egg-info/dependency_links.txt
|
|
9
|
+
devopsdriver.egg-info/requires.txt
|
|
10
|
+
devopsdriver.egg-info/top_level.txt
|
|
11
|
+
tests/test_settings.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
PyYAML==6.0.1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
devopsdriver
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "devopsdriver"
|
|
3
|
+
description = "DevOps tools"
|
|
4
|
+
readme = "README.md"
|
|
5
|
+
license = {file = "LICENSE"}
|
|
6
|
+
dynamic = ["version"]
|
|
7
|
+
requires-python = ">= 3.12"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"PyYAML==6.0.1",
|
|
10
|
+
]
|
|
11
|
+
keywords = ["azure", "devops", "jira", "confluence", "email", "pipelines", "tools"]
|
|
12
|
+
classifiers=[
|
|
13
|
+
"Development Status :: 1 - Planning",
|
|
14
|
+
"Environment :: Console",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Programming Language :: Python",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"License :: OSI Approved :: The Unlicense (Unlicense)",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Topic :: Utilities",
|
|
21
|
+
"Topic :: Software Development",
|
|
22
|
+
]
|
|
23
|
+
authors = [
|
|
24
|
+
{name = "Marc Page", email = "marcallenpage@gmail.com"},
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://github.com/marcpage/devops-driver"
|
|
29
|
+
Documentation = "https://github.com/marcpage/devops-driver"
|
|
30
|
+
Repository = "https://github.com/marcpage/devops-driver.git"
|
|
31
|
+
Issues = "https://github.com/marcpage/devops-driver/issues"
|
|
32
|
+
Changelog = "https://github.com/marcpage/devops-driver"
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.dynamic]
|
|
35
|
+
version = {attr = "devopsdriver.__version__"}
|
|
36
|
+
|
|
37
|
+
[build-system]
|
|
38
|
+
requires = ["setuptools >= 69.2"]
|
|
39
|
+
build-backend = "setuptools.build_meta"
|
|
40
|
+
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
""" Tests Settings class """
|
|
4
|
+
|
|
5
|
+
from tempfile import TemporaryDirectory
|
|
6
|
+
from os.path import join, splitext, dirname
|
|
7
|
+
from os import makedirs
|
|
8
|
+
from json import dump
|
|
9
|
+
from string import ascii_lowercase
|
|
10
|
+
from itertools import product
|
|
11
|
+
|
|
12
|
+
from yaml import safe_dump
|
|
13
|
+
|
|
14
|
+
import devopsdriver.settings as settings
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def __setup_settings(os: str = "Linux", shared: str = "test", **pref_dirs) -> None:
|
|
18
|
+
settings.ENVIRON = {}
|
|
19
|
+
settings.ARGV = []
|
|
20
|
+
settings.SYSTEM = lambda: os
|
|
21
|
+
settings.SHARED = shared
|
|
22
|
+
settings.PRINT = lambda s: s
|
|
23
|
+
# settings.MAKEDIRS = lambda p: p
|
|
24
|
+
# settings.Settings.FORMATS = None
|
|
25
|
+
settings.Settings.PREF_DIR = pref_dirs
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def ensure(directory: str) -> str:
|
|
29
|
+
"""Ensures that a directory exists before using
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
directory (str): The directory to create
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
str: The directory that now exists
|
|
36
|
+
"""
|
|
37
|
+
makedirs(directory, exist_ok=True)
|
|
38
|
+
return directory
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def __write(path: str, **options) -> None:
|
|
42
|
+
ensure(dirname(path))
|
|
43
|
+
|
|
44
|
+
with open(path, "w", encoding="utf-8") as settings_file:
|
|
45
|
+
if splitext(path)[1] == ".json":
|
|
46
|
+
dump(options, settings_file)
|
|
47
|
+
else:
|
|
48
|
+
safe_dump(options, settings_file)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def __setup_files(directory: str, dir1: str, dir2: str) -> None:
|
|
52
|
+
"""
|
|
53
|
+
Priorities:
|
|
54
|
+
<dir>/main.yml a
|
|
55
|
+
<dir>/main.yaml b
|
|
56
|
+
<dir>/main.json c
|
|
57
|
+
<dir1>/main.yml d
|
|
58
|
+
<dir1>/main.yaml e
|
|
59
|
+
<dir1>/main.json f
|
|
60
|
+
<dir2>/main.yml g
|
|
61
|
+
<dir2>/main.yaml h
|
|
62
|
+
<dir2>/main.json i
|
|
63
|
+
<pref>/main.yml j
|
|
64
|
+
<pref>/main.yaml k
|
|
65
|
+
<pref>/main.json l
|
|
66
|
+
<dir>/test.yml m
|
|
67
|
+
<dir>/test.yaml n
|
|
68
|
+
<dir>/test.json o
|
|
69
|
+
<dir1>/test.yml p
|
|
70
|
+
<dir1>/test.yaml q
|
|
71
|
+
<dir1>/test.json r
|
|
72
|
+
<dir2>/test.yml s
|
|
73
|
+
<dir2>/test.yaml t
|
|
74
|
+
<dir2>/test.json u
|
|
75
|
+
<pref>/test.yml v
|
|
76
|
+
<pref>/test.yaml w
|
|
77
|
+
<pref>/test.json x
|
|
78
|
+
"""
|
|
79
|
+
lin_dir = ensure(settings.Settings.PREF_DIR["Linux"])
|
|
80
|
+
mac_dir = ensure(settings.Settings.PREF_DIR["Darwin"])
|
|
81
|
+
win_dir = ensure(settings.Settings.PREF_DIR["Windows"])
|
|
82
|
+
letters = list(ascii_lowercase)
|
|
83
|
+
|
|
84
|
+
for name in ("test", "main"):
|
|
85
|
+
for letter, os_dir in (("l", lin_dir), ("w", win_dir), ("m", mac_dir)):
|
|
86
|
+
__write(
|
|
87
|
+
join(os_dir, "test.json"),
|
|
88
|
+
**{l: f"{name[0]}{letter}{l}" for l in letters},
|
|
89
|
+
dp={l: f"{name[0]}{letter}{l}" for l in letters},
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
letters.pop()
|
|
93
|
+
|
|
94
|
+
for directory, name, ext in product(
|
|
95
|
+
(dir2, dir1, directory), ("test", "main"), (".json", ".yaml", ".yml")
|
|
96
|
+
):
|
|
97
|
+
__write(
|
|
98
|
+
join(directory, name + ext),
|
|
99
|
+
**{l: f"{directory[-1]}{ext[2]}{name[0]}{letter}{l}" for l in letters},
|
|
100
|
+
dp={l: f"{directory[-1]}{ext[2]}{name[0]}{letter}{l}" for l in letters},
|
|
101
|
+
)
|
|
102
|
+
letters.pop()
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_basic():
|
|
106
|
+
"""test the basic functionality"""
|
|
107
|
+
with TemporaryDirectory() as working_dir:
|
|
108
|
+
base_dir = join(working_dir, "base")
|
|
109
|
+
|
|
110
|
+
for os in ("Linux", "Darwin", "Windows", "Unknown"):
|
|
111
|
+
__setup_settings(
|
|
112
|
+
os=os,
|
|
113
|
+
shared="test",
|
|
114
|
+
Linux=join(base_dir, "Linux"),
|
|
115
|
+
Darwin=join(base_dir, "macOS"),
|
|
116
|
+
Windows=join(base_dir, "Windows"),
|
|
117
|
+
)
|
|
118
|
+
dir1 = join(base_dir, "dir1")
|
|
119
|
+
dir2 = join(base_dir, "dir2")
|
|
120
|
+
|
|
121
|
+
__setup_files(base_dir, dir1, dir2)
|
|
122
|
+
settings.ENVIRON = {
|
|
123
|
+
"alpha": "aaenviron",
|
|
124
|
+
"yota": "yyenviron",
|
|
125
|
+
"zeta": "zzenviron",
|
|
126
|
+
}
|
|
127
|
+
settings.ARGV = ["exe", "--beta", "bbcli", "--zeta", "zzcli"]
|
|
128
|
+
opts = (
|
|
129
|
+
settings.Settings(
|
|
130
|
+
join(base_dir, "main.py"), dir1, dir2, aa=1, bb=2, cc=3
|
|
131
|
+
)
|
|
132
|
+
.cli("bb", "--beta")
|
|
133
|
+
.cli("zz", "--zeta")
|
|
134
|
+
.env("aa", "alpha")
|
|
135
|
+
.env("yy", "yota")
|
|
136
|
+
)
|
|
137
|
+
ltr = {"Linux": "l", "Darwin": "m", "Windows": "w", "Unknown": "l"}
|
|
138
|
+
assert opts["yy"] == "yyenviron", opts["yy"]
|
|
139
|
+
assert opts["aa"] == 1, f"{opts['aa']} {os}"
|
|
140
|
+
assert opts["bb"] == 2, f"{opts['bb']} {os}"
|
|
141
|
+
assert opts["cc"] == 3, f"{opts['cc']} {os}"
|
|
142
|
+
assert opts["zz"] == "zzcli", opts["zz"]
|
|
143
|
+
assert opts["a"] == "emmma", f"{opts['a']} {os}"
|
|
144
|
+
assert opts["dp.a"] == "emmma", f"{opts['dp.a']} {os}"
|
|
145
|
+
assert opts["b"] == "emmmb", f"{opts['b']} {os}"
|
|
146
|
+
assert opts["dp.b"] == "emmmb", f"{opts['dp.b']} {os}"
|
|
147
|
+
assert opts["c"] == "emmmc", f"{opts['c']} {os}"
|
|
148
|
+
assert opts["dp.c"] == "emmmc", f"{opts['dp.c']} {os}"
|
|
149
|
+
assert opts["d"] == "emmmd", f"{opts['d']} {os}"
|
|
150
|
+
assert opts["dp.d"] == "emmmd", f"{opts['dp.d']} {os}"
|
|
151
|
+
assert opts["e"] == "emmme", f"{opts['e']} {os}"
|
|
152
|
+
assert opts["dp.e"] == "emmme", f"{opts['dp.e']} {os}"
|
|
153
|
+
assert opts["f"] == "emmmf", f"{opts['f']} {os}"
|
|
154
|
+
assert opts["dp.f"] == "emmmf", f"{opts['dp.f']} {os}"
|
|
155
|
+
assert opts["g"] == "emmmg", f"{opts['g']} {os}"
|
|
156
|
+
assert opts["dp.g"] == "emmmg", f"{opts['dp.g']} {os}"
|
|
157
|
+
assert opts["h"] == "eammh", f"{opts['h']} {os}"
|
|
158
|
+
assert opts["dp.h"] == "eammh", f"{opts['dp.h']} {os}"
|
|
159
|
+
assert opts["i"] == "esmmi", f"{opts['i']} {os}"
|
|
160
|
+
assert opts["dp.i"] == "esmmi", f"{opts['dp.i']} {os}"
|
|
161
|
+
assert opts["j"] == "1mmmj", f"{opts['j']} {os}"
|
|
162
|
+
assert opts["dp.j"] == "1mmmj", f"{opts['dp.j']} {os}"
|
|
163
|
+
assert opts["k"] == "1mmmk", f"{opts['k']} {os}"
|
|
164
|
+
assert opts["dp.k"] == "1mmmk", f"{opts['dp.k']} {os}"
|
|
165
|
+
assert opts["l"] == "1mmml", f"{opts['l']} {os}"
|
|
166
|
+
assert opts["dp.l"] == "1mmml", f"{opts['dp.l']} {os}"
|
|
167
|
+
assert opts["m"] == "1mmmm", f"{opts['m']} {os}"
|
|
168
|
+
assert opts["dp.m"] == "1mmmm", f"{opts['dp.m']} {os}"
|
|
169
|
+
assert opts["n"] == "1ammn", f"{opts['n']} {os}"
|
|
170
|
+
assert opts["dp.n"] == "1ammn", f"{opts['dp.n']} {os}"
|
|
171
|
+
assert opts["o"] == "1smmo", f"{opts['o']} {os}"
|
|
172
|
+
assert opts["dp.o"] == "1smmo", f"{opts['dp.o']} {os}"
|
|
173
|
+
assert opts["p"] == "2mmmp", f"{opts['p']} {os}"
|
|
174
|
+
assert opts["dp.p"] == "2mmmp", f"{opts['dp.p']} {os}"
|
|
175
|
+
assert opts["q"] == "2mmmq", f"{opts['q']} {os}"
|
|
176
|
+
assert opts["dp.q"] == "2mmmq", f"{opts['dp.q']} {os}"
|
|
177
|
+
assert opts["r"] == "2mmmr", f"{opts['r']} {os}"
|
|
178
|
+
assert opts["dp.r"] == "2mmmr", f"{opts['dp.r']} {os}"
|
|
179
|
+
assert opts["s"] == "2mmms", f"{opts['s']} {os}"
|
|
180
|
+
assert opts["dp.s"] == "2mmms", f"{opts['dp.s']} {os}"
|
|
181
|
+
assert opts["t"] == "2ammt", f"{opts['t']} {os}"
|
|
182
|
+
assert opts["dp.t"] == "2ammt", f"{opts['dp.t']} {os}"
|
|
183
|
+
assert opts["u"] == "2smmu", f"{opts['u']} {os}"
|
|
184
|
+
assert opts["dp.u"] == "2smmu", f"{opts['dp.u']} {os}"
|
|
185
|
+
assert opts["v"] == "2mtmv", f"{opts['v']} {os}"
|
|
186
|
+
assert opts["dp.v"] == "2mtmv", f"{opts['dp.v']} {os}"
|
|
187
|
+
assert opts["w"] == "2atmw", f"{opts['w']} {os}"
|
|
188
|
+
assert opts["dp.w"] == "2atmw", f"{opts['dp.w']} {os}"
|
|
189
|
+
assert opts["x"] == "2stmx", f"{opts['x']} {os}"
|
|
190
|
+
assert opts["dp.x"] == "2stmx", f"{opts['dp.x']} {os}"
|
|
191
|
+
assert opts["y"] == f"m{ltr[os]}y", f"{opts['y']} m{ltr[os]}y {os}"
|
|
192
|
+
assert opts["dp.y"] == f"m{ltr[os]}y", f"{opts['dp.y']} m{ltr[os]}y {os}"
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
assert opts["yoodle"] is not None
|
|
196
|
+
assert True, "Should have thrown exception"
|
|
197
|
+
|
|
198
|
+
except KeyError:
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def test_cli_env_in_yaml():
|
|
203
|
+
"""test setting cli and env lookups in the yaml itself"""
|
|
204
|
+
with TemporaryDirectory() as working_dir:
|
|
205
|
+
base_dir = join(working_dir, "base")
|
|
206
|
+
__setup_settings(
|
|
207
|
+
os="Linux",
|
|
208
|
+
shared="test",
|
|
209
|
+
Linux=join(base_dir, "Linux"),
|
|
210
|
+
Darwin=join(base_dir, "macOS"),
|
|
211
|
+
Windows=join(base_dir, "Windows"),
|
|
212
|
+
)
|
|
213
|
+
__write(
|
|
214
|
+
join(base_dir, "main.yml"),
|
|
215
|
+
env={"aa": "alpha", "yy": "yota"},
|
|
216
|
+
cli={"bb": "--beta"},
|
|
217
|
+
yy="main yy",
|
|
218
|
+
aa="main aa",
|
|
219
|
+
bb="main bb",
|
|
220
|
+
)
|
|
221
|
+
__write(
|
|
222
|
+
join(base_dir, "test.yml"),
|
|
223
|
+
env={"zz": "zeta"},
|
|
224
|
+
cli={"dd": "--delta"},
|
|
225
|
+
zz="test zz",
|
|
226
|
+
dd="test dd",
|
|
227
|
+
)
|
|
228
|
+
settings.ENVIRON = {
|
|
229
|
+
"alpha": "environ aa",
|
|
230
|
+
"yota": "environ yy",
|
|
231
|
+
"zeta": "environ zz",
|
|
232
|
+
"delta": "environ dd",
|
|
233
|
+
}
|
|
234
|
+
settings.ARGV = [
|
|
235
|
+
"exe",
|
|
236
|
+
"--beta",
|
|
237
|
+
"cli bb",
|
|
238
|
+
"--zeta",
|
|
239
|
+
"cli zz",
|
|
240
|
+
"--delta",
|
|
241
|
+
"cli dd",
|
|
242
|
+
]
|
|
243
|
+
opts = (
|
|
244
|
+
settings.Settings(join(base_dir, "main.py"), aa="code aa")
|
|
245
|
+
.cli("cli")
|
|
246
|
+
.env("env")
|
|
247
|
+
)
|
|
248
|
+
assert opts["aa"] == "code aa", opts["aa"]
|
|
249
|
+
assert opts["bb"] == "cli bb", opts["bb"]
|
|
250
|
+
assert opts["dd"] == "cli dd", opts["dd"]
|
|
251
|
+
assert opts["yy"] == "environ yy", opts["yy"]
|
|
252
|
+
assert opts["zz"] == "environ zz", opts["zz"]
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def test_environ_values():
|
|
256
|
+
"""test environment variable substitution"""
|
|
257
|
+
with TemporaryDirectory() as working_dir:
|
|
258
|
+
base_dir = join(working_dir, "base")
|
|
259
|
+
__setup_settings(
|
|
260
|
+
os="Linux",
|
|
261
|
+
shared="test",
|
|
262
|
+
Linux=join(base_dir, "Linux"),
|
|
263
|
+
Darwin=join(base_dir, "macOS"),
|
|
264
|
+
Windows=join(base_dir, "Windows"),
|
|
265
|
+
)
|
|
266
|
+
__write(
|
|
267
|
+
join(base_dir, "main.yml"),
|
|
268
|
+
output="${home}/reports",
|
|
269
|
+
settings="${appDir}/settings.json",
|
|
270
|
+
value="testing ${noenv} for noenv",
|
|
271
|
+
)
|
|
272
|
+
settings.ENVIRON = {
|
|
273
|
+
"HOME": "sweet home",
|
|
274
|
+
"APPDIR": "app data dir",
|
|
275
|
+
}
|
|
276
|
+
opts = (
|
|
277
|
+
settings.Settings(join(base_dir, "main.py"), aa="code aa")
|
|
278
|
+
.cli("cli")
|
|
279
|
+
.env("env")
|
|
280
|
+
)
|
|
281
|
+
assert opts["output"] == "sweet home/reports", opts["output"]
|
|
282
|
+
assert opts["settings"] == "app data dir/settings.json", opts["settings"]
|
|
283
|
+
assert opts["value"] == "testing ${noenv} for noenv", opts["value"]
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def test_main():
|
|
287
|
+
"""test the main entry point"""
|
|
288
|
+
with TemporaryDirectory() as working_dir:
|
|
289
|
+
__setup_settings(shared="test", Linux=join(working_dir, "Linux"))
|
|
290
|
+
settings.ARGV = ["ignore", "test"]
|
|
291
|
+
__write(join(working_dir, "Linux", "test.yml"), test=3)
|
|
292
|
+
settings.main()
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
if __name__ == "__main__":
|
|
296
|
+
test_main()
|
|
297
|
+
test_environ_values()
|
|
298
|
+
test_cli_env_in_yaml()
|
|
299
|
+
test_basic()
|