spaceforge 0.0.2__py3-none-any.whl → 0.0.4__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.
- spaceforge/__init__.py +12 -4
- spaceforge/__main__.py +3 -3
- spaceforge/_version.py +0 -1
- spaceforge/_version_scm.py +2 -2
- spaceforge/cls.py +16 -12
- spaceforge/conftest.py +89 -0
- spaceforge/generator.py +119 -54
- spaceforge/plugin.py +106 -12
- spaceforge/runner.py +0 -12
- spaceforge/schema.json +38 -29
- spaceforge/templates/binary_install.sh.j2 +23 -0
- spaceforge/templates/ensure_spaceforge_and_run.sh.j2 +22 -0
- spaceforge/{generator_test.py → test_generator.py} +263 -51
- spaceforge/test_generator_binaries.py +194 -0
- spaceforge/test_generator_core.py +180 -0
- spaceforge/test_generator_hooks.py +90 -0
- spaceforge/test_generator_parameters.py +59 -0
- spaceforge/test_plugin.py +357 -0
- spaceforge/test_plugin_file_operations.py +118 -0
- spaceforge/test_plugin_hooks.py +100 -0
- spaceforge/test_plugin_inheritance.py +102 -0
- spaceforge/{runner_test.py → test_runner.py} +2 -65
- spaceforge/test_runner_cli.py +69 -0
- spaceforge/test_runner_core.py +124 -0
- spaceforge/test_runner_execution.py +169 -0
- spaceforge-0.0.4.dist-info/METADATA +605 -0
- spaceforge-0.0.4.dist-info/RECORD +33 -0
- spaceforge/plugin_test.py +0 -621
- spaceforge-0.0.2.dist-info/METADATA +0 -163
- spaceforge-0.0.2.dist-info/RECORD +0 -20
- /spaceforge/{cls_test.py → test_cls.py} +0 -0
- {spaceforge-0.0.2.dist-info → spaceforge-0.0.4.dist-info}/WHEEL +0 -0
- {spaceforge-0.0.2.dist-info → spaceforge-0.0.4.dist-info}/entry_points.txt +0 -0
- {spaceforge-0.0.2.dist-info → spaceforge-0.0.4.dist-info}/licenses/LICENSE +0 -0
- {spaceforge-0.0.2.dist-info → spaceforge-0.0.4.dist-info}/top_level.txt +0 -0
spaceforge/__init__.py
CHANGED
|
@@ -4,10 +4,18 @@ Spaceforge - Spacelift Plugin Framework
|
|
|
4
4
|
A Python framework for building Spacelift plugins with hook-based functionality.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from ._version import get_version
|
|
8
|
-
from .cls import
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
from spaceforge._version import get_version
|
|
8
|
+
from spaceforge.cls import (
|
|
9
|
+
Binary,
|
|
10
|
+
Context,
|
|
11
|
+
MountedFile,
|
|
12
|
+
Parameter,
|
|
13
|
+
Policy,
|
|
14
|
+
Variable,
|
|
15
|
+
Webhook,
|
|
16
|
+
)
|
|
17
|
+
from spaceforge.plugin import SpaceforgePlugin
|
|
18
|
+
from spaceforge.runner import PluginRunner
|
|
11
19
|
|
|
12
20
|
__version__ = get_version()
|
|
13
21
|
__all__ = [
|
spaceforge/__main__.py
CHANGED
|
@@ -4,9 +4,9 @@ Main entry point for spaceforge module.
|
|
|
4
4
|
|
|
5
5
|
import click
|
|
6
6
|
|
|
7
|
-
from ._version import get_version
|
|
8
|
-
from .generator import generate_command
|
|
9
|
-
from .runner import runner_command
|
|
7
|
+
from spaceforge._version import get_version
|
|
8
|
+
from spaceforge.generator import generate_command
|
|
9
|
+
from spaceforge.runner import runner_command
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
@click.group()
|
spaceforge/_version.py
CHANGED
spaceforge/_version_scm.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
31
|
+
__version__ = version = '0.0.4'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 0, 4)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
spaceforge/cls.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import uuid
|
|
1
2
|
from typing import Dict, List, Literal, Optional
|
|
2
3
|
|
|
3
4
|
from pydantic import Field
|
|
@@ -19,8 +20,7 @@ class Binary:
|
|
|
19
20
|
|
|
20
21
|
Attributes:
|
|
21
22
|
name (str): The name of the binary file.
|
|
22
|
-
|
|
23
|
-
sensitive (bool): Whether the binary file is sensitive.
|
|
23
|
+
download_urls (Dict[BinaryType, str]): A dictionary mapping binary types to their download URLs.
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
26
|
name: str
|
|
@@ -38,6 +38,7 @@ class Parameter:
|
|
|
38
38
|
sensitive (bool): Whether the parameter contains sensitive information.
|
|
39
39
|
required (bool): Whether the parameter is required.
|
|
40
40
|
default (Optional[str]): The default value of the parameter, if any. (required if sensitive is False)
|
|
41
|
+
id (str): Unique identifier for the parameter.
|
|
41
42
|
"""
|
|
42
43
|
|
|
43
44
|
name: str
|
|
@@ -45,6 +46,7 @@ class Parameter:
|
|
|
45
46
|
sensitive: bool = False
|
|
46
47
|
required: bool = False
|
|
47
48
|
default: Optional[str] = None
|
|
49
|
+
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
48
50
|
|
|
49
51
|
def __post_init__(self) -> None:
|
|
50
52
|
if not self.required and self.default is None:
|
|
@@ -116,7 +118,7 @@ class Context:
|
|
|
116
118
|
Attributes:
|
|
117
119
|
name_prefix (str): The name of the context, will be appended with a unique ID.
|
|
118
120
|
description (str): A description of the context.
|
|
119
|
-
labels (
|
|
121
|
+
labels (Optional[List[str]]): Labels associated with the context.
|
|
120
122
|
env (list): List of variables associated with the context.
|
|
121
123
|
hooks (dict): Hooks associated with the context.
|
|
122
124
|
"""
|
|
@@ -126,7 +128,7 @@ class Context:
|
|
|
126
128
|
env: Optional[List[Variable]] = optional_field
|
|
127
129
|
mounted_files: Optional[List[MountedFile]] = optional_field
|
|
128
130
|
hooks: Optional[Dict[HookType, List[str]]] = optional_field
|
|
129
|
-
labels: Optional[
|
|
131
|
+
labels: Optional[List[str]] = optional_field
|
|
130
132
|
|
|
131
133
|
|
|
132
134
|
@pydantic_dataclass
|
|
@@ -137,14 +139,14 @@ class Webhook:
|
|
|
137
139
|
Attributes:
|
|
138
140
|
name_prefix (str): The name of the webhook, will be appended with a unique ID.
|
|
139
141
|
endpoint (str): The URL endpoint for the webhook.
|
|
140
|
-
labels (Optional[
|
|
141
|
-
|
|
142
|
+
labels (Optional[List[str]]): Labels associated with the webhook.
|
|
143
|
+
secret (str): the ID of the parameter where the webhook secret is retrieved from
|
|
142
144
|
"""
|
|
143
145
|
|
|
144
146
|
name_prefix: str
|
|
145
147
|
endpoint: str
|
|
146
|
-
|
|
147
|
-
|
|
148
|
+
secretFromParameter: str
|
|
149
|
+
labels: Optional[List[str]] = optional_field
|
|
148
150
|
|
|
149
151
|
|
|
150
152
|
@pydantic_dataclass
|
|
@@ -156,13 +158,13 @@ class Policy:
|
|
|
156
158
|
name_prefix (str): The name of the policy, will be appended with a unique ID.
|
|
157
159
|
type (str): The type of the policy (e.g., "terraform", "kubernetes").
|
|
158
160
|
body (str): The body of the policy, typically a configuration or script.
|
|
159
|
-
labels (Optional[
|
|
161
|
+
labels (Optional[List[str]]): Labels associated with the policy.
|
|
160
162
|
"""
|
|
161
163
|
|
|
162
164
|
name_prefix: str
|
|
163
165
|
type: str
|
|
164
166
|
body: str
|
|
165
|
-
labels: Optional[
|
|
167
|
+
labels: Optional[List[str]] = optional_field
|
|
166
168
|
|
|
167
169
|
|
|
168
170
|
@pydantic_dataclass
|
|
@@ -171,19 +173,21 @@ class PluginManifest:
|
|
|
171
173
|
A class to represent the manifest of a Spacelift plugin.
|
|
172
174
|
|
|
173
175
|
Attributes:
|
|
174
|
-
|
|
176
|
+
name (str): The name of the plugin, will be appended with a unique ID.
|
|
175
177
|
description (str): A description of the plugin.
|
|
176
178
|
author (str): The author of the plugin.
|
|
179
|
+
labels (list[str]): List of labels for the plugin.
|
|
177
180
|
parameters (list[Parameter]): List of parameters for the plugin.
|
|
178
181
|
contexts (list[Context]): List of contexts for the plugin.
|
|
179
182
|
webhooks (list[Webhook]): List of webhooks for the plugin.
|
|
180
183
|
policies (list[Policy]): List of policies for the plugin.
|
|
181
184
|
"""
|
|
182
185
|
|
|
183
|
-
|
|
186
|
+
name: str
|
|
184
187
|
version: str
|
|
185
188
|
description: str
|
|
186
189
|
author: str
|
|
190
|
+
labels: Optional[List[str]] = optional_field
|
|
187
191
|
parameters: Optional[List[Parameter]] = optional_field
|
|
188
192
|
contexts: Optional[List[Context]] = optional_field
|
|
189
193
|
webhooks: Optional[List[Webhook]] = optional_field
|
spaceforge/conftest.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Shared test fixtures for spaceforge tests."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import tempfile
|
|
5
|
+
from typing import Dict, Generator, List
|
|
6
|
+
from unittest.mock import Mock
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from spaceforge.plugin import SpaceforgePlugin
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.fixture
|
|
14
|
+
def temp_dir() -> Generator[str, None, None]:
|
|
15
|
+
"""Provide a temporary directory for test files."""
|
|
16
|
+
temp_dir = tempfile.mkdtemp()
|
|
17
|
+
yield temp_dir
|
|
18
|
+
import shutil
|
|
19
|
+
|
|
20
|
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def test_plugin_content() -> str:
|
|
25
|
+
"""Basic test plugin content."""
|
|
26
|
+
return """
|
|
27
|
+
from spaceforge import SpaceforgePlugin, Parameter
|
|
28
|
+
|
|
29
|
+
class TestPlugin(SpaceforgePlugin):
|
|
30
|
+
__plugin_name__ = "test"
|
|
31
|
+
__version__ = "1.0.0"
|
|
32
|
+
__author__ = "Test Author"
|
|
33
|
+
|
|
34
|
+
__parameters__ = [
|
|
35
|
+
Parameter(
|
|
36
|
+
name="test_param",
|
|
37
|
+
description="Test parameter",
|
|
38
|
+
required=False,
|
|
39
|
+
default="default_value"
|
|
40
|
+
)
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
def after_plan(self) -> None:
|
|
44
|
+
pass
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@pytest.fixture
|
|
49
|
+
def test_plugin_file(temp_dir: str, test_plugin_content: str) -> str:
|
|
50
|
+
"""Create a test plugin file."""
|
|
51
|
+
plugin_path = os.path.join(temp_dir, "plugin.py")
|
|
52
|
+
with open(plugin_path, "w") as f:
|
|
53
|
+
f.write(test_plugin_content)
|
|
54
|
+
return plugin_path
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@pytest.fixture
|
|
58
|
+
def mock_env() -> Dict[str, str]:
|
|
59
|
+
"""Common test environment variables."""
|
|
60
|
+
return {
|
|
61
|
+
"SPACELIFT_API_TOKEN": "test_token",
|
|
62
|
+
"TF_VAR_spacelift_graphql_endpoint": "https://test.spacelift.io",
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@pytest.fixture
|
|
67
|
+
def mock_api_response() -> Mock:
|
|
68
|
+
"""Mock API response for testing."""
|
|
69
|
+
mock_response = Mock()
|
|
70
|
+
mock_response.read.return_value = b'{"data": {"test": "result"}}'
|
|
71
|
+
return mock_response
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class ExampleTestPlugin(SpaceforgePlugin):
|
|
75
|
+
"""Reusable test plugin class."""
|
|
76
|
+
|
|
77
|
+
__plugin_name__ = "example"
|
|
78
|
+
__version__ = "1.0.0"
|
|
79
|
+
__author__ = "Test"
|
|
80
|
+
|
|
81
|
+
def __init__(self) -> None:
|
|
82
|
+
super().__init__()
|
|
83
|
+
self.hook_calls: List[str] = []
|
|
84
|
+
|
|
85
|
+
def after_plan(self) -> None:
|
|
86
|
+
self.hook_calls.append("after_plan")
|
|
87
|
+
|
|
88
|
+
def before_apply(self) -> None:
|
|
89
|
+
self.hook_calls.append("before_apply")
|
spaceforge/generator.py
CHANGED
|
@@ -4,9 +4,10 @@ YAML generator for Spacelift plugins.
|
|
|
4
4
|
|
|
5
5
|
import importlib.util
|
|
6
6
|
import os
|
|
7
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, Union
|
|
8
8
|
|
|
9
9
|
import yaml
|
|
10
|
+
from jinja2 import Environment, PackageLoader, select_autoescape
|
|
10
11
|
|
|
11
12
|
if TYPE_CHECKING:
|
|
12
13
|
from .plugin import SpaceforgePlugin
|
|
@@ -46,6 +47,10 @@ class PluginGenerator:
|
|
|
46
47
|
self.plugin_class: Optional[Type[SpaceforgePlugin]] = None
|
|
47
48
|
self.plugin_instance: Optional[SpaceforgePlugin] = None
|
|
48
49
|
self.plugin_working_directory: Optional[str] = None
|
|
50
|
+
self.config: Optional[Dict[str, Any]] = None
|
|
51
|
+
self.jinja = Environment(
|
|
52
|
+
loader=PackageLoader("spaceforge"), autoescape=select_autoescape()
|
|
53
|
+
)
|
|
49
54
|
|
|
50
55
|
def load_plugin(self) -> None:
|
|
51
56
|
"""Load the plugin class from the specified path."""
|
|
@@ -80,8 +85,15 @@ class PluginGenerator:
|
|
|
80
85
|
self.plugin_working_directory = (
|
|
81
86
|
"/mnt/workspace/plugins/" + plugin_class.__plugin_name__.lower()
|
|
82
87
|
)
|
|
88
|
+
self.config = {
|
|
89
|
+
"setup_virtual_env": (
|
|
90
|
+
f"cd {self.plugin_working_directory} && python -m venv ./venv && "
|
|
91
|
+
+ "source venv/bin/activate && (command -v spaceforge &> /dev/null || pip install spaceforge)"
|
|
92
|
+
),
|
|
93
|
+
"plugin_mounted_path": f"{self.plugin_working_directory}/{os.path.basename(self.plugin_path)}",
|
|
94
|
+
}
|
|
83
95
|
|
|
84
|
-
def get_plugin_metadata(self) -> Dict[str, str]:
|
|
96
|
+
def get_plugin_metadata(self) -> Dict[str, Union[str, List[str]]]:
|
|
85
97
|
"""Extract metadata from the plugin class."""
|
|
86
98
|
if self.plugin_class is None:
|
|
87
99
|
raise ValueError("Plugin class not loaded. Call load_plugin() first.")
|
|
@@ -97,6 +109,7 @@ class PluginGenerator:
|
|
|
97
109
|
self.plugin_class.__name__.lower().replace("plugin", ""),
|
|
98
110
|
),
|
|
99
111
|
"version": getattr(self.plugin_class, "__version__", "1.0.0"),
|
|
112
|
+
"labels": getattr(self.plugin_class, "__labels__", []),
|
|
100
113
|
"description": doc
|
|
101
114
|
or f"A Spacelift plugin built with {self.plugin_class.__name__}",
|
|
102
115
|
"author": getattr(self.plugin_class, "__author__", "Unknown"),
|
|
@@ -128,22 +141,27 @@ class PluginGenerator:
|
|
|
128
141
|
|
|
129
142
|
return hook_methods
|
|
130
143
|
|
|
131
|
-
def
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
def _add_to_mounted_files(
|
|
145
|
+
self,
|
|
146
|
+
hooks: Dict[str, List[str]],
|
|
147
|
+
mounted_files: List[MountedFile],
|
|
148
|
+
phase: str,
|
|
149
|
+
filepath: str,
|
|
150
|
+
filecontent: str,
|
|
151
|
+
) -> None:
|
|
152
|
+
file = f"{self.plugin_working_directory}/{filepath}"
|
|
153
|
+
hooks[phase].append(f"chmod +x {file} && {file}")
|
|
154
|
+
mounted_files.append(
|
|
155
|
+
MountedFile(
|
|
156
|
+
path=f"{self.plugin_working_directory}/{filepath}",
|
|
157
|
+
content=filecontent,
|
|
158
|
+
sensitive=False,
|
|
146
159
|
)
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
def _update_with_requirements(self, mounted_files: List[MountedFile]) -> None:
|
|
163
|
+
"""Update the plugin hooks if there is a requirements.txt"""
|
|
164
|
+
if os.path.exists("requirements.txt") and self.config is not None:
|
|
147
165
|
# read the requirements.txt file
|
|
148
166
|
with open("requirements.txt", "r") as f:
|
|
149
167
|
mounted_files.append(
|
|
@@ -154,33 +172,80 @@ class PluginGenerator:
|
|
|
154
172
|
)
|
|
155
173
|
)
|
|
156
174
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
)
|
|
161
|
-
if os.path.exists(self.plugin_path):
|
|
175
|
+
def _update_with_python_file(self, mounted_files: List[MountedFile]) -> None:
|
|
176
|
+
"""Ensure the plugin file itself is mounted."""
|
|
177
|
+
if os.path.exists(self.plugin_path) and self.config is not None:
|
|
162
178
|
with open(self.plugin_path, "r") as f:
|
|
163
179
|
mounted_files.append(
|
|
164
180
|
MountedFile(
|
|
165
|
-
path=plugin_mounted_path,
|
|
181
|
+
path=self.config["plugin_mounted_path"],
|
|
166
182
|
content=f.read(),
|
|
167
183
|
sensitive=False,
|
|
168
184
|
)
|
|
169
185
|
)
|
|
170
186
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
187
|
+
def _add_spaceforge_hooks(
|
|
188
|
+
self, hooks: Dict[str, List[str]], mounted_files: List[MountedFile]
|
|
189
|
+
) -> None:
|
|
175
190
|
# Add the spaceforge hook to actually run the plugin
|
|
191
|
+
if self.config is None:
|
|
192
|
+
raise ValueError("Plugin config not set. Call load_plugin() first.")
|
|
193
|
+
|
|
176
194
|
available_hooks = self.get_available_hooks()
|
|
177
195
|
for hook in available_hooks:
|
|
178
196
|
# Ensure the hook exists in the first context
|
|
179
197
|
if hook not in hooks:
|
|
180
198
|
hooks[hook] = []
|
|
181
|
-
|
|
182
|
-
|
|
199
|
+
|
|
200
|
+
directory = os.path.dirname(self.config["plugin_mounted_path"])
|
|
201
|
+
template = self.jinja.get_template("ensure_spaceforge_and_run.sh.j2")
|
|
202
|
+
render = template.render(
|
|
203
|
+
plugin_path=directory,
|
|
204
|
+
plugin_file=self.config["plugin_mounted_path"],
|
|
205
|
+
phase=hook,
|
|
183
206
|
)
|
|
207
|
+
self._add_to_mounted_files(hooks, mounted_files, hook, f"{hook}.sh", render)
|
|
208
|
+
|
|
209
|
+
def _map_variables_to_parameters(self, contexts: List[Context]) -> None:
|
|
210
|
+
for context in contexts:
|
|
211
|
+
# Get the variables from the plugin and change the value_from_parameter to the ID of the parameter
|
|
212
|
+
# based on its name.
|
|
213
|
+
if context.env is None:
|
|
214
|
+
continue
|
|
215
|
+
|
|
216
|
+
for variable in context.env:
|
|
217
|
+
if variable.value_from_parameter:
|
|
218
|
+
parameter_name = variable.value_from_parameter
|
|
219
|
+
parameters = self.get_plugin_parameters()
|
|
220
|
+
if parameters:
|
|
221
|
+
parameter = next(
|
|
222
|
+
(
|
|
223
|
+
p
|
|
224
|
+
for p in parameters
|
|
225
|
+
if p.name == parameter_name or p.id == parameter_name
|
|
226
|
+
),
|
|
227
|
+
None,
|
|
228
|
+
)
|
|
229
|
+
if parameter:
|
|
230
|
+
variable.value_from_parameter = parameter.id
|
|
231
|
+
else:
|
|
232
|
+
raise ValueError(
|
|
233
|
+
f"Parameter {parameter_name} not found for variable {variable.key}"
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
def get_plugin_contexts(self) -> List[Context]:
|
|
237
|
+
"""Get context definitions from the plugin class."""
|
|
238
|
+
|
|
239
|
+
f"cd {self.plugin_working_directory}"
|
|
240
|
+
hooks: Dict[str, List[str]] = {
|
|
241
|
+
"before_init": [f"mkdir -p {self.plugin_working_directory}"]
|
|
242
|
+
}
|
|
243
|
+
mounted_files: List[MountedFile] = []
|
|
244
|
+
|
|
245
|
+
self._update_with_requirements(mounted_files)
|
|
246
|
+
self._update_with_python_file(mounted_files)
|
|
247
|
+
self._generate_binary_install_command(hooks, mounted_files)
|
|
248
|
+
self._add_spaceforge_hooks(hooks, mounted_files)
|
|
184
249
|
|
|
185
250
|
# Get the contexts and append the hooks and mounted files to it.
|
|
186
251
|
if self.plugin_class is None:
|
|
@@ -208,42 +273,41 @@ class PluginGenerator:
|
|
|
208
273
|
contexts[0].hooks.update(hooks)
|
|
209
274
|
contexts[0].mounted_files.extend(mounted_files)
|
|
210
275
|
|
|
276
|
+
self._map_variables_to_parameters(contexts)
|
|
277
|
+
|
|
211
278
|
return contexts
|
|
212
279
|
|
|
213
|
-
def
|
|
280
|
+
def _generate_binary_install_command(
|
|
281
|
+
self, hooks: Dict[str, List[str]], mounted_files: List[MountedFile]
|
|
282
|
+
) -> None:
|
|
214
283
|
binaries = self.get_plugin_binaries()
|
|
215
284
|
if binaries is None:
|
|
216
|
-
return
|
|
285
|
+
return None
|
|
217
286
|
|
|
218
|
-
binary_cmd = ""
|
|
219
|
-
if len(binaries) > 0:
|
|
220
|
-
binary_cmd = f"mkdir -p {static_binary_directory} && cd {static_binary_directory} && "
|
|
221
287
|
for i, binary in enumerate(binaries):
|
|
222
288
|
amd64_url = binary.download_urls.get("amd64", None)
|
|
223
289
|
arm64_url = binary.download_urls.get("arm64", None)
|
|
290
|
+
binary_path = f"{static_binary_directory}/{binary.name}"
|
|
224
291
|
if amd64_url is None and arm64_url is None:
|
|
225
292
|
raise ValueError(
|
|
226
293
|
f"Binary {binary.name} must have at least one download URL defined (amd64 or arm64)"
|
|
227
294
|
)
|
|
228
295
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
f"curl {arm64_url} -o {binary_path} -L && chmod +x {binary_path}"
|
|
237
|
-
if arm64_url is not None
|
|
238
|
-
else "echo 'arm64 binary not available' && exit 1"
|
|
296
|
+
template = self.jinja.get_template("binary_install.sh.j2")
|
|
297
|
+
render = template.render(
|
|
298
|
+
binary=binary,
|
|
299
|
+
amd64_url=amd64_url,
|
|
300
|
+
arm64_url=arm64_url,
|
|
301
|
+
binary_path=binary_path,
|
|
302
|
+
static_binary_directory=static_binary_directory,
|
|
239
303
|
)
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
304
|
+
self._add_to_mounted_files(
|
|
305
|
+
hooks,
|
|
306
|
+
mounted_files,
|
|
307
|
+
"before_init",
|
|
308
|
+
f"binary_install_{binary.name}.sh",
|
|
309
|
+
render,
|
|
243
310
|
)
|
|
244
|
-
if i < len(binaries) - 1:
|
|
245
|
-
binary_cmd += " && "
|
|
246
|
-
return binary_cmd
|
|
247
311
|
|
|
248
312
|
def get_plugin_binaries(self) -> Optional[List[Binary]]:
|
|
249
313
|
"""Get binary definitions from the plugin class."""
|
|
@@ -265,10 +329,11 @@ class PluginGenerator:
|
|
|
265
329
|
metadata = self.get_plugin_metadata()
|
|
266
330
|
|
|
267
331
|
return PluginManifest(
|
|
268
|
-
|
|
269
|
-
version=metadata.get("version", "1.0.0"),
|
|
270
|
-
description=metadata.get("description", ""),
|
|
271
|
-
author=metadata.get("author", "Unknown"),
|
|
332
|
+
name=str(metadata.get("name_prefix", "unknown")),
|
|
333
|
+
version=str(metadata.get("version", "1.0.0")),
|
|
334
|
+
description=str(metadata.get("description", "")),
|
|
335
|
+
author=str(metadata.get("author", "Unknown")),
|
|
336
|
+
labels=list(metadata.get("labels", [])),
|
|
272
337
|
parameters=self.get_plugin_parameters(),
|
|
273
338
|
contexts=self.get_plugin_contexts(),
|
|
274
339
|
webhooks=self.get_plugin_webhooks(),
|