@nitra/cursor 12.6.0 → 12.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. package/.claude-template/settings.template.json +1 -1
  2. package/CHANGELOG.md +16 -0
  3. package/bin/docs/n-cursor.md +4 -20
  4. package/bin/n-cursor.js +7 -53
  5. package/docs/stryker.config.md +20 -28
  6. package/package.json +1 -1
  7. package/rules/abie/docs/index.md +1 -0
  8. package/rules/abie/docs/main.md +29 -0
  9. package/rules/abie/{fix.mjs → main.mjs} +5 -3
  10. package/rules/adr/docs/index.md +1 -0
  11. package/rules/adr/docs/main.md +29 -0
  12. package/rules/adr/{fix.mjs → main.mjs} +5 -3
  13. package/rules/bun/docs/index.md +1 -0
  14. package/rules/bun/docs/main.md +30 -0
  15. package/rules/bun/js/docs/layout.md +11 -36
  16. package/rules/bun/{fix.mjs → main.mjs} +5 -3
  17. package/rules/capacitor/docs/index.md +1 -0
  18. package/rules/capacitor/docs/main.md +29 -0
  19. package/rules/capacitor/{fix.mjs → main.mjs} +5 -3
  20. package/rules/changelog/docs/index.md +1 -0
  21. package/rules/changelog/docs/main.md +27 -0
  22. package/rules/changelog/main.mjs +20 -0
  23. package/rules/ci4/docs/index.md +1 -0
  24. package/rules/ci4/docs/main.md +30 -0
  25. package/rules/ci4/main.mjs +20 -0
  26. package/rules/doc-files/docs/index.md +1 -0
  27. package/rules/doc-files/docs/main.md +31 -0
  28. package/rules/doc-files/js/docgen-files-batch.mjs +47 -3
  29. package/rules/doc-files/js/docgen-scan.mjs +89 -9
  30. package/rules/doc-files/js/docs/docgen-files-batch.md +15 -15
  31. package/rules/doc-files/js/docs/docgen-scan.md +34 -34
  32. package/rules/doc-files/js/docs/index.md +1 -0
  33. package/rules/doc-files/js/docs/run-lint.md +27 -0
  34. package/rules/doc-files/{lint/lint.mjs → js/run-lint.mjs} +23 -9
  35. package/rules/doc-files/{js/lint.mjs → main.mjs} +60 -10
  36. package/rules/docker/docs/index.md +1 -0
  37. package/rules/docker/docs/main.md +28 -0
  38. package/rules/docker/js/docs/lint.md +26 -54
  39. package/rules/docker/js/lint.mjs +11 -0
  40. package/rules/docker/lib/docker-hadolint.mjs +1 -1
  41. package/rules/docker/lib/docs/docker-hadolint.md +16 -173
  42. package/rules/docker/main.mjs +20 -0
  43. package/rules/efes/docs/index.md +1 -0
  44. package/rules/efes/docs/main.md +29 -0
  45. package/rules/efes/main.mjs +20 -0
  46. package/rules/feedback/docs/index.md +1 -0
  47. package/rules/feedback/docs/main.md +30 -0
  48. package/rules/feedback/main.mjs +20 -0
  49. package/rules/ga/docs/index.md +1 -0
  50. package/rules/ga/docs/main.md +29 -0
  51. package/rules/ga/{lint/lint.mjs → main.mjs} +36 -10
  52. package/rules/graphql/docs/index.md +1 -0
  53. package/rules/graphql/docs/main.md +36 -0
  54. package/rules/graphql/main.mjs +20 -0
  55. package/rules/hasura/docs/index.md +1 -0
  56. package/rules/hasura/docs/main.md +30 -0
  57. package/rules/hasura/main.mjs +20 -0
  58. package/rules/image-avif/docs/index.md +1 -0
  59. package/rules/image-avif/docs/main.md +30 -0
  60. package/rules/image-avif/js/docs/avif_generation.md +20 -233
  61. package/rules/image-avif/main.mjs +20 -0
  62. package/rules/image-compress/docs/index.md +1 -0
  63. package/rules/image-compress/docs/main.md +29 -0
  64. package/rules/image-compress/js/docs/package_setup.md +12 -11
  65. package/rules/image-compress/{js/lint.mjs → main.mjs} +21 -5
  66. package/rules/js-bun-db/docs/index.md +1 -0
  67. package/rules/js-bun-db/docs/main.md +30 -0
  68. package/rules/js-bun-db/main.mjs +20 -0
  69. package/rules/js-bun-redis/docs/index.md +1 -0
  70. package/rules/js-bun-redis/docs/main.md +29 -0
  71. package/rules/js-bun-redis/main.mjs +20 -0
  72. package/rules/js-lint/docs/index.md +1 -0
  73. package/rules/js-lint/docs/main.md +29 -0
  74. package/rules/js-lint/js/check.mjs +268 -0
  75. package/rules/js-lint/js/docs/check.md +39 -0
  76. package/rules/js-lint/js/docs/index.md +1 -1
  77. package/rules/js-lint/js/docs/tooling.md +12 -32
  78. package/rules/js-lint/js/tooling.mjs +1 -265
  79. package/rules/js-lint/{js/lint.mjs → main.mjs} +19 -2
  80. package/rules/js-lint-ci/docs/index.md +1 -0
  81. package/rules/js-lint-ci/docs/main.md +27 -0
  82. package/rules/js-lint-ci/main.mjs +33 -0
  83. package/rules/js-mssql/docs/index.md +1 -0
  84. package/rules/js-mssql/docs/main.md +30 -0
  85. package/rules/js-mssql/main.mjs +20 -0
  86. package/rules/js-run/docs/index.md +1 -0
  87. package/rules/js-run/docs/main.md +30 -0
  88. package/rules/js-run/main.mjs +20 -0
  89. package/rules/k8s/docs/index.md +1 -0
  90. package/rules/k8s/docs/main.md +40 -0
  91. package/rules/k8s/js/docs/index.md +12 -0
  92. package/rules/k8s/{lint/lint.mjs → main.mjs} +32 -10
  93. package/rules/nginx-default-tpl/docs/index.md +1 -0
  94. package/rules/nginx-default-tpl/docs/main.md +30 -0
  95. package/rules/nginx-default-tpl/main.mjs +20 -0
  96. package/rules/npm-module/docs/index.md +1 -0
  97. package/rules/npm-module/docs/main.md +29 -0
  98. package/rules/npm-module/js/docs/rule_meta.md +17 -16
  99. package/rules/npm-module/js/rule_meta.mjs +13 -3
  100. package/rules/npm-module/main.mjs +20 -0
  101. package/rules/php/docs/index.md +1 -0
  102. package/rules/php/docs/main.md +33 -0
  103. package/rules/php/js/docs/tooling.md +10 -10
  104. package/rules/php/{lint/lint.mjs → main.mjs} +32 -6
  105. package/rules/python/docs/index.md +1 -0
  106. package/rules/python/docs/main.md +31 -0
  107. package/rules/python/js/docs/tooling.md +17 -17
  108. package/rules/python/{lint/lint.mjs → main.mjs} +29 -5
  109. package/rules/rego/docs/index.md +1 -0
  110. package/rules/rego/docs/main.md +37 -0
  111. package/rules/rego/{lint/lint.mjs → main.mjs} +27 -5
  112. package/rules/release/docs/index.md +1 -0
  113. package/rules/release/docs/main.md +29 -0
  114. package/rules/release/docs/release.md +0 -3
  115. package/rules/release/release.mdc +10 -0
  116. package/rules/rust/docs/index.md +1 -0
  117. package/rules/rust/docs/main.md +27 -0
  118. package/rules/rust/{js/lint.mjs → main.mjs} +20 -3
  119. package/rules/security/docs/index.md +1 -0
  120. package/rules/security/docs/main.md +28 -0
  121. package/rules/security/main.mjs +45 -0
  122. package/rules/style-lint/docs/index.md +1 -0
  123. package/rules/style-lint/docs/main.md +29 -0
  124. package/rules/style-lint/{js/lint.mjs → main.mjs} +19 -1
  125. package/rules/tauri/docs/index.md +1 -0
  126. package/rules/tauri/docs/main.md +29 -0
  127. package/rules/tauri/main.mjs +20 -0
  128. package/rules/test/docs/index.md +1 -0
  129. package/rules/test/docs/main.md +30 -0
  130. package/rules/test/main.mjs +20 -0
  131. package/rules/text/docs/index.md +1 -0
  132. package/rules/text/docs/main.md +29 -0
  133. package/rules/text/js/docs/cspell-fix.md +30 -0
  134. package/rules/text/js/docs/formatting.md +12 -45
  135. package/rules/text/js/docs/index.md +4 -0
  136. package/rules/text/js/docs/run-dotenv-linter.md +31 -0
  137. package/rules/text/js/docs/run-shellcheck.md +28 -0
  138. package/rules/text/js/docs/run-v8r.md +29 -0
  139. package/rules/text/{lint/lint.mjs → main.mjs} +38 -9
  140. package/rules/text/text.mdc +5 -14
  141. package/rules/tool-surface/docs/index.md +1 -0
  142. package/rules/tool-surface/docs/main.md +29 -0
  143. package/rules/tool-surface/main.mjs +20 -0
  144. package/rules/vue/docs/index.md +1 -0
  145. package/rules/vue/docs/main.md +29 -0
  146. package/rules/vue/main.mjs +20 -0
  147. package/rules/worktree/docs/index.md +1 -0
  148. package/rules/worktree/docs/main.md +28 -0
  149. package/rules/worktree/main.mjs +20 -0
  150. package/scripts/docs/index.md +1 -0
  151. package/scripts/docs/post-tool-use-check.md +29 -0
  152. package/scripts/docs/sync-claude-config.md +64 -92
  153. package/scripts/lib/adr/docs/normalize-cli.md +0 -3
  154. package/scripts/lib/adr/docs/normalize-pipeline.md +0 -3
  155. package/scripts/lib/docs/gha-workflow.md +25 -317
  156. package/scripts/lib/docs/index.md +1 -0
  157. package/scripts/lib/docs/list-project-rules-mdc.md +5 -4
  158. package/scripts/lib/docs/list-rule-ids.md +15 -148
  159. package/scripts/lib/docs/read-n-cursor-config-lite.md +12 -16
  160. package/scripts/lib/docs/run-lint-step.md +13 -13
  161. package/scripts/lib/docs/run-lint.md +30 -0
  162. package/scripts/lib/docs/run-rule-cli.md +14 -10
  163. package/scripts/lib/docs/run-standard-lint.md +27 -10
  164. package/scripts/lib/docs/run-standard-rule.md +12 -11
  165. package/scripts/lib/docs/timing-summary.md +11 -12
  166. package/scripts/lib/docs/worktree-notice.md +0 -3
  167. package/scripts/lib/fix/docs/index.md +1 -0
  168. package/scripts/lib/fix/docs/orchestrator.md +23 -18
  169. package/scripts/lib/fix/docs/run-conformance-check.md +32 -0
  170. package/scripts/lib/fix/docs/t0.md +10 -9
  171. package/scripts/lib/fix/orchestrator.mjs +5 -5
  172. package/scripts/lib/fix/{run-fix-check.mjs → run-conformance-check.mjs} +13 -13
  173. package/scripts/lib/fix/t0.mjs +3 -3
  174. package/scripts/lib/gha-workflow.mjs +1 -1
  175. package/scripts/lib/list-project-rules-mdc.mjs +1 -1
  176. package/scripts/lib/list-rule-ids.mjs +12 -3
  177. package/scripts/lib/read-n-cursor-config-lite.mjs +2 -2
  178. package/scripts/lib/run-lint-step.mjs +1 -1
  179. package/{rules/lint/js/orchestrate.mjs → scripts/lib/run-lint.mjs} +42 -20
  180. package/scripts/lib/run-rule-cli.mjs +4 -4
  181. package/scripts/lib/run-standard-lint.mjs +19 -6
  182. package/scripts/lib/run-standard-rule.mjs +4 -4
  183. package/scripts/lib/timing-summary.mjs +1 -1
  184. package/scripts/{post-tool-use-fix.mjs → post-tool-use-check.mjs} +9 -9
  185. package/scripts/sync-claude-config.mjs +2 -2
  186. package/rules/changelog/fix.mjs +0 -18
  187. package/rules/ci4/fix.mjs +0 -18
  188. package/rules/doc-files/fix.mjs +0 -19
  189. package/rules/doc-files/js/docs/lint.md +0 -34
  190. package/rules/doc-files/lint/docs/index.md +0 -11
  191. package/rules/doc-files/lint/docs/lint.md +0 -35
  192. package/rules/docker/fix.mjs +0 -18
  193. package/rules/docker/lint/docs/index.md +0 -11
  194. package/rules/docker/lint/docs/lint.md +0 -200
  195. package/rules/docker/lint/lint.mjs +0 -95
  196. package/rules/efes/fix.mjs +0 -18
  197. package/rules/feedback/fix.mjs +0 -18
  198. package/rules/ga/fix.mjs +0 -18
  199. package/rules/ga/js/docs/lint.md +0 -20
  200. package/rules/ga/js/lint.mjs +0 -12
  201. package/rules/ga/lint/docs/index.md +0 -11
  202. package/rules/ga/lint/docs/lint.md +0 -31
  203. package/rules/graphql/fix.mjs +0 -18
  204. package/rules/hasura/fix.mjs +0 -18
  205. package/rules/image-avif/fix.mjs +0 -18
  206. package/rules/image-compress/fix.mjs +0 -18
  207. package/rules/image-compress/js/docs/lint.md +0 -24
  208. package/rules/js-bun-db/fix.mjs +0 -18
  209. package/rules/js-bun-redis/fix.mjs +0 -18
  210. package/rules/js-lint/fix.mjs +0 -18
  211. package/rules/js-lint/js/docs/lint.md +0 -32
  212. package/rules/js-lint-ci/fix.mjs +0 -18
  213. package/rules/js-lint-ci/js/docs/lint.md +0 -22
  214. package/rules/js-lint-ci/js/lint.mjs +0 -15
  215. package/rules/js-mssql/fix.mjs +0 -18
  216. package/rules/js-run/fix.mjs +0 -18
  217. package/rules/k8s/fix.mjs +0 -18
  218. package/rules/k8s/js/lint.mjs +0 -14
  219. package/rules/k8s/lint/docs/index.md +0 -11
  220. package/rules/k8s/lint/docs/lint.md +0 -413
  221. package/rules/lint/docs/fix.md +0 -25
  222. package/rules/lint/docs/index.md +0 -11
  223. package/rules/lint/fix.mjs +0 -18
  224. package/rules/lint/js/docs/index.md +0 -11
  225. package/rules/lint/js/docs/orchestrate.md +0 -31
  226. package/rules/lint/meta.json +0 -1
  227. package/rules/nginx-default-tpl/fix.mjs +0 -18
  228. package/rules/npm-module/fix.mjs +0 -18
  229. package/rules/php/fix.mjs +0 -18
  230. package/rules/php/js/docs/lint.md +0 -20
  231. package/rules/php/js/lint.mjs +0 -15
  232. package/rules/php/lint/docs/index.md +0 -11
  233. package/rules/php/lint/docs/lint.md +0 -219
  234. package/rules/python/fix.mjs +0 -18
  235. package/rules/python/js/docs/lint.md +0 -21
  236. package/rules/python/js/lint.mjs +0 -14
  237. package/rules/python/lint/docs/index.md +0 -11
  238. package/rules/python/lint/docs/lint.md +0 -29
  239. package/rules/rego/fix.mjs +0 -18
  240. package/rules/rego/js/docs/lint.md +0 -21
  241. package/rules/rego/js/lint.mjs +0 -12
  242. package/rules/rego/lint/docs/index.md +0 -11
  243. package/rules/rego/lint/docs/lint.md +0 -208
  244. package/rules/rust/fix.mjs +0 -18
  245. package/rules/rust/js/docs/lint.md +0 -21
  246. package/rules/security/fix.mjs +0 -18
  247. package/rules/security/js/docs/lint.md +0 -175
  248. package/rules/security/js/lint.mjs +0 -26
  249. package/rules/style-lint/fix.mjs +0 -18
  250. package/rules/style-lint/js/docs/lint.md +0 -31
  251. package/rules/tauri/fix.mjs +0 -18
  252. package/rules/test/fix.mjs +0 -18
  253. package/rules/text/fix.mjs +0 -18
  254. package/rules/text/js/docs/lint.md +0 -23
  255. package/rules/text/js/lint.mjs +0 -15
  256. package/rules/text/lint/docs/cspell-fix.md +0 -32
  257. package/rules/text/lint/docs/index.md +0 -15
  258. package/rules/text/lint/docs/lint.md +0 -36
  259. package/rules/text/lint/docs/run-dotenv-linter.md +0 -161
  260. package/rules/text/lint/docs/run-shellcheck.md +0 -216
  261. package/rules/text/lint/docs/run-v8r.md +0 -201
  262. package/rules/tool-surface/fix.mjs +0 -18
  263. package/rules/vue/fix.mjs +0 -18
  264. package/rules/worktree/fix.mjs +0 -18
  265. /package/rules/release/{fix.mjs → main.mjs} +0 -0
  266. /package/rules/text/{lint → js}/cspell-fix.mjs +0 -0
  267. /package/rules/text/{lint → js}/run-dotenv-linter.mjs +0 -0
  268. /package/rules/text/{lint → js}/run-shellcheck.mjs +0 -0
  269. /package/rules/text/{lint → js}/run-v8r.mjs +0 -0
