@nitra/cursor 1.8.165 → 1.8.168
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 +24 -0
- package/bin/n-cursor.js +10 -1
- package/mdc/ga.mdc +13 -3
- package/package.json +2 -2
- package/scripts/check-ga.mjs +36 -7
- package/scripts/lint-ga.mjs +158 -0
- package/scripts/utils/resolve-cmd.mjs +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,30 @@
|
|
|
4
4
|
|
|
5
5
|
Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
|
|
6
6
|
|
|
7
|
+
## [1.8.168] - 2026-05-03
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `lint-ga.mjs`: до preflight на `shellcheck` додано preflight на [`uv`](https://docs.astral.sh/uv/) (постачає `uvx` для `uvx zizmor`). Якщо `uv` відсутній у `PATH` — `n-cursor lint-ga` падає з exit 1 і підказками `brew install uv` / `curl -LsSf https://astral.sh/uv/install.sh | sh` / `pip install uv`. Обидва preflight’и повідомляються незалежно: якщо нема одночасно й `shellcheck`, і `uv`, користувач одразу бачить обидві підказки, а не лише першу.
|
|
12
|
+
- `lint-ga.mjs`: винесено внутрішній `PreflightDep` із `bin`/`winBins`/`explanation`/`install`/`successMsg` — однотипний pattern для додавання нових залежностей у preflight без копіпасти.
|
|
13
|
+
|
|
14
|
+
## [1.8.167] - 2026-05-03
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- `lint-ga.mjs` / `bin/n-cursor.js`: нова CLI-підкоманда `n-cursor lint-ga` (експорт `runLintGaCli`). Робить preflight на `shellcheck` (exit 1 + brew/apt/pacman підказки, коли його немає в `PATH`), тоді послідовно запускає `bunx github-actionlint` і `uvx zizmor --offline --collect=workflows .` через `spawnSync` з `stdio: 'inherit'`. Тепер і `bun lint-ga` сигналізує про відсутність shellcheck — раніше це робила лише `check ga`.
|
|
19
|
+
- `ga.mdc` (v1.5): канонічний скрипт `lint-ga` у `package.json` тепер `n-cursor lint-ga` (а не `bunx github-actionlint && uvx zizmor …`); `check-ga.mjs` валідує саме цю форму. Виклик через bin-ім’я `n-cursor`, бо `bun run` транслює `npx` у `bun x`, а `bun x @nitra/cursor` для скоупованого пакету з одним bin-ім’ям повертає 0 без виконання.
|
|
20
|
+
|
|
21
|
+
## [1.8.166] - 2026-05-03
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- `ga.mdc` (v1.4) / `check-ga.mjs`: нова перевірка локального [`shellcheck`](https://www.shellcheck.net/) у `PATH`. Без нього `actionlint` (`bunx github-actionlint`) мовчки пропускає shell-перевірки в `run:` блоках, тож локальний `bun lint-ga` дає зелений результат, який падає в CI на `ubuntu-latest` (де shellcheck передвстановлений). `npx @nitra/cursor check ga` тепер `fail` з підказкою встановлення (`brew install shellcheck` / `apt-get install -y shellcheck` / `pacman -S shellcheck`).
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- `utils/resolve-cmd.mjs`: явно передаємо `process.env` у `spawnSync('which'/'where', ...)`, щоб у Bun зміни `PATH` у runtime (наприклад, підстановка стабів у тестах) бачилися дочірнім процесом. Без цього Bun використовував би snapshot оточення на старті.
|
|
30
|
+
|
|
7
31
|
## [1.8.165] - 2026-05-01
|
|
8
32
|
|
|
9
33
|
### Changed
|
package/bin/n-cursor.js
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* `npx \@nitra/cursor rename-yaml-extensions` — k8s `*.yml` → `*.yaml`, `.github` `*.yaml` → `*.yml` (опції: `--dry-run`, `--root=…`; див. bin/rename-yaml-extensions.mjs)
|
|
12
12
|
* `npx \@nitra/cursor stop-hook` — точка входу Stop hook Claude Code (читає stdin, виходить 0 при `stop_hook_active`,
|
|
13
13
|
* інакше викликає `check`); прописується автоматично в `.claude/settings.json`
|
|
14
|
+
* `npx \@nitra/cursor lint-ga` — канонічний lint-ga (ga.mdc): preflight на `shellcheck` →
|
|
15
|
+
* `bunx github-actionlint` → `uvx zizmor --offline --collect=workflows .`
|
|
14
16
|
*
|
|
15
17
|
* Claude Code інтеграція: під час синку, окрім `.cursor/rules` і `.claude/commands` (з skills), CLI ще раз
|
|
16
18
|
* синхронізує `.claude/settings.json` (hooks + permissions; merge — користувацькі поля зберігаються),
|
|
@@ -57,6 +59,7 @@ import { buildAgentsCommandBulletItems } from '../scripts/build-agents-commands.
|
|
|
57
59
|
import { detectAutoRulesAndSkills, mergeConfigWithAutoDetected, normalizeIdList } from '../scripts/auto-rules.mjs'
|
|
58
60
|
import { runStopHookCli } from '../scripts/claude-stop-hook.mjs'
|
|
59
61
|
import { ensureNitraCursorInRootDevDependencies } from '../scripts/ensure-nitra-cursor-dev-dependencies.mjs'
|
|
62
|
+
import { runLintGaCli } from '../scripts/lint-ga.mjs'
|
|
60
63
|
import { syncClaudeConfig } from '../scripts/sync-claude-config.mjs'
|
|
61
64
|
import { upgradeNitraCursorToLatestAndBunInstall } from '../scripts/upgrade-nitra-cursor-and-install.mjs'
|
|
62
65
|
import { runRenameYamlExtensionsCli } from './rename-yaml-extensions.mjs'
|
|
@@ -1222,6 +1225,12 @@ try {
|
|
|
1222
1225
|
|
|
1223
1226
|
break
|
|
1224
1227
|
}
|
|
1228
|
+
case 'lint-ga': {
|
|
1229
|
+
// Канонічний lint-ga з preflight на shellcheck → actionlint → zizmor (ga.mdc).
|
|
1230
|
+
process.exitCode = runLintGaCli()
|
|
1231
|
+
|
|
1232
|
+
break
|
|
1233
|
+
}
|
|
1225
1234
|
case undefined:
|
|
1226
1235
|
case '': {
|
|
1227
1236
|
await runSync()
|
|
@@ -1231,7 +1240,7 @@ try {
|
|
|
1231
1240
|
default: {
|
|
1232
1241
|
console.error(`❌ Невідома команда: ${command}`)
|
|
1233
1242
|
console.error(
|
|
1234
|
-
` Очікується: (без аргументів) синхронізація правил, check, rename-yaml-extensions, stop-hook`
|
|
1243
|
+
` Очікується: (без аргументів) синхронізація правил, check, rename-yaml-extensions, stop-hook, lint-ga`
|
|
1235
1244
|
)
|
|
1236
1245
|
process.exitCode = 1
|
|
1237
1246
|
}
|
package/mdc/ga.mdc
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Правила форматів для .github/workflows
|
|
3
3
|
alwaysApply: true
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.6'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
У `.github/workflows/` лише **`.yml`**. Мають бути **`clean-ga-workflows.yml`**, **`clean-merged-branch.yml`**, **`lint-ga.yml`**, **`git-ai.yml`**. Якщо є **`apply-k8s.yml`** / **`apply-nats-consumer.yml`** — paths у тригері як у фрагментах.
|
|
@@ -231,14 +231,24 @@ jobs:
|
|
|
231
231
|
- uses: ./.github/actions/setup-bun-deps
|
|
232
232
|
```
|
|
233
233
|
|
|
234
|
-
**Лінт:** [actionlint](https://github.com/rhysd/actionlint) через [github-actionlint](https://www.npmjs.com/package/github-actionlint); [zizmor](https://docs.zizmor.sh) — `uvx`, офлайн.
|
|
234
|
+
**Лінт:** [actionlint](https://github.com/rhysd/actionlint) через [github-actionlint](https://www.npmjs.com/package/github-actionlint); [zizmor](https://docs.zizmor.sh) — `uvx`, офлайн. Канонічний скрипт у корені делегує виконання CLI `n-cursor lint-ga` (бінарка з `node_modules/.bin/` пакету `@nitra/cursor`), який робить preflight на `shellcheck` і послідовно запускає `actionlint` та `zizmor`:
|
|
235
235
|
|
|
236
236
|
```json title="package.json"
|
|
237
237
|
"scripts": {
|
|
238
|
-
"lint-ga": "
|
|
238
|
+
"lint-ga": "n-cursor lint-ga"
|
|
239
239
|
}
|
|
240
240
|
```
|
|
241
241
|
|
|
242
|
+
> Не використовуй `npx --no @nitra/cursor lint-ga` — `bun run` автоматично транслює `npx` у `bun x`, а `bun x` для скоупованого пакету з одним bin-ім’ям повертає 0 без виконання. Виклик через bin-ім’я `n-cursor` працює і у `bun run`, і у `npm run`.
|
|
243
|
+
|
|
244
|
+
CLI виконує:
|
|
245
|
+
|
|
246
|
+
1. Preflight на [`shellcheck`](https://www.shellcheck.net/) у `PATH` — без нього `actionlint` мовчки пропускає shell-перевірки в `run:` блоках, тож локальний прогін зеленіє, а CI на `ubuntu-latest` (де shellcheck передвстановлений) падає на тих самих workflow. Встановлення: `brew install shellcheck` (macOS), `sudo apt-get install -y shellcheck` (Debian/Ubuntu), `sudo pacman -S shellcheck` (Arch).
|
|
247
|
+
2. Preflight на [`uv`](https://docs.astral.sh/uv/) у `PATH` — постачає `uvx`, без якого не запуститься `uvx zizmor`. Встановлення: `brew install uv` (macOS), `curl -LsSf https://astral.sh/uv/install.sh | sh` (universal), `pip install uv`.
|
|
248
|
+
3. Якщо хоча б один preflight не пройшов — exit 1 (підказки для всіх відсутніх залежностей друкуються разом, а не лише для першої).
|
|
249
|
+
4. `bunx github-actionlint`.
|
|
250
|
+
5. `uvx zizmor --offline --collect=workflows .`.
|
|
251
|
+
|
|
242
252
|
**`.github/zizmor.yml`:** для [unpinned-uses](https://docs.zizmor.sh/audits/#unpinned-uses) — політика **`ref-pin`**, якщо в `uses:` семантичні теги. За потреби вимкни [template-injection](https://docs.zizmor.sh/audits/#template-injection):
|
|
243
253
|
|
|
244
254
|
```yaml title=".github/zizmor.yml"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nitra/cursor",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.168",
|
|
4
4
|
"description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -49,6 +49,6 @@
|
|
|
49
49
|
"node": ">=25"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@nitra/cursor": "^1.8.
|
|
52
|
+
"@nitra/cursor": "^1.8.168"
|
|
53
53
|
}
|
|
54
54
|
}
|
package/scripts/check-ga.mjs
CHANGED
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
getStepUses,
|
|
36
36
|
parseWorkflowYaml
|
|
37
37
|
} from './utils/gha-workflow.mjs'
|
|
38
|
+
import { resolveCmd } from './utils/resolve-cmd.mjs'
|
|
38
39
|
|
|
39
40
|
/** Шаблони наявності MegaLinter у вмісті workflow */
|
|
40
41
|
const MEGALINTER_USE_PATTERNS = [/oxsecurity\/megalinter-action/i, /megalinter\/megalinter/i]
|
|
@@ -738,16 +739,43 @@ async function checkLintGaScript(passFn, failFn) {
|
|
|
738
739
|
return
|
|
739
740
|
}
|
|
740
741
|
passFn('package.json містить lint-ga')
|
|
741
|
-
|
|
742
|
-
|
|
742
|
+
// Канонічний скрипт делегує виконання CLI `n-cursor lint-ga` (bin з `@nitra/cursor`) — там preflight
|
|
743
|
+
// на shellcheck + послідовно `bunx github-actionlint` і `uvx zizmor --offline --collect=workflows .`.
|
|
744
|
+
// Виклик через bin-ім’я `n-cursor`, а не `npx --no @nitra/cursor`, бо `bun run` транслює `npx` у `bun x`,
|
|
745
|
+
// а `bun x @nitra/cursor` для скоупованого пакету з одним bin-ім’ям повертає 0 без виконання.
|
|
746
|
+
if (/\bn-cursor\s+lint-ga\b/.test(lg)) {
|
|
747
|
+
passFn('lint-ga делегує CLI n-cursor lint-ga (preflight shellcheck + actionlint + zizmor)')
|
|
743
748
|
} else {
|
|
744
|
-
failFn(
|
|
749
|
+
failFn(
|
|
750
|
+
'lint-ga має бути "n-cursor lint-ga" — CLI робить preflight shellcheck перед actionlint/zizmor (ga.mdc)'
|
|
751
|
+
)
|
|
745
752
|
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Перевіряє наявність локального `shellcheck` у PATH. `actionlint` (`bunx github-actionlint`)
|
|
757
|
+
* запускає shell-перевірки в кроках `run:` workflow тільки коли `shellcheck` доступний; інакше
|
|
758
|
+
* мовчки пропускає SC-правила, через що локальний `bun lint-ga` зелений, а CI на ubuntu-latest
|
|
759
|
+
* (де shellcheck передвстановлений) падає. Тому відсутність бінарника локально — `fail`.
|
|
760
|
+
*
|
|
761
|
+
* Кросплатформно: `resolveCmd` використовує `which`/`where` і однаково знаходить `shellcheck`
|
|
762
|
+
* та `shellcheck.exe` на Windows.
|
|
763
|
+
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
764
|
+
* @param {(msg: string) => void} failFn callback при помилці
|
|
765
|
+
*/
|
|
766
|
+
function checkShellcheckInstalled(passFn, failFn) {
|
|
767
|
+
if (resolveCmd('shellcheck')) {
|
|
768
|
+
passFn('shellcheck встановлений локально, actionlint виконуватиме SC-правила, як у CI')
|
|
769
|
+
return
|
|
750
770
|
}
|
|
771
|
+
failFn(
|
|
772
|
+
[
|
|
773
|
+
'shellcheck не знайдено в PATH — actionlint без нього мовчки пропускає shell-перевірки в run: блоках,',
|
|
774
|
+
'тому локальний `bun lint-ga` буде зелений, а CI на ubuntu-latest (де shellcheck передвстановлений) падатиме.',
|
|
775
|
+
'Встанови: macOS — `brew install shellcheck`; Debian/Ubuntu — `sudo apt-get install -y shellcheck`;',
|
|
776
|
+
'Arch — `sudo pacman -S shellcheck` (ga.mdc)'
|
|
777
|
+
].join(' ')
|
|
778
|
+
)
|
|
751
779
|
}
|
|
752
780
|
|
|
753
781
|
/**
|
|
@@ -978,6 +1006,7 @@ export async function check() {
|
|
|
978
1006
|
await checkLintGaScript(pass, fail)
|
|
979
1007
|
await checkLintGaWorkflow(wfDir, pass, fail)
|
|
980
1008
|
await checkGitAiWorkflow(wfDir, pass, fail)
|
|
1009
|
+
checkShellcheckInstalled(pass, fail)
|
|
981
1010
|
|
|
982
1011
|
return reporter.getExitCode()
|
|
983
1012
|
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI-обгортка над канонічним `lint-ga` (ga.mdc): робить preflight на `shellcheck` і `uv` (для `uvx`),
|
|
3
|
+
* тоді послідовно виконує `bunx github-actionlint` і `uvx zizmor --offline --collect=workflows .`.
|
|
4
|
+
*
|
|
5
|
+
* Без preflight `actionlint` (через `bunx github-actionlint`) мовчки пропускає shell-перевірки в
|
|
6
|
+
* `run:` блоках, коли `shellcheck` відсутній у PATH; локально `bun lint-ga` лишається зеленим, а CI
|
|
7
|
+
* на ubuntu-latest (де shellcheck передвстановлений) падає. Preflight робить цю різницю явною.
|
|
8
|
+
*
|
|
9
|
+
* `uv` потрібен для `uvx zizmor`. Якщо його нема — `uvx zizmor` падає неінформативно («command not
|
|
10
|
+
* found»); підказка з командою встановлення коротша й корисніша.
|
|
11
|
+
*
|
|
12
|
+
* Експортовано окремо `runLintGaCli` — використовується з `bin/n-cursor.js` як підкоманда `lint-ga`.
|
|
13
|
+
*/
|
|
14
|
+
import { spawnSync } from 'node:child_process'
|
|
15
|
+
import { platform } from 'node:process'
|
|
16
|
+
|
|
17
|
+
import { resolveCmd } from './utils/resolve-cmd.mjs'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Опис залежності preflight-ом: бінарник, для чого потрібен, і команди встановлення.
|
|
21
|
+
*
|
|
22
|
+
* @typedef {object} PreflightDep
|
|
23
|
+
* @property {string} bin ім'я виконуваного файлу (на Windows додається `.exe` за потреби)
|
|
24
|
+
* @property {string[]} winBins альтернативні імена на Windows (`shellcheck.exe`); якщо нема — fallback на `bin`
|
|
25
|
+
* @property {string} explanation 1-2 рядки про наслідки відсутності
|
|
26
|
+
* @property {string[]} install список рядків з командами встановлення (друкуються як є, з відступом)
|
|
27
|
+
* @property {string} successMsg повідомлення на pass-шлях
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/** @type {PreflightDep} */
|
|
31
|
+
const SHELLCHECK_PREFLIGHT = {
|
|
32
|
+
bin: 'shellcheck',
|
|
33
|
+
winBins: ['shellcheck.exe'],
|
|
34
|
+
explanation: [
|
|
35
|
+
'Без нього `actionlint` пропускає shell-перевірки в run: блоках,',
|
|
36
|
+
'тож локальний прогін зеленіє, а CI на ubuntu-latest (де shellcheck',
|
|
37
|
+
'передвстановлений) падає на тих самих workflow.'
|
|
38
|
+
].join('\n '),
|
|
39
|
+
install: [
|
|
40
|
+
'macOS: brew install shellcheck',
|
|
41
|
+
'Debian/Ubuntu: sudo apt-get install -y shellcheck',
|
|
42
|
+
'Arch: sudo pacman -S shellcheck'
|
|
43
|
+
],
|
|
44
|
+
successMsg: '✅ shellcheck знайдено в PATH — actionlint виконуватиме SC-правила, як у CI'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** @type {PreflightDep} */
|
|
48
|
+
const UV_PREFLIGHT = {
|
|
49
|
+
bin: 'uv',
|
|
50
|
+
winBins: ['uv.exe'],
|
|
51
|
+
explanation: [
|
|
52
|
+
'Без `uv` (а отже без `uvx`) не виконається `uvx zizmor` — second-stage аудит',
|
|
53
|
+
'workflow на ризики GitHub Actions просто не запуститься.'
|
|
54
|
+
].join('\n '),
|
|
55
|
+
install: [
|
|
56
|
+
'macOS: brew install uv',
|
|
57
|
+
'Universal: curl -LsSf https://astral.sh/uv/install.sh | sh',
|
|
58
|
+
'pip: pip install uv'
|
|
59
|
+
],
|
|
60
|
+
successMsg: '✅ uv знайдено в PATH — uvx zizmor запуститься'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Шукає бінарник у PATH з урахуванням Windows: спершу `winBins`, потім `bin`.
|
|
65
|
+
* @param {PreflightDep} dep опис залежності
|
|
66
|
+
* @returns {string | null} абсолютний шлях або null
|
|
67
|
+
*/
|
|
68
|
+
function resolvePreflightBin(dep) {
|
|
69
|
+
if (platform === 'win32') {
|
|
70
|
+
for (const name of dep.winBins) {
|
|
71
|
+
const r = resolveCmd(name)
|
|
72
|
+
if (r) return r
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return resolveCmd(dep.bin)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Друкує блок з причиною fail і командами встановлення.
|
|
80
|
+
* @param {PreflightDep} dep опис залежності
|
|
81
|
+
* @returns {void}
|
|
82
|
+
*/
|
|
83
|
+
function printPreflightMissingMessage(dep) {
|
|
84
|
+
console.error(`❌ ${dep.bin} не знайдено в PATH.`)
|
|
85
|
+
console.error(` ${dep.explanation}`)
|
|
86
|
+
console.error(' Встанови:')
|
|
87
|
+
for (const line of dep.install) {
|
|
88
|
+
console.error(` ${line}`)
|
|
89
|
+
}
|
|
90
|
+
console.error(' Деталі: ga.mdc → секція про lint-ga.')
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Запускає preflight-перевірку: pass → лог success і повертає true; fail → лог hint і повертає false.
|
|
95
|
+
* @param {PreflightDep} dep опис залежності
|
|
96
|
+
* @returns {boolean} чи знайдено бінарник
|
|
97
|
+
*/
|
|
98
|
+
function preflight(dep) {
|
|
99
|
+
if (resolvePreflightBin(dep)) {
|
|
100
|
+
console.log(dep.successMsg)
|
|
101
|
+
return true
|
|
102
|
+
}
|
|
103
|
+
printPreflightMissingMessage(dep)
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Запускає крок lint-ga з відображенням команди користувачу. Stdout/stderr дочірнього процесу
|
|
109
|
+
* передається користувачу як є (`stdio: 'inherit'`), щоб виглядало як прямий виклик у shell.
|
|
110
|
+
* @param {string} title заголовок для логу (наприклад `actionlint`)
|
|
111
|
+
* @param {string} cmd ім'я команди (`bunx`, `uvx`)
|
|
112
|
+
* @param {string[]} args аргументи команди
|
|
113
|
+
* @returns {number} код виходу дочірнього процесу (0 — OK, інше — помилка)
|
|
114
|
+
*/
|
|
115
|
+
function runStep(title, cmd, args) {
|
|
116
|
+
console.log(`\n▶ ${title}: ${cmd} ${args.join(' ')}`)
|
|
117
|
+
const resolved = resolveCmd(cmd)
|
|
118
|
+
if (!resolved) {
|
|
119
|
+
console.error(`❌ ${cmd} не знайдено в PATH (${title}).`)
|
|
120
|
+
return 127
|
|
121
|
+
}
|
|
122
|
+
const r = spawnSync(resolved, args, { stdio: 'inherit', env: process.env })
|
|
123
|
+
if (r.error) {
|
|
124
|
+
console.error(`❌ Не вдалося запустити ${cmd}: ${r.error.message}`)
|
|
125
|
+
return 1
|
|
126
|
+
}
|
|
127
|
+
return r.status ?? 1
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Виконує канонічний `lint-ga` з preflight-перевірками й послідовним запуском actionlint + zizmor.
|
|
132
|
+
*
|
|
133
|
+
* Послідовність:
|
|
134
|
+
* 1) preflight: `shellcheck` (для actionlint SC-правил) і `uv` (для `uvx zizmor`); відсутній → exit 1;
|
|
135
|
+
* 2) `bunx github-actionlint`;
|
|
136
|
+
* 3) `uvx zizmor --offline --collect=workflows .`.
|
|
137
|
+
*
|
|
138
|
+
* Якщо хоча б один preflight не пройшов — виходимо одразу з кодом 1, **до** запуску actionlint/zizmor,
|
|
139
|
+
* бо їхні власні повідомлення про відсутність залежностей менш інформативні (особливо для shellcheck —
|
|
140
|
+
* actionlint мовчки пропускає SC-правила; ця перевірка — головний сенс обгортки).
|
|
141
|
+
*
|
|
142
|
+
* Першу помилку від actionlint/zizmor повертаємо як код виходу; наступні кроки не запускаються
|
|
143
|
+
* (відповідає `&&` у package.json).
|
|
144
|
+
* @returns {number} 0 — все OK, інакше — код першого кроку, що впав
|
|
145
|
+
*/
|
|
146
|
+
export function runLintGaCli() {
|
|
147
|
+
let preflightOk = true
|
|
148
|
+
for (const dep of [SHELLCHECK_PREFLIGHT, UV_PREFLIGHT]) {
|
|
149
|
+
if (!preflight(dep)) preflightOk = false
|
|
150
|
+
}
|
|
151
|
+
if (!preflightOk) return 1
|
|
152
|
+
|
|
153
|
+
const actionlintCode = runStep('actionlint', 'bunx', ['github-actionlint'])
|
|
154
|
+
if (actionlintCode !== 0) return actionlintCode
|
|
155
|
+
|
|
156
|
+
const zizmorCode = runStep('zizmor', 'uvx', ['zizmor', '--offline', '--collect=workflows', '.'])
|
|
157
|
+
return zizmorCode
|
|
158
|
+
}
|
|
@@ -14,7 +14,9 @@ import { platform } from 'node:process'
|
|
|
14
14
|
*/
|
|
15
15
|
export function resolveCmd(cmd) {
|
|
16
16
|
const whichCmd = platform === 'win32' ? 'where' : 'which'
|
|
17
|
-
|
|
17
|
+
// Явно передаємо `process.env`, інакше Bun вмикає snapshot оточення на старті процесу
|
|
18
|
+
// і не бачить змін `process.env.PATH` у runtime (типова ситуація — підстановка стабів у тестах).
|
|
19
|
+
const result = spawnSync(whichCmd, [cmd], { encoding: 'utf8', env: process.env })
|
|
18
20
|
if (result.status !== 0 || result.error) {
|
|
19
21
|
return null
|
|
20
22
|
}
|