gitlab-compliance 2.6.0__tar.gz → 2.7.0__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 (84) hide show
  1. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/PKG-INFO +7 -7
  2. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/pyproject.toml +12 -12
  3. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/behave_support/environment.py +7 -0
  4. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/behave_support/steps/given_steps.py +19 -0
  5. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/include_fetch.py +6 -0
  6. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/include_resolution.py +4 -0
  7. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/model.py +39 -0
  8. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/runner.py +6 -0
  9. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/gitlab_compliance.py +142 -0
  10. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/pipeline_data.py +539 -0
  11. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/LICENSE +0 -0
  12. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/docs/index.md +0 -0
  13. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/__init__.py +0 -0
  14. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/__init__.py +0 -0
  15. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/api_config.py +0 -0
  16. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/api_enrichment.py +0 -0
  17. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/behave_support/steps/__init__.py +0 -0
  18. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/behave_support/steps/script_steps.py +0 -0
  19. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/behave_support/steps/then_steps.py +0 -0
  20. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/behave_support/steps/when_steps.py +0 -0
  21. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/__init__.py +0 -0
  22. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/baseline-jobs.feature +0 -0
  23. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/component-inputs.feature +0 -0
  24. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/include-pinning.feature +0 -0
  25. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/shell/shell-ci-conventions.feature +0 -0
  26. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/shell/shell-command-substitution.feature +0 -0
  27. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/shell/shell-conditionals.feature +0 -0
  28. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/shell/shell-error-handling.feature +0 -0
  29. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/shell/shell-file-operations.feature +0 -0
  30. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/shell/shell-pinning.feature +0 -0
  31. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/shell/shell-pipelines.feature +0 -0
  32. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/shell/shell-portability.feature +0 -0
  33. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/shell/shell-quoting.feature +0 -0
  34. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/shell/shell-references.feature +0 -0
  35. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/shell/shell-security.feature +0 -0
  36. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/supply-chain/image-pinning.feature +0 -0
  37. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/supply-chain/include-versions.feature +0 -0
  38. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/supply-chain/service-pinning.feature +0 -0
  39. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/builtin_policies/variable-allowlist.feature +0 -0
  40. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/console.py +0 -0
  41. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/container_fix.py +0 -0
  42. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/gitlab_api.py +0 -0
  43. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/image_versions.py +0 -0
  44. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/include_fix.py +0 -0
  45. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/include_versions.py +0 -0
  46. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/merge_requests.py +0 -0
  47. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/metadata.py +0 -0
  48. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/models.py +0 -0
  49. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/oci_registry.py +0 -0
  50. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/policy_doc.py +0 -0
  51. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/policy_fix.py +0 -0
  52. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/release_cache.py +0 -0
  53. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/render.py +0 -0
  54. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/script_analysis.py +0 -0
  55. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/secret_redact.py +0 -0
  56. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/shell_lex.py +0 -0
  57. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/shell_render.py +0 -0
  58. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/stash.py +0 -0
  59. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/compliance/supply_chain_fix.py +0 -0
  60. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/gitlab_docs.py +0 -0
  61. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/__init__.py +0 -0
  62. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/command_reference.py +0 -0
  63. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/common.py +0 -0
  64. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/constants.py +0 -0
  65. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/doc_controller.py +0 -0
  66. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/gitlab_reference.py +0 -0
  67. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/gitstrings.py +0 -0
  68. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/job_composition.py +0 -0
  69. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/logging.py +0 -0
  70. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/output_filters.py +0 -0
  71. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/release.py +0 -0
  72. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/swagger_html.py +0 -0
  73. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/swagger_markdown.py +0 -0
  74. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/yaml_lines.py +0 -0
  75. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/modules/yaml_md_table.py +0 -0
  76. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/properties/__init__.py +0 -0
  77. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/properties/extract_job_attribute.py +0 -0
  78. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/properties/includes.py +0 -0
  79. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/properties/inputs.py +0 -0
  80. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/properties/jobs.py +0 -0
  81. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/properties/table_render.py +0 -0
  82. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/properties/variables.py +0 -0
  83. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/properties/workflows.py +0 -0
  84. {gitlab_compliance-2.6.0 → gitlab_compliance-2.7.0}/src/properties/yaml_paths.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitlab-compliance
