@nitra/cursor 1.11.3 → 1.11.6
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 +32 -3
- package/bin/n-cursor.js +40 -6
- package/package.json +2 -1
- package/rules/abie/js/applies/check.mjs +4 -4
- package/rules/abie/js/env_dns/check.mjs +1 -1
- package/rules/abie/js/firebase_hosting/check.mjs +1 -1
- package/rules/abie/js/hc_pairing/check.mjs +3 -3
- package/rules/abie/js/ua_http_route/check.mjs +3 -3
- package/rules/abie/js/ua_node_selector/check.mjs +4 -2
- package/rules/abie/policy/base_deployment_preem/target.json +1 -5
- package/rules/abie/utils/enabled.mjs +1 -1
- package/rules/abie/utils/env-dns.mjs +4 -4
- package/rules/abie/utils/http-route.mjs +5 -5
- package/rules/abie/utils/k8s-tree.mjs +23 -15
- package/rules/abie/utils/kustomization-patches.mjs +20 -20
- package/rules/abie/utils/overlay-paths.mjs +8 -8
- package/rules/abie/utils/yaml.mjs +4 -4
- package/rules/adr/adr.mdc +2 -2
- package/rules/adr/js/hooks/check.mjs +5 -5
- package/rules/docker/docker.mdc +2 -2
- package/rules/docker/js/run.mjs +3 -2
- package/rules/docker/policy/package_json/package_json.rego +1 -1
- package/rules/ga/js/lint.mjs +3 -26
- package/rules/hasura/js/internal_urls/check.mjs +1 -1
- package/rules/js-bun-redis/js/imports/check.mjs +5 -1
- package/rules/js-run/js/runtime/check.mjs +4 -1
- package/rules/k8s/js/run.mjs +3 -2
- package/rules/k8s/k8s.mdc +2 -4
- package/rules/k8s/policy/base_manifest/target.json +1 -5
- package/rules/nginx-default-tpl/js/template/check.mjs +4 -2
- package/rules/npm-module/js/package_structure/check.mjs +8 -3
- package/rules/npm-module/npm-module.mdc +3 -3
- package/rules/rego/js/applies/check.mjs +2 -2
- package/rules/rego/js/lint.mjs +4 -1
- package/rules/rego/policy/package_json/package_json.rego +5 -3
- package/rules/rego/rego.mdc +3 -3
- package/rules/style-lint/js/tooling/check.mjs +1 -1
- package/rules/style-lint/style-lint.mdc +1 -1
- package/rules/tauri/js/tooling/check.mjs +3 -1
- package/rules/text/js/formatting/check.mjs +8 -24
- package/rules/text/js/lint.mjs +34 -0
- package/rules/text/js/run-shellcheck.mjs +2 -2
- package/rules/text/js/run-v8r.mjs +2 -2
- package/rules/text/text.mdc +5 -5
- package/schemas/v8r-catalog.json +6 -0
- package/scripts/auto-skills.mjs +3 -7
- package/scripts/utils/discover-checkable-rules.mjs +4 -3
- package/scripts/utils/resolve-target-files.mjs +1 -1
- package/scripts/utils/run-lint-step.mjs +33 -0
- package/scripts/utils/run-rule.mjs +5 -3
- package/skills/abie-clean/SKILL.md +13 -11
- package/skills/adr-normalize/SKILL.md +0 -1
- package/skills/fix/SKILL.md +3 -7
- package/rules/abie/policy/base_deployment_preem/base_deployment_preem_test.rego +0 -60
- package/rules/abie/policy/clean_merged_ignore_branches/clean_merged_ignore_branches_test.rego +0 -48
- package/rules/abie/policy/health_check_policy/health_check_policy_test.rego +0 -99
- package/rules/abie/policy/http_route_base/http_route_base_test.rego +0 -64
- package/rules/bun/policy/package_json/package_json_test.rego +0 -109
- package/rules/docker/policy/lint_docker_yml/lint_docker_yml_test.rego +0 -104
- package/rules/docker/policy/package_json/package_json_test.rego +0 -42
- package/rules/graphql/policy/vscode_extensions/vscode_extensions_test.rego +0 -34
- package/rules/image-avif/policy/package_json/package_json_test.rego +0 -69
- package/rules/js-lint/policy/package_json/package_json_test.rego +0 -130
- package/rules/js-run/policy/jsconfig/jsconfig_test.rego +0 -88
- package/rules/k8s/policy/base_kustomization/base_kustomization_test.rego +0 -73
- package/rules/k8s/policy/base_manifest/base_manifest_test.rego +0 -94
- package/rules/k8s/policy/gateway/gateway_test.rego +0 -122
- package/rules/k8s/policy/hasura_configmap/hasura_configmap_test.rego +0 -49
- package/rules/k8s/policy/hasura_httproute/hasura_httproute_test.rego +0 -148
- package/rules/k8s/policy/hpa_pdb/hpa_pdb_test.rego +0 -101
- package/rules/k8s/policy/kustomization/kustomization_test.rego +0 -128
- package/rules/k8s/policy/manifest/manifest_test.rego +0 -309
- package/rules/k8s/policy/svc_hl_yaml/svc_hl_yaml_test.rego +0 -42
- package/rules/k8s/policy/svc_yaml/svc_yaml_test.rego +0 -41
- package/rules/nginx-default-tpl/policy/vscode_extensions/vscode_extensions_test.rego +0 -30
- package/rules/nginx-default-tpl/policy/vscode_settings/vscode_settings_test.rego +0 -53
- package/rules/npm-module/policy/npm_package_json/npm_package_json_test.rego +0 -81
- package/rules/rego/policy/package_json/package_json_test.rego +0 -42
- package/rules/rego/policy/vscode_extensions/vscode_extensions_test.rego +0 -34
- package/rules/rego/policy/vscode_settings/vscode_settings_test.rego +0 -55
- package/rules/style-lint/policy/vscode_extensions/vscode_extensions_test.rego +0 -39
- package/rules/style-lint/policy/vscode_settings/vscode_settings_test.rego +0 -49
- package/rules/tauri/policy/vscode_extensions/vscode_extensions_test.rego +0 -44
- package/rules/text/policy/markdownlint/markdownlint_test.rego +0 -98
- package/rules/text/policy/vscode_extensions/vscode_extensions_test.rego +0 -51
- package/rules/text/policy/vscode_settings/vscode_settings_test.rego +0 -85
package/rules/text/text.mdc
CHANGED
|
@@ -113,14 +113,14 @@ version: '1.26'
|
|
|
113
113
|
|
|
114
114
|
**`package.json`:** скрипт **`lint-text`** і devDependencies **`@nitra/cspell-dict`** (**`^2.0.0`** або новіший у лінії 2.x) — з **2.0.0** у пакет транзитивно входять типові **`@cspell/dict-*`**, тому **не** додавай їх окремо в корінь. **`markdownlint-cli2`** викликай у `lint-text` лише через **`bunx markdownlint-cli2`**, не додавай пакет до devDependencies. **`v8r`** лише через **`bunx v8r`** (зазвичай **`bunx v8r`**), не в devDependencies. Окремий пакет **`markdownlint`** не потрібний.
|
|
115
115
|
|
|
116
|
-
**shellcheck:** інструмент лише в **`PATH`** (як у **ga.mdc** для `lint-ga`), **не** додавай до `dependencies` / `devDependencies`. Якщо `shellcheck` відсутній — встанови локально (**macOS:** `brew install shellcheck`; **Debian/Ubuntu:** `sudo apt-get install -y shellcheck`; **Arch:** `sudo pacman -S shellcheck`).
|
|
116
|
+
**shellcheck:** інструмент лише в **`PATH`** (як у **ga.mdc** для `lint-ga`), **не** додавай до `dependencies` / `devDependencies`. Якщо `shellcheck` відсутній — встанови локально (**macOS:** `brew install shellcheck`; **Debian/Ubuntu:** `sudo apt-get install -y shellcheck`; **Arch:** `sudo pacman -S shellcheck`). Канонічний **`lint-text`** делегує до CLI **`n-cursor lint-text`** (бінарка з `node_modules/.bin/` пакету `@nitra/cursor`): після **`cspell .`** виконується **`runShellcheckText()`** з пакета — циклічно **`shellcheck -f diff`** і **`patch -p1`** для авто-виправлень, потім повний прогін **`shellcheck`** по всіх tracked `*.sh` (у git) або по `**/*.sh` без `node_modules`. Потрібен також **`patch`** у `PATH` (на macOS зазвичай уже є).
|
|
117
117
|
|
|
118
|
-
У v8r **немає** прапорця тихого режиму;
|
|
118
|
+
У v8r **немає** прапорця тихого режиму; CLI-обгортка **`n-cursor lint-text`** на четвертому кроці викликає **`runV8rWithGlobs()`** з пакета (реалізація — `npm/rules/text/js/run-v8r.mjs`): під капотом послідовні **`bunx v8r`** для кожного типу (**json**, **json5**, **yml**, **yaml**, **toml**), бо один процес v8r з кількома глобами падає з **98**, якщо хоч один glob порожній, і тоді інші розширення не перевіряються. Вивід при кодах **0** і **98** не показується. Каталог схем **`schemas/v8r-catalog.json`** пакета `@nitra/cursor` обгортка підставляє в v8r сама.
|
|
119
119
|
|
|
120
120
|
```json title="package.json"
|
|
121
121
|
{
|
|
122
122
|
"scripts": {
|
|
123
|
-
"lint-text": "
|
|
123
|
+
"lint-text": "n-cursor lint-text"
|
|
124
124
|
},
|
|
125
125
|
"devDependencies": {
|
|
126
126
|
"@nitra/cspell-dict": "^2.1.0"
|
|
@@ -239,7 +239,7 @@ jobs:
|
|
|
239
239
|
```json title="package.json"
|
|
240
240
|
{
|
|
241
241
|
"scripts": {
|
|
242
|
-
"lint-text": "
|
|
242
|
+
"lint-text": "n-cursor lint-text"
|
|
243
243
|
},
|
|
244
244
|
"devDependencies": {
|
|
245
245
|
"@nitra/cspell-dict": "^2.1.0"
|
|
@@ -256,7 +256,7 @@ jobs:
|
|
|
256
256
|
```json title="package.json"
|
|
257
257
|
{
|
|
258
258
|
"scripts": {
|
|
259
|
-
"lint-text": "
|
|
259
|
+
"lint-text": "n-cursor lint-text"
|
|
260
260
|
},
|
|
261
261
|
"devDependencies": {
|
|
262
262
|
"@nitra/cspell-dict": "^2.1.0"
|
package/schemas/v8r-catalog.json
CHANGED
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
"url": "https://unpkg.com/@nitra/cursor/schemas/n-cursor.json",
|
|
9
9
|
"fileMatch": [".n-cursor.json", "**/.n-cursor.json", ".n-cursor.example.json", "**/.n-cursor.example.json"]
|
|
10
10
|
},
|
|
11
|
+
{
|
|
12
|
+
"name": "rego-target.json",
|
|
13
|
+
"description": "Маніфест rego-полісі (npm/rules/<id>/policy/<name>/target.json) — які файли проєкту фідити в conftest",
|
|
14
|
+
"url": "https://unpkg.com/@nitra/cursor/schemas/target.json",
|
|
15
|
+
"fileMatch": ["npm/rules/*/policy/*/target.json", "rules/*/policy/*/target.json"]
|
|
16
|
+
},
|
|
11
17
|
{
|
|
12
18
|
"name": "schema-catalog",
|
|
13
19
|
"description": "Каталог схем Schema Store (v8r-catalog.json у пакеті)",
|
package/scripts/auto-skills.mjs
CHANGED
|
@@ -52,7 +52,7 @@ function parseSkillAutoSpec(text) {
|
|
|
52
52
|
* Сканує `npm/skills/<id>/auto.md`. Скіли без `auto.md` або з нерозпізнаним
|
|
53
53
|
* вмістом не потрапляють у результат — їх можна вмикати лише вручну в конфізі.
|
|
54
54
|
* @param {string} [skillsDir] override для тестів
|
|
55
|
-
* @returns {Record<string, SkillAutoSpec>}
|
|
55
|
+
* @returns {Record<string, SkillAutoSpec>} мапа `skillId → spec`
|
|
56
56
|
*/
|
|
57
57
|
export function discoverSkillAutoActivation(skillsDir = SKILLS_DIR) {
|
|
58
58
|
if (!existsSync(skillsDir)) return {}
|
|
@@ -75,9 +75,7 @@ const SKILL_AUTO_ACTIVATION = discoverSkillAutoActivation()
|
|
|
75
75
|
* Стабільний алфавітний порядок скілів з автоактивацією. Експортовано для зворотної
|
|
76
76
|
* сумісності (попередня версія мала жорстко прописаний `AUTO_SKILL_ORDER`).
|
|
77
77
|
*/
|
|
78
|
-
export const AUTO_SKILL_ORDER = Object.freeze(
|
|
79
|
-
Object.keys(SKILL_AUTO_ACTIVATION).toSorted((a, b) => a.localeCompare(b))
|
|
80
|
-
)
|
|
78
|
+
export const AUTO_SKILL_ORDER = Object.freeze(Object.keys(SKILL_AUTO_ACTIVATION).toSorted((a, b) => a.localeCompare(b)))
|
|
81
79
|
|
|
82
80
|
/**
|
|
83
81
|
* Похідна view на `SKILL_AUTO_ACTIVATION`: лише скіли з rule-залежностями.
|
|
@@ -111,9 +109,7 @@ export function detectAutoSkills({ availableSkills, detectedRules, disableSkills
|
|
|
111
109
|
|
|
112
110
|
for (const [skillId, spec] of Object.entries(SKILL_AUTO_ACTIVATION)) {
|
|
113
111
|
if (!normalizedSkills.has(skillId) || disableSkillsSet.has(skillId)) continue
|
|
114
|
-
if ('always' in spec) {
|
|
115
|
-
detected.add(skillId)
|
|
116
|
-
} else if (spec.rules.every(d => detectedRulesSet.has(d))) {
|
|
112
|
+
if ('always' in spec || spec.rules.every(d => detectedRulesSet.has(d))) {
|
|
117
113
|
detected.add(skillId)
|
|
118
114
|
}
|
|
119
115
|
}
|
|
@@ -32,8 +32,8 @@ const TEST_SUFFIX = '.test.mjs'
|
|
|
32
32
|
/**
|
|
33
33
|
* @typedef {object} CheckableRule
|
|
34
34
|
* @property {string} id ідентифікатор правила (імʼя каталогу `rules/<id>/`)
|
|
35
|
-
* @property {JsConcern[]} jsConcerns
|
|
36
|
-
* @property {PolicyConcern[]} policyConcerns
|
|
35
|
+
* @property {JsConcern[]} jsConcerns JS-концерни правила (алфавітно)
|
|
36
|
+
* @property {PolicyConcern[]} policyConcerns policy-концерни правила (алфавітно)
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
39
|
/**
|
|
@@ -52,7 +52,8 @@ async function listJsConcerns(jsDir) {
|
|
|
52
52
|
for (const entry of topLevel) {
|
|
53
53
|
if (!entry.isDirectory() || entry.name === 'utils' || entry.name.startsWith('.')) continue
|
|
54
54
|
const concernDir = join(jsDir, entry.name)
|
|
55
|
-
const
|
|
55
|
+
const dirContents = await readdir(concernDir)
|
|
56
|
+
const files = dirContents
|
|
56
57
|
.filter(n => CHECK_FILENAME_RE.test(n) && !n.endsWith(TEST_SUFFIX))
|
|
57
58
|
.toSorted((a, b) => a.localeCompare(b))
|
|
58
59
|
if (files.length > 0) {
|
|
@@ -61,7 +61,7 @@ async function walkAllRelative(root, ignorePaths) {
|
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Витягує (або обчислює і кешує) список усіх файлів у дереві для заданого набору ignore-шляхів.
|
|
64
|
-
* Кеш —
|
|
64
|
+
* Кеш — мапа `signature → Promise<string[]>`, тож паралельні виклики одного й того ж набору
|
|
65
65
|
* чекають один обхід.
|
|
66
66
|
* @param {string} root абсолютний корінь репозиторію
|
|
67
67
|
* @param {string[]} ignorePaths абсолютні posix-шляхи виключених каталогів
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Спільний хелпер для CLI-обгорток `lint-<rule>`: запускає один крок ланцюжка з логуванням
|
|
3
|
+
* команди і прокидає stdout/stderr на користувацькі stream-и (`stdio: 'inherit'`), щоб виглядало
|
|
4
|
+
* як прямий виклик у shell.
|
|
5
|
+
*
|
|
6
|
+
* Використовується з `n-cursor lint-ga`, `n-cursor lint-text` та інших підкоманд, щоб не дублювати
|
|
7
|
+
* одну й ту саму обгортку у кожному `rules/<id>/js/lint.mjs` (jscpd-clone).
|
|
8
|
+
*/
|
|
9
|
+
import { spawnSync } from 'node:child_process'
|
|
10
|
+
|
|
11
|
+
import { resolveCmd } from './resolve-cmd.mjs'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Запускає один крок lint-обгортки: резолвить `cmd` у PATH і `spawnSync` із успадкованим stdio.
|
|
15
|
+
* @param {string} title заголовок для логу (наприклад `actionlint`)
|
|
16
|
+
* @param {string} cmd ім'я команди (`bunx`, `uvx`, `npx`, …)
|
|
17
|
+
* @param {string[]} args аргументи команди
|
|
18
|
+
* @returns {number} код виходу дочірнього процесу: 0 — OK, 127 — команда відсутня в PATH, інше — помилка
|
|
19
|
+
*/
|
|
20
|
+
export function runLintStep(title, cmd, args) {
|
|
21
|
+
console.log(`\n▶ ${title}: ${cmd} ${args.join(' ')}`)
|
|
22
|
+
const resolved = resolveCmd(cmd)
|
|
23
|
+
if (!resolved) {
|
|
24
|
+
console.error(`❌ ${cmd} не знайдено в PATH (${title}).`)
|
|
25
|
+
return 127
|
|
26
|
+
}
|
|
27
|
+
const r = spawnSync(resolved, args, { stdio: 'inherit', env: process.env })
|
|
28
|
+
if (r.error) {
|
|
29
|
+
console.error(`❌ Не вдалося запустити ${cmd}: ${r.error.message}`)
|
|
30
|
+
return 1
|
|
31
|
+
}
|
|
32
|
+
return r.status ?? 1
|
|
33
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 2. **JS-концерни** — кожен `check*.mjs` у `js/<concern>/`. Concern `applies` теж може мати
|
|
8
8
|
* `check()` для друку контексту (його `applies()` уже відпрацював на кроці 1, він не повторюється).
|
|
9
9
|
* 3. **Policy-концерни** — кожен `policy/<concern>/target.json` через `runConftestBatch`.
|
|
10
|
-
*
|
|
10
|
+
* Резолвер `resolveTargetFiles` ділить cache (`walkCache`) між концернами.
|
|
11
11
|
*
|
|
12
12
|
* Кожен concern має власний `createCheckReporter` — їхні exit-коди OR-яться в один на рівні правила.
|
|
13
13
|
* Це дає той самий 0/1 контракт, що й попередня модель «один check.mjs на правило».
|
|
@@ -45,6 +45,7 @@ async function evaluateAppliesGate(bundledRulesDir, rule) {
|
|
|
45
45
|
const concern = rule.jsConcerns.find(c => c.name === APPLIES_CONCERN_NAME)
|
|
46
46
|
if (!concern || concern.files.length === 0) return true
|
|
47
47
|
const path = resolveJsCheckPath(bundledRulesDir, rule.id, concern, concern.files[0])
|
|
48
|
+
// eslint-disable-next-line no-unsanitized/method -- path будується з discovered concern/file, які пройшли regex CHECK_FILENAME_RE
|
|
48
49
|
const mod = await import(path)
|
|
49
50
|
if (typeof mod.applies !== 'function') return true
|
|
50
51
|
return Boolean(await mod.applies())
|
|
@@ -68,7 +69,8 @@ async function runPolicyConcern(bundledRulesDir, ruleId, concernName, walkCache)
|
|
|
68
69
|
if (files.length === 0) {
|
|
69
70
|
if (target.files.required && target.files.single) {
|
|
70
71
|
const msg =
|
|
71
|
-
target.missingMessage ??
|
|
72
|
+
target.missingMessage ??
|
|
73
|
+
`${target.files.single} не існує — створи згідно ${ruleId}.mdc (${ruleId}.${concernName})`
|
|
72
74
|
reporter.fail(msg)
|
|
73
75
|
}
|
|
74
76
|
return reporter.getExitCode()
|
|
@@ -91,7 +93,7 @@ async function runPolicyConcern(bundledRulesDir, ruleId, concernName, walkCache)
|
|
|
91
93
|
|
|
92
94
|
/**
|
|
93
95
|
* Запускає одне правило: applies-гейт → JS-концерни → policy-концерни.
|
|
94
|
-
* @param {import('./discover-checkable-rules.mjs').CheckableRule} rule
|
|
96
|
+
* @param {import('./discover-checkable-rules.mjs').CheckableRule} rule опис правила з discovery
|
|
95
97
|
* @param {string} bundledRulesDir абсолютний шлях до `rules/`
|
|
96
98
|
* @param {Map<string, Promise<string[]>>} walkCache shared cache (один на check-прогон)
|
|
97
99
|
* @returns {Promise<number>} 0 — OK, 1 — є порушення в одному чи більше концернів
|
|
@@ -131,15 +131,15 @@ RUN bun install && bun vite build --mode "prod-$BRANCH" --base="$BASE"
|
|
|
131
131
|
|
|
132
132
|
Те саме стосується `nginx`-конфігів (`server_name`, `proxy_pass` з ru-доменами), `*.sh`-скриптів та `package.json` scripts (`build:ru`, `deploy:ru`, `prod-ru` тощо).
|
|
133
133
|
|
|
134
|
-
|
|
135
134
|
## 5. Переклади
|
|
136
135
|
|
|
137
136
|
Замінити переклад з російської на англійську в @nitra/tfm, @nitra/tf та @nitra/tfm-node. Якщо англійська вже є, то прибираємо російську:
|
|
138
137
|
|
|
139
138
|
Приклад БУЛО:
|
|
140
139
|
|
|
140
|
+
```vue
|
|
141
141
|
<template>
|
|
142
|
-
|
|
142
|
+
<h5>{{ t`Привіт` }}</h5>
|
|
143
143
|
</template>
|
|
144
144
|
|
|
145
145
|
<script setup>
|
|
@@ -147,16 +147,18 @@ import tf from '@nitra/tf/webpack'
|
|
|
147
147
|
|
|
148
148
|
// Translate
|
|
149
149
|
const tr = {
|
|
150
|
-
|
|
150
|
+
Привіт: 'Привет'
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
const t = tf.bind({ tr })
|
|
154
154
|
</script>
|
|
155
|
+
```
|
|
155
156
|
|
|
156
|
-
|
|
157
|
+
СТАЛО:
|
|
157
158
|
|
|
159
|
+
```vue
|
|
158
160
|
<template>
|
|
159
|
-
|
|
161
|
+
<h5>{{ t`Привіт` }}</h5>
|
|
160
162
|
</template>
|
|
161
163
|
|
|
162
164
|
<script setup>
|
|
@@ -164,19 +166,20 @@ import tf from '@nitra/tf/webpack'
|
|
|
164
166
|
|
|
165
167
|
// Translate
|
|
166
168
|
const tr = {
|
|
167
|
-
|
|
169
|
+
Привіт: 'Hello'
|
|
168
170
|
}
|
|
169
171
|
|
|
170
172
|
const t = tf.bind({ tr })
|
|
171
173
|
</script>
|
|
174
|
+
```
|
|
172
175
|
|
|
173
176
|
або
|
|
174
177
|
|
|
175
178
|
import { tf } from '@nitra/tfm'
|
|
176
179
|
|
|
177
180
|
const tr = {
|
|
178
|
-
|
|
179
|
-
|
|
181
|
+
Так: { ru: 'Да' },
|
|
182
|
+
Ні: { ru: 'Нет' }
|
|
180
183
|
}
|
|
181
184
|
|
|
182
185
|
на
|
|
@@ -184,11 +187,10 @@ const tr = {
|
|
|
184
187
|
import { tf } from '@nitra/tfm'
|
|
185
188
|
|
|
186
189
|
const tr = {
|
|
187
|
-
|
|
188
|
-
|
|
190
|
+
Так: { ru: 'Yes' },
|
|
191
|
+
Ні: { ru: 'No' }
|
|
189
192
|
}
|
|
190
193
|
|
|
191
|
-
|
|
192
194
|
## 6. Після очистки
|
|
193
195
|
|
|
194
196
|
- Переконайся, що `kustomization.yaml` у кожній директорії `k8s/` не посилається на видалені overlay або файли.
|
|
@@ -51,7 +51,6 @@ description: >-
|
|
|
51
51
|
Видалені файли — `delete`-операція. Нові файли `<slug>.md` — `rewrite`. Модифіковані clean-файли — `merge-into`.
|
|
52
52
|
|
|
53
53
|
4. **Прийняти / відкотити:**
|
|
54
|
-
|
|
55
54
|
- Прийняти все: `git add docs/adr/ && git commit -m "adr: normalize batch"`.
|
|
56
55
|
- Відкотити конкретний файл: `git checkout -- docs/adr/<file>` (для `rewrite` цього мало — треба ще `git restore --staged` і `rm` нового).
|
|
57
56
|
- Відкотити весь батч: `git checkout -- docs/adr/ && git clean -f docs/adr/` (видалить і untracked rewrite-результати).
|
package/skills/fix/SKILL.md
CHANGED
|
@@ -36,17 +36,13 @@ bun i
|
|
|
36
36
|
oxfmt .
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
6.
|
|
39
|
+
6. **Лінт коду** — викликай скіл **`/n-lint`** (один канонічний прогон `bun run lint`):
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
bun run lint
|
|
43
|
-
bun run lint-text
|
|
44
|
-
bun run lint-style
|
|
42
|
+
bun run lint
|
|
45
43
|
```
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
Підсумок: Перелічи запущені скрипти та зміни; опиши блокери, якщо лишились.
|
|
45
|
+
Лінт-логіку (auto-fix, рефакторинг під `sonarjs/cognitive-complexity`, обмеження `cspell`/`jscpd`/`knip`, заборона паралельних запусків ESLint) **не дублюй** тут — вона повністю у скілі **`/n-lint`**. Цей скіл (`/n-fix`) відповідає лише за **структуру** проєкту (правила `.cursor/rules/` + `npx @nitra/cursor check`); за **чистоту коду** відповідає **`/n-lint`**.
|
|
50
46
|
|
|
51
47
|
7. **Верифікація** — перевір що все виправлено:
|
|
52
48
|
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# Тести для `abie.base_deployment_preem`. Запуск:
|
|
2
|
-
# conftest verify -p npm/policy/abie/base_deployment_preem
|
|
3
|
-
package abie.base_deployment_preem_test
|
|
4
|
-
|
|
5
|
-
import rego.v1
|
|
6
|
-
|
|
7
|
-
import data.abie.base_deployment_preem
|
|
8
|
-
|
|
9
|
-
mk_deployment(node_selector) := {
|
|
10
|
-
"apiVersion": "apps/v1",
|
|
11
|
-
"kind": "Deployment",
|
|
12
|
-
"metadata": {"name": "api", "namespace": "dev"},
|
|
13
|
-
"spec": {"template": {"spec": object.union(
|
|
14
|
-
{"containers": [{"name": "main", "image": "x"}]},
|
|
15
|
-
{"nodeSelector": node_selector},
|
|
16
|
-
)}},
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
test_deny_no_node_selector if {
|
|
20
|
-
input_doc := {
|
|
21
|
-
"apiVersion": "apps/v1",
|
|
22
|
-
"kind": "Deployment",
|
|
23
|
-
"metadata": {"name": "api"},
|
|
24
|
-
"spec": {"template": {"spec": {"containers": [{"name": "main", "image": "x"}]}}},
|
|
25
|
-
}
|
|
26
|
-
count(base_deployment_preem.deny) > 0 with input as input_doc
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
test_deny_node_selector_without_preem if {
|
|
30
|
-
count(base_deployment_preem.deny) > 0 with input as mk_deployment({"role": "worker"})
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
test_deny_preem_false if {
|
|
34
|
-
count(base_deployment_preem.deny) > 0 with input as mk_deployment({"preem": false})
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
test_deny_preem_string_false if {
|
|
38
|
-
count(base_deployment_preem.deny) > 0 with input as mk_deployment({"preem": "false"})
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
test_allow_preem_boolean_true if {
|
|
42
|
-
count(base_deployment_preem.deny) == 0 with input as mk_deployment({"preem": true})
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
test_allow_preem_string_true if {
|
|
46
|
-
count(base_deployment_preem.deny) == 0 with input as mk_deployment({"preem": "true"})
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
test_allow_preem_string_uppercase_true if {
|
|
50
|
-
count(base_deployment_preem.deny) == 0 with input as mk_deployment({"preem": "TRUE"})
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
# Не Deployment — пакет не діє (дзеркало JS-предиката).
|
|
54
|
-
test_allow_non_deployment if {
|
|
55
|
-
count(base_deployment_preem.deny) == 0 with input as {
|
|
56
|
-
"apiVersion": "v1",
|
|
57
|
-
"kind": "ConfigMap",
|
|
58
|
-
"metadata": {"name": "x"},
|
|
59
|
-
}
|
|
60
|
-
}
|
package/rules/abie/policy/clean_merged_ignore_branches/clean_merged_ignore_branches_test.rego
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# Тести для `abie.clean_merged_ignore_branches`. Запуск:
|
|
2
|
-
# conftest verify -p npm/policy/abie/clean_merged_ignore_branches
|
|
3
|
-
package abie.clean_merged_ignore_branches_test
|
|
4
|
-
|
|
5
|
-
import rego.v1
|
|
6
|
-
|
|
7
|
-
import data.abie.clean_merged_ignore_branches
|
|
8
|
-
|
|
9
|
-
# Каркас workflow з одним job, що містить step із заданим with.
|
|
10
|
-
mk_workflow(step_with) := {"jobs": {"cleanup": {"steps": [{
|
|
11
|
-
"uses": "phpdocker-io/github-actions-delete-abandoned-branches@v2",
|
|
12
|
-
"with": step_with,
|
|
13
|
-
}]}}}
|
|
14
|
-
|
|
15
|
-
other_step_workflow := {"jobs": {"cleanup": {"steps": [{"uses": "actions/checkout@v6"}]}}}
|
|
16
|
-
|
|
17
|
-
# Workflow без потрібного кроку.
|
|
18
|
-
test_deny_step_missing if {
|
|
19
|
-
count(clean_merged_ignore_branches.deny) > 0 with input as other_step_workflow
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
test_deny_ignore_branches_missing if {
|
|
23
|
-
count(clean_merged_ignore_branches.deny) > 0 with input as mk_workflow({})
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
test_deny_missing_required_token if {
|
|
27
|
-
count(clean_merged_ignore_branches.deny) > 0 with input as mk_workflow({"ignore_branches": "dev"})
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
test_deny_completely_wrong_tokens if {
|
|
31
|
-
count(clean_merged_ignore_branches.deny) > 0 with input as mk_workflow({"ignore_branches": "main,develop"})
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
test_allow_required_tokens if {
|
|
35
|
-
count(clean_merged_ignore_branches.deny) == 0 with input as mk_workflow({"ignore_branches": "dev,ua"})
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
# Регістронезалежне порівняння і пропуск пробілів.
|
|
39
|
-
test_allow_uppercase_with_spaces if {
|
|
40
|
-
count(clean_merged_ignore_branches.deny) == 0 with input as mk_workflow({"ignore_branches": " DEV , UA "})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
extra_branches_workflow := mk_workflow({"ignore_branches": "dev,ua,main,release/*"})
|
|
44
|
-
|
|
45
|
-
# Додаткові гілки після обов'язкових — дозволено.
|
|
46
|
-
test_allow_extra_branches if {
|
|
47
|
-
count(clean_merged_ignore_branches.deny) == 0 with input as extra_branches_workflow
|
|
48
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
# Тести для `abie.health_check_policy`. Запуск:
|
|
2
|
-
# conftest verify -p npm/policy/abie/health_check_policy
|
|
3
|
-
package abie.health_check_policy_test
|
|
4
|
-
|
|
5
|
-
import rego.v1
|
|
6
|
-
|
|
7
|
-
import data.abie.health_check_policy
|
|
8
|
-
|
|
9
|
-
valid_hcp := {
|
|
10
|
-
"apiVersion": "networking.gke.io/v1",
|
|
11
|
-
"kind": "HealthCheckPolicy",
|
|
12
|
-
"metadata": {"name": "api"},
|
|
13
|
-
"spec": {
|
|
14
|
-
"default": {"config": {
|
|
15
|
-
"type": "HTTP",
|
|
16
|
-
"httpHealthCheck": {"requestPath": "/healthz", "port": 8080},
|
|
17
|
-
}},
|
|
18
|
-
"targetRef": {"group": "", "kind": "Service", "name": "api-hl"},
|
|
19
|
-
},
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
# ── happy path ────────────────────────────────────────────────────────────
|
|
23
|
-
|
|
24
|
-
test_allow_canonical if {
|
|
25
|
-
count(health_check_policy.deny) == 0 with input as valid_hcp
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
# ── apiVersion ────────────────────────────────────────────────────────────
|
|
29
|
-
|
|
30
|
-
test_deny_wrong_api_version if {
|
|
31
|
-
bad := json.patch(valid_hcp, [{"op": "replace", "path": "/apiVersion", "value": "networking.gke.io/v1beta1"}])
|
|
32
|
-
count(health_check_policy.deny) > 0 with input as bad
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
# ── metadata.name ─────────────────────────────────────────────────────────
|
|
36
|
-
|
|
37
|
-
test_deny_empty_name if {
|
|
38
|
-
bad := json.patch(valid_hcp, [{"op": "replace", "path": "/metadata/name", "value": ""}])
|
|
39
|
-
count(health_check_policy.deny) > 0 with input as bad
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
# ── spec.default.config.type ──────────────────────────────────────────────
|
|
43
|
-
|
|
44
|
-
test_deny_config_type_not_http if {
|
|
45
|
-
bad := json.patch(valid_hcp, [{"op": "replace", "path": "/spec/default/config/type", "value": "TCP"}])
|
|
46
|
-
count(health_check_policy.deny) > 0 with input as bad
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
# ── requestPath ───────────────────────────────────────────────────────────
|
|
50
|
-
|
|
51
|
-
test_deny_empty_request_path if {
|
|
52
|
-
bad := json.patch(valid_hcp, [{
|
|
53
|
-
"op": "replace",
|
|
54
|
-
"path": "/spec/default/config/httpHealthCheck/requestPath",
|
|
55
|
-
"value": "",
|
|
56
|
-
}])
|
|
57
|
-
count(health_check_policy.deny) > 0 with input as bad
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
test_deny_request_path_without_slash if {
|
|
61
|
-
bad := json.patch(valid_hcp, [{
|
|
62
|
-
"op": "replace",
|
|
63
|
-
"path": "/spec/default/config/httpHealthCheck/requestPath",
|
|
64
|
-
"value": "healthz",
|
|
65
|
-
}])
|
|
66
|
-
count(health_check_policy.deny) > 0 with input as bad
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
# ── port ──────────────────────────────────────────────────────────────────
|
|
70
|
-
|
|
71
|
-
test_deny_port_not_8080 if {
|
|
72
|
-
bad := json.patch(valid_hcp, [{
|
|
73
|
-
"op": "replace",
|
|
74
|
-
"path": "/spec/default/config/httpHealthCheck/port",
|
|
75
|
-
"value": 9090,
|
|
76
|
-
}])
|
|
77
|
-
count(health_check_policy.deny) > 0 with input as bad
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
# ── targetRef ─────────────────────────────────────────────────────────────
|
|
81
|
-
|
|
82
|
-
test_deny_target_ref_kind_not_service if {
|
|
83
|
-
bad := json.patch(valid_hcp, [{"op": "replace", "path": "/spec/targetRef/kind", "value": "Gateway"}])
|
|
84
|
-
count(health_check_policy.deny) > 0 with input as bad
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
test_deny_target_ref_name_without_hl if {
|
|
88
|
-
bad := json.patch(valid_hcp, [{"op": "replace", "path": "/spec/targetRef/name", "value": "api"}])
|
|
89
|
-
count(health_check_policy.deny) > 0 with input as bad
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
# Не HCP — пакет не діє.
|
|
93
|
-
test_allow_other_kind if {
|
|
94
|
-
count(health_check_policy.deny) == 0 with input as {
|
|
95
|
-
"apiVersion": "v1",
|
|
96
|
-
"kind": "ConfigMap",
|
|
97
|
-
"metadata": {"name": "x"},
|
|
98
|
-
}
|
|
99
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# Тести для `abie.http_route_base`. Запуск:
|
|
2
|
-
# conftest verify -p npm/policy/abie/http_route_base
|
|
3
|
-
package abie.http_route_base_test
|
|
4
|
-
|
|
5
|
-
import rego.v1
|
|
6
|
-
|
|
7
|
-
import data.abie.http_route_base
|
|
8
|
-
|
|
9
|
-
mk_route(hostnames) := {
|
|
10
|
-
"apiVersion": "gateway.networking.k8s.io/v1",
|
|
11
|
-
"kind": "HTTPRoute",
|
|
12
|
-
"metadata": {"name": "r", "namespace": "dev"},
|
|
13
|
-
"spec": {"hostnames": hostnames},
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
# ── allow ────────────────────────────────────────────────────────────────
|
|
17
|
-
|
|
18
|
-
test_allow_apex if {
|
|
19
|
-
count(http_route_base.deny) == 0 with input as mk_route(["aiml.live"])
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
test_allow_subdomain if {
|
|
23
|
-
count(http_route_base.deny) == 0 with input as mk_route(["api.aiml.live"])
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
test_allow_wildcard if {
|
|
27
|
-
count(http_route_base.deny) == 0 with input as mk_route(["*.aiml.live"])
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
test_allow_uppercase_apex if {
|
|
31
|
-
count(http_route_base.deny) == 0 with input as mk_route(["AIML.LIVE"])
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
test_allow_multiple_subdomains if {
|
|
35
|
-
count(http_route_base.deny) == 0 with input as mk_route(["api.aiml.live", "admin.aiml.live"])
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
# ── deny ─────────────────────────────────────────────────────────────────
|
|
39
|
-
|
|
40
|
-
test_deny_other_apex if {
|
|
41
|
-
count(http_route_base.deny) > 0 with input as mk_route(["example.com"])
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
test_deny_wrong_subdomain if {
|
|
45
|
-
count(http_route_base.deny) > 0 with input as mk_route(["api.example.com"])
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
test_deny_mixed_one_bad if {
|
|
49
|
-
count(http_route_base.deny) > 0 with input as mk_route(["api.aiml.live", "evil.com"])
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
test_deny_aiml_live_substring if {
|
|
53
|
-
# "aiml.live.example.com" не має закінчуватись на ".aiml.live" — це інший домен.
|
|
54
|
-
count(http_route_base.deny) > 0 with input as mk_route(["aiml.live.example.com"])
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
# Не HTTPRoute — пакет не діє.
|
|
58
|
-
test_allow_non_httproute if {
|
|
59
|
-
count(http_route_base.deny) == 0 with input as {
|
|
60
|
-
"apiVersion": "v1",
|
|
61
|
-
"kind": "Service",
|
|
62
|
-
"metadata": {"name": "x"},
|
|
63
|
-
}
|
|
64
|
-
}
|