@7n/rules 1.88.0 → 1.88.2

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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.88.2] - 2026-08-08
4
+
5
+ ### Changed
6
+
7
+ - `k8s/manifests` — третій зріз порту в `rules-core`, після якого **весь** `lint()` концерну має native-дзеркало. Портовано kubescape-контур (`findKustomizationDirs` + `kubectl kustomize`, auto-exceptions C-0056/C-0018 для probe у `Job`/`CronJob`, користувацький `.kubescape-exceptions.json`, 5-хвилинний wall-clock ліміт із власним текстом порушення) і kustomize-резолюцію з пʼятьма залежними `validate*` (`collectResourceDescriptorsForKustomizationWalk`, `kustomizeResourceTreeHpaPdbDeploymentFlags`, `prodOverlayHpaPdbOverrideNeeds`, `kustomizationTreeHasHasuraDeployment`). Концерн у `NATIVE_CONCERNS` як і раніше **не** заведено — гейт `check-mjs-contract` вимагає, щоб у native-концерну не було `main.mjs`, тобто заведення в реєстр і видалення JS-канону є одним неподільним кроком, який чекає на порт fix-поверхні; поведінка лінту незмінна.
8
+
9
+ Полагоджено дефект канону: **обидва детектори застарілих `apiVersion` не спрацьовували ніколи**. `BATCH_V1BETA1_API_VERSION_LINE_RE` і `GATEWAY_HTTPROUTE_V1BETA1_LINE_RE` — рядкові якірні регулярні вирази (`^…$` без прапорця `m`), і саме так їх застосовує T0-фікс (`rewriteLine*` — по одному рядку), але `detectBatchV1beta1InK8sYamlFiles` і `detectGatewayHttpRouteV1beta1InK8sYamlFiles` звіряли їх із **усім** текстом файла. Такий збіг можливий лише на файлі, що цілком складається з одного рядка `apiVersion: batch/v1beta1`, тобто ніколи на справжньому маніфесті: заборона `batch/v1beta1` і `gateway.networking.k8s.io/v1beta1` була мертвою, попри повідомлення, T0-патерни `fix-manifests.mjs` і розділи `manifest.mdc` / `gateway.mdc`, що обіцяють автоматичне переписування. Тепер обидва детектори звіряють regex по рядках — так само, як сусідня перевірка `kind: HTTPRoute` робила від початку. **Зміна поведінки, напрямок fail-closed:** маніфести під `k8s` із застарілими `apiVersion: batch/v1beta1` та `apiVersion: gateway.networking.k8s.io/v1beta1` у `HTTPRoute` тепер дають порушення (з fix-hint, тож `--fix` їх виправляє), а раніше мовчки проходили.
10
+
11
+ ## [1.88.1] - 2026-08-07
12
+
13
+ ### Changed
14
+
15
+ - chore(release): синк пінів платформних napi-пакетів @7n/rules
16
+
3
17
  ## [1.88.0] - 2026-08-05
4
18
 
5
19
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@7n/rules",
3
- "version": "1.88.0",
3
+ "version": "1.88.2",
4
4
  "description": "CLI еталонних правил і skills (префікс n-): синк у репозиторій, дельта-lint, конформність",
5
5
  "keywords": [
6
6
  "cli",
@@ -66,7 +66,7 @@
66
66
  "rename-yaml-extensions": "bun ./bin/n-rules.js rename-yaml-extensions"
67
67
  },
68
68
  "dependencies": {
69
- "@7n/llm-lib": "3.0.1",
69
+ "@7n/llm-lib": "3.0.3",
70
70
  "ajv": "^8.20.0",
71
71
  "cli-progress": "^3.12.0",
72
72
  "github-actionlint": "^1.7.12",
@@ -83,9 +83,9 @@
83
83
  "zod": "^4.4.3"
84
84
  },
85
85
  "optionalDependencies": {
86
- "@7n/rules-darwin-arm64": "1.88.0",
87
- "@7n/rules-linux-x64": "1.88.0",
88
- "@7n/rules-win32-x64": "1.88.0",
86
+ "@7n/rules-darwin-arm64": "1.88.2",
87
+ "@7n/rules-linux-x64": "1.88.2",
88
+ "@7n/rules-win32-x64": "1.88.2",
89
89
  "@earendil-works/pi-ai": "0.80.10",
90
90
  "@earendil-works/pi-coding-agent": "0.80.10"
91
91
  },
