spaceforge 0.0.3__tar.gz → 0.0.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {spaceforge-0.0.3 → spaceforge-0.0.5}/PKG-INFO +11 -1
- {spaceforge-0.0.3 → spaceforge-0.0.5}/README.md +9 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/plugins/sops/plugin.yaml +53 -4
- {spaceforge-0.0.3 → spaceforge-0.0.5}/plugins/wiz/plugin.py +6 -4
- {spaceforge-0.0.3 → spaceforge-0.0.5}/plugins/wiz/plugin.yaml +60 -8
- {spaceforge-0.0.3 → spaceforge-0.0.5}/pyproject.toml +1 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/setup.py +1 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/_version_scm.py +3 -3
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/cls.py +8 -8
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/generator.py +53 -40
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/plugin.py +29 -23
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/schema.json +15 -24
- spaceforge-0.0.5/spaceforge/templates/binary_install.sh.j2 +23 -0
- spaceforge-0.0.5/spaceforge/templates/ensure_spaceforge_and_run.sh.j2 +22 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/test_generator.py +159 -58
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/test_generator_binaries.py +40 -13
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge.egg-info/PKG-INFO +11 -1
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge.egg-info/SOURCES.txt +3 -1
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge.egg-info/requires.txt +1 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/test.sh +1 -1
- {spaceforge-0.0.3 → spaceforge-0.0.5}/.github/workflows/ci.yml +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/.github/workflows/release.yml +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/.gitignore +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/LICENSE +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/MANIFEST.in +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/go.mod +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/plugins/infracost/plugin.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/plugins/infracost/plugin.yaml +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/plugins/sops/plugin.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/plugins/sops/requirements.txt +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/setup.cfg +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/README.md +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/__init__.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/__main__.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/_version.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/conftest.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/runner.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/test_cls.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/test_generator_core.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/test_generator_hooks.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/test_generator_parameters.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/test_plugin.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/test_plugin_file_operations.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/test_plugin_hooks.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/test_plugin_inheritance.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/test_runner.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/test_runner_cli.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/test_runner_core.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge/test_runner_execution.py +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge.egg-info/dependency_links.txt +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge.egg-info/entry_points.txt +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge.egg-info/not-zip-safe +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/spaceforge.egg-info/top_level.txt +0 -0
- {spaceforge-0.0.3 → spaceforge-0.0.5}/templates.go +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spaceforge
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: A Python framework for building Spacelift plugins
|
|
5
5
|
Home-page: https://github.com/spacelift-io/plugins
|
|
6
6
|
Author: Spacelift
|
|
@@ -28,6 +28,7 @@ License-File: LICENSE
|
|
|
28
28
|
Requires-Dist: PyYAML>=6.0
|
|
29
29
|
Requires-Dist: click>=8.0.0
|
|
30
30
|
Requires-Dist: pydantic>=2.11.7
|
|
31
|
+
Requires-Dist: Jinja2>=3.1.0
|
|
31
32
|
Provides-Extra: dev
|
|
32
33
|
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
33
34
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
@@ -584,6 +585,15 @@ export SPACEFORGE_PARAM_SEVERITY_THRESHOLD="high"
|
|
|
584
585
|
spaceforge runner after_plan
|
|
585
586
|
```
|
|
586
587
|
|
|
588
|
+
## Speeding up plugin execution
|
|
589
|
+
|
|
590
|
+
There are a few things you can do to speed up plugin execution.
|
|
591
|
+
|
|
592
|
+
1. Ensure your runner has `spaceforge` preinstalled. This will avoid the overhead of installing it during the run. (15-30 seconds)
|
|
593
|
+
2. If youre using binaries, we will only install the binary if its not found. You can gain a few seconds by ensuring its already on the runner.
|
|
594
|
+
3. If your plugin has a lot of dependencies, consider using a prebuilt runner image with your plugin and its dependencies installed. This avoids the overhead of installing them during each run.
|
|
595
|
+
4. Ensure your runner has enough core resources (CPU, memory) to handle the plugin execution efficiently. If your plugin is resource-intensive, consider using a more powerful runner.
|
|
596
|
+
|
|
587
597
|
## Next Steps
|
|
588
598
|
|
|
589
599
|
1. **Install spaceforge:** `pip install spaceforge`
|
|
@@ -540,6 +540,15 @@ export SPACEFORGE_PARAM_SEVERITY_THRESHOLD="high"
|
|
|
540
540
|
spaceforge runner after_plan
|
|
541
541
|
```
|
|
542
542
|
|
|
543
|
+
## Speeding up plugin execution
|
|
544
|
+
|
|
545
|
+
There are a few things you can do to speed up plugin execution.
|
|
546
|
+
|
|
547
|
+
1. Ensure your runner has `spaceforge` preinstalled. This will avoid the overhead of installing it during the run. (15-30 seconds)
|
|
548
|
+
2. If youre using binaries, we will only install the binary if its not found. You can gain a few seconds by ensuring its already on the runner.
|
|
549
|
+
3. If your plugin has a lot of dependencies, consider using a prebuilt runner image with your plugin and its dependencies installed. This avoids the overhead of installing them during each run.
|
|
550
|
+
4. Ensure your runner has enough core resources (CPU, memory) to handle the plugin execution efficiently. If your plugin is resource-intensive, consider using a more powerful runner.
|
|
551
|
+
|
|
543
552
|
## Next Steps
|
|
544
553
|
|
|
545
554
|
1. **Install spaceforge:** `pip install spaceforge`
|
|
@@ -131,10 +131,59 @@ contexts:
|
|
|
131
131
|
except Exception as e:
|
|
132
132
|
self.logger.error(f"An unexpected error occurred: {e}")
|
|
133
133
|
sensitive: false
|
|
134
|
+
- path: /mnt/workspace/plugins/sops/binary_install_sops.sh
|
|
135
|
+
content: |-
|
|
136
|
+
#!/bin/sh
|
|
137
|
+
|
|
138
|
+
set -e
|
|
139
|
+
|
|
140
|
+
if command -v sops; then
|
|
141
|
+
echo "sops is already installed."
|
|
142
|
+
return
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
mkdir -p /mnt/workspace/plugins/plugin_binaries
|
|
146
|
+
|
|
147
|
+
echo "Installing sops..."
|
|
148
|
+
mkdir -p /mnt/workspace/plugins/plugin_binaries
|
|
149
|
+
cd /mnt/workspace/plugins/plugin_binaries
|
|
150
|
+
|
|
151
|
+
if [ "$(arch)" = "x86_64" ]; then
|
|
152
|
+
curl https://github.com/getsops/sops/releases/download/v3.9.1/sops-v3.9.1.linux.amd64 -o /mnt/workspace/plugins/plugin_binaries/sops -L
|
|
153
|
+
else
|
|
154
|
+
curl https://github.com/getsops/sops/releases/download/v3.9.1/sops-v3.9.1.linux.arm64 -o /mnt/workspace/plugins/plugin_binaries/sops -L
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
chmod +x /mnt/workspace/plugins/plugin_binaries/sops
|
|
158
|
+
cd /mnt/workspace/source/$TF_VAR_spacelift_project_root
|
|
159
|
+
sensitive: false
|
|
160
|
+
- path: /mnt/workspace/plugins/sops/before_init.sh
|
|
161
|
+
content: |-
|
|
162
|
+
#!/bin/sh
|
|
163
|
+
|
|
164
|
+
set -e
|
|
165
|
+
|
|
166
|
+
cd /mnt/workspace/plugins/sops
|
|
167
|
+
|
|
168
|
+
if [ ! -d "./venv" ]; then
|
|
169
|
+
python -m venv ./venv
|
|
170
|
+
fi
|
|
171
|
+
. venv/bin/activate
|
|
172
|
+
|
|
173
|
+
if ! command -v spaceforge; then
|
|
174
|
+
pip install spaceforge
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
if [ -f requirements.txt ] && [ ! -f .spaceforge_installed_requirements ]; then
|
|
178
|
+
pip install -r requirements.txt
|
|
179
|
+
touch .spaceforge_installed_requirements
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
cd /mnt/workspace/source/$TF_VAR_spacelift_project_root
|
|
183
|
+
spaceforge runner --plugin-file /mnt/workspace/plugins/sops/plugin.py before_init
|
|
184
|
+
sensitive: false
|
|
134
185
|
hooks:
|
|
135
186
|
before_init:
|
|
136
187
|
- mkdir -p /mnt/workspace/plugins/sops
|
|
137
|
-
-
|
|
138
|
-
-
|
|
139
|
-
- mkdir -p /mnt/workspace/plugins/plugin_binaries && cd /mnt/workspace/plugins/plugin_binaries && ([[ "$(echo "$(arch)")" == "x86_64" ]] && curl https://github.com/getsops/sops/releases/download/v3.9.1/sops-v3.9.1.linux.amd64 -o /mnt/workspace/plugins/plugin_binaries/sops -L && chmod +x /mnt/workspace/plugins/plugin_binaries/sops || curl https://github.com/getsops/sops/releases/download/v3.9.1/sops-v3.9.1.linux.arm64 -o /mnt/workspace/plugins/plugin_binaries/sops -L && chmod +x /mnt/workspace/plugins/plugin_binaries/sops) && cd /mnt/workspace/source/$TF_VAR_spacelift_project_root
|
|
140
|
-
- cd /mnt/workspace/source/$TF_VAR_spacelift_project_root && python -m spaceforge runner --plugin-file /mnt/workspace/plugins/sops/plugin.py before_init
|
|
188
|
+
- chmod +x /mnt/workspace/plugins/sops/binary_install_sops.sh && /mnt/workspace/plugins/sops/binary_install_sops.sh
|
|
189
|
+
- chmod +x /mnt/workspace/plugins/sops/before_init.sh && /mnt/workspace/plugins/sops/before_init.sh
|
|
@@ -88,9 +88,9 @@ webhook[{"endpoint_id": "wiz-alert-endpoint"}] {
|
|
|
88
88
|
input.run_updated.run.marked_unsafe == true
|
|
89
89
|
}
|
|
90
90
|
""",
|
|
91
|
-
labels=
|
|
92
|
-
"
|
|
93
|
-
|
|
91
|
+
labels=[
|
|
92
|
+
"wiz-plugin"
|
|
93
|
+
]
|
|
94
94
|
)
|
|
95
95
|
]
|
|
96
96
|
|
|
@@ -179,4 +179,6 @@ webhook[{"endpoint_id": "wiz-alert-endpoint"}] {
|
|
|
179
179
|
markdown += "\n"
|
|
180
180
|
if "reportUrl" in stdout_json:
|
|
181
181
|
markdown += f"<a href=\"{stdout_json['reportUrl']}\" rel=\"noopener noreferrer\">View Report</a>\n"
|
|
182
|
-
self.send_markdown(markdown)
|
|
182
|
+
result = self.send_markdown(markdown)
|
|
183
|
+
if not result:
|
|
184
|
+
self.logger.error("Failed to send Wiz CLI output to spacelift")
|
|
@@ -134,9 +134,9 @@ contexts:
|
|
|
134
134
|
input.run_updated.run.marked_unsafe == true
|
|
135
135
|
}
|
|
136
136
|
""",
|
|
137
|
-
labels=
|
|
138
|
-
"
|
|
139
|
-
|
|
137
|
+
labels=[
|
|
138
|
+
"wiz-plugin"
|
|
139
|
+
]
|
|
140
140
|
)
|
|
141
141
|
]
|
|
142
142
|
|
|
@@ -225,15 +225,67 @@ contexts:
|
|
|
225
225
|
markdown += "\n"
|
|
226
226
|
if "reportUrl" in stdout_json:
|
|
227
227
|
markdown += f"<a href=\"{stdout_json['reportUrl']}\" rel=\"noopener noreferrer\">View Report</a>\n"
|
|
228
|
-
self.send_markdown(markdown)
|
|
228
|
+
result = self.send_markdown(markdown)
|
|
229
|
+
if not result:
|
|
230
|
+
self.logger.error("Failed to send Wiz CLI output to spacelift")
|
|
231
|
+
sensitive: false
|
|
232
|
+
- path: /mnt/workspace/plugins/wiz/binary_install_wizcli.sh
|
|
233
|
+
content: |-
|
|
234
|
+
#!/bin/sh
|
|
235
|
+
|
|
236
|
+
set -e
|
|
237
|
+
|
|
238
|
+
if command -v wizcli; then
|
|
239
|
+
echo "wizcli is already installed."
|
|
240
|
+
return
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
mkdir -p /mnt/workspace/plugins/plugin_binaries
|
|
244
|
+
|
|
245
|
+
echo "Installing wizcli..."
|
|
246
|
+
mkdir -p /mnt/workspace/plugins/plugin_binaries
|
|
247
|
+
cd /mnt/workspace/plugins/plugin_binaries
|
|
248
|
+
|
|
249
|
+
if [ "$(arch)" = "x86_64" ]; then
|
|
250
|
+
curl https://downloads.wiz.io/wizcli/0.94.0/wizcli-linux-amd64 -o /mnt/workspace/plugins/plugin_binaries/wizcli -L
|
|
251
|
+
else
|
|
252
|
+
curl https://downloads.wiz.io/wizcli/0.94.0/wizcli-linux-arm64 -o /mnt/workspace/plugins/plugin_binaries/wizcli -L
|
|
253
|
+
fi
|
|
254
|
+
|
|
255
|
+
chmod +x /mnt/workspace/plugins/plugin_binaries/wizcli
|
|
256
|
+
cd /mnt/workspace/source/$TF_VAR_spacelift_project_root
|
|
257
|
+
sensitive: false
|
|
258
|
+
- path: /mnt/workspace/plugins/wiz/before_plan.sh
|
|
259
|
+
content: |-
|
|
260
|
+
#!/bin/sh
|
|
261
|
+
|
|
262
|
+
set -e
|
|
263
|
+
|
|
264
|
+
cd /mnt/workspace/plugins/wiz
|
|
265
|
+
|
|
266
|
+
if [ ! -d "./venv" ]; then
|
|
267
|
+
python -m venv ./venv
|
|
268
|
+
fi
|
|
269
|
+
. venv/bin/activate
|
|
270
|
+
|
|
271
|
+
if ! command -v spaceforge; then
|
|
272
|
+
pip install spaceforge
|
|
273
|
+
fi
|
|
274
|
+
|
|
275
|
+
if [ -f requirements.txt ] && [ ! -f .spaceforge_installed_requirements ]; then
|
|
276
|
+
pip install -r requirements.txt
|
|
277
|
+
touch .spaceforge_installed_requirements
|
|
278
|
+
fi
|
|
279
|
+
|
|
280
|
+
cd /mnt/workspace/source/$TF_VAR_spacelift_project_root
|
|
281
|
+
spaceforge runner --plugin-file /mnt/workspace/plugins/wiz/plugin.py before_plan
|
|
229
282
|
sensitive: false
|
|
230
283
|
hooks:
|
|
231
284
|
before_init:
|
|
232
285
|
- mkdir -p /mnt/workspace/plugins/wiz
|
|
233
|
-
-
|
|
286
|
+
- chmod +x /mnt/workspace/plugins/wiz/binary_install_wizcli.sh && /mnt/workspace/plugins/wiz/binary_install_wizcli.sh
|
|
234
287
|
before_plan:
|
|
235
|
-
-
|
|
236
|
-
- cd /mnt/workspace/source/$TF_VAR_spacelift_project_root && python -m spaceforge runner --plugin-file /mnt/workspace/plugins/wiz/plugin.py before_plan
|
|
288
|
+
- chmod +x /mnt/workspace/plugins/wiz/before_plan.sh && /mnt/workspace/plugins/wiz/before_plan.sh
|
|
237
289
|
policies:
|
|
238
290
|
- name_prefix: wiz_policy
|
|
239
291
|
type: notification
|
|
@@ -245,4 +297,4 @@ policies:
|
|
|
245
297
|
input.run_updated.run.marked_unsafe == true
|
|
246
298
|
}
|
|
247
299
|
labels:
|
|
248
|
-
|
|
300
|
+
- wiz-plugin
|
|
@@ -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.5'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 0, 5)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'gd83c2c6ce'
|
|
@@ -118,7 +118,7 @@ class Context:
|
|
|
118
118
|
Attributes:
|
|
119
119
|
name_prefix (str): The name of the context, will be appended with a unique ID.
|
|
120
120
|
description (str): A description of the context.
|
|
121
|
-
labels (
|
|
121
|
+
labels (Optional[List[str]]): Labels associated with the context.
|
|
122
122
|
env (list): List of variables associated with the context.
|
|
123
123
|
hooks (dict): Hooks associated with the context.
|
|
124
124
|
"""
|
|
@@ -128,7 +128,7 @@ class Context:
|
|
|
128
128
|
env: Optional[List[Variable]] = optional_field
|
|
129
129
|
mounted_files: Optional[List[MountedFile]] = optional_field
|
|
130
130
|
hooks: Optional[Dict[HookType, List[str]]] = optional_field
|
|
131
|
-
labels: Optional[
|
|
131
|
+
labels: Optional[List[str]] = optional_field
|
|
132
132
|
|
|
133
133
|
|
|
134
134
|
@pydantic_dataclass
|
|
@@ -139,14 +139,14 @@ class Webhook:
|
|
|
139
139
|
Attributes:
|
|
140
140
|
name_prefix (str): The name of the webhook, will be appended with a unique ID.
|
|
141
141
|
endpoint (str): The URL endpoint for the webhook.
|
|
142
|
-
labels (Optional[
|
|
143
|
-
|
|
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
|
|
144
144
|
"""
|
|
145
145
|
|
|
146
146
|
name_prefix: str
|
|
147
147
|
endpoint: str
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
secretFromParameter: str
|
|
149
|
+
labels: Optional[List[str]] = optional_field
|
|
150
150
|
|
|
151
151
|
|
|
152
152
|
@pydantic_dataclass
|
|
@@ -158,13 +158,13 @@ class Policy:
|
|
|
158
158
|
name_prefix (str): The name of the policy, will be appended with a unique ID.
|
|
159
159
|
type (str): The type of the policy (e.g., "terraform", "kubernetes").
|
|
160
160
|
body (str): The body of the policy, typically a configuration or script.
|
|
161
|
-
labels (Optional[
|
|
161
|
+
labels (Optional[List[str]]): Labels associated with the policy.
|
|
162
162
|
"""
|
|
163
163
|
|
|
164
164
|
name_prefix: str
|
|
165
165
|
type: str
|
|
166
166
|
body: str
|
|
167
|
-
labels: Optional[
|
|
167
|
+
labels: Optional[List[str]] = optional_field
|
|
168
168
|
|
|
169
169
|
|
|
170
170
|
@pydantic_dataclass
|
|
@@ -7,6 +7,7 @@ import os
|
|
|
7
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
|
|
@@ -47,6 +48,9 @@ class PluginGenerator:
|
|
|
47
48
|
self.plugin_instance: Optional[SpaceforgePlugin] = None
|
|
48
49
|
self.plugin_working_directory: Optional[str] = None
|
|
49
50
|
self.config: Optional[Dict[str, Any]] = None
|
|
51
|
+
self.jinja = Environment(
|
|
52
|
+
loader=PackageLoader("spaceforge"), autoescape=select_autoescape()
|
|
53
|
+
)
|
|
50
54
|
|
|
51
55
|
def load_plugin(self) -> None:
|
|
52
56
|
"""Load the plugin class from the specified path."""
|
|
@@ -84,7 +88,7 @@ class PluginGenerator:
|
|
|
84
88
|
self.config = {
|
|
85
89
|
"setup_virtual_env": (
|
|
86
90
|
f"cd {self.plugin_working_directory} && python -m venv ./venv && "
|
|
87
|
-
+ "source venv/bin/activate && pip install spaceforge"
|
|
91
|
+
+ "source venv/bin/activate && (command -v spaceforge &> /dev/null || pip install spaceforge)"
|
|
88
92
|
),
|
|
89
93
|
"plugin_mounted_path": f"{self.plugin_working_directory}/{os.path.basename(self.plugin_path)}",
|
|
90
94
|
}
|
|
@@ -137,14 +141,27 @@ class PluginGenerator:
|
|
|
137
141
|
|
|
138
142
|
return hook_methods
|
|
139
143
|
|
|
140
|
-
def
|
|
141
|
-
self,
|
|
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,
|
|
142
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,
|
|
159
|
+
)
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
def _update_with_requirements(self, mounted_files: List[MountedFile]) -> None:
|
|
143
163
|
"""Update the plugin hooks if there is a requirements.txt"""
|
|
144
164
|
if os.path.exists("requirements.txt") and self.config is not None:
|
|
145
|
-
if self.config["setup_virtual_env"] not in hooks["before_init"]:
|
|
146
|
-
hooks["before_init"].append(self.config["setup_virtual_env"])
|
|
147
|
-
hooks["before_init"].append(f"pip install -r requirements.txt")
|
|
148
165
|
# read the requirements.txt file
|
|
149
166
|
with open("requirements.txt", "r") as f:
|
|
150
167
|
mounted_files.append(
|
|
@@ -167,7 +184,9 @@ class PluginGenerator:
|
|
|
167
184
|
)
|
|
168
185
|
)
|
|
169
186
|
|
|
170
|
-
def _add_spaceforge_hooks(
|
|
187
|
+
def _add_spaceforge_hooks(
|
|
188
|
+
self, hooks: Dict[str, List[str]], mounted_files: List[MountedFile]
|
|
189
|
+
) -> None:
|
|
171
190
|
# Add the spaceforge hook to actually run the plugin
|
|
172
191
|
if self.config is None:
|
|
173
192
|
raise ValueError("Plugin config not set. Call load_plugin() first.")
|
|
@@ -178,12 +197,14 @@ class PluginGenerator:
|
|
|
178
197
|
if hook not in hooks:
|
|
179
198
|
hooks[hook] = []
|
|
180
199
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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,
|
|
186
206
|
)
|
|
207
|
+
self._add_to_mounted_files(hooks, mounted_files, hook, f"{hook}.sh", render)
|
|
187
208
|
|
|
188
209
|
def _map_variables_to_parameters(self, contexts: List[Context]) -> None:
|
|
189
210
|
for context in contexts:
|
|
@@ -221,10 +242,10 @@ class PluginGenerator:
|
|
|
221
242
|
}
|
|
222
243
|
mounted_files: List[MountedFile] = []
|
|
223
244
|
|
|
224
|
-
self._update_with_requirements(
|
|
245
|
+
self._update_with_requirements(mounted_files)
|
|
225
246
|
self._update_with_python_file(mounted_files)
|
|
226
|
-
self._generate_binary_install_command(hooks)
|
|
227
|
-
self._add_spaceforge_hooks(hooks)
|
|
247
|
+
self._generate_binary_install_command(hooks, mounted_files)
|
|
248
|
+
self._add_spaceforge_hooks(hooks, mounted_files)
|
|
228
249
|
|
|
229
250
|
# Get the contexts and append the hooks and mounted files to it.
|
|
230
251
|
if self.plugin_class is None:
|
|
@@ -256,46 +277,38 @@ class PluginGenerator:
|
|
|
256
277
|
|
|
257
278
|
return contexts
|
|
258
279
|
|
|
259
|
-
def _generate_binary_install_command(
|
|
280
|
+
def _generate_binary_install_command(
|
|
281
|
+
self, hooks: Dict[str, List[str]], mounted_files: List[MountedFile]
|
|
282
|
+
) -> None:
|
|
260
283
|
binaries = self.get_plugin_binaries()
|
|
261
284
|
if binaries is None:
|
|
262
285
|
return None
|
|
263
286
|
|
|
264
|
-
binary_cmd = ""
|
|
265
|
-
if len(binaries) > 0:
|
|
266
|
-
binary_cmd = f"mkdir -p {static_binary_directory} && cd {static_binary_directory} && "
|
|
267
287
|
for i, binary in enumerate(binaries):
|
|
268
288
|
amd64_url = binary.download_urls.get("amd64", None)
|
|
269
289
|
arm64_url = binary.download_urls.get("arm64", None)
|
|
290
|
+
binary_path = f"{static_binary_directory}/{binary.name}"
|
|
270
291
|
if amd64_url is None and arm64_url is None:
|
|
271
292
|
raise ValueError(
|
|
272
293
|
f"Binary {binary.name} must have at least one download URL defined (amd64 or arm64)"
|
|
273
294
|
)
|
|
274
295
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
f"curl {arm64_url} -o {binary_path} -L && chmod +x {binary_path}"
|
|
283
|
-
if arm64_url is not None
|
|
284
|
-
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,
|
|
285
303
|
)
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
304
|
+
self._add_to_mounted_files(
|
|
305
|
+
hooks,
|
|
306
|
+
mounted_files,
|
|
307
|
+
"before_init",
|
|
308
|
+
f"binary_install_{binary.name}.sh",
|
|
309
|
+
render,
|
|
291
310
|
)
|
|
292
311
|
|
|
293
|
-
if binary_cmd != "":
|
|
294
|
-
binary_cmd += "cd /mnt/workspace/source/$TF_VAR_spacelift_project_root"
|
|
295
|
-
|
|
296
|
-
hooks["before_init"].append(binary_cmd)
|
|
297
|
-
return None
|
|
298
|
-
|
|
299
312
|
def get_plugin_binaries(self) -> Optional[List[Binary]]:
|
|
300
313
|
"""Get binary definitions from the plugin class."""
|
|
301
314
|
return getattr(self.plugin_class, "__binaries__", None)
|
|
@@ -233,7 +233,7 @@ class SpaceforgePlugin(ABC):
|
|
|
233
233
|
data: Dict[str, Any] = json.load(f)
|
|
234
234
|
return data
|
|
235
235
|
|
|
236
|
-
def send_markdown(self, markdown: str) ->
|
|
236
|
+
def send_markdown(self, markdown: str) -> bool:
|
|
237
237
|
"""
|
|
238
238
|
Send a markdown message to the Spacelift run.
|
|
239
239
|
|
|
@@ -245,13 +245,13 @@ class SpaceforgePlugin(ABC):
|
|
|
245
245
|
"Spacelift run is local. Not uploading markdown. Below is a preview of what would be sent"
|
|
246
246
|
)
|
|
247
247
|
self.logger.info(markdown)
|
|
248
|
-
return
|
|
248
|
+
return True
|
|
249
249
|
|
|
250
250
|
if self._spacelift_markdown_endpoint is None:
|
|
251
251
|
self.logger.error(
|
|
252
252
|
'API is not enabled, please export "SPACELIFT_API_TOKEN" and "TF_VAR_spacelift_graphql_endpoint".'
|
|
253
253
|
)
|
|
254
|
-
|
|
254
|
+
return False
|
|
255
255
|
|
|
256
256
|
headers = {"Authorization": f"Bearer {self._api_token}"}
|
|
257
257
|
body = {
|
|
@@ -263,28 +263,33 @@ class SpaceforgePlugin(ABC):
|
|
|
263
263
|
self._spacelift_markdown_endpoint,
|
|
264
264
|
json.dumps(body).encode("utf-8"),
|
|
265
265
|
headers,
|
|
266
|
+
method="POST",
|
|
266
267
|
)
|
|
267
268
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
269
|
+
try:
|
|
270
|
+
with urllib.request.urlopen(req) as response:
|
|
271
|
+
if response.status != 200:
|
|
272
|
+
self.logger.error(
|
|
273
|
+
f"Error getting signed URL for markdown upload: {response}"
|
|
274
|
+
)
|
|
275
|
+
return False
|
|
276
|
+
|
|
277
|
+
raw_response = response.read().decode("utf-8")
|
|
278
|
+
self.logger.debug(raw_response)
|
|
279
|
+
resp: Dict[str, Any] = json.loads(raw_response)
|
|
280
|
+
if "url" not in resp or "headers" not in resp:
|
|
281
|
+
self.logger.error(
|
|
282
|
+
"Markdown signed url response does not contain 'url' or 'headers' key."
|
|
283
|
+
)
|
|
284
|
+
return False
|
|
283
285
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
286
|
+
signed_url = resp["url"]
|
|
287
|
+
headers = resp["headers"]
|
|
288
|
+
headers["Content-Type"] = "text/markdown"
|
|
289
|
+
headers["Content-Length"] = str(len(markdown))
|
|
290
|
+
except urllib.request.HTTPError as e:
|
|
291
|
+
self.logger.error(f"HTTP error occurred: {e.code} - {e.reason}")
|
|
292
|
+
return False
|
|
288
293
|
|
|
289
294
|
# Now we upload the markdown content to the signed URL
|
|
290
295
|
req = urllib.request.Request(
|
|
@@ -299,8 +304,9 @@ class SpaceforgePlugin(ABC):
|
|
|
299
304
|
self.logger.error(
|
|
300
305
|
f"Error uploading markdown content: {put_response.status}"
|
|
301
306
|
)
|
|
302
|
-
return
|
|
307
|
+
return False
|
|
303
308
|
self.logger.debug("Markdown content uploaded successfully.")
|
|
309
|
+
return True
|
|
304
310
|
|
|
305
311
|
def add_to_policy_input(self, input_name: str, data: Dict[str, Any]) -> None:
|
|
306
312
|
"""
|