@nitra/cursor 3.2.1 → 3.2.2

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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.2.2] - 2026-06-01
4
+
5
+ ### Changed
6
+
7
+ - js-lint: додано e18e/\* deny-правила та канонічні ignorePatterns у .oxlintrc.json
8
+
9
+ ### Fixed
10
+
11
+ - k8s NetworkPolicy: мітку `app` для Job беремо з `spec.template.metadata.labels.app`, для CronJob — з `spec.jobTemplate.spec.template.metadata.labels.app` (ручний `spec.selector` у Job/CronJob невалідний без `manualSelector: true`)
12
+
3
13
  ## [3.2.1] - 2026-06-01
4
14
 
5
15
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "3.2.1",
3
+ "version": "3.2.2",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -4009,10 +4009,35 @@ function appLabelFromSpecSelector(spec) {
4009
4009
  return typeof app === 'string' && app.trim() !== '' ? app : null
4010
4010
  }
4011
4011
 
4012
+ /**
4013
+ * Витягує мітку `app` з `spec.template.metadata.labels.app` (pod-template labels).
4014
+ *
4015
+ * Джерело для Job і CronJob: у Job/CronJob **не можна** задавати ручний `spec.selector`
4016
+ * без `manualSelector: true` — apiserver сам генерує селектор з `controller-uid`, тож
4017
+ * `spec.selector.matchLabels.app` там невалідне джерело. Pod-template labels — валідне
4018
+ * й завжди присутнє поле.
4019
+ * @param {Record<string, unknown>} spec об'єкт `spec` (Job — `spec`, CronJob — `jobTemplate.spec`)
4020
+ * @returns {string | null} непорожнє значення `app` або null
4021
+ */
4022
+ function appLabelFromPodTemplate(spec) {
4023
+ const template = getNestedObject(spec, 'template')
4024
+ if (template === null) return null
4025
+ const metadata = getNestedObject(template, 'metadata')
4026
+ if (metadata === null) return null
4027
+ const labels = getNestedObject(metadata, 'labels')
4028
+ if (labels === null) return null
4029
+ const app = labels.app
4030
+ return typeof app === 'string' && app.trim() !== '' ? app : null
4031
+ }
4032
+
4012
4033
  /**
4013
4034
  * Витягує мітку `app` для workload, для якого потрібен NetworkPolicy.
4014
- * Deployment / StatefulSet / DaemonSet / Job — `spec.selector.matchLabels.app`;
4015
- * CronJob — `spec.jobTemplate.spec.selector.matchLabels.app`.
4035
+ * Deployment / StatefulSet / DaemonSet — `spec.selector.matchLabels.app`;
4036
+ * Job — `spec.template.metadata.labels.app`;
4037
+ * CronJob — `spec.jobTemplate.spec.template.metadata.labels.app`.
4038
+ *
4039
+ * Job і CronJob читаються з pod-template labels, бо ручний `spec.selector` у них невалідний
4040
+ * без `manualSelector: true` (селектор генерує контролер). Див. `appLabelFromPodTemplate`.
4016
4041
  * @param {Record<string, unknown>} manifest AST workload
4017
4042
  * @returns {string | null} непорожнє значення `app` або null
4018
4043
  */
@@ -4022,10 +4047,11 @@ export function workloadAppLabel(manifest) {
4022
4047
  if (kind === 'CronJob') {
4023
4048
  const jobTemplate = getNestedObject(getNestedObject(manifest, 'spec'), 'jobTemplate')
4024
4049
  const jobSpec = jobTemplate === null ? null : getNestedObject(jobTemplate, 'spec')
4025
- return jobSpec === null ? null : appLabelFromSpecSelector(jobSpec)
4050
+ return jobSpec === null ? null : appLabelFromPodTemplate(jobSpec)
4026
4051
  }
4027
4052
  const spec = getNestedObject(manifest, 'spec')
4028
4053
  if (spec === null) return null
4054
+ if (kind === 'Job') return appLabelFromPodTemplate(spec)
4029
4055
  return appLabelFromSpecSelector(spec)
4030
4056
  }
4031
4057
 
