@7n/rules-ci-github 1.2.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.
Files changed (145) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/package.json +49 -0
  3. package/rules/abie/clean_merged_ignore_branches/clean_merged_ignore_branches.mdc +19 -0
  4. package/rules/abie/clean_merged_ignore_branches/clean_merged_ignore_branches.rego +81 -0
  5. package/rules/abie/clean_merged_ignore_branches/concern.json +8 -0
  6. package/rules/abie/clean_merged_ignore_branches/docs/fix-clean_merged_ignore_branches.md +30 -0
  7. package/rules/abie/clean_merged_ignore_branches/docs/index.md +9 -0
  8. package/rules/abie/clean_merged_ignore_branches/fix-clean_merged_ignore_branches.mjs +8 -0
  9. package/rules/abie/clean_merged_ignore_branches/template/clean-merged-branch.yml.snippet.yml +9 -0
  10. package/rules/docker/lint_docker_yml/concern.json +11 -0
  11. package/rules/docker/lint_docker_yml/docs/fix-lint_docker_yml.md +30 -0
  12. package/rules/docker/lint_docker_yml/docs/index.md +9 -0
  13. package/rules/docker/lint_docker_yml/fix-lint_docker_yml.mjs +5 -0
  14. package/rules/docker/lint_docker_yml/lint_docker_yml.mdc +14 -0
  15. package/rules/docker/lint_docker_yml/lint_docker_yml.rego +79 -0
  16. package/rules/docker/lint_docker_yml/template/lint-docker.yml.snippet.yml +41 -0
  17. package/rules/ga/clean_ga_workflows/clean_ga_workflows.mdc +20 -0
  18. package/rules/ga/clean_ga_workflows/clean_ga_workflows.rego +112 -0
  19. package/rules/ga/clean_ga_workflows/concern.json +10 -0
  20. package/rules/ga/clean_ga_workflows/docs/fix-clean_ga_workflows.md +33 -0
  21. package/rules/ga/clean_ga_workflows/docs/index.md +9 -0
  22. package/rules/ga/clean_ga_workflows/fix-clean_ga_workflows.mjs +8 -0
  23. package/rules/ga/clean_ga_workflows/template/clean-ga-workflows.yml.snippet.yml +26 -0
  24. package/rules/ga/clean_merged_branch/clean_merged_branch.mdc +24 -0
  25. package/rules/ga/clean_merged_branch/clean_merged_branch.rego +163 -0
  26. package/rules/ga/clean_merged_branch/concern.json +10 -0
  27. package/rules/ga/clean_merged_branch/docs/fix-clean_merged_branch.md +27 -0
  28. package/rules/ga/clean_merged_branch/docs/index.md +9 -0
  29. package/rules/ga/clean_merged_branch/fix-clean_merged_branch.mjs +8 -0
  30. package/rules/ga/clean_merged_branch/template/clean-merged-branch.yml.snippet.yml +37 -0
  31. package/rules/ga/docs/index.md +11 -0
  32. package/rules/ga/git_ai/concern.json +11 -0
  33. package/rules/ga/git_ai/docs/fix-git_ai.md +30 -0
  34. package/rules/ga/git_ai/docs/index.md +9 -0
  35. package/rules/ga/git_ai/fix-git_ai.mjs +5 -0
  36. package/rules/ga/git_ai/git_ai.mdc +19 -0
  37. package/rules/ga/git_ai/git_ai.rego +76 -0
  38. package/rules/ga/git_ai/template/git-ai.yml.snippet.yml +30 -0
  39. package/rules/ga/lint_ga/concern.json +10 -0
  40. package/rules/ga/lint_ga/docs/fix-lint_ga.md +32 -0
  41. package/rules/ga/lint_ga/docs/index.md +9 -0
  42. package/rules/ga/lint_ga/fix-lint_ga.mjs +5 -0
  43. package/rules/ga/lint_ga/lint_ga.mdc +21 -0
  44. package/rules/ga/lint_ga/lint_ga.rego +132 -0
  45. package/rules/ga/lint_ga/template/lint-ga.yml.snippet.yml +41 -0
  46. package/rules/ga/main.json +1 -0
  47. package/rules/ga/main.mdc +17 -0
  48. package/rules/ga/vscode_extensions/concern.json +11 -0
  49. package/rules/ga/vscode_extensions/docs/fix-vscode_extensions.md +25 -0
  50. package/rules/ga/vscode_extensions/docs/index.md +11 -0
  51. package/rules/ga/vscode_extensions/fix-vscode_extensions.mjs +1 -0
  52. package/rules/ga/vscode_extensions/template/extensions.json.snippet.json +1 -0
  53. package/rules/ga/vscode_extensions/vscode_extensions.mdc +9 -0
  54. package/rules/ga/vscode_extensions/vscode_extensions.rego +18 -0
  55. package/rules/ga/vscode_settings/concern.json +11 -0
  56. package/rules/ga/vscode_settings/docs/fix-vscode_settings.md +26 -0
  57. package/rules/ga/vscode_settings/docs/index.md +9 -0
  58. package/rules/ga/vscode_settings/fix-vscode_settings.mjs +5 -0
  59. package/rules/ga/vscode_settings/template/settings.json.snippet.json +1 -0
  60. package/rules/ga/vscode_settings/vscode_settings.mdc +9 -0
  61. package/rules/ga/vscode_settings/vscode_settings.rego +22 -0
  62. package/rules/ga/workflow_common/concern.json +9 -0
  63. package/rules/ga/workflow_common/template/uses-min-versions.snippet.json +4 -0
  64. package/rules/ga/workflow_common/workflow_common.mdc +18 -0
  65. package/rules/ga/workflow_common/workflow_common.rego +288 -0
  66. package/rules/ga/workflows/concern.json +8 -0
  67. package/rules/ga/workflows/docs/fix-workflows.md +37 -0
  68. package/rules/ga/workflows/docs/main.md +40 -0
  69. package/rules/ga/workflows/fix-workflows.mjs +212 -0
  70. package/rules/ga/workflows/main.mjs +436 -0
  71. package/rules/ga/workflows/workflows.mdc +33 -0
  72. package/rules/ga/zizmor_yml/concern.json +11 -0
  73. package/rules/ga/zizmor_yml/docs/fix-zizmor_yml.md +28 -0
  74. package/rules/ga/zizmor_yml/docs/index.md +9 -0
  75. package/rules/ga/zizmor_yml/fix-zizmor_yml.mjs +3 -0
  76. package/rules/ga/zizmor_yml/template/zizmor.yml.snippet.yml +5 -0
  77. package/rules/ga/zizmor_yml/zizmor_yml.mdc +9 -0
  78. package/rules/ga/zizmor_yml/zizmor_yml.rego +27 -0
  79. package/rules/js/lint_js_yml/concern.json +11 -0
  80. package/rules/js/lint_js_yml/docs/fix-lint_js_yml.md +27 -0
  81. package/rules/js/lint_js_yml/docs/index.md +9 -0
  82. package/rules/js/lint_js_yml/fix-lint_js_yml.mjs +5 -0
  83. package/rules/js/lint_js_yml/lint_js_yml.mdc +14 -0
  84. package/rules/js/lint_js_yml/lint_js_yml.rego +117 -0
  85. package/rules/js/lint_js_yml/template/lint-js.yml.snippet.yml +40 -0
  86. package/rules/k8s/lint_k8s_yml/concern.json +11 -0
  87. package/rules/k8s/lint_k8s_yml/docs/fix-lint_k8s_yml.md +30 -0
  88. package/rules/k8s/lint_k8s_yml/docs/index.md +9 -0
  89. package/rules/k8s/lint_k8s_yml/fix-lint_k8s_yml.mjs +5 -0
  90. package/rules/k8s/lint_k8s_yml/lint_k8s_yml.rego +57 -0
  91. package/rules/k8s/lint_k8s_yml/template/lint-k8s.yml.snippet.yml +43 -0
  92. package/rules/npm-module/npm_publish_yml/concern.json +12 -0
  93. package/rules/npm-module/npm_publish_yml/docs/fix-npm_publish_yml.md +27 -0
  94. package/rules/npm-module/npm_publish_yml/docs/index.md +9 -0
  95. package/rules/npm-module/npm_publish_yml/fix-npm_publish_yml.mjs +8 -0
  96. package/rules/npm-module/npm_publish_yml/template/npm-publish.yml.snippet.yml +49 -0
  97. package/rules/php/lint_php_yml/concern.json +11 -0
  98. package/rules/php/lint_php_yml/docs/fix-lint_php_yml.md +27 -0
  99. package/rules/php/lint_php_yml/docs/index.md +9 -0
  100. package/rules/php/lint_php_yml/fix-lint_php_yml.mjs +5 -0
  101. package/rules/php/lint_php_yml/lint_php_yml.mdc +23 -0
  102. package/rules/php/lint_php_yml/lint_php_yml.rego +34 -0
  103. package/rules/php/lint_php_yml/template/lint-php.yml.snippet.yml +47 -0
  104. package/rules/python/lint_python_yml/concern.json +11 -0
  105. package/rules/python/lint_python_yml/docs/fix-lint_python_yml.md +26 -0
  106. package/rules/python/lint_python_yml/docs/index.md +9 -0
  107. package/rules/python/lint_python_yml/fix-lint_python_yml.mjs +5 -0
  108. package/rules/python/lint_python_yml/lint_python_yml.mdc +12 -0
  109. package/rules/python/lint_python_yml/lint_python_yml.rego +68 -0
  110. package/rules/python/lint_python_yml/template/lint-python.yml.snippet.yml +41 -0
  111. package/rules/rust/lint_rust_yml/concern.json +11 -0
  112. package/rules/rust/lint_rust_yml/docs/fix-lint_rust_yml.md +27 -0
  113. package/rules/rust/lint_rust_yml/docs/index.md +9 -0
  114. package/rules/rust/lint_rust_yml/fix-lint_rust_yml.mjs +5 -0
  115. package/rules/rust/lint_rust_yml/lint_rust_yml.mdc +17 -0
  116. package/rules/rust/lint_rust_yml/lint_rust_yml.rego +79 -0
  117. package/rules/rust/lint_rust_yml/template/lint-rust.yml.snippet.yml +44 -0
  118. package/rules/rust/toolchain_cache/concern.json +11 -0
  119. package/rules/rust/toolchain_cache/docs/fix-toolchain_cache.md +35 -0
  120. package/rules/rust/toolchain_cache/docs/index.md +12 -0
  121. package/rules/rust/toolchain_cache/docs/main.md +57 -0
  122. package/rules/rust/toolchain_cache/fix-toolchain_cache.mjs +117 -0
  123. package/rules/rust/toolchain_cache/main.mjs +177 -0
  124. package/rules/rust/toolchain_cache/toolchain_cache.mdc +23 -0
  125. package/rules/security/lint_security_yml/concern.json +11 -0
  126. package/rules/security/lint_security_yml/docs/fix-lint_security_yml.md +30 -0
  127. package/rules/security/lint_security_yml/docs/index.md +9 -0
  128. package/rules/security/lint_security_yml/fix-lint_security_yml.mjs +8 -0
  129. package/rules/security/lint_security_yml/lint_security_yml.mdc +7 -0
  130. package/rules/security/lint_security_yml/lint_security_yml.rego +33 -0
  131. package/rules/security/lint_security_yml/template/lint-security.yml.snippet.yml +31 -0
  132. package/rules/style/lint_style_yml/concern.json +11 -0
  133. package/rules/style/lint_style_yml/docs/fix-lint_style_yml.md +30 -0
  134. package/rules/style/lint_style_yml/docs/index.md +9 -0
  135. package/rules/style/lint_style_yml/fix-lint_style_yml.mjs +5 -0
  136. package/rules/style/lint_style_yml/lint_style_yml.mdc +13 -0
  137. package/rules/style/lint_style_yml/lint_style_yml.rego +38 -0
  138. package/rules/style/lint_style_yml/template/lint-style.yml.snippet.yml +39 -0
  139. package/rules/text/lint_text/concern.json +10 -0
  140. package/rules/text/lint_text/docs/fix-lint_text.md +27 -0
  141. package/rules/text/lint_text/docs/index.md +9 -0
  142. package/rules/text/lint_text/fix-lint_text.mjs +5 -0
  143. package/rules/text/lint_text/lint_text.mdc +19 -0
  144. package/rules/text/lint_text/lint_text.rego +104 -0
  145. package/rules/text/lint_text/template/lint-text.yml.snippet.yml +61 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ ## [1.2.0] - 2026-07-16
