@nitra/cursor 1.8.73 → 1.8.74
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/mdc/abie.mdc +20 -16
- package/mdc/k8s.mdc +2 -12
- package/package.json +1 -1
- package/scripts/check-abie.mjs +22 -21
- package/scripts/utils/check-reporter.mjs +1 -1
package/mdc/abie.mdc
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Правила для проєктів AbInBev Efes
|
|
3
3
|
alwaysApply: true
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.7'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Правило **abie** для споживачів **@nitra/cursor**: **k8s** (
|
|
7
|
+
Правило **abie** для споживачів **@nitra/cursor**: **k8s** (Deployment + **HealthCheckPolicy** у **`hc.yaml`**, overlay **ua** / **ru** — **nodeSelector**, **HTTPRoute** (будь-який непорожній **`target.name`**), видалення **HealthCheckPolicy** у **ru**), а також гілки **dev**, **ua**, **ru** у **clean-merged-branch**.
|
|
8
|
+
|
|
9
|
+
**`npx @nitra/cursor check abie`** виконується лише якщо в **`.n-cursor.json`** у **`rules`** є **`abie`** — інакше вихід **0** без зауважень.
|
|
10
|
+
|
|
11
|
+
**Канон перевірки** — **`npm/scripts/check-abie.mjs`**: верхній JSDoc і реалізація задають точні умови, допустимі домени для hostnames, тексти помилок. Нижче — зміст правила й орієнтовні фрагменти YAML; не дублюй тут покроковий алгоритм зі скрипта.
|
|
8
12
|
|
|
9
13
|
## k8s: `hc.yaml` поруч із Deployment
|
|
10
14
|
|
|
11
|
-
Якщо під **`k8s`** є
|
|
15
|
+
Якщо під **`k8s`** є **Deployment**, у **тій самій директорії** має бути **`hc.yaml`** з **HealthCheckPolicy** (**`networking.gke.io/v1`**): коректний modeline **`$schema`**, **`/healthz`**, порт **8080**, **`targetRef.name`** = **`metadata.name`**.
|
|
12
16
|
|
|
13
17
|
```yaml title="hc.yaml"
|
|
14
18
|
# yaml-language-server: $schema=https://datreeio.github.io/CRDs-catalog/networking.gke.io/healthcheckpolicy_v1.json
|
|
@@ -30,19 +34,19 @@ spec:
|
|
|
30
34
|
name: СЕРВІС
|
|
31
35
|
```
|
|
32
36
|
|
|
33
|
-
## k8s: overlay **HTTPRoute**
|
|
37
|
+
## k8s: overlay **HTTPRoute** (**ua** / **ru**)
|
|
34
38
|
|
|
35
|
-
|
|
39
|
+
За наявності **Deployment** під **k8s** у **кожному** **`ua/kustomization.yaml`** та **`ru/kustomization.yaml`** потрібні **inline JSON6902** у **`patches`**: **target** **`kind: HTTPRoute`**, **непорожній `name`** (як у маніфесті маршруту). Мають бути зміни **`/spec/hostnames`** (домени abie — у скрипті) та **`/spec/parentRefs/0/namespace`** (**`ua`** / **`ru`**). Для **ru** — анотація **`gwin.yandex.cloud/rules.http.upgradeTypes: websocket`**. Як обирати **`op`** (**add** / **replace** тощо) у patch — **k8s.mdc** (розділ про JSON patch у kustomization).
|
|
36
40
|
|
|
37
41
|
```yaml title="…/ua/kustomization.yaml (фрагмент)"
|
|
38
42
|
- target:
|
|
39
43
|
kind: HTTPRoute
|
|
40
|
-
name:
|
|
44
|
+
name: my-httproute
|
|
41
45
|
patch: |-
|
|
42
46
|
- op: replace
|
|
43
47
|
path: /spec/hostnames
|
|
44
48
|
value:
|
|
45
|
-
- "abie.app" #
|
|
49
|
+
- "abie.app" # зокрема vybeerai.com.ua, *.vybeerai.com.ua, *.abie.app
|
|
46
50
|
- op: replace
|
|
47
51
|
path: /spec/parentRefs/0/namespace
|
|
48
52
|
value: ua
|
|
@@ -51,12 +55,12 @@ spec:
|
|
|
51
55
|
```yaml title="…/ru/kustomization.yaml (фрагмент)"
|
|
52
56
|
- target:
|
|
53
57
|
kind: HTTPRoute
|
|
54
|
-
name:
|
|
58
|
+
name: my-httproute
|
|
55
59
|
patch: |-
|
|
56
60
|
- op: replace
|
|
57
61
|
path: /spec/hostnames
|
|
58
62
|
value:
|
|
59
|
-
- "napitkivmeste.tech" #
|
|
63
|
+
- "napitkivmeste.tech" # зокрема выбирайонлайн.рф, *.napitkivmeste.tech, *.выбирайонлайн.рф
|
|
60
64
|
- op: replace
|
|
61
65
|
path: /spec/parentRefs/0/namespace
|
|
62
66
|
value: ru
|
|
@@ -65,7 +69,7 @@ spec:
|
|
|
65
69
|
```yaml title="…/ru/kustomization.yaml (фрагмент)"
|
|
66
70
|
- target:
|
|
67
71
|
kind: HTTPRoute
|
|
68
|
-
name:
|
|
72
|
+
name: my-httproute
|
|
69
73
|
patch: |-
|
|
70
74
|
- op: add
|
|
71
75
|
path: /metadata/annotations
|
|
@@ -73,9 +77,9 @@ spec:
|
|
|
73
77
|
gwin.yandex.cloud/rules.http.upgradeTypes: "websocket"
|
|
74
78
|
```
|
|
75
79
|
|
|
76
|
-
## k8s: overlay
|
|
80
|
+
## k8s: overlay **ru** і HealthCheckPolicy
|
|
77
81
|
|
|
78
|
-
Якщо в дереві **k8s** є **HealthCheckPolicy**,
|
|
82
|
+
Якщо в дереві **k8s** є **HealthCheckPolicy**, у **`ru/kustomization.yaml`** має бути patch **`$patch: delete`** для політики (узгоджено з **k8s.mdc**; перевірка в **`check-k8s.mjs`**, **`ruKustomizationHasHealthCheckDeletePatch`**). Підстав реальне ім’я замість **`СЕРВІС`**:
|
|
79
83
|
|
|
80
84
|
```yaml title="…/ru/kustomization.yaml (фрагмент)"
|
|
81
85
|
patches:
|
|
@@ -89,9 +93,9 @@ patches:
|
|
|
89
93
|
$patch: delete
|
|
90
94
|
```
|
|
91
95
|
|
|
92
|
-
## k8s: overlay
|
|
96
|
+
## k8s: overlay **ua** / **ru** і nodeSelector
|
|
93
97
|
|
|
94
|
-
|
|
98
|
+
За наявності **Deployment** під **k8s** у **кожному** **`…/ua/kustomization.yaml`** та **`…/ru/kustomization.yaml`** — patch на **`kind: Deployment`**: **ua** — **`spec.template.spec.nodeSelector`** з **`preem: false`**; **ru** — **`spec.template.spec.nodeSelector`** з **`yandex.cloud/preemptible: false`**. Форму **JSON6902** (шлях **`/spec/template/spec/nodeSelector`**, **`op`**) див. **k8s.mdc**.
|
|
95
99
|
|
|
96
100
|
```yaml title="…/ua/kustomization.yaml (фрагмент)"
|
|
97
101
|
patches:
|
|
@@ -119,7 +123,7 @@ patches:
|
|
|
119
123
|
|
|
120
124
|
### Базовий Deployment (`…/base/`)
|
|
121
125
|
|
|
122
|
-
Якщо **Deployment** у YAML під **`k8s`** лежить у шляху з сегментом **`base
|
|
126
|
+
Якщо **Deployment** у YAML під **`k8s`** лежить у шляху з сегментом **`base`**, у **`spec.template.spec.nodeSelector`** має бути **`preem`** зі значенням **істинно** (**`true`** або рядок **`'true'`**); overlay **ua** / **ru** підміняє селектор.
|
|
123
127
|
|
|
124
128
|
```yaml title="…/base/deploy.yaml (фрагмент)"
|
|
125
129
|
spec:
|
|
@@ -131,7 +135,7 @@ spec:
|
|
|
131
135
|
|
|
132
136
|
## Git branches
|
|
133
137
|
|
|
134
|
-
У **`.github/workflows/clean-merged-branch.yml`** у кроці **`phpdocker-io/github-actions-delete-abandoned-branches`** значення **`with.ignore_branches`** має містити **dev**, **ua** та **ru** (разом з іншими гілками, якщо потрібно)
|
|
138
|
+
У **`.github/workflows/clean-merged-branch.yml`** у кроці **`phpdocker-io/github-actions-delete-abandoned-branches`** значення **`with.ignore_branches`** має містити **dev**, **ua** та **ru** (разом з іншими гілками, якщо потрібно):
|
|
135
139
|
|
|
136
140
|
```yaml title=".github/workflows/clean-merged-branch.yml (фрагмент)"
|
|
137
141
|
with:
|
package/mdc/k8s.mdc
CHANGED
|
@@ -206,20 +206,10 @@ patches:
|
|
|
206
206
|
value: dev
|
|
207
207
|
```
|
|
208
208
|
|
|
209
|
-
4.
|
|
210
|
-
|
|
211
|
-
```yaml title="overlay/kustomization.yaml (фрагмент)"
|
|
212
|
-
- op: remove
|
|
213
|
-
path: /spec/template/spec/nodeSelector
|
|
214
|
-
- op: add
|
|
215
|
-
path: /spec/template/spec/nodeSelector
|
|
216
|
-
value:
|
|
217
|
-
preem: "false"
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
заміняй на replace:
|
|
209
|
+
4. **JSON patch у kustomization:** де можливо, змінюй ресурс через **`op: replace`** (одна операція на `path`), а не пару **`remove` + `add`** на той самий шлях. **`add`** / **`remove`** лишай лише коли **`replace`** не підходить (наприклад додати новий ключ або прибрати поле без заміни).
|
|
221
210
|
|
|
222
211
|
```yaml title="overlay/kustomization.yaml (фрагмент)"
|
|
212
|
+
patches:
|
|
223
213
|
- target:
|
|
224
214
|
kind: Deployment
|
|
225
215
|
name: x
|
package/package.json
CHANGED
package/scripts/check-abie.mjs
CHANGED
|
@@ -21,9 +21,10 @@
|
|
|
21
21
|
* має бути inline **JSON6902** patch на **`kind: Deployment`**: для **ua** — **`op: add`**, **`path: /spec/template/spec/nodeSelector`**,
|
|
22
22
|
* **`preem: false`**; для **ru** — **`op: replace`**, той самий **path**, **`yandex.cloud/preemptible: false`** (див. abie.mdc).
|
|
23
23
|
*
|
|
24
|
-
* **HTTPRoute
|
|
25
|
-
* inline **JSON6902** на **`kind: HTTPRoute
|
|
26
|
-
* **replace** **`/spec/parentRefs/0/namespace`** (**ua** / **ru**); для **ru** також
|
|
24
|
+
* **HTTPRoute (overlay):** за тієї ж умови (**Deployment** під **k8s**) у **кожному** **`ua`/`ru` kustomization** має бути
|
|
25
|
+
* inline **JSON6902** на **`kind: HTTPRoute`** з **непорожнім `target.name`** (будь-яке ім’я): **replace** **`/spec/hostnames`**
|
|
26
|
+
* (домени з abie.mdc), **replace** **`/spec/parentRefs/0/namespace`** (**ua** / **ru**); для **ru** також
|
|
27
|
+
* **`gwin.yandex.cloud/rules.http.upgradeTypes: websocket`**.
|
|
27
28
|
*/
|
|
28
29
|
import { existsSync } from 'node:fs'
|
|
29
30
|
import { readFile } from 'node:fs/promises'
|
|
@@ -484,11 +485,11 @@ const ABIE_RU_HTTPROUTE_HOST_MARKERS = [
|
|
|
484
485
|
]
|
|
485
486
|
|
|
486
487
|
/**
|
|
487
|
-
* Збирає тексти inline **patch** для **HTTPRoute
|
|
488
|
+
* Збирає тексти inline **patch** для **HTTPRoute** (будь-який непорожній **target.name**) з одного документа **Kustomization**.
|
|
488
489
|
* @param {import('yaml').Document} doc документ після **parseAllDocuments**
|
|
489
490
|
* @returns {string[]} непорожні рядки **patch**
|
|
490
491
|
*/
|
|
491
|
-
function
|
|
492
|
+
function collectAbieHttpRoutePatchStringsFromKustomizationDoc(doc) {
|
|
492
493
|
if (doc.errors.length > 0) {
|
|
493
494
|
return []
|
|
494
495
|
}
|
|
@@ -512,7 +513,7 @@ function collectNginxRunPatchStringsFromKustomizationDoc(doc) {
|
|
|
512
513
|
const target = pr.target
|
|
513
514
|
if (target !== null && typeof target === 'object' && !Array.isArray(target)) {
|
|
514
515
|
const tg = /** @type {Record<string, unknown>} */ (target)
|
|
515
|
-
if (tg.kind === 'HTTPRoute' && tg.name === '
|
|
516
|
+
if (tg.kind === 'HTTPRoute' && typeof tg.name === 'string' && tg.name.trim() !== '') {
|
|
516
517
|
const patchStr = pr.patch
|
|
517
518
|
if (typeof patchStr === 'string' && patchStr.trim() !== '') {
|
|
518
519
|
out.push(patchStr)
|
|
@@ -525,7 +526,7 @@ function collectNginxRunPatchStringsFromKustomizationDoc(doc) {
|
|
|
525
526
|
}
|
|
526
527
|
|
|
527
528
|
/**
|
|
528
|
-
* Збирає всі inline **JSON6902**-фрагменти для **HTTPRoute
|
|
529
|
+
* Збирає всі inline **JSON6902**-фрагменти для **HTTPRoute** (непорожній **target.name**) у **kustomization.yaml** (усі документи у файлі).
|
|
529
530
|
* @param {string} raw повний текст файлу
|
|
530
531
|
* @returns {string} текст для **`validateAbieNginxRunHttpRoutePatches`** (може бути порожнім)
|
|
531
532
|
*/
|
|
@@ -544,44 +545,44 @@ export function getCombinedNginxRunPatchTextFromKustomization(raw) {
|
|
|
544
545
|
/** @type {string[]} */
|
|
545
546
|
const chunks = []
|
|
546
547
|
for (const doc of docs) {
|
|
547
|
-
chunks.push(...
|
|
548
|
+
chunks.push(...collectAbieHttpRoutePatchStringsFromKustomizationDoc(doc))
|
|
548
549
|
}
|
|
549
550
|
return chunks.join('\n')
|
|
550
551
|
}
|
|
551
552
|
|
|
552
553
|
/**
|
|
553
|
-
* Перевіряє сукупний текст patch(ів) **HTTPRoute
|
|
554
|
+
* Перевіряє сукупний текст patch(ів) **HTTPRoute** (будь-яке **target.name**) на відповідність abie.mdc.
|
|
554
555
|
* @param {string} combined текст одного або кількох inline **patch**, розділених символом нового рядка
|
|
555
556
|
* @param {'ua' | 'ru'} mode **ua** або **ru**
|
|
556
557
|
* @returns {string | null} повідомлення про помилку або **null**
|
|
557
558
|
*/
|
|
558
559
|
export function validateAbieNginxRunHttpRoutePatches(combined, mode) {
|
|
559
560
|
if (typeof combined !== 'string' || combined.trim() === '') {
|
|
560
|
-
return `очікується patch target kind HTTPRoute name
|
|
561
|
+
return `очікується patch target kind HTTPRoute з непорожнім target.name (replace hostnames, parentRefs namespace ${mode}; для ru — також gwin… upgradeTypes websocket) — abie.mdc`
|
|
561
562
|
}
|
|
562
563
|
const hasHostnamesReplace = /-\s*op:\s*replace\b[\s\S]{0,200}?path:\s*\/spec\/hostnames\b/m.test(combined)
|
|
563
564
|
if (!hasHostnamesReplace) {
|
|
564
|
-
return 'HTTPRoute
|
|
565
|
+
return 'HTTPRoute: потрібен блок op replace з path /spec/hostnames (abie.mdc)'
|
|
565
566
|
}
|
|
566
567
|
const markers = mode === 'ua' ? ABIE_UA_HTTPROUTE_HOST_MARKERS : ABIE_RU_HTTPROUTE_HOST_MARKERS
|
|
567
568
|
if (!markers.some(m => combined.includes(m))) {
|
|
568
|
-
return `HTTPRoute
|
|
569
|
+
return `HTTPRoute: у value для /spec/hostnames має бути один із доменів abie (${markers.join(', ')}) — abie.mdc`
|
|
569
570
|
}
|
|
570
571
|
const namespaceOk =
|
|
571
572
|
mode === 'ua'
|
|
572
573
|
? /path:\s*\/spec\/parentRefs\/0\/namespace\b[\s\S]{0,200}?value:\s*['"]?ua['"]?(?:\s|$)/mu.test(combined)
|
|
573
574
|
: /path:\s*\/spec\/parentRefs\/0\/namespace\b[\s\S]{0,200}?value:\s*['"]?ru['"]?(?:\s|$)/mu.test(combined)
|
|
574
575
|
if (!namespaceOk) {
|
|
575
|
-
return `HTTPRoute
|
|
576
|
+
return `HTTPRoute: потрібен replace path /spec/parentRefs/0/namespace з value ${mode} (abie.mdc)`
|
|
576
577
|
}
|
|
577
578
|
if (mode === 'ru' && !/gwin\.yandex\.cloud\/rules\.http\.upgradeTypes:\s*['"]?websocket['"]?/m.test(combined)) {
|
|
578
|
-
return 'HTTPRoute
|
|
579
|
+
return 'HTTPRoute (ru): потрібна анотація gwin.yandex.cloud/rules.http.upgradeTypes: websocket (abie.mdc)'
|
|
579
580
|
}
|
|
580
581
|
return null
|
|
581
582
|
}
|
|
582
583
|
|
|
583
584
|
/**
|
|
584
|
-
* Чи **kustomization** містить валідні для abie
|
|
585
|
+
* Чи **kustomization** містить валідні для abie **patch** для **HTTPRoute** з непорожнім **target.name** (**ua** або **ru**).
|
|
585
586
|
* @param {string} raw повний текст **kustomization.yaml**
|
|
586
587
|
* @param {'ua' | 'ru'} mode overlay
|
|
587
588
|
* @returns {boolean} true, якщо **`validateAbieNginxRunHttpRoutePatches`** повертає **null**
|
|
@@ -833,7 +834,7 @@ async function ensureUaRuAbieNodeSelectorPatches(root, yamlFilesAbs, fail, passF
|
|
|
833
834
|
}
|
|
834
835
|
|
|
835
836
|
/**
|
|
836
|
-
* Якщо є **Deployment** під **k8s**, вимагає в кожному overlay **ua** та **ru** patch **HTTPRoute
|
|
837
|
+
* Якщо є **Deployment** під **k8s**, вимагає в кожному overlay **ua** та **ru** patch **HTTPRoute** (непорожній **target.name**) за abie.mdc.
|
|
837
838
|
* @param {string} root корінь репозиторію
|
|
838
839
|
* @param {string[]} yamlFilesAbs yaml під k8s
|
|
839
840
|
* @param {(msg: string) => void} fail callback
|
|
@@ -844,7 +845,7 @@ async function ensureUaRuAbieHttpRoutePatches(root, yamlFilesAbs, fail, passFn)
|
|
|
844
845
|
const uaAbsList = yamlFilesAbs.filter(abs => isUaKustomizationPath(relative(root, abs).replaceAll('\\', '/') || abs))
|
|
845
846
|
if (uaAbsList.length === 0) {
|
|
846
847
|
fail(
|
|
847
|
-
'Є Deployment у k8s — додай ua/kustomization.yaml з patch HTTPRoute
|
|
848
|
+
'Є Deployment у k8s — додай ua/kustomization.yaml з patch HTTPRoute (будь-який target.name: hostnames, parentRefs namespace ua) — abie.mdc'
|
|
848
849
|
)
|
|
849
850
|
return
|
|
850
851
|
}
|
|
@@ -864,13 +865,13 @@ async function ensureUaRuAbieHttpRoutePatches(root, yamlFilesAbs, fail, passFn)
|
|
|
864
865
|
fail(`${rel}: ${v}`)
|
|
865
866
|
return
|
|
866
867
|
}
|
|
867
|
-
passFn(`${rel}: HTTPRoute
|
|
868
|
+
passFn(`${rel}: HTTPRoute patch (ua) відповідає abie.mdc`)
|
|
868
869
|
}
|
|
869
870
|
|
|
870
871
|
const ruAbsList = yamlFilesAbs.filter(abs => isRuKustomizationPath(relative(root, abs).replaceAll('\\', '/') || abs))
|
|
871
872
|
if (ruAbsList.length === 0) {
|
|
872
873
|
fail(
|
|
873
|
-
'Є Deployment у k8s — додай ru/kustomization.yaml з patch HTTPRoute
|
|
874
|
+
'Є Deployment у k8s — додай ru/kustomization.yaml з patch HTTPRoute (будь-який target.name: hostnames, namespace ru, gwin websocket) — abie.mdc'
|
|
874
875
|
)
|
|
875
876
|
return
|
|
876
877
|
}
|
|
@@ -890,7 +891,7 @@ async function ensureUaRuAbieHttpRoutePatches(root, yamlFilesAbs, fail, passFn)
|
|
|
890
891
|
fail(`${rel}: ${v}`)
|
|
891
892
|
return
|
|
892
893
|
}
|
|
893
|
-
passFn(`${rel}: HTTPRoute
|
|
894
|
+
passFn(`${rel}: HTTPRoute patch (ru) відповідає abie.mdc`)
|
|
894
895
|
}
|
|
895
896
|
}
|
|
896
897
|
|
|
@@ -983,7 +984,7 @@ export async function check() {
|
|
|
983
984
|
if (deploymentDirs.size > 0) {
|
|
984
985
|
pass('Є Deployment — перевіряємо nodeSelector у ua/ru kustomization (abie.mdc)')
|
|
985
986
|
await ensureUaRuAbieNodeSelectorPatches(root, yamlFiles, fail, pass)
|
|
986
|
-
pass('Є Deployment — перевіряємо HTTPRoute
|
|
987
|
+
pass('Є Deployment — перевіряємо HTTPRoute у ua/ru kustomization (abie.mdc)')
|
|
987
988
|
await ensureUaRuAbieHttpRoutePatches(root, yamlFiles, fail, pass)
|
|
988
989
|
}
|
|
989
990
|
|
|
@@ -10,7 +10,7 @@ import { pass } from './pass.mjs'
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Створює пару `pass` / `fail` з накопиченням ненульового коду виходу.
|
|
13
|
-
* @returns {{ pass: typeof pass, fail: (msg: string) => void, getExitCode: () => number }}
|
|
13
|
+
* @returns {{ pass: typeof pass, fail: (msg: string) => void, getExitCode: () => number }} об’єкт з `pass`, `fail` і `getExitCode()` (0 або 1 після будь-якого `fail`)
|
|
14
14
|
*/
|
|
15
15
|
export function createCheckReporter() {
|
|
16
16
|
let exitCode = 0
|