docassemblecli3 0.2.2__tar.gz → 0.3.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.
- {docassemblecli3-0.2.2 → docassemblecli3-0.3.3}/PKG-INFO +14 -7
- {docassemblecli3-0.2.2 → docassemblecli3-0.3.3}/README.md +12 -5
- {docassemblecli3-0.2.2 → docassemblecli3-0.3.3}/docassemblecli3/docassemblecli3.py +346 -86
- {docassemblecli3-0.2.2 → docassemblecli3-0.3.3}/pyproject.toml +3 -3
- {docassemblecli3-0.2.2 → docassemblecli3-0.3.3}/LICENSE +0 -0
- {docassemblecli3-0.2.2 → docassemblecli3-0.3.3}/docassemblecli3/__init__.py +0 -0
- {docassemblecli3-0.2.2 → docassemblecli3-0.3.3}/docassemblecli3/__main__.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: docassemblecli3
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Multi-platform CLI utility for working with docassemble packages and servers
|
|
5
5
|
Keywords: docassemble
|
|
6
6
|
Author-email: Jack Adamson <jackadamson@gmail.com>, Jonathan Pyle <jhpyle@gmail.com>
|
|
7
|
-
Requires-Python: >= 3.
|
|
7
|
+
Requires-Python: >= 3.10
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Programming Language :: Python
|
|
@@ -41,7 +41,7 @@ released under the MIT License.
|
|
|
41
41
|
|
|
42
42
|
## Prerequisites
|
|
43
43
|
|
|
44
|
-
This program should only require that you have Python 3.
|
|
44
|
+
This program should only require that you have Python 3.10 installed on your
|
|
45
45
|
computer, but it was developed and tested with Python 3.12. Please report any
|
|
46
46
|
bugs or errors you experience.
|
|
47
47
|
|
|
@@ -172,7 +172,7 @@ works:
|
|
|
172
172
|
directory]
|
|
173
173
|
-c, --config PATH Specify the config file to use or leave it
|
|
174
174
|
blank to skip using any config file [default:
|
|
175
|
-
C:\Users\
|
|
175
|
+
C:\Users\current_user\.docassemblecli]
|
|
176
176
|
-p, --playground (PROJECT) Install into the default Playground or into the
|
|
177
177
|
specified Playground project.
|
|
178
178
|
-r, --restart [yes|no|auto] On package install: yes, force a restart | no,
|
|
@@ -262,7 +262,7 @@ works:
|
|
|
262
262
|
directory]
|
|
263
263
|
-c, --config PATH Specify the config file to use or leave it
|
|
264
264
|
blank to skip using any config file [default:
|
|
265
|
-
C:\Users\
|
|
265
|
+
C:\Users\current_user\.docassemblecli]
|
|
266
266
|
-p, --playground (PROJECT) Install into the default Playground or into the
|
|
267
267
|
specified Playground project.
|
|
268
268
|
-a, --api <URL TEXT>... URL of the docassemble server and API key of
|
|
@@ -282,11 +282,18 @@ Your package's `.gitignore` file is also used by `watch` to decide which files
|
|
|
282
282
|
to ignore. If you don't have a `.gitignore` file in your package, then the
|
|
283
283
|
default `.gitignore` that `create` makes is used instead. The `.git/` directory
|
|
284
284
|
and `.gitignore` file are both also ignored by `watch` (note: don't add them to
|
|
285
|
-
your `.gitignore`).
|
|
285
|
+
your `.gitignore`). The following directories are always ignored by `watch`:
|
|
286
|
+
`.git`, `__pycache__`, `.mypy_cache`, `.venv`, `.history`, `build`.
|
|
287
|
+
|
|
288
|
+
If you manually add a `path` key to a server in your `.docassemblecli` config
|
|
289
|
+
file, it will cause that server to be used if no `server` is provided and the
|
|
290
|
+
`path` matches the `directory` that `watch` was given
|
|
291
|
+
[default: current directory]. Additionally, if you manually add a `playground`
|
|
292
|
+
key to that server, it will be used when using `watch`.
|
|
286
293
|
|
|
287
294
|
#### watchdog
|
|
288
295
|
|
|
289
|
-
The `watch` command
|
|
296
|
+
The `watch` command depends on the
|
|
290
297
|
[watchdog](https://pypi.org/project/watchdog/) Python package. This allows
|
|
291
298
|
`watch` to work on the following platforms that [watchdog] supports:
|
|
292
299
|
|
|
@@ -22,7 +22,7 @@ released under the MIT License.
|
|
|
22
22
|
|
|
23
23
|
## Prerequisites
|
|
24
24
|
|
|
25
|
-
This program should only require that you have Python 3.
|
|
25
|
+
This program should only require that you have Python 3.10 installed on your
|
|
26
26
|
computer, but it was developed and tested with Python 3.12. Please report any
|
|
27
27
|
bugs or errors you experience.
|
|
28
28
|
|
|
@@ -153,7 +153,7 @@ works:
|
|
|
153
153
|
directory]
|
|
154
154
|
-c, --config PATH Specify the config file to use or leave it
|
|
155
155
|
blank to skip using any config file [default:
|
|
156
|
-
C:\Users\
|
|
156
|
+
C:\Users\current_user\.docassemblecli]
|
|
157
157
|
-p, --playground (PROJECT) Install into the default Playground or into the
|
|
158
158
|
specified Playground project.
|
|
159
159
|
-r, --restart [yes|no|auto] On package install: yes, force a restart | no,
|
|
@@ -243,7 +243,7 @@ works:
|
|
|
243
243
|
directory]
|
|
244
244
|
-c, --config PATH Specify the config file to use or leave it
|
|
245
245
|
blank to skip using any config file [default:
|
|
246
|
-
C:\Users\
|
|
246
|
+
C:\Users\current_user\.docassemblecli]
|
|
247
247
|
-p, --playground (PROJECT) Install into the default Playground or into the
|
|
248
248
|
specified Playground project.
|
|
249
249
|
-a, --api <URL TEXT>... URL of the docassemble server and API key of
|
|
@@ -263,11 +263,18 @@ Your package's `.gitignore` file is also used by `watch` to decide which files
|
|
|
263
263
|
to ignore. If you don't have a `.gitignore` file in your package, then the
|
|
264
264
|
default `.gitignore` that `create` makes is used instead. The `.git/` directory
|
|
265
265
|
and `.gitignore` file are both also ignored by `watch` (note: don't add them to
|
|
266
|
-
your `.gitignore`).
|
|
266
|
+
your `.gitignore`). The following directories are always ignored by `watch`:
|
|
267
|
+
`.git`, `__pycache__`, `.mypy_cache`, `.venv`, `.history`, `build`.
|
|
268
|
+
|
|
269
|
+
If you manually add a `path` key to a server in your `.docassemblecli` config
|
|
270
|
+
file, it will cause that server to be used if no `server` is provided and the
|
|
271
|
+
`path` matches the `directory` that `watch` was given
|
|
272
|
+
[default: current directory]. Additionally, if you manually add a `playground`
|
|
273
|
+
key to that server, it will be used when using `watch`.
|
|
267
274
|
|
|
268
275
|
#### watchdog
|
|
269
276
|
|
|
270
|
-
The `watch` command
|
|
277
|
+
The `watch` command depends on the
|
|
271
278
|
[watchdog](https://pypi.org/project/watchdog/) Python package. This allows
|
|
272
279
|
`watch` to work on the following platforms that [watchdog] supports:
|
|
273
280
|
|
|
@@ -34,6 +34,12 @@ FILE_CHECKSUMS = {}
|
|
|
34
34
|
global DEBUG
|
|
35
35
|
DEBUG = False
|
|
36
36
|
|
|
37
|
+
global EXCLUDED_DIRECTORIES
|
|
38
|
+
EXCLUDED_DIRECTORIES = [".git", "__pycache__", ".mypy_cache", ".venv", ".history", "build"]
|
|
39
|
+
|
|
40
|
+
global GITMATCH_COMPILED
|
|
41
|
+
GITMATCH_COMPILED = None
|
|
42
|
+
|
|
37
43
|
global GITIGNORE
|
|
38
44
|
GITIGNORE = """\
|
|
39
45
|
__pycache__/
|
|
@@ -92,7 +98,13 @@ CONTEXT_SETTINGS = dict(help_option_names=["--help", "-h"])
|
|
|
92
98
|
|
|
93
99
|
@click.group(context_settings=CONTEXT_SETTINGS)
|
|
94
100
|
@click.version_option()
|
|
95
|
-
@click.option(
|
|
101
|
+
@click.option(
|
|
102
|
+
"--color/--no-color",
|
|
103
|
+
"-C/-N",
|
|
104
|
+
default=None,
|
|
105
|
+
show_default=True,
|
|
106
|
+
help="Overrides color auto-detection in interactive terminals.",
|
|
107
|
+
)
|
|
96
108
|
@click.option("--debug/--no-debug", default=False, hidden=True)
|
|
97
109
|
def cli(color, debug):
|
|
98
110
|
"""
|
|
@@ -113,34 +125,76 @@ def config():
|
|
|
113
125
|
|
|
114
126
|
|
|
115
127
|
def common_params_for_api(func):
|
|
116
|
-
@click.option(
|
|
128
|
+
@click.option(
|
|
129
|
+
"--api",
|
|
130
|
+
"-a",
|
|
131
|
+
type=(APIURLType(), str),
|
|
132
|
+
default=(None, None),
|
|
133
|
+
help="URL of the docassemble server and API key of the user (admin or developer)",
|
|
134
|
+
)
|
|
117
135
|
@click.option("--server", "-s", metavar="SERVER", default="", help="Specify a server from the config file")
|
|
118
136
|
@wraps(func)
|
|
119
137
|
def wrapper(*args, **kwargs):
|
|
120
138
|
return func(*args, **kwargs)
|
|
139
|
+
|
|
121
140
|
return wrapper
|
|
122
141
|
|
|
123
142
|
|
|
124
143
|
def common_params_for_config(func):
|
|
125
|
-
@click.option(
|
|
144
|
+
@click.option(
|
|
145
|
+
"--config",
|
|
146
|
+
"-c",
|
|
147
|
+
default=DEFAULT_CONFIG,
|
|
148
|
+
type=click.Path(),
|
|
149
|
+
callback=validate_and_load_or_create_config,
|
|
150
|
+
show_default=True,
|
|
151
|
+
help="Specify the config file to use",
|
|
152
|
+
)
|
|
126
153
|
@wraps(func)
|
|
127
154
|
def wrapper(*args, **kwargs):
|
|
128
155
|
return func(*args, **kwargs)
|
|
156
|
+
|
|
129
157
|
return wrapper
|
|
130
158
|
|
|
131
159
|
|
|
132
160
|
def common_params_for_installation(func):
|
|
133
|
-
@click.option(
|
|
134
|
-
|
|
135
|
-
|
|
161
|
+
@click.option(
|
|
162
|
+
"--directory",
|
|
163
|
+
"-d",
|
|
164
|
+
default=os.getcwd(),
|
|
165
|
+
type=click.Path(),
|
|
166
|
+
callback=validate_package_directory,
|
|
167
|
+
help="Specify package directory [default: current directory]",
|
|
168
|
+
)
|
|
169
|
+
@click.option(
|
|
170
|
+
"--config",
|
|
171
|
+
"-c",
|
|
172
|
+
is_flag=False,
|
|
173
|
+
flag_value="",
|
|
174
|
+
default=DEFAULT_CONFIG,
|
|
175
|
+
type=click.Path(),
|
|
176
|
+
callback=validate_and_load_or_create_config,
|
|
177
|
+
show_default=True,
|
|
178
|
+
help="Specify the config file to use or leave it blank to skip using any config file",
|
|
179
|
+
)
|
|
180
|
+
@click.option(
|
|
181
|
+
"--playground",
|
|
182
|
+
"-p",
|
|
183
|
+
metavar="(PROJECT)",
|
|
184
|
+
is_flag=False,
|
|
185
|
+
flag_value="default",
|
|
186
|
+
help="Install into the default Playground or into the specified Playground project.",
|
|
187
|
+
)
|
|
136
188
|
@wraps(func)
|
|
137
189
|
def wrapper(*args, **kwargs):
|
|
138
190
|
return func(*args, **kwargs)
|
|
191
|
+
|
|
139
192
|
return wrapper
|
|
140
193
|
|
|
141
194
|
|
|
142
195
|
class APIURLType(click.ParamType):
|
|
143
196
|
name = "url"
|
|
197
|
+
|
|
144
198
|
def convert(self, value, param, ctx):
|
|
145
199
|
parsed_url = urlparse(value)
|
|
146
200
|
if all([re.search(r"""^https?://[^\s]+$""", value), parsed_url.scheme, parsed_url.netloc]):
|
|
@@ -154,7 +208,9 @@ def validate_package_directory(ctx, param, directory: str) -> str:
|
|
|
154
208
|
if not os.path.exists(directory):
|
|
155
209
|
raise click.BadParameter(f"""Directory "{directory}" does not exist.""")
|
|
156
210
|
if not os.path.isfile(os.path.join(directory, "setup.py")):
|
|
157
|
-
raise click.BadParameter(
|
|
211
|
+
raise click.BadParameter(
|
|
212
|
+
f"""Directory "{directory}" does not contain a setup.py file, so it is not the directory of a valid Python package."""
|
|
213
|
+
)
|
|
158
214
|
else:
|
|
159
215
|
return directory
|
|
160
216
|
|
|
@@ -185,6 +241,7 @@ def validate_and_load_or_create_config(ctx, param, config: str) -> tuple[str, li
|
|
|
185
241
|
# utility functions
|
|
186
242
|
# -----------------------------------------------------------------------------
|
|
187
243
|
|
|
244
|
+
|
|
188
245
|
def name_from_url(url: str) -> str:
|
|
189
246
|
if not url:
|
|
190
247
|
return ""
|
|
@@ -203,7 +260,7 @@ def display_servers(env: list = None) -> list[str]:
|
|
|
203
260
|
return servers
|
|
204
261
|
|
|
205
262
|
|
|
206
|
-
def select_server(cfg: str = None, env: list = None, apiurl: str = None, apikey: str = None, server: str = "") -> dict:
|
|
263
|
+
def select_server(cfg: str = None, env: list = None, apiurl: str = None, apikey: str = None, server: str = "", **kwargs) -> dict:
|
|
207
264
|
if apiurl and apikey:
|
|
208
265
|
return add_server_to_env(cfg=cfg, env=env, apiurl=apiurl, apikey=apikey)[-1]
|
|
209
266
|
if isinstance(env, list):
|
|
@@ -216,6 +273,10 @@ def select_server(cfg: str = None, env: list = None, apiurl: str = None, apikey:
|
|
|
216
273
|
return item
|
|
217
274
|
raise click.BadParameter(f"""Server "{server}" was not found.""", param_hint="--server")
|
|
218
275
|
if len(env) > 0:
|
|
276
|
+
if "watch" in kwargs:
|
|
277
|
+
for item in env:
|
|
278
|
+
if item.get("path", None) == kwargs["watch"]:
|
|
279
|
+
return item
|
|
219
280
|
return env[0]
|
|
220
281
|
if "DOCASSEMBLEAPIURL" in os.environ and "DOCASSEMBLEAPIKEY" in os.environ:
|
|
221
282
|
apiurl: str = os.environ["DOCASSEMBLEAPIURL"]
|
|
@@ -256,7 +317,11 @@ def prompt_for_api(retry: str = False, previous_url: str = None, previous_key: s
|
|
|
256
317
|
if retry:
|
|
257
318
|
if not click.confirm("Do you want to try another URL and API key?", default=True):
|
|
258
319
|
raise click.Abort()
|
|
259
|
-
apiurl = click.prompt(
|
|
320
|
+
apiurl = click.prompt(
|
|
321
|
+
"""Base URL of your docassemble server (e.g., https://da.example.com)""",
|
|
322
|
+
type=APIURLType(),
|
|
323
|
+
default=previous_url,
|
|
324
|
+
)
|
|
260
325
|
apikey = click.prompt(f"""API key of admin or developer user on {apiurl}""", default=previous_key).strip()
|
|
261
326
|
return apiurl, apikey
|
|
262
327
|
|
|
@@ -267,9 +332,13 @@ def test_apiurl_apikey(apiurl: str, apikey: str) -> bool:
|
|
|
267
332
|
api_test = requests.get(apiurl + "/api/package", headers={"X-API-Key": apikey})
|
|
268
333
|
if api_test.status_code != 200:
|
|
269
334
|
if api_test.status_code == 403:
|
|
270
|
-
click.secho(
|
|
335
|
+
click.secho(
|
|
336
|
+
f"""\nThe API KEY is invalid. ({api_test.status_code} {api_test.text.strip()})\n""", fg="red"
|
|
337
|
+
)
|
|
271
338
|
else:
|
|
272
|
-
click.secho(
|
|
339
|
+
click.secho(
|
|
340
|
+
f"""\nThe API URL or KEY is invalid. ({api_test.status_code} {api_test.text.strip()})\n""", fg="red"
|
|
341
|
+
)
|
|
273
342
|
return False
|
|
274
343
|
except Exception as err:
|
|
275
344
|
click.secho(f"""\n{err.__class__.__name__}""", fg="red")
|
|
@@ -291,14 +360,14 @@ def add_server_to_env(cfg: str = None, env: list = None, apiurl: str = None, api
|
|
|
291
360
|
return env
|
|
292
361
|
|
|
293
362
|
|
|
294
|
-
def select_env(cfg: str = None, env: list = None, apiurl: str = None, apikey: str = None, server: str = None) -> dict:
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
363
|
+
# def select_env(cfg: str = None, env: list = None, apiurl: str = None, apikey: str = None, server: str = None) -> dict:
|
|
364
|
+
# if apiurl and apikey:
|
|
365
|
+
# return add_server_to_env(cfg=cfg, env=env, apiurl=apiurl, apikey=apikey)[-1]
|
|
366
|
+
# else:
|
|
367
|
+
# return select_server(cfg=cfg, env=env, server=server)
|
|
299
368
|
|
|
300
369
|
|
|
301
|
-
def wait_for_server(playground:bool, task_id: str, apikey: str, apiurl: str, server_version_da: str = "0"):
|
|
370
|
+
def wait_for_server(playground: bool, task_id: str, apikey: str, apiurl: str, server_version_da: str = "0"):
|
|
302
371
|
click.secho("Waiting for package to install...", fg="cyan")
|
|
303
372
|
tries = 0
|
|
304
373
|
before_wait_for_server = time.time()
|
|
@@ -312,7 +381,7 @@ def wait_for_server(playground:bool, task_id: str, apikey: str, apiurl: str, ser
|
|
|
312
381
|
except requests.exceptions.RequestException:
|
|
313
382
|
pass
|
|
314
383
|
if r.status_code != 200:
|
|
315
|
-
return
|
|
384
|
+
return "package_update_status returned " + str(r.status_code) + ": " + r.text
|
|
316
385
|
info = r.json()
|
|
317
386
|
if info["status"] == "completed" or info["status"] == "unknown":
|
|
318
387
|
break
|
|
@@ -325,7 +394,10 @@ def wait_for_server(playground:bool, task_id: str, apikey: str, apiurl: str, ser
|
|
|
325
394
|
success = True
|
|
326
395
|
elif info.get("ok", False):
|
|
327
396
|
success = True
|
|
328
|
-
if not (
|
|
397
|
+
if not (
|
|
398
|
+
server_version_da == "norestart"
|
|
399
|
+
or packaging_version.parse(server_version_da) >= packaging_version.parse("1.5.3")
|
|
400
|
+
):
|
|
329
401
|
if DEBUG:
|
|
330
402
|
click.echo(f"""Package install duration: {(after_wait_for_server - before_wait_for_server):.2f}s""")
|
|
331
403
|
click.echo("""Manually waiting for background processes.""")
|
|
@@ -345,11 +417,19 @@ def wait_for_server(playground:bool, task_id: str, apikey: str, apiurl: str, ser
|
|
|
345
417
|
# package_installer
|
|
346
418
|
# -----------------------------------------------------------------------------
|
|
347
419
|
|
|
420
|
+
|
|
348
421
|
def package_installer(directory, apiurl, apikey, playground, restart):
|
|
349
422
|
archive = tempfile.NamedTemporaryFile(suffix=".zip")
|
|
350
423
|
zf = zipfile.ZipFile(archive, compression=zipfile.ZIP_DEFLATED, mode="w")
|
|
351
424
|
try:
|
|
352
|
-
ignore_process = subprocess.run(
|
|
425
|
+
ignore_process = subprocess.run(
|
|
426
|
+
["git", "ls-files", "-i", "--directory", "-o", "--exclude-standard"],
|
|
427
|
+
stdout=subprocess.PIPE,
|
|
428
|
+
stderr=subprocess.PIPE,
|
|
429
|
+
universal_newlines=True,
|
|
430
|
+
cwd=directory,
|
|
431
|
+
check=False,
|
|
432
|
+
)
|
|
353
433
|
ignore_process.check_returncode()
|
|
354
434
|
raw_ignore = ignore_process.stdout.splitlines()
|
|
355
435
|
except Exception:
|
|
@@ -361,7 +441,13 @@ def package_installer(directory, apiurl, apikey, playground, restart):
|
|
|
361
441
|
dependencies = {}
|
|
362
442
|
for root, dirs, files in os.walk(directory, topdown=True):
|
|
363
443
|
adjusted_root = os.sep.join(root.split(os.sep)[1:])
|
|
364
|
-
dirs[:] = [
|
|
444
|
+
dirs[:] = [
|
|
445
|
+
d
|
|
446
|
+
for d in dirs
|
|
447
|
+
if d not in EXCLUDED_DIRECTORIES
|
|
448
|
+
and not d.endswith(".egg-info")
|
|
449
|
+
and os.path.join(adjusted_root, d) not in to_ignore
|
|
450
|
+
]
|
|
365
451
|
if root_directory is None and ("setup.py" in files or "setup.cfg" in files):
|
|
366
452
|
root_directory = root
|
|
367
453
|
if "setup.py" in files:
|
|
@@ -374,24 +460,48 @@ def package_installer(directory, apiurl, apikey, playground, restart):
|
|
|
374
460
|
if m:
|
|
375
461
|
for package_text in m.group(1).split(","):
|
|
376
462
|
package_name = package_text.strip()
|
|
377
|
-
if
|
|
463
|
+
if (
|
|
464
|
+
len(package_name) >= 3
|
|
465
|
+
and package_name[0] == package_name[-1]
|
|
466
|
+
and package_name[0] in (""", """)
|
|
467
|
+
):
|
|
378
468
|
package_name = package_name[1:-1]
|
|
379
469
|
mm = re.search(r"""(.*)(<=|>=|==|<|>)(.*)""", package_name)
|
|
380
470
|
if mm:
|
|
381
|
-
dependencies[mm.group(1).strip()] = {
|
|
471
|
+
dependencies[mm.group(1).strip()] = {
|
|
472
|
+
"installed": False,
|
|
473
|
+
"operator": mm.group(2),
|
|
474
|
+
"version": mm.group(3).strip(),
|
|
475
|
+
}
|
|
382
476
|
else:
|
|
383
477
|
dependencies[package_name] = {"installed": False, "operator": None, "version": None}
|
|
384
478
|
for the_file in files:
|
|
385
|
-
if
|
|
479
|
+
if (
|
|
480
|
+
the_file.endswith("~")
|
|
481
|
+
or the_file.endswith(".pyc")
|
|
482
|
+
or the_file.endswith(".swp")
|
|
483
|
+
or the_file.startswith("#")
|
|
484
|
+
or the_file.startswith(".#")
|
|
485
|
+
or (the_file == ".gitignore" and root_directory == root)
|
|
486
|
+
or os.path.join(adjusted_root, the_file) in to_ignore
|
|
487
|
+
):
|
|
386
488
|
continue
|
|
387
|
-
if
|
|
489
|
+
if (
|
|
490
|
+
not has_python_files
|
|
491
|
+
and the_file.endswith(".py")
|
|
492
|
+
and not (the_file == "setup.py" and root == root_directory)
|
|
493
|
+
and the_file != "__init__.py"
|
|
494
|
+
):
|
|
388
495
|
has_python_files = True
|
|
389
|
-
zf.write(
|
|
496
|
+
zf.write(
|
|
497
|
+
os.path.join(root, the_file),
|
|
498
|
+
os.path.relpath(os.path.join(root, the_file), os.path.join(directory, "..")),
|
|
499
|
+
)
|
|
390
500
|
zf.close()
|
|
391
501
|
archive.seek(0)
|
|
392
502
|
if restart == "no":
|
|
393
503
|
should_restart = False
|
|
394
|
-
elif restart =="yes" or has_python_files:
|
|
504
|
+
elif restart == "yes" or has_python_files:
|
|
395
505
|
should_restart = True
|
|
396
506
|
elif len(dependencies) > 0 or this_package_name:
|
|
397
507
|
try:
|
|
@@ -400,7 +510,7 @@ def package_installer(directory, apiurl, apikey, playground, restart):
|
|
|
400
510
|
click.secho(f"""\n{err.__class__.__name__}""", fg="red")
|
|
401
511
|
raise click.ClickException(f"""{err}\n""")
|
|
402
512
|
if r.status_code != 200:
|
|
403
|
-
return
|
|
513
|
+
return "/api/package returned " + str(r.status_code) + ": " + r.text
|
|
404
514
|
installed_packages = r.json()
|
|
405
515
|
already_installed = False
|
|
406
516
|
for package_info in installed_packages:
|
|
@@ -410,20 +520,33 @@ def package_installer(directory, apiurl, apikey, playground, restart):
|
|
|
410
520
|
condition = True
|
|
411
521
|
if dependency_info["operator"]:
|
|
412
522
|
if dependency_info["operator"] == "==":
|
|
413
|
-
condition = packaging_version.parse(package_info["version"]) == packaging_version.parse(
|
|
523
|
+
condition = packaging_version.parse(package_info["version"]) == packaging_version.parse(
|
|
524
|
+
dependency_info["version"]
|
|
525
|
+
)
|
|
414
526
|
elif dependency_info["operator"] == "<=":
|
|
415
|
-
condition = packaging_version.parse(package_info["version"]) <= packaging_version.parse(
|
|
527
|
+
condition = packaging_version.parse(package_info["version"]) <= packaging_version.parse(
|
|
528
|
+
dependency_info["version"]
|
|
529
|
+
)
|
|
416
530
|
elif dependency_info["operator"] == ">=":
|
|
417
|
-
condition = packaging_version.parse(package_info["version"]) >= packaging_version.parse(
|
|
531
|
+
condition = packaging_version.parse(package_info["version"]) >= packaging_version.parse(
|
|
532
|
+
dependency_info["version"]
|
|
533
|
+
)
|
|
418
534
|
elif dependency_info["operator"] == "<":
|
|
419
|
-
condition = packaging_version.parse(package_info["version"]) < packaging_version.parse(
|
|
535
|
+
condition = packaging_version.parse(package_info["version"]) < packaging_version.parse(
|
|
536
|
+
dependency_info["version"]
|
|
537
|
+
)
|
|
420
538
|
elif dependency_info["operator"] == ">":
|
|
421
|
-
condition = packaging_version.parse(package_info["version"]) > packaging_version.parse(
|
|
539
|
+
condition = packaging_version.parse(package_info["version"]) > packaging_version.parse(
|
|
540
|
+
dependency_info["version"]
|
|
541
|
+
)
|
|
422
542
|
if condition:
|
|
423
543
|
dependency_info["installed"] = True
|
|
424
544
|
if this_package_name and this_package_name in (package_info["name"], package_info["alt_name"]):
|
|
425
545
|
already_installed = True
|
|
426
|
-
should_restart = bool(
|
|
546
|
+
should_restart = bool(
|
|
547
|
+
(not already_installed and len(dependencies) > 0)
|
|
548
|
+
or not all(item["installed"] for item in dependencies.values())
|
|
549
|
+
)
|
|
427
550
|
else:
|
|
428
551
|
should_restart = True
|
|
429
552
|
data = {}
|
|
@@ -458,12 +581,20 @@ def package_installer(directory, apiurl, apikey, playground, restart):
|
|
|
458
581
|
try:
|
|
459
582
|
requests.post(project_endpoint, data={"project": playground}, headers={"X-API-Key": apikey})
|
|
460
583
|
except Exception:
|
|
461
|
-
return
|
|
584
|
+
return "create project POST returned " + project_list.text
|
|
462
585
|
else:
|
|
463
586
|
click.echo("\n")
|
|
464
|
-
return
|
|
587
|
+
return (
|
|
588
|
+
"playground list of projects GET returned " + str(project_list.status_code) + ": " + project_list.text
|
|
589
|
+
)
|
|
465
590
|
try:
|
|
466
|
-
r = requests.post(
|
|
591
|
+
r = requests.post(
|
|
592
|
+
apiurl + "/api/playground_install",
|
|
593
|
+
data=data,
|
|
594
|
+
files={"file": archive},
|
|
595
|
+
headers={"X-API-Key": apikey},
|
|
596
|
+
timeout=600,
|
|
597
|
+
)
|
|
467
598
|
except Exception as err:
|
|
468
599
|
click.secho(f"""\n{err.__class__.__name__}""", fg="red")
|
|
469
600
|
raise click.ClickException(f"""{err}\n""")
|
|
@@ -473,17 +604,33 @@ def package_installer(directory, apiurl, apikey, playground, restart):
|
|
|
473
604
|
except Exception:
|
|
474
605
|
error_message = ""
|
|
475
606
|
if "project" not in data or error_message != "Invalid project.":
|
|
476
|
-
return
|
|
607
|
+
return "playground_install POST returned " + str(r.status_code) + ": " + r.text
|
|
477
608
|
try:
|
|
478
|
-
r = requests.post(
|
|
609
|
+
r = requests.post(
|
|
610
|
+
apiurl + "/api/playground/project",
|
|
611
|
+
data={"project": data["project"]},
|
|
612
|
+
headers={"X-API-Key": apikey},
|
|
613
|
+
timeout=600,
|
|
614
|
+
)
|
|
479
615
|
except Exception as err:
|
|
480
616
|
click.secho(f"""\n{err.__class__.__name__}""", fg="red")
|
|
481
617
|
raise click.ClickException(f"""{err}\n""")
|
|
482
618
|
if r.status_code != 204:
|
|
483
|
-
return
|
|
619
|
+
return (
|
|
620
|
+
"needed to create playground project but POST to api/playground/project returned "
|
|
621
|
+
+ str(r.status_code)
|
|
622
|
+
+ ": "
|
|
623
|
+
+ r.text
|
|
624
|
+
)
|
|
484
625
|
archive.seek(0)
|
|
485
626
|
try:
|
|
486
|
-
r = requests.post(
|
|
627
|
+
r = requests.post(
|
|
628
|
+
apiurl + "/api/playground_install",
|
|
629
|
+
data=data,
|
|
630
|
+
files={"file": archive},
|
|
631
|
+
headers={"X-API-Key": apikey},
|
|
632
|
+
timeout=600,
|
|
633
|
+
)
|
|
487
634
|
except Exception as err:
|
|
488
635
|
click.secho(f"""\n{err.__class__.__name__}""", fg="red")
|
|
489
636
|
raise click.ClickException(f"""{err}\n""")
|
|
@@ -491,14 +638,20 @@ def package_installer(directory, apiurl, apikey, playground, restart):
|
|
|
491
638
|
try:
|
|
492
639
|
info = r.json()
|
|
493
640
|
except Exception:
|
|
494
|
-
return
|
|
641
|
+
return r.text
|
|
495
642
|
task_id = info["task_id"]
|
|
496
|
-
success = wait_for_server(
|
|
643
|
+
success = wait_for_server(
|
|
644
|
+
playground=bool(playground),
|
|
645
|
+
task_id=task_id,
|
|
646
|
+
apikey=apikey,
|
|
647
|
+
apiurl=apiurl,
|
|
648
|
+
server_version_da=server_version_da,
|
|
649
|
+
)
|
|
497
650
|
elif r.status_code == 204:
|
|
498
651
|
success = True
|
|
499
652
|
else:
|
|
500
653
|
click.echo("\n")
|
|
501
|
-
return
|
|
654
|
+
return "playground_install POST returned " + str(r.status_code) + ": " + r.text
|
|
502
655
|
if success:
|
|
503
656
|
click.secho(f"""[{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}] Installed.""", fg="green")
|
|
504
657
|
else:
|
|
@@ -506,15 +659,23 @@ def package_installer(directory, apiurl, apikey, playground, restart):
|
|
|
506
659
|
return 1
|
|
507
660
|
else:
|
|
508
661
|
try:
|
|
509
|
-
r = requests.post(
|
|
662
|
+
r = requests.post(
|
|
663
|
+
apiurl + "/api/package", data=data, files={"zip": archive}, headers={"X-API-Key": apikey}, timeout=600
|
|
664
|
+
)
|
|
510
665
|
except Exception as err:
|
|
511
666
|
click.secho(f"""\n{err.__class__.__name__}""", fg="red")
|
|
512
667
|
raise click.ClickException(f"""{err}\n""")
|
|
513
668
|
if r.status_code != 200:
|
|
514
|
-
return
|
|
669
|
+
return "package POST returned " + str(r.status_code) + ": " + r.text
|
|
515
670
|
info = r.json()
|
|
516
671
|
task_id = info["task_id"]
|
|
517
|
-
if wait_for_server(
|
|
672
|
+
if wait_for_server(
|
|
673
|
+
playground=bool(playground),
|
|
674
|
+
task_id=task_id,
|
|
675
|
+
apikey=apikey,
|
|
676
|
+
apiurl=apiurl,
|
|
677
|
+
server_version_da=server_version_da,
|
|
678
|
+
):
|
|
518
679
|
click.secho(f"""[{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}] Installed.""", fg="green")
|
|
519
680
|
if not should_restart:
|
|
520
681
|
try:
|
|
@@ -523,7 +684,7 @@ def package_installer(directory, apiurl, apikey, playground, restart):
|
|
|
523
684
|
click.secho(f"""\n{err.__class__.__name__}""", fg="red")
|
|
524
685
|
raise click.ClickException(f"""{err}\n""")
|
|
525
686
|
if r.status_code != 204:
|
|
526
|
-
return
|
|
687
|
+
return "clear_cache returned " + str(r.status_code) + ": " + r.text
|
|
527
688
|
return 0
|
|
528
689
|
|
|
529
690
|
|
|
@@ -531,10 +692,18 @@ def package_installer(directory, apiurl, apikey, playground, restart):
|
|
|
531
692
|
# install
|
|
532
693
|
# =============================================================================
|
|
533
694
|
|
|
695
|
+
|
|
534
696
|
@cli.command(context_settings=CONTEXT_SETTINGS)
|
|
535
697
|
@common_params_for_api
|
|
536
698
|
@common_params_for_installation
|
|
537
|
-
@click.option(
|
|
699
|
+
@click.option(
|
|
700
|
+
"--restart",
|
|
701
|
+
"-r",
|
|
702
|
+
type=click.Choice(["yes", "no", "auto"]),
|
|
703
|
+
default="auto",
|
|
704
|
+
show_default=True,
|
|
705
|
+
help="On package install: yes, force a restart | no, do not restart | auto, only restart if the package has any .py files or if there are dependencies to be installed",
|
|
706
|
+
)
|
|
538
707
|
def install(directory, config, api, server, playground, restart):
|
|
539
708
|
"""
|
|
540
709
|
Install a docassemble package on a docassemble server.
|
|
@@ -548,7 +717,13 @@ def install(directory, config, api, server, playground, restart):
|
|
|
548
717
|
else:
|
|
549
718
|
click.echo(f"""Location: Playground "{playground}" """)
|
|
550
719
|
click.secho(f"""[{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}] Installing...""", fg="yellow")
|
|
551
|
-
package_installer(
|
|
720
|
+
package_installer(
|
|
721
|
+
directory=directory,
|
|
722
|
+
apiurl=selected_server["apiurl"],
|
|
723
|
+
apikey=selected_server["apikey"],
|
|
724
|
+
playground=playground,
|
|
725
|
+
restart=restart,
|
|
726
|
+
)
|
|
552
727
|
return 0
|
|
553
728
|
|
|
554
729
|
|
|
@@ -556,11 +731,12 @@ def install(directory, config, api, server, playground, restart):
|
|
|
556
731
|
# watchdog & hashlib
|
|
557
732
|
# -----------------------------------------------------------------------------
|
|
558
733
|
|
|
734
|
+
|
|
559
735
|
def calculate_md5(filepath: str) -> str:
|
|
560
736
|
hash_md5 = hashlib.md5()
|
|
561
737
|
try:
|
|
562
738
|
with open(filepath, "rb") as f:
|
|
563
|
-
|
|
739
|
+
while chunk := f.read(4096):
|
|
564
740
|
hash_md5.update(chunk)
|
|
565
741
|
except FileNotFoundError:
|
|
566
742
|
return ""
|
|
@@ -568,25 +744,35 @@ def calculate_md5(filepath: str) -> str:
|
|
|
568
744
|
|
|
569
745
|
|
|
570
746
|
def scan_directory(directory):
|
|
747
|
+
if DEBUG:
|
|
748
|
+
click.secho("Scanning files...", fg="cyan")
|
|
571
749
|
global FILE_CHECKSUMS
|
|
572
|
-
for
|
|
750
|
+
for current_directory, subdirectories, files in os.walk(directory):
|
|
751
|
+
excluded_directories = EXCLUDED_DIRECTORIES
|
|
752
|
+
subdirectories[:] = [d for d in subdirectories if d not in excluded_directories]
|
|
573
753
|
for file in files:
|
|
574
|
-
filepath = os.path.join(
|
|
754
|
+
filepath = os.path.join(current_directory, file)
|
|
575
755
|
if not matches_ignore_patterns(path=filepath, directory=directory):
|
|
576
756
|
FILE_CHECKSUMS[filepath] = calculate_md5(filepath)
|
|
757
|
+
if DEBUG:
|
|
758
|
+
click.secho("Scanning complete.", fg="green")
|
|
577
759
|
|
|
578
760
|
|
|
579
761
|
def matches_ignore_patterns(path: str, directory: str) -> bool:
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
762
|
+
global GITMATCH_COMPILED
|
|
763
|
+
if not GITMATCH_COMPILED:
|
|
764
|
+
if DEBUG:
|
|
765
|
+
click.echo("GITMATCH_COMPILED")
|
|
766
|
+
if os.path.exists(gitignore_path := os.path.join(directory, ".gitignore")):
|
|
767
|
+
with open(gitignore_path) as file:
|
|
768
|
+
ignore_patterns = [line.strip() for line in file]
|
|
769
|
+
else:
|
|
770
|
+
ignore_patterns = GITIGNORE.split("\n")
|
|
771
|
+
ignore_patterns.extend([".git/", ".gitignore"])
|
|
772
|
+
GITMATCH_COMPILED = gitmatch.compile(ignore_patterns)
|
|
587
773
|
# Convert the absolute path to a relative path for gitmatch to work
|
|
588
774
|
path = os.path.relpath(path, directory)
|
|
589
|
-
return
|
|
775
|
+
return GITMATCH_COMPILED.match(path=path)
|
|
590
776
|
|
|
591
777
|
|
|
592
778
|
class WatchHandler(FileSystemEventHandler):
|
|
@@ -601,7 +787,9 @@ class WatchHandler(FileSystemEventHandler):
|
|
|
601
787
|
if event.event_type == "created" or event.event_type == "modified":
|
|
602
788
|
if not matches_ignore_patterns(path=event.src_path.replace("\\", "/"), directory=self.directory):
|
|
603
789
|
new_checksum = calculate_md5(event.src_path)
|
|
604
|
-
if event.src_path not in FILE_CHECKSUMS or (
|
|
790
|
+
if event.src_path not in FILE_CHECKSUMS or (
|
|
791
|
+
new_checksum and FILE_CHECKSUMS[event.src_path] != new_checksum
|
|
792
|
+
):
|
|
605
793
|
FILE_CHECKSUMS[event.src_path] = new_checksum
|
|
606
794
|
LAST_MODIFIED["time"] = time.time()
|
|
607
795
|
LAST_MODIFIED["files"][str(event.src_path)] = True
|
|
@@ -613,16 +801,31 @@ class WatchHandler(FileSystemEventHandler):
|
|
|
613
801
|
# watch
|
|
614
802
|
# =============================================================================
|
|
615
803
|
|
|
804
|
+
|
|
616
805
|
@cli.command(context_settings=CONTEXT_SETTINGS)
|
|
617
806
|
@common_params_for_installation
|
|
618
807
|
@common_params_for_api
|
|
619
|
-
@click.option(
|
|
620
|
-
|
|
808
|
+
@click.option(
|
|
809
|
+
"--restart",
|
|
810
|
+
"-r",
|
|
811
|
+
type=click.Choice(["yes", "no", "auto"]),
|
|
812
|
+
default="auto",
|
|
813
|
+
show_default=True,
|
|
814
|
+
help="On package install: yes, force a restart | no, do not restart | auto, only restart if any .py files were changed",
|
|
815
|
+
)
|
|
816
|
+
@click.option(
|
|
817
|
+
"--buffer",
|
|
818
|
+
"-b",
|
|
819
|
+
metavar="SECONDS",
|
|
820
|
+
default=3,
|
|
821
|
+
show_default=True,
|
|
822
|
+
help="(On server restart only) Set the buffer (wait time) between a file change event and package installation. If you are experiencing multiple installs back-to-back, try increasing this value.",
|
|
823
|
+
)
|
|
621
824
|
def watch(directory, config, api, server, playground, restart, buffer):
|
|
622
825
|
"""
|
|
623
826
|
Watch a package directory and `install` any changes. Press Ctrl + c to exit.
|
|
624
827
|
"""
|
|
625
|
-
selected_server = select_server(*config, *api, server)
|
|
828
|
+
selected_server = select_server(*config, *api, server, watch=directory)
|
|
626
829
|
restart_param = restart
|
|
627
830
|
scan_directory(directory)
|
|
628
831
|
global LAST_MODIFIED
|
|
@@ -632,10 +835,14 @@ def watch(directory, config, api, server, playground, restart, buffer):
|
|
|
632
835
|
observer.start()
|
|
633
836
|
click.echo()
|
|
634
837
|
click.echo(f"""Server: {selected_server["name"]}""")
|
|
838
|
+
|
|
839
|
+
if "path" in selected_server and selected_server["path"] == directory:
|
|
840
|
+
playground = selected_server.get("playground", playground)
|
|
635
841
|
if not playground:
|
|
636
842
|
click.echo("Location: Package")
|
|
637
843
|
else:
|
|
638
844
|
click.echo(f"""Location: Playground "{playground}" """)
|
|
845
|
+
|
|
639
846
|
click.echo(f"""Watching: {directory}""")
|
|
640
847
|
click.secho(f"""[{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}] Started""", fg="green")
|
|
641
848
|
try:
|
|
@@ -652,20 +859,27 @@ def watch(directory, config, api, server, playground, restart, buffer):
|
|
|
652
859
|
LAST_MODIFIED["time"] = 0
|
|
653
860
|
LAST_MODIFIED["files"] = {}
|
|
654
861
|
LAST_MODIFIED["restart"] = False
|
|
655
|
-
package_installer(
|
|
862
|
+
package_installer(
|
|
863
|
+
directory=directory,
|
|
864
|
+
apiurl=selected_server["apiurl"],
|
|
865
|
+
apikey=selected_server["apikey"],
|
|
866
|
+
playground=playground,
|
|
867
|
+
restart=restart,
|
|
868
|
+
)
|
|
656
869
|
time.sleep(1)
|
|
657
870
|
except Exception as e:
|
|
658
871
|
click.echo(f"\nException occurred: {e}")
|
|
659
872
|
finally:
|
|
660
873
|
observer.stop()
|
|
661
874
|
observer.join()
|
|
662
|
-
return
|
|
875
|
+
return """\nStopping "docassemblecli3 watch"."""
|
|
663
876
|
|
|
664
877
|
|
|
665
878
|
# =============================================================================
|
|
666
879
|
# create
|
|
667
880
|
# =============================================================================
|
|
668
881
|
|
|
882
|
+
|
|
669
883
|
@cli.command(context_settings=CONTEXT_SETTINGS)
|
|
670
884
|
@click.option("--package", metavar="PACKAGE", help="Name of the package you want to create")
|
|
671
885
|
@click.option("--developer-name", metavar="NAME", help="Name of the developer of the package")
|
|
@@ -681,10 +895,10 @@ def create(package, developer_name, developer_email, description, url, license,
|
|
|
681
895
|
"""
|
|
682
896
|
pkgname = package
|
|
683
897
|
if not pkgname:
|
|
684
|
-
|
|
898
|
+
pkgname = click.prompt("Name of the package you want to create (e.g., childsupport)")
|
|
685
899
|
pkgname = re.sub(r"\s", "", pkgname)
|
|
686
900
|
if not pkgname:
|
|
687
|
-
return
|
|
901
|
+
return "The package name you entered is invalid."
|
|
688
902
|
pkgname = re.sub(r"^docassemble[\-\.]", "", pkgname, flags=re.IGNORECASE)
|
|
689
903
|
if output:
|
|
690
904
|
packagedir = output
|
|
@@ -692,10 +906,10 @@ def create(package, developer_name, developer_email, description, url, license,
|
|
|
692
906
|
packagedir = "docassemble-" + pkgname
|
|
693
907
|
if os.path.exists(packagedir):
|
|
694
908
|
if not os.path.isdir(packagedir):
|
|
695
|
-
return
|
|
909
|
+
return "Cannot create the directory " + packagedir + " because the path already exists."
|
|
696
910
|
dir_listing = list(os.listdir(packagedir))
|
|
697
911
|
if "setup.py" in dir_listing or "setup.cfg" in dir_listing:
|
|
698
|
-
return
|
|
912
|
+
return "The directory " + packagedir + " already has a package in it."
|
|
699
913
|
else:
|
|
700
914
|
os.makedirs(packagedir, exist_ok=True)
|
|
701
915
|
if not developer_name:
|
|
@@ -724,7 +938,12 @@ __import__("pkg_resources").declare_namespace(__name__)
|
|
|
724
938
|
|
|
725
939
|
"""
|
|
726
940
|
if "MIT" in license:
|
|
727
|
-
licensetext =
|
|
941
|
+
licensetext = (
|
|
942
|
+
"The MIT License (MIT)\n\nCopyright (c) "
|
|
943
|
+
+ str(datetime.datetime.now().year)
|
|
944
|
+
+ " "
|
|
945
|
+
+ developer_name
|
|
946
|
+
+ """
|
|
728
947
|
|
|
729
948
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
730
949
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -744,10 +963,21 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
744
963
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
745
964
|
SOFTWARE.
|
|
746
965
|
"""
|
|
966
|
+
)
|
|
747
967
|
else:
|
|
748
968
|
licensetext = license + "\n"
|
|
749
969
|
|
|
750
|
-
readme =
|
|
970
|
+
readme = (
|
|
971
|
+
"# docassemble."
|
|
972
|
+
+ pkgname
|
|
973
|
+
+ "\n\n"
|
|
974
|
+
+ description
|
|
975
|
+
+ "\n\n## Author\n\n"
|
|
976
|
+
+ developer_name
|
|
977
|
+
+ ", "
|
|
978
|
+
+ developer_email
|
|
979
|
+
+ "\n"
|
|
980
|
+
)
|
|
751
981
|
manifestin = """\
|
|
752
982
|
include README.md
|
|
753
983
|
"""
|
|
@@ -802,22 +1032,44 @@ def find_package_data(where=".", package="", exclude=standard_exclude, exclude_d
|
|
|
802
1032
|
return out
|
|
803
1033
|
|
|
804
1034
|
"""
|
|
805
|
-
setuppy +=
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
1035
|
+
setuppy += (
|
|
1036
|
+
"setup(name="
|
|
1037
|
+
+ repr("docassemble." + pkgname)
|
|
1038
|
+
+ """,
|
|
1039
|
+
version="""
|
|
1040
|
+
+ repr(version)
|
|
1041
|
+
+ """,
|
|
1042
|
+
description=("""
|
|
1043
|
+
+ repr(description)
|
|
1044
|
+
+ """),
|
|
1045
|
+
long_description="""
|
|
1046
|
+
+ repr(readme)
|
|
1047
|
+
+ """,
|
|
809
1048
|
long_description_content_type="text/markdown",
|
|
810
|
-
author="""
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
1049
|
+
author="""
|
|
1050
|
+
+ repr(developer_name)
|
|
1051
|
+
+ """,
|
|
1052
|
+
author_email="""
|
|
1053
|
+
+ repr(developer_email)
|
|
1054
|
+
+ """,
|
|
1055
|
+
license="""
|
|
1056
|
+
+ repr(license)
|
|
1057
|
+
+ """,
|
|
1058
|
+
url="""
|
|
1059
|
+
+ repr(package_url)
|
|
1060
|
+
+ """,
|
|
814
1061
|
packages=find_packages(),
|
|
815
1062
|
namespace_packages=["docassemble"],
|
|
816
1063
|
install_requires=[],
|
|
817
1064
|
zip_safe=False,
|
|
818
|
-
package_data=find_package_data(where='docassemble/"""
|
|
1065
|
+
package_data=find_package_data(where='docassemble/"""
|
|
1066
|
+
+ pkgname
|
|
1067
|
+
+ """/', package='docassemble."""
|
|
1068
|
+
+ pkgname
|
|
1069
|
+
+ """'),
|
|
819
1070
|
)
|
|
820
1071
|
"""
|
|
1072
|
+
)
|
|
821
1073
|
# maindir = os.path.join(packagedir, "docassemble", pkgname)
|
|
822
1074
|
questionsdir = os.path.join(packagedir, "docassemble", pkgname, "data", "questions")
|
|
823
1075
|
templatesdir = os.path.join(packagedir, "docassemble", pkgname, "data", "templates")
|
|
@@ -831,7 +1083,7 @@ def find_package_data(where=".", package="", exclude=standard_exclude, exclude_d
|
|
|
831
1083
|
os.makedirs(staticdir, exist_ok=True)
|
|
832
1084
|
if not os.path.isdir(sourcesdir):
|
|
833
1085
|
os.makedirs(sourcesdir, exist_ok=True)
|
|
834
|
-
with open(os.path.join(packagedir,
|
|
1086
|
+
with open(os.path.join(packagedir, ".gitignore"), "w", encoding="utf-8") as the_file:
|
|
835
1087
|
the_file.write(GITIGNORE)
|
|
836
1088
|
with open(os.path.join(packagedir, "README.md"), "w", encoding="utf-8") as the_file:
|
|
837
1089
|
the_file.write(readme)
|
|
@@ -854,9 +1106,16 @@ def find_package_data(where=".", package="", exclude=standard_exclude, exclude_d
|
|
|
854
1106
|
# config
|
|
855
1107
|
# =============================================================================
|
|
856
1108
|
|
|
1109
|
+
|
|
857
1110
|
@config.command(context_settings=CONTEXT_SETTINGS)
|
|
858
1111
|
@common_params_for_config
|
|
859
|
-
@click.option(
|
|
1112
|
+
@click.option(
|
|
1113
|
+
"--api",
|
|
1114
|
+
"-a",
|
|
1115
|
+
type=(APIURLType(), str),
|
|
1116
|
+
default=(None, None),
|
|
1117
|
+
help="URL of the docassemble server and API key of the user (admin or developer)",
|
|
1118
|
+
)
|
|
860
1119
|
def add(config, api):
|
|
861
1120
|
"""
|
|
862
1121
|
Add a server to the config file.
|
|
@@ -924,7 +1183,9 @@ def new(config):
|
|
|
924
1183
|
def server_version(config, api, server):
|
|
925
1184
|
selected_server = select_server(*config, *api, server)
|
|
926
1185
|
try:
|
|
927
|
-
r = requests.get(
|
|
1186
|
+
r = requests.get(
|
|
1187
|
+
selected_server["apiurl"] + "/api/package", headers={"X-API-Key": selected_server["apikey"]}, timeout=600
|
|
1188
|
+
)
|
|
928
1189
|
if DEBUG:
|
|
929
1190
|
click.echo(type(r.status_code))
|
|
930
1191
|
click.echo(r.status_code)
|
|
@@ -953,4 +1214,3 @@ def test(config, api, server):
|
|
|
953
1214
|
apikey = selected_server["apikey"]
|
|
954
1215
|
click.echo(apiurl)
|
|
955
1216
|
test_apiurl_apikey(apiurl=apiurl, apikey=apikey)
|
|
956
|
-
|
|
@@ -4,7 +4,7 @@ build-backend = "flit_core.buildapi"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "docassemblecli3"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.3"
|
|
8
8
|
authors = [
|
|
9
9
|
{name = "Jack Adamson", email = "jackadamson@gmail.com"},
|
|
10
10
|
{name = "Jonathan Pyle", email = "jhpyle@gmail.com"},
|
|
@@ -16,7 +16,7 @@ classifiers = [
|
|
|
16
16
|
"Programming Language :: Python",
|
|
17
17
|
]
|
|
18
18
|
dynamic = ["description"]
|
|
19
|
-
requires-python = ">= 3.
|
|
19
|
+
requires-python = ">= 3.10"
|
|
20
20
|
keywords = ["docassemble"]
|
|
21
21
|
dependencies = [
|
|
22
22
|
"click",
|
|
@@ -24,7 +24,7 @@ dependencies = [
|
|
|
24
24
|
"packaging",
|
|
25
25
|
"PyYAML",
|
|
26
26
|
"requests",
|
|
27
|
-
"watchdog"
|
|
27
|
+
"watchdog",
|
|
28
28
|
]
|
|
29
29
|
|
|
30
30
|
[project.urls]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|