3
- Version: 2.6.0
3
+ Version: 2.7.0
4
4
  Summary: BDD compliance testing for GitLab CI/CD pipelines and project settings — by MaturityBuilder
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -19,21 +19,21 @@ Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Topic :: Software Development :: Quality Assurance
20
20
  Classifier: Topic :: Software Development :: Testing
21
21
  Requires-Dist: behave (>=1.2.6,<2.0.0)
22
- Requires-Dist: click (>=8.1.8,<9.0.0)
23
- Requires-Dist: idna (==3.18)
22
+ Requires-Dist: click (>=8.5.0,<9.0.0)
23
+ Requires-Dist: idna (==3.20)
24
24
  Requires-Dist: jq (==1.12.0)
25
25
  Requires-Dist: loguru (==0.7.2)
26
26
  Requires-Dist: markdown-analysis (==0.1.5)
27
- Requires-Dist: oras (==0.2.42)
27
+ Requires-Dist: oras (==0.2.43)
28
28
  Requires-Dist: oyaml
29
29
  Requires-Dist: prettytable
30
30
  Requires-Dist: pytablewriter (==1.2.1)
31
- Requires-Dist: python-gitlab (==8.4.0)
31
+ Requires-Dist: python-gitlab (==8.6.0)
32
32
  Requires-Dist: pyyaml (==6.0.3)
33
33
  Requires-Dist: requests (==2.34.2)
34
34
  Requires-Dist: rich (==15.0.0)
35
- Requires-Dist: semver (==3.0.4)
36
- Requires-Dist: typer (==0.27.0)
35
+ Requires-Dist: semver (==3.1.0)
36
+ Requires-Dist: typer (==0.27.2)
37
37
  Requires-Dist: urllib3 (==2.8.0)
38
38
  Project-URL: Documentation, https://maturitybuilder.github.io/gitlab-compliance/
39
39
  Project-URL: Home Page, https://maturitybuilder.github.io/gitlab-compliance/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gitlab-compliance"
3
- version = "2.6.0"
3
+ version = "2.7.0"
4
4
  dynamic = [ "readme" ]
5
5
  license = "Apache-2.0"
6
6
  requires-python = ">=3.12"
@@ -40,18 +40,18 @@ python = ">=3.12"
40
40
  prettytable = "*"
41
41
  pytablewriter = "1.2.1"
42
42
  oyaml = "*"
43
- typer = "0.27.0"
43
+ typer = "0.27.2"
44
44
  markdown-analysis = "0.1.5"
45
45
  urllib3 = "2.8.0"
46
46
  jq = "1.12.0"
47
- click = "^8.1.8"
48
- semver = "3.0.4"
47
+ click = "^8.5.0"
48
+ semver = "3.1.0"
49
49
  pyyaml = "6.0.3"
50
50
  loguru = "0.7.2"
51
- python-gitlab = "8.4.0"
51
+ python-gitlab = "8.6.0"
52
52
  rich = "15.0.0"
53
- oras = "0.2.42"
54
- idna = "3.18"
53
+ oras = "0.2.43"
54
+ idna = "3.20"
55
55
  requests = "2.34.2"
56
56
  behave = "^1.2.6"
57
57
 
@@ -60,13 +60,13 @@ gitlab-docs = 'src.gitlab_compliance:gitlab_compliance'
60
60
  gitlab-compliance = 'src.gitlab_compliance:gitlab_compliance'
61
61
 
62
62
  [tool.poetry.group.dev.dependencies]
63
- setuptools = "83.0.0"
63
+ setuptools = "84.1.0"
64
64
  pytest = "^9.0.3"
65
65
  pytest-xdist = "^3.8.0"
66
- pytest-cov = "^6.2.1"
67
- pre-commit = "^4.6.1"
66
+ pytest-cov = "^7.1.0"
67
+ pre-commit = "^4.6.2"
68
68
  pyyaml = "^6.0.3"
69
- coverage = "^7.9.2"
69
+ coverage = "^7.16.2"
70
70
 
71
71
  [tool.black]
72
72
  line-length = 88
@@ -79,7 +79,7 @@ max-line-length = 88
79
79
  extend-ignore = ["E203", "W503"]
80
80
 
81
81
  [tool.poetry.group.docs.dependencies]