@@ -1,18 +0,0 @@
1
- import { isRunAsCli, runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
2
- import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
3
-
4
- /**
5
- * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
6
- * Library mode: викликається CLI orchestration через `import + run(ctx)`.
7
- * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
8
- * @returns {Promise<number>} 0 — OK, 1 — порушення
9
- */
10
- export function run(ctx) {
11
- return runStandardRule(import.meta.dirname, ctx)
12
- }
13
-
14
- if (isRunAsCli(import.meta.url)) {
15
- // Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
16
- // (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
17
- process.exitCode = await runRuleCli(import.meta.dirname)
18
- }
@@ -1,22 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: lint.mjs
4
- resource: npm/rules/js-lint-ci/js/lint.mjs
5
- docgen:
6
- crc: 5d06673f
7
- score: 100
8
- ---
9
-
10
- Інструмент виконує аналіз коду за допомогою jscpd та knip. Інструменти ініціюються для проведення перевірок. Результати перевірок фіксуються.
11
-
12
- ## Поведінка
13
-
14
- 1. Запуск інструменту jscpd для перевірки коду.
15
- 2. Перевірка статусу виводу jscpd.
16
- 3. Запуск інструменту knip для перевірки коду.
17
- 4. Перевірка статусу виводу knip.
18
-
19
- ## Гарантії поведінки
20
-
21
- - Read-only: файл не виконує операцій запису у файлову систему.
22
- - Не звертається до мережі.
@@ -1,15 +0,0 @@
1
- /** @see ./docs/lint.md */
2
- import { spawnSync } from 'node:child_process'
3
-
4
- /**
5
- * @param {string[] | undefined} _files ігнорується (крос-файловий аналіз)
6
- * @param {string} [cwd] корінь репо
7
- * @returns {Promise<number>} 0 — OK, ≠0 — порушення
8
- */
9
- export function lint(_files, cwd = process.cwd()) {
10
- const jscpd = spawnSync('bunx', ['jscpd', '.'], { cwd, stdio: 'inherit' })
11
- const jc = typeof jscpd.status === 'number' ? jscpd.status : 1
12
- if (jc !== 0) return Promise.resolve(jc)
13
- const knip = spawnSync('bunx', ['knip', '--no-config-hints'], { cwd, stdio: 'inherit' })
14
- return Promise.resolve(typeof knip.status === 'number' ? knip.status : 1)
15
- }
@@ -1,18 +0,0 @@
1
- import { isRunAsCli, runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
2
- import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
3
-
4
- /**
5
- * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
6
- * Library mode: викликається CLI orchestration через `import + run(ctx)`.
7
- * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
8
- * @returns {Promise<number>} 0 — OK, 1 — порушення
9
- */
10
- export function run(ctx) {
11
- return runStandardRule(import.meta.dirname, ctx)
12
- }
13
-
14
- if (isRunAsCli(import.meta.url)) {
15
- // Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
16
- // (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
17
- process.exitCode = await runRuleCli(import.meta.dirname)
18
- }
@@ -1,18 +0,0 @@
1
- import { isRunAsCli, runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
2
- import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
3
-
4
- /**
5
- * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
6
- * Library mode: викликається CLI orchestration через `import + run(ctx)`.
7
- * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
8
- * @returns {Promise<number>} 0 — OK, 1 — порушення
9
- */
10
- export function run(ctx) {
11
- return runStandardRule(import.meta.dirname, ctx)
12
- }
13
-
14
- if (isRunAsCli(import.meta.url)) {
15
- // Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
16
- // (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
17
- process.exitCode = await runRuleCli(import.meta.dirname)
18
- }
package/rules/k8s/fix.mjs DELETED
@@ -1,18 +0,0 @@
1
- import { isRunAsCli, runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
2
- import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
3
-
4
- /**
5
- * Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
6
- * Library mode: викликається CLI orchestration через `import + run(ctx)`.
7
- * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
8
- * @returns {Promise<number>} 0 — OK, 1 — порушення
9
- */
10
- export function run(ctx) {
11
- return runStandardRule(import.meta.dirname, ctx)
12
- }
13
-
14
- if (isRunAsCli(import.meta.url)) {
15
- // Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
16
- // (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
17
- process.exitCode = await runRuleCli(import.meta.dirname)
18
- }
@@ -1,14 +0,0 @@
1
- /** @see ./docs/lint.md */
2
- import { runLintK8s } from '../lint/lint.mjs'
3
-
4
- /**
5
- * Оркестраторний адаптер `n-cursor lint k8s` (лінтер-фаза): kubeconform + kubescape по деревах
6
- * `.../k8s/*.yaml` через `runLintK8s` (read-only тули — мутацій немає, тож `opts` ігнорується).
7
- * Структурні k8s.mdc-перевірки (manifest/kustomization/network_policy) — у конформність-фазі.
8
- * Без `.../k8s`-маніфестів крок — no-op.
9
- * @param {string[] | undefined} _files ігнорується (whole-repo обхід `.../k8s`)
10
- * @returns {Promise<number>} exit code
11
- */
12
- export function lint(_files) {
13
- return runLintK8s()
14
- }
@@ -1,11 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: npm/rules/k8s/lint
4
- resource: npm/rules/k8s/lint/
5
- ---
6
-
7
- # npm/rules/k8s/lint
8
-
9
- | Файл | Тип |
10
- |---|---|
11
- | [lint.mjs](lint.md) | JS Module |
@@ -1,413 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: lint.mjs
4
- resource: npm/rules/k8s/lint/lint.mjs
5
- docgen:
6
- crc: b6119c36
7
- ---
8
-
9
- Модуль `lint.mjs` реалізує підкоманду `lint-k8s` CLI `n-cursor`. Він автоматично знаходить у репозиторії всі дерева Kubernetes-маніфестів за конвенційним сегментом шляху `k8s/`, а потім послідовно валідує їх двома інструментами:
10
-
11
- 1. **`kubeconform`** — структурна валідація YAML-маніфестів проти OpenAPI-схем Kubernetes; підтримує CRD-схеми з каталогу Datree.
12
- 2. **`kubescape`** — сканування на misconfiguration / compliance (NSA, MITRE, CIS тощо), з пріоритетом по `kustomize`-білдах (через `kubectl kustomize <dir>`), щоб коректно матчити `namespace`, `podSelector`, network-policies та overlay-структури.
13
-
14
- Логіка реалізує канон правила `k8s.mdc`:
15
-
16
- - шукаємо лише `*.yaml` (розширення `.yml` під `k8s` заборонене каноном);
17
- - виключаємо `.github/` — це домен `ga.mdc`;
18
- - враховуємо `.cursorignore` для виключення дерев;
19
- - якщо `*.yaml`-файлів під `k8s` немає — виходимо з кодом `0` без запуску CLI;
20
- - версія Kubernetes для `kubeconform` (`-kubernetes-version`) синхронізована з `YANNH_PIN` із `rules/k8s/fix.mjs` / `k8s.mdc`.
21
-
22
- Канонічно публічна форма `runLintK8s` обгорнута в `runStandardLint` (із `scripts/lib/run-standard-lint.mjs`), який забезпечує:
23
-
24
- - **серіалізацію** через `withLock('lint-k8s')` — щоб уникнути паралельних запусків важких CLI на одній машині (див. `scripts.mdc`, секція «Серіалізація важких CLI-команд»);
25
- - **дедуплікацію** за станом git-дерева (повторний запуск без змін — no-op).
26
-
27
- Модуль одночасно є і бібліотекою (експортує допоміжні функції для тестів та реюзу), і CLI-точкою входу (через `isRunAsCli(import.meta.url)`).
28
-
29
- ## Експорти / API
30
-
31
- | Експорт | Тип | Призначення |
32
- | ------------------------------------ | -------------- | -------------------------------------------------------------------------------- |
33
- | `pathHasK8sSegment(filePath, root?)` | function | Перевіряє, чи має шлях сегмент каталогу `k8s` (відносно `root`, якщо переданий). |
34
- | `k8sRootFromFile(absFile)` | function | Підіймається вгору від файлу до найближчого предка з назвою `k8s`. |
35
- | `findK8sRoots(root, ignorePaths?)` | async function | Повертає унікальні сортовані `…/k8s`-корені під `root`, що містять `*.yaml`. |
36
- | `buildKubescapeExceptionsArgs(root)` | function | Формує `['--exceptions', <abs>]` якщо в корені є `.kubescape-exceptions.json`. |
37
- | `findKustomizationDirs(dir)` | async function | Знаходить «точки входу» Kustomize (`kustomization.yaml` з `kind` ≠ `Component`). |
38
- | `runLintK8s` | async function | Публічна CLI-форма: `runStandardLint(import.meta.dirname, runLintK8sSteps)`. |
39
-
40
- CLI-режим: при прямому виконанні скрипта (`bun npm/rules/k8s/lint/lint.mjs`) встановлюється `process.exitCode = await runLintK8s()`.
41
-
42
- ### Внутрішні (без `export`) функції
43
-
44
- | Функція | Роль |
45
- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
46
- | `runKubeconform(dirs)` | Запуск `kubeconform` для переданого списку каталогів. |
47
- | `runKustomizeBuild(kubectlPath, dir)` | `kubectl kustomize <dir>` → `{ status, stdout: Buffer }`. |
48
- | `runKubescapeManifest(kubescapePath, manifest, exceptionsArgs)` | Скан зібраного маніфесту через тимчасовий файл. |
49
- | `scanRawK8sDir(kubescapePath, dir, exceptionsArgs)` | Сирий dir-скан kubescape для k8s-кореня без Kustomize. |
50
- | `scanKustomizeK8sDirs(kubectlPath, kubescapePath, kdirs, exceptionsArgs)` | Цикл `kustomize build` → `kubescape scan` по всіх `kdirs`. |
51
- | `runKubescape(dirs, root)` | Оркестратор фази kubescape: Kustomize-білди або fallback на сирий dir-скан. |
52
- | `runLintK8sSteps()` | Внутрішня послідовність (без локу): пошук дерев → kubeconform → kubescape. |
53
-
54
- ### Константи модуля
55
-
56
- | Константа | Значення | Призначення |
57
- | ---------------------------- | ---------------------------- | ----------------------------------------------------------------- |
58
- | `KUBESCAPE_EXCEPTIONS_FILE` | `.kubescape-exceptions.json` | Ім'я per-project файлу винятків для kubescape. |
59
- | `KUSTOMIZATION_FILE` | `kustomization.yaml` | Канонічна назва маніфесту Kustomize (`.yml` заборонено). |
60
- | `KUBESCAPE_MISSING_HINT` | рядок з URL | Підказка користувачу при відсутності kubescape у PATH. |
61
- | `PATH_SEPARATOR_RE` | `/[/\\]/u` | Регексп розбиття шляху по `/` або `\`. |
62
- | `YAML_EXT_RE` | `/\.yaml$/iu` | Регексп фільтра YAML-файлів. |
63
- | `KUBERNETES_VERSION` | `1.33.9` | Версія схем Kubernetes для kubeconform (узгоджена з `YANNH_PIN`). |
64
- | `DATREE_CRD_SCHEMA_LOCATION` | URL-шаблон | Додаткова локація схем для CRD-ресурсів (Datree CRDs-catalog). |
65
-
66
- ## Функції
67
-
68
- ### `pathHasK8sSegment(filePath, root)`
69
-
70
- **Сигнатура:** `(filePath: string, root?: string) => boolean`
71
-
72
- **Параметри:**
73
-
74
- - `filePath` — абсолютний або відносний шлях до файлу.
75
- - `root` (необов'язковий) — корінь репо для relativize.
76
-
77
- **Повертає:** `true`, якщо серед компонентів шляху (відносно `root`, якщо передано) є сегмент `k8s`.
78
-
79
- **Поведінка / нюанси:**
80
-
81
- - Без `root` працює напряму з `filePath` — корисно для перевірки відносного шляху.
82
- - З `root` обов'язково релятивізує: інакше, якщо сам корінь репо містить компонент `k8s` (наприклад `/Users/.../abie/k8s/`), функція повернула б `true` для **усіх** файлів проєкту, включно з `.github/workflows/*.yml`.
83
- - Бекслеші нормалізуються в `/` через `replaceAll('\\', '/')`.
84
- - Якщо після relativize рядок порожній — повертає `false`.
85
-
86
- **Side effects:** немає (чиста функція).
87
-
88
- ### `k8sRootFromFile(absFile)`
89
-
90
- **Сигнатура:** `(absFile: string) => string | null`
91
-
92
- **Параметри:**
93
-
94
- - `absFile` — абсолютний шлях до YAML-файлу.
95
-
96
- **Повертає:** абсолютний шлях до найближчого предка з ім'ям `k8s` або `null`, якщо такого сегмента в ланцюжку немає.
97
-
98
- **Алгоритм:** ітеративно піднімається `dirname → parent` до 64 рівнів вгору; зупиняється, коли `basename(dir) === 'k8s'` або коли `dirname(dir) === dir` (корінь файлової системи).
99
-
100
- **Side effects:** немає.
101
-
102
- ### `findK8sRoots(root, ignorePaths)`
103
-
104
- **Сигнатура:** `async (root: string, ignorePaths?: string[]) => Promise<string[]>`
105
-
106
- **Параметри:**
107
-
108
- - `root` — корінь репозиторію.
109
- - `ignorePaths` (необов'язковий, default `[]`) — абсолютні шляхи каталогів, повністю виключених з обходу (передається у `walkDir`).
110
-
111
- **Повертає:** Promise з масивом унікальних, відсортованих за `localeCompare` абсолютних шляхів до `…/k8s`-каталогів, у яких знайдено хоча б один `*.yaml`.
112
-
113
- **Алгоритм:**
114
-
115
- 1. Викликає `walkDir(root, visitor, ignorePaths)`.
116
- 2. Для кожного відвіданого `p`:
117
- - вираховує відносний шлях `rel` (нормалізує бекслеші);
118
- - **пропускає** все, що під `.github/` (це домен `ga.mdc`);
119
- - пропускає файли без сегмента `k8s` у шляху;
120
- - пропускає не-`.yaml` файли;
121
- - визначає `k8sRoot` через `k8sRootFromFile`; якщо знайдено — додає до `Set`.
122
- 3. Конвертує `Set` у масив і сортує `localeCompare`.
123
-
124
- **Side effects:** виконує файлову систему через `walkDir` (read-only обхід).
125
-
126
- ### `buildKubescapeExceptionsArgs(root)`
127
-
128
- **Сигнатура:** `(root: string) => string[]`
129
-
130
- **Параметри:**
131
-
132
- - `root` — корінь репозиторію (де шукається `.kubescape-exceptions.json`).
133
-
134
- **Повертає:** `['--exceptions', '<абсолютний шлях>']` якщо файл існує, інакше `[]`.
135
-
136
- **Side effects:** один синхронний `existsSync`.
137
-
138
- ### `findKustomizationDirs(dir)`
139
-
140
- **Сигнатура:** `async (dir: string) => Promise<string[]>`
141
-
142
- **Параметри:**
143
-
144
- - `dir` — абсолютний шлях до `…/k8s` (або іншого) каталогу.
145
-
146
- **Повертає:** Promise з відсортованим списком абсолютних шляхів до каталогів, що містять **білдабельний** `kustomization.yaml` (тобто такий, що `kustomize build` буде здатний рендерити локально).
147
-
148
- **Семантика «білдабельний»:**
149
-
150
- - Файл називається саме `kustomization.yaml` (без `.yml` — заборонено каноном).
151
- - YAML парситься без помилок (інакше — `continue`).
152
- - Перший документ — об'єкт, у якого `kind !== 'Component'`. `kind: Kustomization` або відсутній `kind` (типово Kustomization) — приймаються; `kind: Component` пропускається, бо Components не білдяться окремо й підключаються через `components:` із overlay.
153
-
154
- **Алгоритм:**
155
-
156
- 1. `walkDir(dir, …)` збирає `candidates` — усі шляхи з `basename === 'kustomization.yaml'`.
157
- 2. Послідовно по `candidates`:
158
- - `readFile(p, 'utf8')` (помилка → skip);
159
- - `parse(text)` через пакет `yaml` (помилка → skip);
160
- - якщо `kind === 'Component'` → skip;
161
- - інакше — `result.add(dirname(p))`.
162
- 3. Сортування `localeCompare`.
163
-
164
- **Side effects:** обхід ФС + читання вмісту YAML; парсинг без винятку назовні.
165
-
166
- ### `runKubeconform(dirs)` _(внутрішня)_
167
-
168
- **Сигнатура:** `(dirs: string[]) => number`
169
-
170
- **Параметри:**
171
-
172
- - `dirs` — абсолютні шляхи до `…/k8s`-каталогів.
173
-
174
- **Повертає:** код виходу процесу `kubeconform` (`r.status ?? 1`); `127` якщо kubeconform відсутній (`ENOENT`).
175
-
176
- **Прапори, що передаються `kubeconform`:**
177
-
178
- - `-summary` — компактний підсумок наприкінці.
179
- - `-kubernetes-version 1.33.9` — `KUBERNETES_VERSION`.
180
- - `-schema-location default` — офіційні схеми Kubernetes.
181
- - `-schema-location <DATREE_CRD_SCHEMA_LOCATION>` — реєстр CRD-схем Datree.
182
- - `-ignore-missing-schemas` — пропустити CRD, для яких не знайдено схеми.
183
- - `…dirs` — список цільових каталогів.
184
-
185
- **Side effects:** `spawnSync` (stdio inherit) — друкує вихід kubeconform у термінал; на `ENOENT` пише інструкцію встановлення в `stderr`.
186
-
187
- ### `runKustomizeBuild(kubectlPath, dir)` _(внутрішня)_
188
-
189
- **Сигнатура:** `(kubectlPath: string, dir: string) => { status: number, stdout: Buffer }`
190
-
191
- **Поведінка:** запускає `kubectl kustomize <dir>` з `stdio: ['ignore', 'pipe', 'inherit']` — stdout захоплює як буфер, stderr інхеритимо в термінал (щоб помилки збірки одразу були видимі). Використовується `kubectl kustomize` замість окремого бінарника `kustomize`, бо `kubectl` є штатним інструментом, а підкоманда `kustomize` локальна і не вимагає доступу до кластера.
192
-
193
- **Повертає:** `{ status: r.status ?? 1, stdout: r.stdout ?? Buffer.alloc(0) }`.
194
-
195
- **Side effects:** дочірній процес з inherit stderr.
196
-
197
- ### `runKubescapeManifest(kubescapePath, manifest, exceptionsArgs)` _(внутрішня)_
198
-
199
- **Сигнатура:** `(kubescapePath: string, manifest: Buffer, exceptionsArgs: string[]) => { status: number, enoent: boolean }`
200
-
201
- **Поведінка:**
202
-
203
- 1. Створює тимчасову директорію `mkdtempSync(join(tmpdir(), 'nitra-cursor-k8s-'))`.
204
- 2. Пише `manifest` у файл `manifest.yaml` усередині неї.
205
- 3. Запускає `kubescape scan <file> --severity-threshold high <...exceptionsArgs>` зі `stdio: 'inherit'`.
206
- 4. У `finally` гарантовано видаляє створену директорію (`rmSync(dir, { recursive: true, force: true })`).
207
-
208
- **Чому тимчасовий файл, а не stdin:** `kubescape scan` у v4.x **не читає stdin** — `-` як шлях не розпізнається (`no resources found to scan`), а прапорця `--input`/`--stdin` у CLI немає.
209
-
210
- **Повертає:** `{ status, enoent }` — `enoent: true` якщо `r.error.code === 'ENOENT'`.
211
-
212
- **Side effects:** створення/видалення тимчасової директорії, запис файлу, дочірній процес.
213
-
214
- ### `scanRawK8sDir(kubescapePath, dir, exceptionsArgs)` _(внутрішня)_
215
-
216
- **Сигнатура:** `(kubescapePath: string, dir: string, exceptionsArgs: string[]) => number`
217
-
218
- **Поведінка:** сирий dir-скан kubescape для `…/k8s`-кореня без білдабельного `kustomization.yaml`. Друкує лог `run-k8s: kubescape scan <dir> (без kustomization — сирий dir-скан)` і запускає `kubescape scan <dir> --severity-threshold high <...exceptionsArgs>` зі `stdio: 'inherit'`.
219
-
220
- **Повертає:** `0` при успіху, `127` якщо kubescape зник з PATH (`ENOENT`), інакше `r.status ?? 1`.
221
-
222
- ### `scanKustomizeK8sDirs(kubectlPath, kubescapePath, kdirs, exceptionsArgs)` _(внутрішня)_
223
-
224
- **Сигнатура:** `(kubectlPath: string, kubescapePath: string, kdirs: string[], exceptionsArgs: string[]) => number`
225
-
226
- **Поведінка:** для кожного `kdir` із `kdirs`:
227
-
228
- 1. Друкує лог `run-k8s: kubectl kustomize <kdir> | kubescape scan <tmp>`.
229
- 2. `runKustomizeBuild(kubectlPath, kdir)` — якщо `status !== 0`, негайно повертає цей `status`.
230
- 3. `runKubescapeManifest(kubescapePath, build.stdout, exceptionsArgs)`:
231
- - якщо `ks.enoent` — пише `KUBESCAPE_MISSING_HINT` у `stderr` і повертає `127`;
232
- - якщо `ks.status !== 0` — повертає `ks.status`.
233
-
234
- **Повертає:** `0` лише якщо всі каталоги пройшли; інакше — код першого невдалого процесу.
235
-
236
- ### `runKubescape(dirs, root)` _(внутрішня)_
237
-
238
- **Сигнатура:** `async (dirs: string[], root: string) => Promise<number>`
239
-
240
- **Алгоритм:**
241
-
242
- 1. `exceptionsArgs = buildKubescapeExceptionsArgs(root)`; якщо непорожній — лог про використання exceptions-файлу.
243
- 2. `kubescapePath = ensureTool('kubescape')` — забезпечує наявність бінарника (інсталює, якщо налаштовано).
244
- 3. `kubectlPath = null` (lazy resolve).
245
- 4. Для кожного `d` з `dirs`:
246
- - `kdirs = await findKustomizationDirs(d)`.
247
- - Якщо `kdirs` порожній → fallback: `scanRawK8sDir(kubescapePath, d, exceptionsArgs)`; помилка → return.
248
- - Інакше (перший раз): `kubectlPath = resolveCmd('kubectl')`. Якщо `null` → лог про відсутність kubectl + return `127`.
249
- - `scanKustomizeK8sDirs(kubectlPath, kubescapePath, kdirs, exceptionsArgs)`; помилка → return.
250
- 5. Поверне `0`, якщо всі `dirs` пройшли.
251
-
252
- **Чому через kustomize-білд, а не сирий скан:** збірка нормалізує `namespace` на workload-маніфестах і `base/networkpolicy.yaml` (через `base/kustomization.yaml` `namespace:`), що дає коректний матчинг `podSelector` у control'і C-0260 (`Missing network policy`) і дозволяє kubescape бачити дерево overlays/components зі справжніми ресурсами.
253
-
254
- **Fallback:** якщо в `…/k8s` немає білдабельного `kustomization.yaml` — сирий dir-скан (не блокувати YAML-only проєкт без Kustomize).
255
-
256
- ### `runLintK8sSteps()` _(внутрішня)_
257
-
258
- **Сигнатура:** `async () => Promise<number>`
259
-
260
- **Поведінка:**
261
-
262
- 1. `root = process.cwd()`.
263
- 2. `ignorePaths = await loadCursorIgnorePaths(root)` — підвантажує патерни з `.cursorignore`.
264
- 3. `dirs = await findK8sRoots(root, ignorePaths)`.
265
- 4. Якщо `dirs.length === 0` — лог `run-k8s: немає *.yaml під k8s — kubeconform і kubescape пропущено` і `return 0`.
266
- 5. Лог `run-k8s: каталоги k8s (<n>):` + перелік кожного `d`.
267
- 6. `kc = runKubeconform(dirs)`; якщо `!= 0` — `return kc`.
268
- 7. `ks = await runKubescape(dirs, root)`; `return ks`.
269
-
270
- **Повертає:** код виходу для `process.exitCode` (`0` — успіх або пропуск).
271
-
272
- ### `runLintK8s` _(експортована CLI-форма)_
273
-
274
- **Сигнатура:** `() => Promise<number>`
275
-
276
- **Реалізація:** `runStandardLint(import.meta.dirname, runLintK8sSteps)` — обгортка з канону `scripts.mdc`, що додає:
277
-
278
- - серіалізацію через `withLock('lint-k8s')` (блокування паралельних запусків);
279
- - дедуплікацію за станом git-дерева (пропуск повторного запуску без змін).
280
-
281
- Експорт використовується з `bin/n-cursor.js` як підкоманда `lint-k8s`.
282
-
283
- ## Залежності
284
-
285
- ### Node.js builtin
286
-
287
- | Модуль | Що використовується |
288
- | -------------------- | ---------------------------------------------------------------------------------------- |
289
- | `node:child_process` | `spawnSync` для запуску `kubeconform`, `kubectl`, `kubescape`. |
290
- | `node:fs` | `existsSync` (exceptions-файл), `mkdtempSync`, `rmSync`, `writeFileSync` (tmp-маніфест). |
291
- | `node:fs/promises` | `readFile` для парсингу `kustomization.yaml`. |
292
- | `node:os` | `tmpdir()` як база для тимчасової директорії. |
293
- | `node:path` | `basename`, `dirname`, `join`, `relative`. |
294
-
295
- ### Зовнішні npm-пакети
296
-
297
- | Пакет | Використання |
298
- | ----------------------------- | --------------------------------------------------------------- |
299
- | `yaml` (named import `parse`) | Парсинг `kustomization.yaml` для відсіювання `kind: Component`. |
300
-
301
- ### Внутрішні модулі репозиторію
302
-
303
- | Шлях | Використання |
304
- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
305
- | `../../../scripts/cli-entry.mjs` (`isRunAsCli`) | Детектор «запущено як CLI» (а не імпортовано). |
306
- | `../../../scripts/lib/ensure-tool.mjs` (`ensureTool`) | Гарантує наявність CLI-інструмента (kubeconform, kubescape) у PATH (інсталює якщо налаштовано). |
307
- | `../../../scripts/lib/load-cursor-config.mjs` (`loadCursorIgnorePaths`) | Зчитує `.cursorignore` і повертає абсолютні шляхи виключень. |
308
- | `../../../scripts/utils/resolve-cmd.mjs` (`resolveCmd`) | Знаходить абсолютний шлях до бінарника (для `kubectl`, без installation hook). |
309
- | `../../../scripts/utils/walkDir.mjs` (`walkDir`) | Рекурсивний обхід ФС із підтримкою ignore-патернів. |
310
- | `../../../scripts/lib/run-standard-lint.mjs` (`runStandardLint`) | Стандартна обгортка серіалізації + дедупу для lint-команд. |
311
-
312
- ### Зовнішні CLI-інструменти
313
-
314
- - **`kubeconform`** — очікується в `PATH`, ставиться через Homebrew (macOS) або релізами з GitHub (`yannh/kubeconform`); у CI — крок установки з `k8s.mdc`.
315
- - **`kubescape`** — очікується в `PATH`; інструкція встановлення — `https://github.com/kubescape/kubescape#readme`.
316
- - **`kubectl`** — стандартний інструмент; для підкоманди `kubectl kustomize` доступ до кластера **не потрібен** (рендер локальний).
317
-
318
- ## Потік виконання / Використання
319
-
320
- ### CLI-режим
321
-
322
- ```
323
- n-cursor lint k8s # rule orchestration entrypoint
324
- # або (наприкінці файлу — прямий запуск)
325
- bun npm/rules/k8s/lint/lint.mjs
326
- ```
327
-
328
- Послідовність:
329
-
330
- ```
331
- runLintK8s
332
- └── runStandardLint(dirname, runLintK8sSteps)
333
- ├── withLock('lint-k8s') # серіалізація
334
- ├── (дедуп за git-станом)
335
- └── runLintK8sSteps
336
- ├── loadCursorIgnorePaths(cwd)
337
- ├── findK8sRoots(cwd, ignorePaths)
338
- │ └── walkDir … pathHasK8sSegment … k8sRootFromFile
339
- ├── [якщо dirs порожні] → return 0
340
- ├── runKubeconform(dirs)
341
- │ └── ensureTool('kubeconform') + spawnSync
342
- └── runKubescape(dirs, root)
343
- ├── buildKubescapeExceptionsArgs(root)
344
- ├── ensureTool('kubescape')
345
- └── for d of dirs:
346
- ├── findKustomizationDirs(d)
347
- ├── [empty] scanRawK8sDir
348
- └── [else]
349
- ├── resolveCmd('kubectl') [lazy, один раз]
350
- └── scanKustomizeK8sDirs
351
- └── for kdir:
352
- ├── runKustomizeBuild(kubectl, kdir)
353
- └── runKubescapeManifest(kubescape, stdout, exceptionsArgs)
354
- ├── mkdtempSync + writeFileSync
355
- ├── spawnSync('kubescape scan <tmp> --severity-threshold high …')
356
- └── finally: rmSync(tmpdir, recursive, force)
357
- ```
358
-
359
- ### Імпортний режим (бібліотека)
360
-
361
- ```js
362
- import {
363
- findK8sRoots,
364
- findKustomizationDirs,
365
- buildKubescapeExceptionsArgs,
366
- k8sRootFromFile,
367
- pathHasK8sSegment,
368
- runLintK8s
369
- } from './lint.mjs'
370
-
371
- const roots = await findK8sRoots(process.cwd())
372
- ```
373
-
374
- Чисті помічники (`pathHasK8sSegment`, `k8sRootFromFile`, `buildKubescapeExceptionsArgs`) можна тестувати ізольовано (стек тестів — у сусідньому `tests/`).
375
-
376
- ### Коди виходу (повертаються через `process.exitCode`)
377
-
378
- | Код | Значення |
379
- | ---------- | ----------------------------------------------------------------------------------------------------------- |
380
- | `0` | Успіх або пропуск (немає `*.yaml` під `k8s`). |
381
- | `127` | Відсутній зовнішній CLI у PATH: `kubeconform`, `kubescape` або `kubectl`. У stderr — підказка встановлення. |
382
- | ≠ 0 (інше) | Код невдалого процесу (`kubeconform`, `kubectl kustomize` або `kubescape`). |
383
- | `1` | Дефолт `r.status ?? 1`, якщо процес завершився без статусу. |
384
-
385
- ### Файли конфігурації, що впливають на роботу
386
-
387
- - **`.cursorignore`** у корені — патерни виключення для обходу ФС (через `loadCursorIgnorePaths`).
388
- - **`.kubescape-exceptions.json`** у корені — точкові винятки control'ів для kubescape (підмішується через `--exceptions <file>`; приклад — виняток C-0012 на ConfigMap з публічним JWT-конфігом; див. `k8s.mdc`).
389
- - **`kustomization.yaml`** у `…/k8s`-піддеревах — визначає, які каталоги білдяться через `kubectl kustomize`. Файл з `kind: Component` пропускається.
390
-
391
- ### Конвенції каталогів
392
-
393
- - Сегмент шляху `k8s/` — маркер дерева Kubernetes-маніфестів.
394
- - Дозволено лише `.yaml` (не `.yml`) — це канон `k8s.mdc`.
395
- - `.github/` повністю виключається з обходу (домен `ga.mdc`).
396
- - Глибина пошуку `k8s`-предка у `k8sRootFromFile` — до 64 рівнів каталогів (захист від нескінченного циклу на дивних ФС).
397
-
398
- ### Логи у stdout/stderr
399
-
400
- - `run-k8s: немає *.yaml під k8s — kubeconform і kubescape пропущено` — рання гілка no-op.
401
- - `run-k8s: каталоги k8s (<n>):` + перелік — стартовий лог із виявленими деревами.
402
- - `run-k8s: kubescape exceptions — .kubescape-exceptions.json` — якщо exceptions-файл присутній.
403
- - `run-k8s: kubectl kustomize <kdir> | kubescape scan <tmp>` — Kustomize-pipeline.
404
- - `run-k8s: kubescape scan <dir> (без kustomization — сирий dir-скан)` — fallback.
405
- - `stderr`: підказки встановлення kubeconform/kubescape/kubectl при `ENOENT`.
406
-
407
- ### Side effects (підсумок)
408
-
409
- - Читання файлової системи (обхід дерев, читання `kustomization.yaml`).
410
- - Створення/видалення тимчасової директорії у `os.tmpdir()` (`nitra-cursor-k8s-*`) — лише на час сканування одного Kustomize-білду.
411
- - Запис тимчасового `manifest.yaml` у цю директорію.
412
- - Запуск дочірніх процесів (`kubeconform`, `kubectl`, `kubescape`) із наслідуванням stdio.
413
- - Встановлення `process.exitCode` лише в CLI-режимі (`isRunAsCli(import.meta.url)`).
@@ -1,25 +0,0 @@
1
- ---
2
- type: JS Module
3
- title: fix.mjs
4
- resource: npm/rules/lint/fix.mjs
5
- docgen:
6
- crc: f85a9e1d
7
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
8
- score: 100
9
- ---
10
-
11
- Модуль забезпечує виконання логіки, визначеної правилом. Він ініціює виконання правила через публічну функцію `run`.
12
-
13
- ## Поведінка
14
-
15
- 1. Викликати функцію `run` для виконання логіки правила.
16
- 2. Якщо код виконується як CLI, викликати функцію для запуску правила через CLI.
17
-
18
- ## Публічний API
19
-
20
- run — виконує правило `lint` в lint-оркестраторі. Якщо правило не містить перевірок (concern-ів/policy), то `run` не робить нічого (no-op), оскільки не знаходить жодних concern-ів.
21
-
22
- ## Гарантії поведінки
23
-
24
- - Read-only: файл не виконує операцій запису у файлову систему.
25
- - Не звертається до мережі.
@@ -1,11 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: npm/rules/lint
4
- resource: npm/rules/lint/
5
- ---
6
-
7
- # npm/rules/lint
8
-
9
- | Файл | Тип |
10
- |---|---|
11
- | [fix.mjs](fix.md) | JS Module |
@@ -1,18 +0,0 @@
1
- import { isRunAsCli, runRuleCli } from '../../scripts/lib/run-rule-cli.mjs'
2
- import { runStandardRule } from '../../scripts/lib/run-standard-rule.mjs'
3
-
4
- /**
5
- * Правило `lint` — дім lint-оркестратора (`js/orchestrate.mjs`). Самого по собі правила
6
- * для перевірки немає (немає check-concern-ів/policy), тож `run` — no-op (повертає 0
7
- * через runStandardRule, який не знаходить жодного concern). fix.mjs обов'язковий за
8
- * контрактом усіх правил (`tests/fix-mjs-contract.test.mjs`).
9
- * @param {import('../../scripts/lib/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону
10
- * @returns {Promise<number>} 0 — OK
11
- */
12
- export function run(ctx) {
13
- return runStandardRule(import.meta.dirname, ctx)
14
- }
15
-
16
- if (isRunAsCli(import.meta.url)) {
17
- process.exitCode = await runRuleCli(import.meta.dirname)
18
- }
@@ -1,11 +0,0 @@
1
- ---
2
- type: Directory Index
3
- title: npm/rules/lint/js
4
- resource: npm/rules/lint/js/
5
- ---
6
-
7
- # npm/rules/lint/js
8
-
9
- | Файл | Тип |
10
- |---|---|
11
- | [orchestrate.mjs](orchestrate.md) | JS Module |