spaceforge 0.0.5__tar.gz → 0.0.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.
Files changed (57) hide show
  1. {spaceforge-0.0.5 → spaceforge-0.0.7}/PKG-INFO +2 -1
  2. spaceforge-0.0.7/plugins/enviroment_manager/plugin.py +248 -0
  3. spaceforge-0.0.7/plugins/enviroment_manager/plugin.yaml +416 -0
  4. spaceforge-0.0.7/plugins/enviroment_manager/requirements.txt +1 -0
  5. {spaceforge-0.0.5 → spaceforge-0.0.7}/plugins/infracost/plugin.py +1 -1
  6. {spaceforge-0.0.5 → spaceforge-0.0.7}/plugins/infracost/plugin.yaml +2 -2
  7. {spaceforge-0.0.5 → spaceforge-0.0.7}/plugins/sops/plugin.py +1 -1
  8. {spaceforge-0.0.5 → spaceforge-0.0.7}/plugins/sops/plugin.yaml +5 -3
  9. {spaceforge-0.0.5 → spaceforge-0.0.7}/plugins/wiz/plugin.py +25 -5
  10. {spaceforge-0.0.5 → spaceforge-0.0.7}/plugins/wiz/plugin.yaml +52 -10
  11. {spaceforge-0.0.5 → spaceforge-0.0.7}/pyproject.toml +1 -0
  12. {spaceforge-0.0.5 → spaceforge-0.0.7}/setup.py +1 -0
  13. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/_version_scm.py +3 -3
  14. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/cls.py +4 -1
  15. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/generator.py +16 -8
  16. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/plugin.py +22 -17
  17. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/schema.json +7 -0
  18. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/templates/ensure_spaceforge_and_run.sh.j2 +3 -1
  19. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/test_generator.py +2 -2
  20. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/test_plugin.py +7 -7
  21. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge.egg-info/PKG-INFO +2 -1
  22. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge.egg-info/SOURCES.txt +3 -0
  23. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge.egg-info/requires.txt +1 -0
  24. {spaceforge-0.0.5 → spaceforge-0.0.7}/.github/workflows/ci.yml +0 -0
  25. {spaceforge-0.0.5 → spaceforge-0.0.7}/.github/workflows/release.yml +0 -0
  26. {spaceforge-0.0.5 → spaceforge-0.0.7}/.gitignore +0 -0
  27. {spaceforge-0.0.5 → spaceforge-0.0.7}/LICENSE +0 -0
  28. {spaceforge-0.0.5 → spaceforge-0.0.7}/MANIFEST.in +0 -0
  29. {spaceforge-0.0.5 → spaceforge-0.0.7}/README.md +0 -0
  30. {spaceforge-0.0.5 → spaceforge-0.0.7}/go.mod +0 -0
  31. {spaceforge-0.0.5 → spaceforge-0.0.7}/plugins/sops/requirements.txt +0 -0
  32. {spaceforge-0.0.5 → spaceforge-0.0.7}/setup.cfg +0 -0
  33. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/README.md +0 -0
  34. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/__init__.py +0 -0
  35. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/__main__.py +0 -0
  36. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/_version.py +0 -0
  37. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/conftest.py +0 -0
  38. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/runner.py +0 -0
  39. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/templates/binary_install.sh.j2 +0 -0
  40. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/test_cls.py +0 -0
  41. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/test_generator_binaries.py +0 -0
  42. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/test_generator_core.py +0 -0
  43. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/test_generator_hooks.py +0 -0
  44. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/test_generator_parameters.py +0 -0
  45. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/test_plugin_file_operations.py +0 -0
  46. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/test_plugin_hooks.py +0 -0
  47. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/test_plugin_inheritance.py +0 -0
  48. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/test_runner.py +0 -0
  49. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/test_runner_cli.py +0 -0
  50. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/test_runner_core.py +0 -0
  51. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge/test_runner_execution.py +0 -0
  52. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge.egg-info/dependency_links.txt +0 -0
  53. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge.egg-info/entry_points.txt +0 -0
  54. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge.egg-info/not-zip-safe +0 -0
  55. {spaceforge-0.0.5 → spaceforge-0.0.7}/spaceforge.egg-info/top_level.txt +0 -0
  56. {spaceforge-0.0.5 → spaceforge-0.0.7}/templates.go +0 -0
  57. {spaceforge-0.0.5 → spaceforge-0.0.7}/test.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spaceforge