4
+
5
+ ### Added
6
+
7
+ - Mixin-концерни з ядра: lint_*_yml для js/python/docker/k8s/style/php/rust/security/text, npm_publish_yml, rust/toolchain_cache, abie/clean_merged_ignore_branches — GitHub-реалізація CI-lint намірів доменних правил
8
+
9
+ ## [1.1.0] - 2026-07-15
10
+
11
+ ### Added
12
+
13
+ - Перший реліз @7n/rules-ci-github: правило `ga` (канон GitHub Actions) винесено з ядра @7n/rules у плагін; capability `ci:github`
14
+
15
+ All notable changes to this project will be documented in this file.
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@7n/rules-ci-github",
3
+ "version": "1.2.0",
4
+ "description": "\u041f\u043b\u0430\u0433\u0456\u043d @7n/rules: \u043a\u0430\u043d\u043e\u043d GitHub Actions (.github/workflows) \u2014 \u043e\u0431\u043e\u0432'\u044f\u0437\u043a\u043e\u0432\u0456 workflow, zizmor, VS Code",
5
+ "keywords": [
6
+ "github-actions",
7
+ "ci",
8
+ "lint",
9
+ "n-rules-plugin"
10
+ ],
11
+ "homepage": "https://github.com/nitra/7n-rules#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/nitra/7n-rules/issues"
14
+ },
15
+ "license": "ISC",
16
+ "author": "v@nitra.ai",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/nitra/7n-rules.git",
20
+ "directory": "plugins/ci-github"
21
+ },
22
+ "type": "module",
23
+ "files": [
24
+ "rules",
25
+ "CHANGELOG.md",
26
+ "!**/*.test.mjs",
27
+ "!**/*_test.rego",
28
+ "!**/fixtures/**",
29
+ "!**/tests/**"
30
+ ],
31
+ "n-rules": {
32
+ "capabilities": [
33
+ "ci:github"
34
+ ],
35
+ "contributes": {
36
+ "rules": true
37
+ }
38
+ },
39
+ "peerDependencies": {
40
+ "@7n/rules": ">=1.2.0"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "engines": {
46
+ "bun": ">=1.3",
47
+ "node": ">=24"
48
+ }
49
+ }
@@ -0,0 +1,19 @@
1
+ ## Rego: `ignore_branches` у `clean-merged-branch.yml`
2
+
3
+ Rego-пакет: `abie.clean_merged_ignore_branches`
4
+
5
+ Цільовий файл: `.github/workflows/clean-merged-branch.yml`.
6
+
7
+ Перевіряє, що у workflow-файлі є крок з `uses: phpdocker-io/github-actions-delete-abandoned-branches` і що його `with.ignore_branches` містить обидва токени `dev` та `ua` (case-insensitive, пробіли навколо токенів обрізаються). Токени зчитуються з template-файлу `data.template.snippet` — зміна канону автоматично змінює вимоги.
8
+
9
+ Канонічний snippet: [clean-merged-branch.yml.snippet.yml](./template/clean-merged-branch.yml.snippet.yml)
10
+
11
+ ```yaml title=".github/workflows/clean-merged-branch.yml (фрагмент)"
12
+ - uses: phpdocker-io/github-actions-delete-abandoned-branches@v2
13
+ with:
14
+ ignore_branches: "dev,ua,main" # ✓ містить dev та ua
15
+ ```
16
+
17
+ ```yaml
18
+ ignore_branches: "main" # ✗ не вистачає dev,ua
19
+ ```
@@ -0,0 +1,81 @@
1
+ # Перевірка `clean-merged-branch.yml` для abie-проєктів (abie.mdc).
2
+ #
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/clean-merged-branch.yml.snippet.yml.
5
+ # Action-маркер (`uses:` substring) і required branches (parsed з template's
6
+ # `ignore_branches`) читаються з template. ga.clean_merged_branch перевіряє
7
+ # повний канон workflow окремо; цей пакет — лише abie-specific шар.
8
+ package abie.clean_merged_ignore_branches
9
+
10
+ import rego.v1
11
+
12
+ # Експектації з template's step (першого з steps).
13
+ expected_step := step if some step in data.template.snippet.jobs.cleanup_old_branches.steps
14
+
15
+ target_action_marker := expected_step.uses
16
+
17
+ required_branches := parsed_ignore_tokens(expected_step.with.ignore_branches)
18
+
19
+ step_missing_msg := sprintf(
20
+ "clean-merged-branch.yml: не знайдено крок з uses: %s (abie.mdc)",
21
+ [target_action_marker],
22
+ )
23
+
24
+ ignore_branches_missing_msg := sprintf(
25
+ "clean-merged-branch.yml: не знайдено with.ignore_branches у кроці %s (abie.mdc)",
26
+ [target_action_marker],
27
+ )
28
+
29
+ # ── deny ─────────────────────────────────────────────────────────────────
30
+
31
+ deny contains step_missing_msg if {
32
+ count(target_steps) == 0
33
+ }
34
+
35
+ deny contains ignore_branches_missing_msg if {
36
+ count(target_steps) > 0
37
+ not has_ignore_branches_value
38
+ }
39
+
40
+ deny contains msg if {
41
+ count(target_steps) > 0
42
+ ignore_branches_value != ""
43
+ missing := required_branches - parsed_ignore_tokens(ignore_branches_value)
44
+ count(missing) > 0
45
+ msg := sprintf(
46
+ "clean-merged-branch.yml: ignore_branches має містити %v (зараз: %q; не вистачає: %v) (abie.mdc)",
47
+ [concat(",", sort(required_branches)), ignore_branches_value, concat(",", sort(missing))],
48
+ )
49
+ }
50
+
51
+ # ── helpers ──────────────────────────────────────────────────────────────
52
+
53
+ target_steps contains step if {
54
+ some job in object.get(input, "jobs", {})
55
+ some step in object.get(job, "steps", [])
56
+ uses := object.get(step, "uses", "")
57
+ is_string(uses)
58
+ contains(uses, target_action_marker)
59
+ }
60
+
61
+ has_ignore_branches_value if {
62
+ some step in target_steps
63
+ v := object.get(object.get(step, "with", {}), "ignore_branches", null)
64
+ is_string(v)
65
+ }
66
+
67
+ default ignore_branches_value := ""
68
+
69
+ ignore_branches_value := values[0] if {
70
+ values := [v |
71
+ some step in target_steps
72
+ v := object.get(object.get(step, "with", {}), "ignore_branches", null)
73
+ is_string(v)
74
+ ]
75
+ count(values) > 0
76
+ }
77
+
78
+ parsed_ignore_tokens(value) := {lower(trim_space(part)) |
79
+ some part in split(value, ",")
80
+ trim_space(part) != ""
81
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
+ "policy": {
4
+ "files": {
5
+ "single": ".github/workflows/clean-merged-branch.yml"
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,30 @@
1
+ ---
2
+ type: JS Module
3
+ title: fix-clean_merged_ignore_branches.mjs
4
+ resource: plugins/ci-github/rules/abie/clean_merged_ignore_branches/fix-clean_merged_ignore_branches.mjs
5
+ docgen:
6
+ crc: f3646572
7
+ model: openai-codex/gpt-5.5
8
+ score: 100
9
+ issues: judge:inaccurate:0.99
10
+ judgeModel: openai-codex/gpt-5.4-mini
11
+ ---
12
+
13
+ ## Огляд
14
+
15
+ `patterns` визначає набір шляхів репозиторію, які правило має враховувати під час перевірки. Воно свідомо не охоплює `.git` і `.github`, щоб не аналізувати службові дані Git та конфігурацію GitHub. Правило працює read-only: лише повідомляє про невідповідність очікуваній структурі, не змінюючи файли чи зовнішні сховища.
16
+
17
+ ## Поведінка
18
+
19
+ 1. `patterns` визначає правило виправлення, яке забезпечує наявність workflow для очищення merged branches.
20
+
21
+ 2. Правило орієнтується на шаблонний стан файлу `.github/workflows/clean-merged-branch.yml`, щоб уніфікувати поведінку репозиторіїв щодо прибирання вже злитих гілок.
22
+
23
+ 3. Під час перевірки свідомо не обходить службові шляхи `.github` і `.git`, окрім цільового workflow-файлу, який є предметом виправлення.
24
+
25
+ 4. `patterns` лише описує очікуване виправлення й не виконує самостійного запису у файлову систему чи інші сховища.
26
+
27
+ ## Гарантії поведінки
28
+
29
+ - Read-only: не виконує операцій запису (ФС/БД).
30
+ - Свідомо пропускає шляхи: `.github`, `.git`.
@@ -0,0 +1,9 @@
1
+ ---
2
+ type: Directory Index
3
+ title: npm/rules/abie/clean_merged_ignore_branches
4
+ resource: npm/rules/abie/clean_merged_ignore_branches/
5
+ ---
6
+
7
+ | Файл | Тип |
8
+ | --------------------------------------------------------------------------- | --------- |
9
+ | [fix-clean_merged_ignore_branches.mjs](fix-clean_merged_ignore_branches.md) | JS Module |
@@ -0,0 +1,8 @@
1
+ import { createTemplateFixPattern } from '@7n/rules/scripts/lib/fix/template-deep-merge.mjs'
2
+
3
+ export const patterns = [
4
+ createTemplateFixPattern({
5
+ id: 'abie-clean_merged_ignore_branches-template',
6
+ targetPath: '.github/workflows/clean-merged-branch.yml'
7
+ })
8
+ ]
@@ -0,0 +1,9 @@
1
+ # abie-specific шар поверх ga.clean_merged_branch:
2
+ # у `clean-merged-branch.yml` крок phpdocker-io/github-actions-delete-abandoned-branches
3
+ # має містити `ignore_branches: dev,ua` (обовʼязкові гілки для abie-проєктів).
4
+ jobs:
5
+ cleanup_old_branches:
6
+ steps:
7
+ - uses: phpdocker-io/github-actions-delete-abandoned-branches
8
+ with:
9
+ ignore_branches: dev,ua
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
+ "fixability": "config",
4
+ "policy": {
5
+ "files": {
6
+ "single": ".github/workflows/lint-docker.yml",
7
+ "required": true
8
+ },
9
+ "missingMessage": ".github/workflows/lint-docker.yml не існує — створи за каноном правила (mixin @7n/rules-ci-github)"
10
+ }
11
+ }
@@ -0,0 +1,30 @@
1
+ ---
2
+ type: JS Module
3
+ title: fix-lint_docker_yml.mjs
4
+ resource: plugins/ci-github/rules/docker/lint_docker_yml/fix-lint_docker_yml.mjs
5
+ docgen:
6
+ crc: cbc93b9d
7
+ model: openai-codex/gpt-5.5
8
+ score: 100
9
+ issues: judge:inaccurate:0.98
10
+ judgeModel: openai-codex/gpt-5.4-mini
11
+ ---
12
+
13
+ ## Огляд
14
+
15
+ `patterns` визначає поведінку правила для обходу проєкту без змін у файловій системі чи зовнішніх сховищах. Службові директорії `.github` і `.git` свідомо не входять до перевірюваних шляхів, щоб правило працювало лише з релевантним кодом проєкту.
16
+
17
+ ## Поведінка
18
+
19
+ 1. `patterns` визначає правило автоматичного виправлення для workflow перевірки Docker.
20
+
21
+ 2. `patterns` забезпечує наявність стандартного файлу `.github/workflows/lint-docker.yml`, щоб проєкт мав єдиний CI-контроль Docker-конфігурації.
22
+
23
+ 3. `patterns` працює як read-only опис виправлення: сам модуль не змінює файлову систему чи зовнішні сховища.
24
+
25
+ 4. Під час супровідного обходу шляхів свідомо не аналізуються `.github` і `.git`, щоб не змішувати службові директорії з основним кодом проєкту.
26
+
27
+ ## Гарантії поведінки
28
+
29
+ - Read-only: не виконує операцій запису (ФС/БД).
30
+ - Свідомо пропускає шляхи: `.github`, `.git`.
@@ -0,0 +1,9 @@
1
+ ---
2
+ type: Directory Index
3
+ title: npm/rules/docker/lint_docker_yml
4
+ resource: npm/rules/docker/lint_docker_yml/
5
+ ---
6
+
7
+ | Файл | Тип |
8
+ | ------------------------------------------------- | --------- |
9
+ | [fix-lint_docker_yml.mjs](fix-lint_docker_yml.md) | JS Module |
@@ -0,0 +1,5 @@
1
+ import { createTemplateFixPattern } from '@7n/rules/scripts/lib/fix/template-deep-merge.mjs'
2
+
3
+ export const patterns = [
4
+ createTemplateFixPattern({ id: 'docker-lint_docker_yml-template', targetPath: '.github/workflows/lint-docker.yml' })
5
+ ]
@@ -0,0 +1,14 @@
1
+ ## Перевірка `.github/workflows/lint-docker.yml`
2
+
3
+ Rego-пакет: `docker.lint_docker_yml`
4
+
5
+ Цільовий файл: `.github/workflows/lint-docker.yml`
6
+
7
+ Перевіряє, що CI-workflow lint-docker відповідає канонічному сніпету:
8
+
9
+ - `on.push.paths` містить усі три glob-маски: `**/Dockerfile`, `**/*.Dockerfile`, `**/*.dockerfile`
10
+ - присутні кроки `uses: actions/checkout@v6` та `uses: ./.github/actions/setup-bun-deps`
11
+ - крок install hadolint містить рядок із версією `v2.12.0` (substring-перевірка)
12
+ - крок lint містить `n-rules lint docker --no-fix`
13
+
14
+ Канонічний шаблон: [lint-docker.yml.snippet.yml](./template/lint-docker.yml.snippet.yml)
@@ -0,0 +1,79 @@
1
+ # Перевірка `.github/workflows/lint-docker.yml` (docker.mdc).
2
+ #
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/lint-docker.yml.snippet.yml.
5
+ # Path-маркери, hadolint версія (substring "v2.12.0" у run), setup-bun-deps
6
+ # composite, `n-rules lint docker --no-fix` substring — все читається з template's snippet.
7
+ # Універсальні workflow-перевірки — у `ga.workflow_common`.
8
+ package docker.lint_docker_yml
9
+
10
+ import rego.v1
11
+
12
+ # Очікувані літерали з template.
13
+ expected_paths := {p | some p in data.template.snippet.on.push.paths}
14
+
15
+ # Required uses set — з template's steps.
16
+ expected_uses_set contains u if {
17
+ some step in data.template.snippet.jobs["lint-docker"].steps
18
+ u := object.get(step, "uses", "")
19
+ u != ""
20
+ }
21
+
22
+ # Required run substrings — collected per-step з template.
23
+ expected_run_substrings contains r if {
24
+ some step in data.template.snippet.jobs["lint-docker"].steps
25
+ r := object.get(step, "run", "")
26
+ r != ""
27
+ }
28
+
29
+ # Аліаси на input.
30
+ all_step_uses contains u if {
31
+ some job in object.get(input, "jobs", {})
32
+ some step in object.get(job, "steps", [])
33
+ u := object.get(step, "uses", "")
34
+ u != ""
35
+ }
36
+
37
+ all_run_text := concat("\n", [run_text |
38
+ some job in object.get(input, "jobs", {})
39
+ some step in object.get(job, "steps", [])
40
+ run_text := step_run_to_text(step)
41
+ ])
42
+
43
+ # conftest парсить YAML 1.1, тож канонічний `on:` без лапок стає булевим ключем
44
+ # `true` (як у `ga.lint_ga`). Тому читаємо через `input["true"]`.
45
+ push_paths_set := {p |
46
+ some p in object.get(object.get(object.get(input, "true", {}), "push", {}), "paths", [])
47
+ }
48
+
49
+ # ── deny: on.push.paths subset-of ──────────────────────────────────────
50
+
51
+ deny contains msg if {
52
+ some required in expected_paths
53
+ not required in push_paths_set
54
+ msg := sprintf("lint-docker.yml: on.push.paths має містити %q (docker.mdc)", [required])
55
+ }
56
+
57
+ # ── deny: required uses present ────────────────────────────────────────
58
+
59
+ deny contains msg if {
60
+ some required_use in expected_uses_set
61
+ not required_use in all_step_uses
62
+ msg := sprintf("lint-docker.yml: відсутній крок `uses: %s` (docker.mdc)", [required_use])
63
+ }
64
+
65
+ # ── deny: required run substrings ──────────────────────────────────────
66
+
67
+ deny contains msg if {
68
+ some required_run in expected_run_substrings
69
+ not contains(all_run_text, required_run)
70
+ msg := sprintf("lint-docker.yml: жоден крок run не містить %q (docker.mdc)", [required_run])
71
+ }
72
+
73
+ # ── helpers ────────────────────────────────────────────────────────────
74
+
75
+ step_run_to_text(step) := step.run if is_string(step.run)
76
+
77
+ else := concat("\n", [s | some s in step.run]) if is_array(step.run)
78
+
79
+ else := ""
@@ -0,0 +1,41 @@
1
+ name: Lint Docker
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - dev
7
+ - main
8
+ paths:
9
+ - '**/Dockerfile'
10
+ - '**/*.Dockerfile'
11
+ - '**/*.dockerfile'
12
+
13
+ pull_request:
14
+ branches:
15
+ - dev
16
+ - main
17
+
18
+ concurrency:
19
+ group: ${{ github.ref }}-${{ github.workflow }}
20
+ cancel-in-progress: true
21
+
22
+ jobs:
23
+ lint-docker:
24
+ runs-on: ubuntu-latest
25
+ permissions:
26
+ contents: read
27
+ steps:
28
+ - uses: actions/checkout@v6
29
+ with:
30
+ persist-credentials: false
31
+
32
+ - name: Install hadolint
33
+ run: |
34
+ curl -sSL -o /tmp/hadolint https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64
35
+ chmod +x /tmp/hadolint
36
+ sudo mv /tmp/hadolint /usr/local/bin/hadolint
37
+
38
+ - uses: ./.github/actions/setup-bun-deps
39
+
40
+ - name: Lint Docker
41
+ run: bunx n-rules lint docker --no-fix
@@ -0,0 +1,20 @@
1
+ ## Структура `clean-ga-workflows.yml` — видалення завершених workflow runs
2
+
3
+ Rego-пакет: `ga.clean_ga_workflows`
4
+
5
+ **Цільовий файл:** `.github/workflows/clean-ga-workflows.yml`
6
+
7
+ ### Що перевіряється
8
+
9
+ - `name` — точна відповідність значенню з template
10
+ - `on.schedule[].cron` — має містити cron із template (`0 1 16 * *`)
11
+ - `on.workflow_dispatch` — має бути об'єктом `{}`
12
+ - `jobs.cleanup_old_workflows` — job має існувати
13
+ - `jobs.cleanup_old_workflows.runs-on` — відповідно до template (`ubuntu-latest`)
14
+ - `jobs.cleanup_old_workflows.permissions` — `actions: write`, `contents: read`
15
+ - `steps[0].name` і `steps[0].uses` — відповідно до template (`dmvict/clean-workflow-runs@v1`)
16
+ - `steps[0].with` — `token`, `save_period`, `save_min_runs_number` як у template
17
+
18
+ **Pin-aware `uses`:** SHA-пін того самого action (`owner/action@<40-hex SHA>`, з тег-коментарем `# vX` чи без) задовольняє канонічний тег із template. Якщо в репо вже SHA-пін (zizmor-політика ref-pin) — НЕ замінюй його тегом.
19
+
20
+ Канон: [clean-ga-workflows.yml.snippet.yml](./template/clean-ga-workflows.yml.snippet.yml)
@@ -0,0 +1,112 @@
1
+ # Перевірка `.github/workflows/clean-ga-workflows.yml` (ga.mdc).
2
+ #
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/clean-ga-workflows.yml.snippet.yml
5
+ # (повний YAML канон). Path-and-value перевірки — у цьому rego (per-concern
6
+ # field-by-field з .data.template.snippet.<path>); жодних inline literals.
7
+ package ga.clean_ga_workflows
8
+
9
+ import rego.v1
10
+
11
+ # ── Аліаси на input ────────────────────────────────────────────────────────
12
+ # GHA YAML quirk: ключ `on:` — YAML 1.1 boolean `true`, у conftest серіалізується
13
+ # як рядковий ключ "true". Template (через --data JSON) має ключ "on".
14
+
15
+ gha_on := input["true"]
16
+
17
+ step0 := input.jobs.cleanup_old_workflows.steps[0]
18
+
19
+ # Експектації з template.
20
+ expected_name := data.template.snippet.name
21
+
22
+ expected_cron := data.template.snippet.on.schedule[0].cron
23
+
24
+ expected_step0 := data.template.snippet.jobs.cleanup_old_workflows.steps[0]
25
+
26
+ expected_perms := data.template.snippet.jobs.cleanup_old_workflows.permissions
27
+
28
+ expected_runs_on := data.template.snippet.jobs.cleanup_old_workflows["runs-on"]
29
+
30
+ # ── deny rules ─────────────────────────────────────────────────────────────
31
+
32
+ deny contains msg if {
33
+ input.name != expected_name
34
+ msg := sprintf("clean-ga-workflows.yml: name має бути %q (ga.mdc)", [expected_name])
35
+ }
36
+
37
+ deny contains msg if {
38
+ not has_expected_cron
39
+ msg := sprintf("clean-ga-workflows.yml: on.schedule має містити cron: '%s' (ga.mdc)", [expected_cron])
40
+ }
41
+
42
+ deny contains msg if {
43
+ not is_object(object.get(gha_on, "workflow_dispatch", null))
44
+ msg := "clean-ga-workflows.yml: має бути workflow_dispatch: {} (ga.mdc)"
45
+ }
46
+
47
+ deny contains msg if {
48
+ not input.jobs.cleanup_old_workflows
49
+ msg := "clean-ga-workflows.yml: jobs.cleanup_old_workflows відсутній (ga.mdc)"
50
+ }
51
+
52
+ deny contains msg if {
53
+ job := input.jobs.cleanup_old_workflows
54
+ job["runs-on"] != expected_runs_on
55
+ msg := sprintf("clean-ga-workflows.yml: runs-on має бути %s (ga.mdc)", [expected_runs_on])
56
+ }
57
+
58
+ deny contains msg if {
59
+ perms := input.jobs.cleanup_old_workflows.permissions
60
+ not perms_match(perms, expected_perms)
61
+ msg := "clean-ga-workflows.yml: permissions мають бути actions: write, contents: read (ga.mdc)"
62
+ }
63
+
64
+ deny contains msg if {
65
+ step0.name != expected_step0.name
66
+ msg := sprintf("clean-ga-workflows.yml: перший крок має мати name: %s (ga.mdc)", [expected_step0.name])
67
+ }
68
+
69
+ deny contains msg if {
70
+ actual := step0.uses
71
+ not uses_satisfies(actual, expected_step0.uses)
72
+ msg := sprintf("clean-ga-workflows.yml: перший крок має uses: %s (ga.mdc)", [expected_step0.uses])
73
+ }
74
+
75
+ deny contains msg if {
76
+ not step0_with_canonical
77
+ msg := "clean-ga-workflows.yml: with має містити token/save_period/save_min_runs_number як у template (ga.mdc)"
78
+ }
79
+
80
+ deny contains msg if {
81
+ step0.with.save_period != expected_step0.with.save_period
82
+ msg := sprintf("clean-ga-workflows.yml: with.save_period має бути %d (ga.mdc)", [expected_step0.with.save_period])
83
+ }
84
+
85
+ # ── helpers ────────────────────────────────────────────────────────────────
86
+
87
+ # `uses:` з input задовольняє канонічний `owner/action@tag`: точний збіг…
88
+ uses_satisfies(actual, expected) if actual == expected
89
+
90
+ # …або той самий action-slug і ref — повний 40-hex commit SHA (zizmor ref-pin).
91
+ # SHA-пін відповідної версії задовольняє вимогу тега — не даунгрейдимо до тега.
92
+ uses_satisfies(actual, expected) if {
93
+ slug := split(expected, "@")[0]
94
+ startswith(actual, concat("", [slug, "@"]))
95
+ parts := split(actual, "@")
96
+ regex.match(`^[0-9a-fA-F]{40}$`, parts[count(parts) - 1])
97
+ }
98
+
99
+ has_expected_cron if {
100
+ gha_on.schedule[_].cron == expected_cron
101
+ }
102
+
103
+ perms_match(actual, expected) if {
104
+ actual.actions == expected.actions
105
+ actual.contents == expected.contents
106
+ }
107
+
108
+ step0_with_canonical if {
109
+ step0.with.token == expected_step0.with.token
110
+ step0.with.save_period == expected_step0.with.save_period
111
+ step0.with.save_min_runs_number == expected_step0.with.save_min_runs_number
112
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
+ "policy": {
4
+ "files": {
5
+ "single": ".github/workflows/clean-ga-workflows.yml",
6
+ "required": true
7
+ },
8
+ "missingMessage": ".github/workflows/clean-ga-workflows.yml не існує — створи за каноном ga.mdc"
9
+ }
10
+ }
@@ -0,0 +1,33 @@
1
+ ---
2
+ type: JS Module
3
+ title: fix-clean_ga_workflows.mjs
4
+ resource: plugins/ci-github/rules/ga/clean_ga_workflows/fix-clean_ga_workflows.mjs
5
+ docgen:
6
+ crc: f149bf34
7
+ model: openai-codex/gpt-5.5
8
+ tier: cloud-avg
9
+ score: 100
10
+ issues: judge:inaccurate:0.99
11
+ judgeModel: openai-codex/gpt-5.4-mini
12
+ ---
13
+
14
+ ## Огляд
15
+
16
+ `patterns` визначає, які шляхи правило свідомо не розглядає як цільові для перевірки: `.github` і `.git` пропускаються, щоб службові каталоги репозиторію не змішувалися з робочим кодом.
17
+
18
+ Правило працює read-only: не записує у файлову систему чи бази даних.
19
+
20
+ ## Поведінка
21
+
22
+ 1. `patterns` визначає правило автоматичного виправлення для workflow очищення GitHub Actions.
23
+
24
+ 2. Правило гарантує наявність очікуваного файлу `.github/workflows/clean-ga-workflows.yml` на основі затвердженого шаблону.
25
+
26
+ 3. Виправлення працює як read-only опис дії: саме по собі не записує зміни у файлову систему чи зовнішні сховища.
27
+
28
+ 4. Під час застосування загальних перевірок свідомо не розглядаються службові шляхи `.github` і `.git`, щоб не аналізувати інфраструктурні каталоги як звичайний код.
29
+
30
+ ## Гарантії поведінки
31
+
32
+ - Read-only: не виконує операцій запису (ФС/БД).
33
+ - Свідомо пропускає шляхи: `.github`, `.git`.
@@ -0,0 +1,9 @@
1
+ ---
2
+ type: Directory Index
3
+ title: plugins/ci-github/rules/ga/clean_ga_workflows
4
+ resource: plugins/ci-github/rules/ga/clean_ga_workflows/
5
+ ---
6
+
7
+ | Файл | Тип |
8
+ | ------------------------------------------------------- | --------- |
9
+ | [fix-clean_ga_workflows.mjs](fix-clean_ga_workflows.md) | JS Module |
@@ -0,0 +1,8 @@
1
+ import { createTemplateFixPattern } from '@7n/rules/scripts/lib/fix/template-deep-merge.mjs'
2
+
3
+ export const patterns = [
4
+ createTemplateFixPattern({
5
+ id: 'ga-clean_ga_workflows-template',
6
+ targetPath: '.github/workflows/clean-ga-workflows.yml'
7
+ })
8
+ ]