@7n/rules 1.8.2 → 1.8.3

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,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.8.3] - 2026-07-17
4
+
5
+ ### Changed
6
+
7
+ - release: @7n/rules@1.8.2
8
+
3
9
  ## [1.8.2] - 2026-07-17
4
10
 
5
11
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@7n/rules",
3
- "version": "1.8.2",
3
+ "version": "1.8.3",
4
4
  "description": "CLI еталонних правил і skills (префікс n-): синк у репозиторій, дельта-lint, конформність",
5
5
  "keywords": [
6
6
  "cli",
@@ -11,7 +11,7 @@ Rego-пакет: `k8s.hasura_configmap`
11
11
  | `HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS` | `"true"` |
12
12
  | `HASURA_GRAPHQL_ENABLE_RELAY` | `"false"` |
13
13
  | `HASURA_GRAPHQL_ENABLE_TELEMETRY` | `"false"` |
14
- | `HASURA_GRAPHQL_ENABLED_LOG_TYPES` | `"startup,http-log"` (точно) |
14
+ | `HASURA_GRAPHQL_ENABLED_LOG_TYPES` | `"startup,http-log"` (точно; base/dev; overlay має патчем замінити на `"startup"` — `http-log` лише для base/dev) |
15
15
  | `HASURA_GRAPHQL_ENABLED_APIS` | `"metadata,graphql,pgdump"` (base/dev; overlay має патчем замінити на `"metadata,graphql"`) |
16
16
  | `HASURA_GRAPHQL_DISABLE_EVENTING` | ключ обов'язковий, значення довільне (рекомендовано `"true"`) |
17
17
 
@@ -4922,6 +4922,12 @@ const HASURA_OVERLAY_ENABLED_APIS = 'metadata,graphql'
4922
4922
  /** JSON-Pointer ключа `HASURA_GRAPHQL_ENABLED_APIS` у `data` ConfigMap (для JSON6902-патчів). */
4923
4923
  const HASURA_ENABLED_APIS_DATA_POINTER = '/data/HASURA_GRAPHQL_ENABLED_APIS'
4924
4924
 
4925
+ /** Очікуване `HASURA_GRAPHQL_ENABLED_LOG_TYPES` у non-base/dev overlay (без `http-log` — він лише для base/dev). */
4926
+ const HASURA_OVERLAY_ENABLED_LOG_TYPES = 'startup'
4927
+
4928
+ /** JSON-Pointer ключа `HASURA_GRAPHQL_ENABLED_LOG_TYPES` у `data` ConfigMap (для JSON6902-патчів). */
4929
+ const HASURA_ENABLED_LOG_TYPES_DATA_POINTER = '/data/HASURA_GRAPHQL_ENABLED_LOG_TYPES'
4930
+
4925
4931
  /**
4926
4932
  * Чи дерево kustomization (`resources` / `bases` / `components` / `crds`, рекурсивно) містить
4927
4933
  * **Hasura-Deployment** у шарі base (образ `hasura/graphql-engine`). Маркер того, що overlay успадковує
@@ -4965,18 +4971,19 @@ function firstValidYamlJsonFromPatchText(patchText) {
4965
4971
  }
4966
4972
 
4967
4973
  /**
4968
- * Значення `data.HASURA_GRAPHQL_ENABLED_APIS` з JSON6902-патчу (масив операцій).
4969
- * Повертає значення першої `add`/`replace`-операції на `/data/HASURA_GRAPHQL_ENABLED_APIS`.
4974
+ * Значення `data.<dataKey>` з JSON6902-патчу (масив операцій).
4975
+ * Повертає значення першої `add`/`replace`-операції на `/data/<dataKey>`.
4970
4976
  * @param {unknown[]} ops масив JSON6902-операцій
4977
+ * @param {string} dataPointer JSON-Pointer ключа (`/data/<dataKey>`)
4971
4978
  * @returns {string | null} присвоєне значення (рядок) або null
4972
4979
  */
4973
- function enabledApisValueFromJson6902(ops) {
4980
+ function hasuraDataKeyValueFromJson6902(ops, dataPointer) {
4974
4981
  for (const item of ops) {
4975
4982
  if (item === null || typeof item !== 'object' || Array.isArray(item)) continue
4976
4983
  const rec = /** @type {Record<string, unknown>} */ (item)
4977
4984
  const op = typeof rec.op === 'string' ? rec.op.trim().toLowerCase() : ''
4978
4985
  const path = typeof rec.path === 'string' ? normalizeJsonPatchPath(rec.path) : ''
4979
- if ((op === 'add' || op === 'replace') && path === HASURA_ENABLED_APIS_DATA_POINTER) {
4986
+ if ((op === 'add' || op === 'replace') && path === dataPointer) {
4980
4987
  return typeof rec.value === 'string' ? rec.value : JSON.stringify(rec.value)
4981
4988
  }
4982
4989
  }
@@ -4984,42 +4991,58 @@ function enabledApisValueFromJson6902(ops) {
4984
4991
  }
4985
4992
 
4986
4993
  /**
4987
- * Значення `data.HASURA_GRAPHQL_ENABLED_APIS` зі Strategic-Merge-патчу (об'єкт із `data`).
4994
+ * Значення `data.<dataKey>` зі Strategic-Merge-патчу (об'єкт із `data`).
4988
4995
  * @param {object} parsed розпарсений об'єкт patch
4996
+ * @param {string} dataKey ключ у `data` (наприклад `HASURA_GRAPHQL_ENABLED_APIS`)
4989
4997
  * @returns {string | null} присвоєне значення (рядок) або null
4990
4998
  */
4991
- function enabledApisValueFromStrategicMerge(parsed) {
4999
+ function hasuraDataKeyValueFromStrategicMerge(parsed, dataKey) {
4992
5000
  const data = /** @type {Record<string, unknown>} */ (parsed).data
4993
5001
  if (data === null || typeof data !== 'object' || Array.isArray(data)) return null
4994
5002
  const d = /** @type {Record<string, unknown>} */ (data)
4995
- if (!Object.hasOwn(d, 'HASURA_GRAPHQL_ENABLED_APIS')) return null
4996
- const v = d.HASURA_GRAPHQL_ENABLED_APIS
5003
+ if (!Object.hasOwn(d, dataKey)) return null
5004
+ const v = d[dataKey]
4997
5005
  return typeof v === 'string' ? v : JSON.stringify(v)
4998
5006
  }
4999
5007
 
5000
5008
  /**
5001
- * Значення, яке inline-`patch` присвоює `data.HASURA_GRAPHQL_ENABLED_APIS`. Підтримка двох форматів:
5002
- * **JSON6902** (`op` add/replace на `/data/HASURA_GRAPHQL_ENABLED_APIS`) і **Strategic Merge**
5003
- * (`data.HASURA_GRAPHQL_ENABLED_APIS`). Зовнішні patch-файли (`patches[].path`) не охоплені — Plan B trade-off.
5009
+ * Значення, яке inline-`patch` присвоює `data.<dataKey>`. Підтримка двох форматів:
5010
+ * **JSON6902** (`op` add/replace на `/data/<dataKey>`) і **Strategic Merge** (`data.<dataKey>`).
5011
+ * Зовнішні patch-файли (`patches[].path`) не охоплені — Plan B trade-off.
5004
5012
  * @param {string} patchText вміст поля `patch`
5013
+ * @param {string} dataKey ключ у `data`
5014
+ * @param {string} dataPointer JSON-Pointer ключа (`/data/<dataKey>`)
5005
5015
  * @returns {string | null} присвоєне значення (рядок) або null, якщо patch не чіпає цей ключ
5006
5016
  */
5007
- export function enabledApisValueFromPatchText(patchText) {
5017
+ function hasuraDataKeyValueFromPatchText(patchText, dataKey, dataPointer) {
5008
5018
  const t = typeof patchText === 'string' ? patchText.trim() : ''
5009
5019
  if (t === '') return null
5010
5020
  const parsed = firstValidYamlJsonFromPatchText(t)
5011
- if (Array.isArray(parsed)) return enabledApisValueFromJson6902(parsed)
5021
+ if (Array.isArray(parsed)) return hasuraDataKeyValueFromJson6902(parsed, dataPointer)
5012
5022
  if (parsed === null || typeof parsed !== 'object') return null
5013
- return enabledApisValueFromStrategicMerge(parsed)
5023
+ return hasuraDataKeyValueFromStrategicMerge(parsed, dataKey)
5014
5024
  }
5015
5025
 
5016
5026
  /**
5017
- * Значення, яке `patches[]` kustomization присвоюють `data.HASURA_GRAPHQL_ENABLED_APIS` на цілі **ConfigMap**.
5027
+ * Значення, яке inline-`patch` присвоює `data.HASURA_GRAPHQL_ENABLED_APIS`. Підтримка двох форматів:
5028
+ * **JSON6902** (`op` add/replace на `/data/HASURA_GRAPHQL_ENABLED_APIS`) і **Strategic Merge**
5029
+ * (`data.HASURA_GRAPHQL_ENABLED_APIS`). Зовнішні patch-файли (`patches[].path`) не охоплені — Plan B trade-off.
5030
+ * @param {string} patchText вміст поля `patch`
5031
+ * @returns {string | null} присвоєне значення (рядок) або null, якщо patch не чіпає цей ключ
5032
+ */
5033
+ export function enabledApisValueFromPatchText(patchText) {
5034
+ return hasuraDataKeyValueFromPatchText(patchText, 'HASURA_GRAPHQL_ENABLED_APIS', HASURA_ENABLED_APIS_DATA_POINTER)
5035
+ }
5036
+
5037
+ /**
5038
+ * Значення, яке `patches[]` kustomization присвоюють `data.<dataKey>` на цілі **ConfigMap**.
5018
5039
  * Повертає значення першого patch-а, що чіпає цей ключ, або null, якщо такого немає.
5019
5040
  * @param {Record<string, unknown>} kust об'єкт kustomization.yaml
5041
+ * @param {string} dataKey ключ у `data`
5042
+ * @param {string} dataPointer JSON-Pointer ключа (`/data/<dataKey>`)
5020
5043
  * @returns {string | null} присвоєне значення або null
5021
5044
  */
5022
- export function hasuraEnabledApisOverrideValue(kust) {
5045
+ function hasuraDataKeyOverrideValue(kust, dataKey, dataPointer) {
5023
5046
  const patches = kust.patches
5024
5047
  if (!Array.isArray(patches)) return null
5025
5048
  for (const p of patches) {
@@ -5027,12 +5050,32 @@ export function hasuraEnabledApisOverrideValue(kust) {
5027
5050
  const pr = /** @type {Record<string, unknown>} */ (p)
5028
5051
  if (typeof pr.patch !== 'string') continue
5029
5052
  if (resolvePatchTargetKind(pr) !== 'ConfigMap') continue
5030
- const v = enabledApisValueFromPatchText(pr.patch)
5053
+ const v = hasuraDataKeyValueFromPatchText(pr.patch, dataKey, dataPointer)
5031
5054
  if (v !== null) return v
5032
5055
  }
5033
5056
  return null
5034
5057
  }
5035
5058
 
5059
+ /**
5060
+ * Значення, яке `patches[]` kustomization присвоюють `data.HASURA_GRAPHQL_ENABLED_APIS` на цілі **ConfigMap**.
5061
+ * Повертає значення першого patch-а, що чіпає цей ключ, або null, якщо такого немає.
5062
+ * @param {Record<string, unknown>} kust об'єкт kustomization.yaml
5063
+ * @returns {string | null} присвоєне значення або null
5064
+ */
5065
+ export function hasuraEnabledApisOverrideValue(kust) {
5066
+ return hasuraDataKeyOverrideValue(kust, 'HASURA_GRAPHQL_ENABLED_APIS', HASURA_ENABLED_APIS_DATA_POINTER)
5067
+ }
5068
+
5069
+ /**
5070
+ * Значення, яке `patches[]` kustomization присвоюють `data.HASURA_GRAPHQL_ENABLED_LOG_TYPES` на цілі **ConfigMap**.
5071
+ * Повертає значення першого patch-а, що чіпає цей ключ, або null, якщо такого немає.
5072
+ * @param {Record<string, unknown>} kust об'єкт kustomization.yaml
5073
+ * @returns {string | null} присвоєне значення або null
5074
+ */
5075
+ export function hasuraEnabledLogTypesOverrideValue(kust) {
5076
+ return hasuraDataKeyOverrideValue(kust, 'HASURA_GRAPHQL_ENABLED_LOG_TYPES', HASURA_ENABLED_LOG_TYPES_DATA_POINTER)
5077
+ }
5078
+
5036
5079
  /**
5037
5080
  * Для кожного **non-base/dev** overlay `kustomization.yaml`, що успадковує Hasura-base (Deployment з
5038
5081
  * `hasura/graphql-engine`), вимагає у `patches[]` перевизначення **`data.HASURA_GRAPHQL_ENABLED_APIS`**
@@ -5070,6 +5113,43 @@ async function validateHasuraOverlayEnabledApisOverride(root, yamlFilesAbs, fail
5070
5113
  }
5071
5114
  }
5072
5115
 
5116
+ /**
5117
+ * Для кожного **non-base/dev** overlay `kustomization.yaml`, що успадковує Hasura-base (Deployment з
5118
+ * `hasura/graphql-engine`), вимагає у `patches[]` перевизначення **`data.HASURA_GRAPHQL_ENABLED_LOG_TYPES`**
5119
+ * до **`"startup"`** (`http-log` лишається строго для base/dev — прод не пише per-request access-лог у
5120
+ * Cloud Logging). `kind: Component` пропускається (env-неутральне джерело, не overlay).
5121
+ * @param {string} root корінь репозиторію
5122
+ * @param {string[]} yamlFilesAbs yaml під k8s
5123
+ * @param {(msg: string) => void} fail callback при помилці
5124
+ * @param {(msg: string) => void} passFn callback при успіху
5125
+ */
5126
+ async function validateHasuraOverlayEnabledLogTypesOverride(root, yamlFilesAbs, fail, passFn) {
5127
+ const rootNorm = resolve(root)
5128
+ const kustFiles = yamlFilesAbs.filter(abs => basename(abs) === 'kustomization.yaml')
5129
+ for (const kustAbs of kustFiles) {
5130
+ const rel = (relative(rootNorm, kustAbs) || kustAbs).replaceAll('\\', '/')
5131
+ const segment = k8sEnvSegmentFromRelPath(rel)
5132
+ if (segment === null || segment === 'base' || segment === 'dev') continue
5133
+ const kust = await readFirstYamlObject(kustAbs)
5134
+ if (kust === null || kust.kind === 'Component') continue
5135
+ if (!(await kustomizationTreeHasHasuraDeployment(kustAbs, rootNorm))) continue
5136
+ const value = hasuraEnabledLogTypesOverrideValue(kust)
5137
+ if (value === HASURA_OVERLAY_ENABLED_LOG_TYPES) {
5138
+ passFn(
5139
+ `${rel}: overlay '${segment}' перевизначає HASURA_GRAPHQL_ENABLED_LOG_TYPES="${HASURA_OVERLAY_ENABLED_LOG_TYPES}" (k8s.mdc)`
5140
+ )
5141
+ } else if (value === null) {
5142
+ fail(
5143
+ `${rel}: overlay '${segment}' має у patches[] перевизначати data.HASURA_GRAPHQL_ENABLED_LOG_TYPES до "${HASURA_OVERLAY_ENABLED_LOG_TYPES}" (http-log лише для base/dev) (k8s.mdc)`
5144
+ )
5145
+ } else {
5146
+ fail(
5147
+ `${rel}: overlay '${segment}' patch data.HASURA_GRAPHQL_ENABLED_LOG_TYPES має бути "${HASURA_OVERLAY_ENABLED_LOG_TYPES}" (зараз: ${JSON.stringify(value)}) (k8s.mdc)`
5148
+ )
5149
+ }
5150
+ }
5151
+ }
5152
+
5073
5153
  /**
5074
5154
  * Шукає HPA за `scaleTargetRef.name` серед документів.
5075
5155
  * @param {Record<string, unknown>[]} hpaDocs масив HPA-документів
@@ -6658,6 +6738,8 @@ export async function lint(ctx) {
6658
6738
 
6659
6739
  await validateHasuraOverlayEnabledApisOverride(root, yamlFiles, fail, pass)
6660
6740
 
6741
+ await validateHasuraOverlayEnabledLogTypesOverride(root, yamlFiles, fail, pass)
6742
+
6661
6743
  return reporter.result()
6662
6744
  }
6663
6745