spaceforge 1.1.3__tar.gz → 1.1.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-1.1.3 → spaceforge-1.1.5}/PKG-INFO +13 -18
- {spaceforge-1.1.3 → spaceforge-1.1.5}/README.md +12 -12
- {spaceforge-1.1.3 → spaceforge-1.1.5}/plugins/enviroment_manager/plugin.py +5 -5
- {spaceforge-1.1.3 → spaceforge-1.1.5}/plugins/enviroment_manager/plugin.yaml +11 -11
- {spaceforge-1.1.3 → spaceforge-1.1.5}/plugins/sops/plugin.yaml +1 -1
- {spaceforge-1.1.3 → spaceforge-1.1.5}/plugins/wiz/plugin.yaml +1 -1
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/README.md +5 -5
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/__main__.py +13 -2
- spaceforge-1.1.5/spaceforge/_version.py +39 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/_version_scm.py +3 -3
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/runner.py +2 -2
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/templates/ensure_spaceforge_and_run.sh.j2 +1 -1
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/test_generator.py +1 -2
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/test_runner.py +5 -6
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/test_runner_cli.py +3 -3
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge.egg-info/PKG-INFO +13 -18
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge.egg-info/SOURCES.txt +0 -2
- spaceforge-1.1.3/setup.py +0 -66
- spaceforge-1.1.3/spaceforge/_version.py +0 -80
- spaceforge-1.1.3/spaceforge.egg-info/not-zip-safe +0 -1
- {spaceforge-1.1.3 → spaceforge-1.1.5}/.github/workflows/ci.yml +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/.github/workflows/release.yml +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/.gitignore +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/LICENSE +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/MANIFEST.in +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/go.mod +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/plugins/enviroment_manager/requirements.txt +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/plugins/envsubst/plugin.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/plugins/envsubst/plugin.yaml +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/plugins/infracost/plugin.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/plugins/infracost/plugin.yaml +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/plugins/sops/plugin.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/plugins/sops/requirements.txt +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/plugins/wiz/plugin.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/pyproject.toml +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/setup.cfg +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/__init__.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/cls.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/conftest.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/generator.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/plugin.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/schema.json +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/templates/binary_install.sh.j2 +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/test_cls.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/test_generator_binaries.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/test_generator_core.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/test_generator_hooks.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/test_generator_parameters.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/test_plugin.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/test_plugin_file_operations.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/test_plugin_hooks.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/test_plugin_inheritance.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/test_runner_core.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge/test_runner_execution.py +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge.egg-info/dependency_links.txt +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge.egg-info/entry_points.txt +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge.egg-info/requires.txt +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/spaceforge.egg-info/top_level.txt +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/templates.go +0 -0
- {spaceforge-1.1.3 → spaceforge-1.1.5}/test.sh +0 -0
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spaceforge
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.5
|
|
4
4
|
Summary: A Python framework for building Spacelift plugins
|
|
5
|
-
Home-page: https://github.com/spacelift-io/plugins
|
|
6
|
-
Author: Spacelift
|
|
7
5
|
Author-email: Spacelift <support@spacelift.io>
|
|
8
6
|
Maintainer-email: Spacelift <support@spacelift.io>
|
|
9
7
|
License: MIT
|
|
@@ -39,10 +37,7 @@ Requires-Dist: mypy; extra == "dev"
|
|
|
39
37
|
Requires-Dist: types-PyYAML; extra == "dev"
|
|
40
38
|
Requires-Dist: setuptools-scm[toml]>=6.2; extra == "dev"
|
|
41
39
|
Requires-Dist: autoflake; extra == "dev"
|
|
42
|
-
Dynamic: author
|
|
43
|
-
Dynamic: home-page
|
|
44
40
|
Dynamic: license-file
|
|
45
|
-
Dynamic: requires-python
|
|
46
41
|
|
|
47
42
|
# Spaceforge - Build Spacelift Plugins in Python
|
|
48
43
|
|
|
@@ -60,15 +55,15 @@ pip install spaceforge
|
|
|
60
55
|
|
|
61
56
|
### 1. Create Your Plugin
|
|
62
57
|
|
|
63
|
-
Create a Python file (e.g., `
|
|
58
|
+
Create a Python file (e.g., `plugin.py`) and inherit from `SpaceforgePlugin`:
|
|
64
59
|
|
|
65
60
|
```python
|
|
66
|
-
from spaceforge import SpaceforgePlugin, Parameter, Variable, Context
|
|
61
|
+
from spaceforge import SpaceforgePlugin, Parameter, Variable, Context
|
|
67
62
|
import os
|
|
68
63
|
|
|
69
64
|
class MyPlugin(SpaceforgePlugin):
|
|
70
65
|
# Plugin metadata
|
|
71
|
-
__plugin_name__ = "my-
|
|
66
|
+
__plugin_name__ = "my-plugin"
|
|
72
67
|
__version__ = "1.0.0"
|
|
73
68
|
__author__ = "Your Name"
|
|
74
69
|
__labels__ = ["security", "monitoring"] # Optional labels for categorization
|
|
@@ -127,7 +122,7 @@ class MyPlugin(SpaceforgePlugin):
|
|
|
127
122
|
Generate the Spacelift plugin YAML manifest:
|
|
128
123
|
|
|
129
124
|
```bash
|
|
130
|
-
spaceforge generate
|
|
125
|
+
spaceforge generate plugin.py
|
|
131
126
|
```
|
|
132
127
|
|
|
133
128
|
This creates `plugin.yaml` that you can upload to Spacelift.
|
|
@@ -142,7 +137,7 @@ export API_KEY="your-api-key"
|
|
|
142
137
|
export ENVIRONMENT="staging"
|
|
143
138
|
|
|
144
139
|
# Test the after_plan hook
|
|
145
|
-
spaceforge
|
|
140
|
+
spaceforge run after_plan
|
|
146
141
|
```
|
|
147
142
|
|
|
148
143
|
## Available Hooks
|
|
@@ -527,13 +522,13 @@ export API_KEY="test-key"
|
|
|
527
522
|
export TIMEOUT="60"
|
|
528
523
|
|
|
529
524
|
# Test specific hook
|
|
530
|
-
spaceforge
|
|
525
|
+
spaceforge run after_plan
|
|
531
526
|
|
|
532
527
|
# Test with specific plugin file
|
|
533
|
-
spaceforge
|
|
528
|
+
spaceforge run --plugin-file my_plugin.py before_apply
|
|
534
529
|
|
|
535
530
|
# Get help
|
|
536
|
-
spaceforge
|
|
531
|
+
spaceforge run --help
|
|
537
532
|
```
|
|
538
533
|
|
|
539
534
|
## Plugin Development Tips
|
|
@@ -577,7 +572,7 @@ def after_plan(self):
|
|
|
577
572
|
### 4. Testing and Debugging
|
|
578
573
|
|
|
579
574
|
- Set `SPACELIFT_DEBUG=true` to enable debug logging
|
|
580
|
-
- Use the
|
|
575
|
+
- Use the `run` command to test hooks during development
|
|
581
576
|
- Test with different parameter combinations
|
|
582
577
|
- Validate your generated YAML before uploading to Spacelift
|
|
583
578
|
|
|
@@ -718,7 +713,7 @@ spaceforge generate security_scanner.py
|
|
|
718
713
|
# Test locally
|
|
719
714
|
export API_TOKEN="your-token"
|
|
720
715
|
export SEVERITY_THRESHOLD="high"
|
|
721
|
-
spaceforge
|
|
716
|
+
spaceforge run after_plan
|
|
722
717
|
```
|
|
723
718
|
|
|
724
719
|
## Speeding up plugin execution
|
|
@@ -734,8 +729,8 @@ There are a few things you can do to speed up plugin execution.
|
|
|
734
729
|
|
|
735
730
|
1. **Install spaceforge:** `pip install spaceforge`
|
|
736
731
|
2. **Create your plugin:** Start with the quick start example
|
|
737
|
-
3. **Test locally:** Use the
|
|
738
|
-
4. **Generate manifest:** Use the generate command to create plugin.yaml
|
|
732
|
+
3. **Test locally:** Use the `run` command to test your hooks
|
|
733
|
+
4. **Generate manifest:** Use the `generate` command to create plugin.yaml
|
|
739
734
|
5. **Upload to Spacelift:** Add your plugin manifest to your Spacelift account
|
|
740
735
|
|
|
741
736
|
For more advanced examples, see the [plugins](plugins/) directory in this repository.
|
|
@@ -14,15 +14,15 @@ pip install spaceforge
|
|
|
14
14
|
|
|
15
15
|
### 1. Create Your Plugin
|
|
16
16
|
|
|
17
|
-
Create a Python file (e.g., `
|
|
17
|
+
Create a Python file (e.g., `plugin.py`) and inherit from `SpaceforgePlugin`:
|
|
18
18
|
|
|
19
19
|
```python
|
|
20
|
-
from spaceforge import SpaceforgePlugin, Parameter, Variable, Context
|
|
20
|
+
from spaceforge import SpaceforgePlugin, Parameter, Variable, Context
|
|
21
21
|
import os
|
|
22
22
|
|
|
23
23
|
class MyPlugin(SpaceforgePlugin):
|
|
24
24
|
# Plugin metadata
|
|
25
|
-
__plugin_name__ = "my-
|
|
25
|
+
__plugin_name__ = "my-plugin"
|
|
26
26
|
__version__ = "1.0.0"
|
|
27
27
|
__author__ = "Your Name"
|
|
28
28
|
__labels__ = ["security", "monitoring"] # Optional labels for categorization
|
|
@@ -81,7 +81,7 @@ class MyPlugin(SpaceforgePlugin):
|
|
|
81
81
|
Generate the Spacelift plugin YAML manifest:
|
|
82
82
|
|
|
83
83
|
```bash
|
|
84
|
-
spaceforge generate
|
|
84
|
+
spaceforge generate plugin.py
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
This creates `plugin.yaml` that you can upload to Spacelift.
|
|
@@ -96,7 +96,7 @@ export API_KEY="your-api-key"
|
|
|
96
96
|
export ENVIRONMENT="staging"
|
|
97
97
|
|
|
98
98
|
# Test the after_plan hook
|
|
99
|
-
spaceforge
|
|
99
|
+
spaceforge run after_plan
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
## Available Hooks
|
|
@@ -481,13 +481,13 @@ export API_KEY="test-key"
|
|
|
481
481
|
export TIMEOUT="60"
|
|
482
482
|
|
|
483
483
|
# Test specific hook
|
|
484
|
-
spaceforge
|
|
484
|
+
spaceforge run after_plan
|
|
485
485
|
|
|
486
486
|
# Test with specific plugin file
|
|
487
|
-
spaceforge
|
|
487
|
+
spaceforge run --plugin-file my_plugin.py before_apply
|
|
488
488
|
|
|
489
489
|
# Get help
|
|
490
|
-
spaceforge
|
|
490
|
+
spaceforge run --help
|
|
491
491
|
```
|
|
492
492
|
|
|
493
493
|
## Plugin Development Tips
|
|
@@ -531,7 +531,7 @@ def after_plan(self):
|
|
|
531
531
|
### 4. Testing and Debugging
|
|
532
532
|
|
|
533
533
|
- Set `SPACELIFT_DEBUG=true` to enable debug logging
|
|
534
|
-
- Use the
|
|
534
|
+
- Use the `run` command to test hooks during development
|
|
535
535
|
- Test with different parameter combinations
|
|
536
536
|
- Validate your generated YAML before uploading to Spacelift
|
|
537
537
|
|
|
@@ -672,7 +672,7 @@ spaceforge generate security_scanner.py
|
|
|
672
672
|
# Test locally
|
|
673
673
|
export API_TOKEN="your-token"
|
|
674
674
|
export SEVERITY_THRESHOLD="high"
|
|
675
|
-
spaceforge
|
|
675
|
+
spaceforge run after_plan
|
|
676
676
|
```
|
|
677
677
|
|
|
678
678
|
## Speeding up plugin execution
|
|
@@ -688,8 +688,8 @@ There are a few things you can do to speed up plugin execution.
|
|
|
688
688
|
|
|
689
689
|
1. **Install spaceforge:** `pip install spaceforge`
|
|
690
690
|
2. **Create your plugin:** Start with the quick start example
|
|
691
|
-
3. **Test locally:** Use the
|
|
692
|
-
4. **Generate manifest:** Use the generate command to create plugin.yaml
|
|
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
693
|
5. **Upload to Spacelift:** Add your plugin manifest to your Spacelift account
|
|
694
694
|
|
|
695
695
|
For more advanced examples, see the [plugins](plugins/) directory in this repository.
|
|
@@ -109,15 +109,15 @@ The above configuration will create the following in a plan:
|
|
|
109
109
|
|
|
110
110
|
__parameters__ = [
|
|
111
111
|
Parameter(
|
|
112
|
-
name="Spacelift API
|
|
112
|
+
name="Spacelift API key ID",
|
|
113
113
|
id="spacelift_api_key_id",
|
|
114
|
-
description="The API
|
|
114
|
+
description="The API key that will trigger the stack previews",
|
|
115
115
|
required=True,
|
|
116
116
|
),
|
|
117
117
|
Parameter(
|
|
118
|
-
name="Spacelift API
|
|
118
|
+
name="Spacelift API key secret",
|
|
119
119
|
id="spacelift_api_key_secret",
|
|
120
|
-
description="The API
|
|
120
|
+
description="The API key secret that will trigger the stack previews",
|
|
121
121
|
required=True,
|
|
122
122
|
sensitive=True
|
|
123
123
|
),
|
|
@@ -126,7 +126,7 @@ The above configuration will create the following in a plan:
|
|
|
126
126
|
__contexts__ = [
|
|
127
127
|
Context(
|
|
128
128
|
name_prefix="Environment Manager",
|
|
129
|
-
description="Environment Manager
|
|
129
|
+
description="Environment Manager plugin",
|
|
130
130
|
hooks = {
|
|
131
131
|
"before_init": [
|
|
132
132
|
"mv /mnt/workspace/__environment_manager.tf /mnt/workspace/source/$TF_VAR_spacelift_project_root/__environment_manager.tf",
|
|
@@ -100,19 +100,19 @@ labels:
|
|
|
100
100
|
- management
|
|
101
101
|
- infrastructure
|
|
102
102
|
parameters:
|
|
103
|
-
- name: Spacelift API
|
|
104
|
-
description: The API
|
|
103
|
+
- name: Spacelift API key ID
|
|
104
|
+
description: The API key that will trigger the stack previews
|
|
105
105
|
sensitive: false
|
|
106
106
|
required: true
|
|
107
107
|
id: spacelift_api_key_id
|
|
108
|
-
- name: Spacelift API
|
|
109
|
-
description: The API
|
|
108
|
+
- name: Spacelift API key secret
|
|
109
|
+
description: The API key secret that will trigger the stack previews
|
|
110
110
|
sensitive: true
|
|
111
111
|
required: true
|
|
112
112
|
id: spacelift_api_key_secret
|
|
113
113
|
contexts:
|
|
114
114
|
- name_prefix: Environment Manager
|
|
115
|
-
description: Environment Manager
|
|
115
|
+
description: Environment Manager plugin
|
|
116
116
|
env:
|
|
117
117
|
- key: SPACELIFT_API_KEY_ID
|
|
118
118
|
value_from_parameter: spacelift_api_key_id
|
|
@@ -263,15 +263,15 @@ contexts:
|
|
|
263
263
|
|
|
264
264
|
__parameters__ = [
|
|
265
265
|
Parameter(
|
|
266
|
-
name="Spacelift API
|
|
266
|
+
name="Spacelift API key ID",
|
|
267
267
|
id="spacelift_api_key_id",
|
|
268
|
-
description="The API
|
|
268
|
+
description="The API key that will trigger the stack previews",
|
|
269
269
|
required=True,
|
|
270
270
|
),
|
|
271
271
|
Parameter(
|
|
272
|
-
name="Spacelift API
|
|
272
|
+
name="Spacelift API key secret",
|
|
273
273
|
id="spacelift_api_key_secret",
|
|
274
|
-
description="The API
|
|
274
|
+
description="The API key secret that will trigger the stack previews",
|
|
275
275
|
required=True,
|
|
276
276
|
sensitive=True
|
|
277
277
|
),
|
|
@@ -280,7 +280,7 @@ contexts:
|
|
|
280
280
|
__contexts__ = [
|
|
281
281
|
Context(
|
|
282
282
|
name_prefix="Environment Manager",
|
|
283
|
-
description="Environment Manager
|
|
283
|
+
description="Environment Manager plugin",
|
|
284
284
|
hooks = {
|
|
285
285
|
"before_init": [
|
|
286
286
|
"mv /mnt/workspace/__environment_manager.tf /mnt/workspace/source/$TF_VAR_spacelift_project_root/__environment_manager.tf",
|
|
@@ -458,7 +458,7 @@ contexts:
|
|
|
458
458
|
fi
|
|
459
459
|
|
|
460
460
|
cd /mnt/workspace/source/$TF_VAR_spacelift_project_root
|
|
461
|
-
spaceforge
|
|
461
|
+
spaceforge run --plugin-file /mnt/workspace/plugins/environment_manager/plugin.py before_init
|
|
462
462
|
sensitive: false
|
|
463
463
|
hooks:
|
|
464
464
|
before_init:
|
|
@@ -181,7 +181,7 @@ contexts:
|
|
|
181
181
|
export PATH="/mnt/workspace/plugins/plugin_binaries:$PATH"
|
|
182
182
|
|
|
183
183
|
cd /mnt/workspace/source/$TF_VAR_spacelift_project_root
|
|
184
|
-
spaceforge
|
|
184
|
+
spaceforge run --plugin-file /mnt/workspace/plugins/sops/plugin.py before_init
|
|
185
185
|
sensitive: false
|
|
186
186
|
hooks:
|
|
187
187
|
before_init:
|
|
@@ -299,7 +299,7 @@ contexts:
|
|
|
299
299
|
export PATH="/mnt/workspace/plugins/plugin_binaries:$PATH"
|
|
300
300
|
|
|
301
301
|
cd /mnt/workspace/source/$TF_VAR_spacelift_project_root
|
|
302
|
-
spaceforge
|
|
302
|
+
spaceforge run --plugin-file /mnt/workspace/plugins/wiz/plugin.py before_plan
|
|
303
303
|
sensitive: false
|
|
304
304
|
hooks:
|
|
305
305
|
before_init:
|
|
@@ -13,7 +13,7 @@ Spaceforge provides a simple, declarative way to create Spacelift plugins by inh
|
|
|
13
13
|
- **SpaceforgePlugin** (`plugin.py`) - Base class with hook methods and utilities
|
|
14
14
|
- **PluginRunner** (`runner.py`) - Executes hook methods, loads parameters from environment
|
|
15
15
|
- **PluginGenerator** (`generator.py`) - Analyzes Python plugins and generates `plugin.yaml`
|
|
16
|
-
- **CLI Interface** (`__main__.py`) - Click-based CLI with `generate` and `
|
|
16
|
+
- **CLI Interface** (`__main__.py`) - Click-based CLI with `generate` and `run` subcommands
|
|
17
17
|
- **Pydantic Dataclasses** (`cls.py`) - Type-safe data structures with validation
|
|
18
18
|
|
|
19
19
|
### Data Validation
|
|
@@ -242,10 +242,10 @@ python -m spaceforge generate my_plugin.py -o my_plugin.yaml
|
|
|
242
242
|
export API_KEY="your-key"
|
|
243
243
|
|
|
244
244
|
# Run specific hook
|
|
245
|
-
python -m spaceforge
|
|
245
|
+
python -m spaceforge run after_plan
|
|
246
246
|
|
|
247
247
|
# Run with specific plugin file
|
|
248
|
-
python -m spaceforge
|
|
248
|
+
python -m spaceforge run --plugin-file my_plugin.py before_apply
|
|
249
249
|
```
|
|
250
250
|
|
|
251
251
|
### Get Help
|
|
@@ -253,7 +253,7 @@ python -m spaceforge runner --plugin-file my_plugin.py before_apply
|
|
|
253
253
|
```bash
|
|
254
254
|
python -m spaceforge --help
|
|
255
255
|
python -m spaceforge generate --help
|
|
256
|
-
python -m spaceforge
|
|
256
|
+
python -m spaceforge run --help
|
|
257
257
|
```
|
|
258
258
|
|
|
259
259
|
## Generated YAML Structure
|
|
@@ -264,7 +264,7 @@ The framework automatically generates standard Spacelift plugin YAML:
|
|
|
264
264
|
|
|
265
265
|
1. **Requirements**: If your plugin has dependencies, create a `requirements.txt` file. The generator will automatically add a `before_init` hook to install them.
|
|
266
266
|
|
|
267
|
-
2. **Testing**: Use the
|
|
267
|
+
2. **Testing**: Use the `run` command to test individual hooks during development.
|
|
268
268
|
|
|
269
269
|
3. **Debugging**: Set `SPACELIFT_DEBUG=true` to enable debug logging.
|
|
270
270
|
|
|
@@ -6,7 +6,7 @@ import click
|
|
|
6
6
|
|
|
7
7
|
from spaceforge._version import get_version
|
|
8
8
|
from spaceforge.generator import generate_command
|
|
9
|
-
from spaceforge.runner import
|
|
9
|
+
from spaceforge.runner import run_command
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
@click.group()
|
|
@@ -21,7 +21,18 @@ def cli() -> None:
|
|
|
21
21
|
|
|
22
22
|
# Add subcommands
|
|
23
23
|
cli.add_command(generate_command)
|
|
24
|
-
cli.add_command(
|
|
24
|
+
cli.add_command(run_command)
|
|
25
|
+
|
|
26
|
+
# KLUDGE: Add a hidden "runner" alias to the "run" command for backward compatibility.
|
|
27
|
+
# It could be removed in the next major version.
|
|
28
|
+
runner_alias = click.Command(
|
|
29
|
+
callback=run_command.callback,
|
|
30
|
+
help=run_command.help,
|
|
31
|
+
hidden=True,
|
|
32
|
+
name="runner",
|
|
33
|
+
params=run_command.params,
|
|
34
|
+
)
|
|
35
|
+
cli.add_command(runner_alias)
|
|
25
36
|
|
|
26
37
|
|
|
27
38
|
def main() -> None:
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Version detection using setuptools_scm.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_version() -> str:
|
|
7
|
+
"""
|
|
8
|
+
Get the package version.
|
|
9
|
+
|
|
10
|
+
Uses setuptools_scm generated version file, which is created during build.
|
|
11
|
+
Falls back to a development version if not available.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
Version string
|
|
15
|
+
"""
|
|
16
|
+
# Try setuptools-scm generated version file (created during build)
|
|
17
|
+
try:
|
|
18
|
+
from ._version_scm import version # type: ignore[import-not-found]
|
|
19
|
+
|
|
20
|
+
return str(version)
|
|
21
|
+
except ImportError:
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
# Try setuptools-scm directly (works in development)
|
|
25
|
+
try:
|
|
26
|
+
from setuptools_scm import (
|
|
27
|
+
get_version as scm_get_version, # type: ignore[import-untyped]
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
result = scm_get_version(root="..", relative_to=__file__)
|
|
31
|
+
return str(result)
|
|
32
|
+
except ImportError:
|
|
33
|
+
pass
|
|
34
|
+
except Exception:
|
|
35
|
+
# setuptools_scm might fail in various ways, ignore
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
# Fall back to default version for development
|
|
39
|
+
return "0.1.0-dev"
|
|
@@ -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 = '1.1.
|
|
32
|
-
__version_tuple__ = version_tuple = (1, 1,
|
|
31
|
+
__version__ = version = '1.1.5'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 1, 5)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'gf45d03256'
|
|
@@ -84,7 +84,7 @@ class PluginRunner:
|
|
|
84
84
|
import click
|
|
85
85
|
|
|
86
86
|
|
|
87
|
-
@click.command(name="
|
|
87
|
+
@click.command(name="run")
|
|
88
88
|
@click.argument("hook_name")
|
|
89
89
|
@click.option(
|
|
90
90
|
"--plugin-file",
|
|
@@ -92,7 +92,7 @@ import click
|
|
|
92
92
|
type=click.Path(exists=True, dir_okay=False, readable=True),
|
|
93
93
|
help="Path to the plugin Python file (default: plugin.py)",
|
|
94
94
|
)
|
|
95
|
-
def
|
|
95
|
+
def run_command(hook_name: str, plugin_file: str) -> None:
|
|
96
96
|
"""Run a specific hook method from a plugin.
|
|
97
97
|
|
|
98
98
|
HOOK_NAME: Name of the hook method to execute (e.g., after_plan, before_apply)
|
|
@@ -88,7 +88,6 @@ class PluginExample(SpaceforgePlugin):
|
|
|
88
88
|
|
|
89
89
|
|
|
90
90
|
class TestPluginGenerator:
|
|
91
|
-
|
|
92
91
|
def setup_method(self) -> None:
|
|
93
92
|
"""Setup test fixtures."""
|
|
94
93
|
self.temp_dir = tempfile.mkdtemp()
|
|
@@ -534,7 +533,7 @@ class NotAPlugin:
|
|
|
534
533
|
after_plan_script = mf
|
|
535
534
|
break
|
|
536
535
|
assert after_plan_script is not None
|
|
537
|
-
assert "spaceforge
|
|
536
|
+
assert "spaceforge run" in after_plan_script.content
|
|
538
537
|
assert "after_plan" in after_plan_script.content
|
|
539
538
|
|
|
540
539
|
def test_generate_manifest(self) -> None:
|
|
@@ -6,7 +6,7 @@ from unittest.mock import Mock, patch
|
|
|
6
6
|
import pytest
|
|
7
7
|
|
|
8
8
|
from spaceforge.plugin import SpaceforgePlugin
|
|
9
|
-
from spaceforge.runner import PluginRunner,
|
|
9
|
+
from spaceforge.runner import PluginRunner, run_command
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class PluginForTesting(SpaceforgePlugin):
|
|
@@ -41,7 +41,6 @@ class PluginForTesting(SpaceforgePlugin):
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
class TestPluginRunner:
|
|
44
|
-
|
|
45
44
|
def setup_method(self) -> None:
|
|
46
45
|
"""Setup test fixtures."""
|
|
47
46
|
self.temp_dir = tempfile.mkdtemp()
|
|
@@ -293,7 +292,7 @@ class ClickTestPlugin(SpaceforgePlugin):
|
|
|
293
292
|
|
|
294
293
|
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
|
295
294
|
|
|
296
|
-
def
|
|
295
|
+
def test_run_command(self) -> None:
|
|
297
296
|
"""Test the Click runner command."""
|
|
298
297
|
from click.testing import CliRunner
|
|
299
298
|
|
|
@@ -305,7 +304,7 @@ class ClickTestPlugin(SpaceforgePlugin):
|
|
|
305
304
|
|
|
306
305
|
# Test the command using Click's test runner
|
|
307
306
|
result = cli_runner.invoke(
|
|
308
|
-
|
|
307
|
+
run_command, ["after_plan", "--plugin-file", self.test_plugin_path]
|
|
309
308
|
)
|
|
310
309
|
|
|
311
310
|
assert result.exit_code == 0
|
|
@@ -316,7 +315,7 @@ class ClickTestPlugin(SpaceforgePlugin):
|
|
|
316
315
|
# Verify run_hook was called with correct hook name
|
|
317
316
|
mock_runner.run_hook.assert_called_once_with("after_plan")
|
|
318
317
|
|
|
319
|
-
def
|
|
318
|
+
def test_run_command_default_plugin_file(self) -> None:
|
|
320
319
|
"""Test runner command with default plugin file."""
|
|
321
320
|
from click.testing import CliRunner
|
|
322
321
|
|
|
@@ -328,7 +327,7 @@ class ClickTestPlugin(SpaceforgePlugin):
|
|
|
328
327
|
|
|
329
328
|
# Test with explicit plugin file path
|
|
330
329
|
result = cli_runner.invoke(
|
|
331
|
-
|
|
330
|
+
run_command, ["before_apply", "--plugin-file", self.test_plugin_path]
|
|
332
331
|
)
|
|
333
332
|
|
|
334
333
|
assert result.exit_code == 0
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import os
|
|
4
4
|
from unittest.mock import Mock, patch
|
|
5
5
|
|
|
6
|
-
from spaceforge.runner import
|
|
6
|
+
from spaceforge.runner import run_command
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class TestRunnerClickCommand:
|
|
@@ -34,7 +34,7 @@ class ClickTestPlugin(SpaceforgePlugin):
|
|
|
34
34
|
mock_runner_class.return_value = mock_runner
|
|
35
35
|
|
|
36
36
|
result = cli_runner.invoke(
|
|
37
|
-
|
|
37
|
+
run_command, ["after_plan", "--plugin-file", click_plugin_path]
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
# Assert
|
|
@@ -60,7 +60,7 @@ class ClickTestPlugin(SpaceforgePlugin):
|
|
|
60
60
|
mock_runner_class.return_value = mock_runner
|
|
61
61
|
|
|
62
62
|
result = cli_runner.invoke(
|
|
63
|
-
|
|
63
|
+
run_command, ["before_apply", "--plugin-file", custom_plugin_path]
|
|
64
64
|
)
|
|
65
65
|
|
|
66
66
|
# Assert
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spaceforge
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.5
|
|
4
4
|
Summary: A Python framework for building Spacelift plugins
|
|
5
|
-
Home-page: https://github.com/spacelift-io/plugins
|
|
6
|
-
Author: Spacelift
|
|
7
5
|
Author-email: Spacelift <support@spacelift.io>
|
|
8
6
|
Maintainer-email: Spacelift <support@spacelift.io>
|
|
9
7
|
License: MIT
|
|
@@ -39,10 +37,7 @@ Requires-Dist: mypy; extra == "dev"
|
|
|
39
37
|
Requires-Dist: types-PyYAML; extra == "dev"
|
|
40
38
|
Requires-Dist: setuptools-scm[toml]>=6.2; extra == "dev"
|
|
41
39
|
Requires-Dist: autoflake; extra == "dev"
|
|
42
|
-
Dynamic: author
|
|
43
|
-
Dynamic: home-page
|
|
44
40
|
Dynamic: license-file
|
|
45
|
-
Dynamic: requires-python
|
|
46
41
|
|
|
47
42
|
# Spaceforge - Build Spacelift Plugins in Python
|
|
48
43
|
|
|
@@ -60,15 +55,15 @@ pip install spaceforge
|
|
|
60
55
|
|
|
61
56
|
### 1. Create Your Plugin
|
|
62
57
|
|
|
63
|
-
Create a Python file (e.g., `
|
|
58
|
+
Create a Python file (e.g., `plugin.py`) and inherit from `SpaceforgePlugin`:
|
|
64
59
|
|
|
65
60
|
```python
|
|
66
|
-
from spaceforge import SpaceforgePlugin, Parameter, Variable, Context
|
|
61
|
+
from spaceforge import SpaceforgePlugin, Parameter, Variable, Context
|
|
67
62
|
import os
|
|
68
63
|
|
|
69
64
|
class MyPlugin(SpaceforgePlugin):
|
|
70
65
|
# Plugin metadata
|
|
71
|
-
__plugin_name__ = "my-
|
|
66
|
+
__plugin_name__ = "my-plugin"
|
|
72
67
|
__version__ = "1.0.0"
|
|
73
68
|
__author__ = "Your Name"
|
|
74
69
|
__labels__ = ["security", "monitoring"] # Optional labels for categorization
|
|
@@ -127,7 +122,7 @@ class MyPlugin(SpaceforgePlugin):
|
|
|
127
122
|
Generate the Spacelift plugin YAML manifest:
|
|
128
123
|
|
|
129
124
|
```bash
|
|
130
|
-
spaceforge generate
|
|
125
|
+
spaceforge generate plugin.py
|
|
131
126
|
```
|
|
132
127
|
|
|
133
128
|
This creates `plugin.yaml` that you can upload to Spacelift.
|
|
@@ -142,7 +137,7 @@ export API_KEY="your-api-key"
|
|
|
142
137
|
export ENVIRONMENT="staging"
|
|
143
138
|
|
|
144
139
|
# Test the after_plan hook
|
|
145
|
-
spaceforge
|
|
140
|
+
spaceforge run after_plan
|
|
146
141
|
```
|
|
147
142
|
|
|
148
143
|
## Available Hooks
|
|
@@ -527,13 +522,13 @@ export API_KEY="test-key"
|
|
|
527
522
|
export TIMEOUT="60"
|
|
528
523
|
|
|
529
524
|
# Test specific hook
|
|
530
|
-
spaceforge
|
|
525
|
+
spaceforge run after_plan
|
|
531
526
|
|
|
532
527
|
# Test with specific plugin file
|
|
533
|
-
spaceforge
|
|
528
|
+
spaceforge run --plugin-file my_plugin.py before_apply
|
|
534
529
|
|
|
535
530
|
# Get help
|
|
536
|
-
spaceforge
|
|
531
|
+
spaceforge run --help
|
|
537
532
|
```
|
|
538
533
|
|
|
539
534
|
## Plugin Development Tips
|
|
@@ -577,7 +572,7 @@ def after_plan(self):
|
|
|
577
572
|
### 4. Testing and Debugging
|
|
578
573
|
|
|
579
574
|
- Set `SPACELIFT_DEBUG=true` to enable debug logging
|
|
580
|
-
- Use the
|
|
575
|
+
- Use the `run` command to test hooks during development
|
|
581
576
|
- Test with different parameter combinations
|
|
582
577
|
- Validate your generated YAML before uploading to Spacelift
|
|
583
578
|
|
|
@@ -718,7 +713,7 @@ spaceforge generate security_scanner.py
|
|
|
718
713
|
# Test locally
|
|
719
714
|
export API_TOKEN="your-token"
|
|
720
715
|
export SEVERITY_THRESHOLD="high"
|
|
721
|
-
spaceforge
|
|
716
|
+
spaceforge run after_plan
|
|
722
717
|
```
|
|
723
718
|
|
|
724
719
|
## Speeding up plugin execution
|
|
@@ -734,8 +729,8 @@ There are a few things you can do to speed up plugin execution.
|
|
|
734
729
|
|
|
735
730
|
1. **Install spaceforge:** `pip install spaceforge`
|
|
736
731
|
2. **Create your plugin:** Start with the quick start example
|
|
737
|
-
3. **Test locally:** Use the
|
|
738
|
-
4. **Generate manifest:** Use the generate command to create plugin.yaml
|
|
732
|
+
3. **Test locally:** Use the `run` command to test your hooks
|
|
733
|
+
4. **Generate manifest:** Use the `generate` command to create plugin.yaml
|
|
739
734
|
5. **Upload to Spacelift:** Add your plugin manifest to your Spacelift account
|
|
740
735
|
|
|
741
736
|
For more advanced examples, see the [plugins](plugins/) directory in this repository.
|
|
@@ -4,7 +4,6 @@ MANIFEST.in
|
|
|
4
4
|
README.md
|
|
5
5
|
go.mod
|
|
6
6
|
pyproject.toml
|
|
7
|
-
setup.py
|
|
8
7
|
templates.go
|
|
9
8
|
test.sh
|
|
10
9
|
.github/workflows/ci.yml
|
|
@@ -50,7 +49,6 @@ spaceforge.egg-info/PKG-INFO
|
|
|
50
49
|
spaceforge.egg-info/SOURCES.txt
|
|
51
50
|
spaceforge.egg-info/dependency_links.txt
|
|
52
51
|
spaceforge.egg-info/entry_points.txt
|
|
53
|
-
spaceforge.egg-info/not-zip-safe
|
|
54
52
|
spaceforge.egg-info/requires.txt
|
|
55
53
|
spaceforge.egg-info/top_level.txt
|
|
56
54
|
spaceforge/templates/binary_install.sh.j2
|
spaceforge-1.1.3/setup.py
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Setup script for spaceforge - Spacelift Plugin Framework
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from setuptools import setup, find_packages
|
|
6
|
-
|
|
7
|
-
with open("README.md", "r", encoding="utf-8") as fh:
|
|
8
|
-
long_description = fh.read()
|
|
9
|
-
|
|
10
|
-
setup(
|
|
11
|
-
name="spaceforge",
|
|
12
|
-
author="Spacelift",
|
|
13
|
-
author_email="support@spacelift.io",
|
|
14
|
-
description="A Python framework for building Spacelift plugins",
|
|
15
|
-
long_description=long_description,
|
|
16
|
-
long_description_content_type="text/markdown",
|
|
17
|
-
url="https://github.com/spacelift-io/plugins",
|
|
18
|
-
project_urls={
|
|
19
|
-
"Bug Reports": "https://github.com/spacelift-io/plugins/issues",
|
|
20
|
-
"Source": "https://github.com/spacelift-io/plugins",
|
|
21
|
-
},
|
|
22
|
-
packages=find_packages(),
|
|
23
|
-
package_data={
|
|
24
|
-
"spaceforge": ["schema.json"],
|
|
25
|
-
},
|
|
26
|
-
include_package_data=True,
|
|
27
|
-
classifiers=[
|
|
28
|
-
"Development Status :: 3 - Alpha",
|
|
29
|
-
"Intended Audience :: Developers",
|
|
30
|
-
"License :: OSI Approved :: MIT License",
|
|
31
|
-
"Operating System :: OS Independent",
|
|
32
|
-
"Programming Language :: Python :: 3",
|
|
33
|
-
"Programming Language :: Python :: 3.8",
|
|
34
|
-
"Programming Language :: Python :: 3.9",
|
|
35
|
-
"Programming Language :: Python :: 3.10",
|
|
36
|
-
"Programming Language :: Python :: 3.11",
|
|
37
|
-
"Programming Language :: Python :: 3.12",
|
|
38
|
-
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
39
|
-
"Topic :: System :: Systems Administration",
|
|
40
|
-
],
|
|
41
|
-
python_requires=">=3.9",
|
|
42
|
-
install_requires=[
|
|
43
|
-
"PyYAML>=6.0",
|
|
44
|
-
"click>=8.0.0",
|
|
45
|
-
"pydantic>=2.11.7",
|
|
46
|
-
"Jinja2>=3.1.0",
|
|
47
|
-
"mergedeep>=1.3.4",
|
|
48
|
-
],
|
|
49
|
-
extras_require={
|
|
50
|
-
"dev": [
|
|
51
|
-
"pytest>=6.0",
|
|
52
|
-
"pytest-cov",
|
|
53
|
-
"black",
|
|
54
|
-
"isort",
|
|
55
|
-
"mypy",
|
|
56
|
-
"autoflake"
|
|
57
|
-
],
|
|
58
|
-
},
|
|
59
|
-
entry_points={
|
|
60
|
-
"console_scripts": [
|
|
61
|
-
"spaceforge=spaceforge.__main__:main",
|
|
62
|
-
],
|
|
63
|
-
},
|
|
64
|
-
keywords="spacelift plugin framework infrastructure devops spaceforge",
|
|
65
|
-
zip_safe=False,
|
|
66
|
-
)
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Dynamic version detection from git tags.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import subprocess
|
|
6
|
-
from typing import Optional
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def get_git_version() -> Optional[str]:
|
|
10
|
-
"""
|
|
11
|
-
Get version from git tags.
|
|
12
|
-
|
|
13
|
-
Returns:
|
|
14
|
-
Version string (without 'v' prefix) or None if not available
|
|
15
|
-
"""
|
|
16
|
-
try:
|
|
17
|
-
# Try to get the current tag
|
|
18
|
-
result = subprocess.run(
|
|
19
|
-
["git", "describe", "--tags", "--exact-match"],
|
|
20
|
-
capture_output=True,
|
|
21
|
-
text=True,
|
|
22
|
-
check=True,
|
|
23
|
-
)
|
|
24
|
-
tag = result.stdout.strip()
|
|
25
|
-
# Remove 'v' prefix if present
|
|
26
|
-
return tag[1:] if tag.startswith("v") else tag
|
|
27
|
-
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
28
|
-
# Fall back to describe with commit info
|
|
29
|
-
try:
|
|
30
|
-
result = subprocess.run(
|
|
31
|
-
["git", "describe", "--tags", "--always"],
|
|
32
|
-
capture_output=True,
|
|
33
|
-
text=True,
|
|
34
|
-
check=True,
|
|
35
|
-
)
|
|
36
|
-
tag = result.stdout.strip()
|
|
37
|
-
# Remove 'v' prefix if present
|
|
38
|
-
return tag[1:] if tag.startswith("v") else tag
|
|
39
|
-
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
40
|
-
return None
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def get_version() -> str:
|
|
44
|
-
"""
|
|
45
|
-
Get the package version.
|
|
46
|
-
|
|
47
|
-
Tries git tags first, then setuptools-scm, falls back to default version.
|
|
48
|
-
|
|
49
|
-
Returns:
|
|
50
|
-
Version string
|
|
51
|
-
"""
|
|
52
|
-
# Try git version first
|
|
53
|
-
git_version = get_git_version()
|
|
54
|
-
if git_version:
|
|
55
|
-
return git_version
|
|
56
|
-
|
|
57
|
-
# Try setuptools-scm generated version file
|
|
58
|
-
try:
|
|
59
|
-
from ._version_scm import version # type: ignore[import-not-found]
|
|
60
|
-
|
|
61
|
-
return str(version)
|
|
62
|
-
except ImportError:
|
|
63
|
-
pass
|
|
64
|
-
|
|
65
|
-
# Try setuptools-scm directly
|
|
66
|
-
try:
|
|
67
|
-
from setuptools_scm import (
|
|
68
|
-
get_version as scm_get_version, # type: ignore[import-untyped]
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
result = scm_get_version(root="..", relative_to=__file__)
|
|
72
|
-
return str(result)
|
|
73
|
-
except ImportError:
|
|
74
|
-
pass
|
|
75
|
-
except Exception:
|
|
76
|
-
# setuptools_scm might fail in various ways, ignore
|
|
77
|
-
pass
|
|
78
|
-
|
|
79
|
-
# Fall back to default version for development
|
|
80
|
-
return "0.1.0-dev"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|