@nitra/cursor 1.8.203 → 1.8.206
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 +35 -1
- package/bin/auto-rules.md +2 -0
- package/mdc/rego.mdc +77 -0
- package/package.json +1 -1
- package/policy/ga/{clean-ga-workflows.rego → clean_ga_workflows/clean_ga_workflows.rego} +49 -46
- package/policy/ga/clean_merged_branch/clean_merged_branch.rego +167 -0
- package/policy/ga/git_ai/git_ai.rego +109 -0
- package/policy/ga/lint_ga/lint_ga.rego +144 -0
- package/scripts/auto-rules.mjs +10 -0
- package/scripts/check-adr.mjs +4 -1
- package/scripts/check-ga.mjs +0 -504
- package/scripts/check-hasura.mjs +3 -3
- package/scripts/check-js-run.mjs +1 -4
- package/scripts/check-k8s.mjs +2 -1
- package/scripts/lint-ga.mjs +27 -5
- package/scripts/lint-rego.mjs +67 -21
- package/scripts/run-shellcheck-text.mjs +1 -4
- package/scripts/utils/depcheck-workflow.mjs +2 -6
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Порт перевірок `validateLintGaWorkflowStructure` + `validateLintGaOnTriggers` з
|
|
2
|
+
# `npm/scripts/check-ga.mjs` (ga.mdc).
|
|
3
|
+
#
|
|
4
|
+
# Запуск (локально):
|
|
5
|
+
# conftest test .github/workflows/lint-ga.yml \
|
|
6
|
+
# -p npm/policy/ga --namespace ga.lint_ga
|
|
7
|
+
#
|
|
8
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
9
|
+
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
10
|
+
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
11
|
+
package ga.lint_ga
|
|
12
|
+
|
|
13
|
+
import rego.v1
|
|
14
|
+
|
|
15
|
+
# ── Очікувані значення ─────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
expected_concurrency_group := concat("", ["$", "{{ github.ref }}-$", "{{ github.workflow }}"])
|
|
18
|
+
|
|
19
|
+
expected_name := "Lint GA"
|
|
20
|
+
|
|
21
|
+
expected_branches := {"dev", "main"}
|
|
22
|
+
|
|
23
|
+
expected_push_paths := {".github/actions/**", ".github/workflows/**"}
|
|
24
|
+
|
|
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
|
+
# ── Аліаси на input ────────────────────────────────────────────────────────
|
|
33
|
+
#
|
|
34
|
+
# YAML 1.1 quirk: `on:` → boolean true → у конфтесті ключ "true".
|
|
35
|
+
|
|
36
|
+
gha_on := input["true"]
|
|
37
|
+
|
|
38
|
+
# Job-id містить дефіс — звертаємося через `[…]`. Імʼя `job` (без префіксу пакету)
|
|
39
|
+
# — щоб уникнути regal-правила `rule-name-repeats-package`.
|
|
40
|
+
job := input.jobs["lint-ga"]
|
|
41
|
+
|
|
42
|
+
# Усі `uses:` зі steps цього job-а — для перевірки членства.
|
|
43
|
+
job_uses_set contains job.steps[_].uses
|
|
44
|
+
|
|
45
|
+
# Усі `run:` зі steps цього job-а, склеєні в один blob — для substring-перевірки.
|
|
46
|
+
job_run_blob := concat("\n", [run |
|
|
47
|
+
run := job.steps[_].run
|
|
48
|
+
])
|
|
49
|
+
|
|
50
|
+
# ── deny rules (контигно — regal: messy-rule) ──────────────────────────────
|
|
51
|
+
|
|
52
|
+
deny contains msg if {
|
|
53
|
+
input.name != expected_name
|
|
54
|
+
msg := sprintf("lint-ga.yml: name має бути %q (ga.mdc)", [expected_name])
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
deny contains msg if {
|
|
58
|
+
not push_branches_have_dev_and_main
|
|
59
|
+
msg := "lint-ga.yml: on.push.branches має містити dev і main (ga.mdc)"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
deny contains msg if {
|
|
63
|
+
not pr_branches_have_dev_and_main
|
|
64
|
+
msg := "lint-ga.yml: on.pull_request.branches має містити dev і main (ga.mdc)"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
deny contains msg if {
|
|
68
|
+
not push_paths_have_required
|
|
69
|
+
msg := "lint-ga.yml: on.push.paths має містити .github/actions/** і .github/workflows/** (ga.mdc)"
|
|
70
|
+
}
|
|
71
|
+
|
|
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
|
+
deny contains msg if {
|
|
90
|
+
not job
|
|
91
|
+
msg := "lint-ga.yml: jobs.lint-ga відсутній (ga.mdc)"
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
deny contains msg if {
|
|
95
|
+
job["runs-on"] != "ubuntu-latest"
|
|
96
|
+
msg := "lint-ga.yml: runs-on має бути ubuntu-latest (ga.mdc)"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
deny contains msg if {
|
|
100
|
+
job.permissions.contents != "read"
|
|
101
|
+
msg := "lint-ga.yml: permissions мають бути contents: read (ga.mdc)"
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
deny contains msg if {
|
|
105
|
+
count(job.steps) == 0
|
|
106
|
+
msg := "lint-ga.yml: jobs.lint-ga.steps відсутні (ga.mdc)"
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
deny contains msg if {
|
|
110
|
+
not "actions/checkout@v6" in job_uses_set
|
|
111
|
+
msg := "lint-ga.yml: має бути uses: actions/checkout@v6 (ga.mdc)"
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
deny contains msg if {
|
|
115
|
+
not "./.github/actions/setup-bun-deps" in job_uses_set
|
|
116
|
+
msg := "lint-ga.yml: має бути uses: ./.github/actions/setup-bun-deps (ga.mdc)"
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
deny contains msg if {
|
|
120
|
+
not "astral-sh/setup-uv@v8.0.0" in job_uses_set
|
|
121
|
+
msg := "lint-ga.yml: має бути uses: astral-sh/setup-uv@v8.0.0 (ga.mdc)"
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
deny contains msg if {
|
|
125
|
+
not contains(job_run_blob, "bun run lint-ga")
|
|
126
|
+
msg := "lint-ga.yml: має бути крок run: bun run lint-ga (ga.mdc)"
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# ── helpers ────────────────────────────────────────────────────────────────
|
|
130
|
+
|
|
131
|
+
push_branches_have_dev_and_main if {
|
|
132
|
+
branches := gha_on.push.branches
|
|
133
|
+
expected_branches & {b | some b in branches} == expected_branches
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
pr_branches_have_dev_and_main if {
|
|
137
|
+
branches := gha_on.pull_request.branches
|
|
138
|
+
expected_branches & {b | some b in branches} == expected_branches
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
push_paths_have_required if {
|
|
142
|
+
paths := gha_on.push.paths
|
|
143
|
+
expected_push_paths & {p | some p in paths} == expected_push_paths
|
|
144
|
+
}
|
package/scripts/auto-rules.mjs
CHANGED
|
@@ -41,6 +41,7 @@ export const AUTO_RULE_ORDER = Object.freeze([
|
|
|
41
41
|
'nginx-default-tpl',
|
|
42
42
|
'npm-module',
|
|
43
43
|
'php',
|
|
44
|
+
'rego',
|
|
44
45
|
'style-lint',
|
|
45
46
|
'text',
|
|
46
47
|
'vue'
|
|
@@ -104,6 +105,7 @@ export const AUTO_RULE_DEPENDENCIES = Object.freeze(
|
|
|
104
105
|
const ABIE_REPOSITORY_URL_MARKER = 'https://github.com/abinbevefes/'
|
|
105
106
|
const HASURA_CONFIG_MARKER = 'metadata_directory: metadata'
|
|
106
107
|
const JS_LIKE_RE = /\.(?:mjs|cjs|js|jsx|ts|tsx)$/iu
|
|
108
|
+
const REGO_RE = /\.rego$/iu
|
|
107
109
|
const STYLE_RE = /\.(?:css|vue)$/iu
|
|
108
110
|
const VUE_RE = /\.vue$/iu
|
|
109
111
|
const NGINX_DEFAULT_FILES = new Set(['default.conf.template', 'default.conf', 'nginx.conf'])
|
|
@@ -287,6 +289,7 @@ function updateDirFacts(dirName, facts) {
|
|
|
287
289
|
* hasDockerfile: boolean,
|
|
288
290
|
* hasJsLikeSource: boolean,
|
|
289
291
|
* hasNginxDefaultTplFile: boolean,
|
|
292
|
+
* hasRegoFile: boolean,
|
|
290
293
|
* hasVueOrCssSource: boolean,
|
|
291
294
|
* hasVueSource: boolean
|
|
292
295
|
* }} facts агреговані факти
|
|
@@ -311,6 +314,9 @@ function updateFileFacts(fileName, relPath, facts) {
|
|
|
311
314
|
if (STYLE_RE.test(relPath)) {
|
|
312
315
|
facts.hasVueOrCssSource = true
|
|
313
316
|
}
|
|
317
|
+
if (REGO_RE.test(relPath)) {
|
|
318
|
+
facts.hasRegoFile = true
|
|
319
|
+
}
|
|
314
320
|
}
|
|
315
321
|
|
|
316
322
|
/**
|
|
@@ -401,6 +407,7 @@ async function updateHasuraFactFromFile(absPath, fileName, facts) {
|
|
|
401
407
|
* hasHasuraConfig: boolean,
|
|
402
408
|
* hasJsLikeSource: boolean,
|
|
403
409
|
* hasNginxDefaultTplFile: boolean,
|
|
410
|
+
* hasRegoFile: boolean,
|
|
404
411
|
* hasVueOrCssSource: boolean,
|
|
405
412
|
* hasVueSource: boolean
|
|
406
413
|
* }} facts агреговані факти
|
|
@@ -489,6 +496,7 @@ export function isMonorepoPackage(packageJson) {
|
|
|
489
496
|
* hasJsLikeSource: boolean,
|
|
490
497
|
* hasK8sDir: boolean,
|
|
491
498
|
* hasNginxDefaultTplFile: boolean,
|
|
499
|
+
* hasRegoFile: boolean,
|
|
492
500
|
* hasTempoDir: boolean,
|
|
493
501
|
* hasVueSource: boolean,
|
|
494
502
|
* hasVueOrCssSource: boolean
|
|
@@ -505,6 +513,7 @@ export async function collectAutoRuleFacts(root) {
|
|
|
505
513
|
hasJsLikeSource: false,
|
|
506
514
|
hasK8sDir: false,
|
|
507
515
|
hasNginxDefaultTplFile: false,
|
|
516
|
+
hasRegoFile: false,
|
|
508
517
|
hasTempoDir: false,
|
|
509
518
|
hasVueSource: false,
|
|
510
519
|
hasVueOrCssSource: false
|
|
@@ -650,6 +659,7 @@ export async function detectAutoRulesAndSkills({
|
|
|
650
659
|
{ enabled: facts.hasNginxDefaultTplFile, id: 'nginx-default-tpl' },
|
|
651
660
|
{ enabled: npmDirExists, id: 'npm-module' },
|
|
652
661
|
{ enabled: composerJsonExists, id: 'php' },
|
|
662
|
+
{ enabled: facts.hasRegoFile, id: 'rego' },
|
|
653
663
|
{ enabled: facts.hasVueOrCssSource, id: 'style-lint' }
|
|
654
664
|
]
|
|
655
665
|
for (const item of autoRuleChecks) {
|
package/scripts/check-adr.mjs
CHANGED
|
@@ -68,7 +68,10 @@ async function checkHookScript(reporter) {
|
|
|
68
68
|
fail(`канонічний скрипт у пакеті не знайдено: ${BUNDLED_HOOK_PATH} — перевстанови @nitra/cursor`)
|
|
69
69
|
return
|
|
70
70
|
}
|
|
71
|
-
const [project, bundled] = await Promise.all([
|
|
71
|
+
const [project, bundled] = await Promise.all([
|
|
72
|
+
readFile(PROJECT_HOOK_PATH, 'utf8'),
|
|
73
|
+
readFile(BUNDLED_HOOK_PATH, 'utf8')
|
|
74
|
+
])
|
|
72
75
|
if (project === bundled) {
|
|
73
76
|
pass(`${PROJECT_HOOK_PATH} збігається з канонічним`)
|
|
74
77
|
} else {
|