@nitra/cursor 1.9.1 → 1.9.3
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 +12 -0
- package/mdc/k8s.mdc +8 -4
- package/mdc/tauri.mdc +1 -2
- package/package.json +1 -1
- package/scripts/check-abie.mjs +12 -6
- package/scripts/check-ga.mjs +2 -2
- package/scripts/check-k8s.mjs +57 -31
- package/scripts/run-k8s.mjs +17 -7
- package/scripts/utils/run-conftest-batch.mjs +2 -4
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
|
|
6
6
|
|
|
7
|
+
## [1.9.3] - 2026-05-11
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **k8s — `pathHasK8sSegment` тепер відносно кореня репо; `.github/` явно поза скоупом:** функція `pathHasK8sSegment(filePath)` у `npm/scripts/check-k8s.mjs` та `npm/scripts/run-k8s.mjs` розбивала **абсолютний** шлях і шукала компонент `k8s`. У проєктах, де сам корінь репо називається `k8s/` (напр. `/Users/.../abie/k8s/`), сегмент `k8s` присутній в абсолютному шляху **усіх** файлів — і весь репозиторій, включно з `.github/workflows/*.yml`, потрапляв у `findK8sYamlFiles` як k8s-маніфести, після чого `checkK8sYamlFile` падав на «розширення .yml — перейменуй на .yaml» (територія `ga.mdc`, де канон протилежний). Виправлено: (1) сигнатура тепер `pathHasK8sSegment(filePath, root?)` — коли `root` передано, шлях спершу нормалізується через `node:path` `relative(root, filePath)`, і компоненти беруться **відносно кореня** (порожній relative — це сам root, повертає false); (2) `findK8sYamlFiles` у `check-k8s.mjs` і `check-abie.mjs`, а також `findK8sRoots` у `run-k8s.mjs` тепер передають `root` і додатково мають defense-in-depth ранній `return` для шляхів, що починаються з `.github/`; (3) `k8s.mdc` явно фіксує: правило стосується каталогів `k8s` відносно кореня; `.github/workflows/` і `.github/actions/` — поза скоупом (їх веде `ga.mdc`). Без `root` (юніт-тести з відносним шляхом) функція веде себе як раніше. Додано тести у `tests/check-k8s-schema.test.mjs` (worst-case з префіксом `/home/test/some/k8s/`) і `tests/run-k8s-roots.test.mjs` (інтеграційний — `findK8sRoots` у репі, корінь якого називається `k8s/`).
|
|
12
|
+
|
|
13
|
+
## [1.9.2] - 2026-05-11
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- **k8s — modeline `$schema` тепер опційний; `file:…` заборонено як плейсхолдер:** правило `k8s.mdc` уточнено — рядок `# yaml-language-server: $schema=…` обов'язковий **лише** коли для поєднання `apiVersion`/`kind` існує надійна публічна схема (kustomization / yannh / datree CRDs-catalog). Якщо публічної схеми немає, modeline **не додається зовсім** (раніше п. 5 розділу «Визначення схеми YAML» допускав `file:` за узгодженням — це створювало фальшиву видимість валідації, а автовиправлення n-fix залишало плейсхолдер `# yaml-language-server: $schema=file:.`). У `check-k8s.mjs`: (1) файли без modeline більше не падають як «перший рядок має бути коментарем», натомість `pass` із позначкою «без modeline — перевірка $schema пропущена»; (2) `$schema=file:…`тепер реєструється як помилка з підказкою прибрати modeline; (3) modeline нижче першого рядка все ще порушення; (4)`HttpBackendGroup`(Yandex ALB) як виняток без modeline залишається без змін.`lint-k8s`(kubeconform з прапорцем ignore-missing-schemas) продовжує покривати валідацію і для файлів без modeline. JSDoc на початку`check-k8s.mjs` оновлено.
|
|
18
|
+
|
|
7
19
|
## [1.9.1] - 2026-05-11
|
|
8
20
|
|
|
9
21
|
### Added
|
package/mdc/k8s.mdc
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: K8s YAML — $schema (yaml-language-server); lint-k8s (kubeconform, kubescape); check-k8s
|
|
3
|
-
version: '1.
|
|
3
|
+
version: '1.29'
|
|
4
4
|
globs: "**/k8s/**/*.yaml"
|
|
5
5
|
alwaysApply: false
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Kubernetes YAML у шляхах з `k8s`
|
|
9
9
|
|
|
10
|
-
Для кожного файлу `*.yaml`, у шляху якого є сегмент директорії **`k8s`** (наприклад `site/k8s/base/deployment.yaml`), **перший рядок** — коментар-директива для [YAML Language Server](https://github.com/redhat-developer/yaml-language-server)
|
|
10
|
+
Для кожного файлу `*.yaml`, у шляху якого є сегмент директорії **`k8s`** (наприклад `site/k8s/base/deployment.yaml`), якщо існує **публічна** схема (kustomization / yannh / datree CRDs-catalog — див. «Визначення схеми YAML»), **перший рядок** — коментар-директива для [YAML Language Server](https://github.com/redhat-developer/yaml-language-server) з URL за `https://`:
|
|
11
11
|
|
|
12
12
|
```yaml
|
|
13
13
|
# yaml-language-server: $schema=https://...
|
|
@@ -15,10 +15,14 @@ alwaysApply: false
|
|
|
15
15
|
|
|
16
16
|
Далі — вміст маніфесту. Зайвий порожній рядок між коментарем і YAML не додавай, якщо в проєкті не прийнято інше.
|
|
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
|
+
|
|
20
|
+
**Виняток — modeline заборонено:** `apiVersion: alb.yc.io/v1alpha1`, `kind: HttpBackendGroup` (Yandex ALB) — рядка **`# yaml-language-server: $schema=…`** у файлі **не** має бути (ні в першому рядку, ні далі). Перший рядок — одразу YAML (`apiVersion:` тощо). Перевірка — **`check-k8s.mjs`**.
|
|
19
21
|
|
|
20
22
|
**Розширення:** усі маніфести під **`k8s`**, включно з **`kustomization.yaml`**, — лише **`.yaml`** (розширення **`.yml`** не використовуй).
|
|
21
23
|
|
|
24
|
+
**Скоп — поза `.github/`:** правило стосується YAML-маніфестів у каталогах **`k8s`** (визначаються відносно кореня репо, не за абсолютним шляхом). Файли під **`.github/workflows/`** та **`.github/actions/`** ця перевірка **не** зачіпає — їхній скоп визначає **`ga.mdc`** (там канон — **`.yml`**). Це робить два правила несуперечливими навіть у проєктах, де сам корінь репо випадково має ім'я `k8s/` (тоді сегмент `k8s` присутній у абсолютному шляху всіх файлів, але **відносно кореня** його там немає).
|
|
25
|
+
|
|
22
26
|
**Dockerfile / hadolint** — окреме правило **`docker.mdc`** і **`npx @nitra/cursor check docker`**.
|
|
23
27
|
|
|
24
28
|
## lint-k8s: kubeconform і kubescape
|
|
@@ -695,7 +699,7 @@ patch: |-
|
|
|
695
699
|
# yaml-language-server: $schema=https://datreeio.github.io/CRDs-catalog/networking.gke.io/healthcheckpolicy_v1.json
|
|
696
700
|
```
|
|
697
701
|
|
|
698
|
-
5. **Немає надійного публічного URL** — не
|
|
702
|
+
5. **Немає надійного публічного URL** — не вигадуй URL і **не** використовуй `$schema=file:…` як заглушку (фальшива валідація). Залиш файл **без** рядка `# yaml-language-server: $schema=…` зовсім — `check-k8s` пропустить перевірку URL для таких файлів, а `lint-k8s` (kubeconform з `-ignore-missing-schemas`) усе ще покриє валідацію.
|
|
699
703
|
|
|
700
704
|
## Багатодокументні YAML
|
|
701
705
|
|
package/mdc/tauri.mdc
CHANGED
package/package.json
CHANGED
package/scripts/check-abie.mjs
CHANGED
|
@@ -323,7 +323,12 @@ async function findK8sYamlFiles(root, ignorePaths = []) {
|
|
|
323
323
|
await walkDir(
|
|
324
324
|
root,
|
|
325
325
|
p => {
|
|
326
|
-
|
|
326
|
+
const rel = relative(root, p).replaceAll('\\', '/')
|
|
327
|
+
// `.github/` належить `ga.mdc`; check-abie не зачіпає workflow-файли.
|
|
328
|
+
if (rel.startsWith('.github/')) {
|
|
329
|
+
return
|
|
330
|
+
}
|
|
331
|
+
if (!pathHasK8sSegment(p, root)) {
|
|
327
332
|
return
|
|
328
333
|
}
|
|
329
334
|
if (!YAML_EXTENSION_RE.test(p)) {
|
|
@@ -760,9 +765,9 @@ async function collectDeploymentDirs(root, yamlAbs, fail) {
|
|
|
760
765
|
* @param {string[]} yamlFilesAbs yaml під k8s
|
|
761
766
|
* @param {(msg: string) => void} fail callback
|
|
762
767
|
* @param {(msg: string) => void} passFn успішне повідомлення
|
|
763
|
-
* @returns {
|
|
768
|
+
* @returns {void}
|
|
764
769
|
*/
|
|
765
|
-
|
|
770
|
+
function ensureAbieBaseDeploymentPreemNodeSelector(root, yamlFilesAbs, fail, passFn) {
|
|
766
771
|
const baseFiles = yamlFilesAbs.filter(abs => isAbieK8sBaseYamlPath(relative(root, abs).replaceAll('\\', '/') || abs))
|
|
767
772
|
if (baseFiles.length === 0) {
|
|
768
773
|
passFn('Немає файлів у шляхах …/base/… — перевірку preem у base пропущено')
|
|
@@ -1425,9 +1430,9 @@ async function checkHttpRouteKustomization(abs, rel, mode, root, yamlFilesAbs, c
|
|
|
1425
1430
|
* @param {string[]} yamlFilesAbs yaml під k8s
|
|
1426
1431
|
* @param {(msg: string) => void} fail callback при помилці
|
|
1427
1432
|
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
1428
|
-
* @returns {
|
|
1433
|
+
* @returns {void}
|
|
1429
1434
|
*/
|
|
1430
|
-
|
|
1435
|
+
function ensureAbieBaseHttpRouteHostnames(root, yamlFilesAbs, fail, passFn) {
|
|
1431
1436
|
const baseFiles = yamlFilesAbs.filter(abs => isAbieK8sBaseYamlPath(relative(root, abs).replaceAll('\\', '/') || abs))
|
|
1432
1437
|
if (baseFiles.length === 0) {
|
|
1433
1438
|
passFn('Немає файлів у шляхах …/k8s/base/… — перевірку HTTPRoute hostnames пропущено')
|
|
@@ -1531,8 +1536,9 @@ async function ensureNoFirebaseHostingArtifacts(root, passFn, failFn) {
|
|
|
1531
1536
|
* @param {string} root корінь репозиторію
|
|
1532
1537
|
* @param {(msg: string) => void} pass callback при успішній перевірці
|
|
1533
1538
|
* @param {(msg: string) => void} fail callback при помилці
|
|
1539
|
+
* @returns {void}
|
|
1534
1540
|
*/
|
|
1535
|
-
|
|
1541
|
+
function checkCleanMergedBranch(root, pass, fail) {
|
|
1536
1542
|
const cleanMergedPath = join(root, '.github/workflows/clean-merged-branch.yml')
|
|
1537
1543
|
if (!existsSync(cleanMergedPath)) {
|
|
1538
1544
|
fail(`Відсутній ${cleanMergedPath} — потрібен для ignore_branches (abie.mdc)`)
|
package/scripts/check-ga.mjs
CHANGED
|
@@ -365,9 +365,9 @@ const GA_PER_WORKFLOW_REGO_TARGETS = [
|
|
|
365
365
|
* @param {string[]} ymlWorkflows відносні (від `wfDir`) імена файлів `*.yml`
|
|
366
366
|
* @param {(msg: string) => void} pass callback при успішній перевірці
|
|
367
367
|
* @param {(msg: string) => void} fail callback при помилці
|
|
368
|
-
* @returns {
|
|
368
|
+
* @returns {void}
|
|
369
369
|
*/
|
|
370
|
-
|
|
370
|
+
function runAllGaRego(wfDir, ymlWorkflows, pass, fail) {
|
|
371
371
|
for (const target of GA_PER_WORKFLOW_REGO_TARGETS) {
|
|
372
372
|
if (!existsSync(target.workflow)) continue
|
|
373
373
|
const violations = runConftestBatch({
|
package/scripts/check-k8s.mjs
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Перевіряє Kubernetes YAML у шляхах з сегментом `k8s` (див. k8s.mdc).
|
|
3
3
|
*
|
|
4
|
-
* Перший рядок `# yaml-language-server: $schema
|
|
4
|
+
* Перший рядок `# yaml-language-server: $schema=…` (URL за `https://`), без дублікатів, розширення `.yaml`
|
|
5
5
|
* (окрім `kustomization.yaml`); URL схеми за першим документом — kustomization / yannh / datree
|
|
6
6
|
* (**виняток:** `apiVersion: alb.yc.io/v1alpha1`, `kind: HttpBackendGroup` — рядка `# yaml-language-server:` у файлі бути не має).
|
|
7
7
|
* (datree за замовчуванням: GitHub Pages `https://datreeio.github.io/CRDs-catalog/…`).
|
|
8
8
|
*
|
|
9
|
+
* Modeline **опційний**: якщо публічної схеми немає (yannh/datree/schemastore не покривають це поєднання
|
|
10
|
+
* apiVersion/kind), залиш файл **без** рядка `# yaml-language-server: $schema=…` — `check-k8s` пропустить
|
|
11
|
+
* перевірку URL. **Заборонено** ставити `$schema=file:…` як заглушку (це фальшива валідація). Якщо modeline
|
|
12
|
+
* присутній, він має бути **першим рядком** і містити `https://` URL, що відповідає очікуваному за apiVersion/kind.
|
|
13
|
+
*
|
|
9
14
|
* Додатково: у кожному YAML-документі з **`kind: Deployment`** у кожного контейнера
|
|
10
15
|
* **`spec.template.spec.containers[]`** має бути **`resources.requests.cpu`** і **`resources.requests.memory`**
|
|
11
16
|
* (непорожні скаляри). У шарі **`…/k8s/…/base/…`** значення жорстко **`cpu: '0.02'`**, **`memory: '128Mi'`**
|
|
@@ -344,11 +349,23 @@ const BATCH_V1BETA1_API_VERSION_LINE_RE = /^(\s*apiVersion:\s*)["']?batch\/v1bet
|
|
|
344
349
|
|
|
345
350
|
/**
|
|
346
351
|
* Чи містить шлях сегмент директорії `k8s` (рівно ця назва компонента).
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
+
*
|
|
353
|
+
* Якщо передано `root`, перевірка ведеться **відносно** кореня репо — інакше випадає
|
|
354
|
+
* false-positive, коли сам корінь репо вже містить компонент `k8s` (напр.
|
|
355
|
+
* `/Users/.../abie/k8s/`): без relativize функція б повертала true для **усіх** файлів
|
|
356
|
+
* у проєкті, включно з `.github/workflows/*.yml`, які належать іншому правилу (`ga.mdc`).
|
|
357
|
+
*
|
|
358
|
+
* Без `root` (як у юніт-тестах або коли шлях уже відносний) спрацьовує старий шлях:
|
|
359
|
+
* розбиття за `/`/`\` і пошук компонента `k8s`.
|
|
360
|
+
* @param {string} filePath абсолютний або відносний шлях до файлу
|
|
361
|
+
* @param {string} [root] корінь репо для relativize (типово — без relativize)
|
|
362
|
+
* @returns {boolean} true, якщо серед компонентів шляху **відносно root** є каталог `k8s`
|
|
363
|
+
*/
|
|
364
|
+
export function pathHasK8sSegment(filePath, root) {
|
|
365
|
+
const target = root ? relative(root, filePath).replaceAll('\\', '/') : filePath
|
|
366
|
+
// Порожній relative означає сам root — у ньому компонента `k8s` відносно себе немає.
|
|
367
|
+
if (target === '') return false
|
|
368
|
+
const parts = target.split(PATH_SPLIT_RE)
|
|
352
369
|
return parts.includes('k8s')
|
|
353
370
|
}
|
|
354
371
|
|
|
@@ -1718,7 +1735,11 @@ async function findK8sYamlFiles(root, ignorePaths = []) {
|
|
|
1718
1735
|
await walkDir(
|
|
1719
1736
|
root,
|
|
1720
1737
|
p => {
|
|
1721
|
-
|
|
1738
|
+
const rel = relative(root, p).replaceAll('\\', '/')
|
|
1739
|
+
// `.github/` належить правилу `ga.mdc` (там канон — `.yml`); не зачіпай тут навіть
|
|
1740
|
+
// якщо `pathHasK8sSegment` колись зіб'ється на крайовому кейсі.
|
|
1741
|
+
if (rel.startsWith('.github/')) return
|
|
1742
|
+
if (!pathHasK8sSegment(p, root)) return
|
|
1722
1743
|
if (!YAML_EXTENSION_RE.test(p)) return
|
|
1723
1744
|
out.push(p)
|
|
1724
1745
|
},
|
|
@@ -2885,18 +2906,11 @@ export function collectGatewayApiRouteBackendRefsWithRedundantNamespace(spec, ro
|
|
|
2885
2906
|
return out
|
|
2886
2907
|
}
|
|
2887
2908
|
|
|
2888
|
-
/**
|
|
2889
|
-
* Один документ: маршрут Gateway API має посилатися на **Service** з суфіксом **`-hl`**;
|
|
2890
|
-
* у **`backendRef`** не має дублюватися **`namespace`**, що збігається з **`metadata.namespace`** маршруту.
|
|
2891
|
-
* @param {string} rel відносний шлях до файлу
|
|
2892
|
-
* @param {number} docIndex 1-based індекс документа
|
|
2893
|
-
* @param {Record<string, unknown>} rec корінь маніфесту
|
|
2894
|
-
* @param {(msg: string) => void} fail callback помилки
|
|
2895
|
-
* @returns {void}
|
|
2896
|
-
*/
|
|
2897
2909
|
// Plan B: Gateway API маршрут backendRef з суфіксом `-hl` і redundant namespace —
|
|
2898
2910
|
// у rego-пакеті `k8s.gateway`, виклик через `runAllK8sRego`. JS-функції
|
|
2899
|
-
// failIfGatewayRouteUsesNonHeadlessService, scanGatewayApiRouteBackendRefsInYamlBody
|
|
2911
|
+
// failIfGatewayRouteUsesNonHeadlessService, scanGatewayApiRouteBackendRefsInYamlBody видалено;
|
|
2912
|
+
// JSDoc-блок для них прибрано (eslint-plugin-jsdoc trip-ив на «orphan» JSDoc, який
|
|
2913
|
+
// прилипав до asPlainRecord як другий @returns).
|
|
2900
2914
|
|
|
2901
2915
|
/**
|
|
2902
2916
|
* Звузити `unknown` до `Record<string, unknown>` (`null`, масиви, примітиви → null).
|
|
@@ -3547,13 +3561,12 @@ function countSchemaModelines(lines) {
|
|
|
3547
3561
|
return lines.filter(l => OXLINT_SCHEMA_MODELINE_RE.test(l.trim())).length
|
|
3548
3562
|
}
|
|
3549
3563
|
|
|
3550
|
-
|
|
3551
3564
|
/**
|
|
3552
3565
|
* Файл з першим документом **HttpBackendGroup** (ALB Yandex): без modeline **$schema**.
|
|
3553
3566
|
* @param {string} rel відносний шлях
|
|
3554
|
-
* @param {string}
|
|
3555
|
-
* @param {string[]}
|
|
3556
|
-
* @param {(msg: string) => void}
|
|
3567
|
+
* @param {string} _baseLower basename (лишений для уніфікованої сигнатури `checkK8sYamlFile*`)
|
|
3568
|
+
* @param {string[]} _lines рядки файлу (лишені з тієї ж причини)
|
|
3569
|
+
* @param {(msg: string) => void} _fail реєстрація помилки (rego гейтує per-document)
|
|
3557
3570
|
* @param {(msg: string) => void} pass реєстрація успіху
|
|
3558
3571
|
* @returns {void}
|
|
3559
3572
|
*/
|
|
@@ -3593,8 +3606,13 @@ function checkK8sYamlFileWithSchemaModeline(abs, rel, baseLower, lines, fail, pa
|
|
|
3593
3606
|
// topologySpread, HCP, svc/svc-hl) — делегована rego, виконано у `runAllK8sRego` вище.
|
|
3594
3607
|
|
|
3595
3608
|
if (schemaUrl.startsWith('file:')) {
|
|
3596
|
-
|
|
3597
|
-
|
|
3609
|
+
fail(
|
|
3610
|
+
`${rel}: $schema=file:… заборонено (фальшива валідація без публічної схеми). ` +
|
|
3611
|
+
`Якщо публічної схеми для цього apiVersion/kind немає — прибери modeline зовсім (k8s.mdc)`
|
|
3612
|
+
)
|
|
3613
|
+
return
|
|
3614
|
+
}
|
|
3615
|
+
if (HTTPS_SCHEMA_RE.test(schemaUrl)) {
|
|
3598
3616
|
const doc = firstYamlDocument(body)
|
|
3599
3617
|
const { expected, reason } = expectedSchemaUrl(abs, doc)
|
|
3600
3618
|
|
|
@@ -3610,7 +3628,9 @@ function checkK8sYamlFileWithSchemaModeline(abs, rel, baseLower, lines, fail, pa
|
|
|
3610
3628
|
|
|
3611
3629
|
pass(`${rel}: $schema узгоджено (${reason})`)
|
|
3612
3630
|
} else {
|
|
3613
|
-
fail(
|
|
3631
|
+
fail(
|
|
3632
|
+
`${rel}: $schema має бути https URL (file: і інші схеми заборонені — якщо публічної схеми немає, прибери modeline; k8s.mdc)`
|
|
3633
|
+
)
|
|
3614
3634
|
}
|
|
3615
3635
|
}
|
|
3616
3636
|
|
|
@@ -3641,12 +3661,7 @@ async function checkK8sYamlFile(abs, root, fail, pass) {
|
|
|
3641
3661
|
}
|
|
3642
3662
|
|
|
3643
3663
|
const lines = toLines(raw)
|
|
3644
|
-
|
|
3645
|
-
fail(`${rel}: перший рядок порожній — потрібен # yaml-language-server: $schema=…`)
|
|
3646
|
-
return
|
|
3647
|
-
}
|
|
3648
|
-
|
|
3649
|
-
const firstLineIsModeline = MODELINE_RE.test(lines[0])
|
|
3664
|
+
const firstLineIsModeline = lines.length > 0 && MODELINE_RE.test(lines[0])
|
|
3650
3665
|
const bodyForFirstDoc = k8sYamlBodyForDocumentParse(lines)
|
|
3651
3666
|
const isAlbHttpBackendGroup = k8sYamlFirstDocIsAlbYcHttpBackendGroup(bodyForFirstDoc)
|
|
3652
3667
|
|
|
@@ -3668,7 +3683,14 @@ async function checkK8sYamlFile(abs, root, fail, pass) {
|
|
|
3668
3683
|
}
|
|
3669
3684
|
|
|
3670
3685
|
if (!firstLineIsModeline) {
|
|
3671
|
-
|
|
3686
|
+
// Modeline опційний: дозволено, якщо публічної схеми для apiVersion/kind немає (k8s.mdc).
|
|
3687
|
+
// Але `# yaml-language-server: $schema=…` дозволено **лише** у першому рядку — якщо він
|
|
3688
|
+
// зустрічається нижче, це порушення (yaml-language-server чекає на нього у заголовку файлу).
|
|
3689
|
+
if (countSchemaModelines(lines) > 0) {
|
|
3690
|
+
fail(`${rel}: рядок # yaml-language-server: $schema=… має бути першим у файлі (без префіксів перед #; k8s.mdc)`)
|
|
3691
|
+
return
|
|
3692
|
+
}
|
|
3693
|
+
pass(`${rel}: без modeline — перевірка $schema пропущена (немає публічної схеми; k8s.mdc)`)
|
|
3672
3694
|
return
|
|
3673
3695
|
}
|
|
3674
3696
|
|
|
@@ -5949,6 +5971,10 @@ function runAllK8sRego(root, yamlFiles, fail) {
|
|
|
5949
5971
|
}
|
|
5950
5972
|
}
|
|
5951
5973
|
|
|
5974
|
+
/**
|
|
5975
|
+
* Точка входу `check k8s`: повний набір перевірок маніфестів і структури `…/k8s` (див. JSDoc на початку файлу).
|
|
5976
|
+
* @returns {Promise<number>} `process.exitCode`: 0 при успіху, 1 при будь-якому `fail(...)`
|
|
5977
|
+
*/
|
|
5952
5978
|
export async function check() {
|
|
5953
5979
|
const reporter = createCheckReporter()
|
|
5954
5980
|
const { pass, fail } = reporter
|
package/scripts/run-k8s.mjs
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* Kubescape не має аналога цього прапорця; орієнтир цільового кластера — та сама лінія релізу (див. k8s.mdc).
|
|
14
14
|
*/
|
|
15
15
|
import { spawnSync } from 'node:child_process'
|
|
16
|
-
import { basename, dirname } from 'node:path'
|
|
16
|
+
import { basename, dirname, relative } from 'node:path'
|
|
17
17
|
|
|
18
18
|
import { isRunAsCli } from './cli-entry.mjs'
|
|
19
19
|
import { loadCursorIgnorePaths } from './utils/load-cursor-config.mjs'
|
|
@@ -31,12 +31,19 @@ const DATREE_CRD_SCHEMA_LOCATION =
|
|
|
31
31
|
'https://datreeio.github.io/CRDs-catalog/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json'
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* Чи містить шлях сегмент директорії `k8s
|
|
35
|
-
*
|
|
36
|
-
*
|
|
34
|
+
* Чи містить шлях сегмент директорії `k8s` (відносно `root`, якщо передано).
|
|
35
|
+
*
|
|
36
|
+
* Якщо корінь репо сам має компонент `k8s` (напр. `/Users/.../abie/k8s/`), без relativize
|
|
37
|
+
* функція повертала б true для **усіх** файлів проєкту — включно з `.github/workflows/*.yml`,
|
|
38
|
+
* які належать `ga.mdc`. Передавай `root` у викликах з walkDir щоб уникнути false-positive.
|
|
39
|
+
* @param {string} filePath абсолютний або відносний шлях до файлу
|
|
40
|
+
* @param {string} [root] корінь репо для relativize (типово — без relativize)
|
|
41
|
+
* @returns {boolean} true, якщо серед компонентів шляху **відносно root** є каталог `k8s`
|
|
37
42
|
*/
|
|
38
|
-
export function pathHasK8sSegment(filePath) {
|
|
39
|
-
const
|
|
43
|
+
export function pathHasK8sSegment(filePath, root) {
|
|
44
|
+
const target = root ? relative(root, filePath).replaceAll('\\', '/') : filePath
|
|
45
|
+
if (target === '') return false
|
|
46
|
+
const parts = target.split(PATH_SEPARATOR_RE)
|
|
40
47
|
return parts.includes('k8s')
|
|
41
48
|
}
|
|
42
49
|
|
|
@@ -68,7 +75,10 @@ export async function findK8sRoots(root, ignorePaths = []) {
|
|
|
68
75
|
await walkDir(
|
|
69
76
|
root,
|
|
70
77
|
p => {
|
|
71
|
-
|
|
78
|
+
const rel = relative(root, p).replaceAll('\\', '/')
|
|
79
|
+
// `.github/` належить `ga.mdc`; kubeconform/kubescape там не запускаємо.
|
|
80
|
+
if (rel.startsWith('.github/')) return
|
|
81
|
+
if (!pathHasK8sSegment(p, root)) return
|
|
72
82
|
if (!YAML_EXT_RE.test(p)) return
|
|
73
83
|
const k8sRoot = k8sRootFromFile(p)
|
|
74
84
|
if (k8sRoot) roots.add(k8sRoot)
|
|
@@ -30,7 +30,7 @@ const POLICY_ROOT = join(PACKAGE_ROOT, 'policy')
|
|
|
30
30
|
/**
|
|
31
31
|
* Друкує install-hint для conftest і кидає виняток, щоб викликана `check-*`
|
|
32
32
|
* команда ясно завершилась з кодом 1.
|
|
33
|
-
* @returns {never}
|
|
33
|
+
* @returns {never} завжди кидає; для точки виклику — non-returning
|
|
34
34
|
*/
|
|
35
35
|
function failConftestMissing() {
|
|
36
36
|
throw new Error(
|
|
@@ -94,9 +94,7 @@ export function runConftestBatch(opts) {
|
|
|
94
94
|
}
|
|
95
95
|
// conftest exit 1 = є failures (це валідно для нас); >1 = справжня помилка.
|
|
96
96
|
if (result.status !== 0 && result.status !== 1) {
|
|
97
|
-
throw new Error(
|
|
98
|
-
`conftest exit ${result.status}: ${(result.stderr || result.stdout || '').slice(0, 500)}`
|
|
99
|
-
)
|
|
97
|
+
throw new Error(`conftest exit ${result.status}: ${(result.stderr || result.stdout || '').slice(0, 500)}`)
|
|
100
98
|
}
|
|
101
99
|
/** @type {Array<{ filename: string, namespace: string, failures?: Array<{ msg: string }> }>} */
|
|
102
100
|
let parsed
|