3
- Version: 0.0.5
3
+ Version: 0.0.7
4
4
  Summary: A Python framework for building Spacelift plugins
5
5
  Home-page: https://github.com/spacelift-io/plugins
6
6
  Author: Spacelift
@@ -29,6 +29,7 @@ Requires-Dist: PyYAML>=6.0
29
29
  Requires-Dist: click>=8.0.0
30
30
  Requires-Dist: pydantic>=2.11.7
31
31
  Requires-Dist: Jinja2>=3.1.0
32
+ Requires-Dist: mergedeep>=1.3.4
32
33
  Provides-Extra: dev
33
34
  Requires-Dist: pytest>=6.0; extra == "dev"
34
35
  Requires-Dist: pytest-cov; extra == "dev"
@@ -0,0 +1,248 @@
1
+ from spaceforge import SpaceforgePlugin, MountedFile, Context
2
+
3
+ import yaml
4
+ import os
5
+
6
+ class EnvironmentManagerPlugin(SpaceforgePlugin):
7
+ """
8
+ # Spacelift Environment Variable Manager
9
+ This plugin allows you to manage Spacelift environment variables using a centralized YAML configuration file for multiple stacks.
10
+
11
+ ## Features
12
+ - Centralized management of environment variables across multiple stacks.
13
+ - Supports sensitive variables.
14
+ - Preview of environment variable changes across stacks before applying changes here.
15
+
16
+ **Hint:** Use this in combination with the `sops` plugin to manage secrets in your environment variables.
17
+
18
+ ## Usage
19
+ Add this plugin to an **administrative** stack in your Spacelift account, the stack **must** have the `Spacelift` OpenTofu/Terraform provider configured.
20
+ example:
21
+ ```hcl
22
+ terraform {
23
+ required_providers {
24
+ spacelift = {
25
+ source = "spacelift-io/spacelift"
26
+ version = "~> 1.0"
27
+ }
28
+ }
29
+ }
30
+
31
+ provider "spacelift" {}
32
+ ```
33
+
34
+ 1. **YAML Configuration**: Environment variables are defined in `vars.yaml` using the following structure:
35
+ ```yaml
36
+ stack-id:
37
+ - name: VARIABLE_NAME
38
+ value: variable_value
39
+ sensitive: false
40
+ ```
41
+
42
+ 2. **Terraform Processing**: The main Terraform configuration:
43
+ - Reads and parses the YAML file using `yamldecode(file("vars.yaml"))`
44
+ - Flattens the structure into a list of variables with their associated stack IDs
45
+ - Creates `spacelift_environment_variable` resources for each variable
46
+
47
+ 3. **Stack Association**: Variables are automatically associated with their respective stacks based on the stack ids defined in the YAML file.
48
+
49
+ **note:** when using this plugin, if you open a PR to your variables file, the changes of the child stacks will be previewed and linked.
50
+
51
+ ## Example Configuration
52
+
53
+ ### vars.yaml
54
+ ```yaml
55
+ env-var-yaml-1:
56
+ - name: KUBECONFIG
57
+ value: /home/joey/.kube/config
58
+ sensitive: false
59
+
60
+ env-var-yaml-2:
61
+ - name: AWS_PROFILE
62
+ value: test
63
+ sensitive: false
64
+ - name: MY_AWESOME_SECRET
65
+ value: HelloWorld
66
+ sensitive: true
67
+ ```
68
+
69
+ The above configuration will create the following in a plan:
70
+
71
+ ```ansi
72
+ # spacelift_environment_variable.this["env-var-yaml-1_KUBECONFIG"] will be created
73
+ + resource "spacelift_environment_variable" "this" {
74
+ + checksum = (known after apply)
75
+ + id = (known after apply)
76
+ + name = "KUBECONFIG"
77
+ + stack_id = "env-var-yaml-1"
78
+ + value = (sensitive value)
79
+ + write_only = false
80
+ }
81
+
82
+ # spacelift_environment_variable.this["env-var-yaml-2_AWS_PROFILE"] will be created
83
+ + resource "spacelift_environment_variable" "this" {
84
+ + checksum = (known after apply)
85
+ + id = (known after apply)
86
+ + name = "AWS_PROFILE"
87
+ + stack_id = "env-var-yaml-2"
88
+ + value = (sensitive value)
89
+ + write_only = false
90
+ }
91
+
92
+ # spacelift_environment_variable.this["env-var-yaml-2_MY_AWESOME_SECRET"] will be created
93
+ + resource "spacelift_environment_variable" "this" {
94
+ + checksum = (known after apply)
95
+ + id = (known after apply)
96
+ + name = "MY_AWESOME_SECRET"
97
+ + stack_id = "env-var-yaml-2"
98
+ + value = (sensitive value)
99
+ + write_only = true
100
+ }
101
+ ```
102
+ """
103
+
104
+ # Plugin metadata
105
+ __plugin_name__ = "Environment Manager"
106
+ __labels__ = ["management", "infrastructure"]
107
+ __version__ = "1.0.0"
108
+ __author__ = "Spacelift Team"
109
+
110
+ __contexts__ = [
111
+ Context(
112
+ name_prefix="Environment Manager",
113
+ description="Environment Manager Plugin",
114
+ hooks = {
115
+ "before_init": [
116
+ "mv /mnt/workspace/__environment_manager.tf /mnt/workspace/source/$TF_VAR_spacelift_project_root/__environment_manager.tf",
117
+ ]
118
+ },
119
+ mounted_files=[
120
+ MountedFile(
121
+ path="__environment_manager.tf",
122
+ content="""
123
+ locals {
124
+ __env_vars = {
125
+ for obj in flatten([
126
+ for stack_id, values in yamldecode(file("${path.module}/vars.yaml")) : [
127
+ for v in values : {
128
+ stack_id = stack_id
129
+ name = v.name
130
+ value = v.value
131
+ write_only = v.write_only
132
+ }
133
+ ]
134
+ ]) : "${obj.stack_id}_${obj.name}" => obj
135
+ }
136
+ }
137
+
138
+ resource "spacelift_environment_variable" "__this" {
139
+ for_each = local.__env_vars
140
+
141
+ stack_id = each.value.stack_id
142
+ name = each.value.name
143
+ value = each.value.value
144
+ write_only = each.value.write_only
145
+ }
146
+ """
147
+ )
148
+ ]
149
+ )
150
+ ]
151
+
152
+ def load_yaml_file(self, file_path):
153
+ """Load YAML file and return parsed content"""
154
+ try:
155
+ with open(file_path, 'r') as file:
156
+ return yaml.safe_load(file)
157
+ except FileNotFoundError:
158
+ self.logger.error(f"Error: File {file_path} not found")
159
+ exit(1)
160
+ except yaml.YAMLError as e:
161
+ self.logger.error(f"Error parsing YAML: {e}")
162
+ exit(1)
163
+
164
+ def convert_to_runtime_config(self, yaml_data):
165
+ """Convert YAML data to Spacelift runtime config format"""
166
+ runtime_config = {}
167
+
168
+ for stack_id, env_vars in yaml_data.items():
169
+ if not isinstance(env_vars, list):
170
+ self.logger.error(f"Error: Environment variables for stack '{stack_id}' must be a list")
171
+ exit(1)
172
+
173
+ runtime_config[stack_id] = {
174
+ "environment": {}
175
+ }
176
+
177
+ for var in env_vars:
178
+ runtime_config[stack_id]["environment"][var["name"]] = var["value"]
179
+
180
+ return runtime_config
181
+
182
+ def trigger_stack_previews(self, runtime_config: dict):
183
+ """Trigger stack previews using Spacelift API"""
184
+
185
+ markdown = []
186
+
187
+ for stack_id, env in runtime_config.items():
188
+
189
+ # Get the current tracked sha from the stack
190
+ query = "{ stack(id: \"" + stack_id + "\") { trackedCommit { hash } } }"
191
+ response = self.query_api(query)
192
+ if "errors" in response:
193
+ self.logger.error("Error fetching stack tracked commit:", response["errors"])
194
+ continue
195
+
196
+ # Ensure we have a tracked commit
197
+ try:
198
+ tracked_commit = response["data"]["stack"]["trackedCommit"]["hash"]
199
+ except TypeError:
200
+ tracked_commit = None
201
+ if tracked_commit is None:
202
+ self.logger.error(f"Stack {stack_id} has no tracked commit. Skipping.")
203
+ continue
204
+
205
+ # Trigger the stack preview with the current tracked commit SHA
206
+ query = """
207
+ mutation TriggerStackPreview($stack: ID!, $commitSHA: String!, $runtimeConfig: String!) {
208
+ runTrigger(stack: $stack, commitSha: $commitSHA, runType: PROPOSED, runtimeConfig: { yaml: $runtimeConfig }) {
209
+ id
210
+ }
211
+ }
212
+ """
213
+
214
+ variables = {
215
+ "stack": stack_id,
216
+ "commitSHA": tracked_commit,
217
+ "runtimeConfig": yaml.dump(env)
218
+ }
219
+
220
+ response = self.query_api(query, variables)
221
+ if "errors" in response:
222
+ self.logger.error(f"Error triggering stack preview for {stack_id}:", response["errors"])
223
+ else:
224
+ url = f"https://{self.spacelift_domain}/stack/{stack_id}/run/{response['data']['runTrigger']['id']}"
225
+ markdown.append(f"- Triggered [stack preview]({url}) for {stack_id} with commit {tracked_commit}.")
226
+
227
+ if len(markdown) > 0:
228
+ mdown = "# Stack Previews Triggered\n\n" + "\n".join(markdown)
229
+ success = self.send_markdown(mdown)
230
+ if not success:
231
+ self.logger.error("Failed to send markdown message with stack previews.")
232
+ self.logger.info(mdown)
233
+
234
+
235
+ def before_init(self):
236
+ # ensure we are in a proposed run
237
+ if os.getenv("TF_VAR_spacelift_run_type") != "PROPOSED":
238
+ # This script should only be run in a proposed run context.
239
+ return
240
+
241
+ # Load YAML data
242
+ yaml_data = self.load_yaml_file("vars.yaml")
243
+
244
+ # Convert to runtime config
245
+ runtime_config = self.convert_to_runtime_config(yaml_data)
246
+
247
+ # Trigger stack previews
248
+ self.trigger_stack_previews(runtime_config)
@@ -0,0 +1,416 @@
1
+ name: Environment Manager
2
+ version: 1.0.0
3
+ description: |-
4
+ # Spacelift Environment Variable Manager
5
+ This plugin allows you to manage Spacelift environment variables using a centralized YAML configuration file for multiple stacks.
6
+
7
+ ## Features
8
+ - Centralized management of environment variables across multiple stacks.
9
+ - Supports sensitive variables.
10
+ - Preview of environment variable changes across stacks before applying changes here.
11
+
12
+ **Hint:** Use this in combination with the `sops` plugin to manage secrets in your environment variables.
13
+
14
+ ## Usage
15
+ Add this plugin to an **administrative** stack in your Spacelift account, the stack **must** have the `Spacelift` OpenTofu/Terraform provider configured.
16
+ example:
17
+ ```hcl
18
+ terraform {
19
+ required_providers {
20
+ spacelift = {
21
+ source = "spacelift-io/spacelift"
22
+ version = "~> 1.0"
23
+ }
24
+ }
25
+ }
26
+
27
+ provider "spacelift" {}
28
+ ```
29
+
30
+ 1. **YAML Configuration**: Environment variables are defined in `vars.yaml` using the following structure:
31
+ ```yaml
32
+ stack-id:
33
+ - name: VARIABLE_NAME
34
+ value: variable_value
35
+ sensitive: false
36
+ ```
37
+
38
+ 2. **Terraform Processing**: The main Terraform configuration:
39
+ - Reads and parses the YAML file using `yamldecode(file("vars.yaml"))`
40
+ - Flattens the structure into a list of variables with their associated stack IDs
41
+ - Creates `spacelift_environment_variable` resources for each variable
42
+
43
+ 3. **Stack Association**: Variables are automatically associated with their respective stacks based on the stack ids defined in the YAML file.
44
+
45
+ **note:** when using this plugin, if you open a PR to your variables file, the changes of the child stacks will be previewed and linked.
46
+
47
+ ## Example Configuration
48
+
49
+ ### vars.yaml
50
+ ```yaml
51
+ env-var-yaml-1:
52
+ - name: KUBECONFIG
53
+ value: /home/joey/.kube/config
54
+ sensitive: false
55
+
56
+ env-var-yaml-2:
57
+ - name: AWS_PROFILE
58
+ value: test
59
+ sensitive: false
60
+ - name: MY_AWESOME_SECRET
61
+ value: HelloWorld
62
+ sensitive: true
63
+ ```
64
+
65
+ The above configuration will create the following in a plan:
66
+
67
+ ```ansi
68
+ # spacelift_environment_variable.this["env-var-yaml-1_KUBECONFIG"] will be created
69
+ + resource "spacelift_environment_variable" "this" {
70
+ + checksum = (known after apply)
71
+ + id = (known after apply)
72
+ + name = "KUBECONFIG"
73
+ + stack_id = "env-var-yaml-1"
74
+ + value = (sensitive value)
75
+ + write_only = false
76
+ }
77
+
78
+ # spacelift_environment_variable.this["env-var-yaml-2_AWS_PROFILE"] will be created
79
+ + resource "spacelift_environment_variable" "this" {
80
+ + checksum = (known after apply)
81
+ + id = (known after apply)
82
+ + name = "AWS_PROFILE"
83
+ + stack_id = "env-var-yaml-2"
84
+ + value = (sensitive value)
85
+ + write_only = false
86
+ }
87
+
88
+ # spacelift_environment_variable.this["env-var-yaml-2_MY_AWESOME_SECRET"] will be created
89
+ + resource "spacelift_environment_variable" "this" {
90
+ + checksum = (known after apply)
91
+ + id = (known after apply)
92
+ + name = "MY_AWESOME_SECRET"
93
+ + stack_id = "env-var-yaml-2"
94
+ + value = (sensitive value)
95
+ + write_only = true
96
+ }
97
+ ```
98
+ author: Spacelift Team
99
+ labels:
100
+ - management
101
+ - infrastructure
102
+ contexts:
103
+ - name_prefix: Environment Manager
104
+ description: Environment Manager Plugin
105
+ env: []
106
+ mounted_files:
107
+ - path: __environment_manager.tf
108
+ content: |-
109
+ locals {
110
+ __env_vars = {
111
+ for obj in flatten([
112
+ for stack_id, values in yamldecode(file("${path.module}/vars.yaml")) : [
113
+ for v in values : {
114
+ stack_id = stack_id
115
+ name = v.name
116
+ value = v.value
117
+ write_only = v.write_only
118
+ }
119
+ ]
120
+ ]) : "${obj.stack_id}_${obj.name}" => obj
121
+ }
122
+ }
123
+
124
+ resource "spacelift_environment_variable" "__this" {
125
+ for_each = local.__env_vars
126
+
127
+ stack_id = each.value.stack_id
128
+ name = each.value.name
129
+ value = each.value.value
130
+ write_only = each.value.write_only
131
+ }
132
+ sensitive: false
133
+ - path: /mnt/workspace/plugins/environment_manager/requirements.txt
134
+ content: pyyaml==6.0.2
135
+ sensitive: false
136
+ - path: /mnt/workspace/plugins/environment_manager/plugin.py
137
+ content: |-
138
+ from spaceforge import SpaceforgePlugin, MountedFile, Context
139
+
140
+ import yaml
141
+ import os
142
+
143
+ class EnvironmentManagerPlugin(SpaceforgePlugin):
144
+ """
145
+ # Spacelift Environment Variable Manager
146
+ This plugin allows you to manage Spacelift environment variables using a centralized YAML configuration file for multiple stacks.
147
+
148
+ ## Features
149
+ - Centralized management of environment variables across multiple stacks.
150
+ - Supports sensitive variables.
151
+ - Preview of environment variable changes across stacks before applying changes here.
152
+
153
+ **Hint:** Use this in combination with the `sops` plugin to manage secrets in your environment variables.
154
+
155
+ ## Usage
156
+ Add this plugin to an **administrative** stack in your Spacelift account, the stack **must** have the `Spacelift` OpenTofu/Terraform provider configured.
157
+ example:
158
+ ```hcl
159
+ terraform {
160
+ required_providers {
161
+ spacelift = {
162
+ source = "spacelift-io/spacelift"
163
+ version = "~> 1.0"
164
+ }
165
+ }
166
+ }
167
+
168
+ provider "spacelift" {}
169
+ ```
170
+
171
+ 1. **YAML Configuration**: Environment variables are defined in `vars.yaml` using the following structure:
172
+ ```yaml
173
+ stack-id:
174
+ - name: VARIABLE_NAME
175
+ value: variable_value
176
+ sensitive: false
177
+ ```
178
+
179
+ 2. **Terraform Processing**: The main Terraform configuration:
180
+ - Reads and parses the YAML file using `yamldecode(file("vars.yaml"))`
181
+ - Flattens the structure into a list of variables with their associated stack IDs
182
+ - Creates `spacelift_environment_variable` resources for each variable
183
+
184
+ 3. **Stack Association**: Variables are automatically associated with their respective stacks based on the stack ids defined in the YAML file.
185
+
186
+ **note:** when using this plugin, if you open a PR to your variables file, the changes of the child stacks will be previewed and linked.
187
+
188
+ ## Example Configuration
189
+
190
+ ### vars.yaml
191
+ ```yaml
192
+ env-var-yaml-1:
193
+ - name: KUBECONFIG
194
+ value: /home/joey/.kube/config
195
+ sensitive: false
196
+
197
+ env-var-yaml-2:
198
+ - name: AWS_PROFILE
199
+ value: test
200
+ sensitive: false
201
+ - name: MY_AWESOME_SECRET
202
+ value: HelloWorld
203
+ sensitive: true
204
+ ```
205
+
206
+ The above configuration will create the following in a plan:
207
+
208
+ ```ansi
209
+ # spacelift_environment_variable.this["env-var-yaml-1_KUBECONFIG"] will be created
210
+ + resource "spacelift_environment_variable" "this" {
211
+ + checksum = (known after apply)
212
+ + id = (known after apply)
213
+ + name = "KUBECONFIG"
214
+ + stack_id = "env-var-yaml-1"
215
+ + value = (sensitive value)
216
+ + write_only = false
217
+ }
218
+
219
+ # spacelift_environment_variable.this["env-var-yaml-2_AWS_PROFILE"] will be created
220
+ + resource "spacelift_environment_variable" "this" {
221
+ + checksum = (known after apply)
222
+ + id = (known after apply)
223
+ + name = "AWS_PROFILE"
224
+ + stack_id = "env-var-yaml-2"
225
+ + value = (sensitive value)
226
+ + write_only = false
227
+ }
228
+
229
+ # spacelift_environment_variable.this["env-var-yaml-2_MY_AWESOME_SECRET"] will be created
230
+ + resource "spacelift_environment_variable" "this" {
231
+ + checksum = (known after apply)
232
+ + id = (known after apply)
233
+ + name = "MY_AWESOME_SECRET"
234
+ + stack_id = "env-var-yaml-2"
235
+ + value = (sensitive value)
236
+ + write_only = true
237
+ }
238
+ ```
239
+ """
240
+
241
+ # Plugin metadata
242
+ __plugin_name__ = "Environment Manager"
243
+ __labels__ = ["management", "infrastructure"]
244
+ __version__ = "1.0.0"
245
+ __author__ = "Spacelift Team"
246
+
247
+ __contexts__ = [
248
+ Context(
249
+ name_prefix="Environment Manager",
250
+ description="Environment Manager Plugin",
251
+ hooks = {
252
+ "before_init": [
253
+ "mv /mnt/workspace/__environment_manager.tf /mnt/workspace/source/$TF_VAR_spacelift_project_root/__environment_manager.tf",
254
+ ]
255
+ },
256
+ mounted_files=[
257
+ MountedFile(
258
+ path="__environment_manager.tf",
259
+ content="""
260
+ locals {
261
+ __env_vars = {
262
+ for obj in flatten([
263
+ for stack_id, values in yamldecode(file("${path.module}/vars.yaml")) : [
264
+ for v in values : {
265
+ stack_id = stack_id
266
+ name = v.name
267
+ value = v.value
268
+ write_only = v.write_only
269
+ }
270
+ ]
271
+ ]) : "${obj.stack_id}_${obj.name}" => obj
272
+ }
273
+ }
274
+
275
+ resource "spacelift_environment_variable" "__this" {
276
+ for_each = local.__env_vars
277
+
278
+ stack_id = each.value.stack_id
279
+ name = each.value.name
280
+ value = each.value.value
281
+ write_only = each.value.write_only
282
+ }
283
+ """
284
+ )
285
+ ]
286
+ )
287
+ ]
288
+
289
+ def load_yaml_file(self, file_path):
290
+ """Load YAML file and return parsed content"""
291
+ try:
292
+ with open(file_path, 'r') as file:
293
+ return yaml.safe_load(file)
294
+ except FileNotFoundError:
295
+ self.logger.error(f"Error: File {file_path} not found")
296
+ exit(1)
297
+ except yaml.YAMLError as e:
298
+ self.logger.error(f"Error parsing YAML: {e}")
299
+ exit(1)
300
+
301
+ def convert_to_runtime_config(self, yaml_data):
302
+ """Convert YAML data to Spacelift runtime config format"""
303
+ runtime_config = {}
304
+
305
+ for stack_id, env_vars in yaml_data.items():
306
+ if not isinstance(env_vars, list):
307
+ self.logger.error(f"Error: Environment variables for stack '{stack_id}' must be a list")
308
+ exit(1)
309
+
310
+ runtime_config[stack_id] = {
311
+ "environment": {}
312
+ }
313
+
314
+ for var in env_vars:
315
+ runtime_config[stack_id]["environment"][var["name"]] = var["value"]
316
+
317
+ return runtime_config
318
+
319
+ def trigger_stack_previews(self, runtime_config: dict):
320
+ """Trigger stack previews using Spacelift API"""
321
+
322
+ markdown = []
323
+
324
+ for stack_id, env in runtime_config.items():
325
+
326
+ # Get the current tracked sha from the stack
327
+ query = "{ stack(id: \"" + stack_id + "\") { trackedCommit { hash } } }"
328
+ response = self.query_api(query)
329
+ if "errors" in response:
330
+ self.logger.error("Error fetching stack tracked commit:", response["errors"])
331
+ continue
332
+
333
+ # Ensure we have a tracked commit
334
+ try:
335
+ tracked_commit = response["data"]["stack"]["trackedCommit"]["hash"]
336
+ except TypeError:
337
+ tracked_commit = None
338
+ if tracked_commit is None:
339
+ self.logger.error(f"Stack {stack_id} has no tracked commit. Skipping.")
340
+ continue
341
+
342
+ # Trigger the stack preview with the current tracked commit SHA
343
+ query = """
344
+ mutation TriggerStackPreview($stack: ID!, $commitSHA: String!, $runtimeConfig: String!) {
345
+ runTrigger(stack: $stack, commitSha: $commitSHA, runType: PROPOSED, runtimeConfig: { yaml: $runtimeConfig }) {
346
+ id
347
+ }
348
+ }
349
+ """
350
+
351
+ variables = {
352
+ "stack": stack_id,
353
+ "commitSHA": tracked_commit,
354
+ "runtimeConfig": yaml.dump(env)
355
+ }
356
+
357
+ response = self.query_api(query, variables)
358
+ if "errors" in response:
359
+ self.logger.error(f"Error triggering stack preview for {stack_id}:", response["errors"])
360
+ else:
361
+ url = f"https://{self.spacelift_domain}/stack/{stack_id}/run/{response['data']['runTrigger']['id']}"
362
+ markdown.append(f"- Triggered [stack preview]({url}) for {stack_id} with commit {tracked_commit}.")
363
+
364
+ if len(markdown) > 0:
365
+ mdown = "# Stack Previews Triggered\n\n" + "\n".join(markdown)
366
+ success = self.send_markdown(mdown)
367
+ if not success:
368
+ self.logger.error("Failed to send markdown message with stack previews.")
369
+ self.logger.info(mdown)
370
+
371
+
372
+ def before_init(self):
373
+ # ensure we are in a proposed run
374
+ if os.getenv("TF_VAR_spacelift_run_type") != "PROPOSED":
375
+ # This script should only be run in a proposed run context.
376
+ return
377
+
378
+ # Load YAML data
379
+ yaml_data = self.load_yaml_file("vars.yaml")
380
+
381
+ # Convert to runtime config
382
+ runtime_config = self.convert_to_runtime_config(yaml_data)
383
+
384
+ # Trigger stack previews
385
+ self.trigger_stack_previews(runtime_config)
386
+ sensitive: false
387
+ - path: /mnt/workspace/plugins/environment_manager/before_init.sh
388
+ content: |-
389
+ #!/bin/sh
390
+
391
+ set -e
392
+
393
+ cd /mnt/workspace/plugins/environment_manager
394
+
395
+ if [ ! -d "./venv" ]; then
396
+ python -m venv ./venv
397
+ fi
398
+ . venv/bin/activate
399
+
400
+ if ! command -v spaceforge; then
401
+ pip install spaceforge
402
+ fi
403
+
404
+ if [ -f requirements.txt ] && [ ! -f .spaceforge_installed_requirements ]; then
405
+ pip install -r requirements.txt
406
+ touch .spaceforge_installed_requirements
407
+ fi
408
+
409
+ cd /mnt/workspace/source/$TF_VAR_spacelift_project_root
410
+ spaceforge runner --plugin-file /mnt/workspace/plugins/environment_manager/plugin.py before_init
411
+ sensitive: false
412
+ hooks:
413
+ before_init:
414
+ - mv /mnt/workspace/__environment_manager.tf /mnt/workspace/source/$TF_VAR_spacelift_project_root/__environment_manager.tf
415
+ - mkdir -p /mnt/workspace/plugins/environment_manager
416
+ - chmod +x /mnt/workspace/plugins/environment_manager/before_init.sh && /mnt/workspace/plugins/environment_manager/before_init.sh
@@ -0,0 +1 @@
1
+ pyyaml==6.0.2
@@ -6,7 +6,7 @@ class InfracostPlugin(SpaceforgePlugin):
6
6
  """
7
7
 
8
8
  # Plugin metadata
9
- __plugin_name__ = "infracost"
9
+ __plugin_name__ = "Infracost"
10
10
  __labels__ = ["cost estimation", "infrastructure"]
11
11
  __version__ = "1.0.0"
12
12
  __author__ = "Spacelift Team"