@nitra/cursor 1.8.212 → 1.8.216
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 +55 -6
- package/bin/auto-rules.md +2 -0
- package/mdc/js-bun-db.mdc +1 -1
- package/mdc/js-bun-redis.mdc +21 -0
- package/mdc/k8s.mdc +79 -17
- package/package.json +1 -1
- package/policy/js_bun_redis/package_json/package_json.rego +37 -0
- package/scripts/auto-rules.mjs +14 -4
- package/scripts/auto-skills.mjs +1 -7
- package/scripts/check-hasura.mjs +1 -2
- package/scripts/check-js-bun-redis.mjs +98 -0
- package/scripts/check-k8s.mjs +301 -78
- package/scripts/lint-conftest.mjs +14 -6
- package/scripts/utils/bun-sql-scan.mjs +1 -2
- package/scripts/utils/conn-file-rules.mjs +6 -2
- package/scripts/utils/redis-imports.mjs +172 -0
- package/skills/taze/SKILL.md +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,55 @@
|
|
|
4
4
|
|
|
5
5
|
Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
|
|
6
6
|
|
|
7
|
+
## [1.8.216] - 2026-05-09
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **k8s / check-k8s:** орієнтир **`DEFAULT_CONTAINER_MEMORY_REQUEST`** поза base — **`512Mi`** (замість **`512`**).
|
|
12
|
+
|
|
13
|
+
## [1.8.215] - 2026-05-09
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- **k8s / check-k8s:** канон **`resources.requests.memory`** у шарі **`…/k8s/…/base/…`** — **`128Mi`** (замість **`128`**, щоб відповідати Quantity у Kubernetes); приймається **`Mi`** без урахування регістру.
|
|
18
|
+
|
|
19
|
+
## [1.8.214] - 2026-05-09
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **k8s / check-k8s:** конвертація image-replace patches → `images:` падала з `byPatch.keys(...).toSorted is not a function`, бо `Map.keys()` повертає ітератор без `toSorted`. Тепер ключі спершу матеріалізуються у масив (`[...byPatch.keys()].toSorted(...)`).
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **k8s / check-k8s:** у шарі **`…/k8s/…/base/…`** для **Deployment** жорстко **`resources.requests.cpu: '0.02'`** та **`memory: '128'`**; поза base обов’язкові **cpu** і **memory** (орієнтир **`0.5`** / **`512`** у підказках).
|
|
28
|
+
- **k8s / check-k8s:** заборона **`hpa.yaml`** у каталозі **`…/base/`**; якщо HPA є в дереві base — вимагається strategic-merge **`$patch: delete`** для **HorizontalPodAutoscaler** у **`base/kustomization.yaml`**.
|
|
29
|
+
- **k8s / check-k8s:** прод-оверлей вимагає patches на **HPA** (`minReplicas`/`maxReplicas`) лише якщо успадковане base **не** видаляє HPA через delete-patch; **PDB** **`minAvailable`** — якщо в base є PDB.
|
|
30
|
+
- **k8s.mdc:** оновлено правила та приклади під цю модель.
|
|
31
|
+
|
|
32
|
+
## [1.8.213] - 2026-05-09
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- Нове правило `js-bun-redis` (`npm/mdc/js-bun-redis.mdc`): заміна `ioredis` /
|
|
37
|
+
`node-redis` (включно з кореневим `redis` v4 і підпакетами `@redis/*`) на
|
|
38
|
+
Bun native Redis (`import { redis } from 'bun'`,
|
|
39
|
+
<https://bun.com/docs/runtime/redis>).
|
|
40
|
+
- AST-сканер `npm/scripts/utils/redis-imports.mjs` (`oxc-parser`) ловить
|
|
41
|
+
`import` / `require` / динамічний `import()` пакетів `ioredis`, `node-redis`,
|
|
42
|
+
`redis`, підшляхів `ioredis/...` / `redis/...` і `@redis/*`. Не зачіпає
|
|
43
|
+
сторонні `redis-*` (наприклад, `redis-mock`).
|
|
44
|
+
- `npm/scripts/check-js-bun-redis.mjs` запускає AST-скан по JS/TS-джерелах і
|
|
45
|
+
доступний як `npx @nitra/cursor check js-bun-redis`.
|
|
46
|
+
- Rego-полісі `npm/policy/js_bun_redis/package_json/` — заборона
|
|
47
|
+
`ioredis` / `node-redis` / `redis` / `@redis/*` у `dependencies` будь-якого
|
|
48
|
+
`package.json` у дереві; зареєстрована таргетом у
|
|
49
|
+
`npm/scripts/lint-conftest.mjs` (`bun run lint-conftest`).
|
|
50
|
+
- Авто-увімкнення правила в `.n-cursor.json`: `npm/scripts/auto-rules.mjs`
|
|
51
|
+
додає `js-bun-redis`, якщо в `dependencies` хоч одного `package.json` є
|
|
52
|
+
`ioredis` або `node-redis` (умова — у `npm/bin/auto-rules.md`).
|
|
53
|
+
- Тести: `npm/tests/redis-imports.test.mjs` (AST-сканер) і нові кейси у
|
|
54
|
+
`npm/tests/auto-rules.test.mjs` (детект `ioredis` / `node-redis`).
|
|
55
|
+
|
|
7
56
|
## [1.8.212] - 2026-05-08
|
|
8
57
|
|
|
9
58
|
### Changed
|
|
@@ -54,7 +103,7 @@
|
|
|
54
103
|
`sqlFormat` / `pgFmt` з `%L`/`%I`/`%s` у тілі, плюс `quoteLiteral` /
|
|
55
104
|
`quoteIdent` / `escapeLiteral` / `escapeIdent` без додаткової перевірки)
|
|
56
105
|
та `findPgFormatLikeQueryWrapperInText` (`{ query(text, params) { ...
|
|
57
|
-
|
|
106
|
+
<obj>.unsafe(...) ... } }`). Скан запускається лише у файлах з
|
|
58
107
|
`import { sql|SQL } from 'bun'`.
|
|
59
108
|
- `npm/scripts/check-js-bun-db.mjs` рапортує `pgFormatShim` / `queryWrapper` —
|
|
60
109
|
окремі лічильники й `pass`-рядки, без зміни існуючих перевірок.
|
|
@@ -105,10 +154,10 @@
|
|
|
105
154
|
(prettier-залежність, `@nitra/eslint-config ≥ 3.9.2`),
|
|
106
155
|
`checkPackageJsonTypeModule` для root, `checkEnginesNode/Bun` для root,
|
|
107
156
|
канонічний `lint-js`-скрипт, валідація `lint-js.yml` (`verifyLintJsWorkflowStructure`
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
157
|
+
- fallback). Лишилися — `.oxlintrc.json` canonical-snapshot, VSCode-розширення,
|
|
158
|
+
workspace-ітерація для `type: "module"` і engines, дубль JS-кроків у `lint.yml`,
|
|
159
|
+
`.jscpd.json`. Прибрано непотрібні імпорти `parseWorkflowYaml`,
|
|
160
|
+
`verifyLintJsWorkflowStructure` і `OXLINT_FIX_RE`.
|
|
112
161
|
- `npm/scripts/check-js-run.mjs` — без перевірок `bunyan` / `@nitra/bunyan` у
|
|
113
162
|
залежностях, canonical `jsconfig.json` через `deepEqualJson`,
|
|
114
163
|
`OTEL_RESOURCE_ATTRIBUTES` у `configmap.yaml`. Лишилися AST-скан коду
|
|
@@ -220,7 +269,7 @@
|
|
|
220
269
|
- `check-k8s.mjs` (автоконверт `image-replace` patches → `images:`): тепер працює і для `patches[i].patch` із **кількома** ops, а не лише з одинокою image-replace op. Сканує всі ops у патчі, конвертує **кожну** `op: replace` на `/spec/template/spec/containers/<N>/image` (target `kind: Deployment`) у запис `images:`; якщо всі ops патча конвертовано — `patches[i]` видаляється повністю; інакше inline `patch:` переписується через `parseDocument` без конвертованих ops зі збереженням block-literal scalar (`|-`) і вихідного порядку решти ops. Реалізовано через нові функції `tryParseJson6902Array` (≥ 1 op, замість `tryParseSingleJson6902Array`) і `rewriteInlinePatchWithoutOps`; `imageReplaceDeploymentPatchInfo` повертає `{ deployName, totalOps, ops: [{ containerIndex, newImage, opIndex }] }` (раніше — одиничний `{ deployName, containerIndex, newImage }` лише за `length === 1`); `applyConversionsToDoc` групує конвертації по індексу патча й вирізає ops або сам патч за потреби. Сортування решти ops після видалення лишається поза цією зміною — за нього відповідає окрема перевірка `kustomizationInlinePatchOpsSortedViolation`.
|
|
221
270
|
- `mdc/k8s.mdc` (v1.26 → v1.27): уточнено крок 1 авто-перевірки в розділі «Зміна image — через `images:`, не через `patches[]`» — тепер описує і випадок, коли в `patches[i].patch` лишаються не-image ops (їх зберігає, у вихідному порядку, без коментарів).
|
|
222
271
|
- `check-js-lint.mjs` + `mdc/js-lint.mdc` (v1.16 → v1.17): мінімум `@nitra/eslint-config` піднято з `^3.8.0` до `^3.9.2`. Обґрунтування: з 3.9.2 у `getConfig` вбудовано ignore для `**/adr/**`, тож ADR-документи не валідуються ESLint, і консьюмерам не треба додавати цей glob у `eslint.config.js` локально. `nitraEslintConfigMeetsMinVersion` тепер повертає `false` для діапазонів `^3.8.x`–`^3.9.1`; `workspace:*` лишається ok без змін. Pass/fail-повідомлення `checkPackageJsonLintDeps` оновлено під новий мінімум; `for...in`-бан з 3.8.0 згадується як накопичена відмінність. Тести `nitraEslintConfigMeetsMinVersion` розширено: `^3.9.2`/`^3.9.10`/`^3.10.0`/`^4.0.0` — ok; `^3.9.1`/`^3.8.0`/`^3.6.12`/`^3.4.3` — ні.
|
|
223
|
-
- `bin/n-cursor.js` (`reexecIfPackageVersionChanged` + `spawnSync`-виклик): `process.env.NITRA_CURSOR_REEXEC` і `...process.env` замінено на `env.NITRA_CURSOR_REEXEC` і `...env` з `node:process` (`import { cwd, env } from 'node:process'`). Підстава: правило `js-run.mdc` забороняє прямий `process.env.*` у Node-коді; `NITRA_CURSOR_REEXEC` — опційна змінна (виставляється лише при re-exec), тож імпорт `env` з `node:process` (а не з `@nitra/check-env`) — канонічна форма для опційних. Поведінка не змінена; раніше `npm/scripts/check-js-run.mjs` помилявся на `bin/n-cursor.js:1136` (правило `process-env`), тепер
|
|
272
|
+
- `bin/n-cursor.js` (`reexecIfPackageVersionChanged` + `spawnSync`-виклик): `process.env.NITRA_CURSOR_REEXEC` і `...process.env` замінено на `env.NITRA_CURSOR_REEXEC` і `...env` з `node:process` (`import { cwd, env } from 'node:process'`). Підстава: правило `js-run.mdc` забороняє прямий `process.env.*` у Node-коді; `NITRA_CURSOR_REEXEC` — опційна змінна (виставляється лише при re-exec), тож імпорт `env` з `node:process` (а не з `@nitra/check-env`) — канонічна форма для опційних. Поведінка не змінена; раніше `npm/scripts/check-js-run.mjs` помилявся на `bin/n-cursor.js:1136` (правило `process-env`), тепер integration-test `check-* на реальному репозиторії` проходить.
|
|
224
273
|
|
|
225
274
|
### Added
|
|
226
275
|
|
package/bin/auto-rules.md
CHANGED
|
@@ -34,6 +34,8 @@ js-mssql - якщо в хоч одному package.json в секції dependen
|
|
|
34
34
|
|
|
35
35
|
js-bun-db - якщо в хоч одному package.json в секції dependencies присутній пакет pg, pg-format або mysql2 або є імпорт sql/SQL з Bun (приклад: import { sql } from "bun")
|
|
36
36
|
|
|
37
|
+
js-bun-redis - якщо в хоч одному package.json в секції dependencies присутній пакет ioredis або node-redis
|
|
38
|
+
|
|
37
39
|
k8s - якщо присутня хоч одна директорія k8s
|
|
38
40
|
|
|
39
41
|
nginx-default-tpl - якщо присутній хоч один файл з переліку - default.conf.template, default.conf, nginx.conf
|
package/mdc/js-bun-db.mdc
CHANGED
|
@@ -26,7 +26,7 @@ PostgreSQL 18+, MariaDB 10.6+ (сумісний з MySQL-протоколом,
|
|
|
26
26
|
- допоміжні `quoteLiteral`, `quoteIdent`, `escapeLiteral`, `escapeIdent` як публічні експорти модуля;
|
|
27
27
|
- обгортки `pgRead.query(text, params)` / `pgWrite.query(text, params)` / `db.query(text, params)`, які складають SQL-рядок (з або без `format`) і викликають `sql.unsafe(text, params)` — це повертає injection-поверхню, від якої ми йдемо, тільки під «зручним» іменем.
|
|
28
28
|
|
|
29
|
-
Замість цього всі точки використання потрібно перевести на tagged template `sql\`...\${value}...\``.
|
|
29
|
+
Замість цього всі точки використання потрібно перевести на tagged template `sql\`...\${value}...\``. Кожне`${value}` стає окремим параметром bind, без рядкового екранування.
|
|
30
30
|
|
|
31
31
|
### Типові ідіоми `pg-format` → Bun SQL
|
|
32
32
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Використання Redis/Valkey з Bun
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
version: '1.0'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Підтримувані версії redis
|
|
8
|
+
|
|
9
|
+
Redis 7.2+
|
|
10
|
+
|
|
11
|
+
## Заміна на Bun native Redis
|
|
12
|
+
|
|
13
|
+
Якщо в проєкті використовуються бібліотеки `ioredis`, `node-redis`, їх потрібно замінити на Bun native Redis: <https://bun.com/docs/runtime/redis>.
|
|
14
|
+
|
|
15
|
+
- Видалити з `dependencies`: `ioredis`, `node-redis`.
|
|
16
|
+
- Видалити з коду: усі `import` / `require` цих пакетів та власні обгортки над ними.
|
|
17
|
+
- Замінити на `import { redis } from 'bun'`
|
|
18
|
+
|
|
19
|
+
## Перевірка
|
|
20
|
+
|
|
21
|
+
`npx @nitra/cursor check js-bun-redis`.
|
package/mdc/k8s.mdc
CHANGED
|
@@ -96,19 +96,51 @@ jobs:
|
|
|
96
96
|
run: bun run lint-k8s
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
## Deployment: `resources.requests
|
|
99
|
+
## Deployment: `resources.requests` (CPU і memory)
|
|
100
100
|
|
|
101
|
-
У
|
|
101
|
+
У кожному контейнері **`Deployment`** обов’язкові **`resources.requests.cpu`** і **`resources.requests.memory`** (непорожні скаляри Kubernetes Quantity).
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
### Шар **`…/k8s/…/base/…`** (dev / щільний packing)
|
|
104
|
+
|
|
105
|
+
У **всіх** `Deployment` у файлах під **`…/k8s/…/base/…`** значення **жорстко фіксовані** (для **cpu** допускається число **`0.02`** у YAML):
|
|
106
|
+
|
|
107
|
+
```yaml
|
|
108
|
+
resources:
|
|
109
|
+
requests:
|
|
110
|
+
cpu: '0.02'
|
|
111
|
+
memory: '128Mi'
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**HPA у base не тримаємо** у локальному **`hpa.yaml`** поруч із `Deployment`. Якщо **HorizontalPodAutoscaler** потрапляє в дерево Kustomize з **`resources`** / **`components`** / **`bases`**, у **`…/base/kustomization.yaml`** додай strategic-merge patch з **`$patch: delete`** і **`kind: HorizontalPodAutoscaler`** (і **`metadata.name`**, що збігається з ресурсом, який треба прибрати). **`check k8s`** перевіряє наявність такого patch, коли в дереві base одночасно є **Deployment** і **HPA**.
|
|
115
|
+
|
|
116
|
+
### Поза base (оверлеї, окремі каталоги)
|
|
117
|
+
|
|
118
|
+
Якщо ще не підібрано власні ліміти під сервіс, орієнтир для **`requests`**:
|
|
104
119
|
|
|
105
120
|
```yaml
|
|
106
121
|
resources:
|
|
107
122
|
requests:
|
|
108
|
-
cpu: '0.5'
|
|
123
|
+
cpu: '0.5'
|
|
124
|
+
memory: '512Mi'
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
У прод-оверлеях підіймай **`cpu` / `memory`** до реального споживання через **`patches`** або окремі фрагменти маніфестів. **`check k8s`** не вимагає саме **`0.5` / `512Mi`** поза base — лише непорожні **`requests.cpu`** і **`requests.memory`**.
|
|
128
|
+
|
|
129
|
+
```yaml title="k8s/prod/kustomization.yaml (фрагмент)"
|
|
130
|
+
patches:
|
|
131
|
+
- target:
|
|
132
|
+
kind: Deployment
|
|
133
|
+
name: backend-api
|
|
134
|
+
patch: |-
|
|
135
|
+
- op: replace
|
|
136
|
+
path: /spec/template/spec/containers/0/resources/requests/cpu
|
|
137
|
+
value: '500m'
|
|
138
|
+
- op: replace
|
|
139
|
+
path: /spec/template/spec/containers/0/resources/requests/memory
|
|
140
|
+
value: 1Gi
|
|
109
141
|
```
|
|
110
142
|
|
|
111
|
-
|
|
143
|
+
Поле **`imagePullPolicy`** скрипт **не** перевіряє (залишається політиці Kubernetes за тегом образу).
|
|
112
144
|
|
|
113
145
|
Образ **`hasura/graphql-engine`**: дозволений лише канонічний тег із константи **`HASURA_GRAPHQL_ENGINE_IMAGE`** у **`check-k8s.mjs`** (допускається префікс **`docker.io/`**); решта — помилка **check k8s**.
|
|
114
146
|
|
|
@@ -341,15 +373,17 @@ images:
|
|
|
341
373
|
|
|
342
374
|
**`check k8s`:** заборонено **`kind: Ingress`**.
|
|
343
375
|
|
|
344
|
-
## Deployment:
|
|
376
|
+
## Deployment: `pdb.yaml`, `topologySpreadConstraints`, HPA поза dev-base
|
|
345
377
|
|
|
346
|
-
Для **кожного** `kind: Deployment` у каталозі **`…/k8s/…/base/`** (у будь-якому файлі `.yaml`, наприклад **`deploy.yaml`**, **`deployment.yaml`**) у **тому ж каталозі**
|
|
378
|
+
Для **кожного** `kind: Deployment` у каталозі **`…/k8s/…/base/`** (у будь-якому файлі `.yaml`, наприклад **`deploy.yaml`**, **`deployment.yaml`**) у **тому ж каталозі** має бути **`pdb.yaml`** (PDB), а сам Deployment — канонічні **`spec.template.spec.topologySpreadConstraints`**. Локальний **`hpa.yaml`** у каталозі **`…/base/`** **заборонено** — для dev HPA з дерева Kustomize прибирається через **`$patch: delete`** у **`base/kustomization.yaml`** (див. розділ про **`resources.requests`** вище).
|
|
347
379
|
|
|
348
|
-
|
|
380
|
+
У **не-base** оверлеях поруч із `Deployment` лишається звична схема: окремий **`hpa.yaml`**, якщо потрібен HPA для цього середовища. **`check k8s`** звіряє прив’язку за іменами:
|
|
381
|
+
|
|
382
|
+
- **`hpa.yaml`** (поза **`…/base/`**) — `autoscaling/v2`, `HorizontalPodAutoscaler`, `spec.scaleTargetRef.name` **= `metadata.name`** Deployment.
|
|
349
383
|
- **`pdb.yaml`** — `policy/v1`, `PodDisruptionBudget`, `spec.selector.matchLabels.app` **= `spec.selector.matchLabels.app`** Deployment.
|
|
350
384
|
- **`topologySpreadConstraints`** — запис з `maxSkew: 1`, `topologyKey: kubernetes.io/hostname`, `whenUnsatisfiable: ScheduleAnyway`, `labelSelector.matchLabels.app` рівне тій самій мітці `app`.
|
|
351
385
|
|
|
352
|
-
**Kustomize `base` і overlay:** у дереві, зібраному з `k8s/…/base/kustomization.yaml` (`resources` / `bases` / `components` / `crds`, рекурсивно), **HorizontalPodAutoscaler** і **PodDisruptionBudget** допустимі **лише якщо** в тому ж дереві kustomize є хоча б один **`Deployment`** у YAML під **`…/k8s/…/base/`**.
|
|
386
|
+
**Kustomize `base` і overlay:** у дереві, зібраному з `k8s/…/base/kustomization.yaml` (`resources` / `bases` / `components` / `crds`, рекурсивно), **HorizontalPodAutoscaler** і **PodDisruptionBudget** допустимі **лише якщо** в тому ж дереві kustomize є хоча б один **`Deployment`** у YAML під **`…/k8s/…/base/`**. Якщо після збору в дереві є і **Deployment**, і **HPA**, **base** `kustomization.yaml` має містити strategic-merge видалення **HorizontalPodAutoscaler** (`$patch: delete`). У `kustomization.yaml` overlay, який підключає цей `base`, не додавай окремі YAML-файли з HPA / PDB, доки в наслідуваному `base` у дереві не з’явиться такий Deployment. Перевіряє **`check-k8s.mjs`**.
|
|
353
387
|
|
|
354
388
|
**Локальні шляхи в `kustomization.yaml`:** кожен запис без `://` (remote) з `resources` / `bases` / `components` / `crds`, `patchesStrategicMerge`, `patches[].path`, `patchesJson6902[].path`, `configurations[]`, `replacements[].path` має вказувати на **існуючий** у репозиторії файл (`.yaml` / `.yml`) або **каталог**; биті посилання — помилка **`check k8s`**.
|
|
355
389
|
|
|
@@ -359,7 +393,7 @@ images:
|
|
|
359
393
|
|
|
360
394
|
**Dev-like середовища** — сегмент `base`, `dev`, або з суфіксом `-qa` (напр. `tr-qa`):
|
|
361
395
|
|
|
362
|
-
- HPA
|
|
396
|
+
- У **зібраному** маніфесті (після Kustomize), якщо лишився **HPA**: `minReplicas` — рівно **1**, `maxReplicas` — рівно **1**.
|
|
363
397
|
- PDB: `minAvailable` — рівно **0**.
|
|
364
398
|
|
|
365
399
|
**Прод-середовища** — усе інше (будь-який overlay без суфікса `-qa`):
|
|
@@ -369,12 +403,12 @@ images:
|
|
|
369
403
|
|
|
370
404
|
### Прод-оверрайди у `kustomization.yaml`
|
|
371
405
|
|
|
372
|
-
|
|
406
|
+
У прод-накладенні `kustomization.yaml` у `patches[]` **обов’язкові** перевизначення залежно від того, що дає успадковане **base**-дерево:
|
|
373
407
|
|
|
374
|
-
-
|
|
375
|
-
-
|
|
408
|
+
- **`PodDisruptionBudget`**: `spec.minAvailable` — якщо в base-дереві є **Deployment** і **PDB** (типово dev-like `0` → прод ≥1).
|
|
409
|
+
- **`HorizontalPodAutoscaler`**: `spec.minReplicas` **і** `spec.maxReplicas` — **лише якщо** в base-дереві є **HPA**, який **не** прибирається в **base** через **`$patch: delete`**. Якщо base видаляє HPA для dev, у прод-оверлеї додай **HPA** окремим YAML у **`resources`** (або patches на ресурс, що з’являється з іншого шару) з прод-мінімумами.
|
|
376
410
|
|
|
377
|
-
Формат patch — JSON6902 або Strategic Merge; `check k8s` перевіряє **наявність**
|
|
411
|
+
Формат patch — JSON6902 або Strategic Merge; `check k8s` перевіряє **наявність** відповідних JSON Pointer-ів у **`patches[]`**.
|
|
378
412
|
|
|
379
413
|
```yaml title="k8s/prod/kustomization.yaml (фрагмент)"
|
|
380
414
|
patches:
|
|
@@ -397,9 +431,30 @@ patches:
|
|
|
397
431
|
value: 1
|
|
398
432
|
```
|
|
399
433
|
|
|
434
|
+
### Приклад: прибрати HPA у `base/kustomization.yaml`
|
|
435
|
+
|
|
436
|
+
Якщо **HPA** підключений з **`../component`** (або іншого шляху в **`resources`**), а у dev він не потрібен:
|
|
437
|
+
|
|
438
|
+
```yaml title="k8s/base/kustomization.yaml (фрагмент)"
|
|
439
|
+
resources:
|
|
440
|
+
- ../component
|
|
441
|
+
- deploy.yaml
|
|
442
|
+
- pdb.yaml
|
|
443
|
+
patches:
|
|
444
|
+
- target:
|
|
445
|
+
kind: HorizontalPodAutoscaler
|
|
446
|
+
name: backend-api
|
|
447
|
+
patch: |-
|
|
448
|
+
$patch: delete
|
|
449
|
+
apiVersion: autoscaling/v2
|
|
450
|
+
kind: HorizontalPodAutoscaler
|
|
451
|
+
metadata:
|
|
452
|
+
name: backend-api
|
|
453
|
+
```
|
|
454
|
+
|
|
400
455
|
### Приклади
|
|
401
456
|
|
|
402
|
-
```yaml title="k8s/base/hpa.yaml"
|
|
457
|
+
```yaml title="k8s/prod/hpa.yaml (або компонент з HPA — не в …/base/ локальному hpa.yaml)"
|
|
403
458
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.33.9-standalone-strict/horizontalpodautoscaler-autoscaling-v2.json
|
|
404
459
|
apiVersion: autoscaling/v2
|
|
405
460
|
kind: HorizontalPodAutoscaler
|
|
@@ -410,8 +465,8 @@ spec:
|
|
|
410
465
|
apiVersion: apps/v1
|
|
411
466
|
kind: Deployment
|
|
412
467
|
name: backend-api
|
|
413
|
-
minReplicas:
|
|
414
|
-
maxReplicas:
|
|
468
|
+
minReplicas: 2
|
|
469
|
+
maxReplicas: 10
|
|
415
470
|
metrics:
|
|
416
471
|
- type: Resource
|
|
417
472
|
resource:
|
|
@@ -456,6 +511,13 @@ spec:
|
|
|
456
511
|
spec:
|
|
457
512
|
template:
|
|
458
513
|
spec:
|
|
514
|
+
containers:
|
|
515
|
+
- name: backend-api
|
|
516
|
+
image: example.registry/backend-api:tag
|
|
517
|
+
resources:
|
|
518
|
+
requests:
|
|
519
|
+
cpu: '0.02'
|
|
520
|
+
memory: '128Mi'
|
|
459
521
|
topologySpreadConstraints:
|
|
460
522
|
- maxSkew: 1
|
|
461
523
|
topologyKey: kubernetes.io/hostname
|
package/package.json
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Перевірка `dependencies` для правила `js-bun-redis.mdc` — паралель до
|
|
2
|
+
# `npm/policy/js_bun_db/package_json/package_json.rego`.
|
|
3
|
+
#
|
|
4
|
+
# Запуск (локально, для будь-якого `package.json` у дереві):
|
|
5
|
+
# conftest test path/to/package.json -p npm/policy/js_bun_redis \
|
|
6
|
+
# --namespace js_bun_redis.package_json
|
|
7
|
+
#
|
|
8
|
+
# Перевіряє: у `dependencies` не повинно бути `ioredis`, `node-redis`,
|
|
9
|
+
# `redis` або жодного з підпакетів `@redis/*` — заміна на Bun native Redis
|
|
10
|
+
# (https://bun.com/docs/runtime/redis).
|
|
11
|
+
#
|
|
12
|
+
# AST-скан коду (`import` / `require` / dynamic `import()` тих самих пакетів)
|
|
13
|
+
# лишається у `npm/scripts/check-js-bun-redis.mjs` (потребує `oxc-parser`).
|
|
14
|
+
#
|
|
15
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
16
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
17
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
18
|
+
package js_bun_redis.package_json
|
|
19
|
+
|
|
20
|
+
import rego.v1
|
|
21
|
+
|
|
22
|
+
forbidden_dependencies := {
|
|
23
|
+
"ioredis",
|
|
24
|
+
"node-redis",
|
|
25
|
+
"redis",
|
|
26
|
+
"@redis/client",
|
|
27
|
+
"@redis/json",
|
|
28
|
+
"@redis/search",
|
|
29
|
+
"@redis/time-series",
|
|
30
|
+
"@redis/bloom",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
deny contains msg if {
|
|
34
|
+
some pkg_name in forbidden_dependencies
|
|
35
|
+
pkg_name in object.keys(object.get(input, "dependencies", {}))
|
|
36
|
+
msg := sprintf("dependencies містить заборонений %q — заміни на Bun native Redis (js-bun-redis.mdc)", [pkg_name])
|
|
37
|
+
}
|
package/scripts/auto-rules.mjs
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Автовизначення правил для `.n-cursor.json` за умовами з `npm/bin/auto-rules.md`.
|
|
3
3
|
*
|
|
4
4
|
* Модуль аналізує дерево проєкту (наявність файлів/директорій, `gql\`...\`` у source,
|
|
5
|
-
* залежності `mssql` / `pg` / `pg-format` / `mysql2`
|
|
6
|
-
* `package.json`, `config.yaml` з рядком
|
|
7
|
-
* та повертає ідентифікатори правил, які потрібно автододати.
|
|
5
|
+
* залежності `mssql` / `pg` / `pg-format` / `mysql2` / `ioredis` / `node-redis` у `package.json`,
|
|
6
|
+
* імпорт `sql`/`SQL` з `bun`, кореневий `package.json`, `config.yaml` з рядком
|
|
7
|
+
* `metadata_directory: metadata` для hasura) та повертає ідентифікатори правил, які потрібно автододати.
|
|
8
8
|
*
|
|
9
9
|
* Враховує винятки `disable-rules`: елементи зі списку не додаються автоматично.
|
|
10
10
|
*
|
|
@@ -39,6 +39,7 @@ export const AUTO_RULE_ORDER = Object.freeze([
|
|
|
39
39
|
'js-lint',
|
|
40
40
|
'js-mssql',
|
|
41
41
|
'js-bun-db',
|
|
42
|
+
'js-bun-redis',
|
|
42
43
|
'js-run',
|
|
43
44
|
'k8s',
|
|
44
45
|
'nginx-default-tpl',
|
|
@@ -602,10 +603,18 @@ export async function detectAutoRules({
|
|
|
602
603
|
: null
|
|
603
604
|
)
|
|
604
605
|
const isAbie = typeof repositoryUrl === 'string' && repositoryUrl.toLowerCase().includes(ABIE_REPOSITORY_URL_MARKER)
|
|
605
|
-
const depHits = await collectDependencyKeysPresentInPackageJsonTree(root, [
|
|
606
|
+
const depHits = await collectDependencyKeysPresentInPackageJsonTree(root, [
|
|
607
|
+
'mssql',
|
|
608
|
+
'pg',
|
|
609
|
+
'pg-format',
|
|
610
|
+
'mysql2',
|
|
611
|
+
'ioredis',
|
|
612
|
+
'node-redis'
|
|
613
|
+
])
|
|
606
614
|
const hasMssqlDependency = depHits.has('mssql')
|
|
607
615
|
const hasJsBunDbSignal =
|
|
608
616
|
depHits.has('pg') || depHits.has('pg-format') || depHits.has('mysql2') || facts.hasBunSqlImport
|
|
617
|
+
const hasJsBunRedisSignal = depHits.has('ioredis') || depHits.has('node-redis')
|
|
609
618
|
const hasNestedNodePackage = await hasNestedPackageJsonWithoutViteDevDependency(root)
|
|
610
619
|
|
|
611
620
|
/** @type {string[]} */
|
|
@@ -634,6 +643,7 @@ export async function detectAutoRules({
|
|
|
634
643
|
{ enabled: facts.hasJsLikeSource, id: 'js-lint' },
|
|
635
644
|
{ enabled: hasMssqlDependency, id: 'js-mssql' },
|
|
636
645
|
{ enabled: hasJsBunDbSignal, id: 'js-bun-db' },
|
|
646
|
+
{ enabled: hasJsBunRedisSignal, id: 'js-bun-redis' },
|
|
637
647
|
{ enabled: hasNestedNodePackage, id: 'js-run' },
|
|
638
648
|
{ enabled: facts.hasK8sDir, id: 'k8s' },
|
|
639
649
|
{ enabled: facts.hasNginxDefaultTplFile, id: 'nginx-default-tpl' },
|
package/scripts/auto-skills.mjs
CHANGED
|
@@ -12,13 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
/** Порядок автододавання skills відповідно до `auto-skills.md`. */
|
|
15
|
-
export const AUTO_SKILL_ORDER = Object.freeze([
|
|
16
|
-
'abie-kustomize',
|
|
17
|
-
'fix',
|
|
18
|
-
'lint',
|
|
19
|
-
'publish-telegram',
|
|
20
|
-
'taze'
|
|
21
|
-
])
|
|
15
|
+
export const AUTO_SKILL_ORDER = Object.freeze(['abie-kustomize', 'fix', 'lint', 'publish-telegram', 'taze'])
|
|
22
16
|
|
|
23
17
|
/**
|
|
24
18
|
* Залежність скілів від правил (`auto-skills.md` синтаксис `skill - [rules]`).
|
package/scripts/check-hasura.mjs
CHANGED
|
@@ -148,9 +148,8 @@ async function checkEnvFile(relPath, expected, reporter) {
|
|
|
148
148
|
const value = m[1].trim()
|
|
149
149
|
const parsed = parseInternalHasuraEndpoint(value)
|
|
150
150
|
if (!parsed.ok) {
|
|
151
|
-
|
|
152
151
|
const example =
|
|
153
|
-
|
|
152
|
+
'https://<service>.<namespace>.svc.<cluster>.internal:<port> або http://<service>.<namespace>.svc.cluster.local:<port>'
|
|
154
153
|
fail(
|
|
155
154
|
`${relPath}: HASURA_GRAPHQL_ENDPOINT="${value}" — потрібен внутрішній кластерний URL виду ${example} (hasura.mdc)`
|
|
156
155
|
)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Перевіряє правило `js-bun-redis.mdc`.
|
|
3
|
+
*
|
|
4
|
+
* Заборонено в JS/TS-джерелах будь-який `import` / `require` / динамічний `import()` пакетів
|
|
5
|
+
* `ioredis`, `node-redis`, `redis` (та підпакетів `@redis/*`, підшляхів `ioredis/...` /
|
|
6
|
+
* `redis/...`). Замість них треба використовувати Bun native Redis:
|
|
7
|
+
* `import { redis } from 'bun'` (<https://bun.com/docs/runtime/redis>).
|
|
8
|
+
*
|
|
9
|
+
* Перевірку `dependencies` (заборона `ioredis` / `node-redis` / `redis` / `@redis/*` у будь-якому
|
|
10
|
+
* `package.json`) винесено в Rego-полісі `npm/policy/js_bun_redis/package_json/`; її запускає
|
|
11
|
+
* `bun run lint-conftest`. Тут лишився AST-скан коду через `oxc-parser`.
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync } from 'node:fs'
|
|
14
|
+
import { readFile } from 'node:fs/promises'
|
|
15
|
+
import { join, relative } from 'node:path'
|
|
16
|
+
|
|
17
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
18
|
+
import { loadCursorIgnorePaths } from './utils/load-cursor-config.mjs'
|
|
19
|
+
import { findRedisImportsInText, isRedisScanSourceFile, shouldSkipFileForRedisScan } from './utils/redis-imports.mjs'
|
|
20
|
+
import { walkDir } from './utils/walkDir.mjs'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Збирає абсолютні шляхи JS/TS джерел у репозиторії для скану заборонених redis-імпортів.
|
|
24
|
+
* @param {string} repoRoot абсолютний шлях до кореня репозиторію
|
|
25
|
+
* @param {string[]} ignorePaths абсолютні шляхи каталогів, повністю виключених з обходу
|
|
26
|
+
* @returns {Promise<string[]>} абсолютні шляхи, відсортовані за відносним шляхом
|
|
27
|
+
*/
|
|
28
|
+
async function findAllSourcePathsForRedisScan(repoRoot, ignorePaths) {
|
|
29
|
+
/** @type {string[]} */
|
|
30
|
+
const paths = []
|
|
31
|
+
await walkDir(
|
|
32
|
+
repoRoot,
|
|
33
|
+
absPath => {
|
|
34
|
+
const rel = relative(repoRoot, absPath).split('\\').join('/')
|
|
35
|
+
if (isRedisScanSourceFile(rel) && !shouldSkipFileForRedisScan(rel)) {
|
|
36
|
+
paths.push(absPath)
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
ignorePaths
|
|
40
|
+
)
|
|
41
|
+
paths.sort((a, b) => relative(repoRoot, a).localeCompare(relative(repoRoot, b)))
|
|
42
|
+
return paths
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Сканує JS/TS-джерела на заборонені імпорти/require пакетів `ioredis` / `node-redis` / `redis`.
|
|
47
|
+
* @param {string[]} sourcePaths абсолютні шляхи джерел
|
|
48
|
+
* @param {string} repoRoot абсолютний шлях до кореня
|
|
49
|
+
* @param {(msg: string) => void} fail callback при помилці
|
|
50
|
+
* @returns {Promise<number>} кількість знайдених порушень
|
|
51
|
+
*/
|
|
52
|
+
async function scanSourcesForRedisImports(sourcePaths, repoRoot, fail) {
|
|
53
|
+
let violations = 0
|
|
54
|
+
for (const absPath of sourcePaths) {
|
|
55
|
+
const rel = relative(repoRoot, absPath).split('\\').join('/')
|
|
56
|
+
const content = await readFile(absPath, 'utf8')
|
|
57
|
+
for (const v of findRedisImportsInText(content, rel)) {
|
|
58
|
+
violations++
|
|
59
|
+
fail(
|
|
60
|
+
`js-bun-redis: ${rel}:${v.line} — заміни '${v.module}' на Bun native Redis ` +
|
|
61
|
+
`(import { redis } from 'bun', https://bun.com/docs/runtime/redis): ${v.snippet}`
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return violations
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Перевіряє відповідність проєкту правилу `js-bun-redis.mdc`.
|
|
70
|
+
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
71
|
+
*/
|
|
72
|
+
export async function check() {
|
|
73
|
+
const reporter = createCheckReporter()
|
|
74
|
+
const { pass, fail } = reporter
|
|
75
|
+
|
|
76
|
+
const repoRoot = process.cwd()
|
|
77
|
+
if (!existsSync(join(repoRoot, 'package.json'))) {
|
|
78
|
+
pass('js-bun-redis: package.json у корені відсутній — перевірку пропущено')
|
|
79
|
+
return reporter.getExitCode()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const ignorePaths = await loadCursorIgnorePaths(repoRoot)
|
|
83
|
+
const sourcePaths = await findAllSourcePathsForRedisScan(repoRoot, ignorePaths)
|
|
84
|
+
if (sourcePaths.length === 0) {
|
|
85
|
+
pass('js-bun-redis: немає JS/TS файлів для скану імпортів ioredis / node-redis / redis')
|
|
86
|
+
return reporter.getExitCode()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const violations = await scanSourcesForRedisImports(sourcePaths, repoRoot, fail)
|
|
90
|
+
if (violations === 0) {
|
|
91
|
+
pass(
|
|
92
|
+
"js-bun-redis: немає імпортів 'ioredis' / 'node-redis' / 'redis' / '@redis/*' у джерелах " +
|
|
93
|
+
'(використовується Bun native Redis або redis взагалі не задіяно)'
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return reporter.getExitCode()
|
|
98
|
+
}
|