82
- zensical = "^0.0.51"
82
+ zensical = "^0.0.67"
83
83
  [build-system]
84
84
  requires = ["poetry-core","setuptools","poetry-bumpversion","id"]
85
85
  build-backend = "poetry.core.masonry.api"
@@ -37,6 +37,10 @@ def before_scenario(context, scenario):
37
37
  if not context.compliance_entities:
38
38
  depth_raw = context.config.userdata.get("max_include_depth", "")
39
39
  max_include_depth = int(depth_raw) if str(depth_raw).strip() else None
40
+ generated_raw = context.config.userdata.get("generated_configs", "") or ""
41
+ generated_configs = tuple(
42
+ part.strip() for part in str(generated_raw).split(",") if part.strip()
43
+ )
40
44
  context.compliance_entities = load_pipeline_entities(
41
45
  pipeline_file=pipeline,
42
46
  include_nested=context.config.userdata.get("include_nested", "true")
@@ -54,6 +58,9 @@ def before_scenario(context, scenario):
54
58
  resolve_external_includes=_resolve_external_flag(
55
59
  context.config.userdata.get("resolve_external_includes", "auto")
56
60
  ),
61
+ resolve_generated=context.config.userdata.get("resolve_generated", "true")
62
+ == "true",
63
+ generated_configs=generated_configs or None,
57
64
  cache=getattr(context, "release_cache", None),
58
65
  )
59
66
 
@@ -107,6 +107,25 @@ def given_any_container_image_with_release_metadata(context):
107
107
  )
108
108
 
109
109
 
110
+ @given("I have any dynamic child pipeline defined")
111
+ def given_any_dynamic_child_pipeline(context):
112
+ _set_stash(context, context.compliance_entities.get("dynamic_child_pipelines", []))
113
+
114
+
115
+ @given('I have dynamic child pipeline "{name}" defined')
116
+ def given_named_dynamic_child_pipeline(context, name):
117
+ pipelines = [
118
+ item
119
+ for item in context.compliance_entities.get("dynamic_child_pipelines", [])
120
+ if item.get("name") == name or item.get("trigger_job") == name
121
+ ]
122
+ _set_stash_or_skip(
123
+ context,
124
+ pipelines,
125
+ skip_reason=f'No dynamic child pipeline named "{name}" found in pipeline',
126
+ )
127
+
128
+
110
129
  @given("I have any variable defined")
111
130
  def given_any_variable(context):
112
131
  _set_stash(context, context.compliance_entities.get("variables", []))
@@ -322,6 +322,12 @@ def include_reference_label(parsed: dict[str, Any]) -> str:
322
322
  return f"{parsed.get('project', '')}@{parsed.get('version', '')}"
323
323
  if include_type == "template":
324
324
  return str(parsed.get("project", ""))
325
+ if include_type == "artifact":
326
+ artifact = parsed.get("artifact") or parsed.get("project", "")
327
+ generator = parsed.get("generator_job") or ""
328
+ if generator:
329
+ return f"{artifact} (from job {generator})"
330
+ return str(artifact)
325
331
  return str(parsed.get("project", ""))
326
332
 
327
333
 
@@ -16,6 +16,10 @@ REASON_MESSAGES = {
16
16
  "external_resolution_disabled": "external include resolution disabled",
17
17
  "path_rejected": "local include path rejected",
18
18
  "not_found": "include file not found",
19
+ "generated_at_runtime": (
20
+ "generated CI YAML not on disk yet "
21
+ "(run compliance after the generator job writes the artifact)"
22
+ ),
19
23
  }
20
24
 
21
25
 
@@ -106,11 +106,32 @@ def _workflow_rule_entity(
106
106
  }
107
107
 
108
108
 
