@7n/rules-ci-github 2.0.1 → 2.1.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,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.1.0] - 2026-07-29
4
+
5
+ ### Removed
6
+
7
+ - text/lint_text: прибрано хардкод JS/TS/Vue/Python globs зі static template — тепер їх додають lang-js/lang-python через ci.artifact patch-existing (поведінка збережена при активних плагінах)
8
+ - Видалено js-специфічні CI-концерни (lint_js_yml) — власність перенесена в @7n/rules-lang-js через ci.artifact@1 contribution
9
+ - Rust GitHub CI-канон (lint-rust.yml) винесено в @7n/rules-lang-rust ci.artifact@1 contribution — обслуговує generic ci.artifact consumer
10
+ - Python-специфічні CI-артефакти (rules/python/lint_python_yml) видалено — тепер @7n/rules-lang-python own них через ci.artifact@1 contribution, обслуговується generic consumer-ом
11
+
12
+ ## [2.0.2] - 2026-07-28
13
+
14
+ ### Changed
15
+
16
+ - release: @7n/llm-lib@2.10.1, @7n/rules@1.52.1, @7n/rules-lang-js@0.23.1
17
+ - Механічно додано change-файл для поточних змін у workspace.
18
+
3
19
  ## [2.0.1] - 2026-07-27
4
20
 
