@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.
@@ -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
+ }
@@ -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) {
@@ -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([readFile(PROJECT_HOOK_PATH, 'utf8'), readFile(BUNDLED_HOOK_PATH, 'utf8')])
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 {