109
+ def _dynamic_child_pipeline_entity(index: int, record: dict) -> dict:
110
+ trigger_job = str(record.get("trigger_job") or "")
111
+ artifact = str(record.get("artifact") or "")
112
+ name = trigger_job or artifact or f"dynamic-child-{index}"
113
+ return {
114
+ "address": f"dynamic_child_pipeline.{index}",
115
+ "type": "dynamic_child_pipeline",
116
+ "name": name,
117
+ "trigger_job": trigger_job,
118
+ "generator_job": str(record.get("generator_job") or ""),
119
+ "artifact": artifact,
120
+ "resolved": bool(record.get("resolved")),
121
+ "include_type": str(record.get("include_type") or "artifact"),
122
+ "values": dict(record),
123
+ "source_file": str(record.get("source_file") or ""),
124
+ "line": int(record.get("line") or 0),
125
+ }
126
+
127
+
109
128
  def load_yaml_entities(
110
129
  pipeline_file: str,
111
130
  include_nested: bool = True,
112
131
  max_include_depth: int | None = None,
113
132
  resolve_external_includes: bool | None = None,
133
+ resolve_generated: bool = True,
134
+ generated_configs: tuple[str, ...] | list[str] | None = None,
114
135
  gitlab_url: str | None = None,
115
136
  token: str | None = None,
116
137
  ) -> tuple[dict[str, list[dict]], list[dict]]:
@@ -125,6 +146,8 @@ def load_yaml_entities(
125
146
  include_scripts=True,
126
147
  resolve_job_composition=True,
127
148
  resolve_external_includes=resolve_external_includes,
149
+ resolve_generated=resolve_generated,
150
+ generated_configs=generated_configs,
128
151
  gitlab_url=gitlab_url,
129
152
  token=token,
130
153
  )
@@ -168,6 +191,12 @@ def load_yaml_entities(
168
191
  )
169
192
  for index, rule in enumerate(pipeline_data["workflow_rules"], 1)
170
193
  ],
194
+ "dynamic_child_pipelines": [
195
+ _dynamic_child_pipeline_entity(index, record)
196
+ for index, record in enumerate(
197
+ pipeline_data.get("dynamic_child_pipelines") or [], 1
198
+ )
199
+ ],
171
200
  "project_settings": [],
172
201
  "project_ci_variables": [],
173
202
  "group_settings": [],
@@ -201,6 +230,8 @@ def _job_values(job: dict) -> dict:
201
230
  "extends_chain",
202
231
  "unresolved_script_references",
203
232
  "script_provenance",
233
+ "dynamic_parent_job",
234
+ "generated_artifact",
204
235
  ):
205
236
  if key in job:
206
237
  values[key] = job[key]
@@ -212,6 +243,8 @@ def load_merged_job_scripts(
212
243
  include_nested: bool = True,
213
244
  max_include_depth: int | None = None,
214
245
  resolve_external_includes: bool | None = None,
246
+ resolve_generated: bool = True,
247
+ generated_configs: tuple[str, ...] | list[str] | None = None,
215
248
  gitlab_url: str | None = None,
216
249
  token: str | None = None,
217
250
  ) -> list[dict]:
@@ -224,6 +257,8 @@ def load_merged_job_scripts(
224
257
  include_scripts=True,
225
258
  resolve_job_composition=True,
226
259
  resolve_external_includes=resolve_external_includes,
260
+ resolve_generated=resolve_generated,
261
+ generated_configs=generated_configs,
227
262
  gitlab_url=gitlab_url,
228
263
  token=token,
229
264
  )
@@ -242,6 +277,8 @@ def load_pipeline_entities(
242
277
  enrich_images: bool = True,
243
278
  load_api_entities: bool = True,
244
279
  resolve_external_includes: bool | None = None,
280
+ resolve_generated: bool = True,
281
+ generated_configs: tuple[str, ...] | list[str] | None = None,
245
282
  cache: "ReleaseMetadataCache | None" = None,
246
283
  ) -> dict[str, list[dict]]:
247
284
  from src.compliance.api_config import resolve_group, resolve_project, resolve_token
@@ -252,6 +289,8 @@ def load_pipeline_entities(
252
289
  include_nested=include_nested,
253
290
  max_include_depth=max_include_depth,
254
291
  resolve_external_includes=resolve_external_includes,
292
+ resolve_generated=resolve_generated,
293
+ generated_configs=generated_configs,
255
294
  gitlab_url=gitlab_url,
256
295
  token=token,
257
296
  )
