@nitra/cursor 1.9.18 → 1.9.20

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.
@@ -7,11 +7,9 @@
7
7
  * вказує на `https://github.com/nitra/...` або `https://github.com/abinbevefes/...`
8
8
  * (інші репозиторії пропускаються без помилок — як у check-abie).
9
9
  *
10
- * Очікуваний формат URL — два варіанти кластерного DNS-суфікса:
10
+ * Очікуваний формат URL — кластерний DNS-суфікс `<cluster>.internal`:
11
11
  * - GKE / GCP: `http://<service>.<namespace>.svc.<cluster>.internal:<port>`
12
12
  * приклад: `http://contract-h.ua-contract.svc.abie-ua.internal:8080`
13
- * - стандартний k8s / Yandex Cloud: `http://<service>.<namespace>.svc.cluster.local:<port>`
14
- * приклад: `http://apruv-h-hl.ru-apruv.svc.cluster.local:8080`
15
13
  *
16
14
  * Сегменти беруться з `hasura/k8s/base/svc-hl.yaml` (`metadata.name` —
17
15
  * має закінчуватись на `-h`, headless-сервіс) і `hasura/k8s/base/namespace.yaml`
@@ -43,18 +41,15 @@ const HASURA_NAMESPACE_FILE = `${HASURA_BASE_DIR}/namespace.yaml`
43
41
 
44
42
  const ENV_FILE_RE = /\.env$/u
