spaceforge 1.1.5__tar.gz → 1.1.7__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-1.1.5 → spaceforge-1.1.7}/.github/workflows/ci.yml +37 -5
- spaceforge-1.1.5/README.md → spaceforge-1.1.7/CONTRIBUTING.md +101 -268
- {spaceforge-1.1.5 → spaceforge-1.1.7}/LICENSE +1 -1
- spaceforge-1.1.7/PKG-INFO +56 -0
- spaceforge-1.1.7/README.md +15 -0
- spaceforge-1.1.7/plugins/checkov/README.md +69 -0
- spaceforge-1.1.7/plugins/checkov/plugin.py +344 -0
- spaceforge-1.1.7/plugins/checkov/plugin.yaml +514 -0
- spaceforge-1.1.7/plugins/checkov/requirements.txt +1 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/plugins/enviroment_manager/plugin.py +126 -114
- {spaceforge-1.1.5 → spaceforge-1.1.7}/plugins/enviroment_manager/plugin.yaml +128 -116
- {spaceforge-1.1.5 → spaceforge-1.1.7}/plugins/envsubst/plugin.py +26 -26
- {spaceforge-1.1.5 → spaceforge-1.1.7}/plugins/envsubst/plugin.yaml +68 -33
- {spaceforge-1.1.5 → spaceforge-1.1.7}/plugins/infracost/plugin.py +7 -6
- {spaceforge-1.1.5 → spaceforge-1.1.7}/plugins/infracost/plugin.yaml +7 -6
- spaceforge-1.1.7/plugins/opentofu-tracing/plugin.py +443 -0
- spaceforge-1.1.7/plugins/opentofu-tracing/plugin.yaml +719 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/plugins/sops/plugin.py +27 -25
- {spaceforge-1.1.5 → spaceforge-1.1.7}/plugins/sops/plugin.yaml +70 -33
- spaceforge-1.1.7/plugins/terrascan/README.md +50 -0
- spaceforge-1.1.7/plugins/terrascan/plugin.py +284 -0
- spaceforge-1.1.7/plugins/terrascan/plugin.yaml +482 -0
- spaceforge-1.1.7/plugins/trivy/README.md +21 -0
- spaceforge-1.1.7/plugins/trivy/plugin.py +224 -0
- spaceforge-1.1.7/plugins/trivy/plugin.yaml +389 -0
- spaceforge-1.1.7/plugins/trufflehog/README.md +42 -0
- spaceforge-1.1.7/plugins/trufflehog/plugin.py +285 -0
- spaceforge-1.1.7/plugins/trufflehog/plugin.yaml +454 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/plugins/wiz/plugin.py +48 -32
- {spaceforge-1.1.5 → spaceforge-1.1.7}/plugins/wiz/plugin.yaml +91 -40
- spaceforge-1.1.7/regenerate_plugins.sh +81 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/_version_scm.py +3 -3
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/generator.py +10 -4
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/templates/binary_install.sh.j2 +1 -1
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/templates/ensure_spaceforge_and_run.sh.j2 +2 -2
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/test_generator.py +2 -2
- spaceforge-1.1.7/spaceforge.egg-info/PKG-INFO +56 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge.egg-info/SOURCES.txt +17 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/test.sh +4 -4
- spaceforge-1.1.5/PKG-INFO +0 -736
- spaceforge-1.1.5/spaceforge.egg-info/PKG-INFO +0 -736
- {spaceforge-1.1.5 → spaceforge-1.1.7}/.github/workflows/release.yml +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/.gitignore +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/MANIFEST.in +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/go.mod +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/plugins/enviroment_manager/requirements.txt +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/plugins/sops/requirements.txt +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/pyproject.toml +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/setup.cfg +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/README.md +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/__init__.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/__main__.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/_version.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/cls.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/conftest.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/plugin.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/runner.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/schema.json +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/test_cls.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/test_generator_binaries.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/test_generator_core.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/test_generator_hooks.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/test_generator_parameters.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/test_plugin.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/test_plugin_file_operations.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/test_plugin_hooks.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/test_plugin_inheritance.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/test_runner.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/test_runner_cli.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/test_runner_core.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge/test_runner_execution.py +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge.egg-info/dependency_links.txt +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge.egg-info/entry_points.txt +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge.egg-info/requires.txt +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/spaceforge.egg-info/top_level.txt +0 -0
- {spaceforge-1.1.5 → spaceforge-1.1.7}/templates.go +0 -0
|
@@ -39,28 +39,60 @@ jobs:
|
|
|
39
39
|
run: |
|
|
40
40
|
./test.sh
|
|
41
41
|
|
|
42
|
+
check-plugin-yaml:
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
|
|
48
|
+
- name: Set up Python
|
|
49
|
+
uses: actions/setup-python@v5
|
|
50
|
+
with:
|
|
51
|
+
python-version: '3.11'
|
|
52
|
+
|
|
53
|
+
- name: Install dependencies
|
|
54
|
+
run: |
|
|
55
|
+
python -m pip install --upgrade pip
|
|
56
|
+
pip install -e .
|
|
57
|
+
|
|
58
|
+
- name: Regenerate plugin YAML files
|
|
59
|
+
run: ./regenerate_plugins.sh
|
|
60
|
+
|
|
61
|
+
- name: Check for uncommitted changes
|
|
62
|
+
run: |
|
|
63
|
+
if ! git diff --exit-code; then
|
|
64
|
+
echo "❌ Plugin YAML files are out of date!"
|
|
65
|
+
echo ""
|
|
66
|
+
echo "Please run './regenerate_plugins.sh' and commit the changes."
|
|
67
|
+
echo ""
|
|
68
|
+
echo "Files that need to be updated:"
|
|
69
|
+
git diff --name-only
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
echo "✅ All plugin YAML files are up to date"
|
|
73
|
+
|
|
42
74
|
build:
|
|
43
75
|
needs: test
|
|
44
76
|
runs-on: ubuntu-latest
|
|
45
|
-
|
|
77
|
+
|
|
46
78
|
steps:
|
|
47
79
|
- uses: actions/checkout@v4
|
|
48
80
|
with:
|
|
49
81
|
fetch-depth: 0 # Fetch full history for setuptools-scm
|
|
50
|
-
|
|
82
|
+
|
|
51
83
|
- name: Set up Python
|
|
52
84
|
uses: actions/setup-python@v5
|
|
53
85
|
with:
|
|
54
86
|
python-version: '3.11'
|
|
55
|
-
|
|
87
|
+
|
|
56
88
|
- name: Install build dependencies
|
|
57
89
|
run: |
|
|
58
90
|
python -m pip install --upgrade pip
|
|
59
91
|
pip install build setuptools-scm[toml]
|
|
60
|
-
|
|
92
|
+
|
|
61
93
|
- name: Build package
|
|
62
94
|
run: python -m build
|
|
63
|
-
|
|
95
|
+
|
|
64
96
|
- name: Check distribution
|
|
65
97
|
run: |
|
|
66
98
|
pip install twine
|
|
@@ -1,18 +1,30 @@
|
|
|
1
|
-
# Spaceforge
|
|
1
|
+
# Spaceforge Contributing Guide
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Reporting an Issue
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
If you spot a problem with Spaceforge, [search if an issue already exists](https://github.com/spacelift-io/plugins/issues). If not, please [open a new issue](https://github.com/spacelift-io/plugins/issues/new).
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Creating a Plugin
|
|
8
|
+
|
|
9
|
+
Creating a plugin involves five steps:
|
|
10
|
+
|
|
11
|
+
1. **Install spaceforge:** `pip install spaceforge`
|
|
12
|
+
2. **Create your plugin:** Start with the quick start example.
|
|
13
|
+
3. **Test locally:** Use the `run` command to test your hooks.
|
|
14
|
+
4. **Generate manifest:** Use the `generate` command to create plugin.yaml.
|
|
15
|
+
5. **Upload to Spacelift:** Add your plugin manifest to your Spacelift account.
|
|
16
|
+
|
|
17
|
+
Detailed instructions for each step are below.
|
|
18
|
+
|
|
19
|
+
### Quick Start
|
|
20
|
+
|
|
21
|
+
#### 1. Install spaceforge
|
|
8
22
|
|
|
9
23
|
```bash
|
|
10
24
|
pip install spaceforge
|
|
11
25
|
```
|
|
12
26
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
### 1. Create Your Plugin
|
|
27
|
+
#### 2. Create Your Plugin
|
|
16
28
|
|
|
17
29
|
Create a Python file (e.g., `plugin.py`) and inherit from `SpaceforgePlugin`:
|
|
18
30
|
|
|
@@ -76,17 +88,7 @@ class MyPlugin(SpaceforgePlugin):
|
|
|
76
88
|
self.logger.info("Security scan passed!")
|
|
77
89
|
```
|
|
78
90
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
Generate the Spacelift plugin YAML manifest:
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
spaceforge generate plugin.py
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
This creates `plugin.yaml` that you can upload to Spacelift.
|
|
88
|
-
|
|
89
|
-
### 3. Test Your Plugin
|
|
91
|
+
#### 3. Test Your Plugin
|
|
90
92
|
|
|
91
93
|
Test individual hooks locally:
|
|
92
94
|
|
|
@@ -99,21 +101,30 @@ export ENVIRONMENT="staging"
|
|
|
99
101
|
spaceforge run after_plan
|
|
100
102
|
```
|
|
101
103
|
|
|
102
|
-
|
|
104
|
+
#### 4. Generate Plugin Manifest
|
|
103
105
|
|
|
104
|
-
|
|
106
|
+
Generate the Spacelift plugin `plugin.yaml` manifest that you can upload to Spacelift.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
spaceforge generate plugin.py
|
|
110
|
+
```
|
|
105
111
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
- `
|
|
115
|
-
- `
|
|
116
|
-
- `
|
|
112
|
+
#### 5. Upload your Plugin to Spacelift
|
|
113
|
+
|
|
114
|
+
For more advanced examples, see the [plugins](plugins/) directory in this repository.
|
|
115
|
+
|
|
116
|
+
### Available Hooks
|
|
117
|
+
|
|
118
|
+
Implement these methods in your plugin to add custom logic before and after specific run phases:
|
|
119
|
+
|
|
120
|
+
- **Initialization** (`before_init()` and `after_init()`)
|
|
121
|
+
- **Planning** (`before_plan()` and `after_plan()`)
|
|
122
|
+
- **Applying** (`before_apply()` and `after_apply()`)
|
|
123
|
+
- **Destroying** (`before_destroy()` and `after_destroy()`)
|
|
124
|
+
- Used during module test cases.
|
|
125
|
+
- Used by stacks during destruction with corresponding `stack_destructor_resource`.
|
|
126
|
+
- **Performing** (`before_perform()` and `after_perform()`): Used during stack tasks execution.
|
|
127
|
+
- **Finally** (`after_run()`): Executed after each actively processed run, regardless of its outcome. These hooks have access to an environment variable called `TF_VAR_spacelift_final_run_state`, which indicates the final state of the run.
|
|
117
128
|
|
|
118
129
|
## Plugin Components
|
|
119
130
|
|
|
@@ -126,6 +137,27 @@ class MyPlugin(SpaceforgePlugin):
|
|
|
126
137
|
__labels__ = ["security", "monitoring", "compliance"]
|
|
127
138
|
```
|
|
128
139
|
|
|
140
|
+
### Binaries
|
|
141
|
+
|
|
142
|
+
Automatically download and install external tools:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
__binaries__ = [
|
|
146
|
+
Binary(
|
|
147
|
+
name="kubectl",
|
|
148
|
+
download_urls={
|
|
149
|
+
"amd64": "https://dl.k8s.io/release/v1.28.0/bin/linux/amd64/kubectl",
|
|
150
|
+
"arm64": "https://dl.k8s.io/release/v1.28.0/bin/linux/arm64/kubectl"
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Notes**:
|
|
157
|
+
- Only open-source-licensed binaries can be used in plugins unless Spacelift has authorized them.
|
|
158
|
+
- `.tar.bz2`, `.tar.gz`, and `.zip` archives are extracted automatically.
|
|
159
|
+
- Archives must contain a file matching the binary name. All other files are ignored.
|
|
160
|
+
|
|
129
161
|
### Parameters
|
|
130
162
|
|
|
131
163
|
Define user-configurable parameters:
|
|
@@ -149,13 +181,13 @@ __parameters__ = [
|
|
|
149
181
|
]
|
|
150
182
|
```
|
|
151
183
|
|
|
152
|
-
**
|
|
153
|
-
- Parameter `name` is displayed in the Spacelift UI
|
|
154
|
-
- Parameter `id` (optional) is used for programmatic reference
|
|
155
|
-
- `value_from_parameter` can reference either the `id` (if present) or the `name
|
|
156
|
-
- Parameters are made available as environment variables through Variable definitions
|
|
157
|
-
- Default values must be strings
|
|
158
|
-
- Required parameters cannot have default values
|
|
184
|
+
**Notes:**
|
|
185
|
+
- Parameter `name` is displayed in the Spacelift UI.
|
|
186
|
+
- Parameter `id` (optional) is used for programmatic reference.
|
|
187
|
+
- `value_from_parameter` can reference either the `id` (if present) or the `name`.
|
|
188
|
+
- Parameters are made available as environment variables through Variable definitions.
|
|
189
|
+
- Default values must be strings.
|
|
190
|
+
- Required parameters cannot have default values.
|
|
159
191
|
|
|
160
192
|
### Contexts
|
|
161
193
|
|
|
@@ -188,22 +220,6 @@ __contexts__ = [
|
|
|
188
220
|
]
|
|
189
221
|
```
|
|
190
222
|
|
|
191
|
-
### Binaries
|
|
192
|
-
|
|
193
|
-
Automatically download and install external tools:
|
|
194
|
-
|
|
195
|
-
```python
|
|
196
|
-
__binaries__ = [
|
|
197
|
-
Binary(
|
|
198
|
-
name="kubectl",
|
|
199
|
-
download_urls={
|
|
200
|
-
"amd64": "https://dl.k8s.io/release/v1.28.0/bin/linux/amd64/kubectl",
|
|
201
|
-
"arm64": "https://dl.k8s.io/release/v1.28.0/bin/linux/arm64/kubectl"
|
|
202
|
-
}
|
|
203
|
-
)
|
|
204
|
-
]
|
|
205
|
-
```
|
|
206
|
-
|
|
207
223
|
**Context Priority System:**
|
|
208
224
|
|
|
209
225
|
Control the execution order of contexts using the `priority` field:
|
|
@@ -229,13 +245,13 @@ __contexts__ = [
|
|
|
229
245
|
]
|
|
230
246
|
```
|
|
231
247
|
|
|
232
|
-
**
|
|
233
|
-
- Default priority is `0
|
|
234
|
-
- Lower numbers execute first (0, then 1, then 2, etc.)
|
|
235
|
-
- Useful for ensuring setup contexts run before main execution contexts
|
|
248
|
+
**Notes:**
|
|
249
|
+
- Default priority is `0`.
|
|
250
|
+
- Lower numbers execute first (0, then 1, then 2, etc.).
|
|
251
|
+
- Useful for ensuring setup contexts run before main execution contexts.
|
|
236
252
|
|
|
237
253
|
**Binary PATH Management:**
|
|
238
|
-
- When using Python hook methods (e.g., `def before_apply()`), binaries are automatically available in PATH
|
|
254
|
+
- When using Python hook methods (e.g., `def before_apply()`), binaries are automatically available in `$PATH`.
|
|
239
255
|
- When using raw context hooks, you must manually export the PATH:
|
|
240
256
|
|
|
241
257
|
```python
|
|
@@ -280,11 +296,11 @@ __contexts__ = [
|
|
|
280
296
|
]
|
|
281
297
|
```
|
|
282
298
|
|
|
283
|
-
**
|
|
284
|
-
- Files are created at the specified path when the context is applied
|
|
285
|
-
- Content is written exactly as provided
|
|
286
|
-
- Use `sensitive=True` for files containing secrets or sensitive data
|
|
287
|
-
- path is from `/mnt/workspace/`. An example would be `tmp/config.json` which would be mounted at `/mnt/workspace/tmp/config.json
|
|
299
|
+
**Notes:**
|
|
300
|
+
- Files are created at the specified path when the context is applied.
|
|
301
|
+
- Content is written exactly as provided.
|
|
302
|
+
- Use `sensitive=True` for files containing secrets or sensitive data.
|
|
303
|
+
- path is from `/mnt/workspace/`. An example would be `tmp/config.json` which would be mounted at `/mnt/workspace/tmp/config.json`.
|
|
288
304
|
|
|
289
305
|
### Policies
|
|
290
306
|
|
|
@@ -398,11 +414,11 @@ def before_plan(self):
|
|
|
398
414
|
self.logger.info(f"Authenticated as: {result['viewer']['login']}")
|
|
399
415
|
```
|
|
400
416
|
|
|
401
|
-
**
|
|
402
|
-
- Allows plugins to act on behalf of a specific user
|
|
403
|
-
- Useful for operations requiring user-specific permissions
|
|
404
|
-
- User tokens may have different access levels than service tokens
|
|
405
|
-
- Call `use_user_token()` before making API requests
|
|
417
|
+
**Notes:**
|
|
418
|
+
- Allows plugins to act on behalf of a specific user.
|
|
419
|
+
- Useful for operations requiring user-specific permissions.
|
|
420
|
+
- User tokens may have different access levels than service tokens.
|
|
421
|
+
- Call `use_user_token()` before making API requests.
|
|
406
422
|
|
|
407
423
|
### Access Plan and State
|
|
408
424
|
|
|
@@ -423,7 +439,7 @@ def after_plan(self):
|
|
|
423
439
|
|
|
424
440
|
### Send Rich Output
|
|
425
441
|
|
|
426
|
-
Send formatted
|
|
442
|
+
Send formatted Markdown to the Spacelift UI:
|
|
427
443
|
|
|
428
444
|
```python
|
|
429
445
|
def after_plan(self):
|
|
@@ -442,8 +458,6 @@ def after_plan(self):
|
|
|
442
458
|
|
|
443
459
|
### Add to Policy Input
|
|
444
460
|
|
|
445
|
-
Add custom data to the OPA policy input:
|
|
446
|
-
|
|
447
461
|
The following example will create input available via `input.third_party_metadata.custom.my_custom_data` in your OPA policies:
|
|
448
462
|
```python
|
|
449
463
|
def after_plan(self):
|
|
@@ -455,44 +469,9 @@ def after_plan(self):
|
|
|
455
469
|
})
|
|
456
470
|
```
|
|
457
471
|
|
|
458
|
-
## CLI Commands
|
|
459
|
-
|
|
460
|
-
### Generate Plugin Manifest
|
|
461
|
-
|
|
462
|
-
```bash
|
|
463
|
-
# Generate from plugin.py (default filename)
|
|
464
|
-
spaceforge generate
|
|
465
|
-
|
|
466
|
-
# Generate from specific file
|
|
467
|
-
spaceforge generate my_plugin.py
|
|
468
|
-
|
|
469
|
-
# Specify output file
|
|
470
|
-
spaceforge generate my_plugin.py -o custom-output.yaml
|
|
471
|
-
|
|
472
|
-
# Get help
|
|
473
|
-
spaceforge generate --help
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
### Test Plugin Hooks
|
|
477
|
-
|
|
478
|
-
```bash
|
|
479
|
-
# Set parameters for local testing (parameters are normally provided by Spacelift)
|
|
480
|
-
export API_KEY="test-key"
|
|
481
|
-
export TIMEOUT="60"
|
|
482
|
-
|
|
483
|
-
# Test specific hook
|
|
484
|
-
spaceforge run after_plan
|
|
485
|
-
|
|
486
|
-
# Test with specific plugin file
|
|
487
|
-
spaceforge run --plugin-file my_plugin.py before_apply
|
|
488
|
-
|
|
489
|
-
# Get help
|
|
490
|
-
spaceforge run --help
|
|
491
|
-
```
|
|
492
|
-
|
|
493
472
|
## Plugin Development Tips
|
|
494
473
|
|
|
495
|
-
###
|
|
474
|
+
### Handle Dependencies
|
|
496
475
|
|
|
497
476
|
If your plugin needs Python packages, create a `requirements.txt` file. Spaceforge automatically adds a `before_init` hook to install them:
|
|
498
477
|
|
|
@@ -512,7 +491,7 @@ def after_plan(self):
|
|
|
512
491
|
self.logger.info(f"Processing run {run_id} for stack {stack_id}")
|
|
513
492
|
```
|
|
514
493
|
|
|
515
|
-
###
|
|
494
|
+
### Error Handling
|
|
516
495
|
|
|
517
496
|
Always handle errors gracefully:
|
|
518
497
|
|
|
@@ -528,168 +507,22 @@ def after_plan(self):
|
|
|
528
507
|
exit(1)
|
|
529
508
|
```
|
|
530
509
|
|
|
531
|
-
###
|
|
510
|
+
### Testing and Debugging
|
|
532
511
|
|
|
533
|
-
- Set `SPACELIFT_DEBUG=true` to enable debug logging
|
|
534
|
-
- Use the `run` command to test hooks during development
|
|
535
|
-
- Test with different parameter combinations
|
|
536
|
-
- Validate your generated YAML before uploading to Spacelift
|
|
512
|
+
- Set `SPACELIFT_DEBUG=true` to enable debug logging.
|
|
513
|
+
- Use the `run` command to test hooks during development.
|
|
514
|
+
- Test with different parameter combinations.
|
|
515
|
+
- Validate your generated YAML before uploading to Spacelift.
|
|
537
516
|
|
|
538
|
-
|
|
517
|
+
### Speeding Up Plugin Execution
|
|
539
518
|
|
|
540
|
-
|
|
519
|
+
There are a few things you can do to speed up plugin execution:
|
|
541
520
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
521
|
+
- Ensure your runner has `spaceforge` preinstalled. This will avoid the overhead of installing it during the run. (15-30 seconds)
|
|
522
|
+
- If you use binaries, we will only install the binary if its not found. You can gain a few seconds by ensuring its already on the runner.
|
|
523
|
+
- 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.
|
|
524
|
+
- 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.
|
|
546
525
|
|
|
547
|
-
|
|
548
|
-
__plugin_name__ = "security-scanner"
|
|
549
|
-
__version__ = "1.0.0"
|
|
550
|
-
__author__ = "Security Team"
|
|
551
|
-
|
|
552
|
-
__binaries__ = [
|
|
553
|
-
Binary(
|
|
554
|
-
name="security-cli",
|
|
555
|
-
download_urls={
|
|
556
|
-
"amd64": "https://releases.example.com/security-cli-linux-amd64",
|
|
557
|
-
"arm64": "https://releases.example.com/security-cli-linux-arm64"
|
|
558
|
-
}
|
|
559
|
-
)
|
|
560
|
-
]
|
|
561
|
-
|
|
562
|
-
__parameters__ = [
|
|
563
|
-
Parameter(
|
|
564
|
-
name="API Token",
|
|
565
|
-
id="api_token",
|
|
566
|
-
description="Security service API token",
|
|
567
|
-
required=True,
|
|
568
|
-
sensitive=True
|
|
569
|
-
),
|
|
570
|
-
Parameter(
|
|
571
|
-
name="Severity Threshold",
|
|
572
|
-
id="severity_threshold",
|
|
573
|
-
description="Minimum severity level to report",
|
|
574
|
-
required=False,
|
|
575
|
-
default="medium"
|
|
576
|
-
)
|
|
577
|
-
]
|
|
578
|
-
|
|
579
|
-
__contexts__ = [
|
|
580
|
-
Context(
|
|
581
|
-
name_prefix="security-scanner",
|
|
582
|
-
description="Security scanning context",
|
|
583
|
-
env=[
|
|
584
|
-
Variable(
|
|
585
|
-
key="SECURITY_API_TOKEN",
|
|
586
|
-
value_from_parameter="api_token",
|
|
587
|
-
sensitive=True
|
|
588
|
-
),
|
|
589
|
-
Variable(
|
|
590
|
-
key="SEVERITY_THRESHOLD",
|
|
591
|
-
value_from_parameter="severity_threshold"
|
|
592
|
-
)
|
|
593
|
-
]
|
|
594
|
-
)
|
|
595
|
-
]
|
|
596
|
-
|
|
597
|
-
def after_plan(self):
|
|
598
|
-
"""Run security scan after Terraform plan"""
|
|
599
|
-
self.logger.info("Starting security scan of Terraform plan")
|
|
600
|
-
|
|
601
|
-
# Authenticate with security service
|
|
602
|
-
return_code, stdout, stderr = self.run_cli(
|
|
603
|
-
"security-cli", "auth",
|
|
604
|
-
"--token", os.environ["SECURITY_API_TOKEN"]
|
|
605
|
-
)
|
|
606
|
-
|
|
607
|
-
if return_code != 0:
|
|
608
|
-
self.logger.error("Failed to authenticate with security service")
|
|
609
|
-
exit(1)
|
|
610
|
-
|
|
611
|
-
# Scan the Terraform plan
|
|
612
|
-
return_code, stdout, stderr = self.run_cli(
|
|
613
|
-
"security-cli", "scan", "terraform",
|
|
614
|
-
"--plan-file", "spacelift.plan.json",
|
|
615
|
-
"--format", "json",
|
|
616
|
-
"--severity", os.environ.get("SEVERITY_THRESHOLD", "medium"),
|
|
617
|
-
print_output=False
|
|
618
|
-
)
|
|
619
|
-
|
|
620
|
-
if return_code != 0:
|
|
621
|
-
self.logger.error("Security scan failed")
|
|
622
|
-
for line in stderr:
|
|
623
|
-
self.logger.error(line)
|
|
624
|
-
exit(1)
|
|
625
|
-
|
|
626
|
-
# Parse scan results
|
|
627
|
-
try:
|
|
628
|
-
results = json.loads('\n'.join(stdout))
|
|
629
|
-
|
|
630
|
-
# Generate markdown report
|
|
631
|
-
markdown = self._generate_report(results)
|
|
632
|
-
self.send_markdown(markdown)
|
|
633
|
-
|
|
634
|
-
# Fail run if critical issues found
|
|
635
|
-
if results.get('critical_count', 0) > 0:
|
|
636
|
-
self.logger.error(f"Found {results['critical_count']} critical security issues")
|
|
637
|
-
exit(1)
|
|
638
|
-
|
|
639
|
-
self.logger.info("Security scan completed successfully")
|
|
640
|
-
|
|
641
|
-
except json.JSONDecodeError:
|
|
642
|
-
self.logger.error("Failed to parse scan results")
|
|
643
|
-
exit(1)
|
|
644
|
-
|
|
645
|
-
def _generate_report(self, results):
|
|
646
|
-
"""Generate markdown report from scan results"""
|
|
647
|
-
report = "# Security Scan Results\n\n"
|
|
648
|
-
|
|
649
|
-
if results.get('total_issues', 0) == 0:
|
|
650
|
-
report += "✅ **No security issues found!**\n"
|
|
651
|
-
else:
|
|
652
|
-
report += f"Found {results['total_issues']} security issues:\n\n"
|
|
653
|
-
|
|
654
|
-
for severity in ['critical', 'high', 'medium', 'low']:
|
|
655
|
-
count = results.get(f'{severity}_count', 0)
|
|
656
|
-
if count > 0:
|
|
657
|
-
emoji = {'critical': '🔴', 'high': '🟠', 'medium': '🟡', 'low': '🟢'}[severity]
|
|
658
|
-
report += f"- {emoji} **{severity.upper()}:** {count}\n"
|
|
659
|
-
|
|
660
|
-
if results.get('report_url'):
|
|
661
|
-
report += f"\n[View detailed report]({results['report_url']})\n"
|
|
662
|
-
|
|
663
|
-
return report
|
|
664
|
-
```
|
|
665
|
-
|
|
666
|
-
Generate and test this plugin:
|
|
526
|
+
## Working on Spaceforge
|
|
667
527
|
|
|
668
|
-
|
|
669
|
-
# Generate the manifest
|
|
670
|
-
spaceforge generate security_scanner.py
|
|
671
|
-
|
|
672
|
-
# Test locally
|
|
673
|
-
export API_TOKEN="your-token"
|
|
674
|
-
export SEVERITY_THRESHOLD="high"
|
|
675
|
-
spaceforge run after_plan
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
## Speeding up plugin execution
|
|
679
|
-
|
|
680
|
-
There are a few things you can do to speed up plugin execution.
|
|
681
|
-
|
|
682
|
-
1. Ensure your runner has `spaceforge` preinstalled. This will avoid the overhead of installing it during the run. (15-30 seconds)
|
|
683
|
-
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.
|
|
684
|
-
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.
|
|
685
|
-
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.
|
|
686
|
-
|
|
687
|
-
## Next Steps
|
|
688
|
-
|
|
689
|
-
1. **Install spaceforge:** `pip install spaceforge`
|
|
690
|
-
2. **Create your plugin:** Start with the quick start example
|
|
691
|
-
3. **Test locally:** Use the `run` command to test your hooks
|
|
692
|
-
4. **Generate manifest:** Use the `generate` command to create plugin.yaml
|
|
693
|
-
5. **Upload to Spacelift:** Add your plugin manifest to your Spacelift account
|
|
694
|
-
|
|
695
|
-
For more advanced examples, see the [plugins](plugins/) directory in this repository.
|
|
528
|
+
See the [README file](./spaceforge/README.md) in the `spaceforge` folder.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: spaceforge
|
|
3
|
+
Version: 1.1.7
|
|
4
|
+
Summary: A Python framework for building Spacelift plugins
|
|
5
|
+
Author-email: Spacelift <support@spacelift.io>
|
|
6
|
+
Maintainer-email: Spacelift <support@spacelift.io>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/spacelift-io/plugins
|
|
9
|
+
Project-URL: Documentation, https://github.com/spacelift-io/plugins#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/spacelift-io/plugins
|
|
11
|
+
Project-URL: Bug Reports, https://github.com/spacelift-io/plugins/issues
|
|
12
|
+
Keywords: spacelift,plugin,framework,infrastructure,devops,spaceforge
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: System :: Systems Administration
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: PyYAML>=6.0
|
|
27
|
+
Requires-Dist: click>=8.0.0
|
|
28
|
+
Requires-Dist: pydantic>=2.11.7
|
|
29
|
+
Requires-Dist: Jinja2>=3.1.0
|
|
30
|
+
Requires-Dist: mergedeep>=1.3.4
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
34
|
+
Requires-Dist: black; extra == "dev"
|
|
35
|
+
Requires-Dist: isort; extra == "dev"
|
|
36
|
+
Requires-Dist: mypy; extra == "dev"
|
|
37
|
+
Requires-Dist: types-PyYAML; extra == "dev"
|
|
38
|
+
Requires-Dist: setuptools-scm[toml]>=6.2; extra == "dev"
|
|
39
|
+
Requires-Dist: autoflake; extra == "dev"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# Spaceforge - Build Spacelift Plugins in Python
|
|
43
|
+
|
|
44
|
+
Spaceforge is a Python framework for building powerful Spacelift plugins using a declarative, hook-based approach. Define your plugin logic in Python, and Spaceforge automatically generates the plugin manifest for Spacelift.
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
For installation and usage instructions, see [our documentation](https://docs.spacelift.io/integrations/plugins).
|
|
49
|
+
|
|
50
|
+
## Contributing
|
|
51
|
+
|
|
52
|
+
To contribute to Spaceforge or create plugins, see our [CONTRIBUTING.md](./CONTRIBUTING.md) file.
|
|
53
|
+
|
|
54
|
+
## License
|
|
55
|
+
|
|
56
|
+
Spaceforge and Spacelift plugins are licensed under the [MIT license](./LICENSE).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Spaceforge - Build Spacelift Plugins in Python
|
|
2
|
+
|
|
3
|
+
Spaceforge is a Python framework for building powerful Spacelift plugins using a declarative, hook-based approach. Define your plugin logic in Python, and Spaceforge automatically generates the plugin manifest for Spacelift.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
For installation and usage instructions, see [our documentation](https://docs.spacelift.io/integrations/plugins).
|
|
8
|
+
|
|
9
|
+
## Contributing
|
|
10
|
+
|
|
11
|
+
To contribute to Spaceforge or create plugins, see our [CONTRIBUTING.md](./CONTRIBUTING.md) file.
|
|
12
|
+
|
|
13
|
+
## License
|
|
14
|
+
|
|
15
|
+
Spaceforge and Spacelift plugins are licensed under the [MIT license](./LICENSE).
|