## Структурні перевірки `kustomization.yaml`

Rego-пакет: `k8s.kustomization`

**Цільові файли:** будь-який `kustomization.yaml` (`kind: Kustomization`, `apiVersion: kustomize.config.k8s.io/…`).

**Що перевіряється:**

- `resources[]` — має бути масивом рядків; непорожні рядки мають бути відсортовані за алфавітом (en, case-insensitive)
- `patches[]` — має бути масивом; елементи мають бути відсортовані за tuple `(target.kind, target.name, target.namespace, path)` (case-insensitive)
- Inline patch (поле `patch` як YAML-рядок JSON6902): у наборі операцій не може бути одночасно `op: remove` і `op: add` на той самий `path` — потрібен `op: replace`

**Приклади:**

✓ `resources: [deployment.yaml, hpa.yaml, svc.yaml]` (алфавіт)
✗ `resources: [svc.yaml, deployment.yaml]` (не за алфавітом)
✓ `op: replace` для зміни значення поля
✗ `op: remove` + `op: add` на один `path` в одному patch

**Примітка:** резолюція kustomize-дерева, перевірка існування refs на диску, парність `svc.yaml`/`svc-hl.yaml` — JS (`rules/k8s/fix.mjs`).

## Kustomize: структура каталогів (`base` / overlays)

Трансформуй дерева **`**/k8s`**, щоб **винести спільне** через [Kustomize](https://kustomize.io/): один канонічний **`base`** і тонкі **overlays** для інших середовищ.

### Джерело правди — середовище dev

- За основу бери **все, що відповідає середовищу dev** (як воно має виглядати в кластері для dev).
- У **такому вигляді** цей набір стає каталогом **`base`**: спільні маніфести без окремої директорії **`dev/`**.
- Окремої директорії **`k8s/dev/`** **не повинно існувати**: за середовище **dev** відповідає **`base`**. `check k8s` падає на будь-якому шляху `…/k8s/dev/…` (`isForbiddenK8sDevPath` у `crates/rules-core/src/concerns/k8s_manifests_cross_file.rs`).

### Overlays (не-dev)

- У каталозі кожного іншого середовища (наприклад **`ua/`**, **`prod/`**) — мінімум файлів: типово лише **`kustomization.yaml`** і ресурси, **необхідні лише для цього overlay**.
- Відмінності від dev вносяться **оверрайдами** (patches, `images`, `replicas`, `configMapGenerator` тощо), а не копіюванням повного дерева з `base`.

### Рядки, що змінюються між середовищами

У manifest-файлах у **`base`** для полів, які **будуть відрізнятися** в інших середовищах, на **тому самому рядку** додай коментар:

```yaml
image: my-app:dev-tag # буде замінено через kustomize
```

### Зміна image — через `images:`, не через `patches[]`

Підміну image у Pod-шаблоні Deployment в overlay роби директивою `images:`, а не JSON6902-патчем `op: replace` на `/spec/template/spec/containers/<N>/image`.

- У **`name`** — те, що **дослівно** стоїть у `image:` у base **без тегу**.
- **`newName`** — кінцеве ім'я образу; **`newTag`** — тег для прода.
- **`digest`** (`@sha256:…`) у `name` / `newName` не чіпай.

```yaml title="k8s/prod/kustomization.yaml (фрагмент)"
images:
  - name: europe-west4-docker.pkg.dev/abie-ua/c/apply-on-invoice-discount
    newName: europe-west4-docker.pkg.dev/abie-ua/c/apply-on-invoice-discount
    newTag: v2025-04-29
```

**`check k8s` автоматично** для кожного `kustomization.yaml` (конвертація в `crates/rules-core/src/concerns/k8s_manifests.rs`):

1. конвертує кожну JSON6902-операцію `op: replace` на `/spec/template/spec/containers/<N>/image` у запис `images:`;
2. чистить існуючий блок `images:` — зрізає `:tag` з `name` і видаляє `newTag`, який збігається з відрізаним тегом.

### Міграція зі старої структури

Після перенесення у **`base`** та overlays і перевірки (**`check k8s`**, **`lint-k8s`**) **видали** застарілі файли та директорії, що замінені новою схемою.

### `patches[].target`: лише `kind` і `name`

У `patches[].target` залишай **тільки** **`kind`** і **`name`** — поля **`group`** і **`version`** прибирай, якщо в інвентарі ресурсів немає колізії за цим `kind`+`name` між різними API-групами/версіями.

```yaml
# ❌ зайві group / version
patches:
  - target:
      group: gateway.networking.k8s.io
      version: v1beta1
      kind: Gateway
      name: gw

# ✅
patches:
  - target:
      kind: Gateway
      name: gw
```

**Виняток:** залишай `group` / `version`, лише якщо в дереві overlay реально співіснують ресурси з однаковими `kind`+`name`, але різними API-групами/версіями.

### Локальні шляхи в `kustomization.yaml`

Кожен запис без `://` (remote) з `resources` / `bases` / `components` / `crds`, `patchesStrategicMerge`, `patches[].path`, `patchesJson6902[].path`, `configurations[]`, `replacements[].path` має вказувати на **існуючий** у репозиторії файл (`.yaml` / `.yml`) або **каталог**; биті посилання — помилка **`check k8s`**.
