lockss-turtles 0.5.0.dev3__py3-none-any.whl → 0.6.0__py3-none-any.whl
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.
- lockss/turtles/__init__.py +10 -30
- lockss/turtles/__main__.py +7 -3
- lockss/turtles/app.py +520 -109
- lockss/turtles/cli.py +540 -333
- lockss/turtles/plugin.py +207 -50
- lockss/turtles/plugin_registry.py +617 -189
- lockss/turtles/plugin_set.py +534 -187
- lockss/turtles/plugin_signing_credentials.py +84 -0
- lockss/turtles/util.py +70 -21
- {lockss_turtles-0.5.0.dev3.dist-info → lockss_turtles-0.6.0.dist-info}/LICENSE +1 -1
- lockss_turtles-0.6.0.dist-info/METADATA +64 -0
- lockss_turtles-0.6.0.dist-info/RECORD +18 -0
- {lockss_turtles-0.5.0.dev3.dist-info → lockss_turtles-0.6.0.dist-info}/WHEEL +1 -1
- unittest/lockss/turtles/__init__.py +106 -0
- unittest/lockss/turtles/test_plugin_registry.py +417 -0
- unittest/lockss/turtles/test_plugin_set.py +274 -0
- unittest/lockss/turtles/test_plugin_signing_credentials.py +102 -0
- CHANGELOG.rst +0 -113
- lockss/turtles/resources/__init__.py +0 -29
- lockss/turtles/resources/plugin-registry-catalog-schema.json +0 -27
- lockss/turtles/resources/plugin-registry-schema.json +0 -115
- lockss/turtles/resources/plugin-set-catalog-schema.json +0 -27
- lockss/turtles/resources/plugin-set-schema.json +0 -92
- lockss/turtles/resources/plugin-signing-credentials-schema.json +0 -27
- lockss_turtles-0.5.0.dev3.dist-info/METADATA +0 -1041
- lockss_turtles-0.5.0.dev3.dist-info/RECORD +0 -20
- {lockss_turtles-0.5.0.dev3.dist-info → lockss_turtles-0.6.0.dist-info}/entry_points.txt +0 -0
lockss/turtles/cli.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
|
|
3
|
-
# Copyright (c) 2000-
|
|
3
|
+
# Copyright (c) 2000-2025, Board of Trustees of Leland Stanford Jr. University
|
|
4
4
|
#
|
|
5
5
|
# Redistribution and use in source and binary forms, with or without
|
|
6
6
|
# modification, are permitted provided that the following conditions are met:
|
|
@@ -28,50 +28,341 @@
|
|
|
28
28
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
29
29
|
# POSSIBILITY OF SUCH DAMAGE.
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
import sys
|
|
35
|
-
|
|
36
|
-
import rich_argparse
|
|
37
|
-
import tabulate
|
|
38
|
-
|
|
39
|
-
import lockss.turtles
|
|
40
|
-
from lockss.turtles.app import TurtlesApp
|
|
41
|
-
from lockss.turtles.plugin_registry import PluginRegistryLayer
|
|
42
|
-
from lockss.turtles.util import _path
|
|
31
|
+
"""
|
|
32
|
+
Command line tool for managing LOCKSS plugin sets and LOCKSS plugin registries.
|
|
33
|
+
"""
|
|
43
34
|
|
|
35
|
+
# Remove in Python 3.11; see https://docs.python.org/3.11/library/exceptions.html#exception-groups
|
|
36
|
+
from exceptiongroup import ExceptionGroup
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
f = open(_path(path), 'r') if path != '-' else sys.stdin
|
|
49
|
-
return [line for line in [line.partition('#')[0].strip() for line in f] if len(line) > 0]
|
|
50
|
-
finally:
|
|
51
|
-
if f is not None and path != '-':
|
|
52
|
-
f.close()
|
|
38
|
+
from getpass import getpass
|
|
39
|
+
from itertools import chain
|
|
40
|
+
from pathlib import Path
|
|
53
41
|
|
|
42
|
+
from lockss.pybasic.cliutil import BaseCli, StringCommand, COPYRIGHT_DESCRIPTION, LICENSE_DESCRIPTION, VERSION_DESCRIPTION
|
|
43
|
+
from lockss.pybasic.fileutil import file_lines, path
|
|
44
|
+
from lockss.pybasic.outpututil import OutputFormatOptions
|
|
45
|
+
from pydantic.v1 import BaseModel, Field, FilePath
|
|
46
|
+
import tabulate
|
|
47
|
+
from typing import Optional
|
|
48
|
+
|
|
49
|
+
from . import __copyright__, __license__, __version__
|
|
50
|
+
from .app import Turtles
|
|
51
|
+
from .plugin import PluginIdentifier
|
|
52
|
+
from .plugin_registry import PluginRegistryLayerIdentifier
|
|
53
|
+
from .util import file_or
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class PluginBuildingOptions(BaseModel):
|
|
57
|
+
"""
|
|
58
|
+
Pydantic-Argparse (Pydantic v1) model for the ``--plugin-set``/``-s``,
|
|
59
|
+
``--plugin-set-catalog``/``-S``, ``--plugin-signing-credentials``/``-c``,
|
|
60
|
+
``--plugin-signing-password`` options.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
#: The ``--plugin-set``/``-s`` option.
|
|
64
|
+
plugin_set: Optional[list[FilePath]] = Field(aliases=['-s'],
|
|
65
|
+
title='Plugin Sets',
|
|
66
|
+
description=f'(plugin sets) add one or more plugin set definition files to the loaded plugin sets')
|
|
67
|
+
|
|
68
|
+
#: The ``--plugin-set-catalog``/``-S`` option.
|
|
69
|
+
plugin_set_catalog: Optional[list[FilePath]] = Field(aliases=['-S'],
|
|
70
|
+
title='Plugin Set Catalogs',
|
|
71
|
+
description=f'(plugin sets) add one or more plugin set catalogs to the loaded plugin set catalogs; if no plugin set catalogs or plugin sets are specified, load {file_or(Turtles.default_plugin_set_catalog_choices())}')
|
|
72
|
+
|
|
73
|
+
#: The ``--plugin-signing-credentials``/``-c`` option.
|
|
74
|
+
plugin_signing_credentials: Optional[FilePath] = Field(aliases=['-c'],
|
|
75
|
+
title='Plugin Signing Credentials',
|
|
76
|
+
description=f'(plugin signing credentials) load the plugin signing credentials from the given file, or if none, from {file_or(Turtles.default_plugin_signing_credentials_choices())}')
|
|
77
|
+
|
|
78
|
+
#: The ``--plugin-signing-password`` option.
|
|
79
|
+
plugin_signing_password: Optional[str] = Field(title='Plugin Signing Password',
|
|
80
|
+
description='(plugin signing credentials) set the plugin signing password, or if none, prompt interactively')
|
|
81
|
+
|
|
82
|
+
def get_plugin_sets(self) -> list[Path]:
|
|
83
|
+
"""
|
|
84
|
+
Returns the cumulative plugin set files.
|
|
85
|
+
|
|
86
|
+
:return: The cumulative plugin set files (possibly an empty list).
|
|
87
|
+
:rtype: list[Path]
|
|
88
|
+
"""
|
|
89
|
+
return [path(p) for p in self.plugin_set or []]
|
|
90
|
+
|
|
91
|
+
def get_plugin_set_catalogs(self) -> list[Path]:
|
|
92
|
+
"""
|
|
93
|
+
Returns the cumulative plugin set catalog files.
|
|
94
|
+
|
|
95
|
+
:return: The cumulative plugin set catalog files if any plugin set files
|
|
96
|
+
or plugin set catalog files are specified (possibly an empty
|
|
97
|
+
list), or the first default plugin set catalog file if no
|
|
98
|
+
plugin set files nor plugin set catalog files are specified.
|
|
99
|
+
:rtype: list[Path]
|
|
100
|
+
:raise FileNotFoundError: If no plugin set files nor plugin set catalog
|
|
101
|
+
files are specified, and none of the default
|
|
102
|
+
plugin set catalog file choices exist.
|
|
103
|
+
"""
|
|
104
|
+
if self.plugin_set or self.plugin_set_catalog:
|
|
105
|
+
return [path(p) for p in self.plugin_set_catalog or []]
|
|
106
|
+
if single := Turtles.select_default_plugin_set_catalog():
|
|
107
|
+
return [single]
|
|
108
|
+
raise FileNotFoundError(file_or(Turtles.default_plugin_set_catalog_choices()))
|
|
109
|
+
|
|
110
|
+
def get_plugin_signing_credentials(self) -> Path:
|
|
111
|
+
"""
|
|
112
|
+
Returns the plugin signing credentials file.
|
|
113
|
+
|
|
114
|
+
:return: The plugin signing credentials file, or the first default
|
|
115
|
+
plugin signing credentials file if not specified.
|
|
116
|
+
:rtype: Path
|
|
117
|
+
:raise FileNotFoundError: If no plugin signing credentials file is
|
|
118
|
+
specified, and none of the default
|
|
119
|
+
plugin signing credentials file choices exist.
|
|
120
|
+
"""
|
|
121
|
+
if self.plugin_signing_credentials:
|
|
122
|
+
return path(self.plugin_signing_credentials)
|
|
123
|
+
if ret := Turtles.select_default_plugin_signing_credentials():
|
|
124
|
+
return ret
|
|
125
|
+
raise FileNotFoundError(file_or(Turtles.default_plugin_signing_credentials_choices()))
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class PluginDeploymentOptions(BaseModel):
|
|
129
|
+
"""
|
|
130
|
+
Pydantic-Argparse (Pydantic v1) model for the ``--plugin-registry``/``-r``,
|
|
131
|
+
``--plugin-registry-catalog``/``-R``, ``--plugin-registry-layer``/``-l``,
|
|
132
|
+
``--plugin-registry-layers``/``-L``, ``--testing``/``-t``,
|
|
133
|
+
``--production``/``-P`` options.
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
#: The ``--plugin-registry``/``-r`` option.
|
|
137
|
+
plugin_registry: Optional[list[FilePath]] = Field(aliases=['-r'],
|
|
138
|
+
title='Plugin Registries',
|
|
139
|
+
description=f'(plugin registry) add one or more plugin registries to the loaded plugin registries')
|
|
140
|
+
|
|
141
|
+
#: The ``--plugin-registry-catalog``/``-R`` option.
|
|
142
|
+
plugin_registry_catalog: Optional[list[FilePath]] = Field(aliases=['-R'],
|
|
143
|
+
title='Plugin Registry Catalogs',
|
|
144
|
+
description=f'(plugin registry) add one or more plugin registry catalogs to the loaded plugin registry catalogs; if no plugin registry catalogs or plugin registries are specified, load {file_or(Turtles.default_plugin_registry_catalog_choices())}')
|
|
145
|
+
|
|
146
|
+
#: The ``--plugin-registry-layer``/``-l`` option.
|
|
147
|
+
plugin_registry_layer: Optional[list[str]] = Field(aliases=['-l'],
|
|
148
|
+
title='Plugin Registry Layer Identifiers',
|
|
149
|
+
description='(plugin registry layers) add one or more plugin registry layers to the set of plugin registry layers to process')
|
|
150
|
+
|
|
151
|
+
#: The ``--plugin-registry-layers``/``-L`` option.
|
|
152
|
+
plugin_registry_layers: Optional[list[FilePath]] = Field(aliases=['-L'],
|
|
153
|
+
title='Files of Plugin Registry Layer Identifiers',
|
|
154
|
+
description='(plugin registry layers) add the plugin registry layers listed in one or more files to the set of plugin registry layers to process')
|
|
155
|
+
|
|
156
|
+
#: The ``--testing``/``-t`` option.
|
|
157
|
+
testing: Optional[bool] = Field(False,
|
|
158
|
+
aliases=['-t'],
|
|
159
|
+
title='Testing Layer',
|
|
160
|
+
description='(plugin registry layers) synonym for --plugin-registry-layer testing (i.e. add "testing" to the list of plugin registry layers to process)')
|
|
161
|
+
|
|
162
|
+
#: The ``--production``/``-P`` option.
|
|
163
|
+
production: Optional[bool] = Field(False,
|
|
164
|
+
aliases=['-p'],
|
|
165
|
+
title='Production Layer',
|
|
166
|
+
description='(plugin registry layers) synonym for --plugin-registry-layer production (i.e. add "production" to the list of plugin registry layers to process)')
|
|
167
|
+
|
|
168
|
+
def get_plugin_registries(self) -> list[Path]:
|
|
169
|
+
"""
|
|
170
|
+
Returns the cumulative plugin registry files.
|
|
171
|
+
|
|
172
|
+
:return: The cumulative plugin registry files (possibly an empty list).
|
|
173
|
+
:rtype: list[Path]
|
|
174
|
+
"""
|
|
175
|
+
return [path(p) for p in self.plugin_registry or []]
|
|
176
|
+
|
|
177
|
+
def get_plugin_registry_catalogs(self) -> list[Path]:
|
|
178
|
+
"""
|
|
179
|
+
Returns the cumulative plugin registry catalog files.
|
|
180
|
+
|
|
181
|
+
:return: The cumulative plugin registry catalog files if any plugin set
|
|
182
|
+
files or plugin registry catalog files are specified (possibly
|
|
183
|
+
an empty list), or the first default plugin registry catalog
|
|
184
|
+
file if no plugin registry files nor plugin registry catalog
|
|
185
|
+
files are specified.
|
|
186
|
+
:rtype: list[Path]
|
|
187
|
+
:raise FileNotFoundError: If no plugin registry files nor plugin
|
|
188
|
+
registry catalog files are specified, and none
|
|
189
|
+
of the default plugin registry catalog file
|
|
190
|
+
choices exist.
|
|
191
|
+
"""
|
|
192
|
+
if self.plugin_registry or self.plugin_registry_catalog:
|
|
193
|
+
return [path(p) for p in self.plugin_registry_catalog or []]
|
|
194
|
+
if single := Turtles.select_default_plugin_registry_catalog():
|
|
195
|
+
return [single]
|
|
196
|
+
raise FileNotFoundError(file_or(Turtles.default_plugin_set_catalog_choices()))
|
|
197
|
+
|
|
198
|
+
def get_plugin_registry_layers(self) -> list[PluginRegistryLayerIdentifier]:
|
|
199
|
+
"""
|
|
200
|
+
Returns the cumulative list of plugin registry layer identifiers, from
|
|
201
|
+
``plugin_registry_layer`` and the identifiers in
|
|
202
|
+
``plugin_registry_layers`` files.
|
|
203
|
+
|
|
204
|
+
:return: The cumulative list of plugin registry layer identifiers, from
|
|
205
|
+
``plugin_registry_layer`` and the identifiers in
|
|
206
|
+
``plugin_registry_layers`` files.
|
|
207
|
+
:rtype: list[PluginRegistryLayerIdentifier]
|
|
208
|
+
:raise ValueError: If the list of plugin registry layer identifiers is
|
|
209
|
+
empty.
|
|
210
|
+
"""
|
|
211
|
+
ret = [*(self.plugin_registry_layer or []), *chain.from_iterable(file_lines(path(file_path)) for file_path in self.plugin_registry_layers or [])]
|
|
212
|
+
for layer in reversed(['testing', 'production']):
|
|
213
|
+
if getattr(self, layer, False) and layer not in ret:
|
|
214
|
+
ret.insert(0, layer)
|
|
215
|
+
if ret:
|
|
216
|
+
return ret
|
|
217
|
+
raise ValueError('Empty list of plugin registry layers')
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class PluginIdentifierOptions(BaseModel):
|
|
221
|
+
"""
|
|
222
|
+
Pydantic-Argparse (Pydantic v1) models for the
|
|
223
|
+
``--plugin-identifier``/``-i``, ``--plugin-identifiers``/``-I`` options.
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
#: The ``--plugin-identifier``/``-i`` option.
|
|
227
|
+
plugin_identifier: Optional[list[str]] = Field(aliases=['-i'],
|
|
228
|
+
title='Plugin Identifiers',
|
|
229
|
+
description='(plugin identifiers) add one or more plugin identifiers to the set of plugin identifiers to process')
|
|
230
|
+
|
|
231
|
+
#: The ``--plugin-identifiers``/``-I`` option.
|
|
232
|
+
plugin_identifiers: Optional[list[FilePath]] = Field(aliases=['-I'],
|
|
233
|
+
title='Files of Plugin Identifiers',
|
|
234
|
+
description='(plugin identifiers) add the plugin identifiers listed in one or more files to the set of plugin identifiers to process')
|
|
235
|
+
|
|
236
|
+
def get_plugin_identifiers(self) -> list[PluginIdentifier]:
|
|
237
|
+
"""
|
|
238
|
+
Returns the cumulative list of plugin identifiers, from
|
|
239
|
+
``plugin_identifier`` and the identifiers in ``plugin_identifiers``
|
|
240
|
+
files.
|
|
241
|
+
|
|
242
|
+
:return: The cumulative list of plugin identifiers, from
|
|
243
|
+
``plugin_identifier`` and the identifiers in
|
|
244
|
+
``plugin_identifiers`` files.
|
|
245
|
+
:rtype: list[PluginIdentifier]
|
|
246
|
+
:raise ValueError: If the list of plugin identifiers is empty.
|
|
247
|
+
"""
|
|
248
|
+
ret = [*(self.plugin_identifier or []), *chain.from_iterable(file_lines(path(file_path)) for file_path in self.plugin_identifiers or [])]
|
|
249
|
+
if ret:
|
|
250
|
+
return ret
|
|
251
|
+
raise ValueError('Empty list of plugin identifiers')
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class PluginJarOptions(BaseModel):
|
|
255
|
+
"""
|
|
256
|
+
Pydantic-Argparse (Pydantic v1) model for the ``--plugin-jar``/``-j``,
|
|
257
|
+
``--plugin-jars``/``-J`` options.
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
#: The ``--plugin-jar``/``-j`` option.
|
|
261
|
+
plugin_jar: Optional[list[FilePath]] = Field(aliases=['-j'],
|
|
262
|
+
title='Plugin JARs',
|
|
263
|
+
description='(plugin JARs) add one or more plugin JARs to the set of plugin JARs to process')
|
|
264
|
+
|
|
265
|
+
#: The ``--plugin-jars``/``-J`` option.
|
|
266
|
+
plugin_jars: Optional[list[FilePath]] = Field(aliases=['-J'],
|
|
267
|
+
title='Files of Plugin JARs',
|
|
268
|
+
description='(plugin JARs) add the plugin JARs listed in one or more files to the set of plugin JARs to process')
|
|
269
|
+
|
|
270
|
+
def get_plugin_jars(self) -> list[Path]:
|
|
271
|
+
"""
|
|
272
|
+
Returns the cumulative list of plugin JARs, from ``plugin_jar`` and the
|
|
273
|
+
plugin JARs in ``plugin_jars``
|
|
274
|
+
files.
|
|
275
|
+
|
|
276
|
+
:return: The cumulative list of plugin JARs, from ``plugin_jar`` and the
|
|
277
|
+
plugin JARs in ``plugin_jars`` files.
|
|
278
|
+
:rtype: list[Path]
|
|
279
|
+
:raise ValueError: If the list of plugin JARs is empty.
|
|
280
|
+
"""
|
|
281
|
+
ret = [*(self.plugin_jar or []), *chain.from_iterable(file_lines(path(file_path)) for file_path in self.plugin_jars or [])]
|
|
282
|
+
if len(ret):
|
|
283
|
+
return ret
|
|
284
|
+
raise ValueError('Empty list of plugin JARs')
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
class NonInteractiveOptions(BaseModel):
|
|
288
|
+
"""
|
|
289
|
+
Pydantic-Argparse (Pydantic v1) model for the ``--non-interactive`` option.
|
|
290
|
+
"""
|
|
291
|
+
|
|
292
|
+
#: The ``--non-interactive`` option.
|
|
293
|
+
non_interactive: Optional[bool] = Field(False,
|
|
294
|
+
title='Non-Interactive',
|
|
295
|
+
description='(plugin signing credentials) disallow interactive prompts')
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class TurtlesCommand(BaseModel):
|
|
299
|
+
"""
|
|
300
|
+
Pydantic-Argparse (Pydantic v1) model for the ``turtles`` command.
|
|
301
|
+
"""
|
|
302
|
+
|
|
303
|
+
class BuildPluginCommand(OutputFormatOptions, NonInteractiveOptions, PluginBuildingOptions, PluginIdentifierOptions):
|
|
304
|
+
"""
|
|
305
|
+
Pydantic-Argparse (Pydantic v1) model for the ``build-plugin`` command.
|
|
306
|
+
"""
|
|
307
|
+
pass
|
|
308
|
+
|
|
309
|
+
class DeployPluginCommand(OutputFormatOptions, NonInteractiveOptions, PluginDeploymentOptions, PluginJarOptions):
|
|
310
|
+
"""
|
|
311
|
+
Pydantic-Argparse (Pydantic v1) model for the ``deploy-plugin`` command.
|
|
312
|
+
"""
|
|
313
|
+
pass
|
|
314
|
+
|
|
315
|
+
class ReleasePluginCommand(OutputFormatOptions, NonInteractiveOptions, PluginDeploymentOptions, PluginBuildingOptions, PluginIdentifierOptions):
|
|
316
|
+
"""
|
|
317
|
+
Pydantic-Argparse (Pydantic v1) model for the ``release-plugin``
|
|
318
|
+
command.
|
|
319
|
+
"""
|
|
320
|
+
pass
|
|
321
|
+
|
|
322
|
+
#: The ``bp`` synonym for the ``build-plugin`` command.
|
|
323
|
+
bp: Optional[BuildPluginCommand] = Field(description='synonym for: build-plugin')
|
|
324
|
+
|
|
325
|
+
#: The ``build-plugin`` command.
|
|
326
|
+
build_plugin: Optional[BuildPluginCommand] = Field(alias='build-plugin',
|
|
327
|
+
description='build plugins')
|
|
328
|
+
|
|
329
|
+
#: The ``copyright`` command.
|
|
330
|
+
copyright: Optional[StringCommand.type(__copyright__)] = Field(description=COPYRIGHT_DESCRIPTION)
|
|
331
|
+
|
|
332
|
+
#: The ``deploy-plugin`` command.
|
|
333
|
+
deploy_plugin: Optional[DeployPluginCommand] = Field(alias='deploy-plugin',
|
|
334
|
+
description='deploy plugins')
|
|
335
|
+
|
|
336
|
+
#: The ``dp`` synonym for the ``deploy-plugin`` command.
|
|
337
|
+
dp: Optional[DeployPluginCommand] = Field(description='synonym for: deploy-plugin')
|
|
338
|
+
|
|
339
|
+
#: The ``license`` command.
|
|
340
|
+
license: Optional[StringCommand.type(__license__)] = Field(description=LICENSE_DESCRIPTION)
|
|
341
|
+
|
|
342
|
+
#: The ``release-plugin`` command.
|
|
343
|
+
release_plugin: Optional[ReleasePluginCommand] = Field(alias='release-plugin',
|
|
344
|
+
description='release (build and deploy) plugins')
|
|
345
|
+
|
|
346
|
+
#: The ``rp`` synonym for the ``release-plugin`` command.
|
|
347
|
+
rp: Optional[ReleasePluginCommand] = Field(description='synonym for: release-plugin')
|
|
348
|
+
|
|
349
|
+
#: The ``version`` command.
|
|
350
|
+
version: Optional[StringCommand.type(__version__)] = Field(description=VERSION_DESCRIPTION)
|
|
54
351
|
|
|
55
|
-
class TurtlesCli(object):
|
|
56
352
|
|
|
57
|
-
|
|
353
|
+
class TurtlesCli(BaseCli[TurtlesCommand]):
|
|
354
|
+
"""
|
|
355
|
+
Command line tool for Turtles.
|
|
356
|
+
"""
|
|
58
357
|
|
|
59
358
|
def __init__(self):
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
self.
|
|
67
|
-
self._subparsers = None
|
|
68
|
-
|
|
69
|
-
def run(self):
|
|
70
|
-
self._make_parser()
|
|
71
|
-
self._args = self._parser.parse_args()
|
|
72
|
-
if self._args.debug_cli:
|
|
73
|
-
print(self._args)
|
|
74
|
-
self._args.fun()
|
|
359
|
+
"""
|
|
360
|
+
Constructor.
|
|
361
|
+
"""
|
|
362
|
+
super().__init__(model=TurtlesCommand,
|
|
363
|
+
prog='turtles',
|
|
364
|
+
description='Tool for managing LOCKSS plugin sets and LOCKSS plugin registries')
|
|
365
|
+
self._app: Turtles = Turtles()
|
|
75
366
|
|
|
76
367
|
# def _analyze_registry(self):
|
|
77
368
|
# # Prerequisites
|
|
@@ -131,316 +422,232 @@ class TurtlesCli(object):
|
|
|
131
422
|
# if len(result) > 0:
|
|
132
423
|
# self._tabulate(title, result, headers)
|
|
133
424
|
|
|
134
|
-
def
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
425
|
+
def _bp(self,
|
|
426
|
+
command: TurtlesCommand.BuildPluginCommand) -> None:
|
|
427
|
+
"""
|
|
428
|
+
Implementation of the ``bp`` command.
|
|
429
|
+
|
|
430
|
+
:param command: The command object.
|
|
431
|
+
:type command: TurtlesCommand.BuildPluginCommand
|
|
432
|
+
"""
|
|
433
|
+
return self._build_plugin(command)
|
|
434
|
+
|
|
435
|
+
def _build_plugin(self,
|
|
436
|
+
command: TurtlesCommand.BuildPluginCommand) -> None:
|
|
437
|
+
"""
|
|
438
|
+
Implementation of the ``build-plugin`` command.
|
|
439
|
+
|
|
440
|
+
:param command: The command object.
|
|
441
|
+
:type command: TurtlesCommand.BuildPluginCommand
|
|
442
|
+
"""
|
|
443
|
+
errs = []
|
|
444
|
+
for psc in command.get_plugin_set_catalogs():
|
|
445
|
+
try:
|
|
446
|
+
self._app.load_plugin_set_catalogs(psc)
|
|
447
|
+
except ValueError as ve:
|
|
448
|
+
errs.append(ve)
|
|
449
|
+
except ExceptionGroup as eg:
|
|
450
|
+
errs.extend(eg.exceptions)
|
|
451
|
+
for ps in command.get_plugin_sets():
|
|
452
|
+
try:
|
|
453
|
+
self._app.load_plugin_sets(ps)
|
|
454
|
+
except ValueError as ve:
|
|
455
|
+
errs.append(ve)
|
|
456
|
+
except ExceptionGroup as eg:
|
|
457
|
+
errs.extend(eg.exceptions)
|
|
458
|
+
try:
|
|
459
|
+
self._app.load_plugin_signing_credentials(command.get_plugin_signing_credentials())
|
|
460
|
+
except ValueError as ve:
|
|
461
|
+
errs.append(ve)
|
|
462
|
+
except ExceptionGroup as eg:
|
|
463
|
+
errs.extend(eg.exceptions)
|
|
464
|
+
if errs:
|
|
465
|
+
raise ExceptionGroup(f'Errors while setting up the environment for building plugins', errs)
|
|
466
|
+
self._obtain_plugin_signing_password(command, non_interactive=command.non_interactive)
|
|
139
467
|
# Action
|
|
140
468
|
# ... plugin_id -> (set_id, jar_path, plugin)
|
|
141
|
-
ret = self._app.build_plugin(
|
|
469
|
+
ret = self._app.build_plugin(command.get_plugin_identifiers())
|
|
142
470
|
# Output
|
|
143
471
|
print(tabulate.tabulate([[plugin_id, plugin.get_version(), set_id, jar_path] for plugin_id, (set_id, jar_path, plugin) in ret.items()],
|
|
144
472
|
headers=['Plugin identifier', 'Plugin version', 'Plugin set', 'Plugin JAR'],
|
|
145
|
-
tablefmt=
|
|
146
|
-
|
|
147
|
-
def _copyright(self
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
473
|
+
tablefmt=command.output_format))
|
|
474
|
+
|
|
475
|
+
def _copyright(self,
|
|
476
|
+
command: StringCommand) -> None:
|
|
477
|
+
"""
|
|
478
|
+
Implementation of the ``copyright`` command.
|
|
479
|
+
|
|
480
|
+
:param command: The command object.
|
|
481
|
+
:type command: StringCommand
|
|
482
|
+
"""
|
|
483
|
+
self._do_string_command(command)
|
|
484
|
+
|
|
485
|
+
def _deploy_plugin(self,
|
|
486
|
+
command: TurtlesCommand.DeployPluginCommand) -> None:
|
|
487
|
+
"""
|
|
488
|
+
Implementation of the ``deploy_plugin`` command.
|
|
489
|
+
|
|
490
|
+
:param command: The command object.
|
|
491
|
+
:type command: TurtlesCommand.DeployPluginCommand
|
|
492
|
+
"""
|
|
493
|
+
errs = []
|
|
494
|
+
for prc in command.get_plugin_registry_catalogs():
|
|
495
|
+
try:
|
|
496
|
+
self._app.load_plugin_registry_catalogs(prc)
|
|
497
|
+
except ValueError as ve:
|
|
498
|
+
errs.append(ve)
|
|
499
|
+
except ExceptionGroup as eg:
|
|
500
|
+
errs.extend(eg.exceptions)
|
|
501
|
+
for pr in command.get_plugin_registries():
|
|
502
|
+
try:
|
|
503
|
+
self._app.load_plugin_registries(pr)
|
|
504
|
+
except ValueError as ve:
|
|
505
|
+
errs.append(ve)
|
|
506
|
+
except ExceptionGroup as eg:
|
|
507
|
+
errs.extend(eg.exceptions)
|
|
508
|
+
if errs:
|
|
509
|
+
raise ExceptionGroup(f'Errors while setting up the environment for deploying plugins', errs)
|
|
153
510
|
# Action
|
|
154
511
|
# ... (src_path, plugin_id) -> list of (registry_id, layer_id, dst_path, plugin)
|
|
155
|
-
ret = self._app.deploy_plugin(
|
|
156
|
-
|
|
157
|
-
interactive=
|
|
512
|
+
ret = self._app.deploy_plugin(command.get_plugin_jars(),
|
|
513
|
+
command.get_plugin_registry_layers(),
|
|
514
|
+
interactive=not command.non_interactive)
|
|
158
515
|
# Output
|
|
159
516
|
print(tabulate.tabulate([[src_path, plugin_id, plugin.get_version(), registry_id, layer_id, dst_path] for (src_path, plugin_id), val in ret.items() for registry_id, layer_id, dst_path, plugin in val],
|
|
160
517
|
headers=['Plugin JAR', 'Plugin identifier', 'Plugin version', 'Plugin registry', 'Plugin registry layer', 'Deployed JAR'],
|
|
161
|
-
tablefmt=
|
|
162
|
-
|
|
163
|
-
def
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
def _make_option_output_format(self, container):
|
|
210
|
-
container.add_argument('--output-format',
|
|
211
|
-
metavar='FMT',
|
|
212
|
-
choices=tabulate.tabulate_formats,
|
|
213
|
-
default='simple',
|
|
214
|
-
help='set tabular output format to %(metavar)s (default: %(default)s; choices: %(choices)s)')
|
|
215
|
-
|
|
216
|
-
def _make_option_password(self, container):
|
|
217
|
-
container.add_argument('--password',
|
|
218
|
-
metavar='PASS',
|
|
219
|
-
help='set the plugin signing password')
|
|
220
|
-
|
|
221
|
-
def _make_option_plugin_registry_catalog(self, container):
|
|
222
|
-
container.add_argument('--plugin-registry-catalog', '-r',
|
|
223
|
-
metavar='FILE',
|
|
224
|
-
type=Path,
|
|
225
|
-
help=f'load plugin registry catalog from %(metavar)s (default: {" or ".join(map(str, self._app.default_plugin_registry_catalogs()))})')
|
|
226
|
-
|
|
227
|
-
def _make_option_plugin_set_catalog(self, container):
|
|
228
|
-
container.add_argument('--plugin-set-catalog', '-s',
|
|
229
|
-
metavar='FILE',
|
|
230
|
-
type=Path,
|
|
231
|
-
help=f'load plugin set catalog from %(metavar)s (default: {" or ".join(map(str, self._app.default_plugin_set_catalogs()))})')
|
|
232
|
-
|
|
233
|
-
def _make_option_plugin_signing_credentials(self, container):
|
|
234
|
-
container.add_argument('--plugin-signing-credentials', '-c',
|
|
235
|
-
metavar='FILE',
|
|
236
|
-
type=Path,
|
|
237
|
-
help=f'load plugin signing credentials from %(metavar)s (default: {" or ".join(map(str, self._app.default_plugin_signing_credentials()))})')
|
|
238
|
-
|
|
239
|
-
def _make_option_production(self, container):
|
|
240
|
-
container.add_argument('--production', '-p',
|
|
241
|
-
dest='layer',
|
|
242
|
-
action='append_const',
|
|
243
|
-
const=PluginRegistryLayer.PRODUCTION,
|
|
244
|
-
help="synonym for --layer=%(const)s (i.e. add '%(const)s' to the list of plugin registry layers to process)")
|
|
245
|
-
|
|
246
|
-
def _make_option_testing(self, container):
|
|
247
|
-
container.add_argument('--testing', '-t',
|
|
248
|
-
dest='layer',
|
|
249
|
-
action='append_const',
|
|
250
|
-
const=PluginRegistryLayer.TESTING,
|
|
251
|
-
help="synonym for --layer=%(const)s (i.e. add '%(const)s' to the list of plugin registry layers to process)")
|
|
252
|
-
|
|
253
|
-
def _make_options_identifiers(self, container):
|
|
254
|
-
group = container.add_argument_group(title='plugin identifier arguments and options')
|
|
255
|
-
group.add_argument('--identifier', '-i',
|
|
256
|
-
metavar='PLUGID',
|
|
257
|
-
action='append',
|
|
258
|
-
default=list(),
|
|
259
|
-
help='add %(metavar)s to the list of plugin identifiers to build')
|
|
260
|
-
group.add_argument('--identifiers', '-I',
|
|
261
|
-
metavar='FILE',
|
|
262
|
-
action='append',
|
|
263
|
-
default=list(),
|
|
264
|
-
help='add the plugin identifiers in %(metavar)s to the list of plugin identifiers to build')
|
|
265
|
-
group.add_argument('remainder',
|
|
266
|
-
metavar='PLUGID',
|
|
267
|
-
nargs='*',
|
|
268
|
-
help='plugin identifier to build')
|
|
269
|
-
|
|
270
|
-
def _make_options_jars(self, container):
|
|
271
|
-
group = container.add_argument_group(title='plugin JAR arguments and options')
|
|
272
|
-
group.add_argument('--jar', '-j',
|
|
273
|
-
metavar='PLUGJAR',
|
|
274
|
-
type=Path,
|
|
275
|
-
action='append',
|
|
276
|
-
default=list(),
|
|
277
|
-
help='add %(metavar)s to the list of plugin JARs to deploy')
|
|
278
|
-
group.add_argument('--jars', '-J',
|
|
279
|
-
metavar='FILE',
|
|
280
|
-
action='append',
|
|
281
|
-
default=list(),
|
|
282
|
-
help='add the plugin JARs in %(metavar)s to the list of plugin JARs to deploy')
|
|
283
|
-
group.add_argument('remainder',
|
|
284
|
-
metavar='PLUGJAR',
|
|
285
|
-
nargs='*',
|
|
286
|
-
help='plugin JAR to deploy')
|
|
287
|
-
|
|
288
|
-
def _make_options_layers(self, container):
|
|
289
|
-
group = container.add_argument_group(title='plugin registry layer options')
|
|
290
|
-
group.add_argument('--layer', '-l',
|
|
291
|
-
metavar='LAYER',
|
|
292
|
-
action='append',
|
|
293
|
-
default=list(),
|
|
294
|
-
help='add %(metavar)s to the list of plugin registry layers to process')
|
|
295
|
-
group.add_argument('--layers', '-L',
|
|
296
|
-
metavar='FILE',
|
|
297
|
-
action='append',
|
|
298
|
-
default=list(),
|
|
299
|
-
help='add the layers in %(metavar)s to the list of plugin registry layers to process')
|
|
300
|
-
|
|
301
|
-
def _make_parser(self):
|
|
302
|
-
for cls in [rich_argparse.RichHelpFormatter]:
|
|
303
|
-
cls.styles.update({
|
|
304
|
-
'argparse.args': f'bold {cls.styles["argparse.args"]}',
|
|
305
|
-
'argparse.groups': f'bold {cls.styles["argparse.groups"]}',
|
|
306
|
-
'argparse.metavar': f'bold {cls.styles["argparse.metavar"]}',
|
|
307
|
-
'argparse.prog': f'bold {cls.styles["argparse.prog"]}',
|
|
308
|
-
})
|
|
309
|
-
self._parser = argparse.ArgumentParser(prog=TurtlesCli.PROG,
|
|
310
|
-
formatter_class=rich_argparse.RichHelpFormatter)
|
|
311
|
-
self._subparsers = self._parser.add_subparsers(title='commands',
|
|
312
|
-
description="Add --help to see the command's own help message.",
|
|
313
|
-
# With subparsers, metavar is also used as the heading of the column of subcommands
|
|
314
|
-
metavar='COMMAND',
|
|
315
|
-
# With subparsers, help is used as the heading of the column of subcommand descriptions
|
|
316
|
-
help='DESCRIPTION')
|
|
317
|
-
self._make_option_debug_cli(self._parser)
|
|
318
|
-
self._make_option_non_interactive(self._parser)
|
|
319
|
-
#self._make_parser_analyze_registry(self._subparsers)
|
|
320
|
-
self._make_parser_build_plugin(self._subparsers)
|
|
321
|
-
self._make_parser_copyright(self._subparsers)
|
|
322
|
-
self._make_parser_deploy_plugin(self._subparsers)
|
|
323
|
-
self._make_parser_license(self._subparsers)
|
|
324
|
-
self._make_parser_release_plugin(self._subparsers)
|
|
325
|
-
self._make_parser_usage(self._subparsers)
|
|
326
|
-
self._make_parser_version(self._subparsers)
|
|
327
|
-
|
|
328
|
-
# def _make_parser_analyze_registry(self, container):
|
|
329
|
-
# parser = container.add_parser('analyze-registry', aliases=['ar'],
|
|
330
|
-
# description='Analyze plugin registries',
|
|
331
|
-
# help='analyze plugin registries')
|
|
332
|
-
# parser.set_defaults(fun=self._analyze_registry)
|
|
333
|
-
# self._make_option_plugin_registry_catalog(parser)
|
|
334
|
-
# self._make_option_plugin_set_catalog(parser)
|
|
335
|
-
# self._make_option_plugin_signing(parser)
|
|
336
|
-
|
|
337
|
-
def _make_parser_build_plugin(self, container):
|
|
338
|
-
parser = container.add_parser('build-plugin', aliases=['bp'],
|
|
339
|
-
description='Build (package and sign) plugins.',
|
|
340
|
-
help='build (package and sign) plugins',
|
|
341
|
-
formatter_class=self._parser.formatter_class)
|
|
342
|
-
parser.set_defaults(fun=self._build_plugin)
|
|
343
|
-
self._make_option_output_format(parser)
|
|
344
|
-
self._make_option_password(parser)
|
|
345
|
-
self._make_option_plugin_set_catalog(parser)
|
|
346
|
-
self._make_option_plugin_signing_credentials(parser)
|
|
347
|
-
self._make_options_identifiers(parser)
|
|
348
|
-
|
|
349
|
-
def _make_parser_copyright(self, container):
|
|
350
|
-
parser = container.add_parser('copyright',
|
|
351
|
-
description='Show copyright and exit.',
|
|
352
|
-
help='show copyright and exit',
|
|
353
|
-
formatter_class=self._parser.formatter_class)
|
|
354
|
-
parser.set_defaults(fun=self._copyright)
|
|
355
|
-
|
|
356
|
-
def _make_parser_deploy_plugin(self, container):
|
|
357
|
-
parser = container.add_parser('deploy-plugin', aliases=['dp'],
|
|
358
|
-
description='Deploy plugins.',
|
|
359
|
-
help='deploy plugins',
|
|
360
|
-
formatter_class=self._parser.formatter_class)
|
|
361
|
-
parser.set_defaults(fun=self._deploy_plugin)
|
|
362
|
-
self._make_option_output_format(parser)
|
|
363
|
-
self._make_option_plugin_registry_catalog(parser)
|
|
364
|
-
self._make_option_production(parser)
|
|
365
|
-
self._make_option_testing(parser)
|
|
366
|
-
self._make_options_jars(parser)
|
|
367
|
-
self._make_options_layers(parser)
|
|
368
|
-
|
|
369
|
-
def _make_parser_license(self, container):
|
|
370
|
-
parser = container.add_parser('license',
|
|
371
|
-
description='Show license and exit.',
|
|
372
|
-
help='show license and exit',
|
|
373
|
-
formatter_class=self._parser.formatter_class)
|
|
374
|
-
parser.set_defaults(fun=self._license)
|
|
375
|
-
|
|
376
|
-
def _make_parser_release_plugin(self, container):
|
|
377
|
-
parser = container.add_parser('release-plugin', aliases=['rp'],
|
|
378
|
-
description='Release (build and deploy) plugins.',
|
|
379
|
-
help='release (build and deploy) plugins',
|
|
380
|
-
formatter_class=self._parser.formatter_class)
|
|
381
|
-
parser.set_defaults(fun=self._release_plugin)
|
|
382
|
-
self._make_option_output_format(parser)
|
|
383
|
-
self._make_option_password(parser)
|
|
384
|
-
self._make_option_plugin_registry_catalog(parser)
|
|
385
|
-
self._make_option_plugin_set_catalog(parser)
|
|
386
|
-
self._make_option_plugin_signing_credentials(parser)
|
|
387
|
-
self._make_option_production(parser)
|
|
388
|
-
self._make_option_testing(parser)
|
|
389
|
-
self._make_options_identifiers(parser)
|
|
390
|
-
self._make_options_layers(parser)
|
|
391
|
-
|
|
392
|
-
def _make_parser_usage(self, container):
|
|
393
|
-
parser = container.add_parser('usage',
|
|
394
|
-
description='Show detailed usage and exit.',
|
|
395
|
-
help='show detailed usage and exit',
|
|
396
|
-
formatter_class=self._parser.formatter_class)
|
|
397
|
-
parser.set_defaults(fun=self._usage)
|
|
398
|
-
|
|
399
|
-
def _make_parser_version(self, container):
|
|
400
|
-
parser = container.add_parser('version',
|
|
401
|
-
description='Show version and exit.',
|
|
402
|
-
help='show version and exit',
|
|
403
|
-
formatter_class=self._parser.formatter_class)
|
|
404
|
-
parser.set_defaults(fun=self._version)
|
|
405
|
-
|
|
406
|
-
def _obtain_password(self):
|
|
407
|
-
if self._args.password is not None:
|
|
408
|
-
_p = self._args.password
|
|
409
|
-
elif self._args.interactive:
|
|
410
|
-
_p = getpass.getpass('Plugin signing password: ')
|
|
518
|
+
tablefmt=command.output_format))
|
|
519
|
+
|
|
520
|
+
def _do_string_command(self,
|
|
521
|
+
command: StringCommand) -> None:
|
|
522
|
+
"""
|
|
523
|
+
Implementation of string commands.
|
|
524
|
+
|
|
525
|
+
:param command: The command object.
|
|
526
|
+
:type command: StringCommand
|
|
527
|
+
"""
|
|
528
|
+
command()
|
|
529
|
+
|
|
530
|
+
def _dp(self,
|
|
531
|
+
command: TurtlesCommand.DeployPluginCommand) -> None:
|
|
532
|
+
"""
|
|
533
|
+
Implementation of the ``dp`` command.
|
|
534
|
+
|
|
535
|
+
:param command: The command object.
|
|
536
|
+
:type command: TurtlesCommand.DeployPluginCommand
|
|
537
|
+
"""
|
|
538
|
+
return self._deploy_plugin(command)
|
|
539
|
+
|
|
540
|
+
def _license(self,
|
|
541
|
+
command: StringCommand) -> None:
|
|
542
|
+
"""
|
|
543
|
+
Implementation of the ``license`` command.
|
|
544
|
+
|
|
545
|
+
:param command: The command object.
|
|
546
|
+
:type command: StringCommand
|
|
547
|
+
"""
|
|
548
|
+
self._do_string_command(command)
|
|
549
|
+
|
|
550
|
+
def _obtain_plugin_signing_password(self,
|
|
551
|
+
plugin_building_options: PluginBuildingOptions,
|
|
552
|
+
non_interactive: bool=False) -> None:
|
|
553
|
+
"""
|
|
554
|
+
Ensures the plugin signing password is specified.
|
|
555
|
+
|
|
556
|
+
:param plugin_building_options:
|
|
557
|
+
:type plugin_building_options: PluginBuildingOptions
|
|
558
|
+
:param non_interactive:
|
|
559
|
+
:type non_interactive: bool
|
|
560
|
+
"""
|
|
561
|
+
if plugin_building_options.plugin_signing_password:
|
|
562
|
+
_p = plugin_building_options.plugin_signing_password
|
|
563
|
+
elif not non_interactive:
|
|
564
|
+
_p = getpass('Plugin signing password: ')
|
|
411
565
|
else:
|
|
412
566
|
self._parser.error('no plugin signing password specified while in non-interactive mode')
|
|
413
|
-
self._app.
|
|
414
|
-
|
|
415
|
-
def _release_plugin(self
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
567
|
+
self._app.set_plugin_signing_password(lambda: _p)
|
|
568
|
+
|
|
569
|
+
def _release_plugin(self,
|
|
570
|
+
command: TurtlesCommand.ReleasePluginCommand) -> None:
|
|
571
|
+
"""
|
|
572
|
+
Implementation of the ``release-plugin`` command.
|
|
573
|
+
|
|
574
|
+
:param command: The command object.
|
|
575
|
+
:type command: TurtlesCommand.ReleasePluginCommand
|
|
576
|
+
"""
|
|
577
|
+
errs = []
|
|
578
|
+
for psc in command.get_plugin_set_catalogs():
|
|
579
|
+
try:
|
|
580
|
+
self._app.load_plugin_set_catalogs(psc)
|
|
581
|
+
except ValueError as ve:
|
|
582
|
+
errs.append(ve)
|
|
583
|
+
except ExceptionGroup as eg:
|
|
584
|
+
errs.extend(eg.exceptions)
|
|
585
|
+
for ps in command.get_plugin_sets():
|
|
586
|
+
try:
|
|
587
|
+
self._app.load_plugin_sets(ps)
|
|
588
|
+
except ValueError as ve:
|
|
589
|
+
errs.append(ve)
|
|
590
|
+
except ExceptionGroup as eg:
|
|
591
|
+
errs.extend(eg.exceptions)
|
|
592
|
+
for prc in command.get_plugin_registry_catalogs():
|
|
593
|
+
try:
|
|
594
|
+
self._app.load_plugin_registry_catalogs(prc)
|
|
595
|
+
except ValueError as ve:
|
|
596
|
+
errs.append(ve)
|
|
597
|
+
except ExceptionGroup as eg:
|
|
598
|
+
errs.extend(eg.exceptions)
|
|
599
|
+
for pr in command.get_plugin_registries():
|
|
600
|
+
try:
|
|
601
|
+
self._app.load_plugin_registries(pr)
|
|
602
|
+
except ValueError as ve:
|
|
603
|
+
errs.append(ve)
|
|
604
|
+
except ExceptionGroup as eg:
|
|
605
|
+
errs.extend(eg.exceptions)
|
|
606
|
+
try:
|
|
607
|
+
self._app.load_plugin_signing_credentials(command.get_plugin_signing_credentials())
|
|
608
|
+
except ValueError as ve:
|
|
609
|
+
errs.append(ve)
|
|
610
|
+
except ExceptionGroup as eg:
|
|
611
|
+
errs.extend(eg.exceptions)
|
|
612
|
+
if errs:
|
|
613
|
+
raise ExceptionGroup(f'Errors while setting up the environment for deploying plugins', errs)
|
|
614
|
+
self._obtain_plugin_signing_password(command, non_interactive=command.non_interactive)
|
|
421
615
|
# Action
|
|
422
616
|
# ... plugin_id -> list of (registry_id, layer_id, dst_path, plugin)
|
|
423
|
-
ret = self._app.release_plugin(
|
|
424
|
-
|
|
425
|
-
interactive=
|
|
617
|
+
ret = self._app.release_plugin(command.get_plugin_identifiers(),
|
|
618
|
+
command.get_plugin_registry_layers(),
|
|
619
|
+
interactive=not command.non_interactive)
|
|
426
620
|
# Output
|
|
427
621
|
print(tabulate.tabulate([[plugin_id, plugin.get_version(), registry_id, layer_id, dst_path] for plugin_id, val in ret.items() for registry_id, layer_id, dst_path, plugin in val],
|
|
428
622
|
headers=['Plugin identifier', 'Plugin version', 'Plugin registry', 'Plugin registry layer', 'Deployed JAR'],
|
|
429
|
-
tablefmt=
|
|
430
|
-
|
|
431
|
-
def
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
623
|
+
tablefmt=command.output_format))
|
|
624
|
+
|
|
625
|
+
def _rp(self, command: TurtlesCommand.ReleasePluginCommand) -> None:
|
|
626
|
+
"""
|
|
627
|
+
Implementation of the ``rp`` command.
|
|
628
|
+
|
|
629
|
+
:param command: The command object.
|
|
630
|
+
:type command: TurtlesCommand.ReleasePluginCommand
|
|
631
|
+
"""
|
|
632
|
+
self._release_plugin(command)
|
|
633
|
+
|
|
634
|
+
def _version(self, command: StringCommand) -> None:
|
|
635
|
+
"""
|
|
636
|
+
Implementation of the ``version`` command.
|
|
637
|
+
|
|
638
|
+
:param command: The command object.
|
|
639
|
+
:type command: StringCommand
|
|
640
|
+
"""
|
|
641
|
+
self._do_string_command(command)
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
def main() -> None:
|
|
645
|
+
"""
|
|
646
|
+
Main entry point of the module.
|
|
647
|
+
"""
|
|
446
648
|
TurtlesCli().run()
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
# Main entry point of the module.
|
|
652
|
+
if __name__ == '__main__':
|
|
653
|
+
main()
|