spaceforge 0.1.0.dev0__py3-none-any.whl → 1.0.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.
- spaceforge/__init__.py +12 -4
- spaceforge/__main__.py +3 -3
- spaceforge/_version.py +0 -1
- spaceforge/_version_scm.py +34 -0
- spaceforge/cls.py +24 -14
- spaceforge/conftest.py +89 -0
- spaceforge/generator.py +129 -56
- spaceforge/plugin.py +199 -22
- spaceforge/runner.py +0 -12
- spaceforge/schema.json +45 -22
- spaceforge/templates/binary_install.sh.j2 +24 -0
- spaceforge/templates/ensure_spaceforge_and_run.sh.j2 +24 -0
- spaceforge/{generator_test.py → test_generator.py} +265 -53
- 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-1.0.0.dist-info/METADATA +606 -0
- spaceforge-1.0.0.dist-info/RECORD +33 -0
- spaceforge/plugin_test.py +0 -621
- spaceforge-0.1.0.dev0.dist-info/METADATA +0 -163
- spaceforge-0.1.0.dev0.dist-info/RECORD +0 -19
- /spaceforge/{cls_test.py → test_cls.py} +0 -0
- {spaceforge-0.1.0.dev0.dist-info → spaceforge-1.0.0.dist-info}/WHEEL +0 -0
- {spaceforge-0.1.0.dev0.dist-info → spaceforge-1.0.0.dist-info}/entry_points.txt +0 -0
- {spaceforge-0.1.0.dev0.dist-info → spaceforge-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {spaceforge-0.1.0.dev0.dist-info → spaceforge-1.0.0.dist-info}/top_level.txt +0 -0
spaceforge/plugin.py
CHANGED
|
@@ -9,7 +9,8 @@ import os
|
|
|
9
9
|
import subprocess
|
|
10
10
|
import urllib.request
|
|
11
11
|
from abc import ABC
|
|
12
|
-
from typing import Any, Dict, List, Optional, Tuple
|
|
12
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
13
|
+
from urllib.error import HTTPError
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class SpaceforgePlugin(ABC):
|
|
@@ -31,29 +32,41 @@ class SpaceforgePlugin(ABC):
|
|
|
31
32
|
__author__ = "Spacelift Team"
|
|
32
33
|
|
|
33
34
|
def __init__(self) -> None:
|
|
35
|
+
self._run_id = os.environ.get("TF_VAR_spacelift_run_id", "local")
|
|
36
|
+
self._is_local = self._run_id == "local"
|
|
34
37
|
self.logger = self._setup_logger()
|
|
38
|
+
self.spacelift_domain = os.environ.get(
|
|
39
|
+
"TF_VAR_spacelift_graphql_endpoint", ""
|
|
40
|
+
).replace("/graphql", "")
|
|
35
41
|
|
|
36
|
-
self._api_token = os.environ.get("SPACELIFT_API_TOKEN"
|
|
37
|
-
self.
|
|
38
|
-
"TF_VAR_spacelift_graphql_endpoint"
|
|
42
|
+
self._api_token = os.environ.get("SPACELIFT_API_TOKEN") or False
|
|
43
|
+
self._api_endpoint = (
|
|
44
|
+
os.environ.get("TF_VAR_spacelift_graphql_endpoint") or False
|
|
39
45
|
)
|
|
40
|
-
self._api_enabled = self._api_token
|
|
41
|
-
self._workspace_root = os.
|
|
46
|
+
self._api_enabled = bool(self._api_token and self._api_endpoint)
|
|
47
|
+
self._workspace_root = os.getcwd()
|
|
48
|
+
self._spacelift_markdown_endpoint = None
|
|
49
|
+
self._markdown_endpoint_token = os.environ.get("SPACELIFT_API_TOKEN") or False
|
|
42
50
|
|
|
43
51
|
# This should be the last thing we do in the constructor
|
|
44
52
|
# because we set api_enabled to false if the domain is set up incorrectly.
|
|
45
|
-
if self.
|
|
53
|
+
if self._api_endpoint and isinstance(self._api_endpoint, str):
|
|
46
54
|
# this must occur after we check if spacelift domain is false
|
|
47
55
|
# because the domain could be set but not start with https://
|
|
48
|
-
if self.
|
|
49
|
-
if self.
|
|
50
|
-
self.
|
|
56
|
+
if self._api_endpoint.startswith("https://"):
|
|
57
|
+
if self._api_endpoint.endswith("/"):
|
|
58
|
+
self._api_endpoint = self._api_endpoint[:-1]
|
|
51
59
|
else:
|
|
52
60
|
self.logger.warning(
|
|
53
61
|
"SPACELIFT_DOMAIN does not start with https://, api calls will fail."
|
|
54
62
|
)
|
|
55
63
|
self._api_enabled = False
|
|
56
64
|
|
|
65
|
+
if self._api_enabled:
|
|
66
|
+
self._spacelift_markdown_endpoint = self._api_endpoint.replace(
|
|
67
|
+
"/graphql", "/worker/plugin_logs_url"
|
|
68
|
+
)
|
|
69
|
+
|
|
57
70
|
def _setup_logger(self) -> logging.Logger:
|
|
58
71
|
"""Set up logging for the plugin."""
|
|
59
72
|
|
|
@@ -62,7 +75,7 @@ class SpaceforgePlugin(ABC):
|
|
|
62
75
|
warn_color = "\033[33m"
|
|
63
76
|
error_color = "\033[31m"
|
|
64
77
|
end_color = "\033[0m"
|
|
65
|
-
run_id =
|
|
78
|
+
run_id = self._run_id
|
|
66
79
|
plugin_name = self.__plugin_name__
|
|
67
80
|
|
|
68
81
|
class ColorFormatter(logging.Formatter):
|
|
@@ -93,13 +106,62 @@ class SpaceforgePlugin(ABC):
|
|
|
93
106
|
handler.setFormatter(ColorFormatter())
|
|
94
107
|
|
|
95
108
|
# Always check for debug mode spacelift variable
|
|
96
|
-
if os.environ.get("SPACELIFT_DEBUG"):
|
|
109
|
+
if os.environ.get("SPACELIFT_DEBUG") or self._is_local:
|
|
97
110
|
logger.setLevel(logging.DEBUG)
|
|
98
111
|
else:
|
|
99
112
|
logger.setLevel(logging.INFO)
|
|
100
113
|
|
|
101
114
|
return logger
|
|
102
115
|
|
|
116
|
+
def use_user_token(self, id: str, token: str) -> None:
|
|
117
|
+
headers = {"Content-Type": "application/json"}
|
|
118
|
+
|
|
119
|
+
query = """
|
|
120
|
+
mutation requestApiKey($id: ID!, $secret: String!){
|
|
121
|
+
apiKeyUser(id: $id, secret: $secret){
|
|
122
|
+
jwt
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
data: Dict[str, Any] = {
|
|
128
|
+
"query": query,
|
|
129
|
+
"variables": {"id": id, "secret": token},
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
req = urllib.request.Request(
|
|
133
|
+
self._api_endpoint, # type: ignore[arg-type]
|
|
134
|
+
json.dumps(data).encode("utf-8"),
|
|
135
|
+
headers,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
self.logger.debug(f"Sending request to url: {self._api_endpoint}")
|
|
139
|
+
try:
|
|
140
|
+
with urllib.request.urlopen(req) as response:
|
|
141
|
+
resp: Dict[str, Any] = json.loads(response.read().decode("utf-8"))
|
|
142
|
+
except urllib.error.HTTPError as e:
|
|
143
|
+
if hasattr(e, "read"):
|
|
144
|
+
resp = json.loads(e.read().decode("utf-8"))
|
|
145
|
+
else:
|
|
146
|
+
# We should not get here, but if we do re-raise the exception
|
|
147
|
+
self.logger.error(f"HTTP error occurred: ({e.code}) {e.reason} {e.msg}")
|
|
148
|
+
raise e
|
|
149
|
+
|
|
150
|
+
if "errors" in resp:
|
|
151
|
+
self.logger.error(f"Error: {resp['errors']}")
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
if (
|
|
155
|
+
"data" in resp
|
|
156
|
+
and "apiKeyUser" in resp["data"]
|
|
157
|
+
and "jwt" in resp["data"]["apiKeyUser"]
|
|
158
|
+
):
|
|
159
|
+
self._api_token = resp["data"]["apiKeyUser"]["jwt"]
|
|
160
|
+
self._api_enabled = True
|
|
161
|
+
self.logger.debug("Successfully set user token for API calls.")
|
|
162
|
+
else:
|
|
163
|
+
self.logger.error(f"API call returned no data: {resp}")
|
|
164
|
+
|
|
103
165
|
def get_available_hooks(self) -> List[str]:
|
|
104
166
|
"""
|
|
105
167
|
Get list of hook methods available in this plugin.
|
|
@@ -136,9 +198,7 @@ class SpaceforgePlugin(ABC):
|
|
|
136
198
|
Run a CLI command with the given arguments.
|
|
137
199
|
|
|
138
200
|
Args:
|
|
139
|
-
command: The command to run
|
|
140
|
-
*args: Positional arguments for the command
|
|
141
|
-
**kwargs: Keyword arguments for the command
|
|
201
|
+
*command: The command to run
|
|
142
202
|
expect_code: Expected return code
|
|
143
203
|
print_output: Whether to print the output to the logger
|
|
144
204
|
"""
|
|
@@ -175,7 +235,7 @@ class SpaceforgePlugin(ABC):
|
|
|
175
235
|
) -> Dict[str, Any]:
|
|
176
236
|
if not self._api_enabled:
|
|
177
237
|
self.logger.error(
|
|
178
|
-
'API is not enabled, please export "SPACELIFT_API_TOKEN" and "
|
|
238
|
+
'API is not enabled, please export "SPACELIFT_API_TOKEN" and "TF_VAR_spacelift_graphql_endpoint".'
|
|
179
239
|
)
|
|
180
240
|
exit(1)
|
|
181
241
|
|
|
@@ -192,12 +252,22 @@ class SpaceforgePlugin(ABC):
|
|
|
192
252
|
data["variables"] = variables
|
|
193
253
|
|
|
194
254
|
req = urllib.request.Request(
|
|
195
|
-
|
|
255
|
+
self._api_endpoint, # type: ignore[arg-type]
|
|
196
256
|
json.dumps(data).encode("utf-8"),
|
|
197
257
|
headers,
|
|
198
258
|
)
|
|
199
|
-
|
|
200
|
-
|
|
259
|
+
|
|
260
|
+
self.logger.debug(f"Sending request to url: {self._api_endpoint}")
|
|
261
|
+
try:
|
|
262
|
+
with urllib.request.urlopen(req) as response:
|
|
263
|
+
resp: Dict[str, Any] = json.loads(response.read().decode("utf-8"))
|
|
264
|
+
except urllib.error.HTTPError as e:
|
|
265
|
+
if hasattr(e, "read"):
|
|
266
|
+
resp = json.loads(e.read().decode("utf-8"))
|
|
267
|
+
else:
|
|
268
|
+
# We should not get here, but if we do re-raise the exception
|
|
269
|
+
self.logger.error(f"HTTP error occurred: ({e.code}) {e.reason} {e.msg}")
|
|
270
|
+
raise e
|
|
201
271
|
|
|
202
272
|
if "errors" in resp:
|
|
203
273
|
self.logger.error(f"Error: {resp['errors']}")
|
|
@@ -225,9 +295,116 @@ class SpaceforgePlugin(ABC):
|
|
|
225
295
|
data: Dict[str, Any] = json.load(f)
|
|
226
296
|
return data
|
|
227
297
|
|
|
228
|
-
def send_markdown(self, markdown: str) ->
|
|
229
|
-
|
|
230
|
-
|
|
298
|
+
def send_markdown(self, markdown: str) -> bool:
|
|
299
|
+
"""
|
|
300
|
+
Send a markdown message to the Spacelift run.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
markdown: The markdown content to send
|
|
304
|
+
"""
|
|
305
|
+
if self._is_local:
|
|
306
|
+
self.logger.info(
|
|
307
|
+
"Spacelift run is local. Not uploading markdown. Below is a preview of what would be sent"
|
|
308
|
+
)
|
|
309
|
+
self.logger.info(markdown)
|
|
310
|
+
return True
|
|
311
|
+
|
|
312
|
+
if (
|
|
313
|
+
self._spacelift_markdown_endpoint is None
|
|
314
|
+
or not self._markdown_endpoint_token
|
|
315
|
+
):
|
|
316
|
+
self.logger.error(
|
|
317
|
+
'API is not enabled, please export "SPACELIFT_API_TOKEN" and "TF_VAR_spacelift_graphql_endpoint".'
|
|
318
|
+
)
|
|
319
|
+
return False
|
|
320
|
+
|
|
321
|
+
headers = {"Authorization": f"Bearer {self._markdown_endpoint_token}"}
|
|
322
|
+
body = {
|
|
323
|
+
"plugin_name": self.__plugin_name__,
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
# First we get the signed url for uploading
|
|
327
|
+
req = urllib.request.Request(
|
|
328
|
+
self._spacelift_markdown_endpoint,
|
|
329
|
+
json.dumps(body).encode("utf-8"),
|
|
330
|
+
headers,
|
|
331
|
+
method="POST",
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
self.logger.debug(
|
|
335
|
+
f"Sending request to url: {self._spacelift_markdown_endpoint}"
|
|
336
|
+
)
|
|
337
|
+
try:
|
|
338
|
+
with urllib.request.urlopen(req) as response:
|
|
339
|
+
if response.status != 200:
|
|
340
|
+
self.logger.error(
|
|
341
|
+
f"Error getting signed URL for markdown upload: {response}"
|
|
342
|
+
)
|
|
343
|
+
return False
|
|
344
|
+
|
|
345
|
+
raw_response = response.read().decode("utf-8")
|
|
346
|
+
self.logger.debug(raw_response)
|
|
347
|
+
resp: Dict[str, Any] = json.loads(raw_response)
|
|
348
|
+
if "url" not in resp or "headers" not in resp:
|
|
349
|
+
self.logger.error(
|
|
350
|
+
"Markdown signed url response does not contain 'url' or 'headers' key."
|
|
351
|
+
)
|
|
352
|
+
return False
|
|
353
|
+
|
|
354
|
+
signed_url = resp["url"]
|
|
355
|
+
headers = resp["headers"]
|
|
356
|
+
headers["Content-Type"] = "text/markdown"
|
|
357
|
+
headers["Content-Length"] = str(len(markdown.encode("utf-8")))
|
|
358
|
+
except HTTPError as e:
|
|
359
|
+
self.logger.error(f"HTTP error occurred: ({e.code}) {e.reason} {e.msg}")
|
|
360
|
+
return False
|
|
361
|
+
|
|
362
|
+
self.logger.debug("Markdown to send" + markdown)
|
|
363
|
+
|
|
364
|
+
# Now we upload the markdown content to the signed URL
|
|
365
|
+
req = urllib.request.Request(
|
|
366
|
+
signed_url,
|
|
367
|
+
data=markdown.encode("utf-8"),
|
|
368
|
+
headers=headers,
|
|
369
|
+
method="PUT",
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
self.logger.debug(f"Sending request to url: {signed_url}")
|
|
373
|
+
try:
|
|
374
|
+
with urllib.request.urlopen(req) as put_response:
|
|
375
|
+
if put_response.status != 200:
|
|
376
|
+
self.logger.error(
|
|
377
|
+
f"Error uploading markdown content: {put_response.status}"
|
|
378
|
+
)
|
|
379
|
+
return False
|
|
380
|
+
self.logger.debug("Markdown content uploaded successfully.")
|
|
381
|
+
except HTTPError as e:
|
|
382
|
+
self.logger.error(
|
|
383
|
+
f"HTTP error occurred during upload: ({e.code}) {e.reason} {e.msg}"
|
|
384
|
+
)
|
|
385
|
+
return False
|
|
386
|
+
|
|
387
|
+
return True
|
|
388
|
+
|
|
389
|
+
def add_to_policy_input(self, input_name: str, data: Dict[str, Any]) -> None:
|
|
390
|
+
"""
|
|
391
|
+
Add data to the policy input for the current Spacelift run.
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
input_name: The name of the input to add (will be available as input.third_party_metadata.custom.{input_name} to the policy).
|
|
395
|
+
data: Dictionary containing data to add to the policy input
|
|
396
|
+
"""
|
|
397
|
+
if self._is_local:
|
|
398
|
+
self.logger.info(
|
|
399
|
+
"Spacelift run is local. Not writing custom policy input. Below is a preview of what would be written"
|
|
400
|
+
)
|
|
401
|
+
self.logger.info(json.dumps(data, indent=2))
|
|
402
|
+
return
|
|
403
|
+
|
|
404
|
+
with open(
|
|
405
|
+
f"{self._workspace_root}/{input_name}.custom.spacelift.json", "w"
|
|
406
|
+
) as f:
|
|
407
|
+
f.write(json.dumps(data))
|
|
231
408
|
|
|
232
409
|
# Hook methods - override these in your plugin
|
|
233
410
|
def before_init(self) -> None:
|
spaceforge/runner.py
CHANGED
|
@@ -4,7 +4,6 @@ Plugin runner for executing hook methods.
|
|
|
4
4
|
|
|
5
5
|
import importlib.util
|
|
6
6
|
import os
|
|
7
|
-
import sys
|
|
8
7
|
from typing import Optional
|
|
9
8
|
|
|
10
9
|
|
|
@@ -102,14 +101,3 @@ def runner_command(hook_name: str, plugin_file: str) -> None:
|
|
|
102
101
|
"""
|
|
103
102
|
runner = PluginRunner(plugin_file)
|
|
104
103
|
runner.run_hook(hook_name)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def main() -> None:
|
|
108
|
-
"""Legacy main entry point for backward compatibility."""
|
|
109
|
-
if len(sys.argv) != 2:
|
|
110
|
-
print("Usage: python -m spaceforge.runner <hook_name>")
|
|
111
|
-
sys.exit(1)
|
|
112
|
-
|
|
113
|
-
hook_name = sys.argv[1]
|
|
114
|
-
runner = PluginRunner()
|
|
115
|
-
runner.run_hook(hook_name)
|
spaceforge/schema.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$defs": {
|
|
3
3
|
"Context": {
|
|
4
|
-
"description": "A class to represent a context for a plugin.\n\nAttributes:\n name_prefix (str): The name of the context, will be appended with a unique ID.\n description (str): A description of the context.\n labels (
|
|
4
|
+
"description": "A class to represent a context for a plugin.\n\nAttributes:\n name_prefix (str): The name of the context, will be appended with a unique ID.\n description (str): A description of the context.\n labels (Optional[List[str]]): Labels associated with the context.\n env (list): List of variables associated with the context.\n hooks (dict): Hooks associated with the context.",
|
|
5
5
|
"properties": {
|
|
6
6
|
"name_prefix": {
|
|
7
7
|
"title": "Name Prefix",
|
|
@@ -74,10 +74,10 @@
|
|
|
74
74
|
"labels": {
|
|
75
75
|
"anyOf": [
|
|
76
76
|
{
|
|
77
|
-
"
|
|
77
|
+
"items": {
|
|
78
78
|
"type": "string"
|
|
79
79
|
},
|
|
80
|
-
"type": "
|
|
80
|
+
"type": "array"
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
"type": "null"
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
"type": "object"
|
|
119
119
|
},
|
|
120
120
|
"Parameter": {
|
|
121
|
-
"description": "A class to represent a parameter with a name and value.\n\nAttributes:\n name (str): The name of the parameter.\n description (str): A description of the parameter.\n sensitive (bool): Whether the parameter contains sensitive information.\n required (bool): Whether the parameter is required.\n default (Optional[str]): The default value of the parameter, if any. (required if sensitive is False)",
|
|
121
|
+
"description": "A class to represent a parameter with a name and value.\n\nAttributes:\n name (str): The name of the parameter.\n description (str): A description of the parameter.\n sensitive (bool): Whether the parameter contains sensitive information.\n required (bool): Whether the parameter is required.\n default (Optional[str]): The default value of the parameter, if any. (required if sensitive is False)\n id (str): Unique identifier for the parameter.",
|
|
122
122
|
"properties": {
|
|
123
123
|
"name": {
|
|
124
124
|
"title": "Name",
|
|
@@ -149,6 +149,10 @@
|
|
|
149
149
|
],
|
|
150
150
|
"default": null,
|
|
151
151
|
"title": "Default"
|
|
152
|
+
},
|
|
153
|
+
"id": {
|
|
154
|
+
"title": "Id",
|
|
155
|
+
"type": "string"
|
|
152
156
|
}
|
|
153
157
|
},
|
|
154
158
|
"required": [
|
|
@@ -159,13 +163,20 @@
|
|
|
159
163
|
"type": "object"
|
|
160
164
|
},
|
|
161
165
|
"Policy": {
|
|
162
|
-
"description": "A class to represent a policy configuration.\n\nAttributes:\n name_prefix (str): The name of the policy, will be appended with a unique ID.\n type (str): The type of the policy (e.g., \"terraform\", \"kubernetes\").\n body (str): The body of the policy, typically a configuration or script.\n labels (Optional[
|
|
166
|
+
"description": "A class to represent a policy configuration.\n\nAttributes:\n name_prefix (str): The name of the policy, will be appended with a unique ID.\n type (str): The type of the policy (e.g., \"terraform\", \"kubernetes\").\n body (str): The body of the policy, typically a configuration or script.\n labels (Optional[List[str]]): Labels associated with the policy.",
|
|
163
167
|
"properties": {
|
|
164
168
|
"name_prefix": {
|
|
165
169
|
"title": "Name Prefix",
|
|
166
170
|
"type": "string"
|
|
167
171
|
},
|
|
168
172
|
"type": {
|
|
173
|
+
"enum": [
|
|
174
|
+
"PUSH",
|
|
175
|
+
"PLAN",
|
|
176
|
+
"TRIGGER",
|
|
177
|
+
"APPROVAL",
|
|
178
|
+
"NOTIFICATION"
|
|
179
|
+
],
|
|
169
180
|
"title": "Type",
|
|
170
181
|
"type": "string"
|
|
171
182
|
},
|
|
@@ -176,10 +187,10 @@
|
|
|
176
187
|
"labels": {
|
|
177
188
|
"anyOf": [
|
|
178
189
|
{
|
|
179
|
-
"
|
|
190
|
+
"items": {
|
|
180
191
|
"type": "string"
|
|
181
192
|
},
|
|
182
|
-
"type": "
|
|
193
|
+
"type": "array"
|
|
183
194
|
},
|
|
184
195
|
{
|
|
185
196
|
"type": "null"
|
|
@@ -238,7 +249,7 @@
|
|
|
238
249
|
"type": "object"
|
|
239
250
|
},
|
|
240
251
|
"Webhook": {
|
|
241
|
-
"description": "A class to represent a webhook configuration.\n\nAttributes:\n name_prefix (str): The name of the webhook, will be appended with a unique ID.\n endpoint (str): The URL endpoint for the webhook.\n labels (Optional[
|
|
252
|
+
"description": "A class to represent a webhook configuration.\n\nAttributes:\n name_prefix (str): The name of the webhook, will be appended with a unique ID.\n endpoint (str): The URL endpoint for the webhook.\n labels (Optional[List[str]]): Labels associated with the webhook.\n secret (str): the ID of the parameter where the webhook secret is retrieved from",
|
|
242
253
|
"properties": {
|
|
243
254
|
"name_prefix": {
|
|
244
255
|
"title": "Name Prefix",
|
|
@@ -248,25 +259,22 @@
|
|
|
248
259
|
"title": "Endpoint",
|
|
249
260
|
"type": "string"
|
|
250
261
|
},
|
|
251
|
-
"
|
|
262
|
+
"secretFromParameter": {
|
|
252
263
|
"anyOf": [
|
|
253
264
|
{
|
|
254
|
-
"
|
|
255
|
-
"type": "string"
|
|
256
|
-
},
|
|
257
|
-
"type": "object"
|
|
265
|
+
"type": "string"
|
|
258
266
|
},
|
|
259
267
|
{
|
|
260
268
|
"type": "null"
|
|
261
269
|
}
|
|
262
270
|
],
|
|
263
|
-
"title": "
|
|
271
|
+
"title": "Secretfromparameter"
|
|
264
272
|
},
|
|
265
|
-
"
|
|
273
|
+
"labels": {
|
|
266
274
|
"anyOf": [
|
|
267
275
|
{
|
|
268
276
|
"items": {
|
|
269
|
-
"
|
|
277
|
+
"type": "string"
|
|
270
278
|
},
|
|
271
279
|
"type": "array"
|
|
272
280
|
},
|
|
@@ -274,7 +282,7 @@
|
|
|
274
282
|
"type": "null"
|
|
275
283
|
}
|
|
276
284
|
],
|
|
277
|
-
"title": "
|
|
285
|
+
"title": "Labels"
|
|
278
286
|
}
|
|
279
287
|
},
|
|
280
288
|
"required": [
|
|
@@ -285,10 +293,10 @@
|
|
|
285
293
|
"type": "object"
|
|
286
294
|
}
|
|
287
295
|
},
|
|
288
|
-
"description": "A class to represent the manifest of a Spacelift plugin.\n\nAttributes:\n
|
|
296
|
+
"description": "A class to represent the manifest of a Spacelift plugin.\n\nAttributes:\n name (str): The name of the plugin, will be appended with a unique ID.\n description (str): A description of the plugin.\n author (str): The author of the plugin.\n labels (list[str]): List of labels for the plugin.\n parameters (list[Parameter]): List of parameters for the plugin.\n contexts (list[Context]): List of contexts for the plugin.\n webhooks (list[Webhook]): List of webhooks for the plugin.\n policies (list[Policy]): List of policies for the plugin.",
|
|
289
297
|
"properties": {
|
|
290
|
-
"
|
|
291
|
-
"title": "Name
|
|
298
|
+
"name": {
|
|
299
|
+
"title": "Name",
|
|
292
300
|
"type": "string"
|
|
293
301
|
},
|
|
294
302
|
"version": {
|
|
@@ -303,6 +311,20 @@
|
|
|
303
311
|
"title": "Author",
|
|
304
312
|
"type": "string"
|
|
305
313
|
},
|
|
314
|
+
"labels": {
|
|
315
|
+
"anyOf": [
|
|
316
|
+
{
|
|
317
|
+
"items": {
|
|
318
|
+
"type": "string"
|
|
319
|
+
},
|
|
320
|
+
"type": "array"
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
"type": "null"
|
|
324
|
+
}
|
|
325
|
+
],
|
|
326
|
+
"title": "Labels"
|
|
327
|
+
},
|
|
306
328
|
"parameters": {
|
|
307
329
|
"anyOf": [
|
|
308
330
|
{
|
|
@@ -361,11 +383,12 @@
|
|
|
361
383
|
}
|
|
362
384
|
},
|
|
363
385
|
"required": [
|
|
364
|
-
"
|
|
386
|
+
"name",
|
|
365
387
|
"version",
|
|
366
388
|
"description",
|
|
367
389
|
"author"
|
|
368
390
|
],
|
|
369
391
|
"title": "PluginManifest",
|
|
370
|
-
"type": "object"
|
|
392
|
+
"type": "object",
|
|
393
|
+
"$schema": "http://json-schema.org/draft-07/schema#"
|
|
371
394
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
export PATH="/mnt/workspace/plugins/plugin_binaries:$PATH"
|
|
6
|
+
if command -v {{binary.name}}; then
|
|
7
|
+
echo "{{binary.name}} is already installed."
|
|
8
|
+
return
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
mkdir -p {{static_binary_directory}}
|
|
12
|
+
|
|
13
|
+
echo "Installing {{binary.name}}..."
|
|
14
|
+
mkdir -p {{static_binary_directory}}
|
|
15
|
+
cd {{static_binary_directory}}
|
|
16
|
+
|
|
17
|
+
if [ "$(arch)" = "x86_64" ]; then
|
|
18
|
+
curl {{amd64_url}} -o {{binary_path}} -L
|
|
19
|
+
else
|
|
20
|
+
curl {{arm64_url}} -o {{binary_path}} -L
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
chmod +x {{binary_path}}
|
|
24
|
+
cd /mnt/workspace/source/$TF_VAR_spacelift_project_root
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
cd {{plugin_path}}
|
|
6
|
+
|
|
7
|
+
if [ ! -d "./venv" ]; then
|
|
8
|
+
python -m venv ./venv
|
|
9
|
+
fi
|
|
10
|
+
. venv/bin/activate
|
|
11
|
+
|
|
12
|
+
if ! command -v spaceforge; then
|
|
13
|
+
pip install spaceforge
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
if [ -f requirements.txt ] && [ ! -f .spaceforge_installed_requirements ]; then
|
|
17
|
+
pip install -r requirements.txt
|
|
18
|
+
touch .spaceforge_installed_requirements
|
|
19
|
+
fi
|
|
20
|
+
{% if has_binaries %}
|
|
21
|
+
export PATH="/mnt/workspace/plugins/plugin_binaries:$PATH"
|
|
22
|
+
{% endif %}
|
|
23
|
+
cd /mnt/workspace/source/$TF_VAR_spacelift_project_root
|
|
24
|
+
spaceforge runner --plugin-file {{plugin_file}} {{phase}}
|