@@ -1548,7 +1548,7 @@ async function validatePatchTargetsOneKustomizationFile(root, kustAbs, rootNorm,
1548
1548
  * @param {(msg: string) => void} fail реєстрація помилки
1549
1549
  * @returns {Promise<void>} результат
1550
1550
  */
1551
- async function validateKustomizationPatchTargetsResolved(root, yamlFilesAbs, fail) {
1551
+ export async function validateKustomizationPatchTargetsResolved(root, yamlFilesAbs, fail) {
1552
1552
  const rootNorm = resolve(root)
1553
1553
  const kustFiles = yamlFilesAbs.filter(p => basename(p).toLowerCase() === 'kustomization.yaml')
1554
1554
  for (const kustAbs of kustFiles) {
@@ -1770,12 +1770,16 @@ export function replaceGatewayHttpRouteV1beta1ApiVersionInYamlText(raw) {
1770
1770
  /**
1771
1771
  * Read-only детектор: знаходить файли з `apiVersion: gateway.networking.k8s.io/v1beta1` + `kind: HTTPRoute`
1772
1772
  * і реєструє violation з `data: { kind: 'gateway-httproute-v1beta1' }` для T0-фіксу.
1773
+ *
1774
+ * `GATEWAY_HTTPROUTE_V1BETA1_LINE_RE` — **рядкова** якірна (`^…$` без прапорця `m`), тож
1775
+ * звіряти її треба з окремим рядком, а не з усім текстом файлу (див. коментар-близнюк у
1776
+ * `detectBatchV1beta1InK8sYamlFiles`).
1773
1777
  * @param {string[]} yamlFiles абсолютні шляхи до YAML-файлів
1774
1778
  * @param {string} root корінь репозиторію
1775
1779
  * @param {(msg: string, opts?: object) => void} fail колбек реєстрації порушення
1776
1780
  * @returns {Promise<void>}
1777
1781
  */
1778
- async function detectGatewayHttpRouteV1beta1InK8sYamlFiles(yamlFiles, root, fail) {
1782
+ export async function detectGatewayHttpRouteV1beta1InK8sYamlFiles(yamlFiles, root, fail) {
1779
1783
  for (const abs of yamlFiles) {
1780
1784
  const rel = (relative(root, abs) || abs).replaceAll('\\', '/')
1781
1785
  let raw
@@ -1784,9 +1788,10 @@ async function detectGatewayHttpRouteV1beta1InK8sYamlFiles(yamlFiles, root, fail
1784
1788
  } catch {
1785
1789
  continue
1786
1790
  }
1787
- if (!GATEWAY_HTTPROUTE_V1BETA1_LINE_RE.test(raw)) continue
1788
1791
  const body = raw.startsWith('') ? raw.slice(1) : raw
1789
- const hasHttpRouteKind = body.split(YAML_LINE_SPLIT_RE).some(l => HTTPROUTE_KIND_LINE_RE.test(l))
1792
+ const lines = body.split(YAML_LINE_SPLIT_RE)
1793
+ if (lines.every(l => !GATEWAY_HTTPROUTE_V1BETA1_LINE_RE.test(l))) continue
1794
+ const hasHttpRouteKind = lines.some(l => HTTPROUTE_KIND_LINE_RE.test(l))
1790
1795
  if (!hasHttpRouteKind) continue
1791
1796
  fail(
1792
1797
  `${rel}: apiVersion: gateway.networking.k8s.io/v1beta1 заборонено для HTTPRoute — оновіть до gateway.networking.k8s.io/v1 (k8s.mdc)`,
@@ -1798,12 +1803,18 @@ async function detectGatewayHttpRouteV1beta1InK8sYamlFiles(yamlFiles, root, fail
1798
1803
  /**
1799
1804
  * Read-only детектор: знаходить файли з `apiVersion: batch/v1beta1` (застаріле для CronJob/Job)
1800
1805
  * і реєструє violation з `data: { kind: 'batch-v1beta1-apiversion' }` для T0-фіксу.
1806
+ *
1807
+ * `BATCH_V1BETA1_API_VERSION_LINE_RE` — **рядкова** якірна (`^…$` без прапорця `m`), і саме так
1808
+ * її застосовує T0-фікс (`rewriteLineBatchV1beta1ApiVersion` — по одному рядку). Звірка тієї ж
1809
+ * якірної regex із **усім** текстом файлу збігалася б лише на файлі, що цілком складається з
1810
+ * одного рядка `apiVersion: batch/v1beta1`, тобто ніколи на справжньому маніфесті — детектор
1811
+ * мовчав, а T0-фікс не мав чого чинити.
1801
1812
  * @param {string[]} yamlFiles абсолютні шляхи до YAML-файлів
1802
1813
  * @param {string} root корінь репозиторію
1803
1814
  * @param {(msg: string, opts?: object) => void} fail колбек реєстрації порушення
1804
1815
  * @returns {Promise<void>}
1805
1816
  */
1806
- async function detectBatchV1beta1InK8sYamlFiles(yamlFiles, root, fail) {
1817
+ export async function detectBatchV1beta1InK8sYamlFiles(yamlFiles, root, fail) {
1807
1818
  for (const abs of yamlFiles) {
1808
1819
  const rel = (relative(root, abs) || abs).replaceAll('\\', '/')
1809
1820
  let raw
@@ -1812,7 +1823,8 @@ async function detectBatchV1beta1InK8sYamlFiles(yamlFiles, root, fail) {
1812
1823
  } catch {
1813
1824
  continue
1814
1825
  }
1815
- if (!BATCH_V1BETA1_API_VERSION_LINE_RE.test(raw)) continue
1826
+ const body = raw.startsWith('') ? raw.slice(1) : raw
1827
+ if (body.split(YAML_LINE_SPLIT_RE).every(l => !BATCH_V1BETA1_API_VERSION_LINE_RE.test(l))) continue
1816
1828
  fail(`${rel}: apiVersion: batch/v1beta1 застаріло — оновіть до batch/v1 (k8s.mdc)`, {
1817
1829
  reason: 'batch-v1beta1-apiversion',
1818
1830
  file: rel,
@@ -4618,7 +4630,7 @@ async function checkOverlayRefHpaPdb(root, kustDir, rel, ref, baseDirs, anyBaseH
4618
4630
  * @param {(msg: string) => void} passFn pass
4619
4631
  * @returns {Promise<void>} результат
4620
4632
  */
4621
- async function validateKustomizeHpaPdbOnlyWithBaseDeployment(root, yamlFilesAbs, fail, passFn) {
4633
+ export async function validateKustomizeHpaPdbOnlyWithBaseDeployment(root, yamlFilesAbs, fail, passFn) {
4622
4634
  const rootNorm = resolve(root)
4623
4635
  /**
4624
4636
  @type {Map<string, Promise<{ hasDeployment: boolean, hasHpa: boolean, hasPdb: boolean }>>}
@@ -4744,7 +4756,7 @@ export async function prodOverlayHpaPdbOverrideNeeds(rootNorm, kustAbs) {
4744
4756
  * @param {(msg: string) => void} fail callback при помилці
4745
4757
  * @param {(msg: string) => void} passFn callback при успіху
4746
4758
  */
4747
- async function validateProdKustomizationOverrides(root, yamlFilesAbs, fail, passFn) {
4759
+ export async function validateProdKustomizationOverrides(root, yamlFilesAbs, fail, passFn) {
4748
4760
  const rootNorm = resolve(root)
4749
4761
  const kustFiles = yamlFilesAbs.filter(abs => basename(abs) === 'kustomization.yaml')
4750
4762
  for (const kustAbs of kustFiles) {
@@ -4926,7 +4938,7 @@ export function hasuraEnabledLogTypesOverrideValue(kust) {
4926
4938
  * @param {(msg: string) => void} fail callback при помилці
4927
4939
  * @param {(msg: string) => void} passFn callback при успіху
4928
4940
  */
4929
- async function validateHasuraOverlayEnabledApisOverride(root, yamlFilesAbs, fail, passFn) {
4941
+ export async function validateHasuraOverlayEnabledApisOverride(root, yamlFilesAbs, fail, passFn) {
4930
4942
  const rootNorm = resolve(root)
4931
4943
  const kustFiles = yamlFilesAbs.filter(abs => basename(abs) === 'kustomization.yaml')
4932
4944
  for (const kustAbs of kustFiles) {
@@ -4963,7 +4975,7 @@ async function validateHasuraOverlayEnabledApisOverride(root, yamlFilesAbs, fail
4963
4975
  * @param {(msg: string) => void} fail callback при помилці
4964
4976
  * @param {(msg: string) => void} passFn callback при успіху
4965
4977
  */
4966
- async function validateHasuraOverlayEnabledLogTypesOverride(root, yamlFilesAbs, fail, passFn) {
4978
+ export async function validateHasuraOverlayEnabledLogTypesOverride(root, yamlFilesAbs, fail, passFn) {
4967
4979
  const rootNorm = resolve(root)
4968
4980
  const kustFiles = yamlFilesAbs.filter(abs => basename(abs) === 'kustomization.yaml')
4969
4981
  for (const kustAbs of kustFiles) {