@@ -427,6 +427,8 @@ def run_compliance(
427
427
  group: str | None = None,
428
428
  strict: bool = False,
429
429
  resolve_external_includes: bool | None = None,
430
+ resolve_generated: bool = True,
431
+ generated_configs: tuple[str, ...] | list[str] | None = None,
430
432
  dry_run: bool = False,
431
433
  output_format: str = "console",
432
434
  policies_source: str | None = None,
@@ -513,6 +515,8 @@ def run_compliance(
513
515
  include_nested=include_nested,
514
516
  max_include_depth=max_include_depth,
515
517
  resolve_external_includes=resolve_external_includes,
518
+ resolve_generated=resolve_generated,
519
+ generated_configs=generated_configs,
516
520
  gitlab_url=gitlab_url,
517
521
  token=resolved_token or token,
518
522
  )
@@ -615,6 +619,8 @@ def run_compliance(
615
619
  if resolve_external_includes is True
616
620
  else "false" if resolve_external_includes is False else "auto"
617
621
  ),
622
+ "resolve_generated": "true" if resolve_generated else "false",
623
+ "generated_configs": ",".join(generated_configs or ()),
618
624
  "release_cache": release_cache,
619
625
  "shell_check_verbose": "true" if verbose else "false",
620
626
  }
@@ -11,6 +11,7 @@ from datetime import datetime
11
11
  import click
12
12
 
13
13
  import src.gitlab_docs as _gitlab_docs
14
+ import src.modules.common as common
14
15
  import src.modules.doc_controller as md_writer
15
16
  import src.properties.includes as includes
16
17
  import src.properties.inputs as inputs
@@ -93,6 +94,37 @@ def _parse_generate_filters(exclude, group_by):
93
94
  return exclude_sections, exclude_attributes, group_by
94
95
 
95
96
 