@@ -5631,7 +5657,7 @@ async function validateNetworkPoliciesForK8sWorkloads(root, yamlFilesAbs, fail,
5631
5657
  }
5632
5658
  if (appLabel === null) {
5633
5659
  fail(
5634
- `${deployRel}: ${workloadKind} '${workloadName}' без мітки app у selector (spec.selector.matchLabels.app або jobTemplate для CronJob) (k8s.mdc)`
5660
+ `${deployRel}: ${workloadKind} '${workloadName}' без мітки app (spec.selector.matchLabels.app; Job spec.template.metadata.labels.app; CronJob — spec.jobTemplate.spec.template.metadata.labels.app) (k8s.mdc)`
5635
5661
  )
5636
5662
  continue
5637
5663
  }
package/rules/k8s/k8s.mdc CHANGED
@@ -385,7 +385,7 @@ images:
385
385
 
386
386
  Для **кожного** `kind: Deployment` у каталозі **`…/k8s/…/base/`** (у будь-якому файлі `.yaml`, наприклад **`deploy.yaml`**, **`deployment.yaml`**) сам Deployment має канонічні **`spec.template.spec.topologySpreadConstraints`**, а **HPA і PDB** живуть у **sibling каталозі** **`…/k8s/…/components/`** (Kustomize Component, фіксована назва каталогу — `components`). У `base/` локальні `hpa.yaml` і `pdb.yaml` **заборонені** (file-existence error). У дереві base-kustomize HPA / PDB також **не дозволені** через `resources` / `components` / `bases`.
387
387
 
388
- **NetworkPolicy** — інша історія: оскільки обмеження мережі мають діяти **і на dev**, NP лежить **у `base/`** (а не в `components/`). Для **кожного** з **`Deployment`**, **`StatefulSet`**, **`DaemonSet`**, **`Job`**, **`CronJob`** під `k8s` обов'язковий **NetworkPolicy**: у **`…/k8s/…/base/`** — у **`base/networkpolicy.yaml`** поруч з workload-маніфестом (multi-doc, якщо workload-ів кілька); у **не-base** — **`networkpolicy.yaml`** поруч із маніфестом workload у тому ж каталозі (overlay-specific override). `metadata.name` NetworkPolicy **= `metadata.name`** workload; `spec.podSelector.matchLabels.app` **= мітка `app`** з `spec.selector.matchLabels` (для **CronJob** — з `spec.jobTemplate.spec.selector.matchLabels`). У `base/kustomization.yaml` `resources:` має бути `networkpolicy.yaml`. Відсутні документи **`check k8s`** створює автоматично і додає `networkpolicy.yaml` у `base/kustomization.yaml` `resources:`.
388
+ **NetworkPolicy** — інша історія: оскільки обмеження мережі мають діяти **і на dev**, NP лежить **у `base/`** (а не в `components/`). Для **кожного** з **`Deployment`**, **`StatefulSet`**, **`DaemonSet`**, **`Job`**, **`CronJob`** під `k8s` обов'язковий **NetworkPolicy**: у **`…/k8s/…/base/`** — у **`base/networkpolicy.yaml`** поруч з workload-маніфестом (multi-doc, якщо workload-ів кілька); у **не-base** — **`networkpolicy.yaml`** поруч із маніфестом workload у тому ж каталозі (overlay-specific override). `metadata.name` NetworkPolicy **= `metadata.name`** workload; `spec.podSelector.matchLabels.app` **= мітка `app`** з `spec.selector.matchLabels` (для **Job** — з `spec.template.metadata.labels.app`, для **CronJob** — з `spec.jobTemplate.spec.template.metadata.labels.app`; у Job/CronJob ручний `spec.selector` невалідний без `manualSelector: true`, тож джерело — pod-template labels). У `base/kustomization.yaml` `resources:` має бути `networkpolicy.yaml`. Відсутні документи **`check k8s`** створює автоматично і додає `networkpolicy.yaml` у `base/kustomization.yaml` `resources:`.
389
389
 
390
390
  **Канонічна структура `<pkg>/k8s/components/`** (sibling до `base/`) — лише HPA і PDB:
391
391