@7n/rules 1.7.0 → 1.7.1
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 +8 -0
- package/package.json +1 -1
- package/rules/abie/http_route_base/http_route_base.mdc +23 -1
- package/rules/adr/madr_format/concern.json +1 -0
- package/rules/adr/madr_format/madr_format.mdc +119 -0
- package/rules/bun/bunfig/bunfig.mdc +5 -0
- package/rules/bun/lint-surface/concern.json +3 -0
- package/rules/bun/lint-surface/lint-surface.mdc +13 -0
- package/rules/capacitor/platforms/docs/main.md +1 -1
- package/rules/capacitor/platforms/main.mjs +5 -1
- package/rules/capacitor/platforms/platforms.mdc +108 -0
- package/rules/changelog/consistency/comparison-models.mdc +46 -0
- package/rules/changelog/consistency/consistency.mdc +35 -0
- package/rules/docker/main.mdc +235 -2
- package/rules/image-avif/avif_generation/avif_generation.mdc +14 -0
- package/rules/js/check/check.mdc +26 -0
- package/rules/js/file-extensions/concern.json +3 -0
- package/rules/js/file-extensions/file-extensions.mdc +12 -0
- package/rules/js/jscpd_config/jscpd_config.mdc +28 -0
- package/rules/js/knip/knip.mdc +15 -0
- package/rules/js/utils_imports/utils_imports.mdc +15 -0
- package/rules/js-bun-db/connection/concern.json +3 -0
- package/rules/js-bun-db/connection/connection.mdc +42 -0
- package/rules/js-bun-db/package_json/package_json.mdc +15 -1
- package/rules/js-bun-db/pg_format_identifiers/concern.json +3 -0
- package/rules/js-bun-db/pg_format_identifiers/pg_format_identifiers.mdc +104 -0
- package/rules/js-bun-db/safety/safety.mdc +458 -0
- package/rules/js-mssql/main.mdc +130 -0
- package/rules/js-mssql/mssql-tvp/concern.json +3 -0
- package/rules/js-mssql/mssql-tvp/mssql-tvp.mdc +77 -0
- package/rules/js-run/configmap/configmap.mdc +6 -0
- package/rules/js-run/jsconfig/jsconfig.mdc +23 -0
- package/rules/js-run/package_json/package_json.mdc +6 -0
- package/rules/js-run/project-structure/concern.json +3 -0
- package/rules/js-run/project-structure/project-structure.mdc +11 -0
- package/rules/js-run/runtime/runtime.mdc +170 -0
- package/rules/js-run/scope/concern.json +3 -0
- package/rules/js-run/scope/scope.mdc +11 -0
- package/rules/k8s/hasura_configmap/hasura_configmap.mdc +6 -0
- package/rules/k8s/hpa_pdb/hpa_pdb.mdc +134 -0
- package/rules/k8s/kubeconform/kubeconform.mdc +38 -0
- package/rules/k8s/kustomization/kustomization.mdc +73 -0
- package/rules/k8s/main.mdc +68 -0
- package/rules/k8s/manifest/manifest.mdc +37 -0
- package/rules/k8s/manifests/docs/fix-manifests.md +3 -1
- package/rules/k8s/manifests/fix-manifests.mjs +11 -0
- package/rules/k8s/manifests/main.mjs +28 -0
- package/rules/k8s/network_policy/network_policy.mdc +33 -0
- package/rules/nginx-default-tpl/http-route/concern.json +1 -0
- package/rules/nginx-default-tpl/http-route/http-route.mdc +54 -0
- package/rules/nginx-default-tpl/template/template.mdc +152 -0
- package/rules/php/tooling/tooling.mdc +7 -6
- package/rules/python/pyproject_toml/pyproject_toml.mdc +17 -1
- package/rules/python/tooling/tooling.mdc +9 -10
- package/rules/rego/main.mdc +14 -0
- package/rules/rust/check/check.mdc +16 -0
- package/rules/style/admin_table/admin_table.mdc +88 -0
- package/rules/style/admin_table/concern.json +7 -0
- package/rules/style/admin_table/docs/index.md +9 -0
- package/rules/style/admin_table/docs/main.md +14 -0
- package/rules/style/admin_table/main.mjs +46 -0
- package/rules/style/colors/colors.mdc +21 -0
- package/rules/style/colors/concern.json +3 -0
- package/rules/style/gap/concern.json +7 -0
- package/rules/style/gap/docs/index.md +9 -0
- package/rules/style/gap/docs/main.md +15 -0
- package/rules/style/gap/gap.mdc +22 -0
- package/rules/style/gap/main.mjs +51 -0
- package/rules/style/quasar/concern.json +3 -0
- package/rules/style/quasar/quasar.mdc +7 -0
- package/rules/style/quasar_fixes/concern.json +7 -0
- package/rules/style/quasar_fixes/docs/index.md +9 -0
- package/rules/style/quasar_fixes/docs/main.md +16 -0
- package/rules/style/quasar_fixes/main.mjs +57 -0
- package/rules/style/quasar_fixes/quasar_fixes.mdc +32 -0
- package/rules/tauri/tool_surface/concern.json +16 -0
- package/rules/tauri/tool_surface/docs/index.md +9 -0
- package/rules/tauri/tool_surface/docs/main.md +24 -0
- package/rules/tauri/tool_surface/main.mjs +145 -0
- package/rules/tauri/tool_surface/tool_surface.mdc +29 -0
- package/rules/test/vitest-api-conventions/concern.json +7 -0
- package/rules/test/vitest-api-conventions/docs/index.md +9 -0
- package/rules/test/vitest-api-conventions/docs/main.md +40 -0
- package/rules/test/vitest-api-conventions/main.mjs +186 -0
- package/rules/test/vitest-api-conventions/vitest-api-conventions.mdc +129 -0
- package/rules/text/cspell/cspell.mdc +18 -0
- package/rules/text/markdownlint/markdownlint.mdc +4 -0
- package/rules/text/run-dotenv-linter/run-dotenv-linter.mdc +17 -0
- package/rules/text/run-shellcheck/run-shellcheck.mdc +17 -0
- package/rules/text/run-v8r/run-v8r.mdc +23 -0
- package/rules/vue/composition-api/composition-api.mdc +82 -0
- package/rules/vue/composition-api/concern.json +3 -0
- package/rules/vue/main.mdc +1 -1
- package/rules/vue/nheader-layout/concern.json +3 -0
- package/rules/vue/nheader-layout/nheader-layout.mdc +171 -0
- package/rules/vue/packages/packages.mdc +56 -0
- package/rules/vue/quasar-ui/concern.json +3 -0
- package/rules/vue/quasar-ui/quasar-ui.mdc +32 -0
- package/rules/vue/structure/concern.json +3 -0
- package/rules/vue/structure/structure.mdc +101 -0
- package/rules/vue/testing/concern.json +3 -0
- package/rules/vue/testing/testing.mdc +40 -0
- package/rules/vue/tfm-translations/concern.json +7 -0
- package/rules/vue/tfm-translations/docs/main.md +29 -0
- package/rules/vue/tfm-translations/main.mjs +55 -0
- package/rules/vue/tfm-translations/tfm-translations.mdc +32 -0
- package/rules/vue/vite-config/concern.json +3 -0
- package/rules/vue/vite-config/vite-config.mdc +153 -0
- package/rules/vue/vite-env/concern.json +3 -0
- package/rules/vue/vite-env/vite-env.mdc +61 -0
package/rules/k8s/main.mdc
CHANGED
|
@@ -20,3 +20,71 @@ alwaysApply: false
|
|
|
20
20
|
## Швидкий gate через conftest (Rego)
|
|
21
21
|
|
|
22
22
|
Підмножину пер-документних правил продубльовано як rego-полісі у **`npm/rules/k8s/policy/`** (запускається через **`bun run lint-rego`** для `*_test.rego` юніт-тестів і через **`npx @7n/rules fix k8s`** для прогону по реальних YAML). JS authoritative; rego — швидкий gate для одиничного маніфеста.
|
|
23
|
+
|
|
24
|
+
## Визначення схеми YAML (канон `yaml-language-server: $schema`)
|
|
25
|
+
|
|
26
|
+
Для кожного файлу `*.yaml` у шляху з `k8s`, якщо існує **публічна** схема — **перший рядок** — коментар-директива для [YAML Language Server](https://github.com/redhat-developer/yaml-language-server) з URL за `https://`:
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
# yaml-language-server: $schema=https://...
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Далі — вміст маніфесту без зайвого порожнього рядка між коментарем і YAML. Орієнтир для визначення URL — **перший документ** (до наступного `---`); багатодокументні файли — див. нижче.
|
|
33
|
+
|
|
34
|
+
**Modeline — опційний:** якщо для конкретного поєднання `apiVersion`/`kind` **немає** надійної публічної схеми (yannh/datree не покривають), залиш файл **без** рядка `# yaml-language-server: $schema=…`. **Заборонено** ставити `$schema=file:…` як заглушку.
|
|
35
|
+
|
|
36
|
+
**Виняток — modeline заборонено:** `apiVersion: alb.yc.io/v1alpha1`, `kind: HttpBackendGroup` (Yandex ALB) — рядка **`# yaml-language-server: $schema=…`** у файлі **не** має бути. Перший рядок — одразу YAML (`apiVersion:` тощо).
|
|
37
|
+
|
|
38
|
+
Перевірка й побудова URL — **`npm/rules/k8s/manifests/main.mjs`** (`expectedSchemaUrl`, `expectedSchemaUrlForTypedManifest`).
|
|
39
|
+
|
|
40
|
+
### Правила визначення URL схеми
|
|
41
|
+
|
|
42
|
+
1. **Ім'я** `kustomization.yaml` → `https://json.schemastore.org/kustomization.json`.
|
|
43
|
+
|
|
44
|
+
2. **`apiVersion: v1`** → yannh, PIN набору схем **`v1.33.9-standalone-strict`**, ref репозиторію для raw URL — **`master`**:
|
|
45
|
+
`https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/<PIN>/<kind>-v1.json`
|
|
46
|
+
`<kind>`: літери в нижньому регістрі без роздільників між CamelCase (наприклад `Service` → `service`).
|
|
47
|
+
|
|
48
|
+
**`kind: Secret`** і **`type: kubernetes.io/basic-auth`** — той самий шаблон, **`secret-v1.json`**:
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
# yaml-language-server: $schema=https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.33.9-standalone-strict/secret-v1.json
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
3. **`apiVersion: group/version`** і **group** у переліку yannh-груп (`apps`, `autoscaling`, `batch`, `policy`, `networking.k8s.io`, `rbac.authorization.k8s.io`, `flowcontrol.apiserver.k8s.io`, `apiextensions.k8s.io` та ін. — повний перелік `YANNH_GROUPS` у `npm/rules/k8s/manifests/main.mjs`) → yannh:
|
|
55
|
+
`https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/<PIN>/<kind>-<group-частина>-<version>.json`
|
|
56
|
+
де **`<group-частина>`** — **перший сегмент** `group` до першої крапки: для груп без крапок збігається з усією group (`apps/v1` + `Deployment` → `deployment-apps-v1.json`); для `*.k8s.io` / `*.apiserver.k8s.io` — лише префікс до `.k8s.io` (`networking.k8s.io/v1` + `Ingress` → `ingress-networking-v1.json`; `networking.k8s.io/v1` + `NetworkPolicy` → `networkpolicy-networking-v1.json`; `rbac.authorization.k8s.io/v1` + `ClusterRole` → `clusterrole-rbac-v1.json`; `flowcontrol.apiserver.k8s.io/v1` + `FlowSchema` → `flowschema-flowcontrol-v1.json`). У yannh **немає** файлів з фрагментом `-k8s-io-` у назві.
|
|
57
|
+
|
|
58
|
+
4. **Інакше** (CRD, Gateway API тощо) → [datreeio/CRDs-catalog](https://github.com/datreeio/CRDs-catalog). Типово для `$schema` у редакторі — **GitHub Pages**:
|
|
59
|
+
`https://datreeio.github.io/CRDs-catalog/<group>/<kind>_<version>.json`
|
|
60
|
+
(`<kind>` — лише літери та цифри в нижньому регістрі, без роздільників між CamelCase.)
|
|
61
|
+
|
|
62
|
+
**Виняток — `InfisicalSecret`:** `apiVersion: secrets.infisical.com/v1alpha1`, `kind: InfisicalSecret` — канонічний modeline через **raw** на гілці **`main`** (явний запис у таблиці `EXPLICIT_K8S_SCHEMAS`):
|
|
63
|
+
|
|
64
|
+
```yaml
|
|
65
|
+
# yaml-language-server: $schema=https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/secrets.infisical.com/infisicalsecret_v1alpha1.json
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Приклад (Gateway API):** `apiVersion: gateway.networking.k8s.io/v1`, `kind: HTTPRoute` — канон **`v1`** (не `v1beta1`, див. **`gateway.mdc`**):
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
# yaml-language-server: $schema=https://datreeio.github.io/CRDs-catalog/gateway.networking.k8s.io/httproute_v1.json
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Приклад (GKE):** `apiVersion: networking.gke.io/v1`, `kind: HealthCheckPolicy`:
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
# yaml-language-server: $schema=https://datreeio.github.io/CRDs-catalog/networking.gke.io/healthcheckpolicy_v1.json
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
5. **Немає надійного публічного URL** — не вигадуй URL і **не** використовуй `$schema=file:…`. Залиш файл **без** рядка `# yaml-language-server: $schema=…` зовсім.
|
|
81
|
+
|
|
82
|
+
## Багатодокументні YAML
|
|
83
|
+
|
|
84
|
+
Одна схема на файл; перевірка $schema-modeline звіряє **перший** документ (до наступного `---`). Інші `kind` у тому ж файлі — розділи файли або узгодь у рев'ю.
|
|
85
|
+
|
|
86
|
+
## Розширення файлів: лише `.yaml`
|
|
87
|
+
|
|
88
|
+
Усі маніфести під **`k8s`** (визначаються відносно кореня репо), включно з **`kustomization.yaml`**, — лише **`.yaml`**. Розширення **`.yml`** під `k8s` заборонене: `check k8s` падає з порадою перейменувати файл на `.yaml` (перевірка — `npm/rules/k8s/manifests/main.mjs`, повідомлення `розширення .yml — перейменуй на .yaml`).
|
|
89
|
+
|
|
90
|
+
Цей канон стосується лише дерев **`k8s`** — під **`.github/workflows/`** і **`.github/actions/`** діє протилежний канон (`.yml`), див. **`ga.mdc`**.
|
|
@@ -15,3 +15,40 @@ Rego-пакет: `k8s.manifest`
|
|
|
15
15
|
- `kind: Deployment` — канонічний запис у `spec.template.spec.topologySpreadConstraints`: `maxSkew: 1`, `topologyKey: kubernetes.io/hostname`, `whenUnsatisfiable: ScheduleAnyway`, `labelSelector.matchLabels.app: <app-label>` (перевіряється лише для Deployment з міткою `app` у `spec.selector.matchLabels`)
|
|
16
16
|
|
|
17
17
|
**Примітка:** cross-file логіка (Kustomize-резолюція, парність svc/svc-hl, HPA/PDB за каталогом, schema modeline) лишається у JS `rules/k8s/fix.mjs`.
|
|
18
|
+
|
|
19
|
+
## Deployment: `resources.requests` поза `base/`
|
|
20
|
+
|
|
21
|
+
Базовий шар (`…/k8s/…/base/…`) має жорстко фіксовані `resources.requests.cpu`/`memory` — див. **`base_manifest.mdc`**. Поза `base` (overlays, окремі каталоги) ця перевірка (рядок 12 вище) вимагає лише непорожні `resources.requests.cpu` і `resources.requests.memory` — конкретне значення не фіксується.
|
|
22
|
+
|
|
23
|
+
Якщо ще не підібрано власні ліміти під сервіс, орієнтир для **`requests`**:
|
|
24
|
+
|
|
25
|
+
```yaml
|
|
26
|
+
resources:
|
|
27
|
+
requests:
|
|
28
|
+
cpu: '0.5'
|
|
29
|
+
memory: '512Mi'
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
У прод-оверлеях підіймай **`cpu` / `memory`** до реального споживання через **`patches`**:
|
|
33
|
+
|
|
34
|
+
```yaml title="k8s/prod/kustomization.yaml (фрагмент)"
|
|
35
|
+
patches:
|
|
36
|
+
- target:
|
|
37
|
+
kind: Deployment
|
|
38
|
+
name: backend-api
|
|
39
|
+
patch: |-
|
|
40
|
+
- op: replace
|
|
41
|
+
path: /spec/template/spec/containers/0/resources/requests/cpu
|
|
42
|
+
value: '500m'
|
|
43
|
+
- op: replace
|
|
44
|
+
path: /spec/template/spec/containers/0/resources/requests/memory
|
|
45
|
+
value: 1Gi
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**`imagePullPolicy`** — цю перевірку скрипт **не** виконує.
|
|
49
|
+
|
|
50
|
+
## Batch-ресурси: `apiVersion: batch/v1beta1` → `batch/v1`
|
|
51
|
+
|
|
52
|
+
Ресурси **batch** (наприклад **CronJob**, **Job**): застаріле **`apiVersion: batch/v1beta1`** у файлах під **`k8s`** під час `check k8s` автоматично переписується на **`apiVersion: batch/v1`** (рядково, зі збереженням відступів/лапок). Детектор — `detectBatchV1beta1InK8sYamlFiles`, автофікс — T0-патерн `k8s-manifests-batch-v1beta1-apiversion` (обидва в **`npm/rules/k8s/manifests/main.mjs`** / **`fix-manifests.mjs`**), той самий механізм, що й `gateway-httproute-v1beta1` вище по домену gateway (див. **`gateway.mdc`**).
|
|
53
|
+
|
|
54
|
+
`apiVersion: autoscaling/v1` (заборонено для HPA, потрібен `autoscaling/v2`) — рядок 10 вище і **`hpa_pdb.mdc`**.
|
|
@@ -3,7 +3,7 @@ type: JS Module
|
|
|
3
3
|
title: fix-manifests.mjs
|
|
4
4
|
resource: npm/rules/k8s/manifests/fix-manifests.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 7245cdbe
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## Огляд
|
|
@@ -29,6 +29,8 @@ T0-autofix для `k8s/manifests`: детерміновані правки Kuber
|
|
|
29
29
|
що й детектор (kind → name → namespace → path).
|
|
30
30
|
- **gateway-httproute-v1beta1** — піднімає `apiVersion` HTTPRoute з `gateway.networking.k8s.io/v1beta1`
|
|
31
31
|
до `v1` разом із відповідним `$schema`-modeline.
|
|
32
|
+
- **batch-v1beta1-apiversion** — переписує `apiVersion: batch/v1beta1` (CronJob/Job) на `batch/v1`,
|
|
33
|
+
рядково, зі збереженням відступів і лапок.
|
|
32
34
|
|
|
33
35
|
## Публічний API
|
|
34
36
|
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* T0-autofix для `k8s/manifests` — детерміновані правки без LLM, керовані structured
|
|
5
5
|
* `data` детектора (#3 fix-hints). Покриває механічні родини порушень k8s.mdc:
|
|
6
6
|
* - `gateway-httproute-v1beta1` — apiVersion v1beta1 → v1 (+ $schema-modeline);
|
|
7
|
+
* - `batch-v1beta1-apiversion` — apiVersion batch/v1beta1 → batch/v1 (CronJob/Job);
|
|
7
8
|
* - `schema-modeline-first` — перемістити `# yaml-language-server: $schema=…` у перший рядок;
|
|
8
9
|
* - `kustomization-patches-sort` — впорядкувати `patches[]` (реюз детекторних sort-ключів);
|
|
9
10
|
* - `deployment-strategy` — проставити канонічний `spec.strategy` RollingUpdate.
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
compareStringTuplesEn,
|
|
22
23
|
kustomizationPatchSortKey,
|
|
23
24
|
loadSnippetSpec,
|
|
25
|
+
replaceBatchV1beta1ApiVersionInYamlText,
|
|
24
26
|
replaceGatewayHttpRouteV1beta1ApiVersionInYamlText,
|
|
25
27
|
snippetNameForKind
|
|
26
28
|
} from './main.mjs'
|
|
@@ -190,6 +192,15 @@ export const patterns = [
|
|
|
190
192
|
},
|
|
191
193
|
n => `gateway HTTPRoute apiVersion v1beta1 → v1: ${n} файл(ів)`
|
|
192
194
|
),
|
|
195
|
+
fileTransformPattern(
|
|
196
|
+
'k8s-manifests-batch-v1beta1-apiversion',
|
|
197
|
+
'batch-v1beta1-apiversion',
|
|
198
|
+
content => {
|
|
199
|
+
const { changed, content: next } = replaceBatchV1beta1ApiVersionInYamlText(content)
|
|
200
|
+
return changed ? next : null
|
|
201
|
+
},
|
|
202
|
+
n => `batch apiVersion v1beta1 → v1: ${n} файл(ів)`
|
|
203
|
+
),
|
|
193
204
|
fileTransformPattern(
|
|
194
205
|
'k8s-manifests-schema-modeline-first',
|
|
195
206
|
'schema-modeline-first',
|
|
@@ -1795,6 +1795,32 @@ async function detectGatewayHttpRouteV1beta1InK8sYamlFiles(yamlFiles, root, fail
|
|
|
1795
1795
|
}
|
|
1796
1796
|
}
|
|
1797
1797
|
|
|
1798
|
+
/**
|
|
1799
|
+
* Read-only детектор: знаходить файли з `apiVersion: batch/v1beta1` (застаріле для CronJob/Job)
|
|
1800
|
+
* і реєструє violation з `data: { kind: 'batch-v1beta1-apiversion' }` для T0-фіксу.
|
|
1801
|
+
* @param {string[]} yamlFiles абсолютні шляхи до YAML-файлів
|
|
1802
|
+
* @param {string} root корінь репозиторію
|
|
1803
|
+
* @param {(msg: string, opts?: object) => void} fail колбек реєстрації порушення
|
|
1804
|
+
* @returns {Promise<void>}
|
|
1805
|
+
*/
|
|
1806
|
+
async function detectBatchV1beta1InK8sYamlFiles(yamlFiles, root, fail) {
|
|
1807
|
+
for (const abs of yamlFiles) {
|
|
1808
|
+
const rel = (relative(root, abs) || abs).replaceAll('\\', '/')
|
|
1809
|
+
let raw
|
|
1810
|
+
try {
|
|
1811
|
+
raw = await readFile(abs, 'utf8')
|
|
1812
|
+
} catch {
|
|
1813
|
+
continue
|
|
1814
|
+
}
|
|
1815
|
+
if (!BATCH_V1BETA1_API_VERSION_LINE_RE.test(raw)) continue
|
|
1816
|
+
fail(`${rel}: apiVersion: batch/v1beta1 застаріло — оновіть до batch/v1 (k8s.mdc)`, {
|
|
1817
|
+
reason: 'batch-v1beta1-apiversion',
|
|
1818
|
+
file: rel,
|
|
1819
|
+
data: { kind: 'batch-v1beta1-apiversion' }
|
|
1820
|
+
})
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1798
1824
|
/**
|
|
1799
1825
|
* Прибирає BOM і ділить на рядки.
|
|
1800
1826
|
* @param {string} content вміст файлу
|
|
@@ -6566,6 +6592,8 @@ export async function lint(ctx) {
|
|
|
6566
6592
|
|
|
6567
6593
|
await detectGatewayHttpRouteV1beta1InK8sYamlFiles(yamlFiles, root, fail)
|
|
6568
6594
|
|
|
6595
|
+
await detectBatchV1beta1InK8sYamlFiles(yamlFiles, root, fail)
|
|
6596
|
+
|
|
6569
6597
|
// Plan B: пер-документні структурні правила — у rego-полісі `npm/policy/k8s/*`,
|
|
6570
6598
|
// викликаємо одним батчем на namespace через runConftestBatch. JS нижче робить
|
|
6571
6599
|
// лише cross-file orchestration, modeline та FS-existence перевірки.
|
|
@@ -20,3 +20,36 @@ Rego-пакет: `k8s.network_policy`
|
|
|
20
20
|
**Передача snippets:** через `--data` при виклику conftest (JS передає `templateData` для `runConftestBatch`).
|
|
21
21
|
|
|
22
22
|
**Примітка:** cross-file визначення `metadata.name` воркнавантаження і мітки `app` — JS (`validateNetworkPolicyForWorkload`).
|
|
23
|
+
|
|
24
|
+
## Обов'язковість і авто-створення
|
|
25
|
+
|
|
26
|
+
Для **кожного** workload (**Deployment**, **StatefulSet**, **DaemonSet**, **Job**, **CronJob**) під `k8s` обов'язковий **NetworkPolicy** у **`base/networkpolicy.yaml`** поруч з workload-маніфестом (multi-doc через `---`, якщо workload-ів кілька). Відсутні документи **`check k8s`** створює автоматично (канон і snippet-структура — вище) і додає `networkpolicy.yaml` у `base/kustomization.yaml` `resources:`.
|
|
27
|
+
|
|
28
|
+
Перевірка/генерація — **`npm/rules/k8s/manifests/main.mjs`** (`validateNetworkPoliciesForK8sWorkloads`, `regenerateLegacyNetworkPolicyDocsInFile`).
|
|
29
|
+
|
|
30
|
+
## HTTPRoute → NetworkPolicy ingress (GCLB + Envoy)
|
|
31
|
+
|
|
32
|
+
Якщо в каталозі workload є **`HTTPRoute`** (Gateway API, див. **`gateway.mdc`**) з **`backendRef`** на **`<workload>-hl`** Service, **`check k8s`** автоматично додає в NetworkPolicy цього workload **ingress-правило** з фіксованим набором CIDR-ів і **TCP-портами з `backendRefs[].port`** (дедуп, відсортовано за зростанням).
|
|
33
|
+
|
|
34
|
+
Без цього правила трафік від **GKE Gateway** (Envoy proxy-only subnet) і **Google health checks** блокується базовим NetworkPolicy.
|
|
35
|
+
|
|
36
|
+
CIDR-набір зафіксовано (`npm/rules/k8s/manifests/main.mjs`, `NETWORK_POLICY_GCLB_INGRESS_FROM`):
|
|
37
|
+
|
|
38
|
+
- `35.191.0.0/16` — GCP HC global
|
|
39
|
+
- `130.211.0.0/22` — GCP HC global (legacy)
|
|
40
|
+
- `10.0.0.0/8` — широкий range, покриває proxy-only subnets усіх регіонів GKE
|
|
41
|
+
|
|
42
|
+
```yaml title="k8s/base/networkpolicy.yaml — workload з HTTPRoute (з GCLB ingress)"
|
|
43
|
+
spec:
|
|
44
|
+
ingress:
|
|
45
|
+
- from:
|
|
46
|
+
- podSelector: {}
|
|
47
|
+
- from: # auto-added by check k8s for HTTPRoute-paired workloads
|
|
48
|
+
- ipBlock: { cidr: 35.191.0.0/16 }
|
|
49
|
+
- ipBlock: { cidr: 130.211.0.0/22 }
|
|
50
|
+
- ipBlock: { cidr: 10.0.0.0/8 }
|
|
51
|
+
ports:
|
|
52
|
+
- { protocol: TCP, port: 8080 }
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Алгоритм: функція `collectHttpRouteIngressForWorkload` у **`npm/rules/k8s/manifests/main.mjs`** (mapping `backendRef.name` → `Service.metadata.name` → `service.spec.selector.matchLabels.app`).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"$schema": "https://unpkg.com/@7n/rules/schemas/concern.json"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
## HTTPRoute в k8s — редирект і backendRefs
|
|
2
|
+
|
|
3
|
+
Якщо в `default.conf.template` були `proxy_pass`-секції — їхню логіку потрібно перенести
|
|
4
|
+
до HTTPRoute в k8s. `default.conf.template` **не повинен** містити жодного proxy (див.
|
|
5
|
+
`nginx-default-tpl/template/template.mdc`).
|
|
6
|
+
|
|
7
|
+
HTTPRoute має містити два правила:
|
|
8
|
+
|
|
9
|
+
1. **Exact → RequestRedirect 301 (https)** — редирект з `/$PUBLIC_PATH` на `/$PUBLIC_PATH/`
|
|
10
|
+
2. **PathPrefix → backendRefs:8080** — проксі трафіку на сервіс
|
|
11
|
+
|
|
12
|
+
```yaml
|
|
13
|
+
spec:
|
|
14
|
+
rules:
|
|
15
|
+
- matches:
|
|
16
|
+
- path:
|
|
17
|
+
type: Exact
|
|
18
|
+
value: /$PUBLIC_PATH
|
|
19
|
+
filters:
|
|
20
|
+
- type: RequestRedirect
|
|
21
|
+
requestRedirect:
|
|
22
|
+
scheme: https
|
|
23
|
+
path:
|
|
24
|
+
type: ReplaceFullPath
|
|
25
|
+
replaceFullPath: /$PUBLIC_PATH/
|
|
26
|
+
statusCode: 301
|
|
27
|
+
- matches:
|
|
28
|
+
- path:
|
|
29
|
+
type: PathPrefix
|
|
30
|
+
value: /$PUBLIC_PATH/
|
|
31
|
+
backendRefs:
|
|
32
|
+
- name: $SERVICE_NAME
|
|
33
|
+
port: 8080
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
де `$PUBLIC_PATH` підставляється з ini-файлу dev-середовища, а для інших середовищ — через `kustomization.yaml`.
|
|
37
|
+
`$SERVICE_NAME` — ім'я k8s-сервісу, що приймає трафік на порту 8080.
|
|
38
|
+
|
|
39
|
+
Патерн розпізнається функцією `httpRouteMatchesNginxDefaultTpl` у
|
|
40
|
+
`npm/rules/nginx-default-tpl/template/main.mjs`:
|
|
41
|
+
|
|
42
|
+
- перше правило має `path.type: Exact` і фільтр `RequestRedirect` з `scheme: https`, `path.type: ReplaceFullPath`, `statusCode: 301`
|
|
43
|
+
- друге правило має `path.type: PathPrefix` і `backendRefs` з `port: 8080`
|
|
44
|
+
|
|
45
|
+
**Статус: реалізовано, але НЕ підключено.** `httpRouteMatchesNginxDefaultTpl` визначена в коді,
|
|
46
|
+
але не викликається з `lint()` у `template/main.mjs` — перевірка HTTPRoute зараз **не входить**
|
|
47
|
+
в активний прогін `nginx-default-tpl`. Причина: щоб увімкнути її безпечно, потрібно ще визначити,
|
|
48
|
+
*коли саме* HTTPRoute з таким патерном є обов'язковим (навряд чи для будь-якого HTTPRoute в
|
|
49
|
+
репозиторії — лише для того, що відповідає сервісу з `default.conf.template`, з якого прибрали
|
|
50
|
+
proxy), і знайти/розпарсити відповідні YAML-файли (на кшталт інфраструктури
|
|
51
|
+
`npm/rules/abie/lib/http-route.mjs` + `findK8sYamlFiles`). Без цього умова спрацьовування
|
|
52
|
+
неоднозначна, а вмикання наосліп ризикує хибними спрацюваннями на репозиторіях зі своїми
|
|
53
|
+
валідними HTTPRoute-патернами. Перш ніж покладатись на автоматичний контроль цього патерну
|
|
54
|
+
або вмикати його в `lint()`, звір з поточним станом `lint()` у `template/main.mjs`.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
## Канонічна структура default.conf.template
|
|
2
|
+
|
|
3
|
+
`default.conf.template` — єдиний nginx-конфіг для фронтенду зі статичними файлами.
|
|
4
|
+
Він **не повинен** містити жодних `proxy_pass`, `proxy_redirect`, `proxy_set_header`,
|
|
5
|
+
`fastcgi_pass`, `grpc_pass`, `uwsgi_pass`. Усю проксі-логіку потрібно винести в HTTPRoute (k8s).
|
|
6
|
+
|
|
7
|
+
Канонічний вигляд файлу:
|
|
8
|
+
|
|
9
|
+
```nginx
|
|
10
|
+
server_tokens off;
|
|
11
|
+
port_in_redirect off;
|
|
12
|
+
client_max_body_size 0;
|
|
13
|
+
client_body_buffer_size 512M;
|
|
14
|
+
|
|
15
|
+
server {
|
|
16
|
+
listen 8080;
|
|
17
|
+
server_name _;
|
|
18
|
+
|
|
19
|
+
# disable all log
|
|
20
|
+
access_log off;
|
|
21
|
+
# `error_log off;` — НЕ валідний nginx: "off" трактується як ім'я файлу (/etc/nginx/off)
|
|
22
|
+
# і падає під readOnlyRootFilesystem. /dev/null — writable device.
|
|
23
|
+
error_log /dev/null crit;
|
|
24
|
+
|
|
25
|
+
# This would be the directory where your Vite app's static files are stored at
|
|
26
|
+
root /usr/share/nginx/html;
|
|
27
|
+
|
|
28
|
+
location /healthz {
|
|
29
|
+
add_header Content-Type text/plain;
|
|
30
|
+
access_log off;
|
|
31
|
+
return 200 "healthy";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Без gz стиснених файлів, 1 year is 31536000 seconds
|
|
35
|
+
location ~ ^$PUBLIC_PATH/(.+\.(?:gif|jpe?g|png|ico|woff2|xlsx))$ {
|
|
36
|
+
alias /usr/share/nginx/html/$1;
|
|
37
|
+
add_header 'Cache-Control' "public,max-age=31536000,immutable";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# С gz стисненими файлами, 1 year is 31536000 seconds
|
|
41
|
+
location ~ ^$PUBLIC_PATH/(.+\.(?:svg|js|css|ttf|map|xml|webmanifest|wasm))$ {
|
|
42
|
+
alias /usr/share/nginx/html/$1;
|
|
43
|
+
add_header 'Cache-Control' "public,max-age=31536000,immutable";
|
|
44
|
+
|
|
45
|
+
# дозволяє віддавати замість звичайного файлу попередньо стиснутий файл з таким же ім'ям та з розширенням ".gz"
|
|
46
|
+
gzip_static on;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
location $PUBLIC_PATH/ {
|
|
50
|
+
index index.html;
|
|
51
|
+
alias /usr/share/nginx/html/;
|
|
52
|
+
|
|
53
|
+
# eliminates the step of copying the data into the buffer and enables direct copying data from one file descriptor to another.
|
|
54
|
+
sendfile on;
|
|
55
|
+
# to prevent one fast connection from entirely occupying the worker process
|
|
56
|
+
sendfile_max_chunk 512k;
|
|
57
|
+
# to send HTTP response headers in one packet right after the chunk of data has been obtained by sendfile().
|
|
58
|
+
tcp_nopush on;
|
|
59
|
+
|
|
60
|
+
# дозволяє віддавати замість звичайного файлу попередньо стиснутий файл з таким же ім'ям та з розширенням ".gz"
|
|
61
|
+
gzip_static on;
|
|
62
|
+
|
|
63
|
+
try_files $uri $uri/ /index.html =404;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Обов'язкові вимоги до структури (перевіряються автоматично, `nginxTemplateViolations` у
|
|
69
|
+
`npm/rules/nginx-default-tpl/template/main.mjs`):
|
|
70
|
+
|
|
71
|
+
- `server_tokens off` — приховує версію nginx у відповідях
|
|
72
|
+
- `port_in_redirect off` — виключає порт з redirect-заголовків
|
|
73
|
+
- `client_max_body_size 0` — без ліміту тіла запиту
|
|
74
|
+
- `client_body_buffer_size 512M`
|
|
75
|
+
- `listen 8080`
|
|
76
|
+
- `server_name _` — catch-all
|
|
77
|
+
- `access_log off`
|
|
78
|
+
- `error_log /dev/null crit` (НЕ `error_log off;` — це некоректно в nginx)
|
|
79
|
+
- `root /usr/share/nginx/html`
|
|
80
|
+
- `location /healthz` з `return 200` і рядком `healthy`
|
|
81
|
+
- location для статики без gzip (`gif|jpeg|png|ico|woff2|xlsx`) з `Cache-Control: max-age=31536000,immutable`
|
|
82
|
+
- location для статики з gzip (`svg|js|css|ttf|map|xml|webmanifest|wasm`) з `gzip_static on`
|
|
83
|
+
- `gzip_static on` щонайменше двічі (два location зі стисненням)
|
|
84
|
+
- `$PUBLIC_PATH` у location — плейсхолдер підставляється через envsubst
|
|
85
|
+
- `sendfile on`, `sendfile_max_chunk 512k`, `tcp_nopush on`
|
|
86
|
+
- `try_files $uri $uri/ /index.html =404`
|
|
87
|
+
- **Відсутність** будь-якого `proxy_pass` / `fastcgi_pass` / `grpc_pass` / `uwsgi_pass`
|
|
88
|
+
|
|
89
|
+
## Конфігураційні *.ini файли і envsubst
|
|
90
|
+
|
|
91
|
+
Поруч з `default.conf.template` мають бути конфігураційні файли `*.ini` для кожного
|
|
92
|
+
середовища (наприклад, `values-dev.ini`, `values-prod.ini`).
|
|
93
|
+
|
|
94
|
+
Формат ini-файлу:
|
|
95
|
+
|
|
96
|
+
```ini
|
|
97
|
+
PUBLIC_PATH=/app
|
|
98
|
+
SERVICE_NAME=my-frontend
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Кожна змінна з `*.ini` **повинна використовуватись** у `default.conf.template` як `$KEY`
|
|
102
|
+
(через envsubst). Якщо у `*.ini` є ключ, що відсутній у шаблоні — його треба вилучити з ini
|
|
103
|
+
або додати в шаблон.
|
|
104
|
+
|
|
105
|
+
Перевірки (автоматичні, `checkTemplateFile`/`iniKeysMissingInTemplate` у
|
|
106
|
+
`npm/rules/nginx-default-tpl/template/main.mjs`):
|
|
107
|
+
|
|
108
|
+
- Поруч з кожним `default.conf.template` є щонайменше один `*.ini` файл
|
|
109
|
+
- Кожен ключ з `*.ini` (рядки виду `KEY=value`, без коментарів `#`/`;` і порожніх рядків)
|
|
110
|
+
присутній у шаблоні як `$KEY`
|
|
111
|
+
|
|
112
|
+
## Dockerfile — стиснення статики та envsubst шаблону
|
|
113
|
+
|
|
114
|
+
Dockerfile потребує двох кроків, пов'язаних з nginx-шаблоном.
|
|
115
|
+
|
|
116
|
+
### Стиснення статики (gzip_static)
|
|
117
|
+
|
|
118
|
+
Щоб nginx міг використовувати `gzip_static on`, файли необхідно стиснути під час збирання образу:
|
|
119
|
+
|
|
120
|
+
```dockerfile
|
|
121
|
+
RUN find /usr/share/nginx/html -type f \( \
|
|
122
|
+
-name '*.js' -o -name '*.css' -o -name '*.map' -o -name '*.xml' -o \
|
|
123
|
+
-name '*.webmanifest' -o -name '*.html' -o -name '*.wasm' -o -name '*.ttf' \
|
|
124
|
+
\) -exec gzip -k {} +
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Прапорець `-k` зберігає оригінальний файл поруч зі стиснутим `.gz`.
|
|
128
|
+
|
|
129
|
+
### envsubst для default.conf.template
|
|
130
|
+
|
|
131
|
+
Для підстановки змінних середовища у шаблон:
|
|
132
|
+
|
|
133
|
+
```dockerfile
|
|
134
|
+
# 1) Витягнути імена змінних з ini (ігноруємо коментарі/порожні)
|
|
135
|
+
# 2) Зробити список для envsubst: $NAMESPACE $NAMESPACE2 ...
|
|
136
|
+
# 3) Підвантажити значення з ini і підставити лише їх
|
|
137
|
+
RUN NAMES=$(sed -nE '/^\s*[#;]/d; /^\s*$/d; s/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=.*/\1/p' /tpl/values-$BRANCH.ini) && \
|
|
138
|
+
VARS=$(printf '%s\n' $NAMES | awk '{printf "$%s ", $0}') && \
|
|
139
|
+
export $(grep -v '^#' /tpl/values-$BRANCH.ini | xargs) && \
|
|
140
|
+
envsubst "$VARS" < /tpl/default.conf.template > /app/default.conf
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`/tpl/values-$BRANCH.ini` і `/tpl/default.conf.template` — приклад шляхів у контейнері;
|
|
144
|
+
конкретні шляхи залежать від проєкту, важливі лише сама послідовність кроків і імена
|
|
145
|
+
директив (`envsubst`, `default.conf.template`).
|
|
146
|
+
|
|
147
|
+
Перевірки (автоматичні, `checkDockerfiles` у `npm/rules/nginx-default-tpl/template/main.mjs`,
|
|
148
|
+
викликається один раз для всього репозиторію — не окремо по кожному `default.conf.template`):
|
|
149
|
+
|
|
150
|
+
- Десь у репозиторії є Dockerfile/Containerfile, що містить `find … /usr/share/nginx/html … gzip -k` — крок стиснення статики
|
|
151
|
+
- Десь у репозиторії є Dockerfile/Containerfile, що містить `envsubst` і посилання на `default.conf.template`
|
|
152
|
+
- Якщо в репозиторії є `default.conf.template`, але жодного Dockerfile / Containerfile не знайдено — це помилка
|
|
@@ -56,11 +56,12 @@ composer audit
|
|
|
56
56
|
|
|
57
57
|
## Запуск lint-php
|
|
58
58
|
|
|
59
|
-
`composer`-інструменти не мають єдиного CLI, який сам обходить репозиторій, тому php-лінт делегується у JS
|
|
59
|
+
`composer`-інструменти не мають єдиного CLI, який сам обходить репозиторій, тому php-лінт делегується у JS-детектори. Запуск — через **`n-rules lint php`** (CI — `--no-fix`); окремого `package.json`-скрипта немає.
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
Три концерни:
|
|
62
62
|
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
- `
|
|
66
|
-
|
|
63
|
+
- `npm/rules/php/project/main.mjs` — `composer audit` (обовʼязковий) + PHPStan + Psalm; project-wide, поза delta-планом (лише `n-rules lint --full` або scoped `n-rules lint php`);
|
|
64
|
+
- `npm/rules/php/phpcs/main.mjs` — `vendor/bin/phpcs --standard=Security`; per-file;
|
|
65
|
+
- `npm/rules/php/cs_fixer/main.mjs` — `vendor/bin/php-cs-fixer fix --dry-run --diff`; per-file.
|
|
66
|
+
|
|
67
|
+
Якщо `composer.json` у корені відсутній — усі концерни виходять без порушень (проєкт не PHP). Якщо `composer.json` є, але `composer` не знайдено в PATH — це помилка. `vendor/bin/php-cs-fixer`, `vendor/bin/phpcs`, `vendor/bin/phpstan`, `vendor/bin/psalm` — запускаються лише якщо встановлені (інакше крок пропускається з повідомленням).
|
|
@@ -8,6 +8,22 @@ Rego-пакет: `python.pyproject_toml`
|
|
|
8
8
|
|
|
9
9
|
**1. Заборона Poetry** — перевіряє заборонені під-таблиці `[tool.*]` за deny-template (drift-safe через `--data template`): [pyproject.toml.deny.toml](./template/pyproject.toml.deny.toml)
|
|
10
10
|
|
|
11
|
-
**2. PEP 621** —
|
|
11
|
+
**2. PEP 621** — метадані проєкту мають бути у секції **`[project]`** (PEP 621), а **не** в `[tool.poetry]`. Обовʼязкові поля:
|
|
12
|
+
|
|
13
|
+
- `[project].name` — назва пакета (непорожній рядок);
|
|
14
|
+
- `[project].version` — статична версія (непорожній рядок);
|
|
15
|
+
- `[project].requires-python` — мінімальна версія Python (наприклад `>=3.12`);
|
|
16
|
+
- `[project].dependencies` — залежності (навіть порожній список `[]`).
|
|
12
17
|
|
|
13
18
|
Канонічний цільовий вигляд: [pyproject.toml.snippet.toml](./template/pyproject.toml.snippet.toml)
|
|
19
|
+
|
|
20
|
+
## Залежності та lock-файл (uv)
|
|
21
|
+
|
|
22
|
+
Залежності додаються командою `uv add <pkg>`; dev-залежності — `uv add --dev <pkg>`. Lock-файл — **`uv.lock`** (іде в коміт, оновлюється командою `uv lock`).
|
|
23
|
+
|
|
24
|
+
## Міграція з Poetry на uv
|
|
25
|
+
|
|
26
|
+
1. Прибери `[tool.poetry]` і `poetry.lock` / `poetry.toml`.
|
|
27
|
+
2. Перенеси метадані в `[project]` (name, version, requires-python, dependencies) за PEP 621.
|
|
28
|
+
3. Згенеруй lock: `uv lock` → `uv.lock`.
|
|
29
|
+
4. Dev-залежності: `uv add --dev ruff mypy …`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## Перевірка наявності uv-toolchain і відсутності Poetry
|
|
2
2
|
|
|
3
|
-
JS-перевірка (`
|
|
3
|
+
JS-перевірка (`npm/rules/python/tooling/main.mjs`) запускається, якщо в корені репо є `pyproject.toml`. Перевіряє:
|
|
4
4
|
|
|
5
5
|
- `uv.lock` — має існувати (зафіксувати командою `uv lock`);
|
|
6
6
|
- `poetry.lock` / `poetry.toml` — мають **бути відсутніми** (Poetry заборонено);
|
|
@@ -9,15 +9,14 @@ JS-перевірка (`js/tooling.mjs`) запускається, якщо в
|
|
|
9
9
|
|
|
10
10
|
Якщо `pyproject.toml` відсутній — перевірка пропускається з кодом 0 (проєкт не Python).
|
|
11
11
|
|
|
12
|
-
## lint-python: JS
|
|
12
|
+
## lint-python: JS-детектори за concern
|
|
13
13
|
|
|
14
|
-
Інструменти uv-екосистеми не мають єдиного CLI-обходу репо, тому python-лінт делегується у JS
|
|
14
|
+
Інструменти uv-екосистеми не мають єдиного CLI-обходу репо, тому python-лінт делегується у JS-детектори. Запуск — через **`n-rules lint python`** (CI — `--no-fix`); окремого `package.json`-скрипта немає.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Три концерни (кожен — окремий per-file чи project-wide детектор):
|
|
17
17
|
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
- `
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- усі `ruff`/`mypy`-кроки запускаються лише якщо інструмент доступний у середовищі (інакше крок пропускається з повідомленням).
|
|
18
|
+
- `npm/rules/python/project/main.mjs` — `uv lock --check` + `uv sync --frozen` + ліцензійний аудит (`pip-licenses`); project-wide, поза delta-планом (лише `n-rules lint --full` або scoped `n-rules lint python`);
|
|
19
|
+
- `npm/rules/python/ruff/main.mjs` (+ `fix-ruff.mjs`) — `ruff check` + `ruff format --check` (детект), `ruff check --fix` + `ruff format` (autofix); per-file;
|
|
20
|
+
- `npm/rules/python/mypy/main.mjs` — `mypy` (лише детект, немає autofix); per-file.
|
|
21
|
+
|
|
22
|
+
Якщо `pyproject.toml` у корені відсутній — усі концерни виходять без порушень (проєкт не Python). Якщо `pyproject.toml` є, але `uv` не знайдено в PATH — це помилка. `ruff`/`mypy`-кроки запускаються лише якщо інструмент доступний у середовищі (інакше крок пропускається з повідомленням).
|
package/rules/rego/main.mdc
CHANGED
|
@@ -9,6 +9,20 @@ alwaysApply: false
|
|
|
9
9
|
|
|
10
10
|
Синтаксичні правила (`rego.v1`, `import rego.v1`, заборона legacy v0) — у `conftest.mdc` (alwaysApply). Цей файл — про **інструментарій**: VS Code, лінтери, форматування.
|
|
11
11
|
|
|
12
|
+
## Лінт-пайплайн (opa → regal → conftest)
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
n-rules lint rego
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Цілі — `npm/rules/` (рекурсивно знаходить `.rego` у `<rule>/<concern>/`). Три незалежні per-file-детектори (кожен свій concern), у такому порядку:
|
|
19
|
+
|
|
20
|
+
1. **`opa check --strict`** (`npm/rules/rego/opa_check/`) — компіляція з типами і строгим режимом: ловить мертвий код, неоднозначні правила, незадекларовані змінні. Зупиняє відповідний крок при помилці.
|
|
21
|
+
2. **`regal lint`** (`npm/rules/rego/regal/`) — статичний лінтер: v0-синтаксис, неявні set-rules, відхилення від `rego.v1`, bugs/idiomatic/performance-правила.
|
|
22
|
+
3. **`conftest verify`** (`npm/rules/rego/conftest_verify/`) — виконує `test_*` правила у `*_test.rego`. Крос-package (`import data.<pkg>`), тож коректний лише на всьому `npm/rules`; не входить у delta-план — спрацьовує лише через `n-rules lint --full` або scoped `n-rules lint rego`. Якщо `conftest` відсутній у `PATH` — пропускається без помилки.
|
|
23
|
+
|
|
24
|
+
`opa` і `regal` — лише у `PATH`, **не** додавай у `dependencies` / `devDependencies` (toolchain-інструменти, як і в інших мовних правилах цього репо). Це механічно перевіряється Rego-gate'ом `rego.package_json` (`npm/rules/rego/package_json/`) — deny, якщо `opa` чи `regal` з'являються у будь-якій секції залежностей `package.json`.
|
|
25
|
+
|
|
12
26
|
## Швидкий gate через conftest
|
|
13
27
|
|
|
14
28
|
| Пакет | Ціль | Що перевіряє |
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
## Лінт Rust: rustfmt + clippy + cargo-deny
|
|
2
|
+
|
|
3
|
+
**rustfmt** ([rust-lang/rustfmt](https://github.com/rust-lang/rustfmt)) — форматер; **clippy** ([rust-lang/rust-clippy](https://github.com/rust-lang/rust-clippy)) — лінтер; **cargo-deny** — перевірка ліцензій залежностей. Запуск — через **`n-rules lint rust`** (detector `npm/rules/rust/check/main.mjs`, autofix `npm/rules/rust/check/fix-check.mjs`):
|
|
4
|
+
|
|
5
|
+
- **read-only (детект, CI):** `cargo fmt --all -- --check` → `cargo clippy --all-targets --all-features -- -D warnings` → (за наявності `deny.toml` і встановленого `cargo-deny`) `cargo deny check licenses`.
|
|
6
|
+
- **`fix` (локально):** `cargo fmt --all` (реальне форматування) і, якщо `deny.toml` відсутній та `cargo-deny` встановлено, `cargo deny init` для генерації канонічного файлу.
|
|
7
|
+
|
|
8
|
+
`clippy` **не** автофіксимо (`--fix` потенційно небезпечний і може прибрати навмисний код) — clippy-порушення йдуть у LLM-ladder, а не в детермінований autofix.
|
|
9
|
+
|
|
10
|
+
Без `Cargo.toml` у cwd — no-op (exit 0). Окремого `package.json`-скрипта немає.
|
|
11
|
+
|
|
12
|
+
`cargo`, `rustfmt`, `clippy`, `cargo-deny` **не додавай** у `devDependencies` — це Rust toolchain, ставиться через `rustup`/`cargo install` локально або через `dtolnay/rust-toolchain@stable` у CI.
|
|
13
|
+
|
|
14
|
+
### Канон CI workflow `.github/workflows/lint-rust.yml`
|
|
15
|
+
|
|
16
|
+
Канонічний workflow, drift-safe перевірка (`rust.lint_rust_yml`) і вимога `Swatinem/rust-cache@v2` — `npm/rules/rust/lint_rust_yml/lint_rust_yml.mdc`. Тут лише сам лінт-скрипт; CI-канон workflow **не дублюється**.
|