@nitra/cursor 3.25.1 → 3.27.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.
@@ -2,216 +2,31 @@
2
2
 
3
3
  ## Огляд
4
4
 
5
- Модуль `workflows.mjs` реалізує JS-частину перевірки правила **ga.mdc** (GitHub Actions) у пакеті `@nitra/cursor`. Він валідує конфігурацію `.github/workflows/` цільового репозиторію: розширення файлів (`.yml`, не `.yaml`), наявність обовʼязкових workflow, відсутність MegaLinter-залишків, коректність triggers `on.*.paths`, наявність composite-action `setup-bun-deps` і локальне встановлення `shellcheck`.
6
-
7
- Модуль працює в парі з **Rego-полісі** з `npm/policy/ga/`: пер-документні структурні перевірки (поля YAML, `concurrency`, заборона `oven-sh/setup-bun` / `actions/cache` / `bun install`, мінімальні версії `uses`, наявність `actions/checkout@v6` перед локальним `setup-bun-deps`, shell-продовження `\` у `run`) делеговані Rego і викликаються через `runConftestBatch`. У JS лишилися ті перевірки, які потребують доступу до файлової системи / git-індексу (`git ls-files :(glob)`, `existsSync`, читання `readdir`) і не можуть бути виражені declarative-полісі.
8
-
9
- Точка входу — асинхронна функція `check(cwd)`, яка повертає exit code (`0` успіх, `1` — є проблеми). Її викликає `bun run lint-ga` разом з `actionlint` і `zizmor`.
10
-
11
- Plan B-патерн: rego-частина авторитетна для пер-документних правил; JS виконує cross-file і tooling-перевірки. Hard-fail якщо `conftest` відсутній у PATH (узгоджено з `runConftestBatch`).
12
-
13
- ## Експорти / API
14
-
15
- | Експорт | Тип | Призначення |
16
- | -------------------------------------- | ---------------- | ------------------------------------------------------------------------------- |
17
- | `check(cwd?)` | `async function` | Головна точка входу: валідує `ga.mdc` для репозиторію `cwd`. |
18
- | `checkShellcheckInstalled(pass, fail)` | `function` | Перевіряє наявність бінарника `shellcheck` у PATH (актуальне для `actionlint`). |
19
-
20
- Решта функцій модуля — приватні (module-local) хелпери, які не експортуються.
21
-
22
- ## Функції
23
-
24
- ### `gitHasAnyTrackedFileMatchingGlob(globPattern, cwd)`
25
-
26
- - **Сигнатура:** `(globPattern: string, cwd: string) => boolean`
27
- - **Параметри:**
28
- - `globPattern` glob із workflow (наприклад `files/**`, `image-migration-new/**`).
29
- - `cwd` робочий каталог для виклику `git`.
30
- - **Повертає:** `true`, якщо хоча б один tracked-файл у git-індексі матчить glob; `true` для негативних патернів (починаються з `!`); `false` — якщо patternstring порожній або `git` упав.
31
- - **Side effects:** спавн дочірнього процесу `git ls-files -z -- :(glob)<pattern>` через `execFileSync` (синхронно).
32
- - **Особливості:** використовує pathspec `:(glob)`, щоб делегувати glob-матчинг git-у, без ручної реалізації glob-engine і без рекурсивного сканування FS.
33
-
34
- ### `shouldValidateWorkflowPathsGlob(p)`
35
-
36
- - **Сигнатура:** `(p: string) => boolean`
37
- - **Параметри:** `p` — рядковий glob із `on.*.paths`.
38
- - **Повертає:** `true`, якщо glob варто валідувати на існування файлів; `false` для негативних патернів (`!...`) та для патернів зі вставкою `*.` (типу `*.vue`, `*.php` — заготовки для майбутніх файлів).
39
- - **Side effects:** немає (чиста функція).
40
-
41
- ### `verifyOnePathsGlob(relPath, eventName, raw, passFn, failFn, cwd)`
42
-
43
- - **Сигнатура:** `(relPath: string, eventName: string, raw: unknown, passFn: (msg: string) => void, failFn: (msg: string) => void, cwd: string) => void`
44
- - **Параметри:**
45
- - `relPath` — відносний шлях workflow-файлу для повідомлень.
46
- - `eventName` — назва події (`push` або `pull_request`).
47
- - `raw` — сирий елемент із масиву `paths` (може бути будь-яким типом).
48
- - `passFn`/`failFn` — колбеки звітування.
49
- - `cwd` — корінь репозиторію для `git`.
50
- - **Повертає:** `void`.
51
- - **Side effects:** виклики `passFn` або `failFn` (запис у reporter).
52
- - **Поведінка:**
53
- - Порожній/нерядковий glob — ігнорує.
54
- - Glob, який не треба валідувати (`shouldValidateWorkflowPathsGlob === false`) — звітує `pass` з поясненням «пропущено для перевірки існування».
55
- - Інакше викликає `gitHasAnyTrackedFileMatchingGlob` і звітує відповідно `pass` (є збіги) або `fail` (немає жодного).
56
-
57
- ### `verifyWorkflowEventPathsGlobsExist(relPath, root, passFn, failFn, cwd)`
58
-
59
- - **Сигнатура:** `(relPath: string, root: Record<string, unknown>, passFn, failFn, cwd: string) => void`
60
- - **Параметри:** `root` — розпарсений YAML workflow.
61
- - **Повертає:** `void`.
62
- - **Side effects:** делеговано `verifyOnePathsGlob`.
63
- - **Логіка:** дістає `on.push.paths` та `on.pull_request.paths` через `getObjKey`, ітерує масивами і викликає `verifyOnePathsGlob` для кожного елемента. Якщо `on` відсутній, не є обʼєктом, або `paths` не є масивом — нічого не робить.
64
-
65
- ### `getObjKey(obj, key)`
66
-
67
- - **Сигнатура:** `(obj: unknown, key: string) => unknown`
68
- - **Повертає:** значення поля `obj[key]`, якщо `obj` — non-array object; інакше `undefined`.
69
- - **Side effects:** немає.
70
- - **Призначення:** безпечний доступ до вкладеного поля YAML, де root може виявитись не-обʼєктом після парсингу.
71
-
72
- ### `checkApplyWorkflow(wfDir, files, filename, expectedPath, passFn, failFn)`
73
-
74
- - **Сигнатура:** `async (wfDir: string, files: string[], filename: string, expectedPath: string, passFn, failFn) => Promise<void>`
75
- - **Параметри:**
76
- - `wfDir` — абсолютна директорія `.github/workflows`.
77
- - `files` — список файлів у `wfDir`.
78
- - `filename` — імʼя apply-workflow (наприклад `apply-k8s.yml`).
79
- - `expectedPath` — очікуваний шаблон у `on.push.paths` (наприклад `**/k8s/**/*.yaml`).
80
- - **Повертає:** `Promise<void>`.
81
- - **Side effects:** читає файл `wfDir/filename`, звітує через `passFn`/`failFn`.
82
- - **Логіка:** якщо `filename` відсутній у `files` — повертає без перевірки. Інакше парсить YAML через `parseWorkflowYaml`; перевірку наявності `expectedPath` у `on.push.paths` робить точно через `eventPathsIncludeExact`, а якщо YAML не розпарсився — fallback на наївний `content.includes`.
83
-
84
- ### `checkMegalinter(wfDir, ymlWorkflows, wfDirRel, cwd, passFn, failFn)`
85
-
86
- - **Сигнатура:** `async (wfDir, ymlWorkflows: string[], wfDirRel, cwd, passFn, failFn) => Promise<void>`
87
- - **Повертає:** `Promise<void>`.
88
- - **Side effects:** читає всі `*.yml` у `wfDir`, перевіряє `existsSync` для кореневих конфіг-файлів.
89
- - **Що шукає:**
90
- 1. У вмісті кожного `.yml` workflow — патерни `MEGALINTER_USE_PATTERNS` (`oxsecurity/megalinter-action`, `megalinter/megalinter`).
91
- 2. У корені репо — файли з `MEGALINTER_CONFIG_NAMES` (`.mega-linter.yml`, `.megalinter.yaml`, `.mega-linter.yaml`).
92
- - Знайшов — `fail` з вимогою видалити інтеграцію; не знайшов — один `pass`.
93
-
94
- ### `checkShellcheckInstalled(passFn, failFn)` _(export)_
95
-
96
- - **Сигнатура:** `(passFn, failFn) => void`
97
- - **Повертає:** `void`.
98
- - **Side effects:** `resolveCmd('shellcheck')` (`which`/`where`).
99
- - **Призначення:** `actionlint` (через `bunx github-actionlint`) перевіряє shell-код у `run:` блоках лише коли `shellcheck` доступний; інакше тихо пропускає SC-правила. Локальний `bun lint-ga` міг би бути зеленим, тоді як CI на `ubuntu-latest` (де `shellcheck` передвстановлений) падатиме. Тому відсутність бінарника локально — `fail` з порадами встановлення для macOS/Debian/Ubuntu/Arch.
100
- - **Крос-платформність:** через `resolveCmd` коректно знаходить `shellcheck` і `shellcheck.exe` на Windows.
101
-
102
- ### `checkGaWorkflowFiles(wfDirRel, files, pass, fail)`
103
-
104
- - **Сигнатура:** `(wfDirRel: string, files: string[], pass, fail) => void`
105
- - **Повертає:** `void`.
106
- - **Side effects:** виклики `pass`/`fail`.
107
- - **Перевірки:**
108
- - Файли з розширенням `.yaml` → `fail` (вимога перейменувати на `.yml`); якщо таких немає — один `pass` про відповідність розширень.
109
- - Файли без розширення `.yml` → `fail`.
110
- - Кожен з `REQUIRED_WORKFLOWS` (`clean-ga-workflows.yml`, `clean-merged-branch.yml`, `lint-ga.yml`, `git-ai.yml`) має існувати: є — `pass`, немає — `fail`.
111
-
112
- ### `runAllGaRego(wfDir, ymlWorkflows, cwd, pass, fail)`
113
-
114
- - **Сигнатура:** `async (wfDir: string, ymlWorkflows: string[], cwd: string, pass, fail) => Promise<void>`
115
- - **Повертає:** `Promise<void>`.
116
- - **Side effects:** спавн `conftest` через `runConftestBatch`; читання шаблонів через `loadTemplate`.
117
- - **Логіка:**
118
- 1. **Per-workflow Rego (4 окремих спавни):** для кожного запису в `GA_PER_WORKFLOW_REGO_TARGETS` (4 канонічні workflow), якщо файл існує — підтягує `loadTemplate(concernDir)`, бере `templateData[basename(workflow)]` і викликає `runConftestBatch` з відповідним `policyDirRel`/`namespace`/одним файлом. Кожне порушення → `fail` з префіксом шляху; нуль порушень → один `pass` «відповідає `<namespace>` (rego)».
119
- 2. **Workflow-common батч:** один спавн `conftest` з полісі `ga/workflow_common` на ВСІ `*.yml` у `wfDir`. Шаблон `uses-min-versions.snippet` (якщо є) передається у `templateData`. Порушення → `fail` з префіксом filename; нуль — `pass` про кількість файлів, що відповідають `ga.workflow_common`.
120
- - **Чому 4 окремих спавни, а не один:** namespace ↔ конкретний workflow; інакше правила одного workflow застосуються до неправильного файла.
121
-
122
- ### `check(cwd?)` _(export, точка входу)_
123
-
124
- - **Сигнатура:** `async (cwd?: string) => Promise<number>`
125
- - **Параметри:** `cwd` — корінь репозиторію (за замовчуванням `process.cwd()`).
126
- - **Повертає:** exit code: `0` — все OK, `1` — є порушення (отримується з `reporter.getExitCode()`).
127
- - **Side effects:** створення reporter, читання FS, виклики `git`, спавни `conftest`.
128
- - **Послідовність перевірок:**
129
- 1. Створює `reporter` через `createCheckReporter()`.
130
- 2. Перевіряє наявність директорії `.github/workflows` — якщо немає, негайно `fail` і повертає exit code.
131
- 3. Зчитує `files = readdir(wfDir)`; виокремлює `ymlWorkflows` (`*.yml`).
132
- 4. **Rego-крок** (`runAllGaRego`) — першим, як authoritative source для пер-документних правил.
133
- 5. Наявність `composite action` `.github/actions/setup-bun-deps/action.yml` (його розкочує `npx @nitra/cursor`).
134
- 6. `checkGaWorkflowFiles` — розширення і обовʼязкові workflow.
135
- 7. `checkApplyWorkflow` × 2 — `apply-k8s.yml` (`**/k8s/**/*.yaml`) і `apply-nats-consumer.yml` (`**/consumer.yaml`).
136
- 8. `checkMegalinter` — залишки інтеграції/конфіги.
137
- 9. Для кожного `*.yml` — парсить YAML і викликає `verifyWorkflowEventPathsGlobsExist` (git-залежна перевірка `on.*.paths`).
138
- 10. `checkShellcheckInstalled` — наявність бінарника локально.
139
- 11. Повертає `reporter.getExitCode()`.
140
-
141
- ## Залежності
142
-
143
- ### Node.js core
144
-
145
- - `node:fs` — `existsSync` (синхронна перевірка існування шляхів).
146
- - `node:fs/promises` — `readdir`, `readFile` (асинхронні).
147
- - `node:child_process` — `execFileSync` (для `git ls-files`).
148
- - `node:path` — `basename`, `dirname`, `join`.
149
- - `node:url` — `fileURLToPath` (резолв `import.meta.url` → шлях).
150
-
151
- ### Внутрішні модулі (`@nitra/cursor`)
152
-
153
- - `../../../scripts/lib/check-reporter.mjs` → `createCheckReporter` — фабрика reporter з `pass`/`fail`/`getExitCode`.
154
- - `../../../scripts/lib/gha-workflow.mjs` → `eventPathsIncludeExact`, `parseWorkflowYaml` — YAML-парсер workflow і helper для перевірки точного шляху в `on.<event>.paths`.
155
- - `../../../scripts/utils/resolve-cmd.mjs` → `resolveCmd` — крос-платформний `which`/`where`.
156
- - `../../../scripts/lib/run-conftest-batch.mjs` → `runConftestBatch` — батч-обгортка над `conftest` (hard-fail без `conftest` у PATH).
157
- - `../../../scripts/lib/template.mjs` → `loadTemplate` — підтягує `template.json`-снипети з директорій Rego-полісі.
158
-
159
- ### Зовнішні бінарники (runtime)
160
-
161
- - `git` — для `git ls-files -z -- :(glob)<pattern>`.
162
- - `conftest` — викликається через `runConftestBatch` (hard-fail без нього).
163
- - `shellcheck` — перевіряється на наявність у PATH (інформаційно).
164
-
165
- ### Дані конфігурації
166
-
167
- - `MEGALINTER_USE_PATTERNS` — regexp-патерни для пошуку MegaLinter у workflow.
168
- - `MEGALINTER_CONFIG_NAMES` — імена конфіг-файлів MegaLinter у корені.
169
- - `REQUIRED_WORKFLOWS` — 4 обовʼязкових workflow з ga.mdc.
170
- - `GA_PER_WORKFLOW_REGO_TARGETS` — мапінг `workflow → namespace → policyDirRel` для пер-документного rego.
171
- - `GA_POLICY_DIR` — обчислюється як `dirname(import.meta.url)/../policy` → абсолютний шлях до `npm/rules/ga/policy/`.
172
- - `HERE` — `dirname(fileURLToPath(import.meta.url))` (директорія самого `workflows.mjs`).
173
-
174
- ## Потік виконання / Використання
175
-
176
- ### Виклик з лінт-пайплайну
177
-
178
- Модуль викликається непрямо через CLI `@nitra/cursor` як частина правила ga (роутер у `npm/rules/ga/`). Цільовий репозиторій запускає `bun run lint-ga`, який також виконує `actionlint` і `zizmor` поза цією функцією, але `check()` цього модуля — інтегрована частина того ж run.
179
-
180
- ### Програмний виклик
181
-
182
- ```js
183
- import { check } from '@nitra/cursor/rules/ga/js/workflows.mjs'
184
-
185
- const exitCode = await check(process.cwd())
186
- process.exit(exitCode)
187
- ```
188
-
189
- ### Послідовність на рівні `check()`
190
-
191
- 1. `createCheckReporter()` створює пару колбеків `pass`/`fail` і лічильник exit code.
192
- 2. Перевірка наявності `.github/workflows/` — early return при відсутності.
193
- 3. `readdir` всіх файлів у директорії, фільтрація `*.yml`.
194
- 4. **Rego-фаза:** 4 окремих `conftest` для канонічних workflow + 1 батч `ga.workflow_common` на всі `*.yml`. Усі порушення → `fail` через reporter.
195
- 5. **JS-фаза cross-file:**
196
- - Перевірка `.github/actions/setup-bun-deps/action.yml`.
197
- - Розширення (`yaml→yml`) і набір обовʼязкових workflow.
198
- - `apply-k8s.yml` / `apply-nats-consumer.yml` — точна перевірка `paths` через AST.
199
- - MegaLinter-залишки (workflow + конфіги).
200
- - Кожен workflow → парс YAML → перевірка `on.*.paths` через `git ls-files :(glob)`.
201
- - `shellcheck` у PATH.
202
- 6. Повертає `reporter.getExitCode()` (`0` або `1`).
203
-
204
- ### Інтерпретація результатів
205
-
206
- - Кожен виклик `pass(msg)` додає ОК-рядок у reporter.
207
- - Кожен виклик `fail(msg)` додає помилку і встановлює exit code = 1.
208
- - Звіт виводиться форматтером `check-reporter.mjs` (поза цим модулем).
209
-
210
- ### Розширення модуля
211
-
212
- Нові пер-документні перевірки workflow слід додавати в Rego-полісі (`npm/policy/ga/<concern>/`) — не сюди. Тут — лише ті перевірки, які потребують FS/git/external-tool доступу. Якщо додаєш новий канонічний workflow:
213
-
214
- 1. Додай у `REQUIRED_WORKFLOWS`.
215
- 2. Створи новий пакет у `npm/policy/ga/<name>/` з Rego-правилами.
216
- 3. Зареєструй у `GA_PER_WORKFLOW_REGO_TARGETS` (`workflow`/`namespace`/`policyDirRel`).
217
- 4. За потреби — додай `apply-...` стилю trigger-перевірку через `checkApplyWorkflow`.
5
+ Файл перевіряє GitHub Actions workflows на відповідність певним правилам та стандартам. Він забезпечує консистентність та якість workflow, виявляючи потенційні проблеми, такі як відсутність clean/lint workflow або використання непідтримуваних інструментів. Це частина автоматизованого процесу забезпечення дотримання найкращих практик при розробці GitHub Actions.
6
+
7
+ ## Поведінка
8
+
9
+ * `checkShellcheckInstalled`: Перевіряє наявність бінарника `shellcheck` в системному PATH.
10
+ * `checkGaWorkflowFiles`: Перевіряє наявність workflow-файлів з розширенням `.yml` та відсутність інших розширень.
11
+ * `runAllGaRego`: Запускає Rego-перевірки для всіх workflow-файлів, використовуючи `conftest` для аналізу.
12
+ * `check`: Координує всі перевірки, включаючи Rego-аналіз, перевірку workflow-структури та перевірку наявність файлів.
13
+
14
+ ## Публічний API
15
+
16
+ - checkShellcheckInstalled Перевіряє наявність `shellcheck` у системі та зупиняє workflow, якщо його немає.
17
+ - check Перевіряє відповідність проєкту правилам валідації.
18
+ - runAllGaRego Запускає Rego-перевірку правил, як перший етап валідації.
19
+ - lint-ga — Запускає перевірку `bun lint-ga` з використанням `actionlint` та `zizmor`.
20
+
21
+ ## Гарантії поведінки
22
+
23
+ * Гарантується наявність файлів `package.json`, `.vscode/*` та `.github/zizmor.yml`.
24
+ * Гарантується, що workflow використовує `actions/checkout@v6` перед локальними операціями.
25
+ * Гарантується, що workflow використовує composite action `action.yml` з `npx @nitra/cursor`.
26
+ * Гарантується, що workflow містить `clean-ga-workflows.yml`, `clean-merged-branch.yml`, `lint-ga.yml` та `git-ai.yml`.
27
+ * Гарантується, що workflow використовує `concurrency` для паралельного виконання.
28
+ * Гарантується, що workflow не містить `oven-sh/setup-bun`, `actions/cache`, `bun install` у `uses` або `run`.
29
+ * Гарантується, що workflow не використовує shell-продовження `\` у `run`.
30
+ * Гарантується, що workflow використовує `shellcheck` локально.
31
+ * Гарантується, що workflow перевіряє наявність файлів за допомогою `git ls-files :(glob)` та `on.*.paths`.
32
+ * Гарантується, що workflow перевіряє наявність файлів, що залишилися від MegaLinter.
@@ -1,209 +1,27 @@
1
- # `npm/rules/ga/lint/lint.mjs`
1
+ # lint.mjs
2
2
 
3
3
  ## Огляд
4
4
 
5
- Модуль `lint.mjs` — це **CLI-обгортка над канонічним `lint-ga`** (правило `ga.mdc`), яка виконує комплексну перевірку GitHub Actions workflow-файлів проєкту. Модуль не є самостійним перевіряльником: він агрегує кілька стадій external-tools і делегує JS/Rego-частину перевірок в `rules/ga/fix.mjs::check()`.
6
-
7
- Ключові зони відповідальності:
8
-
9
- 1. **Авто-встановлення обов'язкових бінарників** `shellcheck` і `conftest` через `ensureTool` (підбирається менеджер пакетів per-platform: `brew`/`scoop`/GitHub Release).
10
- 2. **Preflight для `uv`** м'яка перевірка, що `uv` отже `uvx`) є в `PATH`; інакше друкується hint з командами встановлення й функція повертає `1` без авто-install.
11
- 3. **Послідовний запуск трьох стадій перевірки** workflow-ів:
12
- - `bunx github-actionlint` синтаксис/семантика GH Actions, включно з вбудованим `shellcheck` у `run:` блоках (звідси preflight на `shellcheck`).
13
- - `uvx zizmor --offline --collect=workflows .` second-stage security-аудит workflow на ризики (через `uv`/`uvx`).
14
- - `rules/ga/fix.mjs::check()` Rego-полісі (батч `conftest` для `npm/policy/ga/`) **плюс** JS cross-file перевірки правил `ga.mdc`. Це та сама перевірка, що й `npx @nitra/cursor check ga`, тож `lint-ga` є суперсетом цієї підкоманди.
15
- 4. **Серіалізація запуску** через `runStandardLint(import.meta.dirname, steps)` згідно з каноном патерну `lint-*` (див. `.cursor/rules/scripts.mdc`, секція «Серіалізація важких CLI-команд») — без прямого `withLock`.
16
-
17
- Plan B-патерн (rego-authoritative): сама Rego-полісі `npm/policy/ga/` запускає `rules/ga/fix.mjs::check()` як перший крок — `lint.mjs` про це не знає. Раніше `lint.mjs` сам спавнив `conftest` per-workflow для `ga.<name>` і `ga.workflow_common` (PoC); тепер уся логіка централізована в `rules/ga/fix.mjs`, тому одне джерело істини без дублювання між `lint-ga` і `npx @nitra/cursor check ga`.
18
-
19
- Чому preflight на `shellcheck` обов'язковий: без нього `bunx github-actionlint` **мовчки** пропускає shell-перевірки у `run:` блоках; локально `bun lint-ga` лишається зеленим, а CI на `ubuntu-latest` (де `shellcheck` передвстановлений) падає. `ensureTool('shellcheck')` усуває цю різницю.
20
-
21
- Чому `uv` окремо й лише як hint: бінарник `uv` не входить у реєстр `ensureTool` для авто-install у цьому проєкті, тому модуль обмежується інформативною підказкою з командами встановлення (brew / curl / pip), щоб користувач не отримав неінформативну помилку від `uvx zizmor`.
22
-
23
- Модуль експортує одну іменовану стрілкову функцію `runLintGaCli`, яку імпортує `bin/n-cursor.js` як обробник підкоманди `lint-ga`.
24
-
25
- ## Експорти / API
26
-
27
- | Символ | Тип | Експорт | Опис |
28
- | -------------- | ------------------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
29
- | `runLintGaCli` | `() => Promise<number>` (arrow) | `export const` (named) | Точка входу CLI-команди `lint-ga`. Обгортає `runLintGaSteps` у `runStandardLint(import.meta.dirname, ...)` — серіалізація + стандартні timing/exit-code/log конвенції. |
30
-
31
- Жодних інших експортів модуль не надає: усі допоміжні функції (`resolvePreflightBin`, `printPreflightMissingMessage`, `preflight`, `runLintGaSteps`) і константа `UV_PREFLIGHT` — внутрішні.
32
-
33
- JSDoc-typedef `PreflightDep` описує форму внутрішніх preflight-конфігів і не експортується.
34
-
35
- ## Функції
36
-
37
- ### `resolvePreflightBin(dep)`
38
-
39
- ```
40
- function resolvePreflightBin(dep: PreflightDep): string | null
41
- ```
42
-
43
- - **Параметри:**
44
- - `dep: PreflightDep` — опис preflight-залежності з полями `bin`, `winBins`, `explanation`, `install`, `successMsg`.
45
- - **Повертає:** `string | null` — абсолютний шлях до бінарника або `null`, якщо нічого не знайдено в `PATH`.
46
- - **Поведінка:**
47
- - Якщо `platform === 'win32'` (з `node:process`), спершу ітерує `dep.winBins` (наприклад, `['uv.exe']`) і повертає перший резолвлений шлях.
48
- - Якщо жоден `winBins` не знайдено або платформа не Windows — фолбек на `resolveCmd(dep.bin)`.
49
- - **Side effects:** немає (читання `PATH` через `resolveCmd` — pure-resolve).
50
-
51
- ### `printPreflightMissingMessage(dep)`
52
-
53
- ```
54
- function printPreflightMissingMessage(dep: PreflightDep): void
55
- ```
56
-
57
- - **Параметри:** `dep: PreflightDep`.
58
- - **Повертає:** `void`.
59
- - **Поведінка:** друкує у `stderr` блок із кількох рядків:
60
- 1. `❌ <bin> не знайдено в PATH.` (червоний хрестик у вигляді емодзі).
61
- 2. Пояснення з `dep.explanation` (одинарний відступ на 3 пробіли).
62
- 3. Заголовок `Встанови:` і список команд із `dep.install` з відступом на 5 пробілів.
63
- 4. Фінальний рядок-вказівка: `Деталі: ga.mdc → секція про lint-ga.`
64
- - **Side effects:** запис у `process.stderr` через `console.error`. Жодних винятків не кидає.
65
-
66
- ### `preflight(dep)`
67
-
68
- ```
69
- function preflight(dep: PreflightDep): boolean
70
- ```
71
-
72
- - **Параметри:** `dep: PreflightDep`.
73
- - **Повертає:** `boolean` — `true`, якщо бінарник знайдено в `PATH`; `false`, якщо ні.
74
- - **Поведінка:**
75
- - Викликає `resolvePreflightBin(dep)`.
76
- - На pass: друкує `dep.successMsg` через `console.log` і повертає `true`.
77
- - На fail: викликає `printPreflightMissingMessage(dep)` і повертає `false`.
78
- - **Side effects:** запис у `stdout` (success) або `stderr` (fail) через `console.log`/`console.error`.
79
-
80
- ### `runLintGaSteps()`
81
-
82
- ```
83
- async function runLintGaSteps(): Promise<number>
84
- ```
85
-
86
- - **Параметри:** немає.
87
- - **Повертає:** `Promise<number>` — `0`, якщо всі кроки успішні; інакше — exit-code першого кроку, що впав.
88
- - **Поведінка (послідовно):**
89
- 1. `ensureTool('shellcheck')` — авто-install або hard-fail (кидає виняток, який підхоплює `runStandardLint` і конвертує в `exit 1`).
90
- 2. `ensureTool('conftest')` — те саме.
91
- 3. `preflight(UV_PREFLIGHT)` — якщо `uv` не знайдено, повертає `1` без падіння (hint-only).
92
- 4. `runLintStep('actionlint', 'bunx', ['github-actionlint'])` → якщо код ≠ 0, повертає його.
93
- 5. `runLintStep('zizmor', 'uvx', ['zizmor', '--offline', '--collect=workflows', '.'])` → якщо код ≠ 0, повертає його.
94
- 6. Друкує заголовок `▶ check-ga (rego-полісі npm/policy/ga/ + JS cross-file перевірки)` і повертає `await checkGa()` (з `../js/workflows.mjs`).
95
- - **Side effects:**
96
- - Можлива інсталяція пакетів через `ensureTool` (мутує систему: brew/scoop/завантаження бінарників).
97
- - Спавн процесів `bunx`, `uvx` через `runLintStep`.
98
- - Виклик `conftest` (батчем) і JS-перевірки всередині `checkGa()`.
99
- - Запис у `stdout`/`stderr`.
100
-
101
- ### `runLintGaCli` (експортовано)
102
-
103
- ```
104
- export const runLintGaCli: () => Promise<number>
105
- ```
106
-
107
- - **Параметри:** немає.
108
- - **Повертає:** `Promise<number>` — результат `runStandardLint(...)`, тобто фінальний exit-code лінт-команди.
109
- - **Поведінка:** обгортає `runLintGaSteps` у `runStandardLint(import.meta.dirname, runLintGaSteps)`. `import.meta.dirname` визначає каталог `npm/rules/ga/lint/`, що використовується для лок-файлу серіалізації та для службових логів.
110
- - **Side effects:** успадковуються від `runStandardLint` (lock-файл / cleanup) і `runLintGaSteps`.
111
-
112
- ## Внутрішні дані
113
-
114
- ### `UV_PREFLIGHT`
115
-
116
- ```
117
- const UV_PREFLIGHT: PreflightDep
118
- ```
119
-
120
- Конфігурація preflight для `uv`:
121
-
122
- - `bin: 'uv'`
123
- - `winBins: ['uv.exe']`
124
- - `explanation`: двохрядкове пояснення про те, що без `uv`/`uvx` не запуститься `uvx zizmor` (second-stage аудит workflow на ризики GitHub Actions).
125
- - `install`:
126
- - `'macOS: brew install uv'`
127
- - `'Universal: curl -LsSf https://astral.sh/uv/install.sh | sh'`
128
- - `'pip: pip install uv'`
129
- - `successMsg: '✅ uv знайдено в PATH — uvx zizmor запуститься'`
130
-
131
- ### `PreflightDep` (JSDoc typedef)
132
-
133
- Структура опису однієї preflight-залежності:
134
-
135
- | Поле | Тип | Опис |
136
- | ------------- | ---------- | ---------------------------------------------------------------------------------------------- |
137
- | `bin` | `string` | Базове ім'я виконуваного файлу (на Windows додається `.exe` за потреби — через `winBins`). |
138
- | `winBins` | `string[]` | Альтернативні імена на Windows (наприклад, `shellcheck.exe`); якщо порожньо — фолбек на `bin`. |
139
- | `explanation` | `string` | 1–2 рядки, що пояснюють наслідки відсутності бінарника. |
140
- | `install` | `string[]` | Список рядків з командами встановлення (друкуються «як є», з відступом). |
141
- | `successMsg` | `string` | Повідомлення, яке друкується на pass-шлях preflight-у. |
142
-
143
- ## Залежності
144
-
145
- ### Зовнішні (Node.js core)
146
-
147
- - `node:process` — імпортується `platform` (рядок типу `'darwin' | 'linux' | 'win32' | ...`) для гілки Windows у `resolvePreflightBin`.
148
-
149
- ### Внутрішні (проєктні)
150
-
151
- - `../js/workflows.mjs` — імпорт `check as checkGa`. Виконує Rego-полісі (батч `conftest` для `npm/policy/ga/`) і JS cross-file перевірки правил `ga.mdc`. Це фінальний крок `runLintGaSteps`.
152
- - `../../../scripts/utils/resolve-cmd.mjs` — `resolveCmd(name): string | null`. Pure-resolve бінарника у `PATH` (без спавну).
153
- - `../../../scripts/lib/run-lint-step.mjs` — `runLintStep(label, cmd, args): number`. Стандартизований обгортковий спавн одного кроку lint-у з консистентним логом і exit-code-семантикою.
154
- - `../../../scripts/lib/run-standard-lint.mjs` — `runStandardLint(dirname, fn): Promise<number>`. Канонічна серіалізація lint-команди (lock-файл, тривалість, sentry-style cleanup). Згідно з `.cursor/rules/scripts.mdc` (секція «Серіалізація важких CLI-команд»), цей хелпер заміняє прямий `withLock` у lint-обгортках.
155
- - `../../../scripts/lib/ensure-tool.mjs` — `ensureTool(name): void`. Перевіряє наявність бінарника в `PATH` і автоматично встановлює його через відповідний менеджер пакетів per-platform (brew/scoop/GitHub Release). Кидає виняток на нездатність встановити.
156
-
157
- ### Зовнішні CLI-інструменти (запускаються в рантаймі)
158
-
159
- - `shellcheck` — авто-install через `ensureTool`. Потрібен для shell-перевірок у `run:` блоках, які виконує `actionlint`.
160
- - `conftest` — авто-install через `ensureTool`. Використовується **всередині** `checkGa()` для виконання Rego-полісі з `npm/policy/ga/`.
161
- - `uv` / `uvx` — hint-only preflight. Потрібен для запуску `uvx zizmor`.
162
- - `bunx` / `github-actionlint` — виконується через `runLintStep('actionlint', 'bunx', ['github-actionlint'])`.
163
- - `uvx` / `zizmor` — виконується через `runLintStep('zizmor', 'uvx', ['zizmor', '--offline', '--collect=workflows', '.'])`.
164
-
165
- ## Потік виконання / Використання
166
-
167
- ### Інтеграція в CLI
168
-
169
- Модуль експортує `runLintGaCli`, яку імпортує `bin/n-cursor.js` як обробник підкоманди:
170
-
171
- ```
172
- npx @nitra/cursor lint-ga
173
- # або
174
- bun lint-ga
175
- ```
176
-
177
- ### Сценарій типового запуску (happy path)
178
-
179
- 1. `runLintGaCli()` → викликає `runStandardLint(import.meta.dirname, runLintGaSteps)` — отримується lock на `npm/rules/ga/lint/`, починається timing.
180
- 2. Усередині `runLintGaSteps`:
181
- - `ensureTool('shellcheck')` — якщо нема, авто-встановлення (brew/scoop/release).
182
- - `ensureTool('conftest')` — те саме.
183
- - `preflight(UV_PREFLIGHT)` — друкує `✅ uv знайдено в PATH — uvx zizmor запуститься`.
184
- - `runLintStep('actionlint', 'bunx', ['github-actionlint'])` — exit `0`.
185
- - `runLintStep('zizmor', 'uvx', ['zizmor', '--offline', '--collect=workflows', '.'])` — exit `0`.
186
- - Лог `▶ check-ga (rego-полісі npm/policy/ga/ + JS cross-file перевірки)`.
187
- - `await checkGa()` — повертає `0`.
188
- 3. `runStandardLint` логує загальну тривалість, знімає lock і повертає `0`.
189
-
190
- ### Сценарії з falure
191
-
192
- - `shellcheck` або `conftest` не встановлено й `ensureTool` не зміг встановити — виняток пробивається крізь `runLintGaSteps`, `runStandardLint` ловить його та конвертує у `exit 1`.
193
- - `uv` відсутній — `preflight(UV_PREFLIGHT)` друкує hint, `runLintGaSteps` повертає `1`, наступні кроки не виконуються.
194
- - `actionlint` повертає не-нульовий код — `runLintGaSteps` повертає цей код, `zizmor` і `checkGa()` не запускаються.
195
- - `zizmor` повертає не-нульовий код — те саме: одразу повертається його exit-code.
196
- - `checkGa()` повертає не-нульовий код — він і є фінальним результатом `runLintGaCli`.
197
-
198
- ### Контракт «суперсет `check ga`»
199
-
200
- Оскільки фінальний крок `runLintGaSteps` — `checkGa()` (та сама `check`-функція з `../js/workflows.mjs`, що використовується підкомандою `npx @nitra/cursor check ga`), `lint-ga` є суперсетом цієї перевірки: він додає `actionlint` (синтаксис) і `zizmor` (security-аудит) перед `check`. Це усуває потребу запускати `check ga` окремо в pipeline-ах, що вже виконують `lint-ga`.
201
-
202
- ### Канон патерну `lint-*`
203
-
204
- Файл сповідує канон патерну `lint-*` із `.cursor/rules/scripts.mdc` (секція «Серіалізація важких CLI-команд»):
205
-
206
- - Серіалізація — через `runStandardLint`, **не** через прямий `withLock`.
207
- - Кожна стадія — через `runLintStep` (узгоджений лог-формат, semantic exit-codes).
208
- - Експортована функція має назву `run<Name>Cli` (тут — `runLintGaCli`).
209
- - Фінальна делегація бізнес-частини — у відповідний `rules/<name>/fix.mjs::check()`, щоб бути одним джерелом істини з `npx @nitra/cursor check <name>`.
5
+ Файл надає CLI-обгортку для інструменту `lint-ga`, який автоматично встановлює необхідні інструменти, такі як `shellcheck` та `conftest`, для перевірки коду. Він послідовно виконує перевірку коду за допомогою Rego та JavaScript, забезпечуючи узгодженість з правилами `ga.mdc`. Цей файл служить централізованим джерелом для виконання та налаштування процесу linting, що спрощує інтеграцію в CI/CD.
6
+
7
+ ## Поведінка
8
+
9
+ 1. **Підготовка середовища:** Перевіряє наявність необхідних інструментів: `shellcheck` та `conftest`. Якщо їх немає, автоматично встановлює їх, щоб забезпечити коректну роботу.
10
+ 2. **Перевірка залежностей:** Перевіряє наявність `uv`. Якщо `uv` відсутній, надає інструкції щодо його встановлення, щоб забезпечити виконання наступного етапу.
11
+ 3. **Запуск попередньої перевірки:** Виконує попередню перевірку за допомогою `github-actionlint`, що дозволяє виявити проблеми на ранніх етапах.
12
+ 4. **Виконання аналізу коду:** Запускає `uvx zizmor` для поглибленого аналізу коду, включаючи перевірку на відповідність стандартам та виявлення потенційних проблем.
13
+ 5. **Перевірка Rego-полісів:** Запускає Rego-полісі з `npm/policy/ga/` для забезпечення відповідності коду політикам.
14
+ 6. **Перевірка правил:** Виконує перевірку правил `ga.mdc` як для JS-коду, так і для Rego-полісів, забезпечуючи загальну відповідність стандартам.
15
+ 7. **Звіт про результати:** Формує звіт про результати перевірки, що включає інформацію про виявлені проблеми та їх серйозність.
16
+ 8. **Повернення статусу:** Повертає код завершення, що вказує на успішне виконання або наявність проблем, які потребують уваги.
17
+
18
+ ## Гарантії поведінки
19
+
20
+ * `ensureTool` встановлює `shellcheck` та `conftest` згідно з обраним методом встановлення (brew, scoop, GitHub Release).
21
+ * Якщо `uv` не встановлено, `uvx zizmor` завершується з помилкою.
22
+ * `bunx github-actionlint` пропускає shell-перевірки в `run:` блоках, якщо `shellcheck` не вказаний у PATH.
23
+ * `runLintGaCli` повертає `false` або `null` при помилках, не викликаючи виняток.
24
+ * Не використовується кешування.
25
+ * `uvx zizmor` вимагає наявності `uv`.
26
+ * Логіка встановлення та запуску перевірок централізована у `rules/ga/fix.mjs`.
27
+ * `runLintGaCli` використовується як підкоманда `lint-ga`.