@nitra/cursor 1.13.75 → 1.13.82
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 +76 -0
- package/package.json +1 -1
- package/rules/abie/abie.mdc +11 -3
- package/rules/abie/policy/base_deployment_preem/base_deployment_preem.rego +9 -11
- package/rules/abie/policy/health_check_policy/health_check_policy.rego +9 -10
- package/rules/abie/policy/http_route_base/http_route_base.rego +7 -8
- package/rules/changelog/fix/consistency/check.mjs +16 -16
- package/{scripts/utils → rules/changelog/fix/consistency}/package-manifest.mjs +1 -1
- package/rules/docker/docker.mdc +3 -3
- package/rules/docker/fix/lint/check.mjs +3 -3
- package/{scripts/utils → rules/docker/fix/lint}/docker-hadolint.mjs +3 -3
- package/rules/docker/lint/lint.mjs +2 -2
- package/rules/ga/fix/workflows/check.mjs +5 -1
- package/rules/ga/ga.mdc +8 -1
- package/rules/ga/lint/lint.mjs +4 -1
- package/rules/ga/policy/workflow_common/template/uses-min-versions.snippet.json +4 -0
- package/rules/ga/policy/workflow_common/workflow_common.rego +89 -0
- package/rules/graphql/fix/tooling/check.mjs +1 -1
- package/{scripts/utils → rules/graphql/fix/tooling}/graphql-gql-scan.mjs +47 -12
- package/rules/hasura/fix/internal_urls/check.mjs +1 -1
- package/{scripts/utils → rules/js-bun-db/fix/safety}/bun-sql-scan.mjs +1 -1
- package/rules/js-bun-db/fix/safety/check.mjs +1 -1
- package/rules/js-lint/fix/tooling/check.mjs +3 -15
- package/{scripts/utils → rules/js-lint/fix/tooling}/rebuild-oxlint-canonical.mjs +1 -1
- package/rules/js-lint/js-lint.mdc +2 -2
- package/rules/js-mssql/fix/deps/check.mjs +1 -1
- package/{scripts/utils → rules/js-mssql/fix/deps}/mssql-pool-scan.mjs +1 -1
- package/{scripts/utils → rules/js-run/fix/runtime}/bunyan-imports.mjs +1 -1
- package/{scripts/utils → rules/js-run/fix/runtime}/check-env-scan.mjs +1 -1
- package/rules/js-run/fix/runtime/check.mjs +5 -5
- package/{scripts/utils → rules/js-run/fix/runtime}/conn-file-rules.mjs +1 -1
- package/{scripts/utils → rules/js-run/fix/runtime}/conn-imports-scan.mjs +1 -1
- package/{scripts/utils → rules/js-run/fix/runtime}/promise-settimeout-scan.mjs +1 -1
- package/rules/k8s/k8s.mdc +1 -1
- package/rules/npm-module/fix/package_structure/check.mjs +11 -1
- package/rules/test/auto.md +1 -0
- package/rules/test/fix/location/check.mjs +77 -0
- package/rules/test/test.mdc +60 -0
- package/rules/vue/fix/packages/check.mjs +2 -2
- package/rules/vue/vue.mdc +1 -1
- package/scripts/auto-rules.mjs +3 -3
- package/scripts/utils/ast-scan-utils.mjs +3 -2
- package/scripts/utils/with-lock.mjs +120 -0
- package/scripts/utils/workspaces.mjs +1 -1
- package/scripts/utils/worktree-fingerprint.mjs +30 -0
- /package/{scripts/utils → rules/docker/fix/lint}/docker-mirror.mjs +0 -0
- /package/{scripts/utils → rules/js-lint/fix/tooling}/knip-canonical.json +0 -0
- /package/{scripts/utils → rules/js-lint/fix/tooling}/oxlint-canonical-skeleton.json +0 -0
- /package/{scripts/utils → rules/js-lint/fix/tooling}/oxlint-canonical.json +0 -0
- /package/{scripts/utils → rules/js-lint/fix/tooling}/oxlint-rules.tsv +0 -0
- /package/{scripts/utils → rules/vue/fix/packages}/vue-forbidden-imports.mjs +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,82 @@
|
|
|
4
4
|
|
|
5
5
|
Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
|
|
6
6
|
|
|
7
|
+
## [1.13.82] - 2026-05-23
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **`rules/test`: виняток для `*_test.rego` файлів — лишаються поряд із полісі (OPA/Conftest community-конвенція)**:
|
|
12
|
+
- **Rego unit-тести (`*_test.rego`) лежать у тому самому каталозі, що й `<name>.rego`** — за загальноприйнятим патерном OPA/Conftest. `package <name>` (полісі) ↔ `package <name>_test` (тест) семантично зв'язані через `package`-декларації, а не локацію файлу; `conftest verify -p <dir>` рекурсивний, тож знаходить тест незалежно від місця, але спільнота тримає їх поруч (бачимо у OPA examples, Gatekeeper library, Datree, Styra DAS bundles). Це **легітимне відхилення** від внутрішньої JS-конвенції «`tests/` всюди» на користь OPA-ідіоми.
|
|
13
|
+
- `rules/test/test.mdc` v1.1 — додано **окрему секцію про виняток** для Rego: «`*_test.rego` лишаються поряд із полісі, бо це загальноприйнятий OPA/Conftest community-патерн» (з прикладом структури `policy/<concern>/{<name>.rego, <name>_test.rego, target.json}`).
|
|
14
|
+
- `rules/test/fix/location/check.mjs` — перевіряє **лише `*.test.mjs`**, `*_test.rego` свідомо виключено з область перевірки (зафіксовано у docstring).
|
|
15
|
+
- Додано test-case у `rules/test/fix/location/tests/check.test.mjs`: `*_test.rego` поряд із полісі НЕ є порушенням.
|
|
16
|
+
- **Відкат переміщення `*_test.rego`**: 69 файлів, які раніше було помилково перенесено у `policy/<concern>/tests/<name>_test.rego`, повернуто у `policy/<concern>/<name>_test.rego` через `git mv`. Порожні `tests/` піддиректорії під `policy/` видалено.
|
|
17
|
+
- **`npx @nitra/cursor check test`** охоплює лише JS-тести: «✅ Всі 77 файлів *.test.mjs у каталозі tests/». Rego-тести продовжують перевірятись через `conftest verify` у правилі `rego`.
|
|
18
|
+
|
|
19
|
+
## [1.13.81] - 2026-05-23
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **`npm-module.package_structure`: carve-out для rule-name сегмента** у `classifyPublishedFileAsTest`. Раніше для шляху `rules/<X>/...` сегмент `<X>` піддавався TEST_DIR_NAMES-перевірці, що давало false positive на правилах із id, що збігається з test-style ім'ям (`test`, `tests`, `fixtures` тощо). Тепер сегмент індекс 1 (ім'я правила, коли індекс 0 — `rules`) пропускається; глибші сегменти (`rules/<r>/fix/<c>/tests/`) продовжують перевірятись.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **Нове правило `test` (`npm/rules/test/`)** — програмний канон розміщення тестів (ADR `docs/adr/20260523-154806-...`):
|
|
28
|
+
- `test.mdc` — конвенція «`*.test.mjs` живуть у `tests/` поряд із джерелом», з описом спецвипадків (root `tests/`, fixtures у `tests/__fixtures__/` і `tests/fixtures/`, test-helpers як shared-infra).
|
|
29
|
+
- `fix/location/check.mjs` — обхід дерева `walkDir`'ом (зі стандартним skip-листом + `.n-cursor.json:ignore`); для кожного `*.test.mjs` басенейм батьківської директорії має бути `tests`, інакше fail з вказівкою куди перенести.
|
|
30
|
+
- `fix/location/tests/check.test.mjs` — 6 тестів самого правила (eats own dogfood).
|
|
31
|
+
- `auto.md` — auto-enable умова: «якщо у проекті є хоча б один файл `*.test.mjs`».
|
|
32
|
+
- Додано `"test"` у `.n-cursor.json:rules` репо `@nitra/cursor`.
|
|
33
|
+
- Додано `"ignore": [".claude/worktrees"]` у `.n-cursor.json` — щоб правило не звітувало про знімки в git worktrees.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- **Тести переміщено з-поряд-із-файлом у `dir/tests/` піддиректорію** (ADR `docs/adr/20260523-154806-...`):
|
|
38
|
+
- **73 sibling-тести** у `rules/...` і `scripts/...`: для кожного `dir/X.test.mjs` → `dir/tests/X.test.mjs` із автоматичним оновленням relative imports.
|
|
39
|
+
- **3 integration-тести у `npm/tests/`** — без змін (вже відповідали конвенції).
|
|
40
|
+
- **`npm/scripts/utils/__fixtures__/`** → `npm/scripts/utils/tests/__fixtures__/`.
|
|
41
|
+
- **`npm/rules/nginx-default-tpl/fix/template/fixtures/`** → `.../tests/fixtures/`; посилання в `npm/tests/check-rule-fixtures.test.mjs` оновлено.
|
|
42
|
+
- Ручні фіксапи 4 тестів із HERE/`..` path patterns (sync-setup-bun-deps-action, inline-template-links, rules/adr/fix/hooks, rules/abie/utils/enabled) — додано додатковий `..`, бо тести стали на рівень глибше.
|
|
43
|
+
- `package.json#files` негативні globs (`!**/*.test.mjs`, `!**/__fixtures__/**`, `!**/fixtures/**`) працюють рекурсивно — без змін.
|
|
44
|
+
- **77 тестів** проходять у новому layout (76 існуючих + 1 нового правила): `bun test` 843 pass / 2 fail (обидва — pre-existing `with-lock` issues, не пов'язані).
|
|
45
|
+
- `npx @nitra/cursor check test` → `✅ Всі 77 файлів *.test.mjs у каталозі tests/ (test.mdc)`.
|
|
46
|
+
|
|
47
|
+
## [1.13.79] - 2026-05-23
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- **Перенесення single-rule сканерів і canonical-конфігів з `npm/scripts/utils/` у `npm/rules/<rule>/fix/<sub>/`** (узгоджено з конвенцією `rules/ga/fix/workflows/`, `rules/nginx-default-tpl/fix/template/` тощо; ADR `docs/adr/20260523-114913-...`, який supersede `20260523-112217-...`):
|
|
52
|
+
- **js-lint** (`rules/js-lint/fix/tooling/`): `knip-canonical.json`, `oxlint-canonical.json`, `oxlint-canonical-skeleton.json`, `oxlint-rules.tsv`, `rebuild-oxlint-canonical.mjs`. Константи `OXLINT_CANONICAL_JSON_PATH` / `KNIP_CANONICAL_JSON_PATH` у `check.mjs` стали локальними (без 4-річневих `..`).
|
|
53
|
+
- **js-run** (`rules/js-run/fix/runtime/`): `bunyan-imports.mjs` (+test), `check-env-scan.mjs`, `conn-file-rules.mjs` (+test), `conn-imports-scan.mjs` (+test), `promise-settimeout-scan.mjs` (+test).
|
|
54
|
+
- **docker** (`rules/docker/fix/lint/`): `docker-hadolint.mjs` (+test), `docker-mirror.mjs`. `rules/docker/lint/lint.mjs` тепер імпортує з `../fix/lint/docker-hadolint.mjs`.
|
|
55
|
+
- **js-bun-db** (`rules/js-bun-db/fix/safety/`): `bun-sql-scan.mjs`.
|
|
56
|
+
- **js-mssql** (`rules/js-mssql/fix/deps/`): `mssql-pool-scan.mjs`.
|
|
57
|
+
- **changelog** (`rules/changelog/fix/consistency/`): `package-manifest.mjs` (+test).
|
|
58
|
+
- **vue** (`rules/vue/fix/packages/`): `vue-forbidden-imports.mjs` (+test).
|
|
59
|
+
- **graphql** (`rules/graphql/fix/tooling/`): `graphql-gql-scan.mjs`. Cross-rule імпорту немає: `extractVueScriptBlocks`, локалізована `contentForGqlScan` і власні `isGqlScanSourceFile` / `shouldSkipFileForGqlScan` (з власною source-regex і skip-list `.d.ts` / `auto-imports.d.ts` / `components.d.ts`) дубльовані всередині `graphql-gql-scan.mjs` — правила залишаються самодостатніми.
|
|
60
|
+
- **`scripts/auto-rules.mjs`** оновлено: імпорти переадресовано на нові локації трьох сканерів (`bun-sql-scan`, `graphql-gql-scan`, `vue-forbidden-imports`).
|
|
61
|
+
- **`.mdc`-документація** оновлена: `rules/js-lint/js-lint.mdc`, `rules/docker/docker.mdc`, `rules/vue/vue.mdc`, `.cursor/rules/n-js-lint.mdc`, `.cursor/rules/n-vue.mdc` — посилання на нові шляхи canonical-файлів і сканерів.
|
|
62
|
+
|
|
63
|
+
## [1.13.78] - 2026-05-23
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- **abie / k8s / hasura — актуалізація посилань на неіснуючий `check-abie.mjs`:** після реструктуризації `rules/abie/` на `fix/<concern>/check.mjs` (+ Rego-пакети у `policy/`) монолітного `check-abie.mjs` більше немає; застарілі посилання в активних `.mdc`/`.rego`/`.mjs` (поза історичним `CHANGELOG.md`) оновлено:
|
|
68
|
+
- `npm/rules/abie/abie.mdc` — три згадки замінено: пер-документна перевірка HTTPRoute base hostnames → Rego `abie.http_route_base`; env-DNS-скан → `fix/env_dns/check.mjs`; cross-file/FS-логіку розбито за концернами (`hc_pairing/`, `ua_http_route/`, `ua_node_selector/`, `env_dns/`, `firebase_hosting/`) з поясненням, що `targetRef.name -hl` cross-check обчислюється з `hcp.metadata.name` у Rego.
|
|
69
|
+
- `npm/rules/abie/policy/{http_route_base,base_deployment_preem,health_check_policy}/*.rego` — у шапках замінено `npm/scripts/check-abie.mjs` на актуальні джерела: cross-file gating через `policy/<pkg>/target.json` (glob), rule-level applies-гейт у `fix/applies/check.mjs`, FS-парність HCP↔Deployment у `fix/hc_pairing/check.mjs`. Прибрано згадки видалених JS-функцій (`validateAbieHcPolicy`, `deploymentDocumentHasAbieBasePreemNodeSelector`).
|
|
70
|
+
- `npm/rules/k8s/k8s.mdc` — рядок про `targetRef -hl` для abie-проєктів вказує на Rego-пакет `abie.health_check_policy` + `abie.mdc` (замість `check-abie.mjs`).
|
|
71
|
+
- `npm/rules/hasura/fix/internal_urls/check.mjs` — у JSDoc згадку `check-abie` замінено на нейтральне «abie-перевірки».
|
|
72
|
+
|
|
73
|
+
### Added
|
|
74
|
+
|
|
75
|
+
- **`with-lock`:** атомарний `mkdirSync`-лок + SHA-256 fingerprint-дедуп для важких команд; пілот — `lint-ga` автоматично серіалізує паралельні запуски та пропускає дублікати при незміненому робочому дереві (TTL 10 хв). Нові модулі: `scripts/utils/worktree-fingerprint.mjs`, `scripts/utils/with-lock.mjs`.
|
|
76
|
+
|
|
77
|
+
## [1.13.76] - 2026-05-22
|
|
78
|
+
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
- **`ga.workflow_common` — мінімальні версії marketplace actions у `uses:`:** `actions/checkout` >= major `v6` (`@v6` і `@v6.0.2` дозволені), `Infisical/secrets-action` >= `v1.0.16` (канон у `policy/workflow_common/template/uses-min-versions.snippet.json`; SHA-pin пропускається). `check-ga` передає template через `--data`. Bump `ga.mdc` `1.9` → `1.10`.
|
|
82
|
+
|
|
7
83
|
## [1.13.75] - 2026-05-22
|
|
8
84
|
|
|
9
85
|
### Removed
|
package/package.json
CHANGED
package/rules/abie/abie.mdc
CHANGED
|
@@ -36,7 +36,7 @@ spec:
|
|
|
36
36
|
|
|
37
37
|
### HTTPRoute: спільні сервіси **`auth-run-hl`**, **`file-link-hl`**
|
|
38
38
|
|
|
39
|
-
У **HTTPRoute** у шляху з **`…/k8s/base/…`** у **`spec.hostnames`** дозволені лише **`aiml.live`**, **`*.aiml.live`** та інші піддомени **aiml.live** (перевірка
|
|
39
|
+
У **HTTPRoute** у шляху з **`…/k8s/base/…`** у **`spec.hostnames`** дозволені лише **`aiml.live`**, **`*.aiml.live`** та інші піддомени **aiml.live** (перевірка — Rego-пакет **`abie.http_route_base`**, див. розділ нижче).
|
|
40
40
|
|
|
41
41
|
Ці **Service** (headless **`-hl`**) живуть у **базовому** неймспейсі **`dev`**. У маніфесті **HTTPRoute** під **`k8s`** (шар без **`ua/`** — наприклад **`…/k8s/base/hr.yaml`**) для кожного **`backendRefs`** до такого сервісу явно вкажи **`namespace: dev`** і порт **8080**:
|
|
42
42
|
|
|
@@ -133,7 +133,7 @@ KVCMS_URL=http://kvcms-hl.ua-apruv.svc.abie-ua.internal:8080
|
|
|
133
133
|
|
|
134
134
|
`<namespace>` (наприклад `dev-apruv` / `ua-apruv`) — `metadata.name` цільового namespace після kustomize-overlay для відповідного середовища; `<service>` — `metadata.name` headless Service (`-hl`) того сервісу, до якого йде URL.
|
|
135
135
|
|
|
136
|
-
**Перевірка `check
|
|
136
|
+
**Перевірка `fix/env_dns/check.mjs`** сканує всі `*.env` файли, basename яких збігається з `dev.env` / `ua.env` (з провідною крапкою чи без), знаходить **усі** internal URL (`http://<svc>.<ns>.svc.<dns>` — як для Hasura-ендпоінта, так і для KVCMS чи будь-якого іншого) і вимагає, щоб для кожного:
|
|
137
137
|
|
|
138
138
|
- DNS-суфікс відповідав env: `abie-dev.internal` / `abie-ua.internal`;
|
|
139
139
|
- namespace починався з `dev-` / `ua-` відповідно.
|
|
@@ -170,4 +170,12 @@ bun add -d @nitra/abie-docs
|
|
|
170
170
|
- **`clean_merged_ignore_branches/`** → `abie.clean_merged_ignore_branches` — у workflow `.github/workflows/clean-merged-branch.yml` крок з `uses: phpdocker-io/github-actions-delete-abandoned-branches` має `with.ignore_branches`, що містить токени `dev,ua` (case-insensitive). **Цільові файли:** `.github/workflows/clean-merged-branch.yml`.
|
|
171
171
|
- **`package_json_docs/`** → `abie.package_json_docs` — у кореневому `package.json` `devDependencies` має містити `@nitra/abie-docs` (presence-only, версію не фіксуємо). **Цільові файли:** `package.json`.
|
|
172
172
|
|
|
173
|
-
Cross-file
|
|
173
|
+
Cross-file / FS-логіка лишається у JS-частинах (`fix/<concern>/check.mjs`) — Rego не читає файлову систему й не робить cross-document резолюцію:
|
|
174
|
+
|
|
175
|
+
- парність HCP↔Deployment у каталозі та modeline `hc.yaml` — `fix/hc_pairing/check.mjs`;
|
|
176
|
+
- валідація ua-overlay JSON6902 patches на HTTPRoute + аналіз cross-namespace `backendRefs` у пакетах — `fix/ua_http_route/check.mjs`;
|
|
177
|
+
- ua-overlay JSON6902 patch на `Deployment.nodeSelector` (`preem: false`) — `fix/ua_node_selector/check.mjs`;
|
|
178
|
+
- env→cluster DNS (`*.dev.env` / `*.ua.env`) — `fix/env_dns/check.mjs`;
|
|
179
|
+
- скан артефактів Firebase Hosting у підкаталогах першого рівня — `fix/firebase_hosting/check.mjs`.
|
|
180
|
+
|
|
181
|
+
Точна звірка `targetRef.name` HealthCheckPolicy з суфіксом `-hl` обчислюється з `hcp.metadata.name` і живе у Rego (`abie.health_check_policy`); за конвенцією `hcp.metadata.name` дорівнює `<deployment.name>`, тому окремий cross-file lookup до маніфесту Deployment не потрібен.
|
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
#
|
|
2
|
-
# `
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# JSON6902-патчем на `preem: false`.
|
|
1
|
+
# Перевірка (abie.mdc): кожен `Deployment` у файлах під `…/k8s/.../base/…` має
|
|
2
|
+
# `spec.template.spec.nodeSelector.preem` зі значенням, що вважається істинним
|
|
3
|
+
# (boolean `true` або рядок `"true"` без урахування регістру). Overlay ua далі
|
|
4
|
+
# підміняє селектор JSON6902-патчем на `preem: false`
|
|
5
|
+
# (див. `fix/ua_node_selector/check.mjs`).
|
|
7
6
|
#
|
|
8
7
|
# Запуск (локально, лише для одного base-YAML з Deployment):
|
|
9
8
|
# conftest test path/to/k8s/base/deployment.yaml \
|
|
10
|
-
# -p npm/
|
|
9
|
+
# -p npm/rules/abie/policy/base_deployment_preem \
|
|
11
10
|
# --namespace abie.base_deployment_preem
|
|
12
11
|
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# `
|
|
16
|
-
# Cross-file gating (правило `abie` у `.n-cursor.json`, шлях файла) — у JS.
|
|
12
|
+
# Cross-file gating: шлях `…/k8s/.../base/…` фільтрується через
|
|
13
|
+
# `policy/base_deployment_preem/target.json` (glob). Rule-level applies-гейт —
|
|
14
|
+
# `fix/applies/check.mjs` (поле `rules` у `.n-cursor.json`).
|
|
17
15
|
#
|
|
18
16
|
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
19
17
|
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
#
|
|
2
|
-
# `npm/scripts/check-abie.mjs` (abie.mdc).
|
|
1
|
+
# Структурна перевірка `HealthCheckPolicy` (abie.mdc).
|
|
3
2
|
#
|
|
4
3
|
# Запуск (локально):
|
|
5
4
|
# conftest test path/to/k8s/.../hc.yaml \
|
|
6
|
-
# -p npm/
|
|
5
|
+
# -p npm/rules/abie/policy/health_check_policy \
|
|
7
6
|
# --namespace abie.health_check_policy
|
|
8
7
|
#
|
|
9
8
|
# Перевіряє для `kind: HealthCheckPolicy`:
|
|
@@ -14,12 +13,13 @@
|
|
|
14
13
|
# починається з `/`;
|
|
15
14
|
# - `spec.default.config.httpHealthCheck.port: 8080`;
|
|
16
15
|
# - `spec.targetRef.kind: Service`;
|
|
17
|
-
# - `spec.targetRef.name`
|
|
16
|
+
# - `spec.targetRef.name` — `<hcp.metadata.name>-hl` (exact, з нормалізацією
|
|
17
|
+
# суфікса).
|
|
18
18
|
#
|
|
19
|
-
# Cross-file gating
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
19
|
+
# Cross-file gating: glob по `hc.yaml` у k8s-дереві — у
|
|
20
|
+
# `policy/health_check_policy/target.json`. FS-парність HCP↔Deployment та
|
|
21
|
+
# modeline `hc.yaml` — `fix/hc_pairing/check.mjs`. Rule-level applies-гейт —
|
|
22
|
+
# `fix/applies/check.mjs`.
|
|
23
23
|
#
|
|
24
24
|
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
25
25
|
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
@@ -122,8 +122,7 @@ deny contains msg if {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
# Нормалізація: якщо `metadata.name` уже закінчується на `-hl` — використовуємо
|
|
125
|
-
# як є; інакше додаємо суфікс.
|
|
126
|
-
# у `check-abie.mjs`.
|
|
125
|
+
# як є; інакше додаємо суфікс.
|
|
127
126
|
expected_target_ref_name(name) := name if {
|
|
128
127
|
endswith(name, "-hl")
|
|
129
128
|
} else := concat("", [name, "-hl"])
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
#
|
|
2
|
-
# `
|
|
3
|
-
# `aiml.live` (включно з піддоменами та `*.aiml.live`).
|
|
1
|
+
# Перевірка `HTTPRoute` у шарі `…/k8s/.../base/...` (abie.mdc): дозволені лише
|
|
2
|
+
# hostnames з домену `aiml.live` (включно з піддоменами та `*.aiml.live`).
|
|
4
3
|
#
|
|
5
4
|
# Запуск (локально):
|
|
6
|
-
# conftest test path/to/k8s/base/hr.yaml
|
|
5
|
+
# conftest test path/to/k8s/base/hr.yaml \
|
|
6
|
+
# -p npm/rules/abie/policy/http_route_base \
|
|
7
7
|
# --namespace abie.http_route_base
|
|
8
8
|
#
|
|
9
|
-
# Cross-file gating (саме шлях `…/base
|
|
10
|
-
#
|
|
11
|
-
# `spec.hostnames`.
|
|
12
|
-
# одиничного YAML.
|
|
9
|
+
# Cross-file gating (саме шлях `…/k8s/.../base/...` визначає, чи застосовувати
|
|
10
|
+
# правило) задає glob у `policy/http_route_base/target.json`. Тут — лише
|
|
11
|
+
# валідація вмісту `spec.hostnames`. Rule-level applies-гейт — `fix/applies/check.mjs`.
|
|
13
12
|
#
|
|
14
13
|
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
15
14
|
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
manifestFilePath,
|
|
29
29
|
parsePyprojectFields,
|
|
30
30
|
readPackageManifest
|
|
31
|
-
} from '
|
|
31
|
+
} from './package-manifest.mjs'
|
|
32
32
|
|
|
33
33
|
const execFileAsync = promisify(execFile)
|
|
34
34
|
|
|
@@ -247,7 +247,7 @@ async function workspaceHasRelevantChangesAgainstBase(baseRef, ws, subWorkspaces
|
|
|
247
247
|
/**
|
|
248
248
|
* Версія з маніфесту на `baseRef`.
|
|
249
249
|
* @param {string} baseRef параметр
|
|
250
|
-
* @param {import('
|
|
250
|
+
* @param {import('./package-manifest.mjs').PackageManifest} manifest параметр
|
|
251
251
|
* @returns {Promise<string | null>} результат
|
|
252
252
|
*/
|
|
253
253
|
async function readBaseVersion(baseRef, manifest) {
|
|
@@ -308,8 +308,8 @@ async function defaultGetPublishedPyPiVersion(name) {
|
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
/**
|
|
311
|
-
* @param {import('
|
|
312
|
-
* @param {(name: string, kind?: import('
|
|
311
|
+
* @param {import('./package-manifest.mjs').PackageManifest} manifest параметр
|
|
312
|
+
* @param {(name: string, kind?: import('./package-manifest.mjs').PackageKind) => Promise<string | null>} getPublishedVersion параметр
|
|
313
313
|
* @returns {Promise<string | null>} результат
|
|
314
314
|
*/
|
|
315
315
|
function resolvePublishedVersion(manifest, getPublishedVersion) {
|
|
@@ -319,7 +319,7 @@ function resolvePublishedVersion(manifest, getPublishedVersion) {
|
|
|
319
319
|
|
|
320
320
|
/**
|
|
321
321
|
* @param {string} name пакет
|
|
322
|
-
* @param {import('
|
|
322
|
+
* @param {import('./package-manifest.mjs').PackageKind} [kind] тип пакета
|
|
323
323
|
* @returns {Promise<string | null>} опублікована версія або null
|
|
324
324
|
*/
|
|
325
325
|
function defaultGetPublishedVersion(name, kind = 'npm') {
|
|
@@ -330,14 +330,14 @@ function defaultGetPublishedVersion(name, kind = 'npm') {
|
|
|
330
330
|
}
|
|
331
331
|
|
|
332
332
|
/**
|
|
333
|
-
* @returns {(name: string, kind?: import('
|
|
333
|
+
* @returns {(name: string, kind?: import('./package-manifest.mjs').PackageKind) => Promise<string | null>} стандартний резолвер
|
|
334
334
|
*/
|
|
335
335
|
function createDefaultGetPublishedVersion() {
|
|
336
336
|
return defaultGetPublishedVersion
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
/**
|
|
340
|
-
* @param {import('
|
|
340
|
+
* @param {import('./package-manifest.mjs').PackageManifest} manifest параметр
|
|
341
341
|
* @param {(msg: string) => void} pass параметр
|
|
342
342
|
* @param {(msg: string) => void} fail параметр
|
|
343
343
|
*/
|
|
@@ -375,7 +375,7 @@ async function verifyChangelogEntry(ws, version, pass, fail) {
|
|
|
375
375
|
}
|
|
376
376
|
|
|
377
377
|
/**
|
|
378
|
-
* @param {import('
|
|
378
|
+
* @param {import('./package-manifest.mjs').PackageManifest} manifest параметр
|
|
379
379
|
* @returns {string} результат
|
|
380
380
|
*/
|
|
381
381
|
function workspaceLabel(manifest) {
|
|
@@ -383,7 +383,7 @@ function workspaceLabel(manifest) {
|
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
/**
|
|
386
|
-
* @param {import('
|
|
386
|
+
* @param {import('./package-manifest.mjs').PackageManifest} manifest параметр
|
|
387
387
|
* @param {string} Vcurrent параметр
|
|
388
388
|
* @param {string[]} subWorkspaces параметр
|
|
389
389
|
* @param {(msg: string) => void} pass параметр
|
|
@@ -438,9 +438,9 @@ async function checkPublishedWorkspacePendingGitChanges(manifest, Vcurrent, subW
|
|
|
438
438
|
}
|
|
439
439
|
|
|
440
440
|
/**
|
|
441
|
-
* @param {import('
|
|
441
|
+
* @param {import('./package-manifest.mjs').PackageManifest} manifest параметр
|
|
442
442
|
* @param {string[]} subWorkspaces параметр
|
|
443
|
-
* @param {(name: string, kind?: import('
|
|
443
|
+
* @param {(name: string, kind?: import('./package-manifest.mjs').PackageKind) => Promise<string | null>} getPublishedVersion параметр
|
|
444
444
|
* @param {(msg: string) => void} pass параметр
|
|
445
445
|
* @param {(msg: string) => void} fail параметр
|
|
446
446
|
* @returns {Promise<void>} результат
|
|
@@ -475,7 +475,7 @@ async function checkPublishedWorkspace(manifest, subWorkspaces, getPublishedVers
|
|
|
475
475
|
|
|
476
476
|
/**
|
|
477
477
|
* @param {string} comparisonRef ref/SHA для `git diff` / `git show`
|
|
478
|
-
* @param {import('
|
|
478
|
+
* @param {import('./package-manifest.mjs').PackageManifest} manifest параметр
|
|
479
479
|
* @param {string} baseLabel параметр
|
|
480
480
|
* @param {(msg: string) => void} pass параметр
|
|
481
481
|
* @param {(msg: string) => void} fail параметр
|
|
@@ -507,7 +507,7 @@ async function checkLocalOnlyChangedWorkspace(comparisonRef, manifest, baseLabel
|
|
|
507
507
|
}
|
|
508
508
|
|
|
509
509
|
/**
|
|
510
|
-
* @param {import('
|
|
510
|
+
* @param {import('./package-manifest.mjs').PackageManifest[]} localOnly параметр
|
|
511
511
|
* @param {string[]} subWorkspaces параметр
|
|
512
512
|
* @param {(msg: string) => void} pass параметр
|
|
513
513
|
* @param {(msg: string) => void} fail параметр
|
|
@@ -543,7 +543,7 @@ async function runLocalOnlyChecks(localOnly, subWorkspaces, pass, fail) {
|
|
|
543
543
|
|
|
544
544
|
/**
|
|
545
545
|
* @param {object} [opts] опції перевірки
|
|
546
|
-
* @param {(name: string, kind?: import('
|
|
546
|
+
* @param {(name: string, kind?: import('./package-manifest.mjs').PackageKind) => Promise<string | null>} [opts.getPublishedVersion] перевизначення npm/PyPI у тестах
|
|
547
547
|
* @returns {Promise<number>} exit-код перевірки
|
|
548
548
|
*/
|
|
549
549
|
export async function check(opts = {}) {
|
|
@@ -558,11 +558,11 @@ export async function check(opts = {}) {
|
|
|
558
558
|
const isMonorepoRoot = subWorkspaces.length > 0
|
|
559
559
|
|
|
560
560
|
/**
|
|
561
|
-
@type {import('
|
|
561
|
+
@type {import('./package-manifest.mjs').PackageManifest[]}
|
|
562
562
|
*/
|
|
563
563
|
const published = []
|
|
564
564
|
/**
|
|
565
|
-
@type {import('
|
|
565
|
+
@type {import('./package-manifest.mjs').PackageManifest[]}
|
|
566
566
|
*/
|
|
567
567
|
const localOnly = []
|
|
568
568
|
|
|
@@ -8,7 +8,7 @@ import { dirname, join, relative } from 'node:path'
|
|
|
8
8
|
|
|
9
9
|
import { parse as parseToml } from 'smol-toml'
|
|
10
10
|
|
|
11
|
-
import { getMonorepoPackageRootDirs, isIgnoredWorkspaceRoot } from '
|
|
11
|
+
import { getMonorepoPackageRootDirs, isIgnoredWorkspaceRoot } from '../../../../scripts/utils/workspaces.mjs'
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @typedef {'npm' | 'python'} PackageKind
|
package/rules/docker/docker.mdc
CHANGED
|
@@ -7,7 +7,7 @@ alwaysApply: false
|
|
|
7
7
|
|
|
8
8
|
# Docker — hadolint
|
|
9
9
|
|
|
10
|
-
Для образів з Docker Hub — **`oven/bun`**, **`alpine`**, **`nginxinc/nginx-unprivileged`**, **`node`** — у **`FROM`** треба вказувати дзеркало GCR, а не pull напряму з Hub: **`mirror.gcr.io/oven/bun`**, **`mirror.gcr.io/library/alpine`**, **`mirror.gcr.io/nginxinc/nginx-unprivileged`**, **`mirror.gcr.io/library/node`**. Перевіряє **`check-docker.mjs`**, деталі в **`npm/
|
|
10
|
+
Для образів з Docker Hub — **`oven/bun`**, **`alpine`**, **`nginxinc/nginx-unprivileged`**, **`node`** — у **`FROM`** треба вказувати дзеркало GCR, а не pull напряму з Hub: **`mirror.gcr.io/oven/bun`**, **`mirror.gcr.io/library/alpine`**, **`mirror.gcr.io/nginxinc/nginx-unprivileged`**, **`mirror.gcr.io/library/node`**. Перевіряє **`check-docker.mjs`**, деталі в **`npm/rules/docker/fix/lint/docker-mirror.mjs`**.
|
|
11
11
|
|
|
12
12
|
Також Dockerfile/Containerfile **має бути multistage build**: окремий build stage (залежності/компіляція) і окремий runtime stage. У фінальному stage дозволені лише мінімальні базові образи:
|
|
13
13
|
|
|
@@ -99,7 +99,7 @@ CLI **`hadolint`** приймає лише **явні шляхи** (`[DOCKERFILE
|
|
|
99
99
|
|
|
100
100
|
**Область lint-docker (вужча, ніж `check docker`):** лише файли з іменем **`Dockerfile`** та **`*.Dockerfile`** (суфікс **`.dockerfile`** без урахування регістру, наприклад **`api.Dockerfile`**). Файли **`Dockerfile.prod`**, **`Containerfile`** тощо **не** входять у **`lint-docker`**; їх ловить **`check docker`** (`check-docker.mjs`).
|
|
101
101
|
|
|
102
|
-
Обхід: **`walkDir`** з тими самими пропусками каталогів, що й **`check-docker.mjs`**. Виклик **`hadolint`**: **`PATH`**, інакше **`docker run`** — спільна логіка **`npm/
|
|
102
|
+
Обхід: **`walkDir`** з тими самими пропусками каталогів, що й **`check-docker.mjs`**. Виклик **`hadolint`**: **`PATH`**, інакше **`docker run`** — спільна логіка **`npm/rules/docker/fix/lint/docker-hadolint.mjs`**.
|
|
103
103
|
|
|
104
104
|
- Канон `package.json#scripts.lint-docker`: [package.json.snippet.json](./policy/package_json/template/package.json.snippet.json)
|
|
105
105
|
|
|
@@ -109,7 +109,7 @@ CLI **`hadolint`** приймає лише **явні шляхи** (`[DOCKERFILE
|
|
|
109
109
|
|
|
110
110
|
- Канон: [lint-docker.yml.snippet.yml](./policy/lint_docker_yml/template/lint-docker.yml.snippet.yml)
|
|
111
111
|
|
|
112
|
-
Узгоджуй версію hadolint **v2.12.0** з **`HADOLINT_IMAGE`** у **`npm/
|
|
112
|
+
Узгоджуй версію hadolint **v2.12.0** з **`HADOLINT_IMAGE`** у **`npm/rules/docker/fix/lint/docker-hadolint.mjs`**.
|
|
113
113
|
|
|
114
114
|
Кореневий скрипт **`lint`** (див. **`n-bun.mdc`**) **обов'язково** містить **`bun run lint-docker`**, коли в проєкті підключено правило **`docker`**.
|
|
115
115
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Запускає hadolint для Dockerfile / Containerfile у всьому репозиторії (див. docker.mdc).
|
|
3
3
|
*
|
|
4
4
|
* Додатково переконуються, що образи `oven/bun`, `alpine`, `nginx`, `node` з Docker Hub
|
|
5
|
-
* вказуються через `mirror.gcr.io` (див.
|
|
5
|
+
* вказуються через `mirror.gcr.io` (див. `./docker-mirror.mjs`).
|
|
6
6
|
*
|
|
7
7
|
* Також перевіряє, що Dockerfile/Containerfile має **multistage build** і що фінальний stage
|
|
8
8
|
* використовує дозволений runtime-образ (див. docker.mdc):
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
import { readFile } from 'node:fs/promises'
|
|
31
31
|
import { basename } from 'node:path'
|
|
32
32
|
|
|
33
|
-
import { getMirrorGcrHint, getFromImageToken } from '
|
|
34
|
-
import { lintDockerfileWithHadolint, posixRel } from '
|
|
33
|
+
import { getMirrorGcrHint, getFromImageToken } from './docker-mirror.mjs'
|
|
34
|
+
import { lintDockerfileWithHadolint, posixRel } from './docker-hadolint.mjs'
|
|
35
35
|
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
36
36
|
import { loadCursorIgnorePaths } from '../../../../scripts/utils/load-cursor-config.mjs'
|
|
37
37
|
import { walkDir } from '../../../../scripts/utils/walkDir.mjs'
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
* Спільна логіка виклику hadolint для шляхів до Dockerfile (див. docker.mdc).
|
|
3
3
|
*
|
|
4
4
|
* Відносні шляхи з прямими слешами для контейнера; спочатку hadolint з PATH,
|
|
5
|
-
* інакше docker run з образом HADOLINT_IMAGE. Використовується check
|
|
6
|
-
*
|
|
5
|
+
* інакше docker run з образом HADOLINT_IMAGE. Використовується `./check.mjs`
|
|
6
|
+
* (check-docker) та `../../lint/lint.mjs` (run-docker).
|
|
7
7
|
*/
|
|
8
8
|
import { spawnSync } from 'node:child_process'
|
|
9
9
|
import { relative, sep } from 'node:path'
|
|
10
10
|
|
|
11
|
-
import { resolveCmd } from '
|
|
11
|
+
import { resolveCmd } from '../../../../scripts/utils/resolve-cmd.mjs'
|
|
12
12
|
|
|
13
13
|
/** Тег образу для резервного запуску (узгоджуй з docker.mdc). */
|
|
14
14
|
export const HADOLINT_IMAGE = 'hadolint/hadolint:v2.12.0'
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
* check docker, не обробляються Dockerfile.*, Containerfile тощо — лише канонічне ім’я
|
|
6
6
|
* Dockerfile та варіанти виду app.Dockerfile (регістр суфікса не важливий).
|
|
7
7
|
*
|
|
8
|
-
* Виклик hadolint — через
|
|
8
|
+
* Виклик hadolint — через ../fix/lint/docker-hadolint.mjs (PATH або docker run).
|
|
9
9
|
*/
|
|
10
10
|
import { basename } from 'node:path'
|
|
11
11
|
|
|
12
12
|
import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
|
|
13
|
-
import { lintDockerfileWithHadolint, posixRel } from '
|
|
13
|
+
import { lintDockerfileWithHadolint, posixRel } from '../fix/lint/docker-hadolint.mjs'
|
|
14
14
|
import { createCheckReporter } from '../../../scripts/utils/check-reporter.mjs'
|
|
15
15
|
import { loadCursorIgnorePaths } from '../../../scripts/utils/load-cursor-config.mjs'
|
|
16
16
|
import { walkDir } from '../../../scripts/utils/walkDir.mjs'
|
|
@@ -303,10 +303,14 @@ async function runAllGaRego(wfDir, ymlWorkflows, pass, fail) {
|
|
|
303
303
|
|
|
304
304
|
if (ymlWorkflows.length === 0) return
|
|
305
305
|
const wfFiles = ymlWorkflows.map(f => join(wfDir, f))
|
|
306
|
+
const workflowCommonDir = join(GA_POLICY_DIR, 'workflow_common')
|
|
307
|
+
const workflowCommonTpl = await loadTemplate(workflowCommonDir)
|
|
308
|
+
const usesMinVersionsSnippet = workflowCommonTpl['uses-min-versions']?.snippet
|
|
306
309
|
const violations = runConftestBatch({
|
|
307
310
|
policyDirRel: 'ga/workflow_common',
|
|
308
311
|
namespace: 'ga.workflow_common',
|
|
309
|
-
files: wfFiles
|
|
312
|
+
files: wfFiles,
|
|
313
|
+
templateData: usesMinVersionsSnippet ? { snippet: usesMinVersionsSnippet } : undefined
|
|
310
314
|
})
|
|
311
315
|
for (const v of violations) fail(`${v.filename}: ${v.message}`)
|
|
312
316
|
if (violations.length === 0) {
|
package/rules/ga/ga.mdc
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Правила форматів для .github/workflows
|
|
3
|
-
version: '1.
|
|
3
|
+
version: '1.10'
|
|
4
4
|
globs: ".github/workflows/*.yml"
|
|
5
5
|
alwaysApply: false
|
|
6
6
|
---
|
|
@@ -35,6 +35,13 @@ concurrency:
|
|
|
35
35
|
|
|
36
36
|
- Канон: [git-ai.yml.snippet.yml](./policy/git_ai/template/git-ai.yml.snippet.yml)
|
|
37
37
|
|
|
38
|
+
**Мінімальні версії marketplace actions** у `uses:` (перевіряє `ga.workflow_common`):
|
|
39
|
+
|
|
40
|
+
- `actions/checkout` — **не нижче major `v6`** (`@v6`, `@v6.0.2` тощо дозволені; `@v5` — ні);
|
|
41
|
+
- `Infisical/secrets-action` — **не нижче `v1.0.16`** (Node 24; нижчі теги лишаються на Node 20, deprecated з червня 2026).
|
|
42
|
+
|
|
43
|
+
Канон: [uses-min-versions.snippet.json](./policy/workflow_common/template/uses-min-versions.snippet.json). SHA-pin (40 hex) semver-політику не застосовує. Для checkout рекомендується **`v6.0.2+`** (Node 24 у action), але мінімум політики — лише major **6**.
|
|
44
|
+
|
|
38
45
|
**Локальний composite** (`uses: ./.github/actions/setup-bun-deps` або `./npm/github-actions/setup-bun-deps`): **спочатку** обов’язковий крок **`actions/checkout@v6`** (`persist-credentials: false`), інакше runner не знайде `action.yml`. Сам composite: **`actions/setup-node@v6`** (**Node 24**), **Bun**, **`actions/cache@v5`**, **`bun install --frozen-lockfile`**.
|
|
39
46
|
|
|
40
47
|
**ЗАБОРОНЕНО** дублювати кроки встановлення Bun та кешування безпосередньо у workflow файлах. Завжди використовуй локальний composite action.
|
package/rules/ga/lint/lint.mjs
CHANGED
|
@@ -29,6 +29,7 @@ import { platform } from 'node:process'
|
|
|
29
29
|
import { check as checkGa } from '../fix/workflows/check.mjs'
|
|
30
30
|
import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
|
|
31
31
|
import { runLintStep } from '../../../scripts/utils/run-lint-step.mjs'
|
|
32
|
+
import { withLock } from '../../../scripts/utils/with-lock.mjs'
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* Опис залежності preflight-ом: бінарник, для чого потрібен, і команди встановлення.
|
|
@@ -147,7 +148,7 @@ function preflight(dep) {
|
|
|
147
148
|
* Першу помилку від actionlint/zizmor/check повертаємо як код виходу; наступні кроки не запускаються.
|
|
148
149
|
* @returns {Promise<number>} 0 — все OK, інакше — код першого кроку, що впав
|
|
149
150
|
*/
|
|
150
|
-
|
|
151
|
+
async function runLintGaSteps() {
|
|
151
152
|
let preflightOk = true
|
|
152
153
|
for (const dep of [SHELLCHECK_PREFLIGHT, UV_PREFLIGHT, CONFTEST_PREFLIGHT]) {
|
|
153
154
|
if (!preflight(dep)) preflightOk = false
|
|
@@ -163,3 +164,5 @@ export async function runLintGaCli() {
|
|
|
163
164
|
console.log('\n▶ check-ga (rego-полісі npm/policy/ga/ + JS cross-file перевірки)')
|
|
164
165
|
return await checkGa()
|
|
165
166
|
}
|
|
167
|
+
|
|
168
|
+
export const runLintGaCli = () => withLock('lint-ga', runLintGaSteps)
|