spaceforge 1.1.3__tar.gz → 1.1.4__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 (61) hide show
  1. {spaceforge-1.1.3 → spaceforge-1.1.4}/PKG-INFO +13 -13
  2. {spaceforge-1.1.3 → spaceforge-1.1.4}/README.md +12 -12
  3. {spaceforge-1.1.3 → spaceforge-1.1.4}/plugins/enviroment_manager/plugin.py +5 -5
  4. {spaceforge-1.1.3 → spaceforge-1.1.4}/plugins/enviroment_manager/plugin.yaml +11 -11
  5. {spaceforge-1.1.3 → spaceforge-1.1.4}/plugins/sops/plugin.yaml +1 -1
  6. spaceforge-1.1.4/plugins/tflint/plugin.py +195 -0
  7. spaceforge-1.1.4/plugins/tflint/plugin.yaml +363 -0
  8. {spaceforge-1.1.3 → spaceforge-1.1.4}/plugins/wiz/plugin.yaml +1 -1
  9. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/README.md +5 -5
  10. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/__main__.py +13 -2
  11. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/_version_scm.py +3 -3
  12. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/runner.py +2 -2
  13. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/templates/ensure_spaceforge_and_run.sh.j2 +1 -1
  14. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/test_generator.py +1 -2
  15. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/test_runner.py +5 -6
  16. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/test_runner_cli.py +3 -3
  17. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge.egg-info/PKG-INFO +13 -13
  18. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge.egg-info/SOURCES.txt +2 -0
  19. {spaceforge-1.1.3 → spaceforge-1.1.4}/.github/workflows/ci.yml +0 -0
  20. {spaceforge-1.1.3 → spaceforge-1.1.4}/.github/workflows/release.yml +0 -0
  21. {spaceforge-1.1.3 → spaceforge-1.1.4}/.gitignore +0 -0
  22. {spaceforge-1.1.3 → spaceforge-1.1.4}/LICENSE +0 -0
  23. {spaceforge-1.1.3 → spaceforge-1.1.4}/MANIFEST.in +0 -0
  24. {spaceforge-1.1.3 → spaceforge-1.1.4}/go.mod +0 -0
  25. {spaceforge-1.1.3 → spaceforge-1.1.4}/plugins/enviroment_manager/requirements.txt +0 -0
  26. {spaceforge-1.1.3 → spaceforge-1.1.4}/plugins/envsubst/plugin.py +0 -0
  27. {spaceforge-1.1.3 → spaceforge-1.1.4}/plugins/envsubst/plugin.yaml +0 -0
  28. {spaceforge-1.1.3 → spaceforge-1.1.4}/plugins/infracost/plugin.py +0 -0
  29. {spaceforge-1.1.3 → spaceforge-1.1.4}/plugins/infracost/plugin.yaml +0 -0
  30. {spaceforge-1.1.3 → spaceforge-1.1.4}/plugins/sops/plugin.py +0 -0
  31. {spaceforge-1.1.3 → spaceforge-1.1.4}/plugins/sops/requirements.txt +0 -0
  32. {spaceforge-1.1.3 → spaceforge-1.1.4}/plugins/wiz/plugin.py +0 -0
  33. {spaceforge-1.1.3 → spaceforge-1.1.4}/pyproject.toml +0 -0
  34. {spaceforge-1.1.3 → spaceforge-1.1.4}/setup.cfg +0 -0
  35. {spaceforge-1.1.3 → spaceforge-1.1.4}/setup.py +0 -0
  36. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/__init__.py +0 -0
  37. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/_version.py +0 -0
  38. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/cls.py +0 -0
  39. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/conftest.py +0 -0
  40. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/generator.py +0 -0
  41. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/plugin.py +0 -0
  42. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/schema.json +0 -0
  43. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/templates/binary_install.sh.j2 +0 -0
  44. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/test_cls.py +0 -0
  45. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/test_generator_binaries.py +0 -0
  46. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/test_generator_core.py +0 -0
  47. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/test_generator_hooks.py +0 -0
  48. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/test_generator_parameters.py +0 -0
  49. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/test_plugin.py +0 -0
  50. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/test_plugin_file_operations.py +0 -0
  51. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/test_plugin_hooks.py +0 -0
  52. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/test_plugin_inheritance.py +0 -0
  53. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/test_runner_core.py +0 -0
  54. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge/test_runner_execution.py +0 -0
  55. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge.egg-info/dependency_links.txt +0 -0
  56. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge.egg-info/entry_points.txt +0 -0
  57. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge.egg-info/not-zip-safe +0 -0
  58. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge.egg-info/requires.txt +0 -0
  59. {spaceforge-1.1.3 → spaceforge-1.1.4}/spaceforge.egg-info/top_level.txt +0 -0
  60. {spaceforge-1.1.3 → spaceforge-1.1.4}/templates.go +0 -0
  61. {spaceforge-1.1.3 → spaceforge-1.1.4}/test.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spaceforge
