@7n/rules 1.88.1 → 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 +8 -0
- package/package.json +5 -5
- package/rules/k8s/manifests/main.mjs +22 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
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
|
+
|
|
3
11
|
## [1.88.1] - 2026-08-07
|
|
4
12
|
|
|
5
13
|
### Changed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@7n/rules",
|
|
3
|
-
"version": "1.88.
|
|
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.
|
|
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.
|
|
87
|
-
"@7n/rules-linux-x64": "1.88.
|
|
88
|
-
"@7n/rules-win32-x64": "1.88.
|
|
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
|
|
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
|
-
|
|
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) {
|