@nitra/cursor 1.13.83 → 1.13.85
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/.claude-template/commands/n-check.md +2 -2
- package/CHANGELOG.md +56 -27
- package/README.md +10 -10
- package/bin/n-cursor.js +40 -60
- package/package.json +1 -1
- package/rules/abie/abie.mdc +2 -2
- package/rules/abie/fix.mjs +5 -1
- package/rules/adr/fix.mjs +5 -1
- package/rules/adr/js/hooks/check.mjs +1 -1
- package/rules/bun/bun.mdc +1 -1
- package/rules/bun/fix.mjs +5 -1
- package/rules/bun/js/layout/check.mjs +1 -1
- package/rules/capacitor/fix.mjs +5 -1
- package/rules/capacitor/policy/package_json/package_json.rego +3 -3
- package/rules/changelog/changelog.mdc +1 -1
- package/rules/changelog/fix.mjs +5 -1
- package/rules/ci4/ci4.mdc +1 -1
- package/rules/ci4/fix.mjs +5 -1
- package/rules/docker/docker.mdc +6 -6
- package/rules/docker/fix.mjs +5 -1
- package/rules/docker/lint/lint.mjs +10 -3
- package/rules/docker/policy/package_json/package_json.rego +1 -1
- package/rules/efes/efes.mdc +1 -1
- package/rules/efes/fix.mjs +5 -1
- package/rules/feedback/feedback.mdc +2 -2
- package/rules/feedback/fix.mjs +5 -1
- package/rules/ga/fix.mjs +5 -1
- package/rules/ga/lint/lint.mjs +5 -5
- package/rules/ga/policy/workflow_common/workflow_common.rego +1 -1
- package/rules/graphql/fix.mjs +5 -1
- package/rules/graphql/policy/vscode_extensions/vscode_extensions.rego +2 -2
- package/rules/hasura/fix.mjs +5 -1
- package/rules/image-avif/fix.mjs +5 -1
- package/rules/image-avif/image-avif.mdc +1 -1
- package/rules/image-avif/js/avif_generation/check.mjs +1 -1
- package/rules/image-compress/fix.mjs +5 -1
- package/rules/image-compress/js/package_setup/check.mjs +1 -1
- package/rules/js-bun-db/fix.mjs +5 -1
- package/rules/js-bun-redis/fix.mjs +5 -1
- package/rules/js-lint/fix.mjs +5 -1
- package/rules/js-lint/js/tooling/check.mjs +2 -2
- package/rules/js-lint/js-lint.mdc +1 -1
- package/rules/js-mssql/fix.mjs +5 -1
- package/rules/js-mssql/policy/package_json/package_json.rego +2 -2
- package/rules/js-run/fix.mjs +5 -1
- package/rules/js-run/js/runtime/check.mjs +3 -3
- package/rules/k8s/fix.mjs +5 -1
- package/rules/k8s/js/manifests/check.mjs +1 -1
- package/rules/k8s/k8s.mdc +12 -12
- package/rules/k8s/lint/lint.mjs +12 -5
- package/rules/k8s/policy/base_kustomization/base_kustomization.rego +3 -3
- package/rules/k8s/policy/base_manifest/base_manifest.rego +2 -2
- package/rules/k8s/policy/gateway/gateway.rego +2 -2
- package/rules/k8s/policy/hasura_configmap/hasura_configmap.rego +3 -3
- package/rules/k8s/policy/hasura_httproute/hasura_httproute.rego +1 -1
- package/rules/k8s/policy/hpa_pdb/hpa_pdb.rego +1 -1
- package/rules/k8s/policy/kustomization/kustomization.rego +2 -2
- package/rules/k8s/policy/manifest/manifest.rego +4 -4
- package/rules/k8s/policy/svc_hl_yaml/svc_hl_yaml.rego +2 -2
- package/rules/k8s/policy/svc_yaml/svc_yaml.rego +2 -2
- package/rules/nginx-default-tpl/fix.mjs +5 -1
- package/rules/nginx-default-tpl/policy/vscode_extensions/vscode_extensions.rego +2 -2
- package/rules/nginx-default-tpl/policy/vscode_settings/vscode_settings.rego +1 -1
- package/rules/npm-module/fix.mjs +5 -1
- package/rules/npm-module/js/package_structure/check.mjs +2 -2
- package/rules/php/fix.mjs +5 -1
- package/rules/php/js/tooling/check.mjs +2 -2
- package/rules/rego/fix.mjs +5 -1
- package/rules/rego/lint/lint.mjs +12 -2
- package/rules/security/fix.mjs +5 -1
- package/rules/security/security.mdc +1 -1
- package/rules/style-lint/fix.mjs +5 -1
- package/rules/style-lint/js/tooling/check.mjs +2 -2
- package/rules/tauri/fix.mjs +5 -1
- package/rules/tauri/js/tooling/check.mjs +1 -1
- package/rules/tauri/policy/vscode_extensions/vscode_extensions.rego +2 -2
- package/rules/test/fix.mjs +5 -1
- package/rules/test/test.mdc +1 -1
- package/rules/text/fix.mjs +5 -1
- package/rules/text/js/formatting/check.mjs +2 -2
- package/rules/text/lint/lint.mjs +9 -2
- package/rules/text/text.mdc +1 -1
- package/rules/vue/fix.mjs +5 -1
- package/rules/vue/js/packages/check.mjs +1 -1
- package/rules/vue/policy/package_json/package_json.rego +1 -1
- package/rules/vue/vue.mdc +1 -1
- package/schemas/n-cursor.json +1 -1
- package/scripts/build-agents-commands.mjs +1 -1
- package/scripts/claude-stop-hook.mjs +1 -1
- package/scripts/utils/discover-check-rules-from-cursor.mjs +1 -1
- package/scripts/utils/read-n-cursor-config-lite.mjs +59 -0
- package/scripts/utils/run-rule-cli.mjs +37 -0
- package/scripts/utils/run-standard-rule.mjs +12 -3
- package/scripts/utils/with-lock.mjs +2 -2
- package/skills/fix/SKILL.md +5 -5
- package/skills/lint/SKILL.md +1 -1
package/rules/js-lint/fix.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
5
6
|
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
7
|
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
8
|
*/
|
|
@@ -10,6 +11,9 @@ export function run(ctx) {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
13
17
|
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
-
process.exit(await
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
15
19
|
}
|
|
@@ -347,9 +347,9 @@ async function checkOxlintRc(passFn, failFn) {
|
|
|
347
347
|
*/
|
|
348
348
|
async function checkLintJsWorkflows(passFn, failFn) {
|
|
349
349
|
if (existsSync('.github/workflows/lint-js.yml')) {
|
|
350
|
-
passFn('.github/workflows/lint-js.yml є (структуру перевіряє npx @nitra/cursor
|
|
350
|
+
passFn('.github/workflows/lint-js.yml є (структуру перевіряє npx @nitra/cursor fix → js_lint.lint_js_yml)')
|
|
351
351
|
} else {
|
|
352
|
-
failFn('.github/workflows/lint-js.yml не існує — створи його (див.
|
|
352
|
+
failFn('.github/workflows/lint-js.yml не існує — створи його (див. rules/js-lint/fix.mjs / js-lint.mdc)')
|
|
353
353
|
}
|
|
354
354
|
|
|
355
355
|
if (existsSync('.github/workflows/lint.yml')) {
|
|
@@ -64,7 +64,7 @@ version: '1.23'
|
|
|
64
64
|
|
|
65
65
|
У корені проєкту має бути **`knip.json`**, який стартує з канонічного baseline з пакета `@nitra/cursor` — файл [`npm/rules/js-lint/js/tooling/knip-canonical.json`](./js/tooling/knip-canonical.json). Він покриває типові false-positives для наших правил: `entry` зі CLI-конфігами (eslint, stylelint, oxlint, jscpd, markdownlint-cli2, `commitlint`), `project` для `**/*.{js,mjs,cjs,jsx,ts,tsx,mts,cts}`, `ignore` для `**/__fixtures__/**`, `ignoreDependencies` для пакетів, посилання на які є лише в не-JS-конфігах (`@nitra/cspell-dict`, `/@cspell\/dict-.+/`), і `ignoreBinaries` для CLI, які канон вимагає викликати через `npx`/`bunx` і яких заборонено додавати в `devDependencies` (`actionlint`, `cspell`, `depcheck`, `eslint`, `git-ai`, `jscpd`, `markdownlint-cli2`, `oxfmt`, `oxlint`, `shellcheck`, `uvx`, `v8r`, `zizmor`).
|
|
66
66
|
|
|
67
|
-
Якщо `knip.json` відсутній — `npx @nitra/cursor
|
|
67
|
+
Якщо `knip.json` відсутній — `npx @nitra/cursor fix js-lint` копіює канон у корінь проєкту (side effect). Після створення модифікуй файл під свій проєкт як завгодно: перевіряємо лише наявність, зміст подальших змін не валідується.
|
|
68
68
|
|
|
69
69
|
Пакет `knip` окремо в `devDependencies` не додавай — `bunx knip` тягне його ad-hoc, як oxlint/eslint/jscpd.
|
|
70
70
|
|
package/rules/js-mssql/fix.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
5
6
|
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
7
|
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
8
|
*/
|
|
@@ -10,6 +11,9 @@ export function run(ctx) {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
13
17
|
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
-
process.exit(await
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
15
19
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Порт перевірки версії `mssql` з `npm/scripts/
|
|
1
|
+
# Порт перевірки версії `mssql` з `npm/scripts/rules/js-mssql/fix.mjs` (js-mssql.mdc).
|
|
2
2
|
#
|
|
3
3
|
# Запуск (локально, для будь-якого `package.json`):
|
|
4
4
|
# conftest test path/to/package.json -p npm/policy/js_mssql \
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
#
|
|
10
10
|
# AST-скан коду на per-request `new sql.ConnectionPool(...)` всередині функцій
|
|
11
11
|
# (потребує парсингу `.js` / `.ts` через oxc-parser), а також full-semver
|
|
12
|
-
# (`major.minor.patch` triple-compare у `
|
|
12
|
+
# (`major.minor.patch` triple-compare у `rules/js-mssql/fix.mjs`) лишаються у JS:
|
|
13
13
|
# JS-перевірка authoritative, ця Rego — швидкий gate для одиничного `package.json`.
|
|
14
14
|
#
|
|
15
15
|
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
package/rules/js-run/fix.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
5
6
|
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
7
|
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
8
|
*/
|
|
@@ -10,6 +11,9 @@ export function run(ctx) {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
13
17
|
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
-
process.exit(await
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
15
19
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* (див. `utils/bunyan-imports.mjs`);
|
|
8
8
|
* - наявність `OTEL_RESOURCE_ATTRIBUTES` зі значеннями `service.name=` та `service.namespace=`
|
|
9
9
|
* у `k8s/base/configmap.yaml`, якщо такий файл існує (відповідність імені ConfigMap імені
|
|
10
|
-
* Deployment перевіряється в `
|
|
10
|
+
* Deployment перевіряється в `rules/k8s/fix.mjs`);
|
|
11
11
|
* - «Внутрішні аліаси» (`#conn/*`): імпорти `bun#SQL`, будь-який `mssql`, `@nitra/graphql-request#GraphQLClient`
|
|
12
12
|
* дозволені лише у каталозі conn (за замовчуванням `src/conn/`; за наявності
|
|
13
13
|
* `package.json#imports['#conn/*']` — у його цільовому каталозі); поза ним — порушення
|
|
@@ -334,7 +334,7 @@ async function checkWorkspacePackage(rootDir, ignorePaths, fail, passFn) {
|
|
|
334
334
|
// Frontend-пакети (vite у devDependencies) виходять за межі js-run:
|
|
335
335
|
// браузерний бандл не має `node:process`, а `process.env.*` бандлер
|
|
336
336
|
// обробляє самостійно. Перевірку process.env / conn-аліасів пропускаємо;
|
|
337
|
-
// bunyan-залежність валідується в Rego (`npx @nitra/cursor
|
|
337
|
+
// bunyan-залежність валідується в Rego (`npx @nitra/cursor fix`).
|
|
338
338
|
if (packageJsonHasViteDevDependency(pkgJson)) {
|
|
339
339
|
passFn(`${label}vite-пакет (frontend) — js-run пропущено (process.env / conn-aliases / OTEL configmap)`)
|
|
340
340
|
return
|
|
@@ -418,7 +418,7 @@ async function loadPackageJson(rootDir) {
|
|
|
418
418
|
function checkOtelConfigmap(rootDir, passFn) {
|
|
419
419
|
const configmapPath = join(rootDir, 'k8s', 'base', 'configmap.yaml')
|
|
420
420
|
if (!existsSync(configmapPath)) return
|
|
421
|
-
passFn(`${rootDir}/k8s/base/configmap.yaml є (OTEL — npx @nitra/cursor
|
|
421
|
+
passFn(`${rootDir}/k8s/base/configmap.yaml є (OTEL — npx @nitra/cursor fix → js_run.configmap)`)
|
|
422
422
|
}
|
|
423
423
|
|
|
424
424
|
/**
|
package/rules/k8s/fix.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
5
6
|
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
7
|
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
8
|
*/
|
|
@@ -10,6 +11,9 @@ export function run(ctx) {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
13
17
|
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
-
process.exit(await
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
15
19
|
}
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
* Явні винятки до загальної логіки yannh/datree — таблиця **`EXPLICIT_K8S_SCHEMAS`** (`Map`): ключ
|
|
80
80
|
* **`apiVersion`, `kind`, `type`** (для CRD без поля `type` у маніфесті — зірочка **`*`** як третій
|
|
81
81
|
* компонент). Спочатку шукається збіг за фактичним `type`, потім за **`*`**.
|
|
82
|
-
* Dockerfile — правило docker.mdc, скрипт
|
|
82
|
+
* Dockerfile — правило docker.mdc, скрипт rules/docker/fix.mjs.
|
|
83
83
|
*
|
|
84
84
|
* **Структура `HTTPRoute` для Hasura-Deployment:** звіряється канон 4 правил у **`spec.rules`** (редиректи **`<prefix>/ql`** і **`<prefix>/ql/`** на **`<prefix>/ql/console`** 302, **`PathPrefix <prefix>/ql`** + **URLRewrite** на **`/`**, окреме WebSocket-правило з **`RequestHeaderModifier`** remove **`Authorization`**). **Префікс параметризовано** (рядок перед **`/ql`** у першому Hasura-правилі). **Прив'язка** — за **`metadata.name`** у тому ж каталозі, що й **Deployment** з образом **`hasura/graphql-engine`** (див. k8s.mdc). **Додаткові правила** поверх канону дозволені.
|
|
85
85
|
*
|
package/rules/k8s/k8s.mdc
CHANGED
|
@@ -17,13 +17,13 @@ alwaysApply: false
|
|
|
17
17
|
|
|
18
18
|
**Modeline — опційний:** якщо для конкретного поєднання `apiVersion`/`kind` **немає** надійної публічної схеми (yannh/datree/schemastore не покривають), залиш файл **без** рядка `# yaml-language-server: $schema=…`. **Заборонено** ставити `$schema=file:…` як заглушку — це створює видимість валідації без неї. Без modeline `check-k8s` не валідує URL, але **`lint-k8s`** (kubeconform/kubescape) продовжить роботу. Якщо modeline присутній — він обов'язково перший рядок і **тільки** `https://` URL, який відповідає очікуваному за `apiVersion`/`kind`.
|
|
19
19
|
|
|
20
|
-
**Виняток — modeline заборонено:** `apiVersion: alb.yc.io/v1alpha1`, `kind: HttpBackendGroup` (Yandex ALB) — рядка **`# yaml-language-server: $schema=…`** у файлі **не** має бути (ні в першому рядку, ні далі). Перший рядок — одразу YAML (`apiVersion:` тощо). Перевірка — **`
|
|
20
|
+
**Виняток — modeline заборонено:** `apiVersion: alb.yc.io/v1alpha1`, `kind: HttpBackendGroup` (Yandex ALB) — рядка **`# yaml-language-server: $schema=…`** у файлі **не** має бути (ні в першому рядку, ні далі). Перший рядок — одразу YAML (`apiVersion:` тощо). Перевірка — **`rules/k8s/fix.mjs`**.
|
|
21
21
|
|
|
22
22
|
**Розширення:** усі маніфести під **`k8s`**, включно з **`kustomization.yaml`**, — лише **`.yaml`** (розширення **`.yml`** не використовуй).
|
|
23
23
|
|
|
24
24
|
**Скоп — поза `.github/`:** правило стосується YAML-маніфестів у каталогах **`k8s`** (визначаються відносно кореня репо, не за абсолютним шляхом). Файли під **`.github/workflows/`** та **`.github/actions/`** ця перевірка **не** зачіпає — їхній скоп визначає **`ga.mdc`** (там канон — **`.yml`**). Це робить два правила несуперечливими навіть у проєктах, де сам корінь репо випадково має ім'я `k8s/` (тоді сегмент `k8s` присутній у абсолютному шляху всіх файлів, але **відносно кореня** його там немає).
|
|
25
25
|
|
|
26
|
-
**Dockerfile / hadolint** — окреме правило **`docker.mdc`** і **`npx @nitra/cursor
|
|
26
|
+
**Dockerfile / hadolint** — окреме правило **`docker.mdc`** і **`npx @nitra/cursor fix docker`**.
|
|
27
27
|
|
|
28
28
|
## lint-k8s: kubeconform і kubescape
|
|
29
29
|
|
|
@@ -31,7 +31,7 @@ alwaysApply: false
|
|
|
31
31
|
|
|
32
32
|
**Залежності:** виконувані файли kubeconform, kubescape і kubectl у **PATH** (kustomize використовуємо як вшиту підкоманду **`kubectl kustomize`** — окремий бінарник `kustomize` не потрібен); не додавай їх у **devDependencies**.
|
|
33
33
|
|
|
34
|
-
**Версія Kubernetes для kubeconform** має відповідати PIN yannh у цьому правилі та в **`
|
|
34
|
+
**Версія Kubernetes для kubeconform** має відповідати PIN yannh у цьому правилі та в **`rules/k8s/fix.mjs`** (зараз **`-kubernetes-version 1.33.9`** — semver без префікса `v`, еквівалент релізу **v1.33.9**; набір схем **`v1.33.9-standalone-strict`**). Для CRD додатково підключай реєстр [datreeio/CRDs-catalog](https://github.com/datreeio/CRDs-catalog) другим **`-schema-location`**, як у [прикладах kubeconform](https://github.com/yannh/kubeconform#readme). За потреби **`-ignore-missing-schemas`**, якщо частина CRD ще без публічної схеми.
|
|
35
35
|
|
|
36
36
|
**kubescape — вхід через зібраний kustomize-маніфест:** для кожного dir-у з `kustomization.yaml` (`kind: Kustomization`; **`kind: Component`** пропускається — він не білдиться окремо) `lint-k8s` виконує **`kubectl kustomize <dir>`** і передає stdout у **`kubescape scan <tmp-file>`** з порогом **`--severity-threshold high`** (вбудована в kubectl підкоманда `kustomize` — окремий бінарник `kustomize` не потрібен; рендеринг локальний і не потребує доступу до кластера). Маніфест проходить через тимчасовий файл, бо **`kubescape scan` у v4.x не читає stdin** (`-` як шлях → `no resources found to scan`; прапорця `--input`/`--stdin` немає); тимчасова директорія створюється під `os.tmpdir()` і прибирається після скану. Збірка через kustomize нормалізує namespace на workload-маніфестах і **NetworkPolicy у `base/networkpolicy.yaml`** (через `base/kustomization.yaml` `namespace:`), що дає коректний матчинг `podSelector` у `C-0260` (`Missing network policy`) і дозволяє kubescape бачити дерево overlays / components зі справжніми ресурсами. Якщо в дереві **`…/k8s`** немає жодного `kustomization.yaml` (проєкт без Kustomize) — fallback на старий dir-скан **`kubescape scan <каталог-k8s>`**. Перший запуск kubescape може завантажувати артефакти — у CI потрібна мережа або [offline](https://github.com/kubescape/kubescape#readme). На відміну від kubeconform, у **kubescape scan** немає прапорця **`-kubernetes-version`**: перевірка йде за **framework/control** (NSA, MITRE, CIS тощо), а не проти OpenAPI-схеми конкретного релізу Kubernetes. **Орієнтир** для репозиторію той самий, що й для kubeconform — кластер **v1.33.9** (див. **`-kubernetes-version 1.33.9`** вище); для CIS і подібних наближень обирай актуальний framework під політику команди (**`kubescape list frameworks`**, див. [CLI reference](https://github.com/kubescape/kubescape/blob/master/docs/cli-reference.md)).
|
|
37
37
|
|
|
@@ -53,7 +53,7 @@ alwaysApply: false
|
|
|
53
53
|
}
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
Якщо правило **`k8s`** підключено в **`.n-cursor.json`** (масив **`rules`**), у **кореневому** `package.json` **мають** бути скрипт **`lint-k8s`** і виклик **`bun run lint-k8s`** у агрегованому **`lint`** (див. **`bun.mdc`**). Це перевіряє **`npx @nitra/cursor
|
|
56
|
+
Якщо правило **`k8s`** підключено в **`.n-cursor.json`** (масив **`rules`**), у **кореневому** `package.json` **мають** бути скрипт **`lint-k8s`** і виклик **`bun run lint-k8s`** у агрегованому **`lint`** (див. **`bun.mdc`**). Це перевіряє **`npx @nitra/cursor fix bun`**.
|
|
57
57
|
|
|
58
58
|
Додай workflow **`.github/workflows/lint-k8s.yml`** (гілки **`dev`** і **`main`**, лише **`.yml`**, узгоджено з **`ga.mdc`**). **Не** дублюй **`setup-node`**, **`oven-sh/setup-bun`**, **`actions/cache`** і **`bun install`** у job — після **`checkout`** використовуй локальний composite **`setup-bun-deps`** (шлях **`./.github/actions/setup-bun-deps`** або **`./npm/github-actions/setup-bun-deps`**, як у репозиторії). Встановлення **kubeconform** / **kubescape** лишаються окремими кроками.
|
|
59
59
|
|
|
@@ -152,7 +152,7 @@ patches:
|
|
|
152
152
|
|
|
153
153
|
Поле **`imagePullPolicy`** скрипт **не** перевіряє (залишається політиці Kubernetes за тегом образу).
|
|
154
154
|
|
|
155
|
-
Образ **`hasura/graphql-engine`**: дозволений лише канонічний тег із константи **`HASURA_GRAPHQL_ENGINE_IMAGE`** у **`
|
|
155
|
+
Образ **`hasura/graphql-engine`**: дозволений лише канонічний тег із константи **`HASURA_GRAPHQL_ENGINE_IMAGE`** у **`rules/k8s/fix.mjs`** (допускається префікс **`docker.io/`**); решта — помилка **check k8s**.
|
|
156
156
|
|
|
157
157
|
### HTTPRoute для Deployment з `hasura/graphql-engine`
|
|
158
158
|
|
|
@@ -254,7 +254,7 @@ spec:
|
|
|
254
254
|
|
|
255
255
|
Пара файлів для кластерного й headless **Service**: **`svc.yaml`** + **`svc-hl.yaml`** в одному каталозі, **`spec.type: ClusterIP`** / **`clusterIP: None`**, імена **`-hl`**, узгодженість пар, маршрути **`gateway.networking.k8s.io`** (**HTTPRoute**, **GRPCRoute**, **TCPRoute**, **TLSRoute**, **UDPRoute**) — **backendRef** лише на сервіси з суфіксом **`-hl`**; якщо **kustomization** посилається на **`svc.yaml`**, у **тому ж** **`kustomization.yaml`** має бути посилання на sibling **`svc-hl.yaml`**. Скрипт **не** створює файли — додай **`svc-hl.yaml`** вручну (копія з правками **name** / **clusterIP**).
|
|
256
256
|
|
|
257
|
-
**Точні умови та повідомлення `fail`** — верхній JSDoc **`npm/scripts/
|
|
257
|
+
**Точні умови та повідомлення `fail`** — верхній JSDoc **`npm/scripts/rules/k8s/fix.mjs`**.
|
|
258
258
|
|
|
259
259
|
### Gateway API: не дублюй namespace у `backendRef`
|
|
260
260
|
|
|
@@ -293,11 +293,11 @@ spec:
|
|
|
293
293
|
|
|
294
294
|
## ConfigMap: ім'я збігається з Deployment
|
|
295
295
|
|
|
296
|
-
Якщо в `k8s/base/` є **`configmap.yaml`** і **Deployment**, і цей Deployment посилається рівно на **один** ConfigMap — `metadata.name` ConfigMap має збігатися з `metadata.name` Deployment. Точні умови перевірки — **`
|
|
296
|
+
Якщо в `k8s/base/` є **`configmap.yaml`** і **Deployment**, і цей Deployment посилається рівно на **один** ConfigMap — `metadata.name` ConfigMap має збігатися з `metadata.name` Deployment. Точні умови перевірки — **`rules/k8s/fix.mjs`**.
|
|
297
297
|
|
|
298
298
|
## ConfigMap для Hasura-Deployment
|
|
299
299
|
|
|
300
|
-
Якщо в `k8s/base/` поруч із **`configmap.yaml`** є **Deployment** з образом **`hasura/graphql-engine`**, у `data` ConfigMap **обов'язково** має бути ключ **`HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS`** зі значенням **`"true"`**. Точні умови перевірки — **`
|
|
300
|
+
Якщо в `k8s/base/` поруч із **`configmap.yaml`** є **Deployment** з образом **`hasura/graphql-engine`**, у `data` ConfigMap **обов'язково** має бути ключ **`HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS`** зі значенням **`"true"`**. Точні умови перевірки — **`rules/k8s/fix.mjs`**.
|
|
301
301
|
|
|
302
302
|
```yaml
|
|
303
303
|
data:
|
|
@@ -323,7 +323,7 @@ data:
|
|
|
323
323
|
|
|
324
324
|
- **`base/kustomization.yaml`:** поле **`namespace:`** має бути **непорожнім** (перевіряє **check k8s**, якщо файл є).
|
|
325
325
|
|
|
326
|
-
- **Коли `metadata.namespace` обов’язковий у файлі:** YAML під **`k8s`** — непорожній **`metadata.namespace`** для namespaced **kind** (винятки — кластерні **kind**, перелік **`CLUSTER_SCOPED_KINDS`** у **`
|
|
326
|
+
- **Коли `metadata.namespace` обов’язковий у файлі:** YAML під **`k8s`** — непорожній **`metadata.namespace`** для namespaced **kind** (винятки — кластерні **kind**, перелік **`CLUSTER_SCOPED_KINDS`** у **`rules/k8s/fix.mjs`**). У overlays Kustomize значення в маніфесті буде перезаписано полем **`namespace:`** з відповідного **`kustomization.yaml`**, тому в `base` пиши канонічний dev-namespace.
|
|
327
327
|
|
|
328
328
|
- **Не додавай** окремі **patches** Kustomize, які лише змінюють **namespace**: **namespace** визначає Kustomize; у overlays додаткові зміни — без дублювання логіки **namespace**.
|
|
329
329
|
|
|
@@ -408,11 +408,11 @@ images:
|
|
|
408
408
|
- **`pdb.yaml`** — `policy/v1`, `PodDisruptionBudget`, `spec.selector.matchLabels.app` **= `spec.selector.matchLabels.app`** Deployment.
|
|
409
409
|
- **`topologySpreadConstraints`** — запис з `maxSkew: 1`, `topologyKey: kubernetes.io/hostname`, `whenUnsatisfiable: ScheduleAnyway`, `labelSelector.matchLabels.app` рівне тій самій мітці `app`.
|
|
410
410
|
|
|
411
|
-
**Перевірка структури `components/`** (для кожного Deployment у `base/`): наявність каталогу, валідний `kustomization.yaml` як Component, `hpa.yaml` і `pdb.yaml` з відповідністю до Deployment-name / app-label. Алгоритм — функція `validateComponentsForBaseDeployment` у **`
|
|
411
|
+
**Перевірка структури `components/`** (для кожного Deployment у `base/`): наявність каталогу, валідний `kustomization.yaml` як Component, `hpa.yaml` і `pdb.yaml` з відповідністю до Deployment-name / app-label. Алгоритм — функція `validateComponentsForBaseDeployment` у **`rules/k8s/fix.mjs`**.
|
|
412
412
|
|
|
413
413
|
**Локальні шляхи в `kustomization.yaml`:** кожен запис без `://` (remote) з `resources` / `bases` / `components` / `crds`, `patchesStrategicMerge`, `patches[].path`, `patchesJson6902[].path`, `configurations[]`, `replacements[].path` має вказувати на **існуючий** у репозиторії файл (`.yaml` / `.yml`) або **каталог**; биті посилання — помилка **`check k8s`**.
|
|
414
414
|
|
|
415
|
-
**Порядок `resources`:** у маніфесті з **`apiVersion: kustomize.config.k8s.io/…`**, **`kind: Kustomization`**, елементи **`resources:`** (лише непорожні рядки) мають бути **відсортовані за алфавітом (англ. локаль, як `localeCompare('en')` у `
|
|
415
|
+
**Порядок `resources`:** у маніфесті з **`apiVersion: kustomize.config.k8s.io/…`**, **`kind: Kustomization`**, елементи **`resources:`** (лише непорожні рядки) мають бути **відсортовані за алфавітом (англ. локаль, як `localeCompare('en')` у `rules/k8s/fix.mjs`)**. Поля **`bases`**, **`components`**, **`crds`** цією перевіркою **не** впорядковуються.
|
|
416
416
|
|
|
417
417
|
### Env-залежні межі (за сегментом після `/k8s/`)
|
|
418
418
|
|
|
@@ -617,7 +617,7 @@ spec:
|
|
|
617
617
|
app: backend-api
|
|
618
618
|
```
|
|
619
619
|
|
|
620
|
-
Точні умови та повідомлення `fail` — JSDoc на початку `npm/scripts/
|
|
620
|
+
Точні умови та повідомлення `fail` — JSDoc на початку `npm/scripts/rules/k8s/fix.mjs` і функції `validateComponentsForBaseDeployment` / `prodOverlayHpaPdbOverrideNeeds`.
|
|
621
621
|
|
|
622
622
|
## HorizontalPodAutoscaler: `autoscaling/v2`
|
|
623
623
|
|
package/rules/k8s/lint/lint.mjs
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* Запуск kubeconform та kubescape для каталогів `…/k8s`, де є YAML-маніфести (див. k8s.mdc).
|
|
3
3
|
*
|
|
4
4
|
* Знаходить унікальні корені каталогів із іменем `k8s` за шляхами файлів **`*.yaml`**
|
|
5
|
-
* (той самий принцип сегмента `k8s`, що й у
|
|
5
|
+
* (той самий принцип сегмента `k8s`, що й у rules/k8s/fix.mjs; розширення **`.yml`** під `k8s` не використовується). Якщо таких файлів немає — вихід 0
|
|
6
6
|
* без виклику зовнішніх CLI.
|
|
7
7
|
*
|
|
8
8
|
* kubeconform перевіряє маніфести проти OpenAPI-схем Kubernetes; kubescape — сканування на
|
|
9
9
|
* misconfiguration / compliance (NSA, MITRE, CIS тощо). Обидві утиліти очікуються в PATH
|
|
10
10
|
* (локально: Homebrew, релізи GitHub; у CI — крок установки з k8s.mdc).
|
|
11
11
|
*
|
|
12
|
-
* Версія `-kubernetes-version` для kubeconform узгоджена з PIN yannh у
|
|
12
|
+
* Версія `-kubernetes-version` для kubeconform узгоджена з PIN yannh у rules/k8s/fix.mjs / k8s.mdc.
|
|
13
13
|
* Kubescape не має аналога цього прапорця; орієнтир цільового кластера — та сама лінія релізу (див. k8s.mdc).
|
|
14
14
|
*/
|
|
15
15
|
import { spawnSync } from 'node:child_process'
|
|
@@ -24,6 +24,7 @@ import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
|
|
|
24
24
|
import { loadCursorIgnorePaths } from '../../../scripts/utils/load-cursor-config.mjs'
|
|
25
25
|
import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
|
|
26
26
|
import { walkDir } from '../../../scripts/utils/walkDir.mjs'
|
|
27
|
+
import { withLock } from '../../../scripts/utils/with-lock.mjs'
|
|
27
28
|
|
|
28
29
|
/** Per-project kubescape exceptions file; підмішується через --exceptions, якщо існує в корені. */
|
|
29
30
|
const KUBESCAPE_EXCEPTIONS_FILE = '.kubescape-exceptions.json'
|
|
@@ -320,11 +321,10 @@ async function runKubescape(dirs, root) {
|
|
|
320
321
|
}
|
|
321
322
|
|
|
322
323
|
/**
|
|
323
|
-
*
|
|
324
|
-
* Експортовано як `runLintK8s` — використовується з `bin/n-cursor.js` як підкоманда `lint-k8s`.
|
|
324
|
+
* Внутрішні кроки `lint-k8s` без локу: kubeconform + kubescape для усіх знайдених дерев `k8s`.
|
|
325
325
|
* @returns {Promise<number>} код виходу для `process.exitCode` (0 — успіх або пропуск)
|
|
326
326
|
*/
|
|
327
|
-
|
|
327
|
+
async function runLintK8sSteps() {
|
|
328
328
|
const root = process.cwd()
|
|
329
329
|
const ignorePaths = await loadCursorIgnorePaths(root)
|
|
330
330
|
const dirs = await findK8sRoots(root, ignorePaths)
|
|
@@ -344,6 +344,13 @@ export async function runLintK8s() {
|
|
|
344
344
|
return ks
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
+
/**
|
|
348
|
+
* Публічна CLI-форма: серіалізує через `withLock('lint-k8s')` + дедуп за станом git-дерева.
|
|
349
|
+
* Експортовано як `runLintK8s` — використовується з `bin/n-cursor.js` як підкоманда `lint-k8s`.
|
|
350
|
+
* @returns {Promise<number>} код виходу
|
|
351
|
+
*/
|
|
352
|
+
export const runLintK8s = () => withLock('lint-k8s', runLintK8sSteps)
|
|
353
|
+
|
|
347
354
|
if (isRunAsCli()) {
|
|
348
355
|
process.exitCode = await runLintK8s()
|
|
349
356
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Порт перевірки `k8s/base/kustomization.yaml` з `npm/scripts/
|
|
1
|
+
# Порт перевірки `k8s/base/kustomization.yaml` з `npm/scripts/rules/k8s/fix.mjs`
|
|
2
2
|
# (k8s.mdc): у base-kustomization обов'язково має бути непорожнє поле
|
|
3
3
|
# `namespace:`.
|
|
4
4
|
#
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# -p npm/policy/k8s/base_kustomization \
|
|
8
8
|
# --namespace k8s.base_kustomization
|
|
9
9
|
#
|
|
10
|
-
# JS authoritative (`
|
|
10
|
+
# JS authoritative (`rules/k8s/fix.mjs`: `baseKustomizationNamespaceViolation`,
|
|
11
11
|
# `isBaseKustomizationPath` для відбору файла, `ensureBaseKustomizationHasNamespace`
|
|
12
12
|
# як оркестратор).
|
|
13
13
|
#
|
|
@@ -39,7 +39,7 @@ deny contains base_namespace_required_msg if {
|
|
|
39
39
|
# на *локальний* `resources:` base/kustomization.yaml (точне ім'я `hpa.yaml`/`pdb.yaml`,
|
|
40
40
|
# у будь-якому підкаталозі). Рекурсивний обхід `resources:`/`components:`/`bases:`
|
|
41
41
|
# (із зануренням у вкладені kustomization.yaml) — JS-оркестратор
|
|
42
|
-
# `verifyK8sBaseKustomizeHasNoHpaPdb` у `
|
|
42
|
+
# `verifyK8sBaseKustomizeHasNoHpaPdb` у `rules/k8s/fix.mjs` (потребує fs-доступу). Цей
|
|
43
43
|
# rego-deny — defense-in-depth: спрацює навіть якщо JS-крок упаде з винятку раніше.
|
|
44
44
|
#
|
|
45
45
|
# NetworkPolicy у base — навпаки, обов'язковий (k8s.mdc): обмеження мережі мають
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
#
|
|
8
8
|
# JS відбирає файли під `…/k8s/.../base/…` (окрім `kustomization.yaml`) і
|
|
9
9
|
# викликає conftest з цією намеспейс. JS authoritative
|
|
10
|
-
# (`
|
|
10
|
+
# (`rules/k8s/fix.mjs`: `metadataNamespaceRequiredViolation` з `inBaseDir=true`,
|
|
11
11
|
# `deploymentResourcesViolation` з `inK8sBaseLayer=true`,
|
|
12
12
|
# `isK8sBaseManifestYamlPath`).
|
|
13
13
|
#
|
|
@@ -25,7 +25,7 @@ package k8s.base_manifest
|
|
|
25
25
|
import rego.v1
|
|
26
26
|
|
|
27
27
|
# Cluster-scoped kind (не вимагають metadata.namespace) — узгоджено з
|
|
28
|
-
# `CLUSTER_SCOPED_KINDS` у `
|
|
28
|
+
# `CLUSTER_SCOPED_KINDS` у `rules/k8s/fix.mjs`.
|
|
29
29
|
cluster_scoped_kinds := {
|
|
30
30
|
"APIService",
|
|
31
31
|
"CertificateSigningRequest",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Порт пер-документних перевірок для Gateway API і HealthCheckPolicy з
|
|
2
|
-
# `npm/scripts/
|
|
2
|
+
# `npm/scripts/rules/k8s/fix.mjs` (k8s.mdc).
|
|
3
3
|
#
|
|
4
4
|
# Запуск (локально, по одному файлу або по дереву):
|
|
5
5
|
# conftest test path/to/manifest.yaml -p npm/policy/k8s/gateway \
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# `metadata.namespace` маршруту, — заборонено (надлишкове поле, ламається при
|
|
15
15
|
# overlay-перенесеннях).
|
|
16
16
|
#
|
|
17
|
-
# JS authoritative (`
|
|
17
|
+
# JS authoritative (`rules/k8s/fix.mjs` — функції `failIfGatewayRouteUsesNonHeadlessService`,
|
|
18
18
|
# `healthCheckPolicyTargetRefHeadlessServiceViolation`,
|
|
19
19
|
# `collectGatewayApiRouteBackendServiceNames`,
|
|
20
20
|
# `collectGatewayApiRouteBackendRefsWithRedundantNamespace`); ця Rego — швидкий
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Порт перевірки ConfigMap для Hasura-Deployment з
|
|
2
|
-
# `npm/scripts/
|
|
2
|
+
# `npm/scripts/rules/k8s/fix.mjs` (k8s.mdc): у ConfigMap, що сусідствує з
|
|
3
3
|
# Hasura-Deployment, у `data` обов'язково має бути ключ
|
|
4
4
|
# `HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS` зі значенням `"true"`.
|
|
5
5
|
#
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# -p npm/policy/k8s/hasura_configmap \
|
|
9
9
|
# --namespace k8s.hasura_configmap
|
|
10
10
|
#
|
|
11
|
-
# Прив'язка ConfigMap-Deployment cross-file — у JS (`
|
|
11
|
+
# Прив'язка ConfigMap-Deployment cross-file — у JS (`rules/k8s/fix.mjs`:
|
|
12
12
|
# `validateHasuraConfigMapRemoteSchemaPermissions` шукає Hasura-Deployment
|
|
13
13
|
# у тому ж dir-у і викликає conftest з цією намеспейс лише для відповідних
|
|
14
14
|
# ConfigMap-ів). JS authoritative (`hasuraConfigMapRemoteSchemaPermissionsViolation`,
|
|
@@ -20,7 +20,7 @@ package k8s.hasura_configmap
|
|
|
20
20
|
|
|
21
21
|
import rego.v1
|
|
22
22
|
|
|
23
|
-
# Обов'язковий ключ у `data` (узгоджено з `
|
|
23
|
+
# Обов'язковий ключ у `data` (узгоджено з `rules/k8s/fix.mjs`).
|
|
24
24
|
required_key := "HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS"
|
|
25
25
|
|
|
26
26
|
key_missing_template := concat(" ", [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Порт перевірки `httpRouteHasuraCanonViolation` з `npm/scripts/
|
|
1
|
+
# Порт перевірки `httpRouteHasuraCanonViolation` з `npm/scripts/rules/k8s/fix.mjs`
|
|
2
2
|
# (k8s.mdc): HTTPRoute, що сусідствує з Hasura-Deployment з тим самим
|
|
3
3
|
# `metadata.name`, має містити канон з 4 правил у такому порядку:
|
|
4
4
|
#
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Порт **структурних** пер-документних перевірок HPA та PDB з
|
|
2
|
-
# `npm/scripts/
|
|
2
|
+
# `npm/scripts/rules/k8s/fix.mjs` (k8s.mdc). Перевіряє лише ті властивості, що
|
|
3
3
|
# не залежать від cross-file контексту (`expectedDeployName`, `expectedAppLabel`,
|
|
4
4
|
# `isDevLike`-сегмента). Cross-file перевірки лишаються в JS
|
|
5
5
|
# (`hpaManifestViolations`, `pdbManifestViolations`,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Порт пер-документних структурних перевірок `kustomization.yaml` з
|
|
2
|
-
# `npm/scripts/
|
|
2
|
+
# `npm/scripts/rules/k8s/fix.mjs` (k8s.mdc).
|
|
3
3
|
#
|
|
4
4
|
# Запуск (локально, на одному kustomization.yaml):
|
|
5
5
|
# conftest test path/to/kustomization.yaml -p npm/policy/k8s/kustomization \
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
#
|
|
15
15
|
# JS authoritative: повна резолюція kustomize-дерева, перевірка існування
|
|
16
16
|
# refs на диску, парність `svc.yaml`/`svc-hl.yaml`, вибір conftest-цілей за
|
|
17
|
-
# patternом `kustomization.yaml` — у `
|
|
17
|
+
# patternом `kustomization.yaml` — у `rules/k8s/fix.mjs`.
|
|
18
18
|
#
|
|
19
19
|
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
20
20
|
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Порт пер-документних структурних перевірок з `npm/scripts/
|
|
1
|
+
# Порт пер-документних структурних перевірок з `npm/scripts/rules/k8s/fix.mjs`
|
|
2
2
|
# (k8s.mdc). Цей пакет описує лише ті правила, що дивляться на ОДИН манифест
|
|
3
3
|
# (один YAML-документ): conftest за замовчуванням розрізає файли по `---` і
|
|
4
4
|
# запускає policy на кожен документ окремо.
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
# CROSS-FILE логіка (Kustomize-резолюція ресурсів, парність svc.yaml/svc-hl.yaml,
|
|
25
25
|
# HPA/PDB/topologySpreadConstraints за каталогом, BackendConfig-сепарація,
|
|
26
26
|
# yaml-language-server schema modeline, namespace-перевірки за деревом
|
|
27
|
-
# `…/k8s/base/`) лишається у `
|
|
28
|
-
# JS authoritative (`
|
|
27
|
+
# `…/k8s/base/`) лишається у `rules/k8s/fix.mjs`: вона потребує файлової системи.
|
|
28
|
+
# JS authoritative (`rules/k8s/fix.mjs` робить ці ж пер-документні перевірки в ширшому
|
|
29
29
|
# контексті); ця Rego — швидкий gate для одиничного маніфеста.
|
|
30
30
|
#
|
|
31
31
|
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
@@ -45,7 +45,7 @@ forbidden_service_annotations := {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
# Дозволені посилання на образ `hasura/graphql-engine` (узгоджено з
|
|
48
|
-
# `HASURA_GRAPHQL_ENGINE_IMAGE` у `
|
|
48
|
+
# `HASURA_GRAPHQL_ENGINE_IMAGE` у `rules/k8s/fix.mjs`). Зараз — один канонічний тег
|
|
49
49
|
# у двох варіантах префіксу (із `docker.io/` і без). Digest (`@sha256:…`)
|
|
50
50
|
# відрізається перед звіркою.
|
|
51
51
|
allowed_hasura_images := {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Порт пер-документної структурної перевірки `svc-hl.yaml` з
|
|
2
|
-
# `npm/scripts/
|
|
2
|
+
# `npm/scripts/rules/k8s/fix.mjs` (k8s.mdc): headless Service з суфіксом
|
|
3
3
|
# `metadata.name` `-hl` і `spec.clusterIP: None`.
|
|
4
4
|
#
|
|
5
5
|
# Запуск (локально, лише для одного svc-hl.yaml):
|
|
6
6
|
# conftest test path/to/k8s/.../svc-hl.yaml -p npm/policy/k8s/svc_hl_yaml \
|
|
7
7
|
# --namespace k8s.svc_hl_yaml
|
|
8
8
|
#
|
|
9
|
-
# JS authoritative (`
|
|
9
|
+
# JS authoritative (`rules/k8s/fix.mjs`: `serviceSvcHlYamlHeadlessViolation`,
|
|
10
10
|
# вибір файла `svc-hl.yaml` через walk).
|
|
11
11
|
#
|
|
12
12
|
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Порт пер-документної структурної перевірки `svc.yaml` з
|
|
2
|
-
# `npm/scripts/
|
|
2
|
+
# `npm/scripts/rules/k8s/fix.mjs` (k8s.mdc): `Service` у файлі `svc.yaml` має
|
|
3
3
|
# мати `spec.type: ClusterIP`.
|
|
4
4
|
#
|
|
5
5
|
# Запуск (локально, лише для одного svc.yaml):
|
|
6
6
|
# conftest test path/to/k8s/.../svc.yaml -p npm/policy/k8s/svc_yaml \
|
|
7
7
|
# --namespace k8s.svc_yaml
|
|
8
8
|
#
|
|
9
|
-
# JS authoritative (`
|
|
9
|
+
# JS authoritative (`rules/k8s/fix.mjs`: `serviceSvcYamlClusterIpTypeViolation`,
|
|
10
10
|
# вибір файла `svc.yaml` через walk). Цю Rego JS викликає окремою таргет-командою
|
|
11
11
|
# лише для basename == `svc.yaml`.
|
|
12
12
|
#
|
|
@@ -2,6 +2,7 @@ import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
5
6
|
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
7
|
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
8
|
*/
|
|
@@ -10,6 +11,9 @@ export function run(ctx) {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
13
17
|
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
-
process.exit(await
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
15
19
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Перевірка `.vscode/extensions.json` для nginx-default-tpl (nginx-default-tpl.mdc).
|
|
2
2
|
#
|
|
3
|
-
# Викликається з `
|
|
3
|
+
# Викликається з `rules/nginx-default-tpl/fix.mjs` через `runConftestBatch` лише
|
|
4
4
|
# ПІСЛЯ того, як JS виявив `default.conf.template` у дереві (умовне правило).
|
|
5
|
-
# Глобально без `target.json` поруч (не auto-discoverable через `n-cursor
|
|
5
|
+
# Глобально без `target.json` поруч (не auto-discoverable через `n-cursor fix`).
|
|
6
6
|
#
|
|
7
7
|
# Canonical: `recommendations` має містити `ahmadalli.vscode-nginx-conf`.
|
|
8
8
|
package nginx_default_tpl.vscode_extensions
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Перевірка `.vscode/settings.json` для nginx-default-tpl (nginx-default-tpl.mdc).
|
|
2
2
|
#
|
|
3
|
-
# Викликається з `
|
|
3
|
+
# Викликається з `rules/nginx-default-tpl/fix.mjs` через `runConftestBatch` лише
|
|
4
4
|
# ПІСЛЯ того, як JS виявив `default.conf.template`. Без `target.json` поруч
|
|
5
5
|
# не реєструється.
|
|
6
6
|
#
|
package/rules/npm-module/fix.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
5
6
|
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
7
|
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
8
|
*/
|
|
@@ -10,6 +11,9 @@ export function run(ctx) {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
13
17
|
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
-
process.exit(await
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
15
19
|
}
|
|
@@ -204,7 +204,7 @@ function checkEmitTypesConfig(passFn, failFn) {
|
|
|
204
204
|
)
|
|
205
205
|
return
|
|
206
206
|
}
|
|
207
|
-
passFn(`${EMIT_TYPES_CONFIG} є (структуру перевіряє npx @nitra/cursor
|
|
207
|
+
passFn(`${EMIT_TYPES_CONFIG} є (структуру перевіряє npx @nitra/cursor fix → npm_module.emit_types_config)`)
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
/**
|
|
@@ -338,7 +338,7 @@ async function checkDirtyNpmRequiresVersionBump(passFn, failFn) {
|
|
|
338
338
|
function checkPublishWorkflow(passFn, failFn) {
|
|
339
339
|
const publishWf = '.github/workflows/npm-publish.yml'
|
|
340
340
|
if (existsSync(publishWf)) {
|
|
341
|
-
passFn(`${publishWf} є (структуру перевіряє npx @nitra/cursor
|
|
341
|
+
passFn(`${publishWf} є (структуру перевіряє npx @nitra/cursor fix → npm_module.npm_publish_yml)`)
|
|
342
342
|
} else {
|
|
343
343
|
failFn(`Відсутній ${publishWf} (npm-module.mdc: npm publish)`)
|
|
344
344
|
}
|
package/rules/php/fix.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
5
6
|
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
6
7
|
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
7
8
|
*/
|
|
@@ -10,6 +11,9 @@ export function run(ctx) {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
13
17
|
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
14
|
-
process.exit(await
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
15
19
|
}
|