3
- Version: 1.1.3
3
+ Version: 1.1.4
4
4
  Summary: A Python framework for building Spacelift plugins
5
5
  Home-page: https://github.com/spacelift-io/plugins
6
6
  Author: Spacelift
@@ -60,15 +60,15 @@ pip install spaceforge
60
60
 
61
61
  ### 1. Create Your Plugin
62
62
 
63
- Create a Python file (e.g., `my_plugin.py`) and inherit from `SpaceforgePlugin`:
63
+ Create a Python file (e.g., `plugin.py`) and inherit from `SpaceforgePlugin`:
64
64
 
65
65
  ```python
66
- from spaceforge import SpaceforgePlugin, Parameter, Variable, Context, Binary, Policy, Webhook, MountedFile
66
+ from spaceforge import SpaceforgePlugin, Parameter, Variable, Context
67
67
  import os
68
68
 
69
69
  class MyPlugin(SpaceforgePlugin):
70
70
  # Plugin metadata
71
- __plugin_name__ = "my-awesome-plugin"
71
+ __plugin_name__ = "my-plugin"
72
72
  __version__ = "1.0.0"
73
73
  __author__ = "Your Name"
74
74
  __labels__ = ["security", "monitoring"] # Optional labels for categorization
@@ -127,7 +127,7 @@ class MyPlugin(SpaceforgePlugin):
127
127
  Generate the Spacelift plugin YAML manifest:
128
128
 
129
129
  ```bash
130
- spaceforge generate my_plugin.py
130
+ spaceforge generate plugin.py
131
131
  ```
132
132
 
133
133
  This creates `plugin.yaml` that you can upload to Spacelift.
@@ -142,7 +142,7 @@ export API_KEY="your-api-key"
142
142
  export ENVIRONMENT="staging"
143
143
 
144
144
  # Test the after_plan hook
145
- spaceforge runner after_plan
145
+ spaceforge run after_plan
146
146
  ```
147
147
 
148
148
  ## Available Hooks
@@ -527,13 +527,13 @@ export API_KEY="test-key"
527
527
  export TIMEOUT="60"
528
528
 
529
529
  # Test specific hook
530
- spaceforge runner after_plan
530
+ spaceforge run after_plan
531
531
 
532
532
  # Test with specific plugin file
533
- spaceforge runner --plugin-file my_plugin.py before_apply
533
+ spaceforge run --plugin-file my_plugin.py before_apply
534
534
 
535
535
  # Get help
536
- spaceforge runner --help
536
+ spaceforge run --help
537
537
  ```
538
538
 
539
539
  ## Plugin Development Tips
@@ -577,7 +577,7 @@ def after_plan(self):
577
577
  ### 4. Testing and Debugging
578
578
 
579
579
  - Set `SPACELIFT_DEBUG=true` to enable debug logging
580
- - Use the runner command to test hooks during development
580
+ - Use the `run` command to test hooks during development
581
581
  - Test with different parameter combinations
582
582
  - Validate your generated YAML before uploading to Spacelift
583
583
 
@@ -718,7 +718,7 @@ spaceforge generate security_scanner.py
718
718
  # Test locally
719
719
  export API_TOKEN="your-token"
720
720
  export SEVERITY_THRESHOLD="high"
721
- spaceforge runner after_plan
721
+ spaceforge run after_plan
722
722
  ```
723
723
 
724
724
  ## Speeding up plugin execution
@@ -734,8 +734,8 @@ There are a few things you can do to speed up plugin execution.
734
734
 
735
735
  1. **Install spaceforge:** `pip install spaceforge`
736
736
  2. **Create your plugin:** Start with the quick start example
737
- 3. **Test locally:** Use the runner command to test your hooks
738
- 4. **Generate manifest:** Use the generate command to create plugin.yaml
737
+ 3. **Test locally:** Use the `run` command to test your hooks
738
+ 4. **Generate manifest:** Use the `generate` command to create plugin.yaml
739
739
  5. **Upload to Spacelift:** Add your plugin manifest to your Spacelift account
740
740
 
741
741
  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., `my_plugin.py`) and inherit from `SpaceforgePlugin`:
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, Binary, Policy, Webhook, MountedFile
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-awesome-plugin"
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 my_plugin.py
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 runner after_plan
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 runner after_plan
484
+ spaceforge run after_plan
485
485
 
