@7n/rules 1.7.0 → 1.7.1
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 +1 -1
- package/rules/abie/http_route_base/http_route_base.mdc +23 -1
- package/rules/adr/madr_format/concern.json +1 -0
- package/rules/adr/madr_format/madr_format.mdc +119 -0
- package/rules/bun/bunfig/bunfig.mdc +5 -0
- package/rules/bun/lint-surface/concern.json +3 -0
- package/rules/bun/lint-surface/lint-surface.mdc +13 -0
- package/rules/capacitor/platforms/docs/main.md +1 -1
- package/rules/capacitor/platforms/main.mjs +5 -1
- package/rules/capacitor/platforms/platforms.mdc +108 -0
- package/rules/changelog/consistency/comparison-models.mdc +46 -0
- package/rules/changelog/consistency/consistency.mdc +35 -0
- package/rules/docker/main.mdc +235 -2
- package/rules/image-avif/avif_generation/avif_generation.mdc +14 -0
- package/rules/js/check/check.mdc +26 -0
- package/rules/js/file-extensions/concern.json +3 -0
- package/rules/js/file-extensions/file-extensions.mdc +12 -0
- package/rules/js/jscpd_config/jscpd_config.mdc +28 -0
- package/rules/js/knip/knip.mdc +15 -0
- package/rules/js/utils_imports/utils_imports.mdc +15 -0
- package/rules/js-bun-db/connection/concern.json +3 -0
- package/rules/js-bun-db/connection/connection.mdc +42 -0
- package/rules/js-bun-db/package_json/package_json.mdc +15 -1
- package/rules/js-bun-db/pg_format_identifiers/concern.json +3 -0
- package/rules/js-bun-db/pg_format_identifiers/pg_format_identifiers.mdc +104 -0
- package/rules/js-bun-db/safety/safety.mdc +458 -0
- package/rules/js-mssql/main.mdc +130 -0
- package/rules/js-mssql/mssql-tvp/concern.json +3 -0
- package/rules/js-mssql/mssql-tvp/mssql-tvp.mdc +77 -0
- package/rules/js-run/configmap/configmap.mdc +6 -0
- package/rules/js-run/jsconfig/jsconfig.mdc +23 -0
- package/rules/js-run/package_json/package_json.mdc +6 -0
- package/rules/js-run/project-structure/concern.json +3 -0
- package/rules/js-run/project-structure/project-structure.mdc +11 -0
- package/rules/js-run/runtime/runtime.mdc +170 -0
- package/rules/js-run/scope/concern.json +3 -0
- package/rules/js-run/scope/scope.mdc +11 -0
- package/rules/k8s/hasura_configmap/hasura_configmap.mdc +6 -0
- package/rules/k8s/hpa_pdb/hpa_pdb.mdc +134 -0
- package/rules/k8s/kubeconform/kubeconform.mdc +38 -0
- package/rules/k8s/kustomization/kustomization.mdc +73 -0
- package/rules/k8s/main.mdc +68 -0
- package/rules/k8s/manifest/manifest.mdc +37 -0
- package/rules/k8s/manifests/docs/fix-manifests.md +3 -1
- package/rules/k8s/manifests/fix-manifests.mjs +11 -0
- package/rules/k8s/manifests/main.mjs +28 -0
- package/rules/k8s/network_policy/network_policy.mdc +33 -0
- package/rules/nginx-default-tpl/http-route/concern.json +1 -0
- package/rules/nginx-default-tpl/http-route/http-route.mdc +54 -0
- package/rules/nginx-default-tpl/template/template.mdc +152 -0
- package/rules/php/tooling/tooling.mdc +7 -6
- package/rules/python/pyproject_toml/pyproject_toml.mdc +17 -1
- package/rules/python/tooling/tooling.mdc +9 -10
- package/rules/rego/main.mdc +14 -0
- package/rules/rust/check/check.mdc +16 -0
- package/rules/style/admin_table/admin_table.mdc +88 -0
- package/rules/style/admin_table/concern.json +7 -0
- package/rules/style/admin_table/docs/index.md +9 -0
- package/rules/style/admin_table/docs/main.md +14 -0
- package/rules/style/admin_table/main.mjs +46 -0
- package/rules/style/colors/colors.mdc +21 -0
- package/rules/style/colors/concern.json +3 -0
- package/rules/style/gap/concern.json +7 -0
- package/rules/style/gap/docs/index.md +9 -0
- package/rules/style/gap/docs/main.md +15 -0
- package/rules/style/gap/gap.mdc +22 -0
- package/rules/style/gap/main.mjs +51 -0
- package/rules/style/quasar/concern.json +3 -0
- package/rules/style/quasar/quasar.mdc +7 -0
- package/rules/style/quasar_fixes/concern.json +7 -0
- package/rules/style/quasar_fixes/docs/index.md +9 -0
- package/rules/style/quasar_fixes/docs/main.md +16 -0
- package/rules/style/quasar_fixes/main.mjs +57 -0
- package/rules/style/quasar_fixes/quasar_fixes.mdc +32 -0
- package/rules/tauri/tool_surface/concern.json +16 -0
- package/rules/tauri/tool_surface/docs/index.md +9 -0
- package/rules/tauri/tool_surface/docs/main.md +24 -0
- package/rules/tauri/tool_surface/main.mjs +145 -0
- package/rules/tauri/tool_surface/tool_surface.mdc +29 -0
- package/rules/test/vitest-api-conventions/concern.json +7 -0
- package/rules/test/vitest-api-conventions/docs/index.md +9 -0
- package/rules/test/vitest-api-conventions/docs/main.md +40 -0
- package/rules/test/vitest-api-conventions/main.mjs +186 -0
- package/rules/test/vitest-api-conventions/vitest-api-conventions.mdc +129 -0
- package/rules/text/cspell/cspell.mdc +18 -0
- package/rules/text/markdownlint/markdownlint.mdc +4 -0
- package/rules/text/run-dotenv-linter/run-dotenv-linter.mdc +17 -0
- package/rules/text/run-shellcheck/run-shellcheck.mdc +17 -0
- package/rules/text/run-v8r/run-v8r.mdc +23 -0
- package/rules/vue/composition-api/composition-api.mdc +82 -0
- package/rules/vue/composition-api/concern.json +3 -0
- package/rules/vue/main.mdc +1 -1
- package/rules/vue/nheader-layout/concern.json +3 -0
- package/rules/vue/nheader-layout/nheader-layout.mdc +171 -0
- package/rules/vue/packages/packages.mdc +56 -0
- package/rules/vue/quasar-ui/concern.json +3 -0
- package/rules/vue/quasar-ui/quasar-ui.mdc +32 -0
- package/rules/vue/structure/concern.json +3 -0
- package/rules/vue/structure/structure.mdc +101 -0
- package/rules/vue/testing/concern.json +3 -0
- package/rules/vue/testing/testing.mdc +40 -0
- package/rules/vue/tfm-translations/concern.json +7 -0
- package/rules/vue/tfm-translations/docs/main.md +29 -0
- package/rules/vue/tfm-translations/main.mjs +55 -0
- package/rules/vue/tfm-translations/tfm-translations.mdc +32 -0
- package/rules/vue/vite-config/concern.json +3 -0
- package/rules/vue/vite-config/vite-config.mdc +153 -0
- package/rules/vue/vite-env/concern.json +3 -0
- package/rules/vue/vite-env/vite-env.mdc +61 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
## TVP (table-valued parameters) — рекомендований підхід для списків
|
|
2
|
+
|
|
3
|
+
TVP дозволений і рекомендований для SQL Server 2019. Для списків значень та пар ключів **найкращий** шлях по безпеці/продуктивності — TVP.
|
|
4
|
+
|
|
5
|
+
Позитивна практика без прямого AST-check: антипатерн (динамічний список через `.join(...)` у `IN (...)`/`VALUES (...)`) ловить `findUnsafeMssqlDynamicSqlListInText` (`lib/mssql-pool-scan.mjs`, див. `main.mdc`), а сам "правильний" спосіб побудови TVP код не сканує.
|
|
6
|
+
|
|
7
|
+
### 1) `IN (...)` для кодів → `JOIN` на TVP
|
|
8
|
+
|
|
9
|
+
Замість складання SQL-рядка:
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
// ❌ НЕ МОЖНА: динамічний список у SQL
|
|
13
|
+
await pool.request().query`
|
|
14
|
+
SELECT *
|
|
15
|
+
FROM promo.SomeTable t
|
|
16
|
+
WHERE t.ExternalCode IN (${codes.map(c => `'${c}'`).join(',')})
|
|
17
|
+
`;
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Роби TVP з 1 колонкою:
|
|
21
|
+
|
|
22
|
+
- створити `new sql.Table()`
|
|
23
|
+
- `columns.add('ExternalCode', sql.NVarChar(N))`
|
|
24
|
+
- `rows.add(code)` після `trim()` + валідації
|
|
25
|
+
- `request.input('codes', table)`
|
|
26
|
+
- в SQL: `JOIN @codes c ON c.ExternalCode = t.ExternalCode`
|
|
27
|
+
|
|
28
|
+
Плюси:
|
|
29
|
+
|
|
30
|
+
- 0% SQL injection
|
|
31
|
+
- текст SQL **не росте** від довжини масиву
|
|
32
|
+
- SQL Server часто оптимізує `JOIN` краще, ніж гігантський `IN (...)`
|
|
33
|
+
|
|
34
|
+
### 2) `DELETE ... VALUES (...)` / `MERGE ... VALUES (...)` → TVP з 2 колонками
|
|
35
|
+
|
|
36
|
+
Замість генерації списку рядків `(...),(...),...` у SQL (навіть якщо воно "через tagged template"):
|
|
37
|
+
|
|
38
|
+
- сформуй TVP-таблицю `@Pairs` з колонками:
|
|
39
|
+
- `PromoActivitiesId` (INT або BIGINT — як у схемі БД)
|
|
40
|
+
- `SupplierOutletId` (INT або BIGINT — як у схемі БД)
|
|
41
|
+
|
|
42
|
+
Delete:
|
|
43
|
+
|
|
44
|
+
```sql
|
|
45
|
+
DELETE abi
|
|
46
|
+
FROM promo.ActivitiesByOutlet abi
|
|
47
|
+
JOIN @Pairs p
|
|
48
|
+
ON p.PromoActivitiesId = abi.PromoActivitiesId
|
|
49
|
+
AND p.SupplierOutletId = abi.SupplierOutletId;
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Insert (без `MERGE`, простіше і безпечніше):
|
|
53
|
+
|
|
54
|
+
```sql
|
|
55
|
+
INSERT INTO promo.ActivitiesByOutlet (PromoActivitiesId, SupplierOutletId, Status)
|
|
56
|
+
SELECT p.PromoActivitiesId, p.SupplierOutletId, 2
|
|
57
|
+
FROM @Pairs p
|
|
58
|
+
WHERE NOT EXISTS (
|
|
59
|
+
SELECT 1
|
|
60
|
+
FROM promo.ActivitiesByOutlet abi
|
|
61
|
+
WHERE abi.PromoActivitiesId = p.PromoActivitiesId
|
|
62
|
+
AND abi.SupplierOutletId = p.SupplierOutletId
|
|
63
|
+
);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Плюси:
|
|
67
|
+
|
|
68
|
+
- не збираєш SQL-рядок із даних
|
|
69
|
+
- менше шансів упіймати edge-case `MERGE` (у SQL Server історично багато "сюрпризів")
|
|
70
|
+
|
|
71
|
+
### Мінімальна валідація перед наповненням TVP
|
|
72
|
+
|
|
73
|
+
Навіть з TVP потрібно:
|
|
74
|
+
|
|
75
|
+
- `ExternalCode`: `typeof === 'string'`, `trim()`, довжина `<= N` (під схему), відкинути пусті.
|
|
76
|
+
- ліміт на кількість елементів (наприклад 5k/10k — залежить від вашого потоку).
|
|
77
|
+
- `supplierId`/ID: число/BigInt, валідне та скінченне.
|
|
@@ -29,3 +29,9 @@ data:
|
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
Ресурси типу `kind: Deployment` та інші non-ConfigMap — ігноруються.
|
|
32
|
+
|
|
33
|
+
### Kustomize-оверлеї: `service.namespace` має відповідати namespace оверлею (не автоматизовано)
|
|
34
|
+
|
|
35
|
+
Конвенція, **не** покрита автоматичною перевіркою — звіряй вручну під час рев'ю: у директоріях з kustomize-оверлеями значення `OTEL_RESOURCE_ATTRIBUTES` повинні бути перевизначені, і в них `service.namespace` повинен відповідати namespace, у якому знаходиться дана директорія оверлею (наприклад, `k8s/overlays/staging/` → `service.namespace=staging`).
|
|
36
|
+
|
|
37
|
+
Rego-gate вище цього не звіряє — він лише перевіряє наявність обох substring-маркерів у кожному ConfigMap незалежно від директорії, тому розбіжність `service.namespace` між `base` і kustomize-оверлеями пройде перевірку мовчки. Кросс-файлова резолюція kustomize-дерева (`base` + `overlays`) — складніша задача, вже реалізована для інших k8s-перевірок у `npm/rules/k8s/manifests/main.mjs`; переносити цю логіку в `js-run` окремо не варто, поки немає підтвердженого запиту на автоматизацію саме цього кейсу.
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
## `jsconfig.json` (редактор / перевірка типів)
|
|
2
|
+
|
|
3
|
+
Якщо в **backend** workspace-пакеті (без `vite` у `devDependencies`) є каталог **`src/`**, у **корені цього пакета** має бути **`jsconfig.json`**. Якщо файлу ще немає — створи його з таким вмістом (канон js-run):
|
|
4
|
+
|
|
5
|
+
```json title="jsconfig.json"
|
|
6
|
+
{
|
|
7
|
+
"compilerOptions": {
|
|
8
|
+
"lib": ["esnext"],
|
|
9
|
+
"module": "NodeNext",
|
|
10
|
+
"moduleResolution": "NodeNext",
|
|
11
|
+
"target": "esnext",
|
|
12
|
+
"checkJs": false
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"]
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Канон і повний перелік перевірених полів — див. розділ нижче **Rego-gate: jsconfig.json**, канонічний файл: [jsconfig.json.snippet.json](./template/jsconfig.json.snippet.json).
|
|
19
|
+
|
|
20
|
+
Якщо пакет не слідує структурі з `src/` (наприклад, лише `scripts/` у корені) — ця вимога не застосовується; для типових сервісів із `src/` файл обов'язковий і має збігатися з каноном.
|
|
21
|
+
|
|
22
|
+
Fs-existence створення `jsconfig.json`, коли його бракує, — T0-автофікс (`../runtime/fix-runtime.mjs`); структурну звірку канону виконує rego-gate нижче.
|
|
23
|
+
|
|
1
24
|
## Rego-gate: jsconfig.json
|
|
2
25
|
|
|
3
26
|
Rego-пакет: `js-run.jsconfig`
|
|
@@ -36,3 +36,9 @@ Rego-пакет: `js-run.package_json`
|
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
Пакети з `vite` у `devDependencies` — frontend, поза областю js-run, перевірка scripts не застосовується.
|
|
39
|
+
|
|
40
|
+
## Використання @nitra/pino для логування
|
|
41
|
+
|
|
42
|
+
Проект використовує @nitra/pino для логування. Якщо в проекті присутній @nitra/bunyan, то він повинен бути замінений на @nitra/pino — як у `package.json`, так і в коді: усі `import` / `require` / динамічні `import()` з `@nitra/bunyan` (і застарілого `bunyan`) треба замінити на `@nitra/pino` і за потреби адаптувати виклики під його API.
|
|
43
|
+
|
|
44
|
+
Заборона `bunyan` / `@nitra/bunyan` у `dependencies` / `devDependencies` — rego-gate вище (канон: [package.json.deny.json](./template/package.json.deny.json)). Заборонені імпорти в коді (AST-сканер): `../lib/bunyan-imports.mjs` (перевірка запускається з `../runtime/main.mjs`).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
## Структура проекту
|
|
2
|
+
|
|
3
|
+
Рекомендується використовувати таку структуру проекту:
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
k8s/ # тут всі файли для деплойменту в Kubernetes, включаючи kustomize
|
|
7
|
+
src/ # тут всі файли необхідні для роботи проекту
|
|
8
|
+
Dockerfile
|
|
9
|
+
package.json
|
|
10
|
+
readme.md
|
|
11
|
+
```
|
|
@@ -12,3 +12,173 @@
|
|
|
12
12
|
Це **не** стосується поля `engines.node` (мінімальна версія Node для сумісності інструментів) і **не** стосується frontend-пакетів з `vite` у `devDependencies`.
|
|
13
13
|
|
|
14
14
|
Канон заборонених патернів у `scripts`: [package.json.deny.json](./policy/package_json/template/package.json.deny.json) (`scriptsForbidden`).
|
|
15
|
+
|
|
16
|
+
## CheckEnv та заборона прямого `process.env`
|
|
17
|
+
|
|
18
|
+
### CheckEnv
|
|
19
|
+
|
|
20
|
+
Усі змінні оточення, які використовуються в коді, повинні бути перевірені за допомогою `checkEnv` з пакету `@nitra/check-env`. Це гарантує, що всі необхідні змінні оточення встановлені перед запуском програми.
|
|
21
|
+
|
|
22
|
+
```javascript title="Приклад підключення до PostgreSQL в /src/conn/pg.mjs"
|
|
23
|
+
import { checkEnv, env } from '@nitra/check-env'
|
|
24
|
+
import { SQL } from 'bun'
|
|
25
|
+
|
|
26
|
+
checkEnv(['PG_CONN'])
|
|
27
|
+
|
|
28
|
+
export const db = new SQL({ url: env.PG_CONN })
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### process.env
|
|
33
|
+
|
|
34
|
+
Прямий доступ до `process.env.X` у коді заборонений — його треба замінити на `env`:
|
|
35
|
+
|
|
36
|
+
> Стосується лише backend-пакетів (див. **Область застосування**). У frontend-пакетах (`vite` у `devDependencies`) — **не змінюй** `process.env.*` і **не додавай** імпорт `node:process`.
|
|
37
|
+
|
|
38
|
+
- **обов'язкова змінна** — `import { checkEnv, env } from '@nitra/check-env'` плюс `checkEnv(['X'])`
|
|
39
|
+
у тому ж файлі (приклад див. вище в розділі **CheckEnv**);
|
|
40
|
+
- **опційна змінна** — `import { env } from 'node:process'`:
|
|
41
|
+
|
|
42
|
+
```javascript title="Опційна змінна — env з node:process"
|
|
43
|
+
import { env } from 'node:process'
|
|
44
|
+
|
|
45
|
+
console.log(env.OPTIONAL_ENV_VAR)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Тимчасово приглушити перевірку для конкретного рядка можна коментарем
|
|
49
|
+
`// @7n/rules ignore-next-line checkEnv` безпосередньо перед використанням
|
|
50
|
+
(escape-hatch для legacy-коду, не для нових файлів).
|
|
51
|
+
|
|
52
|
+
Перевірка (JS-сканер): `../lib/check-env-scan.mjs`.
|
|
53
|
+
|
|
54
|
+
## Внутрішні аліаси для підключень до БД і GraphQL
|
|
55
|
+
|
|
56
|
+
Якщо в проекті є підключення до баз даних, зовнішніх graphql на кшталт:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
import { SQL } from 'bun'
|
|
60
|
+
|
|
61
|
+
// або
|
|
62
|
+
|
|
63
|
+
import sql from 'mssql'
|
|
64
|
+
|
|
65
|
+
// або
|
|
66
|
+
|
|
67
|
+
import { GraphQLClient } from '@nitra/graphql-request'
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
то ці підключення повинні бути винесені в окремий файл, наприклад `/src/conn/pg.mjs`, в package.json повинні бути додано аліас:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"imports": {
|
|
75
|
+
"#conn/*": "./src/conn/*"
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
так виглядатиме підключення до PostgreSQL в коді:
|
|
82
|
+
|
|
83
|
+
```javascript title="Приклад підключення до PostgreSQL в /src/conn/pg.mjs"
|
|
84
|
+
import { checkEnv, env } from '@nitra/check-env'
|
|
85
|
+
import { SQL } from 'bun'
|
|
86
|
+
|
|
87
|
+
checkEnv(['PG_CONN'])
|
|
88
|
+
|
|
89
|
+
export const db = new SQL({ url: env.PG_CONN })
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
а так до GraphQL:
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
import { checkEnv, env } from '@nitra/check-env'
|
|
97
|
+
import { GraphQLClient } from '@nitra/graphql-request'
|
|
98
|
+
|
|
99
|
+
checkEnv(['QL', 'X_HASURA_ADMIN_SECRET'])
|
|
100
|
+
|
|
101
|
+
export { gql } from '@nitra/graphql-request'
|
|
102
|
+
|
|
103
|
+
export const graphQLClientSmart = new GraphQLClient(env.QL, {
|
|
104
|
+
headers: {
|
|
105
|
+
'X-Hasura-Admin-Secret': env.X_HASURA_ADMIN_SECRET
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
а в коді повинно бути використано:
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
import { pool } from '#conn/pg.mjs'
|
|
114
|
+
|
|
115
|
+
// або
|
|
116
|
+
|
|
117
|
+
import { gql, graphQLClient } from '@nitra/graphql-request'
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Нейминг файлів у `src/conn/`
|
|
121
|
+
|
|
122
|
+
Назва файла в `src/conn/` має одразу повідомляти, **до чого** підключаємось і **в якому режимі**:
|
|
123
|
+
|
|
124
|
+
- **GraphQL** — префікс `ql-`, далі ідентифікатор endpoint:
|
|
125
|
+
- `src/conn/ql-contract.mjs`
|
|
126
|
+
- `src/conn/ql-smart.mjs`
|
|
127
|
+
- **PostgreSQL** — префікс `pg-`, далі тип підключення (репліка vs мастер): `read` або `write`:
|
|
128
|
+
- `src/conn/pg-read.mjs`
|
|
129
|
+
- `src/conn/pg-write.mjs`
|
|
130
|
+
- **PostgreSQL до кількох БД** — додатково ідентифікатор підключення після типу:
|
|
131
|
+
- `src/conn/pg-read-smart.mjs`
|
|
132
|
+
- `src/conn/pg-write-contract.mjs`
|
|
133
|
+
- **MySQL** — префікс `mysql-` за тією ж схемою (`mysql-read.mjs`, `mysql-write-<id>.mjs` тощо).
|
|
134
|
+
- **MSSQL** — префікс `mssql-` за тією ж схемою (`mssql-read.mjs`, `mssql-write-<id>.mjs` тощо). Хоча npm-пакет один (`mssql`), а драйвер MS SQL Server під капотом T-SQL — у файловій назві відрізняємо MS SQL Server від MySQL, бо це різні СУБД, різні діалекти, різні рантаймні залежності. Якщо проєкт історично використовує `mysql-…` для MSSQL-підключень — він валідний і далі (для backward-compat), але новий код пишемо з префіксом `mssql-`.
|
|
135
|
+
|
|
136
|
+
Підключення до БД **обов'язково** має бути ідентифіковано як `read` (репліка) або `write` (мастер). Якщо з імені змінної оточення (наприклад, `env.PG_CONN`) це не очевидно — визнач режим за операціями в коді: якщо немає операцій зміни даних (`INSERT`/`UPDATE`/`DELETE`/DDL) — це `pg-read.mjs`, інакше `pg-write.mjs`.
|
|
137
|
+
|
|
138
|
+
### Експорти у файлах `src/conn/`
|
|
139
|
+
|
|
140
|
+
У файлах підключень **заборонений** `export default`. Експорт має бути **іменований** і збігатися з назвою файла в camelCase.
|
|
141
|
+
|
|
142
|
+
Приклад — `src/conn/ql-smart.mjs`:
|
|
143
|
+
|
|
144
|
+
```javascript title="❌ Так не можна"
|
|
145
|
+
export default new GraphQLClient(env.SMART_QL, {
|
|
146
|
+
headers: {
|
|
147
|
+
'X-Hasura-Admin-Secret': env.SMART_X_HASURA_ADMIN_SECRET
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```javascript title="✅ Канон: іменований експорт за іменем файла"
|
|
153
|
+
export const qlSmart = new GraphQLClient(env.SMART_QL, {
|
|
154
|
+
headers: {
|
|
155
|
+
'X-Hasura-Admin-Secret': env.SMART_X_HASURA_ADMIN_SECRET
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Відповідно: `pg-read.mjs` → `export const pgRead = …`, `pg-write-contract.mjs` → `export const pgWriteContract = …`, `ql-contract.mjs` → `export const qlContract = …`.
|
|
161
|
+
|
|
162
|
+
Файли `index.*` у conn-каталозі пропускаються як можливий reexport-барель.
|
|
163
|
+
|
|
164
|
+
Перевірка (JS-сканери): `../lib/conn-file-rules.mjs` (нейминг, експорти), `../lib/conn-imports-scan.mjs` (факторні імпорти поза `src/conn/`); декларація аліаса `imports["#conn/*"]` у `package.json` — `checkConnAliasDeclaration` у `main.mjs`.
|
|
165
|
+
|
|
166
|
+
## Паузи через setTimeout
|
|
167
|
+
|
|
168
|
+
Заборонено робити паузи через `await new Promise(resolve => setTimeout(resolve, ms))` — таку обгортку треба замінити на promise-варіант `setTimeout` з `node:timers/promises`:
|
|
169
|
+
|
|
170
|
+
```javascript title="Замість new Promise + setTimeout"
|
|
171
|
+
import { setTimeout } from 'node:timers/promises'
|
|
172
|
+
|
|
173
|
+
await setTimeout(500)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Імпорт `setTimeout` з `node:timers/promises` затіняє глобальний таймер у файлі — якщо в тому ж файлі потрібен callback-варіант, імпортуй його під іншим іменем (наприклад, `import { setTimeout as setTimeoutCb } from 'node:timers'`).
|
|
177
|
+
|
|
178
|
+
Перевірка (JS-сканер): `../lib/promise-settimeout-scan.mjs`.
|
|
179
|
+
|
|
180
|
+
## Temporal API (заборона у Bun runtime)
|
|
181
|
+
|
|
182
|
+
У backend/Bun runtime-коді **не використовуй `Temporal`** (`Temporal.Now`, `Temporal.Instant`, імпорти з polyfill тощо). Bun 1.3.x (діапазон версій репозиторію — `bun >= 1.3`) ще не має глобального `Temporal` (`typeof Temporal === "undefined"`), тому агентам треба лишатися на сумісному `Date` API або передавати timestamp у чисті функції через параметр.
|
|
183
|
+
|
|
184
|
+
Перевірка `npx @7n/rules check` (AST-сканер `../lib/temporal-scan.mjs`) сканує JS/TS-код на identifier `Temporal` у backend workspace-коді.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
## Область застосування
|
|
2
|
+
|
|
3
|
+
Правило стосується **виключно backend Node.js workspace-пакетів** (jobs, GraphQL/HTTP-сервери, CLI). **Не застосовується** до frontend-пакетів, які бандляться в браузер: маркер — наявність `vite` у `devDependencies` пакета (`site/`, мобільні Capacitor-пакети, будь-яка Vue/Quasar SPA).
|
|
4
|
+
|
|
5
|
+
У браузерному середовищі:
|
|
6
|
+
|
|
7
|
+
- немає `node:process` — імпорт `import { env } from 'node:process'` resolve'иться у `undefined`, і `env.X` падає з `TypeError: Cannot read properties of undefined`;
|
|
8
|
+
- `process.env.X` у джерелах пакета відсутнє в рантаймі — Vite або взагалі не підставляє його, або підставляє лише `process.env.NODE_ENV`;
|
|
9
|
+
- усі змінні оточення для frontend задаються через `VITE_*` і доступні як `import.meta.env.VITE_X` (типобезпечно через `vite-check-env`); режим — `import.meta.env.MODE` / `import.meta.env.PROD`.
|
|
10
|
+
|
|
11
|
+
Тому **у frontend-пакетах не торкайся `process.env.*`** і **не додавай** `import { env } from 'node:process'`. Якщо натрапив на `process.env.NODE_ENV` у frontend-коді — заміна, якщо взагалі потрібна, лише на `import.meta.env.MODE`.
|
|
@@ -18,3 +18,9 @@ Rego-пакет: `k8s.hasura_configmap`
|
|
|
18
18
|
Семантика: `"true"`/`"false"` приймаються як boolean або рядок (case-insensitive); точний рядок — exact match.
|
|
19
19
|
|
|
20
20
|
**Примітка:** відбір ConfigMap за сусідством із Hasura-Deployment — cross-file, у JS. Rego — пер-документна валідація полів `data`.
|
|
21
|
+
|
|
22
|
+
## ConfigMap: ім'я збігається з Deployment
|
|
23
|
+
|
|
24
|
+
Ця перевірка не обмежена Hasura — стосується **будь-якого** `configmap.yaml` під `k8s/base/`. Якщо в тому самому каталозі є **`configmap.yaml`** і **Deployment**, і цей Deployment посилається рівно на **один** ConfigMap (через `envFrom[*].configMapRef.name` або `volumes[*].configMap.name`) — `metadata.name` ConfigMap має збігатися з `metadata.name` Deployment.
|
|
25
|
+
|
|
26
|
+
Перевірка — cross-file, у **`npm/rules/k8s/manifests/main.mjs`** (`validateConfigMapNameMatchesDeployment` / `validateSingleConfigMapNameMatch`). Якщо Deployment посилається на кілька ConfigMap або жодного — перевірка пропускається (немає однозначного відповідника).
|
|
@@ -23,3 +23,137 @@ PDB (`kind: PodDisruptionBudget` або `apiVersion: policy/v1`):
|
|
|
23
23
|
- `spec.selector.matchLabels` — присутній об'єкт
|
|
24
24
|
|
|
25
25
|
**Примітка:** cross-file перевірки (відповідність `expectedDeployName`, `expectedAppLabel`, `isDevLike`-сегмент) лишаються у JS (`hpaManifestViolations`, `pdbManifestViolations`).
|
|
26
|
+
|
|
27
|
+
## Канон `components/`, env-залежні межі, прод-оверрайди
|
|
28
|
+
|
|
29
|
+
Структурні перевірки полів HPA/PDB вище — про **самі поля** (`apiVersion`, `kind`, `spec.metrics`, `spec.selector` тощо). Заборону локальних `hpa.yaml`/`pdb.yaml` у `base/` — див. **`base_kustomization.mdc`**. Цей розділ — про **env-залежні числові межі** і структуру sibling-каталогу **`components/`**.
|
|
30
|
+
|
|
31
|
+
### Канонічна структура `<pkg>/k8s/components/`
|
|
32
|
+
|
|
33
|
+
Для **кожного** `kind: Deployment` у `…/k8s/…/base/` обов'язковий sibling-каталог **`…/k8s/…/components/`** (Kustomize Component, фіксована назва каталогу — `components`) з HPA і PDB для цього Deployment:
|
|
34
|
+
|
|
35
|
+
- **`kustomization.yaml`** — `apiVersion: kustomize.config.k8s.io/v1alpha1`, `kind: Component`, `resources: [hpa.yaml, pdb.yaml]` (як мінімум ці два).
|
|
36
|
+
- **`hpa.yaml`** — `autoscaling/v2`, `HorizontalPodAutoscaler`, `spec.scaleTargetRef.name` **= `metadata.name`** Deployment, dev-like значення `minReplicas: 1`, `maxReplicas: 1`.
|
|
37
|
+
- **`pdb.yaml`** — `policy/v1`, `PodDisruptionBudget`, `spec.selector.matchLabels.app` **= мітка `app`** Deployment, dev-like `minAvailable: 0`.
|
|
38
|
+
|
|
39
|
+
**`kind: Component`** (не `kind: Kustomization`) — це **джерело** канонічних HPA/PDB для всіх overlays, а не overlay сам по собі; всередині самого Component prod-патчі не потрібні (env-неутральний).
|
|
40
|
+
|
|
41
|
+
Перевірка — **`npm/rules/k8s/manifests/main.mjs`** (`validateComponentsForBaseDeployment`).
|
|
42
|
+
|
|
43
|
+
### Env-залежні межі (за сегментом шляху після `/k8s/`)
|
|
44
|
+
|
|
45
|
+
**Dev-like середовища** — сегмент `base`, `dev`, або з суфіксом `-qa` (наприклад `tr-qa`):
|
|
46
|
+
|
|
47
|
+
- HPA: `minReplicas` — рівно **1**, `maxReplicas` — рівно **1**.
|
|
48
|
+
- PDB: `minAvailable` — рівно **0**.
|
|
49
|
+
|
|
50
|
+
**Прод-середовища** — усе інше:
|
|
51
|
+
|
|
52
|
+
- HPA: `minReplicas` — мінімум **2**, `maxReplicas` — мінімум **2** (і `minReplicas <= maxReplicas`).
|
|
53
|
+
- PDB: `minAvailable` — мінімум **1**.
|
|
54
|
+
|
|
55
|
+
Перевірка — **`npm/rules/k8s/manifests/main.mjs`** (`hpaManifestViolations`, `pdbManifestViolations`; сегмент середовища визначає `isDevLikeK8sEnvSegment`).
|
|
56
|
+
|
|
57
|
+
### Прод-оверрайди у `kustomization.yaml`
|
|
58
|
+
|
|
59
|
+
Якщо прод-оверлей (сегмент не dev-like) успадковує HPA/PDB через `components: [- ../components]` (або інший kustomize-tree з HPA/PDB), у `patches[]` **обов'язкові** JSON6902-перевизначення прод-значень:
|
|
60
|
+
|
|
61
|
+
- **`HorizontalPodAutoscaler`**: `/spec/minReplicas` і `/spec/maxReplicas` (мінімум 2).
|
|
62
|
+
- **`PodDisruptionBudget`**: `/spec/minAvailable` (мінімум 1).
|
|
63
|
+
|
|
64
|
+
```yaml title="k8s/prod/kustomization.yaml (фрагмент)"
|
|
65
|
+
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
66
|
+
kind: Kustomization
|
|
67
|
+
namespace: prod
|
|
68
|
+
resources:
|
|
69
|
+
- ../base
|
|
70
|
+
components:
|
|
71
|
+
- ../components
|
|
72
|
+
patches:
|
|
73
|
+
- target:
|
|
74
|
+
kind: HorizontalPodAutoscaler
|
|
75
|
+
name: backend-api
|
|
76
|
+
patch: |-
|
|
77
|
+
- op: replace
|
|
78
|
+
path: /spec/minReplicas
|
|
79
|
+
value: 2
|
|
80
|
+
- op: replace
|
|
81
|
+
path: /spec/maxReplicas
|
|
82
|
+
value: 10
|
|
83
|
+
- target:
|
|
84
|
+
kind: PodDisruptionBudget
|
|
85
|
+
name: backend-api
|
|
86
|
+
patch: |-
|
|
87
|
+
- op: replace
|
|
88
|
+
path: /spec/minAvailable
|
|
89
|
+
value: 1
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Не застосовується до dev-like оточень. Перевірка — **`npm/rules/k8s/manifests/main.mjs`** (`prodOverlayHpaPdbOverrideNeeds`, `validateProdKustomizationOverrides`).
|
|
93
|
+
|
|
94
|
+
### Приклад: `components/hpa.yaml` і `components/pdb.yaml`
|
|
95
|
+
|
|
96
|
+
```yaml title="k8s/components/kustomization.yaml"
|
|
97
|
+
apiVersion: kustomize.config.k8s.io/v1alpha1
|
|
98
|
+
kind: Component
|
|
99
|
+
resources:
|
|
100
|
+
- hpa.yaml
|
|
101
|
+
- pdb.yaml
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```yaml title="k8s/components/hpa.yaml"
|
|
105
|
+
# yaml-language-server: $schema=https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.33.9-standalone-strict/horizontalpodautoscaler-autoscaling-v2.json
|
|
106
|
+
apiVersion: autoscaling/v2
|
|
107
|
+
kind: HorizontalPodAutoscaler
|
|
108
|
+
metadata:
|
|
109
|
+
name: backend-api
|
|
110
|
+
spec:
|
|
111
|
+
scaleTargetRef:
|
|
112
|
+
apiVersion: apps/v1
|
|
113
|
+
kind: Deployment
|
|
114
|
+
name: backend-api
|
|
115
|
+
minReplicas: 1 # прод overlay підіймає до >= 2
|
|
116
|
+
maxReplicas: 1 # прод overlay підіймає до >= 2
|
|
117
|
+
metrics:
|
|
118
|
+
- type: Resource
|
|
119
|
+
resource:
|
|
120
|
+
name: cpu
|
|
121
|
+
target:
|
|
122
|
+
type: Utilization
|
|
123
|
+
averageUtilization: 70
|
|
124
|
+
behavior:
|
|
125
|
+
scaleUp:
|
|
126
|
+
stabilizationWindowSeconds: 15
|
|
127
|
+
policies:
|
|
128
|
+
- type: Percent
|
|
129
|
+
value: 100
|
|
130
|
+
periodSeconds: 30
|
|
131
|
+
- type: Pods
|
|
132
|
+
value: 4
|
|
133
|
+
periodSeconds: 30
|
|
134
|
+
selectPolicy: Max
|
|
135
|
+
scaleDown:
|
|
136
|
+
stabilizationWindowSeconds: 300
|
|
137
|
+
policies:
|
|
138
|
+
- type: Percent
|
|
139
|
+
value: 25
|
|
140
|
+
periodSeconds: 120
|
|
141
|
+
selectPolicy: Min
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```yaml title="k8s/components/pdb.yaml"
|
|
145
|
+
# yaml-language-server: $schema=https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.33.9-standalone-strict/poddisruptionbudget-policy-v1.json
|
|
146
|
+
apiVersion: policy/v1
|
|
147
|
+
kind: PodDisruptionBudget
|
|
148
|
+
metadata:
|
|
149
|
+
name: backend-api
|
|
150
|
+
spec:
|
|
151
|
+
minAvailable: 0 # прод overlay підіймає до >= 1
|
|
152
|
+
selector:
|
|
153
|
+
matchLabels:
|
|
154
|
+
app: backend-api
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Overlays без `components/`
|
|
158
|
+
|
|
159
|
+
У **не-base** оверлеях, що не підключають sibling `components/`, поруч із `Deployment` лишається звична схема: окремі **`hpa.yaml`** і **`pdb.yaml`**, якщо такі потрібні для цього середовища (структурні поля — вище; env-залежні межі — як вище).
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
## lint-k8s: kubeconform і kubescape
|
|
2
|
+
|
|
3
|
+
Окремо від modeline `$schema` у редакторі (**`main.mdc`**) варто ганяти CLI-лінтери (**kubeconform** і **kubescape**) по тих самих деревах **`…/k8s`**.
|
|
4
|
+
|
|
5
|
+
**Залежності:** виконувані файли kubeconform, kubescape і kubectl у **PATH** (kustomize використовуємо як вшиту підкоманду **`kubectl kustomize`** — окремий бінарник `kustomize` не потрібен); не додавай їх у **devDependencies**.
|
|
6
|
+
|
|
7
|
+
**Версія Kubernetes для kubeconform** — **`-kubernetes-version 1.33.9`** (semver без префікса `v`; набір схем **`v1.33.9-standalone-strict`**). Для CRD додатково підключається реєстр [datreeio/CRDs-catalog](https://github.com/datreeio/CRDs-catalog) другим **`-schema-location`**. Прапорець **`-ignore-missing-schemas`** увімкнено завжди. Реалізація — **`npm/rules/k8s/kubeconform/main.mjs`** (`runKubeconform` у **`npm/rules/k8s/manifests/main.mjs`**).
|
|
8
|
+
|
|
9
|
+
**kubescape** — вхід через зібраний kustomize-маніфест: для кожного dir-у з `kustomization.yaml` (`kind: Kustomization`; **`kind: Component`** пропускається) лінт виконує **`kubectl kustomize <dir>`** і передає stdout у **`kubescape scan <tmp-file>`** з порогом **`--severity-threshold high`**. Маніфест проходить через тимчасовий файл, бо **`kubescape scan` у v4.x не читає stdin**. Якщо в дереві **`…/k8s`** немає жодного `kustomization.yaml` — fallback на dir-скан **`kubescape scan <каталог-k8s>`**. У kubescape немає прапорця **`-kubernetes-version`**. Реалізація — **`npm/rules/k8s/manifests/main.mjs`** (`runKubescape`, `scanKustomizeK8sDirs`, `scanRawK8sDir`).
|
|
10
|
+
|
|
11
|
+
Лінт запускається через **`npx @7n/rules lint`** (per-file детектор `k8s/kubeconform`) і **`npx @7n/rules fix k8s`** (cross-file оркестрація kubescape, `k8s/manifests`). Окремий `package.json`-скрипт `lint-k8s` не потрібен.
|
|
12
|
+
|
|
13
|
+
## Винятки kubescape: `.kubescape-exceptions.json`
|
|
14
|
+
|
|
15
|
+
Якщо в **корені проєкту** є файл **`.kubescape-exceptions.json`** — лінт автоматично передає його в `kubescape scan` через **`--exceptions`** ([postureExceptionPolicy](https://github.com/kubescape/kubescape/blob/master/docs/exceptions.md); `buildKubescapeExceptionsArgs` у `npm/rules/k8s/manifests/main.mjs`).
|
|
16
|
+
|
|
17
|
+
Канонічний кейс — **C-0012** (`Applications credentials in configuration files`, High): control тригериться на **ім'я** env, що містить підрядок `secret`/`password`/`key`/`token`, а **не** на значення. Точкове виключення для ConfigMap із цим env — приклад структури:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
[
|
|
21
|
+
{
|
|
22
|
+
"name": "hasura-jwt-public-config",
|
|
23
|
+
"policyType": "postureExceptionPolicy",
|
|
24
|
+
"actions": ["alertOnly"],
|
|
25
|
+
"resources": [
|
|
26
|
+
{
|
|
27
|
+
"designatorType": "Attributes",
|
|
28
|
+
"attributes": { "kind": "ConfigMap", "name": "hasura-config" }
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"posturePolicies": [{ "controlID": "C-0012" }]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Увага:** готовий snippet-файл цього прикладу (`js/templates/kubescape_exceptions/.kubescape-exceptions.json.snippet.json` до `da05f89d`) під час "combine concern" **не був перенесений** у жоден поточний concern-каталог і в репозиторії відсутній — знайти оригінал не вдалося; за потреби відтвори JSON вручну за прикладом вище.
|
|
37
|
+
|
|
38
|
+
Виключай контрольно, а не глобально (не додавай винятки без `attributes.name`/`labels`).
|
|
@@ -18,3 +18,76 @@ Rego-пакет: `k8s.kustomization`
|
|
|
18
18
|
✗ `op: remove` + `op: add` на один `path` в одному patch
|
|
19
19
|
|
|
20
20
|
**Примітка:** резолюція kustomize-дерева, перевірка існування refs на диску, парність `svc.yaml`/`svc-hl.yaml` — JS (`rules/k8s/fix.mjs`).
|
|
21
|
+
|
|
22
|
+
## Kustomize: структура каталогів (`base` / overlays)
|
|
23
|
+
|
|
24
|
+
Трансформуй дерева **`**/k8s`**, щоб **винести спільне** через [Kustomize](https://kustomize.io/): один канонічний **`base`** і тонкі **overlays** для інших середовищ.
|
|
25
|
+
|
|
26
|
+
### Джерело правди — середовище dev
|
|
27
|
+
|
|
28
|
+
- За основу бери **все, що відповідає середовищу dev** (як воно має виглядати в кластері для dev).
|
|
29
|
+
- У **такому вигляді** цей набір стає каталогом **`base`**: спільні маніфести без окремої директорії **`dev/`**.
|
|
30
|
+
- Окремої директорії **`k8s/dev/`** **не повинно існувати**: за середовище **dev** відповідає **`base`**. `check k8s` падає на будь-якому шляху `…/k8s/dev/…` (`isForbiddenK8sDevPath` у `npm/rules/k8s/manifests/main.mjs`).
|
|
31
|
+
|
|
32
|
+
### Overlays (не-dev)
|
|
33
|
+
|
|
34
|
+
- У каталозі кожного іншого середовища (наприклад **`ua/`**, **`prod/`**) — мінімум файлів: типово лише **`kustomization.yaml`** і ресурси, **необхідні лише для цього overlay**.
|
|
35
|
+
- Відмінності від dev вносяться **оверрайдами** (patches, `images`, `replicas`, `configMapGenerator` тощо), а не копіюванням повного дерева з `base`.
|
|
36
|
+
|
|
37
|
+
### Рядки, що змінюються між середовищами
|
|
38
|
+
|
|
39
|
+
У manifest-файлах у **`base`** для полів, які **будуть відрізнятися** в інших середовищах, на **тому самому рядку** додай коментар:
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
image: my-app:dev-tag # буде замінено через kustomize
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Зміна image — через `images:`, не через `patches[]`
|
|
46
|
+
|
|
47
|
+
Підміну image у Pod-шаблоні Deployment в overlay роби директивою `images:`, а не JSON6902-патчем `op: replace` на `/spec/template/spec/containers/<N>/image`.
|
|
48
|
+
|
|
49
|
+
- У **`name`** — те, що **дослівно** стоїть у `image:` у base **без тегу**.
|
|
50
|
+
- **`newName`** — кінцеве ім'я образу; **`newTag`** — тег для прода.
|
|
51
|
+
- **`digest`** (`@sha256:…`) у `name` / `newName` не чіпай.
|
|
52
|
+
|
|
53
|
+
```yaml title="k8s/prod/kustomization.yaml (фрагмент)"
|
|
54
|
+
images:
|
|
55
|
+
- name: europe-west4-docker.pkg.dev/abie-ua/c/apply-on-invoice-discount
|
|
56
|
+
newName: europe-west4-docker.pkg.dev/abie-ua/c/apply-on-invoice-discount
|
|
57
|
+
newTag: v2025-04-29
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**`check k8s` автоматично** для кожного `kustomization.yaml` (конвертація в `npm/rules/k8s/manifests/main.mjs`):
|
|
61
|
+
|
|
62
|
+
1. конвертує кожну JSON6902-операцію `op: replace` на `/spec/template/spec/containers/<N>/image` у запис `images:`;
|
|
63
|
+
2. чистить існуючий блок `images:` — зрізає `:tag` з `name` і видаляє `newTag`, який збігається з відрізаним тегом.
|
|
64
|
+
|
|
65
|
+
### Міграція зі старої структури
|
|
66
|
+
|
|
67
|
+
Після перенесення у **`base`** та overlays і перевірки (**`check k8s`**, **`lint-k8s`**) **видали** застарілі файли та директорії, що замінені новою схемою.
|
|
68
|
+
|
|
69
|
+
### `patches[].target`: лише `kind` і `name`
|
|
70
|
+
|
|
71
|
+
У `patches[].target` залишай **тільки** **`kind`** і **`name`** — поля **`group`** і **`version`** прибирай, якщо в інвентарі ресурсів немає колізії за цим `kind`+`name` між різними API-групами/версіями.
|
|
72
|
+
|
|
73
|
+
```yaml
|
|
74
|
+
# ❌ зайві group / version
|
|
75
|
+
patches:
|
|
76
|
+
- target:
|
|
77
|
+
group: gateway.networking.k8s.io
|
|
78
|
+
version: v1beta1
|
|
79
|
+
kind: Gateway
|
|
80
|
+
name: gw
|
|
81
|
+
|
|
82
|
+
# ✅
|
|
83
|
+
patches:
|
|
84
|
+
- target:
|
|
85
|
+
kind: Gateway
|
|
86
|
+
name: gw
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Виняток:** залишай `group` / `version`, лише якщо в дереві overlay реально співіснують ресурси з однаковими `kind`+`name`, але різними API-групами/версіями.
|
|
90
|
+
|
|
91
|
+
### Локальні шляхи в `kustomization.yaml`
|
|
92
|
+
|
|
93
|
+
Кожен запис без `://` (remote) з `resources` / `bases` / `components` / `crds`, `patchesStrategicMerge`, `patches[].path`, `patchesJson6902[].path`, `configurations[]`, `replacements[].path` має вказувати на **існуючий** у репозиторії файл (`.yaml` / `.yml`) або **каталог**; биті посилання — помилка **`check k8s`**.
|