97
+ def _render_dynamic_child_pipelines(OUTPUT_FILE, pipeline_data: dict) -> None:
98
+ records = pipeline_data.get("dynamic_child_pipelines") or []
99
+ if not records:
100
+ return
101
+ add_between_markers(
102
+ file_path=OUTPUT_FILE, content="\n\n## Dynamic child pipelines\n\n"
103
+ )
104
+ table = common.table_design(
105
+ headers=[
106
+ "**Trigger job**",
107
+ "**Generator job**",
108
+ "**Artifact**",
109
+ "**Resolved**",
110
+ ]
111
+ )
112
+ for record in records:
113
+ table.add_row(
114
+ [
115
+ record.get("trigger_job") or "—",
116
+ record.get("generator_job") or "—",
117
+ record.get("artifact") or "—",
118
+ "yes" if record.get("resolved") else "no",
119
+ ]
120
+ )
121
+ add_between_markers(
122
+ file_path=OUTPUT_FILE,
123
+ content=common.markdown_table_from_prettytable(table),
124
+ )
125
+ add_between_markers(file_path=OUTPUT_FILE, content="\n")
126
+
127
+
96
128
  def _generate_markdown(
97
129
  OUTPUT_FILE,
98
130
  GLDOCS_CONFIG_FILE,
@@ -101,6 +133,8 @@ def _generate_markdown(
101
133
  exclude_attributes=None,
102
134
  group_by=None,
103
135
  max_include_depth=None,
136
+ resolve_generated=True,
137
+ generated_configs=None,
104
138
  ):
105
139
  ENABLE_WORKFLOW_DOCUMENTATION = detailed
106
140
  exclude_sections = exclude_sections or set()
@@ -133,6 +167,7 @@ def _generate_markdown(
133
167
  DISABLE_TITLE=True,
134
168
  OUTPUT_FILE=OUTPUT_FILE,
135
169
  )
170
+ pipeline_data = None
136
171
  if "jobs" not in exclude_sections:
137
172
  pipeline_data = collect_pipeline_data(
138
173
  config_file=GLDOCS_CONFIG_FILE,
@@ -141,12 +176,26 @@ def _generate_markdown(
141
176
  exclude_sections=exclude_sections,
142
177
  exclude_attributes=exclude_attributes,
143
178
  group_by=group_by,
179
+ resolve_generated=resolve_generated,
180
+ generated_configs=generated_configs,
144
181
  )
145
182
  jobs.render_jobs_from_pipeline(
146
183
  OUTPUT_FILE=OUTPUT_FILE,
147
184
  pipeline_data=pipeline_data,
148
185
  detailed=detailed,
149
186
  )
187
+ if pipeline_data is None:
188
+ pipeline_data = collect_pipeline_data(
189
+ config_file=GLDOCS_CONFIG_FILE,
190
+ detailed=detailed,
191
+ max_include_depth=max_include_depth,
192
+ exclude_sections=exclude_sections,
193
+ exclude_attributes=exclude_attributes,
194
+ group_by=group_by,
195
+ resolve_generated=resolve_generated,
196
+ generated_configs=generated_configs,
197
+ )
198
+ _render_dynamic_child_pipelines(OUTPUT_FILE, pipeline_data)
150
199
 
151
200
 
152
201
  def _generate_html(
@@ -157,6 +206,8 @@ def _generate_html(
157
206
  exclude_attributes=None,
158
207
  group_by=None,
159
208
  max_include_depth=None,
209
+ resolve_generated=True,
210
+ generated_configs=None,
160
211
  ):
161
212
  pipeline_data = collect_pipeline_data(
162
213
  config_file=GLDOCS_CONFIG_FILE,
@@ -165,6 +216,8 @@ def _generate_html(
165
216
  exclude_sections=exclude_sections,
166
217
  exclude_attributes=exclude_attributes,
167
218
  group_by=group_by,
219
+ resolve_generated=resolve_generated,
220
+ generated_configs=generated_configs,
168
221
  )
169
222
  html_output = render_swagger_html(pipeline_data)
170
223
  with open(OUTPUT_FILE, "w", encoding="utf-8") as output_file:
@@ -179,6 +232,8 @@ def _generate_swagger_markdown(
179
232
  exclude_attributes=None,
180
233
  group_by=None,
181
234
  max_include_depth=None,
235
+ resolve_generated=True,
236
+ generated_configs=None,
182
237
  ):
183
238
  pipeline_data = collect_pipeline_data(
184
239
  config_file=GLDOCS_CONFIG_FILE,
@@ -187,6 +242,8 @@ def _generate_swagger_markdown(
187
242
  exclude_sections=exclude_sections,
188
243
  exclude_attributes=exclude_attributes,
189
244
  group_by=group_by,
245
+ resolve_generated=resolve_generated,
246
+ generated_configs=generated_configs,
190
247
  )
191
248
  markdown_output = render_swagger_markdown(pipeline_data)
192
249
  with open(OUTPUT_FILE, "w", encoding="utf-8") as output_file:
@@ -365,6 +422,21 @@ def get_attributes(OUTPUT_FILE, GLDOCS_CONFIG_FILE, attributes, json_format):
365
422
  default=None,
366
423
  help="Max local include nesting depth from the root file (omit for unlimited).",
367
424
  )
425
+ @click.option(
426
+ "--resolve-generated/--no-resolve-generated",
427
+ "resolve_generated",
428
+ default=True,
429
+ help=(
430
+ "Load dynamically generated child pipeline YAML from trigger.include "
431
+ "artifact paths when the file exists on disk (default: on)."
432
+ ),
433
+ )
434
+ @click.option(
435
+ "--generated-config",
436
+ "generated_configs",
437
+ multiple=True,
438
+ help=("Extra generated CI YAML path to merge into documentation (repeatable)."),
439
+ )
368
440
  def generate(
369
441
  detailed,
370
442
  output_format,
@@ -374,6 +446,8 @@ def generate(
374
446
  exclude,
375
447
  group_by,
376
448
  max_include_depth,
449
+ resolve_generated,
450
+ generated_configs,
377
451
  ):
378
452
  """
379
453
  Will scan through your gitlab-ci yml and build documentation from the yml.
@@ -403,6 +477,8 @@ def generate(
403
477
  exclude_attributes=exclude_attributes,
404
478
  group_by=group_by,
405
479
  max_include_depth=max_include_depth,
480
+ resolve_generated=resolve_generated,
481
+ generated_configs=generated_configs,
406
482
  )
407
483
 
408
484
  if output_format == "html":
@@ -554,6 +630,24 @@ def _resolve_policies_dir(
554
630
  "project when a token is available)."
555
631
  ),
556
632
  )
633
+ @click.option(
634
+ "--resolve-generated/--no-resolve-generated",
635
+ "resolve_generated",
636
+ default=True,
637
+ help=(
638
+ "Load dynamically generated child pipeline YAML from trigger.include "
639
+ "artifact paths when the file exists on disk (default: on)."
640
+ ),
641
+ )
642
+ @click.option(
643
+ "--generated-config",
644
+ "generated_configs",
645
+ multiple=True,
646
+ help=(
647
+ "Extra generated CI YAML path to merge (repeatable). Use when the artifact "
648
+ "is written somewhere other than the declared trigger.include path."
649
+ ),
650
+ )
557
651
  @click.option(
558
652
  "--gitlab-url",
559
653
  default=None,
@@ -690,6 +784,8 @@ def check(
690
784
  include_nested,
691
785
  max_include_depth,
692
786
  resolve_external_includes,
787
+ resolve_generated,
788
+ generated_configs,
693
789
  gitlab_url,
694
790
  token,
695
791
  project,
@@ -739,6 +835,8 @@ def check(
739
835
  include_nested=include_nested,
740
836
  max_include_depth=max_include_depth,
741
837
  resolve_external_includes=resolve_external_includes,
838
+ resolve_generated=resolve_generated,
839
+ generated_configs=generated_configs,
742
840
  gitlab_url=gitlab_url,
743
841
  token=token,
744
842
  project=project,
@@ -849,6 +947,24 @@ def check(
849
947
  "project when a token is available)."
850
948
  ),
851
949
  )
950
+ @click.option(
951
+ "--resolve-generated/--no-resolve-generated",
952
+ "resolve_generated",
953
+ default=True,
954
+ help=(
955
+ "Load dynamically generated child pipeline YAML from trigger.include "
956
+ "artifact paths when the file exists on disk (default: on)."
957
+ ),
958
+ )
959
+ @click.option(
960
+ "--generated-config",
961
+ "generated_configs",
962
+ multiple=True,
963
+ help=(
964
+ "Extra generated CI YAML path to merge (repeatable). Use when the artifact "
965
+ "is written somewhere other than the declared trigger.include path."
966
+ ),
967
+ )
852
968
  @click.option(
853
969
  "--features",
854
970
  "-f",
@@ -924,6 +1040,8 @@ def supply_chain(
924
1040
  include_nested,
925
1041
  max_include_depth,
926
1042
  resolve_external_includes,
1043
+ resolve_generated,
1044
+ generated_configs,
927
1045
  features_dir,
928
1046
  gitlab_url,
929
1047
  token,
@@ -955,6 +1073,8 @@ def supply_chain(
955
1073
  include_nested=include_nested,
956
1074
  max_include_depth=max_include_depth,
957
1075
  resolve_external_includes=resolve_external_includes,
1076
+ resolve_generated=resolve_generated,
1077
+ generated_configs=generated_configs,
958
1078
  gitlab_url=gitlab_url,
959
1079
  token=token,
960
1080
  project=project,
@@ -1050,6 +1170,24 @@ def supply_chain(
1050
1170
  "project when a token is available)."
1051
1171
  ),
1052
1172
  )
1173
+ @click.option(
1174
+ "--resolve-generated/--no-resolve-generated",
1175
+ "resolve_generated",
1176
+ default=True,
1177
+ help=(
1178
+ "Load dynamically generated child pipeline YAML from trigger.include "
1179
+ "artifact paths when the file exists on disk (default: on)."
1180
+ ),
1181
+ )
1182
+ @click.option(
1183
+ "--generated-config",
1184
+ "generated_configs",
1185
+ multiple=True,
1186
+ help=(
1187
+ "Extra generated CI YAML path to merge (repeatable). Use when the artifact "
1188
+ "is written somewhere other than the declared trigger.include path."
1189
+ ),
1190
+ )
1053
1191
  @click.option(
1054
1192
  "--gitlab-url",
1055
1193
  default=None,
@@ -1118,6 +1256,8 @@ def shell_check(
1118
1256
  include_nested,
1119
1257
  max_include_depth,
1120
1258
  resolve_external_includes,
1259
+ resolve_generated,
1260
+ generated_configs,
1121
1261
  gitlab_url,
1122
1262
  token,
1123
1263
  project,
@@ -1147,6 +1287,8 @@ def shell_check(
1147
1287
  include_nested=include_nested,
1148
1288
  max_include_depth=max_include_depth,
1149
1289
  resolve_external_includes=resolve_external_includes,
1290
+ resolve_generated=resolve_generated,
1291
+ generated_configs=generated_configs,
1150
1292
  gitlab_url=gitlab_url,
1151
1293
  token=token,
1152
1294
  project=project,