486
486
  # Test with specific plugin file
487
- spaceforge runner --plugin-file my_plugin.py before_apply
487
+ spaceforge run --plugin-file my_plugin.py before_apply
488
488
 
489
489
  # Get help
490
- spaceforge runner --help
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 runner command to test hooks during development
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 runner after_plan
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 runner command to test your hooks
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 Key ID",
112
+ name="Spacelift API key ID",
113
113
  id="spacelift_api_key_id",
114
- description="The API Key that will trigger the stack previews",
114
+ description="The API key that will trigger the stack previews",
115
115
  required=True,
116
116
  ),
117
117
  Parameter(
118
- name="Spacelift API Key Secret",
118
+ name="Spacelift API key secret",
119
119
  id="spacelift_api_key_secret",
120
- description="The API Key Secret that will trigger the stack previews",
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 Plugin",
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 Key ID
104
- description: The API Key that will trigger the stack previews
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 Key Secret
109
- description: The API Key Secret that will trigger the stack previews
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 Plugin
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 Key ID",
266
+ name="Spacelift API key ID",
267
267
  id="spacelift_api_key_id",
268
- description="The API Key that will trigger the stack previews",
268
+ description="The API key that will trigger the stack previews",
269
269
  required=True,
270
270
  ),
271
271
  Parameter(
272
- name="Spacelift API Key Secret",
272
+ name="Spacelift API key secret",
273
273
  id="spacelift_api_key_secret",
274
- description="The API Key Secret that will trigger the stack previews",
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 Plugin",
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 runner --plugin-file /mnt/workspace/plugins/environment_manager/plugin.py before_init
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 runner --plugin-file /mnt/workspace/plugins/sops/plugin.py before_init
184
+ spaceforge run --plugin-file /mnt/workspace/plugins/sops/plugin.py before_init
185
185
  sensitive: false
186
186
  hooks:
187
187
  before_init:
@@ -0,0 +1,195 @@
1
+ import json
2
+ import os
3
+
4
+ from spaceforge import Binary, Context, Parameter, Policy, SpaceforgePlugin, Variable
5
+
6
+ DEFAULT_TFLINT_CONFIG_FILE = ""
7
+ DEFAULT_TFLINT_RECURSIVE = "true"
8
+
9
+
10
+ class TFLintPlugin(SpaceforgePlugin):
11
+ """
12
+ # Plugin TFLint
13
+
14
+ The TFLint plugin analyzes your Terraform/OpenTofu files and generates a report with findings categorized by severity.
15
+
16
+ You can also access the data from a plan policy via the `input.third_party_metadata.custom.tflint` object.
17
+ An example Plan policy is included with the plugin.
18
+
19
+ ## Usage
20
+
21
+ 1. Install the plugin
22
+ 2. Add the `autoattach` label to any stack that uses Terraform/OpenTofu.
23
+ """
24
+
25
+ __author__ = "Spacelift"
26
+ __labels__ = ["qa", "security"]
27
+ __plugin_name__ = "tflint"
28
+ __version__ = "0.1.0"
29
+
30
+ __binaries__ = [
31
+ Binary(
32
+ name="tflint",
33
+ download_urls={
34
+ "amd64": "https://github.com/terraform-linters/tflint/releases/download/v0.59.1/tflint_linux_amd64.zip",
35
+ "arm64": "https://github.com/terraform-linters/tflint/releases/download/v0.59.1/tflint_linux_arm64.zip",
36
+ },
37
+ ),
38
+ ]
39
+
40
+ __parameters__ = [
41
+ Parameter(
42
+ name="Configuration file",
43
+ id="tflint_config_file",
44
+ description="Configuration file name",
45
+ default=DEFAULT_TFLINT_CONFIG_FILE,
46
+ ),
47
+ Parameter(
48
+ name="Recursive",
49
+ id="tflint_recursive",
50
+ description="Run command in each directory recursively. Allowed values: true, false",
51
+ default=DEFAULT_TFLINT_RECURSIVE,
52
+ ),
53
+ ]
54
+
55
+ __contexts__ = [
56
+ Context(
57
+ name_prefix="tflint",
58
+ description="TFLint Plugin",
59
+ env=[
60
+ Variable(
61
+ key="TFLINT_CONFIG_FILE",
62
+ value_from_parameter="tflint_config_file",
63
+ ),
64
+ Variable(
65
+ key="TFLINT_RECURSIVE",
66
+ value_from_parameter="tflint_recursive",
67
+ ),
68
+ ],
69
+ )
70
+ ]
71
+
72
+ __policies__ = [
73
+ Policy(
74
+ name_prefix="tflint",
75
+ type="PLAN",
76
+ labels=["tflint"],
77
+ body="""
78
+ package spacelift
79
+
80
+ import rego.v1
81
+
82
+ max_errors := 0
83
+ max_warnings := 0
84
+ max_notices := 3
85
+
86
+ issues := input.third_party_metadata.custom.tflint.issues
87
+
88
+ deny contains sprintf("Too many errors (%d)", [cnt]) if {
89
+ cnt := count([issue | issue := issues[_]; issue.rule.severity == "error"])
90
+ cnt > max_errors
91
+ }
92
+
93
+ deny contains sprintf("Too many warnings (%d)", [cnt]) if {
94
+ cnt := count([issue | issue := issues[_]; issue.rule.severity == "warning"])
95
+ cnt > max_warnings
96
+ }
97
+
98
+ deny contains sprintf("Too many notices (%d)", [cnt]) if {
99
+ cnt := count([issue | issue := issues[_]; issue.rule.severity == "notice"])
100
+ cnt > max_notices
101
+ }
102
+ """,
103
+ )
104
+ ]
105
+
106
+ def before_plan(self):
107
+ try:
108
+ options = ["--format=json"]
109
+
110
+ # Configuration file
111
+ config_file = (
112
+ os.environ.get("TFLINT_CONFIG_FILE") or DEFAULT_TFLINT_CONFIG_FILE
113
+ )
114
+ if config_file:
115
+ options.append(f"--config={config_file}")
116
+
117
+ # Recursive
118
+ recursive = os.environ.get("TFLINT_RECURSIVE") or DEFAULT_TFLINT_RECURSIVE
119
+ if recursive == "true":
120
+ options.append("--recursive")
121
+
122
+ return_code, stdout, stderr = self.run_cli("tflint", "--init")
123
+ if return_code != 0:
124
+ self.logger.error(f"tflint --init failed with code {return_code}")
125
+ if stderr:
126
+ # Display stderr manually because output display is disabled
127
+ self.logger.error("\n".join(stderr))
128
+ exit(1)
129
+
130
+ return_code, stdout, stderr = self.run_cli(
131
+ "tflint", *options, print_output=False
132
+ )
133
+ stdout_json = json.loads("\n".join(stdout))
134
+
135
+ # KLUDGE: The `expect_code` argument does not support multiple values so we need to manually handle this.
136
+ # `0` means success and `2` means issues found which is informational, not a failure.
137
+ if return_code not in [0, 2]:
138
+ for error in stdout_json["errors"]:
139
+ self.logger.error(error["message"])
140
+ exit(1)
141
+
142
+ self.add_to_policy_input("tflint", stdout_json)
143
+
144
+ if len(stdout_json["issues"]) == 0:
145
+ self.logger.info("No issues found")
146
+ return
147
+
148
+ findings = {
149
+ "error": {},
150
+ "warning": {},
151
+ "notice": {},
152
+ }
153
+ for match in stdout_json["issues"]:
154
+ severity = match["rule"]["severity"]
155
+ rule_name = match["rule"]["name"]
156
+
157
+ if rule_name not in findings[severity]:
158
+ findings[severity][rule_name] = []
159
+
160
+ findings[severity][rule_name].append(match)
161
+
162
+ markdown = "# TFLint Findings\n\n"
163
+ for severity, rules in findings.items():
164
+ # Skip severity level if no issues were found
165
+ if len(rules) == 0:
166
+ continue
167
+
168
+ emoji = None
169
+ if severity == "notice":
170
+ emoji = "🟡"
171
+ elif severity == "warning":
172
+ emoji = "🟠"
173
+ elif severity == "error":
174
+ emoji = "🔴"
175
+ if emoji is not None:
176
+ markdown += f"## {emoji} {severity.title()} Findings\n\n"
177
+ else:
178
+ markdown += f"## {severity.title()} Findings\n\n"
179
+
180
+ for rule_name, issues in rules.items():
181
+ markdown += f"### {rule_name}\n\n"
182
+ for issue in issues:
183
+ markdown += f"- {issue['message']} _({issue['range']['filename']}:{issue['range']['start']['line']})_\n"
184
+ markdown += "\n"
185
+
186
+ result = self.send_markdown(markdown)
187
+ if result:
188
+ self.logger.info(
189
+ "Issues found. Check the Plugins Output tab for details."
190
+ )
191
+ else:
192
+ self.logger.error("Failed to upload plugin outputs")
193
+ except Exception as e:
194
+ self.logger.error(f"Plugin failed: {e}")
195
+ exit(1)