45
43
  const HASURA_ENDPOINT_LINE_RE = /^[ \t]*(?:export[ \t]+)?HASURA_GRAPHQL_ENDPOINT[ \t]*=[ \t]*['"]?([^'"\r\n#]+)/mu
46
- // Дозволяємо два DNS-суфікси кластера: `<name>.internal` (GKE/GCP) і `cluster.local`
47
- // (стандартний k8s / Yandex Cloud). У YC namespace.yaml + cluster mode дають коротший суфікс.
48
- const INTERNAL_HASURA_URL_RE = /^http:\/\/([^./]+)\.([^./]+)\.svc\.((?:[^./:]+\.internal)|cluster\.local):(\d+)\/?$/u
44
+ // Дозволяємо лише DNS-суфікс кластера `<name>.internal` (GKE/GCP).
45
+ const INTERNAL_HASURA_URL_RE = /^http:\/\/([^./]+)\.([^./]+)\.svc\.([^./:]+\.internal):(\d+)\/?$/u
49
46
  const INTERNAL_DNS_SUFFIX = '.internal'
50
47
 
51
48
  /**
52
49
  * Розбір значення `HASURA_GRAPHQL_ENDPOINT` як внутрішнього кластерного URL.
53
- * Дозволяє лише `http://` (TLS усередині кластера зайвий) та обидва кластерні
54
- * DNS-суфікси: `<cluster>.internal` (GKE/GCP) і `cluster.local`
55
- * (стандартний k8s / Yandex Cloud). Поле `cluster` для GKE містить ім'я
56
- * кластера без `.internal` (наприклад `abie-ua`); для YC — повний суфікс
57
- * `cluster.local` (бо своєї «назви кластера» в DNS немає).
50
+ * Дозволяє лише `http://` (TLS усередині кластера зайвий) та DNS-суфікс
51
+ * `<cluster>.internal` (GKE/GCP). Поле `cluster` містить ім'я кластера без
52
+ * `.internal` (наприклад `abie-ua`).
58
53
  * @param {string} url значення з `.env` (без огорнутих лапок)
59
54
  * @returns {{ ok: true, service: string, namespace: string, cluster: string, port: string } | { ok: false }}
60
55
  * розібрані сегменти або `{ ok: false }`, якщо формат не відповідає внутрішньому кластерному URL
@@ -65,7 +60,7 @@ export function parseInternalHasuraEndpoint(url) {
65
60
  return { ok: false }
66
61
  }
67
62
  const suffix = m[3]
68
- const cluster = suffix.endsWith(INTERNAL_DNS_SUFFIX) ? suffix.slice(0, -INTERNAL_DNS_SUFFIX.length) : suffix
63
+ const cluster = suffix.slice(0, -INTERNAL_DNS_SUFFIX.length)
69
64
  return { ok: true, service: m[1], namespace: m[2], cluster, port: m[4] }
70
65
  }
71
66
 
@@ -148,8 +143,7 @@ async function checkEnvFile(relPath, expected, reporter) {
148
143
  const value = m[1].trim()
149
144
  const parsed = parseInternalHasuraEndpoint(value)
150
145
  if (!parsed.ok) {
151
- const example =
152
- 'https://<service>.<namespace>.svc.<cluster>.internal:<port> або http://<service>.<namespace>.svc.cluster.local:<port>'
146
+ const example = "https://<service>.<namespace>.svc.<cluster>.internal:<port>"
153
147
  fail(
154
148
  `${relPath}: HASURA_GRAPHQL_ENDPOINT="${value}" — потрібен внутрішній кластерний URL виду ${example} (hasura.mdc)`
155
149
  )
@@ -97,7 +97,7 @@
97
97
  * `hpa.yaml` і `pdb.yaml` (як єдині або принаймні обов'язкові), `hpa.yaml` (валідний `autoscaling/v2`
98
98
  * HorizontalPodAutoscaler з `scaleTargetRef.name` = ім'я Deployment, dev-like `min=max=1`), `pdb.yaml` (валідний
99
99
  * `policy/v1` PodDisruptionBudget з `selector.matchLabels.app` = мітка `app` Deployment, dev-like `minAvailable=0`).
100
- * Overlays (`ua/`, `ru/`, прод-overlays) підключають `components: [- ../components]` і додають JSON6902-патчі для
100
+ * Overlays (`ua/`, прод-overlays) підключають `components: [- ../components]` і додають JSON6902-патчі для
101
101
  * прод-значень: `/spec/minReplicas`, `/spec/maxReplicas` (HPA), `/spec/minAvailable` (PDB). HPA поруч із Deployment
102
102
  * у не-base оверлеях — як раніше (див. k8s.mdc).
103
103
  * Env-залежні межі за сегментом після `/k8s/`: **dev-like** (`base`, `dev`, `*-qa`) — для HPA, що лишився після
@@ -324,10 +324,6 @@ const API_VERSION_FIELD_RE = /^\s*apiVersion:\s*(\S+)\s*$/
324
324
  const KIND_FIELD_RE = /^\s*kind:\s*(\S+)\s*$/
325
325
  const TYPE_FIELD_RE = /^\s*type:\s*(\S+)\s*$/
326
326
  const YAML_DOC_SEPARATOR_LINE_RE = /^---\s*$/
327
- const HEALTHCHECK_DELETE_RE = /\$patch:\s*delete/u
328
- const HEALTHCHECK_KIND_RE = /kind:\s*HealthCheckPolicy/u
329
- const METADATA_LINE_RE = /metadata:/u
330
- const NAME_NON_EMPTY_RE = /name:\s*\S+/u
331
327
  const K8S_BASE_KUSTOMIZATION_PATH_RE = /(^|\/)k8s\/base\/kustomization\.yaml$/u
332
328
  const K8S_BASE_SEGMENT_RE = /(^|\/)k8s\/base\//u
333
329
  const OXLINT_SCHEMA_MODELINE_RE = /^\s*#\s*yaml-language-server:\s*\$schema=\S+/u
@@ -2025,19 +2021,6 @@ export function k8sYamlFirstDocIsAlbYcHttpBackendGroup(yamlBody) {
2025
2021
  return apiVersion === 'alb.yc.io/v1alpha1' && kind === 'HttpBackendGroup'
2026
2022
  }
2027
2023
 
2028
- /**
2029
- * Чи вміст overlay **`ru/kustomization.yaml`** містить Kustomize patch видалення **HealthCheckPolicy**.
2030
- * @param {string} raw повний текст файлу
2031
- * @returns {boolean} true, якщо є `$patch: delete` і блоки kind/metadata для HealthCheckPolicy
2032
- */
2033
- export function ruKustomizationHasHealthCheckDeletePatch(raw) {
2034
- if (!HEALTHCHECK_DELETE_RE.test(raw)) return false
2035
- if (!HEALTHCHECK_KIND_RE.test(raw)) return false
2036
- if (!METADATA_LINE_RE.test(raw)) return false
2037
- if (!NAME_NON_EMPTY_RE.test(raw)) return false
2038
- return true
2039
- }
2040
-
2041
2024
  /**
2042
2025
  * Чи абсолютний шлях лежить усередині кореня репозиторію (без виходу через `..`).
2043
2026
  * @param {string} rootAbs абсолютний корінь
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Перевіряє інструментарій rego (rego.mdc): VSCode та `package.json` для проєктів,
3
+ * які мають хоча б один `.rego` файл у дереві.
4
+ *
5
+ * Cross-file gating (JS):
6
+ * 1. Walk дерева від `cwd` (з типовими skip-ами і `.n-cursor.json:ignore`).
7
+ * 2. Якщо немає жодного `.rego` — пропустити перевірку (rego-tooling не вимагається).
8
+ * 3. Інакше — для кожного канонічного файла:
9
+ * - FS-existence (з повідомленням, якщо відсутній);
10
+ * - делегувати content-валідацію rego-пакетам через `runConftestBatch`:
11
+ * `rego.vscode_extensions` — `.vscode/extensions.json`: `tsandall.opa`
12
+ * у `recommendations`;
13
+ * `rego.vscode_settings` — `.vscode/settings.json`: `[rego]` з
14
+ * `editor.defaultFormatter: "tsandall.opa"` і `editor.formatOnSave: true`;
15
+ * `rego.package_json` — `package.json#scripts.lint-rego` має бути
16
+ * канонічним `"bun ./npm/scripts/lint-rego.mjs"`.
17
+ *
18
+ * Rego-полісі глобально у `lint-conftest` НЕ реєструються — це conditional
19
+ * правило (без `.rego` файлів вимоги не діють). Plan B: Rego-authoritative +
20
+ * JS-orchestrator з `runConftestBatch`.
21
+ *
22
+ * `bun run lint-rego` (`npm/scripts/lint-rego.mjs`) — окрема перевірка САМИХ
23
+ * rego-полісі (opa check / regal lint / conftest verify), не плутати з цим
24
+ * скриптом, який перевіряє ПРОЄКТНЕ оточення для роботи з rego.
25
+ */
26
+ import { existsSync } from 'node:fs'
27
+
28
+ import { createCheckReporter } from './utils/check-reporter.mjs'
29
+ import { loadCursorIgnorePaths } from './utils/load-cursor-config.mjs'
30
+ import { runConftestBatch } from './utils/run-conftest-batch.mjs'
31
+ import { walkDir } from './utils/walkDir.mjs'
32
+
33
+ /** Список (path, namespace, policyDirRel) для трьох канонічних конфігів rego.mdc. */
34
+ const REGO_TARGETS = [
35
+ ['.vscode/extensions.json', 'rego.vscode_extensions', 'rego/vscode_extensions'],
36
+ ['.vscode/settings.json', 'rego.vscode_settings', 'rego/vscode_settings'],
37
+ ['package.json', 'rego.package_json', 'rego/package_json']
38
+ ]
39
+
40
+ /**
41
+ * Чи є хоча б один `.rego` файл у дереві від `cwd`.
42
+ * @param {string} root абсолютний шлях кореня
43
+ * @param {string[]} ignorePaths шляхи каталогів, повністю виключених з обходу
44
+ * @returns {Promise<boolean>} `true`, якщо знайдено хоча б один `.rego`
45
+ */
46
+ async function projectHasRegoFiles(root, ignorePaths) {
47
+ let found = false
48
+ await walkDir(
49
+ root,
50
+ p => {
51
+ if (p.endsWith('.rego')) {
52
+ found = true
53
+ }
54
+ },
55
+ ignorePaths
56
+ )
57
+ return found
58
+ }
59
+
60
+ /**
61
+ * Делегує content-валідацію одного канонічного конфіга rego-пакету через
62
+ * `runConftestBatch`. FS-існування — попередньо перевірено.
63
+ * @param {string} path відносний шлях до файлу
64
+ * @param {string} namespace rego-пакет (наприклад `rego.vscode_extensions`)
65
+ * @param {string} policyDirRel піддиректорія у `npm/policy/`
66
+ * @param {(msg: string) => void} pass success-репортер
67
+ * @param {(msg: string) => void} fail fail-репортер
68
+ * @returns {void}
69
+ */
70
+ function runRegoPolicyOnPath(path, namespace, policyDirRel, pass, fail) {
71
+ const violations = runConftestBatch({ policyDirRel, namespace, files: [path] })
72
+ if (violations.length === 0) {
73
+ pass(`${path} відповідає ${namespace} (rego)`)
74
+ return
75
+ }
76
+ for (const v of violations) fail(v.message)
77
+ }
78
+
79
+ /**
80
+ * Перевіряє відповідність проєкту правилам rego.mdc.
81
+ * @returns {Promise<number>} 0 — все OK, 1 — є проблеми
82
+ */
83
+ export async function check() {
84
+ const reporter = createCheckReporter()
85
+ const { pass, fail } = reporter
86
+
87
+ const root = process.cwd()
88
+ const ignorePaths = await loadCursorIgnorePaths(root)
89
+ const hasRego = await projectHasRegoFiles(root, ignorePaths)
90
+ if (!hasRego) {
91
+ pass('Немає *.rego у дереві — rego-tooling не вимагається (rego.mdc)')
92
+ return reporter.getExitCode()
93
+ }
94
+
95
+ pass('Знайдено *.rego у дереві — перевіряємо канонічні конфіги rego.mdc')
96
+
97
+ for (const [path, namespace, policyDirRel] of REGO_TARGETS) {
98
+ if (!existsSync(path)) {
99
+ fail(`${path} не існує — створи згідно rego.mdc (${namespace})`)
100
+ continue
101
+ }
102
+ runRegoPolicyOnPath(path, namespace, policyDirRel, pass, fail)
103
+ }
104
+
105
+ return reporter.getExitCode()
106
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Перевіряє інструментарій Tauri (tauri.mdc): VSCode `extensions.json` для
3
+ * проєктів, у яких є маркер Tauri.
4
+ *
5
+ * Cross-file gating (JS):
6
+ * 1. Tauri-маркер визначаємо за **будь-яким** з:
7
+ * - існує каталог `src-tauri/` у `cwd`;
8
+ * - існує файл `tauri.conf.json` у `cwd` або в workspace-пакетах;
9
+ * - кореневий `package.json#devDependencies` або `dependencies` містить
10
+ * ключ з префіксом `@tauri-apps/`.
11
+ * 2. Якщо маркера немає — пропустити перевірку (tauri-tooling не вимагається).
12
+ * 3. Інакше — для `.vscode/extensions.json` зробити FS-existence + делегувати
13
+ * content `rego.tauri.vscode_extensions` через `runConftestBatch`.
14
+ *
15
+ * Rego-полісі глобально у `lint-conftest` НЕ реєструється — це conditional
16
+ * правило. Plan B: Rego-authoritative + JS-orchestrator з `runConftestBatch`.
17
+ */
18
+ import { existsSync, statSync } from 'node:fs'
19
+ import { readFile } from 'node:fs/promises'
20
+
21
+ import { createCheckReporter } from './utils/check-reporter.mjs'
22
+ import { runConftestBatch } from './utils/run-conftest-batch.mjs'
23
+
24
+ /**
25
+ * Чи є префікс `@tauri-apps/` у ключах `dependencies` або `devDependencies`.
26
+ * @param {Record<string, unknown> | null | undefined} pkg розпарсений `package.json`
27
+ * @returns {boolean} true, якщо знайдено хоча б один `@tauri-apps/*`
28
+ */
29
+ function packageHasTauriDep(pkg) {
30
+ if (!pkg || typeof pkg !== 'object') return false
31
+ for (const field of ['dependencies', 'devDependencies']) {
32
+ const deps = /** @type {Record<string, unknown> | undefined} */ (pkg[field])
33
+ if (!deps || typeof deps !== 'object') continue
34
+ for (const name of Object.keys(deps)) {
35
+ if (name.startsWith('@tauri-apps/')) return true
36
+ }
37
+ }
38
+ return false
39
+ }
40
+
41
+ /**
42
+ * Чи є у проєкті маркер Tauri: `src-tauri/`, `tauri.conf.json` (root або
43
+ * workspace), або `@tauri-apps/*` у залежностях кореневого `package.json`.
44
+ * @returns {Promise<boolean>} true, якщо проєкт використовує Tauri
45
+ */
46
+ async function projectHasTauriMarker() {
47
+ if (existsSync('src-tauri') && statSync('src-tauri').isDirectory()) return true
48
+ if (existsSync('tauri.conf.json')) return true
49
+ if (!existsSync('package.json')) return false
50
+ const pkg = JSON.parse(await readFile('package.json', 'utf8'))
51
+ if (packageHasTauriDep(pkg)) return true
52
+ return false
53
+ }
54
+
55
+ /**
56
+ * Перевіряє відповідність проєкту правилам tauri.mdc.
57
+ * @returns {Promise<number>} 0 — все OK, 1 — є проблеми
58
+ */
59
+ export async function check() {
60
+ const reporter = createCheckReporter()
61
+ const { pass, fail } = reporter
62
+
63
+ const hasTauri = await projectHasTauriMarker()
64
+ if (!hasTauri) {
65
+ pass('Немає маркера Tauri (src-tauri/, tauri.conf.json, @tauri-apps/*) — tauri-tooling не вимагається')
66
+ return reporter.getExitCode()
67
+ }
68
+
69
+ pass('Знайдено маркер Tauri — перевіряємо канонічні конфіги tauri.mdc')
70
+
71
+ const extPath = '.vscode/extensions.json'
72
+ if (!existsSync(extPath)) {
73
+ fail(`${extPath} не існує — створи з recommendations "tauri-apps.tauri-vscode" і "rust-lang.rust-analyzer" (tauri.mdc)`)
74
+ return reporter.getExitCode()
75
+ }
76
+ const violations = runConftestBatch({
77
+ policyDirRel: 'tauri/vscode_extensions',
78
+ namespace: 'tauri.vscode_extensions',
79
+ files: [extPath]
80
+ })
81
+ if (violations.length === 0) {
82
+ pass(`${extPath} відповідає tauri.vscode_extensions (rego)`)
83
+ } else {
84
+ for (const v of violations) fail(v.message)
85
+ }
86
+
87
+ return reporter.getExitCode()
88
+ }
@@ -363,7 +363,7 @@ const TARGETS = [
363
363
  }
364
364
  },
365
365
 
366
- // abie clean-merged-branch.yml: with.ignore_branches має містити dev/ua/ru.
366
+ // abie clean-merged-branch.yml: with.ignore_branches має містити dev/ua.
367
367
  {
368
368
  namespace: 'abie.clean_merged_ignore_branches',
369
369
  policyDir: 'abie/clean_merged_ignore_branches',
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: n-abie-clean
3
+ description: >-
4
+ Очистка проєкту від ru-середовища: гілка `ru`, директорії `ru/`, файли з суфіксом
5
+ `-ru`/`values-ru.*`, гілки `endsWith(...'ru')` у GitHub Actions, ru-умови у
6
+ Dockerfile/nginx, посилання на `cr.yandex` та раннер `ya`
7
+ version: '1.1'
8
+ ---
9
+
10
+ Скіл прибирає з проєкту все, що належить **ru-середовищу**. Залишаються тільки `dev` (як база) та `ua` як активне продакшн-середовище. Працюй послідовно по секціях нижче — після кожної секції перевіряй, що проєкт лишається консистентним (`kustomization.yaml` посилається лише на наявні файли, GitHub Actions-вирази синтаксично коректні).
11
+
12
+ ## 1. Директорії з назвою `ru`
13
+
14
+ Видали всі директорії з назвою `ru` у проєкті:
15
+
16
+ ```bash
17
+ find . -type d -name "ru" -exec rm -rf {} +
18
+ ```
19
+
20
+ Це чистить як `country/ru/`, так і `k8s/<...>/ru/` (overlay у kustomize). Після видалення overlay `ru/` обов’язково прибери відповідний запис у `resources:` у батьківському `kustomization.yaml`, якщо він там лишився.
21
+
22
+ ## 2. Файли з ru-суфіксом
23
+
24
+ Видали файли, у назві яких є явний ru-маркер:
25
+
26
+ - `values-ru.ini`, `values-ru.yaml`, `values-ru.*` (Helm/абстракції values на середовище)
27
+ - будь-які файли, що закінчуються на `-ru` або `-ru.<ext>`, наприклад `site/.env.prod-ru`, `*.env.prod-ru`, `*.prod-ru.conf`
28
+
29
+ ```bash
30
+ find . -type f \( -name "values-ru.*" -o -name "*-ru" -o -name "*.prod-ru" -o -name "*.prod-ru.*" \) -delete
31
+ ```
32
+
33
+ ## 3. `.github/workflows/*.yml`
34
+
35
+ ### 3.1. Тригери `on.push.branches`
36
+
37
+ Прибирай `ru` зі списку гілок.
38
+
39
+ Було:
40
+
41
+ ```yaml
42
+ on:
43
+ push:
44
+ branches: [dev, ru, ua]
45
+ ```
46
+
47
+ Стало:
48
+
49
+ ```yaml
50
+ on:
51
+ push:
52
+ branches: [dev, ua]
53
+ ```
54
+
55
+ ### 3.2. Тернарні вирази `endsWith(github.ref_name, …)`
56
+
57
+ У ланцюжках `endsWith(...)` залишай тільки гілки `dev` та `ua`. Гілку `ru` (а також пов’язаний з нею fallback на раннер `ya`, реєстр `cr.yandex/...`, NATS `cluster.local` тощо) — прибирай. Останнє значення в ланцюжку стає fallback’ом для всього, що не `dev`.
58
+
59
+ `runs-on` — було:
60
+
61
+ ```yaml
62
+ runs-on: ${{ endsWith(github.ref_name, 'dev') && 'dev' || ( endsWith(github.ref_name, 'ua') && 'ua' || 'ya' ) }}
63
+ ```
64
+
65
+ Стало:
66
+
67
+ ```yaml
68
+ runs-on: ${{ endsWith(github.ref_name, 'dev') && 'dev' || 'ua' }}
69
+ ```
70
+
71
+ `NATS_URL` — було:
72
+
73
+ ```yaml
74
+ NATS_URL: ${{ endsWith(github.ref_name, 'dev') && 'nats.nats.svc.abie-dev.internal:4222' || ( endsWith(github.ref_name, 'ua') && 'nats.nats.svc.abie-ua.internal:4222' || 'nats.nats.svc.cluster.local:4222' ) }}
75
+ ```
76
+
77
+ Стало:
78
+
79
+ ```yaml
80
+ NATS_URL: ${{ endsWith(github.ref_name, 'dev') && 'nats.nats.svc.abie-dev.internal:4222' || 'nats.nats.svc.abie-ua.internal:4222' }}
81
+ ```
82
+
83
+ `NATS_STREAM` — було:
84
+
85
+ ```yaml
86
+ NATS_STREAM: ${{ endsWith(github.ref_name, 'dev') && 'dev' || ( endsWith(github.ref_name, 'ua') && 'ua' || 'ru' ) }}
87
+ ```
88
+
89
+ Стало:
90
+
91
+ ```yaml
92
+ NATS_STREAM: ${{ endsWith(github.ref_name, 'dev') && 'dev' || 'ua' }}
93
+ ```
94
+
95
+ `REGISTRY` — було:
96
+
97
+ ```yaml
98
+ REGISTRY: ${{ endsWith(github.ref_name, 'ru') && 'cr.yandex/crpaerfcq9t16fse5onm' || ( endsWith(github.ref_name, 'ua') && 'europe-west4-docker.pkg.dev/abie-ua/c' || 'europe-north1-docker.pkg.dev/abie-dev/c' ) }}
99
+ ```
100
+
101
+ Стало:
102
+
103
+ ```yaml
104
+ REGISTRY: ${{ endsWith(github.ref_name, 'ua') && 'europe-west4-docker.pkg.dev/abie-ua/c' || 'europe-north1-docker.pkg.dev/abie-dev/c' }}
105
+ ```
106
+
107
+ Загальне правило: у фінальному виразі мають лишитися лише `endsWith(github.ref_name, 'dev')` та `endsWith(github.ref_name, 'ua')` (або лише один із них, якщо середовище одне). Будь-яка згадка `'ru'`, `'ya'`, `cr.yandex`, `cluster.local`-fallback для ru — прибирається.
108
+
109
+ ### 3.3. `ignore_branches` / `branches-ignore`
110
+
111
+ У всіх workflow-файлах та конфігах (включно з тими, що використовуються правилом **abie** `clean_merged_ignore_branches`) прибирай `ru` зі списків ignore-гілок.
112
+
113
+ ## 4. Dockerfile / nginx / build-скрипти
114
+
115
+ Прибирай умовні гілки `if [ "$BRANCH" = "ru" ]; then …` та копіювання `country/ru/*`. Лишається лише той код, що працює для `dev`/`ua`.
116
+
117
+ Було:
118
+
119
+ ```dockerfile
120
+ RUN if [ "$BRANCH" = "ru" ]; then cp -r country/ru/* public/ || true; fi && \
121
+ bun install && \
122
+ if [ "$BRANCH" = "ru" ]; then BASE="/itool/"; else BASE="/contract/"; fi && \
123
+ bun vite build --mode "prod-$BRANCH" --base="$BASE"
124
+ ```
125
+
126
+ Стало:
127
+
128
+ ```dockerfile
129
+ RUN bun install && bun vite build --mode "prod-$BRANCH" --base="$BASE"
130
+ ```
131
+
132
+ Те саме стосується `nginx`-конфігів (`server_name`, `proxy_pass` з ru-доменами), `*.sh`-скриптів та `package.json` scripts (`build:ru`, `deploy:ru`, `prod-ru` тощо).
133
+
134
+ ## 5. Після очистки
135
+
136
+ - Переконайся, що `kustomization.yaml` у кожній директорії `k8s/` не посилається на видалені overlay або файли.
137
+ - Пройдись `git grep` по репозиторію на залишки: `git grep -n -i -e '\bru\b' -e cr\.yandex -e country/ru -e prod-ru -e values-ru -e "'ya'"` — переглянь усі знахідки вручну, бо `ru` як слово може траплятися в легітимних контекстах (наприклад, `truncate`, `Aurum`, `cruft`). Видаляй лише ті входження, що належать ru-середовищу.
138
+ - Перевір CI локально: `npx @nitra/cursor check abie` (якщо правило **abie** ввімкнене у проєкті).
@@ -27,7 +27,7 @@ README має бути в директорії **k8s**.
27
27
 
28
28
  Застарілі файли прибирай.
29
29
 
30
- Для overlays **ru** та **ua** `namespace` задавай у `kustomization.yaml` (без окремих patch лише на зміну namespace). Деталі — **n-k8s** / **abie** у `.cursor/rules/`, якщо ці правила увімкнені в проєкті.
30
+ Для overlay **ua** `namespace` задавай у `kustomization.yaml` (без окремих patch лише на зміну namespace). Деталі — **n-k8s** / **abie** у `.cursor/rules/`, якщо ці правила увімкнені в проєкті.
31
31
 
32
32
  ## Виключення: CNPG `Cluster`
33
33