@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
@@ -0,0 +1,26 @@
1
+ name: Clean action for removing completed workflow runs
2
+
3
+ on:
4
+ schedule:
5
+ - cron: '0 1 16 * *'
6
+
7
+ # Allow workflow to be manually run from the GitHub UI
8
+ workflow_dispatch: {}
9
+
10
+ concurrency:
11
+ group: ${{ github.ref }}-${{ github.workflow }}
12
+ cancel-in-progress: true
13
+
14
+ jobs:
15
+ cleanup_old_workflows:
16
+ runs-on: ubuntu-latest
17
+ permissions:
18
+ actions: write
19
+ contents: read
20
+ steps:
21
+ - name: Delete workflow runs
22
+ uses: dmvict/clean-workflow-runs@v1
23
+ with:
24
+ token: ${{ github.token }}
25
+ save_period: 31
26
+ save_min_runs_number: 0
@@ -0,0 +1,24 @@
1
+ ## Структура `clean-merged-branch.yml` — видалення злитих гілок
2
+
3
+ Rego-пакет: `ga.clean_merged_branch`
4
+
5
+ **Цільовий файл:** `.github/workflows/clean-merged-branch.yml`
6
+
7
+ ### Що перевіряється
8
+
9
+ - `name` — точна відповідність значенню з template
10
+ - `on.schedule[].cron` — має містити cron із template (`0 1 15 * *`)
11
+ - `on.workflow_dispatch` — має бути об'єктом `{}`
12
+ - `jobs.cleanup_old_branches` — job має існувати
13
+ - `jobs.cleanup_old_branches.permissions` — кожне поле із template (`contents: write`, `pull-requests: read`)
14
+ - `steps` — має бути рівно 2 кроки
15
+ - `steps[0].id` — `delete_stuff`; `steps[0].uses` — `phpdocker-io/github-actions-delete-abandoned-branches@v2.0.3`
16
+ - `steps[0].with.github_token`, `last_commit_age_days` — за template
17
+ - `steps[0].with.ignore_branches` — має містити всі гілки з template (`main,dev`)
18
+ - `steps[0].with.dry_run` — `no` (нормалізується: YAML 1.1 `no` → boolean `false` у conftest)
19
+ - `steps[1].name` — за template; `steps[1].env.DELETED_BRANCHES` — за template
20
+ - `steps[1].run` — містить `Deleted branches:` та `${DELETED_BRANCHES}`
21
+
22
+ **Pin-aware `uses`:** SHA-пін того самого action (`owner/action@<40-hex SHA>`, з тег-коментарем `# vX` чи без) задовольняє канонічний тег із template. Якщо в репо вже SHA-пін (zizmor-політика ref-pin) — НЕ замінюй його тегом.
23
+
24
+ Канон: [clean-merged-branch.yml.snippet.yml](./template/clean-merged-branch.yml.snippet.yml)
@@ -0,0 +1,163 @@
1
+ # Перевірка `.github/workflows/clean-merged-branch.yml` (ga.mdc).
2
+ #
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/clean-merged-branch.yml.snippet.yml.
5
+ package ga.clean_merged_branch
6
+
7
+ import rego.v1
8
+
9
+ # ── Аліаси ─────────────────────────────────────────────────────────────────
10
+
11
+ gha_on := input["true"]
12
+
13
+ steps := input.jobs.cleanup_old_branches.steps
14
+
15
+ step0 := steps[0]
16
+
17
+ step1 := steps[1]
18
+
19
+ expected_name := data.template.snippet.name
20
+
21
+ expected_cron := data.template.snippet.on.schedule[0].cron
22
+
23
+ expected_step0 := data.template.snippet.jobs.cleanup_old_branches.steps[0]
24
+
25
+ expected_step1 := data.template.snippet.jobs.cleanup_old_branches.steps[1]
26
+
27
+ expected_perms := data.template.snippet.jobs.cleanup_old_branches.permissions
28
+
29
+ # ── deny rules ─────────────────────────────────────────────────────────────
30
+
31
+ deny contains msg if {
32
+ input.name != expected_name
33
+ msg := sprintf("clean-merged-branch.yml: name має бути %q (ga.mdc)", [expected_name])
34
+ }
35
+
36
+ deny contains msg if {
37
+ not has_expected_cron
38
+ msg := sprintf("clean-merged-branch.yml: on.schedule має містити cron: '%s' (ga.mdc)", [expected_cron])
39
+ }
40
+
41
+ deny contains msg if {
42
+ not is_object(object.get(gha_on, "workflow_dispatch", null))
43
+ msg := "clean-merged-branch.yml: має бути workflow_dispatch: {} (ga.mdc)"
44
+ }
45
+
46
+ deny contains msg if {
47
+ not input.jobs.cleanup_old_branches
48
+ msg := "clean-merged-branch.yml: jobs.cleanup_old_branches відсутній (ga.mdc)"
49
+ }
50
+
51
+ deny contains msg if {
52
+ some permission, expected in expected_perms
53
+ object.get(input.jobs.cleanup_old_branches.permissions, permission, null) != expected
54
+ msg := sprintf("clean-merged-branch.yml: permissions.%s має бути %s (ga.mdc)", [permission, expected])
55
+ }
56
+
57
+ deny contains msg if {
58
+ count(steps) < 2
59
+ msg := "clean-merged-branch.yml: steps має містити 2 кроки як у template (ga.mdc)"
60
+ }
61
+
62
+ # ── Step 0 (delete_stuff) ──────────────────────────────────────────────────
63
+
64
+ deny contains msg if {
65
+ step0.id != expected_step0.id
66
+ msg := sprintf("clean-merged-branch.yml: перший крок має id: %s (ga.mdc)", [expected_step0.id])
67
+ }
68
+
69
+ deny contains msg if {
70
+ actual := step0.uses
71
+ not uses_satisfies(actual, expected_step0.uses)
72
+ msg := sprintf("clean-merged-branch.yml: перший крок має uses: %s (ga.mdc)", [expected_step0.uses])
73
+ }
74
+
75
+ deny contains msg if {
76
+ step0.with.github_token != expected_step0.with.github_token
77
+ msg := sprintf(
78
+ "clean-merged-branch.yml: with.github_token має бути %s (ga.mdc)",
79
+ [expected_step0.with.github_token],
80
+ )
81
+ }
82
+
83
+ deny contains msg if {
84
+ step0.with.last_commit_age_days != expected_step0.with.last_commit_age_days
85
+ msg := sprintf(
86
+ "clean-merged-branch.yml: with.last_commit_age_days має бути %d (ga.mdc)",
87
+ [expected_step0.with.last_commit_age_days],
88
+ )
89
+ }
90
+
91
+ deny contains msg if {
92
+ not ignore_branches_subset
93
+ msg := sprintf(
94
+ "clean-merged-branch.yml: with.ignore_branches має містити %s (ga.mdc)",
95
+ [expected_step0.with.ignore_branches],
96
+ )
97
+ }
98
+
99
+ # YAML 1.1 quirk: `dry_run: no` парситься як boolean false у Go-yaml (conftest).
100
+ # Template (від `yaml` npm) читає `no` як рядок, тому нормалізуємо обидві форми.
101
+ deny contains msg if {
102
+ normalize_dry_run(step0.with.dry_run) != normalize_dry_run(expected_step0.with.dry_run) # noqa: rules-style-no-equality-with-false
103
+ msg := "clean-merged-branch.yml: with.dry_run має бути no (ga.mdc)"
104
+ }
105
+
106
+ # ── Step 1 (Get output) ────────────────────────────────────────────────────
107
+
108
+ deny contains msg if {
109
+ step1.name != expected_step1.name
110
+ msg := sprintf("clean-merged-branch.yml: другий крок має name: %s (ga.mdc)", [expected_step1.name])
111
+ }
112
+
113
+ deny contains msg if {
114
+ step1.env.DELETED_BRANCHES != expected_step1.env.DELETED_BRANCHES
115
+ msg := "clean-merged-branch.yml: env.DELETED_BRANCHES має бути як у template (ga.mdc)"
116
+ }
117
+
118
+ deny contains msg if {
119
+ not echo_deleted_branches
120
+ msg := "clean-merged-branch.yml: run має echo Deleted branches як у template (ga.mdc)"
121
+ }
122
+
123
+ # ── helpers ────────────────────────────────────────────────────────────────
124
+
125
+ # `uses:` з input задовольняє канонічний `owner/action@tag`: точний збіг…
126
+ uses_satisfies(actual, expected) if actual == expected
127
+
128
+ # …або той самий action-slug і ref — повний 40-hex commit SHA (zizmor ref-pin).
129
+ # SHA-пін відповідної версії задовольняє вимогу тега — не даунгрейдимо до тега.
130
+ uses_satisfies(actual, expected) if {
131
+ slug := split(expected, "@")[0]
132
+ startswith(actual, concat("", [slug, "@"]))
133
+ parts := split(actual, "@")
134
+ regex.match(`^[0-9a-fA-F]{40}$`, parts[count(parts) - 1])
135
+ }
136
+
137
+ has_expected_cron if {
138
+ gha_on.schedule[_].cron == expected_cron
139
+ }
140
+
141
+ # Кожна гілка з template-літералу (через кому) має бути присутня у actual.
142
+ ignore_branches_subset if {
143
+ required_branches := split(expected_step0.with.ignore_branches, ",")
144
+ actual := step0.with.ignore_branches
145
+ every b in required_branches {
146
+ contains(actual, trim_space(b))
147
+ }
148
+ }
149
+
150
+ echo_deleted_branches if {
151
+ # Звіряємо substring "echo "Deleted branches: …${DELETED_BRANCHES}…"" — формується з template run.
152
+ contains(step1.run, "Deleted branches:")
153
+ contains(step1.run, "${DELETED_BRANCHES}")
154
+ }
155
+
156
+ normalize_dry_run(false) := false
157
+
158
+ normalize_dry_run(value) := false if lower(sprintf("%v", [value])) == "no"
159
+
160
+ normalize_dry_run(value) := value if {
161
+ value != false # noqa: equals-pattern-matching
162
+ lower(sprintf("%v", [value])) != "no"
163
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
+ "policy": {
4
+ "files": {
5
+ "single": ".github/workflows/clean-merged-branch.yml",
6
+ "required": true
7
+ },
8
+ "missingMessage": ".github/workflows/clean-merged-branch.yml не існує — створи за каноном ga.mdc"
9
+ }
10
+ }
@@ -0,0 +1,27 @@
1
+ ---
2
+ type: JS Module
3
+ title: fix-clean_merged_branch.mjs
4
+ resource: plugins/ci-github/rules/ga/clean_merged_branch/fix-clean_merged_branch.mjs
5
+ docgen:
6
+ crc: e78a4923
7
+ model: openai-codex/gpt-5.4-mini
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-файлом `.github/workflows/clean-merged-branch.yml`.
20
+ 2. `patterns` використовує це правило як частину автоматизованого вирівнювання між шаблоном і робочим репозиторієм, щоб підтримувати однакову поведінку в потрібному workflow.
21
+ 3. `patterns` свідомо не охоплює шляхи `.github` і `.git`, щоб не зачіпати службові та внутрішні дані репозиторію.
22
+ 4. `patterns` працює read-only: лише описує, що треба зіставити, і не записує зміни у файлову систему чи базу даних.
23
+
24
+ ## Гарантії поведінки
25
+
26
+ - Read-only: не виконує операцій запису (ФС/БД).
27
+ - Свідомо пропускає шляхи: `.github`, `.git`.
@@ -0,0 +1,9 @@
1
+ ---
2
+ type: Directory Index
3
+ title: plugins/ci-github/rules/ga/clean_merged_branch
4
+ resource: plugins/ci-github/rules/ga/clean_merged_branch/
5
+ ---
6
+
7
+ | Файл | Тип |
8
+ | --------------------------------------------------------- | --------- |
9
+ | [fix-clean_merged_branch.mjs](fix-clean_merged_branch.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_merged_branch-template',
6
+ targetPath: '.github/workflows/clean-merged-branch.yml'
7
+ })
8
+ ]
@@ -0,0 +1,37 @@
1
+ name: Clean abandoned branches
2
+
3
+ on:
4
+ # Run daily at midnight
5
+ schedule:
6
+ - cron: '0 1 15 * *'
7
+
8
+ # Allow workflow to be manually run from the GitHub UI
9
+ workflow_dispatch: {}
10
+
11
+ concurrency:
12
+ group: ${{ github.ref }}-${{ github.workflow }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ cleanup_old_branches:
17
+ runs-on: ubuntu-latest
18
+ permissions:
19
+ contents: write
20
+ pull-requests: read
21
+ steps:
22
+ - id: delete_stuff
23
+ name: Delete those pesky dead branches
24
+ uses: phpdocker-io/github-actions-delete-abandoned-branches@v2.0.3
25
+ with:
26
+ github_token: ${{ github.token }}
27
+ last_commit_age_days: 90
28
+ ignore_branches: main,dev
29
+ # Action CLI accepts only yes/no. Go-yaml in conftest parses `no` as
30
+ # boolean false, so the Rego policy normalizes both forms.
31
+ dry_run: no
32
+
33
+ - name: Get output
34
+ env:
35
+ DELETED_BRANCHES: ${{ steps.delete_stuff.outputs.deleted_branches }}
36
+ run: |
37
+ echo "Deleted branches: ${DELETED_BRANCHES}"
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: Directory Index
3
+ title: plugins/ci-github/rules/ga
4
+ resource: plugins/ci-github/rules/ga/
5
+ ---
6
+
7
+ # plugins/ci-github/rules/ga
8
+
9
+ | Файл | Тип |
10
+ | ------------------- | --------- |
11
+ | [main.mjs](main.md) | JS Module |
@@ -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/git-ai.yml",
7
+ "required": true
8
+ },
9
+ "missingMessage": ".github/workflows/git-ai.yml не існує — створи за каноном ga.mdc"
10
+ }
11
+ }
@@ -0,0 +1,30 @@
1
+ ---
2
+ type: JS Module
3
+ title: fix-git_ai.mjs
4
+ resource: plugins/ci-github/rules/ga/git_ai/fix-git_ai.mjs
5
+ docgen:
6
+ crc: 1823ea17
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
+ Модуль має read-only поведінку без запису у файлову систему чи базу даних, щоб перевірка лише виявляла релевантні збіги й не змінювала стан репозиторію.
18
+
19
+ ## Поведінка
20
+
21
+ 1. `patterns` визначає автоматичне виправлення для підтримки стандартного workflow Git AI у репозиторії.
22
+
23
+ 2. `patterns` забезпечує наявність очікуваного файлу `.github/workflows/git-ai.yml`, щоб перевірка правила могла запропонувати єдиний канонічний варіант налаштування.
24
+
25
+ 3. Під час пошуку цільових файлів свідомо пропускаються службові шляхи `.github` і `.git`, щоб не аналізувати технічні директорії як звичайний проєктний код.
26
+
27
+ ## Гарантії поведінки
28
+
29
+ - Read-only: не виконує операцій запису (ФС/БД).
30
+ - Свідомо пропускає шляхи: `.github`, `.git`.
@@ -0,0 +1,9 @@
1
+ ---
2
+ type: Directory Index
3
+ title: plugins/ci-github/rules/ga/git_ai
4
+ resource: plugins/ci-github/rules/ga/git_ai/
5
+ ---
6
+
7
+ | Файл | Тип |
8
+ | ------------------------------- | --------- |
9
+ | [fix-git_ai.mjs](fix-git_ai.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: 'ga-git_ai-template', targetPath: '.github/workflows/git-ai.yml' })
5
+ ]
@@ -0,0 +1,19 @@
1
+ ## Структура `git-ai.yml` — автоматичний запуск git-ai після мержу PR
2
+
3
+ Rego-пакет: `ga.git_ai`
4
+
5
+ **Цільовий файл:** `.github/workflows/git-ai.yml`
6
+
7
+ ### Що перевіряється
8
+
9
+ - `name` — точна відповідність значенню з template (`Git AI`)
10
+ - `on.pull_request.types` — має містити `closed`
11
+ - `jobs.git-ai` — job має існувати
12
+ - `jobs.git-ai.if` — містить `github.event.pull_request.merged == true`
13
+ - `jobs.git-ai.permissions.contents` — за template (`write`)
14
+ - `steps[*].run` (сукупно) — містить substring `https://usegitai.com/install.sh` (інсталяція)
15
+ - `steps[*].run` (сукупно) — містить substring `git-ai ci github run` (запуск)
16
+
17
+ Substring-перевірки замість exact-match через крихкість multi-line `run:` блоків.
18
+
19
+ Канон: [git-ai.yml.snippet.yml](./template/git-ai.yml.snippet.yml)
@@ -0,0 +1,76 @@
1
+ # Перевірка `.github/workflows/git-ai.yml` (ga.mdc).
2
+ #
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/git-ai.yml.snippet.yml.
5
+ # Substring-перевірки (`if:`, `run:` блоки) — на основі ключових фраз з template
6
+ # steps, бо повний multi-line `run:` дуже крихкий для exact-match.
7
+ package ga.git_ai
8
+
9
+ import rego.v1
10
+
11
+ # ── Аліаси ─────────────────────────────────────────────────────────────────
12
+
13
+ gha_on := input["true"]
14
+
15
+ job := input.jobs["git-ai"]
16
+
17
+ job_run_blob := concat("\n", [run |
18
+ run := job.steps[_].run
19
+ ])
20
+
21
+ expected_name := data.template.snippet.name
22
+
23
+ expected_types := {t | some t in data.template.snippet.on.pull_request.types}
24
+
25
+ expected_if := data.template.snippet.jobs["git-ai"].if
26
+
27
+ expected_perms := data.template.snippet.jobs["git-ai"].permissions
28
+
29
+ # Substring-маркери з template `run:` блоків — ключові команди, наявність яких
30
+ # гарантує що workflow робить очікувані дії. Конкретний multi-line — не порівнюємо.
31
+ install_substring := "https://usegitai.com/install.sh"
32
+
33
+ run_substring := "git-ai ci github run"
34
+
35
+ # ── deny rules ─────────────────────────────────────────────────────────────
36
+
37
+ deny contains msg if {
38
+ input.name != expected_name
39
+ msg := sprintf("git-ai.yml: name має бути %q (ga.mdc)", [expected_name])
40
+ }
41
+
42
+ deny contains msg if {
43
+ not "closed" in {t | some t in gha_on.pull_request.types}
44
+ msg := "git-ai.yml: on.pull_request.types має містити closed (ga.mdc)"
45
+ }
46
+
47
+ deny contains msg if {
48
+ not job
49
+ msg := "git-ai.yml: jobs.git-ai відсутній (ga.mdc)"
50
+ }
51
+
52
+ deny contains msg if {
53
+ not contains(job_if_str, expected_if)
54
+ msg := sprintf("git-ai.yml: job має містити if: %s (ga.mdc)", [expected_if])
55
+ }
56
+
57
+ deny contains msg if {
58
+ job.permissions.contents != expected_perms.contents
59
+ msg := sprintf("git-ai.yml: permissions.contents має бути %s (ga.mdc)", [expected_perms.contents])
60
+ }
61
+
62
+ deny contains msg if {
63
+ not contains(job_run_blob, install_substring)
64
+ msg := "git-ai.yml: має встановлювати git-ai через curl | bash (ga.mdc)"
65
+ }
66
+
67
+ deny contains msg if {
68
+ not contains(job_run_blob, run_substring)
69
+ msg := sprintf("git-ai.yml: має виконувати %s (ga.mdc)", [run_substring])
70
+ }
71
+
72
+ # ── helpers ────────────────────────────────────────────────────────────────
73
+
74
+ default job_if_str := ""
75
+
76
+ job_if_str := sprintf("%v", [job.if])
@@ -0,0 +1,30 @@
1
+ name: Git AI
2
+
3
+ on:
4
+ pull_request:
5
+ types: [closed]
6
+
7
+ concurrency:
8
+ group: ${{ github.ref }}-${{ github.workflow }}
9
+ cancel-in-progress: true
10
+
11
+ jobs:
12
+ git-ai:
13
+ if: github.event.pull_request.merged == true
14
+ runs-on: ubuntu-latest
15
+ permissions:
16
+ contents: write
17
+
18
+ steps:
19
+ - name: Install git-ai
20
+ run: |
21
+ curl -fsSL https://usegitai.com/install.sh | bash
22
+ echo "$HOME/.git-ai/bin" >> $GITHUB_PATH
23
+ - name: Run git-ai
24
+ id: run-git-ai
25
+ env:
26
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27
+ run: |
28
+ git config --global user.name "github-actions[bot]"
29
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
30
+ git-ai ci github run
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
+ "policy": {
4
+ "files": {
5
+ "single": ".github/workflows/lint-ga.yml",
6
+ "required": true
7
+ },
8
+ "missingMessage": ".github/workflows/lint-ga.yml не існує — створи за каноном ga.mdc"
9
+ }
10
+ }
@@ -0,0 +1,32 @@
1
+ ---
2
+ type: JS Module
3
+ title: fix-lint_ga.mjs
4
+ resource: plugins/ci-github/rules/ga/lint_ga/fix-lint_ga.mjs
5
+ docgen:
6
+ crc: 7f4ab512
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/workflows/lint-ga.yml`, щоб lint workflow для Google Analytics залишався узгодженим із шаблоном.
16
+
17
+ Секція має read-only призначення: вона фіксує очікувану поведінку без запису у файлову систему чи базу даних. Під час загального обходу свідомо не враховуються службові шляхи `.github` і `.git`.
18
+
19
+ ## Поведінка
20
+
21
+ 1. `patterns` оголошує правило автоматичного виправлення для workflow lint-перевірки Google Analytics.
22
+
23
+ 2. `patterns` забезпечує приведення `.github/workflows/lint-ga.yml` до очікуваного шаблонного стану, щоб CI мав стабільну перевірку GA-правил.
24
+
25
+ 3. `patterns` не виконує запис самостійно: воно лише описує доступне виправлення для зовнішнього механізму застосування.
26
+
27
+ 4. `patterns` свідомо не охоплює службові шляхи `.github` і `.git` як вхідні області для загального обходу, окрім цільового workflow-файлу, який має бути синхронізований із шаблоном.
28
+
29
+ ## Гарантії поведінки
30
+
31
+ - Read-only: не виконує операцій запису (ФС/БД).
32
+ - Свідомо пропускає шляхи: `.github`, `.git`.
@@ -0,0 +1,9 @@
1
+ ---
2
+ type: Directory Index
3
+ title: plugins/ci-github/rules/ga/lint_ga
4
+ resource: plugins/ci-github/rules/ga/lint_ga/
5
+ ---
6
+
7
+ | Файл | Тип |
8
+ | --------------------------------- | --------- |
9
+ | [fix-lint_ga.mjs](fix-lint_ga.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: 'ga-lint_ga-template', targetPath: '.github/workflows/lint-ga.yml' })
5
+ ]
@@ -0,0 +1,21 @@
1
+ ## Структура `lint-ga.yml` — CI-лінт GitHub Actions файлів
2
+
3
+ Rego-пакет: `ga.lint_ga`
4
+
5
+ **Цільовий файл:** `.github/workflows/lint-ga.yml`
6
+
7
+ ### Що перевіряється
8
+
9
+ - `name` — точна відповідність значенню з template (`Lint GA`)
10
+ - `on.push.branches` — superset: містить `dev` і `main`
11
+ - `on.pull_request.branches` — superset: містить `dev` і `main`
12
+ - `on.push.paths` — superset: містить `.github/actions/**` і `.github/workflows/**`
13
+ - `jobs.lint-ga` — job має існувати
14
+ - `jobs.lint-ga.runs-on` — за template (`ubuntu-latest`)
15
+ - `jobs.lint-ga.permissions.contents` — `read`
16
+ - `jobs.lint-ga.steps` — не порожній
17
+ - `steps[*].uses` — кожен `uses:` з template має бути присутнім у кроках; SHA-пін того самого action (`owner/action@<40-hex SHA>`, з тег-коментарем `# vX` чи без) задовольняє канонічний тег — НЕ замінюй наявний SHA-пін тегом
18
+ - `steps[*].run` (сукупно) — містить `open-policy-agent/conftest` (Install conftest)
19
+ - `steps[*].run` (сукупно) — містить `n-rules lint ga --no-fix`
20
+
21
+ Канон: [lint-ga.yml.snippet.yml](./template/lint-ga.yml.snippet.yml)