@7n/rules-ci-github 1.7.0 → 1.8.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.8.0] - 2026-07-20
4
+
5
+ ### Added
6
+
7
+ - service_deploy_workflow: перевірка outputs-мапінгу plan-джоби — кожен ключ гейта `needs.plan.outputs.<key>` має бути задекларований у `jobs.plan.outputs` і вказувати на `steps.<id>.outputs.<key>` реального кроку (інакше гейт тихо порожній і джоба скіпається завжди)
8
+
9
+ ## [1.7.1] - 2026-07-19
10
+
11
+ ### Changed
12
+
13
+ - release: @7n/rules@1.27.0, @7n/rules-lang-js@0.3.0, @7n/rules-lang-python@0.5.1, @7n/rules-lang-rust@0.5.1
14
+
3
15
  ## [1.7.0] - 2026-07-18
4
16
 
5
17
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@7n/rules-ci-github",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Плагін @7n/rules: канон GitHub Actions (.github/workflows) — обов'язкові workflow, zizmor, VS Code",
5
5
  "keywords": [
6
6
  "github-actions",
@@ -12,7 +12,7 @@ Rego-пакет: `ga.service_deploy_workflow`
12
12
  4. **`test`-джоба** — `needs: plan`, `if: needs.plan.outputs.any == 'true'`; тести лише сервісу (`bun test run/<service>`; python-стек — `astral-sh/setup-uv` + `uv sync --locked` + `pytest run/<service>`).
13
13
  5. **Деплой** — needs-ланцюг (можна через проміжний `build`) **транзитивно досягає** `plan` і всі lint/test-джоби; кожна джоба з `needs` на умовні lint-джоби має `if` з `!cancelled()` та `!contains(needs.*.result, 'failure')` — skipped-лінт не блокує деплой, failed — блокує.
14
14
 
15
- Що перевіряє rego: наявність plan-джоби з `ci plan --path … --github`; збіг `on.push.paths` ↔ `--path`; для кожної lint-джоби — `needs: plan`, гейт по outputs, той самий `--path`, `--no-fix`, prep-крок, `fetch-depth: 0`; для термінальних джоб — транзитивну досяжність plan і всіх перевірок та skip-толерантний `if`.
15
+ Що перевіряє rego: наявність plan-джоби з `ci plan --path … --github`; збіг `on.push.paths` ↔ `--path`; outputs-мапінг plan-джоби (кожен ключ, на який посилається гейт `needs.plan.outputs.<key>`, задекларований у `jobs.plan.outputs` і вказує на `steps.<id>.outputs.<key>` реального кроку — інакше гейт тихо порожній); для кожної lint-джоби — `needs: plan`, гейт по outputs, той самий `--path`, `--no-fix`, prep-крок, `fetch-depth: 0`; для термінальних джоб — транзитивну досяжність plan і всіх перевірок та skip-толерантний `if`.
16
16
 
17
17
  Що НЕ перевіряється (справа автора): добір доменів під вміст сервісу, зміст test/deploy-кроків, секрети/environment, uv-prep. Спільні workflow-інваріанти (persist-credentials, min-версії uses, існування paths-глобів) покривають `workflow_common` і `workflows`.
18
18
 
@@ -21,8 +21,7 @@
21
21
  # Ланцюги deploy (build → deploy) законні: досяжність — graph.reachable.
22
22
  #
23
23
  # Не перевіряється тут (лишається .mdc-каноном): вибір набору доменів під
24
- # сервіс, зміст test/deploy-кроків, outputs-мапінг plan-джоби, uv-prep для
25
- # python-стеку. Спільні workflow-перевірки (persist-credentials, min-версії
24
+ # сервіс, зміст test/deploy-кроків, uv-prep для python-стеку. Спільні workflow-перевірки (persist-credentials, min-версії
26
25
  # uses) — у ga.workflow_common; існування paths-глобів — у ga/workflows.
27
26
  package ga.service_deploy_workflow
28
27
 
@@ -65,6 +64,26 @@ service_paths contains p if {
65
64
  p := m[0][1]
66
65
  }
67
66
 
67
+ # Outputs-мапінг plan-джоби: гейт `needs.plan.outputs.<key>` працює лише коли
68
+ # jobs.plan.outputs декларує `<key>` з посиланням на `steps.<id>.outputs.<key>`
69
+ # кроку з таким id — інакше значення тихо порожнє і джоба скіпається завжди.
70
+ plan_outputs := object.get(object.get(jobs, "plan", {}), "outputs", {})
71
+
72
+ plan_step_ids contains id if {
73
+ some step in object.get(object.get(jobs, "plan", {}), "steps", [])
74
+ id := object.get(step, "id", "")
75
+ id != ""
76
+ }
77
+
78
+ # Ключі plan-outputs, на які посилаються гейти інших джоб.
79
+ gated_output_keys contains k if {
80
+ some name, job in jobs
81
+ name != "plan"
82
+ cond := object.get(job, "if", "")
83
+ some m in regex.find_all_string_submatch_n(`needs\.plan\.outputs\.([A-Za-z0-9_]+)`, cond, -1)
84
+ k := m[1]
85
+ }
86
+
68
87
  # Lint-джоби (крок `n-rules lint <domain> … --path …`) з розібраною командою.
69
88
  lint_cmds contains cmd if {
70
89
  some name, job in jobs
@@ -135,6 +154,24 @@ deny contains msg if {
135
154
  not contains(r, "--github")
136
155
  }
137
156
 
157
+ # ── deny: outputs-мапінг plan-джоби ─────────────────────────────────────
158
+
159
+ deny contains msg if {
160
+ is_service_workflow
161
+ jobs.plan
162
+ some k in gated_output_keys
163
+ not plan_outputs[k]
164
+ msg := sprintf("plan: jobs.plan.outputs не мапить `%s` (канон: `%s: ${{ steps.plan.outputs.%s }}`) — гейт `needs.plan.outputs.%s` тихо порожній і джоба скіпається завжди (ga.mdc)", [k, k, k, k])
165
+ }
166
+
167
+ deny contains msg if {
168
+ is_service_workflow
169
+ some k in gated_output_keys
170
+ v := plan_outputs[k]
171
+ not output_maps_plan_step(v, k)
172
+ msg := sprintf("plan: outputs.%s не посилається на `steps.<id>.outputs.%s` кроку з таким id — гейт тихо порожній (ga.mdc)", [k, k])
173
+ }
174
+
138
175
  # ── deny: тригер paths ↔ сервісний каталог ─────────────────────────────
139
176
 
140
177
  deny contains msg if {
@@ -245,6 +282,13 @@ glob_covers(sp) if {
245
282
  startswith(g, sp)
246
283
  }
247
284
 
285
+ # Значення output-а посилається на `steps.<id>.outputs.<key>` реального кроку.
286
+ output_maps_plan_step(v, k) if {
287
+ m := regex.find_all_string_submatch_n(sprintf(`steps\.([A-Za-z0-9_-]+)\.outputs\.%s`, [k]), v, 1)
288
+ count(m) > 0
289
+ m[0][1] in plan_step_ids
290
+ }
291
+
248
292
  job_has_prep(job) if {
249
293
  some step in object.get(job, "steps", [])
250
294
  step.uses == "./.github/actions/setup-bun-deps"