lockss-turtles 0.5.0.dev3__py3-none-any.whl → 0.6.0.dev1__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 +7 -30
- lockss/turtles/__main__.py +3 -3
- lockss/turtles/app.py +82 -75
- lockss/turtles/cli.py +159 -320
- lockss/turtles/plugin.py +64 -55
- lockss/turtles/plugin_registry.py +90 -79
- lockss/turtles/plugin_set.py +95 -87
- lockss/turtles/resources/__init__.py +1 -1
- lockss/turtles/util.py +8 -10
- {lockss_turtles-0.5.0.dev3.dist-info → lockss_turtles-0.6.0.dev1.dist-info}/LICENSE +1 -1
- {lockss_turtles-0.5.0.dev3.dist-info → lockss_turtles-0.6.0.dev1.dist-info}/METADATA +17 -19
- lockss_turtles-0.6.0.dev1.dist-info/RECORD +19 -0
- {lockss_turtles-0.5.0.dev3.dist-info → lockss_turtles-0.6.0.dev1.dist-info}/WHEEL +1 -1
- CHANGELOG.rst +0 -113
- lockss_turtles-0.5.0.dev3.dist-info/RECORD +0 -20
- {lockss_turtles-0.5.0.dev3.dist-info → lockss_turtles-0.6.0.dev1.dist-info}/entry_points.txt +0 -0
lockss/turtles/plugin.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,66 +28,35 @@
|
|
|
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
|
-
|
|
31
|
+
"""
|
|
32
|
+
Library to represent a LOCKSS plugin.
|
|
33
|
+
"""
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
# Remove in Python 3.14
|
|
36
|
+
# See https://stackoverflow.com/questions/33533148/how-do-i-type-hint-a-method-with-the-type-of-the-enclosing-class/33533514#33533514
|
|
37
|
+
from __future__ import annotations
|
|
36
38
|
|
|
37
|
-
from
|
|
39
|
+
from collections.abc import Callable
|
|
40
|
+
from pathlib import Path, PurePath
|
|
41
|
+
from typing import Any, List, Optional, Union
|
|
42
|
+
import xml.etree.ElementTree as ET
|
|
43
|
+
from zipfile import ZipFile
|
|
38
44
|
|
|
45
|
+
import java_manifest as JM
|
|
46
|
+
from lockss.pybasic.fileutil import path
|
|
39
47
|
|
|
40
|
-
class Plugin(object):
|
|
41
|
-
|
|
42
|
-
@staticmethod
|
|
43
|
-
def from_jar(jar_path):
|
|
44
|
-
jar_path = _path(jar_path) # in case it's a string
|
|
45
|
-
plugin_id = Plugin.id_from_jar(jar_path)
|
|
46
|
-
plugin_fstr = str(Plugin.id_to_file(plugin_id))
|
|
47
|
-
with zipfile.ZipFile(jar_path, 'r') as zip_file:
|
|
48
|
-
with zip_file.open(plugin_fstr, 'r') as plugin_file:
|
|
49
|
-
return Plugin(plugin_file, plugin_fstr)
|
|
50
|
-
|
|
51
|
-
@staticmethod
|
|
52
|
-
def from_path(path):
|
|
53
|
-
path = _path(path) # in case it's a string
|
|
54
|
-
with open(path, 'r') as input_file:
|
|
55
|
-
return Plugin(input_file, path)
|
|
56
|
-
|
|
57
|
-
@staticmethod
|
|
58
|
-
def file_to_id(plugin_fstr):
|
|
59
|
-
return plugin_fstr.replace('/', '.')[:-4] # 4 is len('.xml')
|
|
60
|
-
|
|
61
|
-
@staticmethod
|
|
62
|
-
def id_from_jar(jar_path):
|
|
63
|
-
jar_path = _path(jar_path) # in case it's a string
|
|
64
|
-
manifest = java_manifest.from_jar(jar_path)
|
|
65
|
-
for entry in manifest:
|
|
66
|
-
if entry.get('Lockss-Plugin') == 'true':
|
|
67
|
-
name = entry.get('Name')
|
|
68
|
-
if name is None:
|
|
69
|
-
raise Exception(f'{jar_path!s}: Lockss-Plugin entry in META-INF/MANIFEST.MF has no Name value')
|
|
70
|
-
return Plugin.file_to_id(name)
|
|
71
|
-
else:
|
|
72
|
-
raise Exception(f'{jar_path!s}: no Lockss-Plugin entry in META-INF/MANIFEST.MF')
|
|
73
|
-
|
|
74
|
-
@staticmethod
|
|
75
|
-
def id_to_dir(plugin_id):
|
|
76
|
-
return Plugin.id_to_file(plugin_id).parent
|
|
77
48
|
|
|
78
|
-
|
|
79
|
-
def id_to_file(plugin_id):
|
|
80
|
-
return Path(f'{plugin_id.replace(".", "/")}.xml')
|
|
49
|
+
class Plugin(object):
|
|
81
50
|
|
|
82
|
-
def __init__(self, plugin_file, plugin_path):
|
|
51
|
+
def __init__(self, plugin_file, plugin_path) -> None:
|
|
83
52
|
super().__init__()
|
|
84
53
|
self._path = plugin_path
|
|
85
|
-
self._parsed =
|
|
54
|
+
self._parsed = ET.parse(plugin_file).getroot()
|
|
86
55
|
tag = self._parsed.tag
|
|
87
56
|
if tag != 'map':
|
|
88
57
|
raise RuntimeError(f'{plugin_path!s}: invalid root element: {tag}')
|
|
89
58
|
|
|
90
|
-
def get_aux_packages(self):
|
|
59
|
+
def get_aux_packages(self) -> List[str]:
|
|
91
60
|
key = 'plugin_aux_packages'
|
|
92
61
|
lst = [x[1] for x in self._parsed.findall('entry') if x[0].tag == 'string' and x[0].text == key]
|
|
93
62
|
if lst is None or len(lst) < 1:
|
|
@@ -96,25 +65,65 @@ class Plugin(object):
|
|
|
96
65
|
raise ValueError(f'plugin declares {len(lst)} entries for {key}')
|
|
97
66
|
return [x.text for x in lst[0].findall('string')]
|
|
98
67
|
|
|
99
|
-
def get_identifier(self):
|
|
68
|
+
def get_identifier(self) -> Optional[str]:
|
|
100
69
|
return self._only_one('plugin_identifier')
|
|
101
70
|
|
|
102
|
-
def get_name(self):
|
|
71
|
+
def get_name(self) -> Optional[str]:
|
|
103
72
|
return self._only_one('plugin_name')
|
|
104
73
|
|
|
105
|
-
def get_parent_identifier(self):
|
|
74
|
+
def get_parent_identifier(self) -> Optional[str]:
|
|
106
75
|
return self._only_one('plugin_parent')
|
|
107
76
|
|
|
108
|
-
def get_parent_version(self):
|
|
77
|
+
def get_parent_version(self) -> Optional[int]:
|
|
109
78
|
return self._only_one('plugin_parent_version', int)
|
|
110
79
|
|
|
111
|
-
def get_version(self):
|
|
80
|
+
def get_version(self) -> Optional[int]:
|
|
112
81
|
return self._only_one('plugin_version', int)
|
|
113
82
|
|
|
114
|
-
def _only_one(self, key, result=str):
|
|
83
|
+
def _only_one(self, key: str, result: Callable=str) -> Optional[Any]:
|
|
115
84
|
lst = [x[1].text for x in self._parsed.findall('entry') if x[0].tag == 'string' and x[0].text == key]
|
|
116
85
|
if lst is None or len(lst) < 1:
|
|
117
86
|
return None
|
|
118
87
|
if len(lst) > 1:
|
|
119
88
|
raise ValueError(f'plugin declares {len(lst)} entries for {key}')
|
|
120
89
|
return result(lst[0])
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
def from_jar(jar_path: Union[PurePath, str]) -> Plugin:
|
|
93
|
+
jar_path = path(jar_path) # in case it's a string
|
|
94
|
+
plugin_id = Plugin.id_from_jar(jar_path)
|
|
95
|
+
plugin_fstr = str(Plugin.id_to_file(plugin_id))
|
|
96
|
+
with ZipFile(jar_path, 'r') as zip_file:
|
|
97
|
+
with zip_file.open(plugin_fstr, 'r') as plugin_file:
|
|
98
|
+
return Plugin(plugin_file, plugin_fstr)
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def from_path(fpath: Union[PurePath, str]) -> Plugin:
|
|
102
|
+
fpath = path(fpath) # in case it's a string
|
|
103
|
+
with open(fpath, 'r') as input_file:
|
|
104
|
+
return Plugin(input_file, fpath)
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def file_to_id(plugin_fstr: str) -> str:
|
|
108
|
+
return plugin_fstr.replace('/', '.')[:-4] # 4 is len('.xml')
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def id_from_jar(jar_path: Union[PurePath, str]) -> str:
|
|
112
|
+
jar_path = path(jar_path) # in case it's a string
|
|
113
|
+
manifest = JM.from_jar(jar_path)
|
|
114
|
+
for entry in manifest:
|
|
115
|
+
if entry.get('Lockss-Plugin') == 'true':
|
|
116
|
+
name = entry.get('Name')
|
|
117
|
+
if name is None:
|
|
118
|
+
raise Exception(f'{jar_path!s}: Lockss-Plugin entry in META-INF/MANIFEST.MF has no Name value')
|
|
119
|
+
return Plugin.file_to_id(name)
|
|
120
|
+
else:
|
|
121
|
+
raise Exception(f'{jar_path!s}: no Lockss-Plugin entry in META-INF/MANIFEST.MF')
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def id_to_dir(plugin_id: str) -> Path:
|
|
125
|
+
return Plugin.id_to_file(plugin_id).parent
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def id_to_file(plugin_id: str) -> Path:
|
|
129
|
+
return Path(f'{plugin_id.replace(".", "/")}.xml')
|
|
@@ -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,121 +28,132 @@
|
|
|
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
|
-
|
|
31
|
+
# Remove in Python 3.14
|
|
32
|
+
# See https://stackoverflow.com/questions/33533148/how-do-i-type-hint-a-method-with-the-type-of-the-enclosing-class/33533514#33533514
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
from abc import ABC, abstractmethod
|
|
36
|
+
import importlib.resources as IR
|
|
32
37
|
from pathlib import Path
|
|
33
38
|
import subprocess
|
|
39
|
+
from typing import List, Optional, Tuple, Union
|
|
40
|
+
|
|
41
|
+
from lockss.pybasic.fileutil import path
|
|
34
42
|
|
|
35
|
-
from
|
|
36
|
-
import
|
|
37
|
-
from
|
|
43
|
+
from . import resources as __resources__
|
|
44
|
+
from .plugin import Plugin
|
|
45
|
+
from .util import YamlT, load_and_validate
|
|
38
46
|
|
|
39
47
|
|
|
40
48
|
class PluginRegistryCatalog(object):
|
|
41
49
|
|
|
42
50
|
PLUGIN_REGISTRY_CATALOG_SCHEMA = 'plugin-registry-catalog-schema.json'
|
|
43
51
|
|
|
44
|
-
|
|
45
|
-
def from_path(plugin_registry_catalog_path):
|
|
46
|
-
plugin_registry_catalog_path = _path(plugin_registry_catalog_path)
|
|
47
|
-
with importlib.resources.path(lockss.turtles.resources, PluginRegistryCatalog.PLUGIN_REGISTRY_CATALOG_SCHEMA) as plugin_registry_catalog_schema_path:
|
|
48
|
-
parsed = _load_and_validate(plugin_registry_catalog_schema_path, plugin_registry_catalog_path)
|
|
49
|
-
return PluginRegistryCatalog(parsed)
|
|
50
|
-
|
|
51
|
-
def __init__(self, parsed):
|
|
52
|
+
def __init__(self, parsed: YamlT) -> None:
|
|
52
53
|
super().__init__()
|
|
53
|
-
self._parsed = parsed
|
|
54
|
+
self._parsed: YamlT = parsed
|
|
54
55
|
|
|
55
|
-
def get_plugin_registry_files(self):
|
|
56
|
+
def get_plugin_registry_files(self) -> List[str]:
|
|
56
57
|
return self._parsed['plugin-registry-files']
|
|
57
58
|
|
|
59
|
+
@staticmethod
|
|
60
|
+
def from_path(plugin_registry_catalog_path: Union[Path, str]) -> PluginRegistryCatalog:
|
|
61
|
+
plugin_registry_catalog_path = path(plugin_registry_catalog_path)
|
|
62
|
+
with IR.path(__resources__, PluginRegistryCatalog.PLUGIN_REGISTRY_CATALOG_SCHEMA) as plugin_registry_catalog_schema_path:
|
|
63
|
+
parsed = load_and_validate(plugin_registry_catalog_schema_path, plugin_registry_catalog_path)
|
|
64
|
+
return PluginRegistryCatalog(parsed)
|
|
58
65
|
|
|
59
|
-
class PluginRegistry(object):
|
|
60
|
-
|
|
61
|
-
PLUGIN_REGISTRY_SCHEMA = 'plugin-registry-schema.json'
|
|
62
66
|
|
|
63
|
-
|
|
64
|
-
def from_path(plugin_registry_file_path):
|
|
65
|
-
plugin_registry_file_path = _path(plugin_registry_file_path)
|
|
66
|
-
with importlib.resources.path(lockss.turtles.resources, PluginRegistry.PLUGIN_REGISTRY_SCHEMA) as plugin_registry_schema_path:
|
|
67
|
-
lst = _load_and_validate(plugin_registry_schema_path, plugin_registry_file_path, multiple=True)
|
|
68
|
-
return [PluginRegistry._from_obj(parsed, plugin_registry_file_path) for parsed in lst]
|
|
67
|
+
class PluginRegistry(ABC):
|
|
69
68
|
|
|
70
|
-
|
|
71
|
-
def _from_obj(parsed, plugin_registry_file_path):
|
|
72
|
-
typ = parsed['layout']['type']
|
|
73
|
-
if typ == DirectoryPluginRegistry.LAYOUT:
|
|
74
|
-
return DirectoryPluginRegistry(parsed)
|
|
75
|
-
elif typ == RcsPluginRegistry.LAYOUT:
|
|
76
|
-
return RcsPluginRegistry(parsed)
|
|
77
|
-
else:
|
|
78
|
-
raise RuntimeError(f'{plugin_registry_file_path!s}: unknown layout type: {typ}')
|
|
69
|
+
PLUGIN_REGISTRY_SCHEMA = 'plugin-registry-schema.json'
|
|
79
70
|
|
|
80
|
-
def __init__(self, parsed):
|
|
71
|
+
def __init__(self, parsed: YamlT):
|
|
81
72
|
super().__init__()
|
|
82
|
-
self._parsed = parsed
|
|
73
|
+
self._parsed: YamlT = parsed
|
|
83
74
|
|
|
84
|
-
def get_id(self):
|
|
75
|
+
def get_id(self) -> str:
|
|
85
76
|
return self._parsed['id']
|
|
86
77
|
|
|
87
|
-
def get_layer(self, layer_id):
|
|
78
|
+
def get_layer(self, layer_id) -> Optional[PluginRegistryLayer]:
|
|
88
79
|
for layer in self.get_layers():
|
|
89
80
|
if layer.get_id() == layer_id:
|
|
90
81
|
return layer
|
|
91
82
|
return None
|
|
92
83
|
|
|
93
|
-
def get_layer_ids(self):
|
|
84
|
+
def get_layer_ids(self) -> List[str]:
|
|
94
85
|
return [layer.get_id() for layer in self.get_layers()]
|
|
95
86
|
|
|
96
|
-
def get_layers(self):
|
|
87
|
+
def get_layers(self) -> List[PluginRegistryLayer]:
|
|
97
88
|
return [self._make_layer(layer_elem) for layer_elem in self._parsed['layers']]
|
|
98
89
|
|
|
99
|
-
def get_layout_type(self):
|
|
90
|
+
def get_layout_type(self) -> str:
|
|
100
91
|
return self._parsed['layout']['type']
|
|
101
92
|
|
|
102
|
-
def get_name(self):
|
|
93
|
+
def get_name(self) -> str:
|
|
103
94
|
return self._parsed['name']
|
|
104
95
|
|
|
105
|
-
def get_plugin_identifiers(self):
|
|
96
|
+
def get_plugin_identifiers(self) -> List[str]:
|
|
106
97
|
return self._parsed['plugin-identifiers']
|
|
107
98
|
|
|
108
|
-
def has_plugin(self, plugin_id):
|
|
99
|
+
def has_plugin(self, plugin_id) -> bool:
|
|
109
100
|
return plugin_id in self.get_plugin_identifiers()
|
|
110
101
|
|
|
111
|
-
|
|
112
|
-
|
|
102
|
+
@abstractmethod
|
|
103
|
+
def _make_layer(self, parsed: YamlT) -> PluginRegistryLayer:
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def from_path(plugin_registry_file_path: Union[Path, str]) -> List[PluginRegistry]:
|
|
108
|
+
plugin_registry_file_path = path(plugin_registry_file_path)
|
|
109
|
+
with IR.path(__resources__, PluginRegistry.PLUGIN_REGISTRY_SCHEMA) as plugin_registry_schema_path:
|
|
110
|
+
lst = load_and_validate(plugin_registry_schema_path, plugin_registry_file_path, multiple=True)
|
|
111
|
+
return [PluginRegistry._from_obj(parsed, plugin_registry_file_path) for parsed in lst]
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def _from_obj(parsed: YamlT, plugin_registry_file_path: Path) -> PluginRegistry:
|
|
115
|
+
typ = parsed['layout']['type']
|
|
116
|
+
if typ == DirectoryPluginRegistry.LAYOUT:
|
|
117
|
+
return DirectoryPluginRegistry(parsed)
|
|
118
|
+
elif typ == RcsPluginRegistry.LAYOUT:
|
|
119
|
+
return RcsPluginRegistry(parsed)
|
|
120
|
+
else:
|
|
121
|
+
raise RuntimeError(f'{plugin_registry_file_path!s}: unknown layout type: {typ}')
|
|
113
122
|
|
|
114
123
|
|
|
115
|
-
class PluginRegistryLayer(
|
|
124
|
+
class PluginRegistryLayer(ABC):
|
|
116
125
|
|
|
117
126
|
PRODUCTION = 'production'
|
|
118
127
|
|
|
119
128
|
TESTING = 'testing'
|
|
120
129
|
|
|
121
|
-
def __init__(self, plugin_registry, parsed):
|
|
130
|
+
def __init__(self, plugin_registry: PluginRegistry, parsed: YamlT):
|
|
122
131
|
super().__init__()
|
|
123
|
-
self._parsed = parsed
|
|
124
|
-
self._plugin_registry = plugin_registry
|
|
132
|
+
self._parsed: YamlT = parsed
|
|
133
|
+
self._plugin_registry: PluginRegistry = plugin_registry
|
|
125
134
|
|
|
126
|
-
|
|
127
|
-
def deploy_plugin(self, plugin_id, jar_path, interactive=False):
|
|
128
|
-
|
|
135
|
+
@abstractmethod
|
|
136
|
+
def deploy_plugin(self, plugin_id: str, jar_path: Path, interactive: bool=False) -> Optional[Tuple[Path, Plugin]]:
|
|
137
|
+
pass
|
|
129
138
|
|
|
130
|
-
|
|
131
|
-
|
|
139
|
+
@abstractmethod
|
|
140
|
+
def get_file_for(self, plugin_id: str) -> Optional[Path]:
|
|
141
|
+
pass
|
|
132
142
|
|
|
133
|
-
def get_id(self):
|
|
143
|
+
def get_id(self) -> str:
|
|
134
144
|
return self._parsed['id']
|
|
135
145
|
|
|
136
|
-
|
|
137
|
-
|
|
146
|
+
@abstractmethod
|
|
147
|
+
def get_jars(self) -> List[Path]:
|
|
148
|
+
pass
|
|
138
149
|
|
|
139
|
-
def get_name(self):
|
|
150
|
+
def get_name(self) -> str:
|
|
140
151
|
return self._parsed['name']
|
|
141
152
|
|
|
142
|
-
def get_path(self):
|
|
143
|
-
return
|
|
153
|
+
def get_path(self) -> Path:
|
|
154
|
+
return path(self._parsed['path'])
|
|
144
155
|
|
|
145
|
-
def get_plugin_registry(self):
|
|
156
|
+
def get_plugin_registry(self) -> PluginRegistry:
|
|
146
157
|
return self._plugin_registry
|
|
147
158
|
|
|
148
159
|
|
|
@@ -158,47 +169,47 @@ class DirectoryPluginRegistry(PluginRegistry):
|
|
|
158
169
|
|
|
159
170
|
DEFAULT_FILE_NAMING_CONVENTION = FILE_NAMING_CONVENTION_IDENTIFIER
|
|
160
171
|
|
|
161
|
-
def __init__(self, parsed):
|
|
172
|
+
def __init__(self, parsed: YamlT) -> None:
|
|
162
173
|
super().__init__(parsed)
|
|
163
174
|
|
|
164
|
-
def _make_layer(self, parsed):
|
|
175
|
+
def _make_layer(self, parsed) -> PluginRegistryLayer:
|
|
165
176
|
return DirectoryPluginRegistryLayer(self, parsed)
|
|
166
177
|
|
|
167
178
|
|
|
168
179
|
class DirectoryPluginRegistryLayer(PluginRegistryLayer):
|
|
169
180
|
|
|
170
|
-
def __init__(self, plugin_registry, parsed):
|
|
181
|
+
def __init__(self, plugin_registry: PluginRegistry, parsed: YamlT):
|
|
171
182
|
super().__init__(plugin_registry, parsed)
|
|
172
183
|
|
|
173
|
-
def deploy_plugin(self, plugin_id, src_path, interactive=False):
|
|
174
|
-
src_path =
|
|
184
|
+
def deploy_plugin(self, plugin_id: str, src_path: Path, interactive: bool=False) -> Optional[Tuple[Path, Plugin]]:
|
|
185
|
+
src_path = path(src_path) # in case it's a string
|
|
175
186
|
dst_path = self._get_dstpath(plugin_id)
|
|
176
187
|
if not self._proceed_copy(src_path, dst_path, interactive=interactive):
|
|
177
188
|
return None
|
|
178
189
|
self._copy_jar(src_path, dst_path, interactive=interactive)
|
|
179
|
-
return
|
|
190
|
+
return dst_path, Plugin.from_jar(src_path)
|
|
180
191
|
|
|
181
|
-
def get_file_for(self, plugin_id):
|
|
192
|
+
def get_file_for(self, plugin_id) -> Optional[Path]:
|
|
182
193
|
jar_path = self._get_dstpath(plugin_id)
|
|
183
194
|
return jar_path if jar_path.is_file() else None
|
|
184
195
|
|
|
185
|
-
def get_file_naming_convention(self):
|
|
196
|
+
def get_file_naming_convention(self) -> str:
|
|
186
197
|
return self.get_plugin_registry()._parsed['layout'].get('file-naming-convention', DirectoryPluginRegistry.DEFAULT_FILE_NAMING_CONVENTION)
|
|
187
198
|
|
|
188
|
-
def get_jars(self):
|
|
199
|
+
def get_jars(self) -> List[Path]:
|
|
189
200
|
return sorted(self.get_path().glob('*.jar'))
|
|
190
201
|
|
|
191
|
-
def _copy_jar(self, src_path, dst_path, interactive=False):
|
|
202
|
+
def _copy_jar(self, src_path: Path, dst_path: Path, interactive: bool=False) -> None:
|
|
192
203
|
basename = dst_path.name
|
|
193
204
|
subprocess.run(['cp', str(src_path), str(dst_path)], check=True, cwd=self.get_path())
|
|
194
205
|
if subprocess.run('command -v selinuxenabled > /dev/null && selinuxenabled && command -v chcon > /dev/null', shell=True).returncode == 0:
|
|
195
206
|
cmd = ['chcon', '-t', 'httpd_sys_content_t', basename]
|
|
196
207
|
subprocess.run(cmd, check=True, cwd=self.get_path())
|
|
197
208
|
|
|
198
|
-
def _get_dstpath(self, plugin_id):
|
|
199
|
-
return
|
|
209
|
+
def _get_dstpath(self, plugin_id: str) -> Path:
|
|
210
|
+
return self.get_path().joinpath(self._get_dstfile(plugin_id))
|
|
200
211
|
|
|
201
|
-
def _get_dstfile(self, plugin_id):
|
|
212
|
+
def _get_dstfile(self, plugin_id: str) -> str:
|
|
202
213
|
conv = self.get_file_naming_convention()
|
|
203
214
|
if conv == DirectoryPluginRegistry.FILE_NAMING_CONVENTION_IDENTIFIER:
|
|
204
215
|
return f'{plugin_id}.jar'
|
|
@@ -209,7 +220,7 @@ class DirectoryPluginRegistryLayer(PluginRegistryLayer):
|
|
|
209
220
|
else:
|
|
210
221
|
raise RuntimeError(f'{self.get_plugin_registry().get_id()}: unknown file naming convention: {conv}')
|
|
211
222
|
|
|
212
|
-
def _proceed_copy(self, src_path, dst_path, interactive=False):
|
|
223
|
+
def _proceed_copy(self, src_path: Path, dst_path: Path, interactive: bool=False) -> bool:
|
|
213
224
|
if not dst_path.exists():
|
|
214
225
|
if interactive:
|
|
215
226
|
i = input(f'{dst_path} does not exist in {self.get_plugin_registry().get_id()}:{self.get_id()} ({self.get_name()}); create it (y/n)? [n] ').lower() or 'n'
|
|
@@ -222,19 +233,19 @@ class RcsPluginRegistry(DirectoryPluginRegistry):
|
|
|
222
233
|
|
|
223
234
|
LAYOUT = 'rcs'
|
|
224
235
|
|
|
225
|
-
def __init__(self, parsed):
|
|
236
|
+
def __init__(self, parsed: YamlT) -> None:
|
|
226
237
|
super().__init__(parsed)
|
|
227
238
|
|
|
228
|
-
def _make_layer(self, parsed):
|
|
239
|
+
def _make_layer(self, parsed: YamlT) -> PluginRegistryLayer:
|
|
229
240
|
return RcsPluginRegistryLayer(self, parsed)
|
|
230
241
|
|
|
231
242
|
|
|
232
243
|
class RcsPluginRegistryLayer(DirectoryPluginRegistryLayer):
|
|
233
244
|
|
|
234
|
-
def __init__(self, plugin_registry, parsed):
|
|
245
|
+
def __init__(self, plugin_registry: PluginRegistry, parsed: YamlT) -> None:
|
|
235
246
|
super().__init__(plugin_registry, parsed)
|
|
236
247
|
|
|
237
|
-
def _copy_jar(self, src_path, dst_path, interactive=False):
|
|
248
|
+
def _copy_jar(self, src_path: Path, dst_path: Path, interactive: bool=False) -> None:
|
|
238
249
|
basename = dst_path.name
|
|
239
250
|
plugin = Plugin.from_jar(src_path)
|
|
240
251
|
rcs_path = self.get_path().joinpath('RCS', f'{basename},v')
|