@nitra/cursor 1.8.206 → 1.8.207
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 +29 -0
- package/package.json +1 -1
- package/policy/abie/health_check_policy/health_check_policy.rego +73 -0
- package/policy/abie/http_route_base/http_route_base.rego +45 -0
- package/policy/adr/settings_json/settings_json.rego +31 -0
- package/policy/adr/settings_local_json/settings_local_json.rego +28 -0
- package/policy/bun/bunfig/bunfig.rego +33 -0
- package/policy/bun/package_json/package_json.rego +94 -0
- package/policy/capacitor/package_json/package_json.rego +45 -0
- package/policy/ga/clean_ga_workflows/clean_ga_workflows.rego +0 -26
- package/policy/ga/clean_merged_branch/clean_merged_branch.rego +0 -25
- package/policy/ga/git_ai/git_ai.rego +0 -26
- package/policy/ga/lint_ga/lint_ga.rego +0 -26
- package/policy/ga/workflow_common/workflow_common.rego +161 -0
- package/policy/graphql/package_json/package_json.rego +35 -0
- package/policy/hasura/svc_hl/svc_hl.rego +27 -0
- package/policy/image_compress/package_json/package_json.rego +94 -0
- package/policy/js_bun_db/package_json/package_json.rego +28 -0
- package/policy/js_lint/lint_js_yml/lint_js_yml.rego +98 -0
- package/policy/js_lint/package_json/package_json.rego +137 -0
- package/policy/js_mssql/package_json/package_json.rego +57 -0
- package/policy/js_run/configmap/configmap.rego +45 -0
- package/policy/js_run/jsconfig/jsconfig.rego +66 -0
- package/policy/js_run/package_json/package_json.rego +31 -0
- package/policy/k8s/manifest/manifest.rego +130 -0
- package/policy/npm_module/emit_types_config/emit_types_config.rego +37 -0
- package/policy/npm_module/npm_package_json/npm_package_json.rego +55 -0
- package/policy/npm_module/npm_publish_yml/npm_publish_yml.rego +79 -0
- package/policy/npm_module/root_package_json/root_package_json.rego +28 -0
- package/policy/php/lint_php_yml/lint_php_yml.rego +32 -0
- package/policy/php/package_json/package_json.rego +19 -0
- package/policy/style_lint/lint_style_yml/lint_style_yml.rego +35 -0
- package/policy/style_lint/package_json/package_json.rego +49 -0
- package/policy/text/cspell/cspell.rego +91 -0
- package/policy/text/markdownlint/markdownlint.rego +21 -0
- package/policy/text/oxfmtrc/oxfmtrc.rego +90 -0
- package/policy/text/package_json/package_json.rego +88 -0
- package/policy/vue/package_json/package_json.rego +54 -0
- package/scripts/check-adr.mjs +3 -2
- package/scripts/check-bun.mjs +21 -117
- package/scripts/check-graphql.mjs +6 -45
- package/scripts/check-hasura.mjs +2 -3
- package/scripts/check-image-avif.mjs +3 -3
- package/scripts/check-image-compress.mjs +25 -132
- package/scripts/check-js-bun-db.mjs +3 -50
- package/scripts/check-js-run.mjs +8 -8
- package/scripts/check-k8s.mjs +4 -4
- package/scripts/check-npm-module.mjs +17 -8
- package/scripts/check-php.mjs +16 -51
- package/scripts/check-style-lint.mjs +28 -52
- package/scripts/check-text.mjs +47 -219
- package/scripts/check-vue.mjs +3 -16
- package/scripts/lint-conftest.mjs +351 -0
- package/scripts/lint-ga.mjs +39 -2
- package/scripts/run-shellcheck-text.mjs +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,35 @@
|
|
|
4
4
|
|
|
5
5
|
Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
|
|
6
6
|
|
|
7
|
+
## [1.8.207] - 2026-05-08
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `npm/policy/ga/workflow_common/workflow_common.rego` — універсальні Rego-перевірки для **кожного** `.github/workflows/*.yml`: блок `concurrency` (group / cancel-in-progress), заборонені `oven-sh/setup-bun` / `actions/cache` / `bun install` у `uses`/`run` будь-якого кроку, заборонене shell-продовження `\` перед NL у `run:`, обовʼязковий `actions/checkout@…` перед локальним composite-action `setup-bun-deps`. Підключено в `lint-ga.mjs` як один прогін `conftest test <…all yml…> --namespace ga.workflow_common`.
|
|
12
|
+
- `npm/policy/bun/{bunfig,package_json}/*.rego` — порт `check-bun.mjs` (TOML і JSON-частина): `[install].linker == "hoisted"` у `bunfig.toml`; у кореневому `package.json` без `packageManager`, без `dependencies`, у `devDependencies` лише `@nitra/*`; агрегований `lint`-скрипт покриває всі `lint-*` через `bun run` і завершується `&& oxfmt .`.
|
|
13
|
+
- `npm/policy/text/{oxfmtrc,cspell,markdownlint,package_json}/*.rego` — порт `check-text.mjs`: `.oxfmtrc.json` обовʼязкові ключі і канонічні значення; `.cspell.json` `version "0.2"`, `language`, імпорт `@nitra/cspell-dict`, заборона `@cspell/dict-*`, обовʼязкові `ignorePaths`; `.markdownlint-cli2.jsonc` `gitignore: true`; `package.json` без Prettier, `@nitra/cspell-dict ^2.0.0+`, без `markdownlint-cli2` у залежностях.
|
|
14
|
+
- `npm/policy/style_lint/{package_json,lint_style_yml}/*.rego` — порт `check-style-lint.mjs`: скрипт `lint-style` через `npx stylelint`, `@nitra/stylelint-config` у `devDependencies`, `stylelint.extends == "@nitra/stylelint-config"`; у `lint-style.yml` хоча б один `run` з `npx stylelint`.
|
|
15
|
+
- `npm/policy/php/{package_json,lint_php_yml}/*.rego` — порт `check-php.mjs`: скрипт `lint-php` у `package.json`; у `lint-php.yml` хоча б один `run` з `bun run lint-php`.
|
|
16
|
+
- `npm/policy/npm_module/{root_package_json,npm_package_json,emit_types_config,npm_publish_yml}/*.rego` — порт `check-npm-module.mjs`: `workspaces ∋ "npm"` у кореневому `package.json`; у `npm/package.json` `types` відповідає одному з канонічних патернів і `files ∋ "types"`; `npm/tsconfig.emit-types.json` має канонічні `compilerOptions`; `.github/workflows/npm-publish.yml` має `on.push.paths ∋ "npm/**"`, `branches ∋ "main"`, `permissions.id-token: write` і крок `JS-DevTools/npm-publish` з `with.package: npm/package.json`.
|
|
17
|
+
- `npm/policy/k8s/manifest/manifest.rego` — порт пер-документних структурних правил `check-k8s.mjs`: `kind: Ingress` заборонено (Gateway API), `apiVersion: autoscaling/v1` заборонено (HPA → v2), у `kind: Service` заборонені анотації `cloud.google.com/neg` / `cloud.google.com/backend-config`, у `kind: Deployment` кожен контейнер `containers`+`initContainers` має непорожнє `resources.requests.cpu`. Cross-file Kustomize-логіка (svc/svc-hl, HPA/PDB, namespace base, kustomization patches) лишається в JS.
|
|
18
|
+
- `npm/policy/js_lint/{package_json,lint_js_yml}/*.rego` — порт `check-js-lint.mjs`: канонічний `lint-js`, `@nitra/eslint-config ≥ 3.9.2`, `engines.node ≥ 24`, `engines.bun ≥ 1.3`, `type: "module"`; у `lint-js.yml` `actions/checkout@v6` з `persist-credentials: false`, `setup-bun-deps`, `bunx oxlint/eslint/jscpd .`, без `--fix` у CI.
|
|
19
|
+
- `npm/policy/js_mssql/package_json/package_json.rego` — порт `check-js-mssql.mjs`: `dependencies.mssql ≥ 12.5.0` (підтримує `^12.5.0`, `>=12.5.0`, `workspace:*`).
|
|
20
|
+
- `npm/policy/js_bun_db/package_json/package_json.rego` — порт `check-js-bun-db.mjs`: у `dependencies` заборонені `pg`, `pg-format`, `mysql2`.
|
|
21
|
+
- `npm/policy/js_run/{package_json,jsconfig,configmap}/*.rego` — порт `check-js-run.mjs`: заборона `bunyan` / `@nitra/bunyan` у залежностях; `jsconfig.json` має канонічні `compilerOptions` і `include`; у k8s ConfigMap `OTEL_RESOURCE_ATTRIBUTES` містить `service.name=` і `service.namespace=`.
|
|
22
|
+
- `npm/policy/vue/package_json/package_json.rego` — порт `check-vue.mjs`: якщо `dependencies.vue` присутній, у `devDependencies` має бути `vite` мажорної версії ≥ 8.
|
|
23
|
+
- `npm/policy/graphql/package_json/package_json.rego` — порт `check-graphql.mjs`: `scripts.dump-schema` точно відповідає канонічному.
|
|
24
|
+
- `npm/policy/image_compress/package_json/package_json.rego` — порт `check-image-compress.mjs`: `lint-image` викликає `npx @nitra/minify-image --src=. --write` без `--avif`; агрегований `lint` містить `bun run lint-image`; `@nitra/minify-image` НЕ у `dependencies`/`devDependencies`.
|
|
25
|
+
- `npm/policy/hasura/svc_hl/svc_hl.rego` — порт `check-hasura.mjs` (мінімум): у `hasura/k8s/base/svc-hl.yaml` Service з `metadata.name` має закінчуватись на `-h`.
|
|
26
|
+
- `npm/policy/adr/{settings_json,settings_local_json}/*.rego` — порт `check-adr.mjs`: `.claude/settings.json` має містити Stop-hook з командою `.claude/hooks/capture-decisions.sh`; `.claude/settings.local.json` (якщо існує) — НЕ повинен мати дубля цього хука.
|
|
27
|
+
- `npm/policy/capacitor/package_json/package_json.rego` — порт `check-capacitor.mjs`: `dependencies['@capacitor/core']` мажорна ≥ 8 (підтримує `workspace:*`).
|
|
28
|
+
- `npm/policy/abie/{health_check_policy,http_route_base}/*.rego` — порт `check-abie.mjs`: `HealthCheckPolicy` (`networking.gke.io/v1`) має непорожній `requestPath` зі слешем, `port: 8080`, `targetRef.name` закінчується на `-hl`; `HTTPRoute` у `…/base/…` приймає лише hostnames у домені `aiml.live`.
|
|
29
|
+
- `npm/scripts/lint-conftest.mjs` (+ `bun run lint-conftest` у `package.json`) — єдиний раннер conftest по всіх нових polysi: для кожного namespace — single-file або walk-предикат, з gating-ом по `.n-cursor.json:rules`, як у `check-*.mjs`. Викликається в кореневому `lint` після `lint-rego`.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- `npm/policy/ga/{lint_ga,clean_ga_workflows,clean_merged_branch,git_ai}/*.rego`: прибрано дублікати правил `concurrency` (group / cancel-in-progress / missing) — їх покриває `ga.workflow_common`. Заодно усунено мовчазний баг `not is_object(input.concurrency)` (коли поля немає, повертає `undefined`, не `true`); у `workflow_common` через `object.get(input, "concurrency", false)` дає визначене значення. Канонічна тригер-група `expected_concurrency_group` теж видалена з кожної per-workflow polysi.
|
|
34
|
+
- `npm/scripts/lint-ga.mjs`: до існуючих per-workflow conftest-таргетів додано фінальний прогін `ga.workflow_common` одним викликом `conftest test <усі .yml> --namespace ga.workflow_common`. Імпорт `readdirSync` з `node:fs` для перерахунку workflow-файлів.
|
|
35
|
+
|
|
7
36
|
## [1.8.206] - 2026-05-08
|
|
8
37
|
|
|
9
38
|
### Added
|
package/package.json
CHANGED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Порт мінімальної структурної перевірки `HealthCheckPolicy` з
|
|
2
|
+
# `npm/scripts/check-abie.mjs` (abie.mdc).
|
|
3
|
+
#
|
|
4
|
+
# Запуск (локально):
|
|
5
|
+
# conftest test path/to/k8s/.../hc.yaml -p npm/policy/abie \
|
|
6
|
+
# --namespace abie.health_check_policy
|
|
7
|
+
#
|
|
8
|
+
# Перевіряє, для документів з `kind: HealthCheckPolicy` (apiVersion
|
|
9
|
+
# `networking.gke.io/v1`):
|
|
10
|
+
# - `spec.config.httpHealthCheck.requestPath` — непорожній шлях, що починається з `/`;
|
|
11
|
+
# - `spec.config.httpHealthCheck.port` (або `spec.targetRef.name` суфікс) — `8080`;
|
|
12
|
+
# - `spec.targetRef.name` має закінчуватись на `-hl` (headless backend).
|
|
13
|
+
#
|
|
14
|
+
# Cross-file gating (`abie` правило в `.n-cursor.json`, парність з Deployment-каталогу,
|
|
15
|
+
# узгодження з `metadata.name` Deployment) — у JS (`check-abie.mjs`).
|
|
16
|
+
#
|
|
17
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
18
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
19
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
20
|
+
package abie.health_check_policy
|
|
21
|
+
|
|
22
|
+
import rego.v1
|
|
23
|
+
|
|
24
|
+
req_path_starts_with_slash_template := concat(" ", [
|
|
25
|
+
"HealthCheckPolicy: requestPath має починатись з `/`",
|
|
26
|
+
"(зараз %q) (abie.mdc)",
|
|
27
|
+
])
|
|
28
|
+
|
|
29
|
+
# ── deny: requestPath ──────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
deny contains msg if {
|
|
32
|
+
is_health_check_policy
|
|
33
|
+
req_path == ""
|
|
34
|
+
msg := "HealthCheckPolicy: spec.config.httpHealthCheck.requestPath має бути непорожнім (abie.mdc)"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
deny contains msg if {
|
|
38
|
+
is_health_check_policy
|
|
39
|
+
req_path != ""
|
|
40
|
+
not startswith(req_path, "/")
|
|
41
|
+
msg := sprintf(req_path_starts_with_slash_template, [req_path])
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# ── deny: port == 8080 ────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
deny contains msg if {
|
|
47
|
+
is_health_check_policy
|
|
48
|
+
port := object.get(http_health_check, "port", null)
|
|
49
|
+
port != null
|
|
50
|
+
port != 8080
|
|
51
|
+
msg := sprintf("HealthCheckPolicy: port має бути 8080 (зараз %v) (abie.mdc)", [port])
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# ── deny: targetRef.name закінчується на `-hl` ────────────────────────────
|
|
55
|
+
|
|
56
|
+
deny contains msg if {
|
|
57
|
+
is_health_check_policy
|
|
58
|
+
name := object.get(object.get(input.spec, "targetRef", {}), "name", "")
|
|
59
|
+
name != ""
|
|
60
|
+
not endswith(name, "-hl")
|
|
61
|
+
msg := sprintf("HealthCheckPolicy: targetRef.name має закінчуватись на `-hl` (зараз %q) (abie.mdc)", [name])
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# ── helpers ────────────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
is_health_check_policy if {
|
|
67
|
+
input.kind == "HealthCheckPolicy"
|
|
68
|
+
startswith(object.get(input, "apiVersion", ""), "networking.gke.io/")
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
http_health_check := object.get(object.get(object.get(input, "spec", {}), "config", {}), "httpHealthCheck", {})
|
|
72
|
+
|
|
73
|
+
req_path := object.get(http_health_check, "requestPath", "")
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Порт перевірки `HTTPRoute` у шарі `…/k8s/.../base/...` з
|
|
2
|
+
# `npm/scripts/check-abie.mjs` (abie.mdc): дозволені лише hostnames з домену
|
|
3
|
+
# `aiml.live` (включно з піддоменами та `*.aiml.live`).
|
|
4
|
+
#
|
|
5
|
+
# Запуск (локально):
|
|
6
|
+
# conftest test path/to/k8s/base/hr.yaml -p npm/policy/abie \
|
|
7
|
+
# --namespace abie.http_route_base
|
|
8
|
+
#
|
|
9
|
+
# Cross-file gating (саме шлях `…/base/…` визначає, чи застосовувати правило)
|
|
10
|
+
# — у JS: conftest викликаємо лише на YAML-ах з base/. Тут — лише валідація вмісту
|
|
11
|
+
# `spec.hostnames`.
|
|
12
|
+
#
|
|
13
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
14
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
15
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
16
|
+
package abie.http_route_base
|
|
17
|
+
|
|
18
|
+
import rego.v1
|
|
19
|
+
|
|
20
|
+
allowed_apex := "aiml.live"
|
|
21
|
+
|
|
22
|
+
deny contains msg if {
|
|
23
|
+
input.kind == "HTTPRoute"
|
|
24
|
+
some host in object.get(object.get(input, "spec", {}), "hostnames", [])
|
|
25
|
+
is_string(host)
|
|
26
|
+
not host_matches_aiml_live(host)
|
|
27
|
+
msg := sprintf("HTTPRoute (base): %q має бути в домені aiml.live (abie.mdc)", [host])
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# Чи hostname належить до aiml.live (точна відповідність, піддомен `*.aiml.live`
|
|
31
|
+
# або довільний субдомен `*.aiml.live`).
|
|
32
|
+
host_matches_aiml_live(host) if {
|
|
33
|
+
host_lower := lower(host)
|
|
34
|
+
host_lower == allowed_apex
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
host_matches_aiml_live(host) if {
|
|
38
|
+
host_lower := lower(host)
|
|
39
|
+
endswith(host_lower, sprintf(".%s", [allowed_apex]))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
host_matches_aiml_live(host) if {
|
|
43
|
+
host_lower := lower(host)
|
|
44
|
+
host_lower == sprintf("*.%s", [allowed_apex])
|
|
45
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Порт перевірки `.claude/settings.json` з `npm/scripts/check-adr.mjs` (adr.mdc):
|
|
2
|
+
# `hooks.Stop[*]` має містити групу, де хоча б один елемент `hooks[]` має `command`
|
|
3
|
+
# зі substring `.claude/hooks/capture-decisions.sh`.
|
|
4
|
+
#
|
|
5
|
+
# Запуск (локально):
|
|
6
|
+
# conftest test .claude/settings.json -p npm/policy/adr \
|
|
7
|
+
# --namespace adr.settings_json
|
|
8
|
+
#
|
|
9
|
+
# Hash-порівняння bash-скрипта з канонічним bundled-варіантом і `.gitignore`-перевірки
|
|
10
|
+
# — у JS (`check-adr.mjs`).
|
|
11
|
+
#
|
|
12
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
13
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
14
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
15
|
+
package adr.settings_json
|
|
16
|
+
|
|
17
|
+
import rego.v1
|
|
18
|
+
|
|
19
|
+
hook_command_marker := ".claude/hooks/capture-decisions.sh"
|
|
20
|
+
|
|
21
|
+
deny contains msg if {
|
|
22
|
+
not has_adr_stop_hook
|
|
23
|
+
msg := ".claude/settings.json: відсутній Stop-hook для `capture-decisions.sh` у hooks.Stop (adr.mdc)"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# Чи є в `hooks.Stop[*].hooks[*].command` рядок з маркером скрипта.
|
|
27
|
+
has_adr_stop_hook if {
|
|
28
|
+
some group in object.get(object.get(input, "hooks", {}), "Stop", [])
|
|
29
|
+
some hook in object.get(group, "hooks", [])
|
|
30
|
+
contains(object.get(hook, "command", ""), hook_command_marker)
|
|
31
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Порт перевірки `.claude/settings.local.json` з `npm/scripts/check-adr.mjs`
|
|
2
|
+
# (adr.mdc): після переходу на project-shared `settings.json` цей файл (якщо є)
|
|
3
|
+
# НЕ повинен мати дубля Stop-хука з маркером `.claude/hooks/capture-decisions.sh`,
|
|
4
|
+
# інакше один і той самий скрипт виконається двічі на одну подію.
|
|
5
|
+
#
|
|
6
|
+
# Запуск (локально):
|
|
7
|
+
# conftest test .claude/settings.local.json -p npm/policy/adr \
|
|
8
|
+
# --namespace adr.settings_local_json
|
|
9
|
+
#
|
|
10
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
11
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
12
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
13
|
+
package adr.settings_local_json
|
|
14
|
+
|
|
15
|
+
import rego.v1
|
|
16
|
+
|
|
17
|
+
hook_command_marker := ".claude/hooks/capture-decisions.sh"
|
|
18
|
+
|
|
19
|
+
duplicate_template := concat(" ", [
|
|
20
|
+
".claude/settings.local.json: видали дубль Stop-хука для",
|
|
21
|
+
"`capture-decisions.sh` — він уже у project-shared settings.json (adr.mdc)",
|
|
22
|
+
])
|
|
23
|
+
|
|
24
|
+
deny contains duplicate_template if {
|
|
25
|
+
some group in object.get(object.get(input, "hooks", {}), "Stop", [])
|
|
26
|
+
some hook in object.get(group, "hooks", [])
|
|
27
|
+
contains(object.get(hook, "command", ""), hook_command_marker)
|
|
28
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Порт перевірки `checkBunfigHoisted` з `npm/scripts/check-bun.mjs` (bun.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально):
|
|
4
|
+
# conftest test bunfig.toml -p npm/policy/bun --namespace bun.bunfig
|
|
5
|
+
#
|
|
6
|
+
# Conftest парсить `.toml` нативно: секція `[install]` стає обʼєктом `input.install`.
|
|
7
|
+
# FS-перевірки (наявність самого `bunfig.toml`, `bun.lock`, заборонені lockfile-и
|
|
8
|
+
# `package-lock.json` тощо, директорія `.yarn/`) живуть у `check-bun.mjs` — Rego
|
|
9
|
+
# працює лише з вже завантаженим input.
|
|
10
|
+
#
|
|
11
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
12
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
13
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
14
|
+
package bun.bunfig
|
|
15
|
+
|
|
16
|
+
import rego.v1
|
|
17
|
+
|
|
18
|
+
deny contains msg if {
|
|
19
|
+
# `object.get(…, false)` дає визначене значення, коли поля немає, інакше
|
|
20
|
+
# `not is_object(input.install)` повернув би `undefined`, і правило мовчки
|
|
21
|
+
# не спрацювало б (той самий патерн, що й у `ga.workflow_common`).
|
|
22
|
+
not is_object(object.get(input, "install", false))
|
|
23
|
+
msg := "bunfig.toml: відсутня секція [install] (bun.mdc)"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
deny contains msg if {
|
|
27
|
+
is_object(object.get(input, "install", false))
|
|
28
|
+
|
|
29
|
+
# `object.get(…, null)` робить значення визначеним, інакше при відсутньому
|
|
30
|
+
# `linker` порівняння `!= "hoisted"` дало б `undefined`, не `true`.
|
|
31
|
+
object.get(input.install, "linker", null) != "hoisted"
|
|
32
|
+
msg := "bunfig.toml: у секції [install] має бути linker = \"hoisted\" (bun.mdc)"
|
|
33
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Порт структурних перевірок `package.json` з `npm/scripts/check-bun.mjs` (bun.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Запуск (локально, КОРЕНЕВИЙ package.json):
|
|
4
|
+
# conftest test package.json -p npm/policy/bun --namespace bun.package_json
|
|
5
|
+
#
|
|
6
|
+
# Перевіряє: відсутність `packageManager`, відсутність кореневих `dependencies`,
|
|
7
|
+
# у `devDependencies` лише `@nitra/*`, агрегований `lint`-скрипт (якщо є `lint-*`
|
|
8
|
+
# скрипти): покриває всі lint-* через `bun run`, закінчується на `&& oxfmt .`.
|
|
9
|
+
#
|
|
10
|
+
# Перевірки, які ЗАЛИШИЛИСЬ у JS (потребують FS / cross-file):
|
|
11
|
+
# - `lint-docker` / `lint-k8s` коли `.n-cursor.json:rules` містить відповідне
|
|
12
|
+
# правило (потрібен другий файл-вхід — у Rego без `--combine` не зробити).
|
|
13
|
+
#
|
|
14
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
15
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
16
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
17
|
+
package bun.package_json
|
|
18
|
+
|
|
19
|
+
import rego.v1
|
|
20
|
+
|
|
21
|
+
# ── Шаблони повідомлень ────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
# Через `concat` — дотримуємося regal style/line-length.
|
|
24
|
+
lint_aggregate_missing_template := concat(" ", [
|
|
25
|
+
"У package.json є скрипти %v, але немає агрегованого `lint`.",
|
|
26
|
+
"Додай скрипт, який запускає їх через `bun run` (bun.mdc)",
|
|
27
|
+
])
|
|
28
|
+
|
|
29
|
+
# ── deny: заборонені поля ──────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
deny contains msg if {
|
|
32
|
+
pm := object.get(input, "packageManager", "")
|
|
33
|
+
pm != ""
|
|
34
|
+
msg := sprintf("package.json містить поле packageManager: %q — видали його (bun.mdc)", [pm])
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# `dependencies` не повинно бути взагалі — навіть пусте `{}`. Сентинельний рядок
|
|
38
|
+
# дозволяє відрізнити «поле відсутнє» від «поле є з будь-яким значенням».
|
|
39
|
+
deny contains msg if {
|
|
40
|
+
object.get(input, "dependencies", "__bun_missing__") != "__bun_missing__"
|
|
41
|
+
msg := "Кореневий package.json не повинен містити поле dependencies — додай залежності в workspace-пакети (bun.mdc)"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# ── deny: devDependencies — лише `@nitra/*` ───────────────────────────────
|
|
45
|
+
|
|
46
|
+
deny contains msg if {
|
|
47
|
+
is_object(input.devDependencies)
|
|
48
|
+
some name, _ in input.devDependencies
|
|
49
|
+
not startswith(name, "@nitra/")
|
|
50
|
+
msg := sprintf("Кореневі devDependencies: дозволені лише @nitra/* — прибери або перенеси: %s (bun.mdc)", [name])
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# ── deny: агрегований lint-скрипт ─────────────────────────────────────────
|
|
54
|
+
#
|
|
55
|
+
# Якщо в `scripts` є хоч один `lint-*`, має бути скрипт `lint`, у якому
|
|
56
|
+
# через `bun run <ім'я>` викликається кожен такий скрипт; рядок завершується
|
|
57
|
+
# на `&& oxfmt .`.
|
|
58
|
+
|
|
59
|
+
deny contains msg if {
|
|
60
|
+
count(lint_prefixed_scripts) > 0
|
|
61
|
+
lint_script == ""
|
|
62
|
+
msg := sprintf(lint_aggregate_missing_template, [lint_prefixed_scripts])
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
deny contains msg if {
|
|
66
|
+
count(lint_prefixed_scripts) > 0
|
|
67
|
+
lint_script != ""
|
|
68
|
+
some script in lint_prefixed_scripts
|
|
69
|
+
not contains(lint_script, sprintf("bun run %s", [script]))
|
|
70
|
+
msg := sprintf("Скрипт `lint` має викликати `%s` через `bun run` (bun.mdc)", [script])
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
deny contains msg if {
|
|
74
|
+
count(lint_prefixed_scripts) > 0
|
|
75
|
+
lint_script != ""
|
|
76
|
+
|
|
77
|
+
# Перевіряємо, що рядок завершується `&& oxfmt .` (з можливими пробілами/табами).
|
|
78
|
+
# Trim не потрібен — пробіли/таби в кінці допускаємо в самому regex (`[ \t]*$`).
|
|
79
|
+
not regex.match(`&&[ \t]+oxfmt[ \t]+\.[ \t]*$`, lint_script)
|
|
80
|
+
msg := "Скрипт `lint` має закінчуватися на `&& oxfmt .` (bun.mdc)"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# ── helpers ────────────────────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
# Ключі скриптів, що починаються з `lint-` (наприклад `lint-js`, `lint-ga`).
|
|
86
|
+
lint_prefixed_scripts := [name |
|
|
87
|
+
some name, _ in object.get(input, "scripts", {})
|
|
88
|
+
startswith(name, "lint-")
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
# Значення `scripts.lint` як рядок (порожній, якщо поля немає або тип не string).
|
|
92
|
+
default lint_script := ""
|
|
93
|
+
|
|
94
|
+
lint_script := input.scripts.lint if is_string(input.scripts.lint)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Порт перевірки версії `@capacitor/core` з `npm/scripts/check-capacitor.mjs`
|
|
2
|
+
# (capacitor.mdc) — мінімальна мажорна версія = 8.
|
|
3
|
+
#
|
|
4
|
+
# Запуск (локально, у пакеті з Capacitor):
|
|
5
|
+
# conftest test path/to/package.json -p npm/policy/capacitor \
|
|
6
|
+
# --namespace capacitor.package_json
|
|
7
|
+
#
|
|
8
|
+
# Перевіряє: якщо в `dependencies['@capacitor/core']` присутній (gating: пакет
|
|
9
|
+
# реально використовує Capacitor), то перша мажорна цифра в діапазоні має бути ≥ 8.
|
|
10
|
+
# Підтримує `^8.0.0`, `>=8`, `8.x`, `workspace:*` тощо.
|
|
11
|
+
#
|
|
12
|
+
# Цей порт спрощує JS-логіку — повна семантика OR-діапазонів (`a || b`) і нижня
|
|
13
|
+
# межа діапазону лишається в JS (`check-capacitor.mjs`: `capacitorVersionRangeMinMajor`).
|
|
14
|
+
# JS-перевірка лишилась authoritative й бігає через `npx @nitra/cursor check capacitor`;
|
|
15
|
+
# ця Rego — швидкий gate для одиничного `package.json` (наприклад через IDE).
|
|
16
|
+
#
|
|
17
|
+
# FS-сканування пакетів через workspaces, iOS-специфічна логіка (Podfile), вибір
|
|
18
|
+
# каталогу пакета з Capacitor — у JS.
|
|
19
|
+
#
|
|
20
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
21
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
22
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
23
|
+
package capacitor.package_json
|
|
24
|
+
|
|
25
|
+
import rego.v1
|
|
26
|
+
|
|
27
|
+
deny contains msg if {
|
|
28
|
+
range := object.get(object.get(input, "dependencies", {}), "@capacitor/core", "")
|
|
29
|
+
range != ""
|
|
30
|
+
not capacitor_major_at_least_8(range)
|
|
31
|
+
msg := sprintf("@capacitor/core має бути >= 8 (зараз %q) (capacitor.mdc)", [range])
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# `workspace:*` / `*` / `x` / `latest` — пропускаємо (як у JS).
|
|
35
|
+
capacitor_major_at_least_8(range) if startswith(trim_space(range), "workspace:")
|
|
36
|
+
|
|
37
|
+
capacitor_major_at_least_8(range) if {
|
|
38
|
+
first_major(range) >= 8
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
first_major(range) := major if {
|
|
42
|
+
match := regex.find_n(`\d+`, range, 1)
|
|
43
|
+
count(match) > 0
|
|
44
|
+
major := to_number(match[0])
|
|
45
|
+
}
|
|
@@ -20,21 +20,12 @@ import rego.v1
|
|
|
20
20
|
# interpolation. Збираємо очікувані рядки з фрагментів через `concat`, як це
|
|
21
21
|
# зроблено в check-ga.mjs, щоб і Rego-парсер, і людина-читач не плуталися.
|
|
22
22
|
|
|
23
|
-
expected_concurrency_group := concat("", ["$", "{{ github.ref }}-$", "{{ github.workflow }}"])
|
|
24
|
-
|
|
25
23
|
expected_github_token := concat("", ["$", "{{ github.token }}"])
|
|
26
24
|
|
|
27
25
|
expected_name := "Clean action for removing completed workflow runs"
|
|
28
26
|
|
|
29
27
|
expected_cron := "0 1 16 * *"
|
|
30
28
|
|
|
31
|
-
# Шаблон повідомлення про відсутню `concurrency`-секцію — винесено через `concat`,
|
|
32
|
-
# щоб дотриматися regal style/line-length.
|
|
33
|
-
concurrency_missing_template := concat(" ", [
|
|
34
|
-
"clean-ga-workflows.yml: відсутня секція concurrency —",
|
|
35
|
-
"додай concurrency.group: %s і cancel-in-progress: true (ga.mdc)",
|
|
36
|
-
])
|
|
37
|
-
|
|
38
29
|
# ── Аліаси на input ────────────────────────────────────────────────────────
|
|
39
30
|
#
|
|
40
31
|
# GHA YAML quirk: ключ `on:` — YAML 1.1 boolean `true`, конфтест серіалізує його
|
|
@@ -62,23 +53,6 @@ deny contains msg if {
|
|
|
62
53
|
msg := "clean-ga-workflows.yml: має бути workflow_dispatch: {} (ga.mdc)"
|
|
63
54
|
}
|
|
64
55
|
|
|
65
|
-
deny contains msg if {
|
|
66
|
-
not is_object(input.concurrency)
|
|
67
|
-
msg := sprintf(concurrency_missing_template, [expected_concurrency_group])
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
deny contains msg if {
|
|
71
|
-
is_object(input.concurrency)
|
|
72
|
-
input.concurrency.group != expected_concurrency_group
|
|
73
|
-
msg := sprintf("clean-ga-workflows.yml: concurrency.group має бути %s (ga.mdc)", [expected_concurrency_group])
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
deny contains msg if {
|
|
77
|
-
is_object(input.concurrency)
|
|
78
|
-
input.concurrency["cancel-in-progress"] != true
|
|
79
|
-
msg := "clean-ga-workflows.yml: concurrency.cancel-in-progress має бути true (ga.mdc)"
|
|
80
|
-
}
|
|
81
|
-
|
|
82
56
|
deny contains msg if {
|
|
83
57
|
not input.jobs.cleanup_old_workflows
|
|
84
58
|
msg := "clean-ga-workflows.yml: jobs.cleanup_old_workflows відсутній (ga.mdc)"
|
|
@@ -16,8 +16,6 @@ import rego.v1
|
|
|
16
16
|
# Шаблонні токени GitHub Actions (`${{ … }}`) збираємо з фрагментів через
|
|
17
17
|
# `concat`, бо `{{` у Rego починає string interpolation.
|
|
18
18
|
|
|
19
|
-
expected_concurrency_group := concat("", ["$", "{{ github.ref }}-$", "{{ github.workflow }}"])
|
|
20
|
-
|
|
21
19
|
expected_github_token := concat("", ["$", "{{ github.token }}"])
|
|
22
20
|
|
|
23
21
|
expected_deleted_branches_expr := concat("", ["$", "{{ steps.delete_stuff.outputs.deleted_branches }}"])
|
|
@@ -28,12 +26,6 @@ expected_name := "Clean abandoned branches"
|
|
|
28
26
|
|
|
29
27
|
expected_cron := "0 1 15 * *"
|
|
30
28
|
|
|
31
|
-
# Шаблони повідомлень — через `concat` для regal style/line-length.
|
|
32
|
-
concurrency_missing_template := concat(" ", [
|
|
33
|
-
"clean-merged-branch.yml: відсутня секція concurrency —",
|
|
34
|
-
"додай concurrency.group: %s і cancel-in-progress: true (ga.mdc)",
|
|
35
|
-
])
|
|
36
|
-
|
|
37
29
|
# ── Аліаси на input ────────────────────────────────────────────────────────
|
|
38
30
|
#
|
|
39
31
|
# YAML 1.1 quirk: `on:` → boolean true → у конфтесті ключ "true".
|
|
@@ -63,23 +55,6 @@ deny contains msg if {
|
|
|
63
55
|
msg := "clean-merged-branch.yml: має бути workflow_dispatch: {} (ga.mdc)"
|
|
64
56
|
}
|
|
65
57
|
|
|
66
|
-
deny contains msg if {
|
|
67
|
-
not is_object(input.concurrency)
|
|
68
|
-
msg := sprintf(concurrency_missing_template, [expected_concurrency_group])
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
deny contains msg if {
|
|
72
|
-
is_object(input.concurrency)
|
|
73
|
-
input.concurrency.group != expected_concurrency_group
|
|
74
|
-
msg := sprintf("clean-merged-branch.yml: concurrency.group має бути %s (ga.mdc)", [expected_concurrency_group])
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
deny contains msg if {
|
|
78
|
-
is_object(input.concurrency)
|
|
79
|
-
input.concurrency["cancel-in-progress"] != true
|
|
80
|
-
msg := "clean-merged-branch.yml: concurrency.cancel-in-progress має бути true (ga.mdc)"
|
|
81
|
-
}
|
|
82
|
-
|
|
83
58
|
deny contains msg if {
|
|
84
59
|
not input.jobs.cleanup_old_branches
|
|
85
60
|
msg := "clean-merged-branch.yml: jobs.cleanup_old_branches відсутній (ga.mdc)"
|
|
@@ -13,8 +13,6 @@ import rego.v1
|
|
|
13
13
|
|
|
14
14
|
# ── Очікувані значення ─────────────────────────────────────────────────────
|
|
15
15
|
|
|
16
|
-
expected_concurrency_group := concat("", ["$", "{{ github.ref }}-$", "{{ github.workflow }}"])
|
|
17
|
-
|
|
18
16
|
expected_name := "Git AI"
|
|
19
17
|
|
|
20
18
|
expected_if_substring := "github.event.pull_request.merged == true"
|
|
@@ -23,13 +21,6 @@ expected_install_substring := "curl -fsSL https://usegitai.com/install.sh | bash
|
|
|
23
21
|
|
|
24
22
|
expected_run_substring := "git-ai ci github run"
|
|
25
23
|
|
|
26
|
-
# Шаблон повідомлення про відсутню `concurrency`-секцію — через `concat` для
|
|
27
|
-
# regal style/line-length.
|
|
28
|
-
concurrency_missing_template := concat(" ", [
|
|
29
|
-
"git-ai.yml: відсутня секція concurrency —",
|
|
30
|
-
"додай concurrency.group: %s і cancel-in-progress: true (ga.mdc)",
|
|
31
|
-
])
|
|
32
|
-
|
|
33
24
|
# ── Аліаси на input ────────────────────────────────────────────────────────
|
|
34
25
|
#
|
|
35
26
|
# YAML 1.1 quirk: `on:` → boolean true → у конфтесті ключ "true".
|
|
@@ -57,23 +48,6 @@ deny contains msg if {
|
|
|
57
48
|
msg := "git-ai.yml: on.pull_request.types має містити closed (ga.mdc)"
|
|
58
49
|
}
|
|
59
50
|
|
|
60
|
-
deny contains msg if {
|
|
61
|
-
not is_object(input.concurrency)
|
|
62
|
-
msg := sprintf(concurrency_missing_template, [expected_concurrency_group])
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
deny contains msg if {
|
|
66
|
-
is_object(input.concurrency)
|
|
67
|
-
input.concurrency.group != expected_concurrency_group
|
|
68
|
-
msg := sprintf("git-ai.yml: concurrency.group має бути %s (ga.mdc)", [expected_concurrency_group])
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
deny contains msg if {
|
|
72
|
-
is_object(input.concurrency)
|
|
73
|
-
input.concurrency["cancel-in-progress"] != true
|
|
74
|
-
msg := "git-ai.yml: concurrency.cancel-in-progress має бути true (ga.mdc)"
|
|
75
|
-
}
|
|
76
|
-
|
|
77
51
|
deny contains msg if {
|
|
78
52
|
not job
|
|
79
53
|
msg := "git-ai.yml: jobs.git-ai відсутній (ga.mdc)"
|
|
@@ -14,21 +14,12 @@ import rego.v1
|
|
|
14
14
|
|
|
15
15
|
# ── Очікувані значення ─────────────────────────────────────────────────────
|
|
16
16
|
|
|
17
|
-
expected_concurrency_group := concat("", ["$", "{{ github.ref }}-$", "{{ github.workflow }}"])
|
|
18
|
-
|
|
19
17
|
expected_name := "Lint GA"
|
|
20
18
|
|
|
21
19
|
expected_branches := {"dev", "main"}
|
|
22
20
|
|
|
23
21
|
expected_push_paths := {".github/actions/**", ".github/workflows/**"}
|
|
24
22
|
|
|
25
|
-
# Шаблон повідомлення про відсутню `concurrency`-секцію — через `concat` для
|
|
26
|
-
# regal style/line-length.
|
|
27
|
-
concurrency_missing_template := concat(" ", [
|
|
28
|
-
"lint-ga.yml: відсутня секція concurrency —",
|
|
29
|
-
"додай concurrency.group: %s і cancel-in-progress: true (ga.mdc)",
|
|
30
|
-
])
|
|
31
|
-
|
|
32
23
|
# ── Аліаси на input ────────────────────────────────────────────────────────
|
|
33
24
|
#
|
|
34
25
|
# YAML 1.1 quirk: `on:` → boolean true → у конфтесті ключ "true".
|
|
@@ -69,23 +60,6 @@ deny contains msg if {
|
|
|
69
60
|
msg := "lint-ga.yml: on.push.paths має містити .github/actions/** і .github/workflows/** (ga.mdc)"
|
|
70
61
|
}
|
|
71
62
|
|
|
72
|
-
deny contains msg if {
|
|
73
|
-
not is_object(input.concurrency)
|
|
74
|
-
msg := sprintf(concurrency_missing_template, [expected_concurrency_group])
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
deny contains msg if {
|
|
78
|
-
is_object(input.concurrency)
|
|
79
|
-
input.concurrency.group != expected_concurrency_group
|
|
80
|
-
msg := sprintf("lint-ga.yml: concurrency.group має бути %s (ga.mdc)", [expected_concurrency_group])
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
deny contains msg if {
|
|
84
|
-
is_object(input.concurrency)
|
|
85
|
-
input.concurrency["cancel-in-progress"] != true
|
|
86
|
-
msg := "lint-ga.yml: concurrency.cancel-in-progress має бути true (ga.mdc)"
|
|
87
|
-
}
|
|
88
|
-
|
|
89
63
|
deny contains msg if {
|
|
90
64
|
not job
|
|
91
65
|
msg := "lint-ga.yml: jobs.lint-ga відсутній (ga.mdc)"
|