@nitra/cursor 1.8.206 → 1.8.208
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 +36 -0
- package/mdc/js-run.mdc +49 -2
- 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 +84 -86
- 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/scripts/utils/conn-file-rules.mjs +170 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,42 @@
|
|
|
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.208] - 2026-05-08
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `mdc/js-run.mdc` (1.6, з 1.5): новий розділ «Нейминг файлів у `src/conn/`» — префікси `ql-` (GraphQL endpoint), `pg-`/`mysql-` з обовʼязковим `read`/`write` режимом і опційним ідентифікатором підключення для multi-БД (`pg-read-smart.js`, `pg-write-contract.js`); якщо режим не очевидний з імені env — визначати за наявністю операцій зміни даних. Також правило про експорти в `src/conn/`: заборонено `export default`, лише іменований експорт у camelCase від назви файла (`ql-smart.js` → `export const qlSmart`, `pg-write-contract.js` → `export const pgWriteContract`).
|
|
12
|
+
- `scripts/utils/conn-file-rules.mjs` + інтеграція в `scripts/check-js-run.mjs` — для кожного файла всередині `#conn/` каталогу пакета перевіряє: (а) basename відповідає канону `ql-<id>` / `(pg|mysql)-(read|write)[-<id>]` (kebab-case `[a-z0-9-]`); (б) відсутній `export default`; (в) серед іменованих експортів є рівно `<camelCase(basename)>` (`pg-write-contract.js` → `pgWriteContract`). `index.*` пропускається як reexport-барель. Розпізнає `export const/let/var`, `export function`, `export class` і `export { x as Y }` через AST на oxc-parser.
|
|
13
|
+
|
|
14
|
+
## [1.8.207] - 2026-05-08
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- `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`.
|
|
19
|
+
- `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 .`.
|
|
20
|
+
- `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` у залежностях.
|
|
21
|
+
- `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`.
|
|
22
|
+
- `npm/policy/php/{package_json,lint_php_yml}/*.rego` — порт `check-php.mjs`: скрипт `lint-php` у `package.json`; у `lint-php.yml` хоча б один `run` з `bun run lint-php`.
|
|
23
|
+
- `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`.
|
|
24
|
+
- `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.
|
|
25
|
+
- `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.
|
|
26
|
+
- `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:*`).
|
|
27
|
+
- `npm/policy/js_bun_db/package_json/package_json.rego` — порт `check-js-bun-db.mjs`: у `dependencies` заборонені `pg`, `pg-format`, `mysql2`.
|
|
28
|
+
- `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=`.
|
|
29
|
+
- `npm/policy/vue/package_json/package_json.rego` — порт `check-vue.mjs`: якщо `dependencies.vue` присутній, у `devDependencies` має бути `vite` мажорної версії ≥ 8.
|
|
30
|
+
- `npm/policy/graphql/package_json/package_json.rego` — порт `check-graphql.mjs`: `scripts.dump-schema` точно відповідає канонічному.
|
|
31
|
+
- `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`.
|
|
32
|
+
- `npm/policy/hasura/svc_hl/svc_hl.rego` — порт `check-hasura.mjs` (мінімум): у `hasura/k8s/base/svc-hl.yaml` Service з `metadata.name` має закінчуватись на `-h`.
|
|
33
|
+
- `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` (якщо існує) — НЕ повинен мати дубля цього хука.
|
|
34
|
+
- `npm/policy/capacitor/package_json/package_json.rego` — порт `check-capacitor.mjs`: `dependencies['@capacitor/core']` мажорна ≥ 8 (підтримує `workspace:*`).
|
|
35
|
+
- `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`.
|
|
36
|
+
- `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`.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- `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.
|
|
41
|
+
- `npm/scripts/lint-ga.mjs`: до існуючих per-workflow conftest-таргетів додано фінальний прогін `ga.workflow_common` одним викликом `conftest test <усі .yml> --namespace ga.workflow_common`. Імпорт `readdirSync` з `node:fs` для перерахунку workflow-файлів.
|
|
42
|
+
|
|
7
43
|
## [1.8.206] - 2026-05-08
|
|
8
44
|
|
|
9
45
|
### Added
|
package/mdc/js-run.mdc
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Це правила для backend проектів на JavaScript/Node.js, сюди входять і job і WEB сервери.
|
|
3
3
|
alwaysApply: true
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.6'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Область застосування
|
|
@@ -121,6 +121,47 @@ import { pool } from '#conn/pg.js'
|
|
|
121
121
|
import { gql, graphQLClient } from '@nitra/graphql-request'
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
+
### Нейминг файлів у `src/conn/`
|
|
125
|
+
|
|
126
|
+
Назва файла в `src/conn/` має одразу повідомляти, **до чого** підключаємось і **в якому режимі**:
|
|
127
|
+
|
|
128
|
+
- **GraphQL** — префікс `ql-`, далі ідентифікатор endpoint:
|
|
129
|
+
- `src/conn/ql-contract.js`
|
|
130
|
+
- `src/conn/ql-smart.js`
|
|
131
|
+
- **PostgreSQL** — префікс `pg-`, далі тип підключення (репліка vs мастер): `read` або `write`:
|
|
132
|
+
- `src/conn/pg-read.js`
|
|
133
|
+
- `src/conn/pg-write.js`
|
|
134
|
+
- **PostgreSQL до кількох БД** — додатково ідентифікатор підключення після типу:
|
|
135
|
+
- `src/conn/pg-read-smart.js`
|
|
136
|
+
- `src/conn/pg-write-contract.js`
|
|
137
|
+
- **MySQL / MSSQL** — префікс `mysql-` за тією ж схемою (`mysql-read.js`, `mysql-write-<id>.js` тощо).
|
|
138
|
+
|
|
139
|
+
Підключення до БД **обов'язково** має бути ідентифіковано як `read` (репліка) або `write` (мастер). Якщо з імені змінної оточення (наприклад, `env.PG_CONN`) це не очевидно — визнач режим за операціями в коді: якщо немає операцій зміни даних (`INSERT`/`UPDATE`/`DELETE`/DDL) — це `pg-read.js`, інакше `pg-write.js`.
|
|
140
|
+
|
|
141
|
+
### Експорти у файлах `src/conn/`
|
|
142
|
+
|
|
143
|
+
У файлах підключень **заборонений** `export default`. Експорт має бути **іменований** і збігатися з назвою файла в camelCase.
|
|
144
|
+
|
|
145
|
+
Приклад — `src/conn/ql-smart.js`:
|
|
146
|
+
|
|
147
|
+
```javascript title="❌ Так не можна"
|
|
148
|
+
export default new GraphQLClient(env.SMART_QL, {
|
|
149
|
+
headers: {
|
|
150
|
+
'X-Hasura-Admin-Secret': env.SMART_X_HASURA_ADMIN_SECRET
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
```javascript title="✅ Канон: іменований експорт за іменем файла"
|
|
156
|
+
export const qlSmart = new GraphQLClient(env.SMART_QL, {
|
|
157
|
+
headers: {
|
|
158
|
+
'X-Hasura-Admin-Secret': env.SMART_X_HASURA_ADMIN_SECRET
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Відповідно: `pg-read.js` → `export const pgRead = …`, `pg-write-contract.js` → `export const pgWriteContract = …`, `ql-contract.js` → `export const qlContract = …`.
|
|
164
|
+
|
|
124
165
|
## CheckEnv
|
|
125
166
|
|
|
126
167
|
Усі змінні оточення, які використовуються в коді, повинні бути перевірені за допомогою `checkEnv` з пакету `@nitra/check-env`. Це гарантує, що всі необхідні змінні оточення встановлені перед запуском програми.
|
|
@@ -189,4 +230,10 @@ on:
|
|
|
189
230
|
|
|
190
231
|
## Перевірка
|
|
191
232
|
|
|
192
|
-
`npx @nitra/cursor check js-run` — зокрема для кожного backend workspace-пакета з каталогом **`src/`** перевіряє наявність **`jsconfig.json`** і збіг вмісту з каноном вище.
|
|
233
|
+
`npx @nitra/cursor check js-run` — зокрема для кожного backend workspace-пакета з каталогом **`src/`** перевіряє наявність **`jsconfig.json`** і збіг вмісту з каноном вище. Додатково для файлів у каталозі `#conn/` (за замовчуванням `src/conn/`) перевіряється:
|
|
234
|
+
|
|
235
|
+
- **basename файла** відповідає канону: `ql-<id>` (GraphQL) / `(pg|mysql)-(read|write)[-<id>]` (БД), kebab-case `[a-z0-9-]`;
|
|
236
|
+
- **відсутній `export default`** — лише іменований експорт;
|
|
237
|
+
- **імʼя експорту** дорівнює camelCase від basename (`pg-write-contract.js` → `export const pgWriteContract`).
|
|
238
|
+
|
|
239
|
+
Файли `index.*` у conn-каталозі пропускаються як можливий reexport-барель.
|
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)"
|