5
21
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@7n/rules-ci-github",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "Плагін @7n/rules: канон GitHub Actions (.github/workflows) — обов'язкові workflow, zizmor, VS Code",
5
5
  "keywords": [
6
6
  "github-actions",
@@ -0,0 +1,10 @@
1
+ ---
2
+ type: Directory Index
3
+ title: plugins/ci-github/rules/ga/workflows
4
+ resource: plugins/ci-github/rules/ga/workflows/
5
+ ---
6
+
7
+ | Файл | Тип |
8
+ | ------------------------------------- | --------- |
9
+ | [fix-workflows.mjs](fix-workflows.md) | JS Module |
10
+ | [main.mjs](main.md) | JS Module |
@@ -6,9 +6,6 @@ on:
6
6
  - dev
7
7
  - main
8
8
  paths:
9
- - '**/*.js'
10
- - '**/*.ts'
11
- - '**/*.vue'
12
9
  - '**/*.html'
13
10
  - '**/*.css'
14
11
  - '**/*.scss'
@@ -24,7 +21,6 @@ on:
24
21
  - '**/*.mdс'
25
22
  - '**/*.txt'
26
23
  - '**/*.go'
27
- - '**/*.py'
28
24
  - '**/*.sh'
29
25
 
30
26
  pull_request:
@@ -32,9 +28,6 @@ on:
32
28
  - dev
33
29
  - main
34
30
  paths:
35
- - '**/*.js'
36
- - '**/*.ts'
37
- - '**/*.vue'
38
31
  - '**/*.html'
39
32
  - '**/*.css'
40
33
  - '**/*.scss'
@@ -50,7 +43,6 @@ on:
50
43
  - '**/*.mdс'
51
44
  - '**/*.txt'
52
45
  - '**/*.go'
53
- - '**/*.py'
54
46
  - '**/*.sh'
55
47
 
56
48
  concurrency:
@@ -1,11 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
- "fixability": "config",
4
- "policy": {
5
- "files": {
6
- "single": ".github/workflows/lint-js.yml",
7
- "required": true
8
- },
9
- "missingMessage": ".github/workflows/lint-js.yml не існує — створи за каноном правила (mixin @7n/rules-ci-github)"
10
- }
11
- }
@@ -1,35 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: fix-lint_js_yml.mjs
4
- resource: plugins/ci-github/rules/js/lint_js_yml/fix-lint_js_yml.mjs
5
- docgen:
6
- crc: e0152cf2
7
- model: openai-codex/gpt-5.4-mini
8
- tier: cloud-min
9
- score: 100
10
- issues: judge-refine:kept-original,judge:inaccurate:0.98
11
- judgeModel: openai-codex/gpt-5.4-mini
12
- ---
13
-
14
- ## Огляд
15
-
16
- `patterns` приводить шлях `.github/workflows/lint-js.yml` до канонічного шаблону концерну, щоб workflow для js-лінту відповідав стандартному корпоративному оформленню.
17
-
18
- ## Поведінка
19
-
20
- 1. `patterns` задає єдиний канонічний сценарій виправлення для CI-workflow лінту js-домену в репозиторіях-консюмерах.
21
- 2. Він узгоджує `.github/workflows/lint-js.yml` із шаблоном концерну: додає відсутні частини, зберігає канонічні значення як пріоритетні та не змінює сторонні коментарі й ключі поза шаблоном.
22
- 3. Якщо цільового файлу немає, `patterns` ініціює його створення з канонічного snippet-а.
23
- 4. Під час застосування механізм свідомо обходить `.github` і `.git`, щоб не зачіпати службові області поза цим сценарієм.
24
-
25
- ## Публічний API
26
-
27
- - patterns — Один детермінований патерн: deep-merge канонічного snippet-а концерну в
28
- `.github/workflows/lint-js.yml` консюмер-репо — відсутні ключі додаються,
29
- канонічні значення мають пріоритет, коментарі й ключі поза шаблоном не
30
- чіпаються; якщо файлу немає — створюється зі snippet-а.
31
-
32
- ## Гарантії поведінки
33
-
34
- - Власних операцій запису (ФС/БД) у файлі немає; виклики імпортованих модулів можуть писати.
35
- - Свідомо пропускає шляхи: `.github`, `.git`.
@@ -1,9 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: plugins/ci-github/rules/js/lint_js_yml
4
- resource: plugins/ci-github/rules/js/lint_js_yml/
5
- ---
6
-
7
- | Файл | Тип |
8
- | ----------------------------------------- | --------- |
9
- | [fix-lint_js_yml.mjs](fix-lint_js_yml.md) | JS Module |
@@ -1,15 +0,0 @@
1
- /**
2
- * T0-фікс концерну lint_js_yml: приводить CI-workflow лінту js-домену
3
- * у консюмер-репо до канонічного шаблону концерну.
4
- */
5
- import { createTemplateFixPattern } from '@7n/rules/scripts/lib/fix/template-deep-merge.mjs'
6
-
7
- /**
8
- * Один детермінований патерн: deep-merge канонічного snippet-а концерну в
9
- * `.github/workflows/lint-js.yml` консюмер-репо — відсутні ключі додаються,
10
- * канонічні значення мають пріоритет, коментарі й ключі поза шаблоном не
11
- * чіпаються; якщо файлу немає — створюється зі snippet-а.
12
- */
13
- export const patterns = [
14
- createTemplateFixPattern({ id: 'js-lint_js_yml-template', targetPath: '.github/workflows/lint-js.yml' })
15
- ]
@@ -1,14 +0,0 @@
1
- ## Rego-gate `.github/workflows/lint-js.yml`
2
-
3
- Rego-пакет: `js_lint.lint_js_yml`
4
-
5
- Цільовий файл: `.github/workflows/lint-js.yml`.
6
-
7
- Що перевіряється:
8
-
9
- - Наявність усіх `uses:`-кроків з канону (subset-of по `jobs.eslint.steps`); SHA-пін того самого action (`owner/action@<40-hex SHA>`, з тег-коментарем `# vX` чи без) задовольняє канонічний тег — НЕ замінюй наявний SHA-пін тегом
10
- - Наявність усіх рядків `run:` з канону (substring-match по всіх кроках)
11
- - `actions/checkout` (будь-який ref) має мати `with.persist-credentials: false` (inverse-check)
12
- - Заборона `--fix` у CI: `bunx oxlint ... --fix` або `eslint --fix` у будь-якому `run:` → deny
13
-
14
- Канон-snippet: [lint-js.yml.snippet.yml](./template/lint-js.yml.snippet.yml)
@@ -1,134 +0,0 @@
1
- # Перевірка `.github/workflows/lint-js.yml` (js.mdc).
2
- #
3
- # Канон надходить через --data: { "template": { "snippet": ... } }
4
- # Структура --data сформована з template/lint-js.yml.snippet.yml.
5
- # Required uses + run substrings зчитуються з template's eslint-job steps.
6
- # Універсальні workflow-перевірки — у `ga.workflow_common`.
7
- #
8
- # Логіка, що ЛИШАЄТЬСЯ у rego (inverse anti-patterns):
9
- # - `oxlint --fix` / `eslint --fix` у CI заборонено;
10
- # - actions/checkout (будь-який ref) має мати `with.persist-credentials: false`.
11
- #
12
- # Pin-aware: SHA-пін `owner/action@<40-hex>` (zizmor ref-pin; тег-коментар після
13
- # `#` YAML-парсер відкидає) ЗАДОВОЛЬНЯЄ канонічний `owner/action@vN` з template —
14
- # не вимагаємо (і не даунгрейдимо) назад до тега.
15
- package js.lint_js_yml
16
-
17
- import rego.v1
18
-
19
- # Conftest поточних версій читає YAML 1.2 (`on`), але старі версії віддавали
20
- # YAML 1.1 boolean-key (`true`). Приймаємо обидві форми, щоб policy не мовчала.
21
- gha_on := object.get(input, "on", object.get(input, "true", {}))
22
-
23
- expected_pr_paths := {p | some p in data.template.snippet.on.pull_request.paths}
24
-
25
- actual_pr_paths := object.get(object.get(gha_on, "pull_request", {}), "paths", [])
26
-
27
- # Required `uses:` зі template — фільтруємо тільки кроки з uses.
28
- expected_uses_set contains u if {
29
- some step in data.template.snippet.jobs.eslint.steps
30
- u := object.get(step, "uses", "")
31
- u != ""
32
- }
33
-
34
- # Required `run:` substrings (per-line з template).
35
- expected_run_substrings contains line if {
36
- some step in data.template.snippet.jobs.eslint.steps
37
- r := object.get(step, "run", "")
38
- r != ""
39
- some raw in split(r, "\n")
40
- line := trim_space(raw)
41
- line != ""
42
- }
43
-
44
- # Аліаси на input.
45
- all_steps contains step if {
46
- some job in object.get(input, "jobs", {})
47
- some step in object.get(job, "steps", [])
48
- }
49
-
50
- all_uses_set contains u if {
51
- some step in all_steps
52
- u := object.get(step, "uses", "")
53
- u != ""
54
- }
55
-
56
- all_run_blob := concat("\n", [r |
57
- some step in all_steps
58
- r := step_run_text(step)
59
- ])
60
-
61
- # ── deny: required uses ─────────────────────────────────────────────────
62
-
63
- deny contains msg if {
64
- some required_use in expected_uses_set
65
- not has_use_satisfying(required_use)
66
- msg := sprintf("lint-js.yml: відсутній крок uses: %s (js.mdc)", [required_use])
67
- }
68
-
69
- deny contains msg if {
70
- not paths_superset_of(actual_pr_paths, expected_pr_paths)
71
- msg := "lint-js.yml: on.pull_request.paths має містити очікувані glob-и (js.mdc)"
72
- }
73
-
74
- # ── deny: required run substrings ───────────────────────────────────────
75
-
76
- deny contains msg if {
77
- some required_run in expected_run_substrings
78
- not contains(all_run_blob, required_run)
79
- msg := sprintf("lint-js.yml: у run немає %q (js.mdc)", [required_run])
80
- }
81
-
82
- # ── deny: actions/checkout has persist-credentials: false (inverse) ─────
83
-
84
- deny contains msg if {
85
- not has_checkout_persist_credentials_false
86
- msg := "lint-js.yml: actions/checkout має бути з with.persist-credentials: false (js.mdc)"
87
- }
88
-
89
- # ── deny: --fix у CI заборонено (inverse) ───────────────────────────────
90
-
91
- deny contains msg if {
92
- regex.match(`bunx\s+oxlint[^\n]*--fix`, all_run_blob)
93
- msg := "lint-js.yml: у run є oxlint з `--fix` (у CI заборонено) (js.mdc)"
94
- }
95
-
96
- deny contains msg if {
97
- contains(all_run_blob, "eslint --fix")
98
- msg := "lint-js.yml: у run є `eslint --fix` (у CI заборонено) (js.mdc)"
99
- }
100
-
101
- # ── helpers ─────────────────────────────────────────────────────────────
102
-
103
- has_checkout_persist_credentials_false if {
104
- some step in all_steps
105
- startswith(object.get(step, "uses", ""), "actions/checkout@")
106
- step.with["persist-credentials"] == false
107
- }
108
-
109
- # `uses:` з input задовольняє канонічний `owner/action@tag`: точний збіг…
110
- uses_satisfies(actual, expected) if actual == expected
111
-
112
- # …або той самий action-slug і ref — повний 40-hex commit SHA (zizmor ref-pin).
113
- # Відповідність версії гарантує сам пін (тег-коментар `# vX` парсер відкидає).
114
- uses_satisfies(actual, expected) if {
115
- slug := split(expected, "@")[0]
116
- startswith(actual, concat("", [slug, "@"]))
117
- parts := split(actual, "@")
118
- regex.match(`^[0-9a-fA-F]{40}$`, parts[count(parts) - 1])
119
- }
120
-
121
- has_use_satisfying(required) if {
122
- some u in all_uses_set
123
- uses_satisfies(u, required)
124
- }
125
-
126
- paths_superset_of(actual, expected) if {
127
- expected & {p | some p in actual} == expected
128
- }
129
-
130
- step_run_text(step) := step.run if is_string(step.run)
131
-
132
- else := concat("\n", [s | some s in step.run]) if is_array(step.run)
133
-
134
- else := ""
@@ -1,51 +0,0 @@
1
- name: Lint JS
2
-
3
- on:
4
- push:
5
- branches:
6
- - dev
7
- - main
8
- paths:
9
- - '**/*.js'
10
- - '**/*.mjs'
11
- - '**/*.cjs'
12
- - '**/*.jsx'
13
- - '**/*.ts'
14
- - '**/*.tsx'
15
- - '**/*.vue'
16
- - '**/eslint.config.*'
17
- - '**/.oxlintrc.json'
18
-
19
- pull_request:
20
- branches:
21
- - dev
22
- - main
23
- paths:
24
- - '**/*.js'
25
- - '**/*.mjs'
26
- - '**/*.cjs'
27
- - '**/*.jsx'
28
- - '**/*.ts'
29
- - '**/*.tsx'
30
- - '**/*.vue'
31
- - '**/eslint.config.*'
32
- - '**/.oxlintrc.json'
33
-
34
- concurrency:
35
- group: ${{ github.ref }}-${{ github.workflow }}
36
- cancel-in-progress: true
37
-
38
- jobs:
39
- eslint:
40
- runs-on: ubuntu-latest
41
- permissions:
42
- contents: read
43
- steps:
44
- - uses: actions/checkout@v6
45
- with:
46
- persist-credentials: false
47
-
48
- - uses: ./.github/actions/setup-bun-deps
49
-
50
- - name: Eslint
51
- run: bunx n-rules lint js --no-fix
@@ -1,11 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
- "fixability": "config",
4
- "policy": {
5
- "files": {
6
- "single": ".github/workflows/lint-python.yml",
7
- "required": true
8
- },
9
- "missingMessage": ".github/workflows/lint-python.yml не існує — створи за каноном правила (mixin @7n/rules-ci-github)"
10
- }
11
- }
@@ -1,39 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: fix-lint_python_yml.mjs
4
- resource: plugins/ci-github/rules/python/lint_python_yml/fix-lint_python_yml.mjs
5
- docgen:
6
- crc: 525f6959
7
- model: openai-codex/gpt-5.5
8
- tier: cloud-avg
9
- score: 100
10
- issues: judge:error
11
- judgeModel: openai-codex/gpt-5.4-mini
12
- ---
13
-
14
- ## Огляд
15
-
16
- Файл декларує автофікс для `python/lint_python_yml`: scaffold відсутнього `.github/workflows/lint-python.yml` з канонічного шаблону правила та `template-deep-merge` для наявного workflow. Він потрібен, щоб дописувати лише канонічні поля для Python lint у GitHub Actions і зберігати локальні налаштування проєкту.
17
-
18
- Публічна точка входу: `patterns`. Свідомо пропущені шляхи: `.github`, `.git`. Власних операцій запису у файлі немає.
19
-
20
- ## Поведінка
21
-
22
- 1. `patterns` оголошує автофікс для правила `python/lint_python_yml`, щоб репозиторій отримував канонічний GitHub Actions workflow для Python lint.
23
-
24
- 2. Якщо `.github/workflows/lint-python.yml` відсутній, автофікс має створити його з шаблону правила.
25
-
26
- 3. Якщо workflow уже існує, автофікс має доповнити його лише канонічними полями правила, не прибираючи локальні налаштування проєкту.
27
-
28
- 4. Правило свідомо не обходить службові шляхи `.github` і `.git`, але працює з цільовим workflow-файлом як із винятком для підтримки потрібної CI-конфігурації.
29
-
30
- 5. Файл лише декларує поведінку автофікса через `patterns`; власних операцій запису не виконує.
31
-
32
- ## Публічний API
33
-
34
- - patterns — Фікс-патерни концерну: один template-deep-merge запис для `.github/workflows/lint-python.yml`.
35
-
36
- ## Гарантії поведінки
37
-
38
- - Власних операцій запису (ФС/БД) у файлі немає; виклики імпортованих модулів можуть писати.
39
- - Свідомо пропускає шляхи: `.github`, `.git`.
@@ -1,9 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: plugins/ci-github/rules/python/lint_python_yml
4
- resource: plugins/ci-github/rules/python/lint_python_yml/
5
- ---
6
-
7
- | Файл | Тип |
8
- | ------------------------------------------------- | --------- |
9
- | [fix-lint_python_yml.mjs](fix-lint_python_yml.md) | JS Module |
@@ -1,11 +0,0 @@
1
- /**
2
- * T0-autofix концерну `python/lint_python_yml`: деклараційний template-deep-merge —
3
- * scaffold відсутнього `.github/workflows/lint-python.yml` з канонічного шаблону правила або
4
- * дописування в наявний файл лише канонічних полів (локальні — зберігаються).
5
- */
6
- import { createTemplateFixPattern } from '@7n/rules/scripts/lib/fix/template-deep-merge.mjs'
7
-
8
- /** Фікс-патерни концерну: один template-deep-merge запис для `.github/workflows/lint-python.yml`. */
9
- export const patterns = [
10
- createTemplateFixPattern({ id: 'python-lint_python_yml-template', targetPath: '.github/workflows/lint-python.yml' })
11
- ]
@@ -1,12 +0,0 @@
1
- ## Rego-gate: перевірка `.github/workflows/lint-python.yml`
2
-
3
- Rego-пакет: `python.lint_python_yml`
4
-
5
- Цільовий файл: `.github/workflows/lint-python.yml`
6
-
7
- Перевіряє (drift-safe через `--data template`):
8
-
9
- - кожен `uses` з канону (підмножина) присутній у workflow: `actions/checkout@v6`, `./.github/actions/setup-bun-deps`, `astral-sh/setup-uv@v8.0.0`;
10
- - кожен `run` з канону є substring серед run-кроків: `uv sync --frozen`, `n-rules lint python --no-fix`.
11
-
12
- Канонічний workflow: [lint-python.yml.snippet.yml](./template/lint-python.yml.snippet.yml)
@@ -1,85 +0,0 @@
1
- # Перевірка `.github/workflows/lint-python.yml` для правила python (python.mdc).
2
- #
3
- # Канон надходить через --data: { "template": { "snippet": ... } }
4
- # Структура --data сформована з template/lint-python.yml.snippet.yml.
5
- # Перевіряємо (drift-safe — усе ведеться з template, без inline-літералів):
6
- # - кожен `uses` з template (підмножина): actions/checkout@v6,
7
- # ./.github/actions/setup-bun-deps, astral-sh/setup-uv@v8.0.0;
8
- # - кожен `run` з template має бути присутнім (як substring) серед run-кроків
9
- # input'а: `uv sync --frozen`, `n-rules lint python --no-fix`.
10
- # Заборона Poetry-кроків (snok/install-poetry, `poetry install`) — через відсутність
11
- # у каноні: правило вимагає uv-кроки, а нав'язаних poetry-кроків у template немає.
12
- # Універсальні workflow-перевірки (name, concurrency, branches) — у `ga.workflow_common`.
13
- package python.lint_python_yml
14
-
15
- import rego.v1
16
-
17
- # Conftest поточних версій читає YAML 1.2 (`on`), але старі версії віддавали
18
- # YAML 1.1 boolean-key (`true`). Приймаємо обидві форми, щоб policy не мовчала.
19
- gha_on := object.get(input, "on", object.get(input, "true", {}))
20
-
21
- expected_pr_paths := {p | some p in data.template.snippet.on.pull_request.paths}
22
-
23
- actual_pr_paths := object.get(object.get(gha_on, "pull_request", {}), "paths", [])
24
-
25
- # Усі `uses` з канону workflow (по всіх job'ах template).
26
- expected_uses contains u if {
27
- some job in data.template.snippet.jobs
28
- some step in job.steps
29
- u := object.get(step, "uses", "")
30
- u != ""
31
- }
32
-
33
- # Усі `uses` з input workflow.
34
- actual_uses contains u if {
35
- some job in object.get(input, "jobs", {})
36
- some step in object.get(job, "steps", [])
37
- u := object.get(step, "uses", "")
38
- u != ""
39
- }
40
-
41
- # Конкатенація всіх `run`-кроків з input workflow.
42
- all_run_text := concat("\n", [run_text |
43
- some job in object.get(input, "jobs", {})
44
- some step in object.get(job, "steps", [])
45
- run_text := step_run_to_text(step)
46
- ])
47
-
48
- deny contains msg if {
49
- some required_use in expected_uses
50
- not required_use in actual_uses
51
- msg := sprintf("lint-python.yml: відсутній step з `uses: %s` (python.mdc)", [required_use])
52
- }
53
-
54
- deny contains msg if {
55
- not paths_superset_of(actual_pr_paths, expected_pr_paths)
56
- msg := "lint-python.yml: on.pull_request.paths має містити очікувані glob-и (python.mdc)"
57
- }
58
-
59
- deny contains msg if {
60
- msg := "lint-python.yml: actions/checkout@v6 потребує `with: persist-credentials: false` (python.mdc)"
61
- some job in object.get(input, "jobs", {})
62
- some step in object.get(job, "steps", [])
63
- step.uses == "actions/checkout@v6"
64
- creds := object.get(object.get(step, "with", {}), "persist-credentials", true)
65
- creds != false
66
- }
67
-
68
- deny contains msg if {
69
- some job in data.template.snippet.jobs
70
- some step in job.steps
71
- expected_run := object.get(step, "run", "")
72
- expected_run != ""
73
- not contains(all_run_text, expected_run)
74
- msg := sprintf("lint-python.yml: жоден крок run не містить %q (python.mdc)", [expected_run])
75
- }
76
-
77
- step_run_to_text(step) := step.run if is_string(step.run)
78
-
79
- else := concat("\n", [s | some s in step.run]) if is_array(step.run)
80
-
81
- else := ""
82
-
83
- paths_superset_of(actual, expected) if {
84
- expected & {p | some p in actual} == expected
85
- }
@@ -1,46 +0,0 @@
1
- name: Lint Python
2
-
3
- on:
4
- push:
5
- branches:
6
- - dev
7
- - main
8
- paths:
9
- - '**/*.py'
10
- - 'pyproject.toml'
11
- - 'uv.lock'
12
- - '.github/workflows/lint-python.yml'
13
-
14
- pull_request:
15
- branches:
16
- - dev
17
- - main
18
- paths:
19
- - '**/*.py'
20
- - 'pyproject.toml'
21
- - 'uv.lock'
22
- - '.github/workflows/lint-python.yml'
23
-
24
- concurrency:
25
- group: ${{ github.ref }}-${{ github.workflow }}
26
- cancel-in-progress: true
27
-
28
- jobs:
29
- python:
30
- runs-on: ubuntu-latest
31
- permissions:
32
- contents: read
33
- steps:
34
- - uses: actions/checkout@v6
35
- with:
36
- persist-credentials: false
37
-
38
- - uses: ./.github/actions/setup-bun-deps
39
-
40
- - uses: astral-sh/setup-uv@v8.0.0
41
-
42
- - name: Install dependencies (uv)
43
- run: uv sync --frozen
44
-
45
- - name: Lint Python
46
- run: bunx n-rules lint python --no-fix
@@ -1,11 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
3
- "fixability": "config",
4
- "policy": {
5
- "files": {
6
- "single": ".github/workflows/lint-rust.yml",
7
- "required": true
8
- },
9
- "missingMessage": ".github/workflows/lint-rust.yml не існує — створи з канонічним вмістом (rust.mdc)"
10
- }
11
- }
@@ -1,37 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: fix-lint_rust_yml.mjs
4
- resource: plugins/ci-github/rules/rust/lint_rust_yml/fix-lint_rust_yml.mjs
5
- docgen:
6
- crc: 4312b7ec
7
- model: openai-codex/gpt-5.5
8
- tier: cloud-avg
9
- score: 100
10
- issues: judge-refine:kept-original,judge:inaccurate:0.99
11
- judgeModel: openai-codex/gpt-5.4-mini
12
- ---
13
-
14
- ## Огляд
15
-
16
- Файл описує T0-autofix для `rust/lint_rust_yml`: scaffold відсутнього `.github/workflows/lint-rust.yml` з канонічного шаблону правила або `template-deep-merge` канонічних полів у наявний workflow зі збереженням локальних налаштувань.
17
-
18
- Публічна функція `patterns` декларує цю поведінку для правила. Обхід свідомо пропускає шляхи `.github` і `.git`, щоб не аналізувати службові директорії як джерела проєктного Rust-коду.
19
-
20
- ## Поведінка
21
-
22
- 1. `patterns` оголошує єдиний сценарій autofix для Rust lint workflow.
23
-
24
- 2. Сценарій створює відсутній `.github/workflows/lint-rust.yml` за канонічним шаблоном правила, щоб проєкт мав стандартну GitHub Actions перевірку Rust lint.
25
-
26
- 3. Якщо workflow уже існує, сценарій доповнює його лише канонічними полями, зберігаючи локальні налаштування проєкту.
27
-
28
- 4. Autofix свідомо не обходить службові шляхи `.github` і `.git` як звичайні цілі аналізу; `.github/workflows/lint-rust.yml` використовується лише як визначений цільовий workflow-файл.
29
-
30
- ## Публічний API
31
-
32
- - patterns — Фікс-патерни концерну: один template-deep-merge запис для `.github/workflows/lint-rust.yml`.
33
-
34
- ## Гарантії поведінки
35
-
36
- - Власних операцій запису (ФС/БД) у файлі немає; виклики імпортованих модулів можуть писати.
37
- - Свідомо пропускає шляхи: `.github`, `.git`.
@@ -1,9 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: plugins/ci-github/rules/rust/lint_rust_yml
4
- resource: plugins/ci-github/rules/rust/lint_rust_yml/
5
- ---
6
-
7
- | Файл | Тип |
8
- | --------------------------------------------- | --------- |
9
- | [fix-lint_rust_yml.mjs](fix-lint_rust_yml.md) | JS Module |
@@ -1,11 +0,0 @@
1
- /**
2
- * T0-autofix концерну `rust/lint_rust_yml`: деклараційний template-deep-merge —
3
- * scaffold відсутнього `.github/workflows/lint-rust.yml` з канонічного шаблону правила або
4
- * дописування в наявний файл лише канонічних полів (локальні — зберігаються).
5
- */
6
- import { createTemplateFixPattern } from '@7n/rules/scripts/lib/fix/template-deep-merge.mjs'
7
-
8
- /** Фікс-патерни концерну: один template-deep-merge запис для `.github/workflows/lint-rust.yml`. */
9
- export const patterns = [
10
- createTemplateFixPattern({ id: 'rust-lint_rust_yml-template', targetPath: '.github/workflows/lint-rust.yml' })
11
- ]
@@ -1,17 +0,0 @@
1
- ## Перевірка `.github/workflows/lint-rust.yml`
2
-
3
- Rego-пакет: `rust.lint_rust_yml`
4
-
5
- Цільовий файл: `.github/workflows/lint-rust.yml`
6
-
7
- Перевіряє:
8
-
9
- - кожен `uses`-крок з канону присутній у workflow (підмножина): `actions/checkout@v6`, `dtolnay/rust-toolchain@stable`, `Swatinem/rust-cache@v2`
10
- - кожен `run`-крок з канону присутній як підрядок серед усіх `run`-кроків: `cargo fmt --all -- --check`, `cargo clippy --all-targets --all-features -- -D warnings`
11
- - канон завантажується через `--data` з template-сніпету — drift-safe: зміна шаблону автоматично рухає перевірку
12
-
13
- Канонічний template: [lint-rust.yml.snippet.yml](./template/lint-rust.yml.snippet.yml)
14
-
15
- Вимога `Swatinem/rust-cache@v2` після `dtolnay/rust-toolchain@…` тут перевіряється лише для цього конкретного файлу (як частина канону). Та сама вимога для **будь-якого** іншого workflow (coverage, release/build через `tauri-apps/tauri-action`, кастомні) — concern `rust/toolchain_cache`.
16
-
17
- Tauri-проєкти (`src-tauri/Cargo.toml`) додатково потребують у цьому файлі apt-кроку системних залежностей Linux перед Clippy — concern `tauri/linux_deps` (tauri.mdc), не частина канону тут.
@@ -1,96 +0,0 @@
1
- # Перевірка `.github/workflows/lint-rust.yml` для правила rust (rust.mdc).
2
- #
3
- # Канон надходить через --data: { "template": { "snippet": ... } }
4
- # Структура --data сформована з template/lint-rust.yml.snippet.yml.
5
- # Перевіряємо:
6
- # - кожен `uses` з template (підмножина): actions/checkout@v6,
7
- # dtolnay/rust-toolchain@stable, Swatinem/rust-cache@v2;
8
- # - кожен `run` з template має бути присутнім (як substring) серед
9
- # run-кроків input'а — drift-safe: зміна template одразу рухає перевірку.
10
- # Універсальні workflow-перевірки (name, concurrency, branches) — у `ga.workflow_common`.
11
- package rust.lint_rust_yml
12
-
13
- import rego.v1
14
-
15
- # Conftest поточних версій читає YAML 1.2 (`on`), але старі версії віддавали
16
- # YAML 1.1 boolean-key (`true`). Приймаємо обидві форми, щоб policy не мовчала.
17
- gha_on := object.get(input, "on", object.get(input, "true", {}))
18
-
19
- expected_pr_paths := {p | some p in data.template.snippet.on.pull_request.paths}
20
-
21
- actual_pr_paths := object.get(object.get(gha_on, "pull_request", {}), "paths", [])
22
-
23
- # Усі `uses` з канону workflow.
24
- expected_uses contains u if {
25
- some step in data.template.snippet.jobs.lint.steps
26
- u := object.get(step, "uses", "")
27
- u != ""
28
- }
29
-
30
- # Усі `uses` з input workflow.
31
- actual_uses contains u if {
32
- some job in object.get(input, "jobs", {})
33
- some step in object.get(job, "steps", [])
34
- u := object.get(step, "uses", "")
35
- u != ""
36
- }
37
-
38
- # Конкатенація всіх `run`-кроків з input workflow.
39
- all_run_text := concat("\n", [run_text |
40
- some job in object.get(input, "jobs", {})
41
- some step in object.get(job, "steps", [])
42
- run_text := step_run_to_text(step)
43
- ])
44
-
45
- # Крок dtolnay/rust-toolchain@stable має мати with.components з rustfmt і clippy.
46
- toolchain_step(step) if {
47
- uses := object.get(step, "uses", "")
48
- startswith(uses, "dtolnay/rust-toolchain@")
49
- }
50
-
51
- deny contains msg if {
52
- some required_use in expected_uses
53
- not required_use in actual_uses
54
- msg := sprintf("lint-rust.yml: відсутній step з `uses: %s` (rust.mdc)", [required_use])
55
- }
56
-
57
- deny contains msg if {
58
- not paths_superset_of(actual_pr_paths, expected_pr_paths)
59
- msg := "lint-rust.yml: on.pull_request.paths має містити очікувані glob-и (rust.mdc)"
60
- }
61
-
62
- deny contains msg if {
63
- some step in data.template.snippet.jobs.lint.steps
64
- expected_run := object.get(step, "run", "")
65
- expected_run != ""
66
- not contains(all_run_text, expected_run)
67
- msg := sprintf("lint-rust.yml: жоден крок run не містить %q (rust.mdc)", [expected_run])
68
- }
69
-
70
- deny contains msg if {
71
- msg := "lint-rust.yml: dtolnay/rust-toolchain step потребує with.components: rustfmt, clippy (rust.mdc)"
72
- some job in object.get(input, "jobs", {})
73
- some step in object.get(job, "steps", [])
74
- toolchain_step(step)
75
- components := object.get(object.get(step, "with", {}), "components", "")
76
- not contains(components, "rustfmt")
77
- }
78
-
79
- deny contains msg if {
80
- msg := "lint-rust.yml: dtolnay/rust-toolchain step потребує with.components: rustfmt, clippy (rust.mdc)"
81
- some job in object.get(input, "jobs", {})
82
- some step in object.get(job, "steps", [])
83
- toolchain_step(step)
84
- components := object.get(object.get(step, "with", {}), "components", "")
85
- not contains(components, "clippy")
86
- }
87
-
88
- step_run_to_text(step) := step.run if is_string(step.run)
89
-
90
- else := concat("\n", [s | some s in step.run]) if is_array(step.run)
91
-
92
- else := ""
93
-
94
- paths_superset_of(actual, expected) if {
95
- expected & {p | some p in actual} == expected
96
- }
@@ -1,50 +0,0 @@
1
- name: Lint Rust
2
-
3
- on:
4
- push:
5
- branches:
6
- - dev
7
- - main
8
- paths:
9
- - '**/*.rs'
10
- - '**/Cargo.toml'
11
- - '**/Cargo.lock'
12
- - '**/rustfmt.toml'
13
- - '**/clippy.toml'
14
-
15
- pull_request:
16
- branches:
17
- - dev
18
- - main
19
- paths:
20
- - '**/*.rs'
21
- - '**/Cargo.toml'
22
- - '**/Cargo.lock'
23
- - '**/rustfmt.toml'
24
- - '**/clippy.toml'
25
-
26
- concurrency:
27
- group: ${{ github.ref }}-${{ github.workflow }}
28
- cancel-in-progress: true
29
-
30
- jobs:
31
- lint:
32
- runs-on: ubuntu-latest
33
- permissions:
34
- contents: read
35
- steps:
36
- - uses: actions/checkout@v6
37
- with:
38
- persist-credentials: false
39
-
40
- - uses: dtolnay/rust-toolchain@stable
41
- with:
42
- components: rustfmt, clippy
43
-
44
- - uses: Swatinem/rust-cache@v2
45
-
46
- - name: Rustfmt
47
- run: cargo fmt --all -- --check
48
-
49
- - name: Clippy
50
- run: cargo clippy --all-targets --all-features -- -D warnings