@nitra/cursor 1.13.38 → 1.13.41
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 +18 -0
- package/bin/n-cursor.js +4 -0
- package/package.json +1 -1
- package/rules/ga/lint/lint.mjs +23 -3
- package/rules/ga/policy/lint_ga/lint_ga.rego +6 -0
- package/rules/ga/policy/lint_ga/template/lint-ga.yml.snippet.yml +6 -0
- package/rules/k8s/fix/manifests/check.mjs +7 -3
- package/rules/k8s/k8s.mdc +4 -4
- package/rules/text/fix/formatting/check.mjs +1 -1
- package/rules/text/lint/lint.mjs +113 -5
- package/rules/text/policy/lint_text/lint_text.rego +100 -0
- package/rules/text/policy/lint_text/target.json +4 -0
- package/rules/text/policy/lint_text/template/lint-text.yml.snippet.yml +61 -0
- package/rules/text/text.mdc +3 -57
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,24 @@
|
|
|
4
4
|
|
|
5
5
|
Формат — [Keep a Changelog](https://keepachangelog.com/uk/1.1.0/), нумерація — [SemVer](https://semver.org/lang/uk/).
|
|
6
6
|
|
|
7
|
+
## [1.13.41] - 2026-05-18
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- `k8s` rule: yannh-патерн для груп з крапками — у назві файлу схеми зберігається лише **перший сегмент** `group` до першої крапки (`networking.k8s.io` → `networking`, `rbac.authorization.k8s.io` → `rbac`, `flowcontrol.apiserver.k8s.io` → `flowcontrol`); попередній `<group-з-крапками-як-дефіси>` давав 404 для всіх ресурсів `*.k8s.io` (Ingress, NetworkPolicy, ClusterRole, StorageClass, FlowSchema, RuntimeClass тощо). Виправлено в `expectedSchemaUrlForTypedManifest` і `buildNetworkPolicyYaml` (`check-k8s.mjs`); опис патерну в `k8s.mdc` переписано з прикладами для усіх типових груп. Bump `k8s.mdc` `1.34` → `1.35`.
|
|
12
|
+
|
|
13
|
+
## [1.13.40] - 2026-05-18
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- `lint-text` CLI: preflight на `shellcheck`, `patch` і `dotenv-linter` до ланцюжка cspell/shellcheck/dotenv. Канон `lint-text.yml.snippet.yml` — кроки `Install shellcheck` (apt) і `Install dotenv-linter` (curl); rego `text.lint_text`. Bump `text.mdc` `1.28` → `1.29`.
|
|
18
|
+
|
|
19
|
+
## [1.13.39] - 2026-05-18
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- `lint-ga` CLI: preflight на `conftest` (поряд із `shellcheck`/`uv`) з install-hint; глобальний `catch` у `bin/n-cursor.js` більше не ковтає повідомлення `failConftestMissing()`. Канон `lint-ga.yml.snippet.yml` — крок `Install conftest` для CI; rego `ga.lint_ga` вимагає curl на release conftest.
|
|
24
|
+
|
|
7
25
|
## [1.13.38] - 2026-05-18
|
|
8
26
|
|
|
9
27
|
### Added
|
package/bin/n-cursor.js
CHANGED
|
@@ -1405,7 +1405,11 @@ try {
|
|
|
1405
1405
|
} catch (error) {
|
|
1406
1406
|
if (error instanceof ReexecHandoff) {
|
|
1407
1407
|
process.exitCode = error.code
|
|
1408
|
+
} else if (error instanceof Error && error.message) {
|
|
1409
|
+
console.error(error.message)
|
|
1410
|
+
process.exitCode = 1
|
|
1408
1411
|
} else {
|
|
1412
|
+
console.error(error)
|
|
1409
1413
|
process.exitCode = 1
|
|
1410
1414
|
}
|
|
1411
1415
|
}
|
package/package.json
CHANGED
package/rules/ga/lint/lint.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CLI-обгортка над канонічним `lint-ga` (ga.mdc): робить preflight на `shellcheck
|
|
2
|
+
* CLI-обгортка над канонічним `lint-ga` (ga.mdc): робить preflight на `shellcheck`, `uv` (для `uvx`)
|
|
3
|
+
* і `conftest` (для rego-полісі у `check-ga`),
|
|
3
4
|
* тоді послідовно виконує `bunx github-actionlint`, `uvx zizmor --offline --collect=workflows .` і
|
|
4
5
|
* делегує до `check-ga.mjs::check()` — там і Rego-частина (через `runConftestBatch`),
|
|
5
6
|
* і JS cross-file перевірки правил `ga.mdc`.
|
|
@@ -17,6 +18,10 @@
|
|
|
17
18
|
* `uv` потрібен для `uvx zizmor`. Якщо його нема — `uvx zizmor` падає неінформативно («command not
|
|
18
19
|
* found»); підказка з командою встановлення коротша й корисніша.
|
|
19
20
|
*
|
|
21
|
+
* `conftest` потрібен для `check-ga.mjs::runAllGaRego` (`runConftestBatch`). Без preflight крок
|
|
22
|
+
* check-ga кидає виняток, який глобальний `catch` у `bin/n-cursor.js` раніше ковтав без логу —
|
|
23
|
+
* локально це виглядало як мовчазний exit 1.
|
|
24
|
+
*
|
|
20
25
|
* Експортовано окремо `runLintGaCli` — використовується з `bin/n-cursor.js` як підкоманда `lint-ga`.
|
|
21
26
|
*/
|
|
22
27
|
import { platform } from 'node:process'
|
|
@@ -68,6 +73,21 @@ const UV_PREFLIGHT = {
|
|
|
68
73
|
successMsg: '✅ uv знайдено в PATH — uvx zizmor запуститься'
|
|
69
74
|
}
|
|
70
75
|
|
|
76
|
+
/** @type {PreflightDep} */
|
|
77
|
+
const CONFTEST_PREFLIGHT = {
|
|
78
|
+
bin: 'conftest',
|
|
79
|
+
winBins: ['conftest.exe'],
|
|
80
|
+
explanation: [
|
|
81
|
+
'Без нього не запускається пер-документна валідація через rego-полісі (npm/rules/*/policy/)',
|
|
82
|
+
'у кроці check-ga — `runConftestBatch` завершується hard-fail.'
|
|
83
|
+
].join('\n '),
|
|
84
|
+
install: [
|
|
85
|
+
'macOS: brew install conftest',
|
|
86
|
+
'Universal: https://www.conftest.dev/install/'
|
|
87
|
+
],
|
|
88
|
+
successMsg: '✅ conftest знайдено в PATH — check-ga виконає rego-полісі через runConftestBatch'
|
|
89
|
+
}
|
|
90
|
+
|
|
71
91
|
/**
|
|
72
92
|
* Шукає бінарник у PATH з урахуванням Windows: спершу `winBins`, потім `bin`.
|
|
73
93
|
* @param {PreflightDep} dep опис залежності
|
|
@@ -116,7 +136,7 @@ function preflight(dep) {
|
|
|
116
136
|
* Виконує канонічний `lint-ga` з preflight-перевірками і делегує до `check-ga.check()`.
|
|
117
137
|
*
|
|
118
138
|
* Послідовність:
|
|
119
|
-
* 1) preflight: `shellcheck` (для
|
|
139
|
+
* 1) preflight: `shellcheck`, `uv` (для `uvx zizmor`) і `conftest` (для check-ga); відсутній → exit 1;
|
|
120
140
|
* 2) `bunx github-actionlint`;
|
|
121
141
|
* 3) `uvx zizmor --offline --collect=workflows .`;
|
|
122
142
|
* 4) `check-ga.mjs::check()` — Rego-полісі (батч conftest з `npm/policy/ga/`) + JS cross-file
|
|
@@ -132,7 +152,7 @@ function preflight(dep) {
|
|
|
132
152
|
*/
|
|
133
153
|
export async function runLintGaCli() {
|
|
134
154
|
let preflightOk = true
|
|
135
|
-
for (const dep of [SHELLCHECK_PREFLIGHT, UV_PREFLIGHT]) {
|
|
155
|
+
for (const dep of [SHELLCHECK_PREFLIGHT, UV_PREFLIGHT, CONFTEST_PREFLIGHT]) {
|
|
136
156
|
if (!preflight(dep)) preflightOk = false
|
|
137
157
|
}
|
|
138
158
|
if (!preflightOk) return 1
|
|
@@ -92,6 +92,12 @@ deny contains msg if {
|
|
|
92
92
|
msg := sprintf("lint-ga.yml: має бути uses: %s (ga.mdc)", [required_use])
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
deny contains msg if {
|
|
96
|
+
expected_run_blob != ""
|
|
97
|
+
not contains(job_run_blob, "open-policy-agent/conftest")
|
|
98
|
+
msg := "lint-ga.yml: має бути крок Install conftest (ga.mdc)"
|
|
99
|
+
}
|
|
100
|
+
|
|
95
101
|
deny contains msg if {
|
|
96
102
|
expected_run_blob != ""
|
|
97
103
|
not contains(job_run_blob, "bun run lint-ga")
|
|
@@ -31,5 +31,11 @@ jobs:
|
|
|
31
31
|
|
|
32
32
|
- uses: astral-sh/setup-uv@v8.0.0
|
|
33
33
|
|
|
34
|
+
- name: Install conftest
|
|
35
|
+
run: >-
|
|
36
|
+
curl -fsSL
|
|
37
|
+
https://github.com/open-policy-agent/conftest/releases/download/v0.62.0/conftest_0.62.0_Linux_x86_64.tar.gz
|
|
38
|
+
| sudo tar -xz -C /usr/local/bin conftest
|
|
39
|
+
|
|
34
40
|
- name: Lint GA
|
|
35
41
|
run: bun run lint-ga
|
|
@@ -3595,10 +3595,14 @@ function expectedSchemaUrlForTypedManifest(doc, apiVersion, kind) {
|
|
|
3595
3595
|
const group = apiVersion.slice(0, Math.max(0, slash))
|
|
3596
3596
|
const version = apiVersion.slice(slash + 1)
|
|
3597
3597
|
const kindPart = kindToSchemaFilePart(kind)
|
|
3598
|
-
const groupDash = group.replaceAll('.', '-')
|
|
3599
3598
|
|
|
3600
3599
|
if (YANNH_GROUPS.has(group)) {
|
|
3601
|
-
|
|
3600
|
+
// yannh для груп типу `*.k8s.io` / `*.apiserver.k8s.io` зберігає у назві файлу
|
|
3601
|
+
// лише перший сегмент `group` до першої крапки: `networking.k8s.io` → `networking`,
|
|
3602
|
+
// `rbac.authorization.k8s.io` → `rbac`, `flowcontrol.apiserver.k8s.io` → `flowcontrol`.
|
|
3603
|
+
// Для груп без крапок (`apps`, `batch`, `autoscaling`, `policy`) це збігається з group.
|
|
3604
|
+
const groupPart = group.split('.')[0]
|
|
3605
|
+
const url = `${YANNH_BASE}${kindPart}-${groupPart}-${version}.json`
|
|
3602
3606
|
return { expected: url, reason: 'вбудований API Kubernetes (yannh)' }
|
|
3603
3607
|
}
|
|
3604
3608
|
|
|
@@ -4284,7 +4288,7 @@ const NETWORK_POLICY_EGRESS_YAML = ` egress:
|
|
|
4284
4288
|
* @returns {string} вміст `networkpolicy.yaml`
|
|
4285
4289
|
*/
|
|
4286
4290
|
export function buildNetworkPolicyYaml(deployName, appLabel) {
|
|
4287
|
-
const schemaUrl = `${YANNH_BASE}networkpolicy-networking-
|
|
4291
|
+
const schemaUrl = `${YANNH_BASE}networkpolicy-networking-v1.json`
|
|
4288
4292
|
return `# yaml-language-server: $schema=${schemaUrl}
|
|
4289
4293
|
apiVersion: networking.k8s.io/v1
|
|
4290
4294
|
kind: NetworkPolicy
|
package/rules/k8s/k8s.mdc
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: K8s YAML — $schema (yaml-language-server); lint-k8s (kubeconform, kubescape); check-k8s
|
|
3
|
-
version: '1.
|
|
3
|
+
version: '1.35'
|
|
4
4
|
globs: "**/k8s/**/*.yaml"
|
|
5
5
|
alwaysApply: false
|
|
6
6
|
---
|
|
@@ -471,7 +471,7 @@ resources:
|
|
|
471
471
|
```
|
|
472
472
|
|
|
473
473
|
```yaml title="k8s/components/networkpolicy.yaml"
|
|
474
|
-
# yaml-language-server: $schema=https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.33.9-standalone-strict/networkpolicy-networking-
|
|
474
|
+
# yaml-language-server: $schema=https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.33.9-standalone-strict/networkpolicy-networking-v1.json
|
|
475
475
|
apiVersion: networking.k8s.io/v1
|
|
476
476
|
kind: NetworkPolicy
|
|
477
477
|
metadata:
|
|
@@ -712,8 +712,8 @@ patch: |-
|
|
|
712
712
|
```
|
|
713
713
|
|
|
714
714
|
3. **`apiVersion: group/version`** і **group** у **`YANNH_GROUPS`** у скрипті → yannh:
|
|
715
|
-
`https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/<PIN>/<kind>-<group
|
|
716
|
-
|
|
715
|
+
`https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/<PIN>/<kind>-<group-частина>-<version>.json`
|
|
716
|
+
де **`<group-частина>`** — **перший сегмент** `group` до першої крапки: для груп без крапок збігається з усією group (`apps/v1` + `Deployment` → `deployment-apps-v1.json`); для `*.k8s.io` / `*.apiserver.k8s.io` — лише префікс до `.k8s.io` (`networking.k8s.io/v1` + `Ingress` → `ingress-networking-v1.json`; `networking.k8s.io/v1` + `NetworkPolicy` → `networkpolicy-networking-v1.json`; `rbac.authorization.k8s.io/v1` + `ClusterRole` → `clusterrole-rbac-v1.json`; `flowcontrol.apiserver.k8s.io/v1` + `FlowSchema` → `flowschema-flowcontrol-v1.json`). У yannh **немає** файлів з фрагментом `-k8s-io-` у назві — патерн `<group-з-крапками-як-дефіси>` дає 404.
|
|
717
717
|
4. **Інакше** (CRD тощо) → [datreeio/CRDs-catalog](https://github.com/datreeio/CRDs-catalog). Типово для `$schema` у редакторі — **GitHub Pages**:
|
|
718
718
|
`https://datreeio.github.io/CRDs-catalog/<group>/<kind>_<version>.json`
|
|
719
719
|
(`<kind>` — лише літери та цифри в нижньому регістрі, без роздільників між CamelCase, як для yannh.)
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* `npm/mdc/text.mdc` (markdown-текст, не JSON/YAML);
|
|
13
13
|
* - складна валідація скрипта `lint-text` (cspell, markdownlint, v8r у трьох
|
|
14
14
|
* варіантах, run-shellcheck-text.mjs, обовʼязкові glob-и);
|
|
15
|
-
* - workflow `lint-text.yml` має крок `bun run lint-text
|
|
15
|
+
* - workflow `lint-text.yml` має крок `bun run lint-text` (структура — rego `text.lint_text`).
|
|
16
16
|
*
|
|
17
17
|
* **Що покрила Rego** (`npx \@nitra/cursor check`):
|
|
18
18
|
* - `npm/policy/text/oxfmtrc/` — обовʼязкові ключі `.oxfmtrc.json` і канонічні
|
package/rules/text/lint/lint.mjs
CHANGED
|
@@ -1,26 +1,134 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CLI-обгортка над канонічним `lint-text` (text.mdc):
|
|
2
|
+
* CLI-обгортка над канонічним `lint-text` (text.mdc): preflight на `shellcheck`, `patch`
|
|
3
|
+
* (для авто-фіксу shellcheck) і `dotenv-linter`; далі послідовно
|
|
3
4
|
* 1) `cspell .` — перевірка правопису з `@nitra/cspell-dict`;
|
|
4
5
|
* 2) `runShellcheckText()` — авто-фікс і фінальна перевірка `*.sh` через `shellcheck`;
|
|
5
6
|
* 3) `runDotenvLinter()` — авто-фікс і фінальна перевірка `.env*` через `dotenv-linter`;
|
|
6
7
|
* 4) `bunx markdownlint-cli2 --fix "**\/*.md" "**\/*.mdc"` — авто-фікс Markdown;
|
|
7
|
-
* 5) `runV8rWithGlobs()` — schema-валідація json/json5/yaml/yml/toml через v8r
|
|
8
|
+
* 5) `runV8rWithGlobs()` — schema-валідація json/json5/yaml/yml/toml через v8r.
|
|
9
|
+
*
|
|
10
|
+
* Без preflight локальний прогін може пройти cspell/markdownlint, а CI на ubuntu-latest
|
|
11
|
+
* (де shellcheck передвстановлений, але dotenv-linter — ні) падає на кроці dotenv-linter
|
|
12
|
+
* з неінформативним повідомленням. Preflight збирає всі відсутні бінарники до першого кроку.
|
|
8
13
|
*
|
|
9
14
|
* Перший ненульовий код з ланцюжка повертається як код виходу; наступні кроки не запускаються.
|
|
10
15
|
* Експортовано як `runLintTextCli` — використовується з `bin/n-cursor.js` як підкоманда `lint-text`.
|
|
11
16
|
*/
|
|
17
|
+
import { platform } from 'node:process'
|
|
18
|
+
|
|
12
19
|
import { runLintStep } from '../../../scripts/utils/run-lint-step.mjs'
|
|
20
|
+
import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
|
|
13
21
|
import { runDotenvLinter } from './run-dotenv-linter.mjs'
|
|
14
22
|
import { runShellcheckText } from './run-shellcheck.mjs'
|
|
15
23
|
import { runV8rWithGlobs } from './run-v8r.mjs'
|
|
16
24
|
|
|
17
25
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
26
|
+
* Опис залежності preflight-ом.
|
|
27
|
+
* @typedef {object} PreflightDep
|
|
28
|
+
* @property {string} bin ім'я виконуваного файлу
|
|
29
|
+
* @property {string[]} [winBins] альтернативні імена на Windows
|
|
30
|
+
* @property {string} explanation наслідки відсутності
|
|
31
|
+
* @property {string[]} install команди встановлення
|
|
32
|
+
* @property {string} successMsg повідомлення на pass-шлях
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/** @type {PreflightDep} */
|
|
36
|
+
const SHELLCHECK_PREFLIGHT = {
|
|
37
|
+
bin: 'shellcheck',
|
|
38
|
+
winBins: ['shellcheck.exe'],
|
|
39
|
+
explanation: [
|
|
40
|
+
'Без нього `runShellcheckText()` пропускає перевірку tracked `*.sh` — локально lint-text',
|
|
41
|
+
'може бути зеленим, а CI (shellcheck + patch) падає на тих самих скриптах.'
|
|
42
|
+
].join('\n '),
|
|
43
|
+
install: [
|
|
44
|
+
'macOS: brew install shellcheck',
|
|
45
|
+
'Debian/Ubuntu: sudo apt-get install -y shellcheck',
|
|
46
|
+
'Arch: sudo pacman -S shellcheck'
|
|
47
|
+
],
|
|
48
|
+
successMsg: '✅ shellcheck знайдено в PATH — lint-text перевірить *.sh'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** @type {PreflightDep} */
|
|
52
|
+
const PATCH_PREFLIGHT = {
|
|
53
|
+
bin: 'patch',
|
|
54
|
+
explanation: [
|
|
55
|
+
'Без `patch` не застосуються авто-виправлення shellcheck (`shellcheck -f diff` + `patch -p1`).'
|
|
56
|
+
].join('\n '),
|
|
57
|
+
install: [
|
|
58
|
+
'macOS: зазвичай уже є в системі',
|
|
59
|
+
'Debian/Ubuntu: sudo apt-get install -y patch'
|
|
60
|
+
],
|
|
61
|
+
successMsg: '✅ patch знайдено в PATH — shellcheck auto-fix працюватиме'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** @type {PreflightDep} */
|
|
65
|
+
const DOTENV_LINTER_PREFLIGHT = {
|
|
66
|
+
bin: 'dotenv-linter',
|
|
67
|
+
winBins: ['dotenv-linter.exe'],
|
|
68
|
+
explanation: [
|
|
69
|
+
'Без нього не виконається крок `.env*` у lint-text — локально cspell/markdownlint',
|
|
70
|
+
'пройдуть, а CI без Install dotenv-linter впаде неінформативно.'
|
|
71
|
+
].join('\n '),
|
|
72
|
+
install: [
|
|
73
|
+
'macOS: brew install dotenv-linter',
|
|
74
|
+
'Linux: curl -sSfL https://git.io/JLbXn | sh -s -- -b /usr/local/bin',
|
|
75
|
+
'cargo: cargo install dotenv-linter'
|
|
76
|
+
],
|
|
77
|
+
successMsg: '✅ dotenv-linter знайдено в PATH — lint-text перевірить .env*'
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {PreflightDep} dep
|
|
82
|
+
* @returns {string | null}
|
|
83
|
+
*/
|
|
84
|
+
function resolvePreflightBin(dep) {
|
|
85
|
+
if (platform === 'win32' && dep.winBins) {
|
|
86
|
+
for (const name of dep.winBins) {
|
|
87
|
+
const r = resolveCmd(name)
|
|
88
|
+
if (r) return r
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return resolveCmd(dep.bin)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @param {PreflightDep} dep
|
|
96
|
+
* @returns {void}
|
|
97
|
+
*/
|
|
98
|
+
function printPreflightMissingMessage(dep) {
|
|
99
|
+
console.error(`❌ ${dep.bin} не знайдено в PATH.`)
|
|
100
|
+
console.error(` ${dep.explanation}`)
|
|
101
|
+
console.error(' Встанови:')
|
|
102
|
+
for (const line of dep.install) {
|
|
103
|
+
console.error(` ${line}`)
|
|
104
|
+
}
|
|
105
|
+
console.error(' Деталі: text.mdc → секція про lint-text.')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @param {PreflightDep} dep
|
|
110
|
+
* @returns {boolean}
|
|
111
|
+
*/
|
|
112
|
+
function preflight(dep) {
|
|
113
|
+
if (resolvePreflightBin(dep)) {
|
|
114
|
+
console.log(dep.successMsg)
|
|
115
|
+
return true
|
|
116
|
+
}
|
|
117
|
+
printPreflightMissingMessage(dep)
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Виконує канонічний `lint-text` з preflight і ланцюжком cspell → shellcheck → dotenv → markdownlint → v8r.
|
|
21
123
|
* @returns {number} 0 — все OK, інакше — код першого кроку, що впав
|
|
22
124
|
*/
|
|
23
125
|
export function runLintTextCli() {
|
|
126
|
+
let preflightOk = true
|
|
127
|
+
for (const dep of [SHELLCHECK_PREFLIGHT, PATCH_PREFLIGHT, DOTENV_LINTER_PREFLIGHT]) {
|
|
128
|
+
if (!preflight(dep)) preflightOk = false
|
|
129
|
+
}
|
|
130
|
+
if (!preflightOk) return 1
|
|
131
|
+
|
|
24
132
|
const cspellCode = runLintStep('cspell', 'npx', ['cspell', '.'])
|
|
25
133
|
if (cspellCode !== 0) return cspellCode
|
|
26
134
|
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Перевірка `.github/workflows/lint-text.yml` (text.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Канон надходить через --data: { "template": { "snippet": ... } }
|
|
4
|
+
# Структура --data сформована з template/lint-text.yml.snippet.yml.
|
|
5
|
+
# Універсальні workflow-перевірки (checkout, permissions) — у `ga.workflow_common`.
|
|
6
|
+
package text.lint_text
|
|
7
|
+
|
|
8
|
+
import rego.v1
|
|
9
|
+
|
|
10
|
+
expected_name := data.template.snippet.name
|
|
11
|
+
|
|
12
|
+
expected_push_branches := {b | some b in data.template.snippet.on.push.branches}
|
|
13
|
+
|
|
14
|
+
expected_pr_branches := {b | some b in data.template.snippet.on.pull_request.branches}
|
|
15
|
+
|
|
16
|
+
expected_push_paths := {p | some p in data.template.snippet.on.push.paths}
|
|
17
|
+
|
|
18
|
+
expected_runs_on := data.template.snippet.jobs.text["runs-on"]
|
|
19
|
+
|
|
20
|
+
expected_perms := data.template.snippet.jobs.text.permissions
|
|
21
|
+
|
|
22
|
+
job := input.jobs.text
|
|
23
|
+
|
|
24
|
+
job_uses_set contains job.steps[_].uses
|
|
25
|
+
|
|
26
|
+
job_run_blob := concat("\n", [run |
|
|
27
|
+
run := job.steps[_].run
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
expected_uses_set contains u if {
|
|
31
|
+
some step in data.template.snippet.jobs.text.steps
|
|
32
|
+
u := object.get(step, "uses", "")
|
|
33
|
+
u != ""
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
expected_run_substrings contains r if {
|
|
37
|
+
some step in data.template.snippet.jobs.text.steps
|
|
38
|
+
r := object.get(step, "run", "")
|
|
39
|
+
r != ""
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
deny contains msg if {
|
|
43
|
+
input.name != expected_name
|
|
44
|
+
msg := sprintf("lint-text.yml: name має бути %q (text.mdc)", [expected_name])
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
deny contains msg if {
|
|
48
|
+
not branches_superset_of(input.on.push.branches, expected_push_branches)
|
|
49
|
+
msg := "lint-text.yml: on.push.branches має містити dev і main (text.mdc)"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
deny contains msg if {
|
|
53
|
+
not branches_superset_of(input.on.pull_request.branches, expected_pr_branches)
|
|
54
|
+
msg := "lint-text.yml: on.pull_request.branches має містити dev і main (text.mdc)"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
deny contains msg if {
|
|
58
|
+
not paths_superset_of(input.on.push.paths, expected_push_paths)
|
|
59
|
+
msg := "lint-text.yml: on.push.paths має містити очікувані glob-и (text.mdc)"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
deny contains msg if {
|
|
63
|
+
not job
|
|
64
|
+
msg := "lint-text.yml: jobs.text відсутній (text.mdc)"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
deny contains msg if {
|
|
68
|
+
job["runs-on"] != expected_runs_on
|
|
69
|
+
msg := sprintf("lint-text.yml: runs-on має бути %s (text.mdc)", [expected_runs_on])
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
deny contains msg if {
|
|
73
|
+
job.permissions.contents != expected_perms.contents
|
|
74
|
+
msg := sprintf("lint-text.yml: permissions.contents має бути %s (text.mdc)", [expected_perms.contents])
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
deny contains msg if {
|
|
78
|
+
count(job.steps) == 0
|
|
79
|
+
msg := "lint-text.yml: jobs.text.steps відсутні (text.mdc)"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
deny contains msg if {
|
|
83
|
+
some required_use in expected_uses_set
|
|
84
|
+
not required_use in job_uses_set
|
|
85
|
+
msg := sprintf("lint-text.yml: має бути uses: %s (text.mdc)", [required_use])
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
deny contains msg if {
|
|
89
|
+
some required_run in expected_run_substrings
|
|
90
|
+
not contains(job_run_blob, required_run)
|
|
91
|
+
msg := sprintf("lint-text.yml: жоден крок run не містить %q (text.mdc)", [required_run])
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
branches_superset_of(actual, expected) if {
|
|
95
|
+
expected & {b | some b in actual} == expected
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
paths_superset_of(actual, expected) if {
|
|
99
|
+
expected & {p | some p in actual} == expected
|
|
100
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
name: Lint Text
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- dev
|
|
7
|
+
- main
|
|
8
|
+
paths:
|
|
9
|
+
- '**/*.js'
|
|
10
|
+
- '**/*.ts'
|
|
11
|
+
- '**/*.vue'
|
|
12
|
+
- '**/*.html'
|
|
13
|
+
- '**/*.css'
|
|
14
|
+
- '**/*.scss'
|
|
15
|
+
- '**/*.less'
|
|
16
|
+
- '**/*.json'
|
|
17
|
+
- '**/*.jsonc'
|
|
18
|
+
- '**/*.yaml'
|
|
19
|
+
- '**/*.yml'
|
|
20
|
+
- '**/*.toml'
|
|
21
|
+
- '**/*.xml'
|
|
22
|
+
- '**/*.md'
|
|
23
|
+
- '**/*.mdc'
|
|
24
|
+
- '**/*.mdс'
|
|
25
|
+
- '**/*.txt'
|
|
26
|
+
- '**/*.go'
|
|
27
|
+
- '**/*.py'
|
|
28
|
+
- '**/*.php'
|
|
29
|
+
- '**/*.sh'
|
|
30
|
+
|
|
31
|
+
pull_request:
|
|
32
|
+
branches:
|
|
33
|
+
- dev
|
|
34
|
+
- main
|
|
35
|
+
|
|
36
|
+
concurrency:
|
|
37
|
+
group: ${{ github.ref }}-${{ github.workflow }}
|
|
38
|
+
cancel-in-progress: true
|
|
39
|
+
|
|
40
|
+
jobs:
|
|
41
|
+
text:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
permissions:
|
|
44
|
+
contents: read
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v6
|
|
47
|
+
with:
|
|
48
|
+
persist-credentials: false
|
|
49
|
+
|
|
50
|
+
- uses: ./.github/actions/setup-bun-deps
|
|
51
|
+
|
|
52
|
+
- name: Install shellcheck
|
|
53
|
+
run: sudo apt-get update && sudo apt-get install -y shellcheck
|
|
54
|
+
|
|
55
|
+
- name: Install dotenv-linter
|
|
56
|
+
run: >-
|
|
57
|
+
curl -sSfL https://git.io/JLbXn
|
|
58
|
+
| sh -s -- -b /usr/local/bin
|
|
59
|
+
|
|
60
|
+
- name: Lint text
|
|
61
|
+
run: bun run lint-text
|
package/rules/text/text.mdc
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Обробка та перевірка текстових файлів, oxfmt, cspell, shellcheck (sh), dotenv-linter (.env*), markdownlint-cli2, v8r, CI
|
|
3
3
|
alwaysApply: true
|
|
4
|
-
version: '1.
|
|
4
|
+
version: '1.29'
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
**oxfmt** (`.oxfmtrc.json`, редактор), **cspell**, **shellcheck** (tracked `*.sh` у `lint-text`), **[dotenv-linter](https://dotenv-linter.github.io/)** (`.env*` у `lint-text`), **markdownlint-cli2**, **[v8r](https://chris48s.github.io/v8r/)** ([Schema Store](https://www.schemastore.org/)), розширення **DavidAnson.vscode-markdownlint** / **timonwong.shellcheck**, workflow **`lint-text`**.
|
|
@@ -172,63 +172,9 @@ version: '1.28'
|
|
|
172
172
|
|
|
173
173
|
Додай workflow `.github/workflows/lint-text.yml`:
|
|
174
174
|
|
|
175
|
-
|
|
176
|
-
name: Lint Text
|
|
177
|
-
|
|
178
|
-
on:
|
|
179
|
-
push:
|
|
180
|
-
branches:
|
|
181
|
-
- dev
|
|
182
|
-
- main
|
|
183
|
-
paths:
|
|
184
|
-
- '**/*.js'
|
|
185
|
-
- '**/*.ts'
|
|
186
|
-
- '**/*.vue'
|
|
187
|
-
- '**/*.html'
|
|
188
|
-
- '**/*.css'
|
|
189
|
-
- '**/*.scss'
|
|
190
|
-
- '**/*.less'
|
|
191
|
-
- '**/*.json'
|
|
192
|
-
- '**/*.jsonc'
|
|
193
|
-
- '**/*.yaml'
|
|
194
|
-
- '**/*.yml'
|
|
195
|
-
- '**/*.toml'
|
|
196
|
-
- '**/*.xml'
|
|
197
|
-
- '**/*.md'
|
|
198
|
-
- '**/*.mdc'
|
|
199
|
-
- '**/*.mdс'
|
|
200
|
-
- '**/*.txt'
|
|
201
|
-
- '**/*.go'
|
|
202
|
-
- '**/*.py'
|
|
203
|
-
- '**/*.php'
|
|
204
|
-
- '**/*.sh'
|
|
205
|
-
|
|
206
|
-
pull_request:
|
|
207
|
-
branches:
|
|
208
|
-
- dev
|
|
209
|
-
- main
|
|
210
|
-
|
|
211
|
-
concurrency:
|
|
212
|
-
group: ${{ github.ref }}-${{ github.workflow }}
|
|
213
|
-
cancel-in-progress: true
|
|
214
|
-
|
|
215
|
-
jobs:
|
|
216
|
-
text:
|
|
217
|
-
runs-on: ubuntu-latest
|
|
218
|
-
permissions:
|
|
219
|
-
contents: read
|
|
220
|
-
steps:
|
|
221
|
-
- uses: actions/checkout@v6
|
|
222
|
-
with:
|
|
223
|
-
persist-credentials: false
|
|
224
|
-
|
|
225
|
-
- uses: ./.github/actions/setup-bun-deps
|
|
226
|
-
|
|
227
|
-
- name: Lint text
|
|
228
|
-
run: bun run lint-text
|
|
229
|
-
```
|
|
175
|
+
- Канон: [lint-text.yml.snippet.yml](./policy/lint_text/template/lint-text.yml.snippet.yml)
|
|
230
176
|
|
|
231
|
-
Перед **`./.github/actions/setup-bun-deps`** — **`actions/checkout@v6`** (див. **ga.mdc**). Composite: Node 24, Bun, кеш, `bun install --frozen-lockfile`.
|
|
177
|
+
Перед **`./.github/actions/setup-bun-deps`** — **`actions/checkout@v6`** (див. **ga.mdc**). Після composite — кроки **`Install shellcheck`** (apt) і **`Install dotenv-linter`** (curl), бо `n-cursor lint-text` вимагає обидва бінарники в PATH; на ubuntu-latest shellcheck часто вже є, dotenv-linter — ні. Composite: Node 24, Bun, кеш, `bun install --frozen-lockfile`.
|
|
232
178
|
|
|
233
179
|
Не дублюй окремий workflow з тими самими кроками cspell/markdownlint.
|
|
234
180
|
|