@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
package/scripts/lint-rego.mjs
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Лінт Rego-полісі (`conftest.mdc` + `rego.mdc`): preflight на `opa` і `regal`,
|
|
3
|
+
* далі послідовно `opa check --strict` і `regal lint`.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Чому два інструменти:
|
|
6
|
+
* - `opa check --strict` — компіляція з типами і строгим режимом (мертвий код, неоднозначні
|
|
7
|
+
* правила, незадекларовані змінні). Ловить помилки, які `regal` навмисно лишає поза скоупом
|
|
8
|
+
* (він — про стиль і ідіоматичність, а не про компіляцію).
|
|
9
|
+
* - `regal lint` (https://docs.styra.com/regal) — статичний лінтер Rego: ловить v0-синтаксис,
|
|
10
|
+
* неявні set-rules та інші відхилення від `rego.v1`, плюс bugs/idiomatic/performance-правила.
|
|
11
|
+
*
|
|
12
|
+
* Без preflight-у на бінарники лінт мовчки злетить з невиразним повідомленням від shell —
|
|
13
|
+
* друкуємо явні install-hints (як це робить `lint-ga.mjs` для shellcheck/uv). `opa` додатково
|
|
14
|
+
* потрібен VS Code-розширенню `tsandall.opa` (LSP, format-on-save через `opa fmt`) — деталі в
|
|
15
|
+
* `mdc/rego.mdc`.
|
|
8
16
|
*
|
|
9
17
|
* Цілі лінту: `npm/policy/` (місце, де поки що живуть Rego-полісі пакета `@nitra/cursor`).
|
|
10
18
|
* Якщо в репозиторії з’являться інші *.rego поза цим деревом, додай шлях у `LINT_TARGETS` —
|
|
11
|
-
*
|
|
19
|
+
* обидва інструменти приймають кілька шляхів і самі рекурсивно обходять директорії.
|
|
12
20
|
*/
|
|
13
21
|
import { spawnSync } from 'node:child_process'
|
|
14
22
|
import { existsSync } from 'node:fs'
|
|
@@ -19,6 +27,24 @@ import { resolveCmd } from './utils/resolve-cmd.mjs'
|
|
|
19
27
|
/** Шляхи з Rego-полісі (відносно cwd). Існують не всі на ранніх стадіях — фільтруємо нижче. */
|
|
20
28
|
const LINT_TARGETS = ['npm/policy']
|
|
21
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Друкує підказку зі встановлення `opa` (потрібен для `opa check --strict` і VS Code LSP).
|
|
32
|
+
* @returns {void}
|
|
33
|
+
*/
|
|
34
|
+
function printOpaInstallHints() {
|
|
35
|
+
process.stderr.write(
|
|
36
|
+
[
|
|
37
|
+
'❌ opa не знайдено в PATH.',
|
|
38
|
+
' Без нього не запускається `opa check --strict` (типи + мертвий код у *.rego),',
|
|
39
|
+
' і не працює VS Code-розширення `tsandall.opa` (LSP, format-on-save через opa fmt).',
|
|
40
|
+
' Встанови:',
|
|
41
|
+
' macOS: brew install opa',
|
|
42
|
+
' Universal: https://www.openpolicyagent.org/docs/latest/#1-download-opa',
|
|
43
|
+
''
|
|
44
|
+
].join('\n')
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
22
48
|
/**
|
|
23
49
|
* Друкує підказку зі встановлення `regal`.
|
|
24
50
|
* @returns {void}
|
|
@@ -29,7 +55,7 @@ function printRegalInstallHints() {
|
|
|
29
55
|
'❌ regal не знайдено в PATH.',
|
|
30
56
|
' Без нього не перевіряється rego.v1 синтаксис у *.rego (правило `conftest`).',
|
|
31
57
|
' Встанови:',
|
|
32
|
-
' macOS:
|
|
58
|
+
' macOS: brew install regal',
|
|
33
59
|
' Universal: https://docs.styra.com/regal#installation',
|
|
34
60
|
''
|
|
35
61
|
].join('\n')
|
|
@@ -37,34 +63,54 @@ function printRegalInstallHints() {
|
|
|
37
63
|
}
|
|
38
64
|
|
|
39
65
|
/**
|
|
40
|
-
* Запускає
|
|
66
|
+
* Запускає крок з відображенням команди користувачу. Stdout/stderr передаємо як є
|
|
67
|
+
* (`stdio: 'inherit'`), щоб виглядало як прямий виклик у shell.
|
|
68
|
+
* @param {string} bin абсолютний шлях до бінарника
|
|
69
|
+
* @param {string[]} args аргументи
|
|
70
|
+
* @param {string} cwd робочий каталог
|
|
71
|
+
* @returns {number} код виходу (0 — OK)
|
|
72
|
+
*/
|
|
73
|
+
function runStep(bin, args, cwd) {
|
|
74
|
+
console.log(`▶ ${bin} ${args.join(' ')}`)
|
|
75
|
+
const result = spawnSync(bin, args, { cwd, stdio: 'inherit', env: process.env })
|
|
76
|
+
if (result.error) {
|
|
77
|
+
process.stderr.write(`❌ Не вдалося запустити ${bin}: ${result.error.message}\n`)
|
|
78
|
+
return 1
|
|
79
|
+
}
|
|
80
|
+
return result.status ?? 1
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Запускає `opa check --strict` і `regal lint` по існуючих цілях. Якщо жодної цілі немає —
|
|
85
|
+
* пропускає лінт із кодом 0. Якщо хоча б один preflight не пройшов — exit 1 ще до запусків.
|
|
41
86
|
* @param {string} [cwd] робочий каталог (за замовчуванням `process.cwd()`)
|
|
42
|
-
* @returns {number} 0 — OK або skip; інакше код виходу
|
|
87
|
+
* @returns {number} 0 — OK або skip; інакше код виходу першого кроку, що впав
|
|
43
88
|
*/
|
|
44
89
|
export function runLintRego(cwd = process.cwd()) {
|
|
45
90
|
const root = resolve(cwd)
|
|
91
|
+
const opa = resolveCmd('opa')
|
|
46
92
|
const regal = resolveCmd('regal')
|
|
93
|
+
|
|
94
|
+
let preflightOk = true
|
|
95
|
+
if (!opa) {
|
|
96
|
+
printOpaInstallHints()
|
|
97
|
+
preflightOk = false
|
|
98
|
+
}
|
|
47
99
|
if (!regal) {
|
|
48
100
|
printRegalInstallHints()
|
|
49
|
-
|
|
101
|
+
preflightOk = false
|
|
50
102
|
}
|
|
103
|
+
if (!preflightOk) return 1
|
|
51
104
|
|
|
52
105
|
const targets = LINT_TARGETS.filter(rel => existsSync(resolve(root, rel)))
|
|
53
106
|
if (targets.length === 0) {
|
|
54
107
|
return 0
|
|
55
108
|
}
|
|
56
109
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
env: process.env
|
|
62
|
-
})
|
|
63
|
-
if (result.error) {
|
|
64
|
-
process.stderr.write(`❌ Не вдалося запустити regal: ${result.error.message}\n`)
|
|
65
|
-
return 1
|
|
66
|
-
}
|
|
67
|
-
return result.status ?? 1
|
|
110
|
+
const opaCode = runStep(opa, ['check', '--strict', ...targets], root)
|
|
111
|
+
if (opaCode !== 0) return opaCode
|
|
112
|
+
|
|
113
|
+
return runStep(regal, ['lint', ...targets], root)
|
|
68
114
|
}
|
|
69
115
|
|
|
70
116
|
process.exitCode = runLintRego()
|
|
@@ -85,10 +85,7 @@ export function listShellScriptPaths(cwd) {
|
|
|
85
85
|
|
|
86
86
|
const fromGlob = globSync('**/*.sh', {
|
|
87
87
|
cwd,
|
|
88
|
-
exclude: p =>
|
|
89
|
-
p.includes('node_modules') ||
|
|
90
|
-
p.startsWith(`node_modules/`) ||
|
|
91
|
-
p.split('/').includes('node_modules')
|
|
88
|
+
exclude: p => p.includes('node_modules') || p.startsWith(`node_modules/`) || p.split('/').includes('node_modules')
|
|
92
89
|
})
|
|
93
90
|
return [...new Set(fromGlob.map(p => p.replaceAll('\\', '/')))].sort()
|
|
94
91
|
}
|
|
@@ -19,11 +19,7 @@
|
|
|
19
19
|
import { readdir, readFile } from 'node:fs/promises'
|
|
20
20
|
import { join, relative } from 'node:path'
|
|
21
21
|
|
|
22
|
-
import {
|
|
23
|
-
flattenWorkflowSteps,
|
|
24
|
-
getStepRun,
|
|
25
|
-
parseWorkflowYaml
|
|
26
|
-
} from './gha-workflow.mjs'
|
|
22
|
+
import { flattenWorkflowSteps, getStepRun, parseWorkflowYaml } from './gha-workflow.mjs'
|
|
27
23
|
|
|
28
24
|
const WORKFLOWS_DIR_REL = '.github/workflows'
|
|
29
25
|
const REQUIRED_IGNORES = ['graphql', 'bun']
|
|
@@ -122,7 +118,7 @@ export function evaluateDepcheckStepForPackage(root, pkgRoot) {
|
|
|
122
118
|
// Усі знайдені кроки існують, але жоден не має повного списку обов'язкових ignores —
|
|
123
119
|
// повертаємо missing з першого, щоб дати конкретний фідбек.
|
|
124
120
|
const firstMissing = REQUIRED_IGNORES.filter(
|
|
125
|
-
req => !(
|
|
121
|
+
req => !(parseDepcheckIgnoresArg(stepsForThisPackage[0].args) ?? []).includes(req)
|
|
126
122
|
)
|
|
127
123
|
return { kind: 'missing-ignores', missing: firstMissing }
|
|
128
124
|
}
|