@nitra/cursor 1.13.15 → 1.13.25

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.
Files changed (62) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/package.json +1 -1
  3. package/rules/abie/abie.mdc +1 -4
  4. package/rules/abie/policy/clean_merged_ignore_branches/clean_merged_ignore_branches.rego +21 -40
  5. package/rules/abie/policy/clean_merged_ignore_branches/template/clean-merged-branch.yml.snippet.yml +9 -0
  6. package/rules/docker/docker.mdc +2 -50
  7. package/rules/docker/policy/lint_docker_yml/lint_docker_yml.rego +33 -47
  8. package/rules/docker/policy/lint_docker_yml/template/lint-docker.yml.snippet.yml +41 -0
  9. package/rules/docker/policy/package_json/package_json.rego +11 -28
  10. package/rules/docker/policy/package_json/template/package.json.snippet.json +1 -0
  11. package/rules/image-avif/policy/package_json/package_json.rego +21 -35
  12. package/rules/image-avif/policy/package_json/template/package.json.deny.json +5 -0
  13. package/rules/image-compress/image-compress.mdc +2 -8
  14. package/rules/image-compress/policy/package_json/package_json.rego +24 -65
  15. package/rules/image-compress/policy/package_json/template/package.json.contains.json +5 -0
  16. package/rules/image-compress/policy/package_json/template/package.json.deny.json +8 -0
  17. package/rules/js-bun-db/policy/package_json/package_json.rego +8 -21
  18. package/rules/js-bun-db/policy/package_json/template/package.json.deny.json +7 -0
  19. package/rules/js-bun-redis/policy/package_json/package_json.rego +8 -30
  20. package/rules/js-bun-redis/policy/package_json/template/package.json.deny.json +12 -0
  21. package/rules/js-lint/policy/jscpd/jscpd.rego +29 -23
  22. package/rules/js-lint/policy/jscpd/template/.jscpd.json.snippet.json +6 -0
  23. package/rules/js-lint/policy/lint_js_yml/lint_js_yml.rego +39 -47
  24. package/rules/js-lint/policy/lint_js_yml/template/lint-js.yml.snippet.yml +44 -0
  25. package/rules/js-lint/policy/package_json/package_json.rego +22 -42
  26. package/rules/js-lint/policy/package_json/template/package.json.snippet.json +6 -0
  27. package/rules/js-lint/policy/vscode_extensions/template/extensions.json.snippet.json +7 -0
  28. package/rules/js-lint/policy/vscode_extensions/vscode_extensions.rego +4 -17
  29. package/rules/js-run/policy/configmap/configmap.rego +11 -35
  30. package/rules/js-run/policy/configmap/template/configmap.yaml.contains.yml +4 -0
  31. package/rules/js-run/policy/jsconfig/jsconfig.rego +39 -46
  32. package/rules/js-run/policy/jsconfig/template/jsconfig.json.snippet.json +10 -0
  33. package/rules/js-run/policy/package_json/package_json.rego +10 -21
  34. package/rules/js-run/policy/package_json/template/package.json.deny.json +10 -0
  35. package/rules/php/php.mdc +2 -56
  36. package/rules/php/policy/lint_php_yml/lint_php_yml.rego +15 -13
  37. package/rules/php/policy/lint_php_yml/template/lint-php.yml.snippet.yml +47 -0
  38. package/rules/php/policy/package_json/package_json.rego +9 -12
  39. package/rules/php/policy/package_json/template/package.json.contains.json +5 -0
  40. package/rules/style-lint/policy/lint_style_yml/lint_style_yml.rego +14 -16
  41. package/rules/style-lint/policy/lint_style_yml/template/lint-style.yml.snippet.yml +39 -0
  42. package/rules/style-lint/policy/package_json/package_json.rego +22 -30
  43. package/rules/style-lint/policy/package_json/template/package.json.contains.json +5 -0
  44. package/rules/style-lint/policy/package_json/template/package.json.snippet.json +5 -0
  45. package/rules/style-lint/policy/vscode_extensions/template/extensions.json.snippet.json +1 -0
  46. package/rules/style-lint/policy/vscode_extensions/vscode_extensions.rego +5 -15
  47. package/rules/style-lint/policy/vscode_settings/template/settings.json.snippet.json +5 -0
  48. package/rules/style-lint/policy/vscode_settings/vscode_settings.rego +7 -16
  49. package/rules/text/policy/cspell/cspell.rego +39 -59
  50. package/rules/text/policy/cspell/template/.cspell.json.contains.json +3 -0
  51. package/rules/text/policy/cspell/template/.cspell.json.deny.json +5 -0
  52. package/rules/text/policy/cspell/template/.cspell.json.snippet.json +12 -0
  53. package/rules/text/policy/markdownlint/markdownlint.rego +37 -50
  54. package/rules/text/policy/markdownlint/template/.markdownlint-cli2.jsonc.snippet.jsonc +11 -0
  55. package/rules/text/policy/oxfmtrc/oxfmtrc.rego +23 -58
  56. package/rules/text/policy/oxfmtrc/template/.oxfmtrc.json.snippet.json +12 -0
  57. package/rules/text/policy/package_json/package_json.rego +14 -52
  58. package/rules/text/policy/package_json/template/package.json.deny.json +15 -0
  59. package/rules/text/policy/vscode_extensions/template/extensions.json.snippet.json +7 -0
  60. package/rules/text/policy/vscode_extensions/vscode_extensions.rego +4 -28
  61. package/rules/text/policy/vscode_settings/template/settings.json.snippet.json +9 -0
  62. package/rules/text/policy/vscode_settings/vscode_settings.rego +20 -42
@@ -1,25 +1,12 @@
1
1
  # Перевірка `.vscode/extensions.json` для js-lint (js-lint.mdc).
2
2
  #
3
- # Canonical: у `recommendations` мають бути ESLint, GitHub Actions і Oxlint.
4
- # Канон задає мінімум — додаткові рекомендації від інших правил дозволені.
5
- #
6
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
7
- # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
8
- # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
9
4
  package js_lint.vscode_extensions
10
5
 
11
6
  import rego.v1
12
7
 
13
- required_extensions := {
14
- "dbaeumer.vscode-eslint",
15
- "github.vscode-github-actions",
16
- "oxc.oxc-vscode",
17
- }
18
-
19
- recommendations_set := {r | some r in object.get(input, "recommendations", [])}
20
-
21
8
  deny contains msg if {
22
- some required in required_extensions
23
- not required in recommendations_set
24
- msg := sprintf(".vscode/extensions.json: recommendations має містити %q (js-lint.mdc)", [required])
9
+ some rec in data.template.snippet.recommendations
10
+ not rec in {r | some r in object.get(input, "recommendations", [])}
11
+ msg := sprintf(".vscode/extensions.json: recommendations має містити %q (js-lint.mdc)", [rec])
25
12
  }
@@ -1,45 +1,21 @@
1
- # Порт перевірки `k8s/base/configmap.yaml` з `npm/scripts/check-js-run.mjs`
2
- # (js-run.mdc) — `OTEL_RESOURCE_ATTRIBUTES` має містити `service.name=` і
3
- # `service.namespace=`.
1
+ # Перевірка ConfigMap (js-run.mdc).
4
2
  #
5
- # Запуск (локально):
6
- # conftest test path/to/k8s/base/configmap.yaml -p npm/policy/js_run \
7
- # --namespace js_run.configmap
8
- #
9
- # Відповідність імені ConfigMap імені Deployment (cross-file) — у JS і `check-k8s.mjs`.
10
- #
11
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
12
- # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
13
- # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
3
+ # Канон надходить через --data: { "template": { "contains": ... } }
4
+ # Структура --data сформована з template/configmap.yaml.contains.yml.
5
+ # Контекст: kind=ConfigMap; OTEL_RESOURCE_ATTRIBUTES має містити кожен substring
6
+ # зі snippet (`service.name=`, `service.namespace=`).
14
7
  package js_run.configmap
15
8
 
16
9
  import rego.v1
17
10
 
18
- # Шаблони повідомлень — через `concat` для regal style/line-length.
19
- otel_service_name_template := concat(" ", [
20
- "ConfigMap %q: OTEL_RESOURCE_ATTRIBUTES має містити",
21
- "`service.name=` (js-run.mdc)",
22
- ])
23
-
24
- otel_service_namespace_template := concat(" ", [
25
- "ConfigMap %q: OTEL_RESOURCE_ATTRIBUTES має містити",
26
- "`service.namespace=` (js-run.mdc)",
27
- ])
28
-
29
- deny contains msg if {
30
- input.kind == "ConfigMap"
31
- otel := object.get(object.get(input, "data", {}), "OTEL_RESOURCE_ATTRIBUTES", "")
32
- otel != ""
33
- not contains(otel, "service.name=")
34
- msg := sprintf(otel_service_name_template, [cm_name])
35
- }
36
-
37
11
  deny contains msg if {
38
12
  input.kind == "ConfigMap"
39
- otel := object.get(object.get(input, "data", {}), "OTEL_RESOURCE_ATTRIBUTES", "")
40
- otel != ""
41
- not contains(otel, "service.namespace=")
42
- msg := sprintf(otel_service_namespace_template, [cm_name])
13
+ some field, needles in data.template.contains.data
14
+ actual := object.get(object.get(input, "data", {}), field, "")
15
+ actual != ""
16
+ some needle in needles
17
+ not contains(actual, needle)
18
+ msg := sprintf("ConfigMap %q: %s має містити %q (js-run.mdc)", [cm_name, field, needle])
43
19
  }
44
20
 
45
21
  cm_name := object.get(object.get(input, "metadata", {}), "name", "?")
@@ -0,0 +1,4 @@
1
+ data:
2
+ OTEL_RESOURCE_ATTRIBUTES:
3
+ - 'service.name='
4
+ - 'service.namespace='
@@ -1,66 +1,59 @@
1
- # Порт перевірки `jsconfig.json` з `npm/scripts/check-js-run.mjs` (js-run.mdc).
1
+ # Перевірка `jsconfig.json` (js-run.mdc).
2
2
  #
3
- # Запуск (локально, у backend-пакеті з каталогом `src/`):
4
- # conftest test path/to/jsconfig.json -p npm/policy/js_run \
5
- # --namespace js_run.jsconfig
6
- #
7
- # Перевіряє: `compilerOptions.{lib, module, moduleResolution, target, checkJs}` і
8
- # `include` мають канонічні значення (js-run.mdc).
9
- #
10
- # FS-перевірка (наявність каталогу `src/` у пакеті, наявність самого `jsconfig.json`)
11
- # і вибір файлу-кандидата — у JS.
12
- #
13
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
14
- # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
15
- # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/jsconfig.json.snippet.yml.
5
+ # Walker: для кожного leaf у template's snippet порівнюємо input[path].
6
+ # Для масивів — equality (не subset-of, бо в jsconfig потрібен точний набір).
16
7
  package js_run.jsconfig
17
8
 
18
9
  import rego.v1
19
10
 
20
- # ── deny: compilerOptions ──────────────────────────────────────────────────
21
-
11
+ # Generic 2-level walker: section → key → expected (для compilerOptions).
22
12
  deny contains msg if {
23
- co := object.get(input, "compilerOptions", {})
24
- not is_array(object.get(co, "lib", null))
25
- msg := "jsconfig.json: compilerOptions.lib має бути [\"esnext\"] (js-run.mdc)"
13
+ some section, expected_inner in data.template.snippet
14
+ is_object(expected_inner)
15
+ inner := object.get(input, section, {})
16
+ is_object(inner)
17
+ some leaf_key, expected_value in expected_inner
18
+ actual := object.get(inner, leaf_key, null)
19
+ not values_match(actual, expected_value)
20
+ msg := sprintf("jsconfig.json: %s.%s має бути %v (js-run.mdc)", [section, leaf_key, expected_value])
26
21
  }
27
22
 
23
+ # Section відсутня або не обʼєкт.
28
24
  deny contains msg if {
29
- co := object.get(input, "compilerOptions", {})
30
- is_array(co.lib)
31
- {l | some l in co.lib} != {"esnext"}
32
- msg := "jsconfig.json: compilerOptions.lib має бути [\"esnext\"] (js-run.mdc)"
25
+ some section, expected_inner in data.template.snippet
26
+ is_object(expected_inner)
27
+ raw := object.get(input, section, null)
28
+ not is_object(raw)
29
+ msg := sprintf("jsconfig.json: відсутній обʼєкт %s (js-run.mdc)", [section])
33
30
  }
34
31
 
32
+ # Top-level масив (наприклад include) — порівнюємо як множину.
35
33
  deny contains msg if {
36
- object.get(object.get(input, "compilerOptions", {}), "module", null) != "NodeNext"
37
- msg := "jsconfig.json: compilerOptions.module має бути \"NodeNext\" (js-run.mdc)"
34
+ some field, expected_array in data.template.snippet
35
+ is_array(expected_array)
36
+ actual := object.get(input, field, null)
37
+ not is_array(actual)
38
+ msg := sprintf("jsconfig.json: %s має бути масив %v (js-run.mdc)", [field, expected_array])
38
39
  }
39
40
 
40
41
  deny contains msg if {
41
- object.get(object.get(input, "compilerOptions", {}), "moduleResolution", null) != "NodeNext"
42
- msg := "jsconfig.json: compilerOptions.moduleResolution має бути \"NodeNext\" (js-run.mdc)"
42
+ some field, expected_array in data.template.snippet
43
+ is_array(expected_array)
44
+ is_array(object.get(input, field, null))
45
+ {x | some x in input[field]} != {x | some x in expected_array}
46
+ msg := sprintf("jsconfig.json: %s має бути %v (js-run.mdc)", [field, expected_array])
43
47
  }
44
48
 
45
- deny contains msg if {
46
- object.get(object.get(input, "compilerOptions", {}), "target", null) != "esnext"
47
- msg := "jsconfig.json: compilerOptions.target має бути \"esnext\" (js-run.mdc)"
49
+ # Helper: leaf-level value match, з підтримкою масивів як множин.
50
+ values_match(actual, expected) if {
51
+ not is_array(expected)
52
+ actual == expected
48
53
  }
49
54
 
50
- deny contains msg if {
51
- object.get(object.get(input, "compilerOptions", {}), "checkJs", null) != false
52
- msg := "jsconfig.json: compilerOptions.checkJs має бути false (js-run.mdc)"
53
- }
54
-
55
- # ── deny: include ──────────────────────────────────────────────────────────
56
-
57
- deny contains msg if {
58
- not is_array(object.get(input, "include", null))
59
- msg := "jsconfig.json: include має бути [\"src/**/*\"] (js-run.mdc)"
60
- }
61
-
62
- deny contains msg if {
63
- is_array(input.include)
64
- {p | some p in input.include} != {"src/**/*"}
65
- msg := "jsconfig.json: include має бути [\"src/**/*\"] (js-run.mdc)"
55
+ values_match(actual, expected) if {
56
+ is_array(expected)
57
+ is_array(actual)
58
+ {x | some x in actual} == {x | some x in expected}
66
59
  }
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["esnext"],
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "target": "esnext",
7
+ "checkJs": false
8
+ },
9
+ "include": ["src/**/*"]
10
+ }
@@ -1,31 +1,20 @@
1
- # Порт перевірки залежностей `package.json` з `npm/scripts/check-js-run.mjs`
2
- # (js-run.mdc) — заборона `bunyan` / `@nitra/bunyan`.
1
+ # Перевірка `package.json` (js-run.mdc).
3
2
  #
4
- # Запуск (локально, для будь-якого `package.json` у дереві):
5
- # conftest test path/to/package.json -p npm/policy/js_run \
6
- # --namespace js_run.package_json
7
- #
8
- # AST-скан коду на імпорти `bunyan` / `process.env` без `checkEnv`,
9
- # `new Promise(resolve => setTimeout(resolve, ...))`, обмеження `#conn/*`-аліасів —
10
- # у JS (потребує парсингу `.js` / `.ts` через oxc-parser).
11
- #
12
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
13
- # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
14
- # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
3
+ # Канон надходить через --data: { "template": { "deny": ... } }
4
+ # Структура --data сформована з template/package.json.deny.json.
5
+ # AST-скан коду (`bunyan`/`process.env`/`#conn/*`) — у JS.
15
6
  package js_run.package_json
16
7
 
17
8
  import rego.v1
18
9
 
19
- forbidden_packages := {"bunyan", "@nitra/bunyan"}
20
-
21
10
  deny contains msg if {
22
- some pkg_name in forbidden_packages
23
- pkg_name in object.keys(object.get(input, "dependencies", {}))
24
- msg := sprintf("dependencies містить %q — використовуй стандартні логери (js-run.mdc)", [pkg_name])
11
+ some pkg, reason in data.template.deny.dependencies
12
+ pkg in object.keys(object.get(input, "dependencies", {}))
13
+ msg := sprintf("dependencies.%s %s", [pkg, reason])
25
14
  }
26
15
 
27
16
  deny contains msg if {
28
- some pkg_name in forbidden_packages
29
- pkg_name in object.keys(object.get(input, "devDependencies", {}))
30
- msg := sprintf("devDependencies містить %q — використовуй стандартні логери (js-run.mdc)", [pkg_name])
17
+ some pkg, reason in data.template.deny.devDependencies
18
+ pkg in object.keys(object.get(input, "devDependencies", {}))
19
+ msg := sprintf("devDependencies.%s %s", [pkg, reason])
31
20
  }
@@ -0,0 +1,10 @@
1
+ {
2
+ "dependencies": {
3
+ "bunyan": "використовуй стандартні логери (js-run.mdc)",
4
+ "@nitra/bunyan": "використовуй стандартні логери (js-run.mdc)"
5
+ },
6
+ "devDependencies": {
7
+ "bunyan": "використовуй стандартні логери (js-run.mdc)",
8
+ "@nitra/bunyan": "використовуй стандартні логери (js-run.mdc)"
9
+ }
10
+ }
package/rules/php/php.mdc CHANGED
@@ -65,13 +65,7 @@ composer audit
65
65
 
66
66
  `composer`-інструмененти не мають єдиного CLI, який сам обходить репозиторій, тому `lint-php` зручно делегувати у JS-скрипт-обгортку (як `lint-docker`, `lint-k8s`).
67
67
 
68
- ```json title="package.json"
69
- {
70
- "scripts": {
71
- "lint-php": "bun ./npm/scripts/run-php.mjs"
72
- }
73
- }
74
- ```
68
+ - Канон `package.json#scripts.lint-php` (substring requirement): [package.json.contains.json](./policy/package_json/template/package.json.contains.json)
75
69
 
76
70
  Скрипт `run-php.mjs`:
77
71
 
@@ -82,52 +76,4 @@ composer audit
82
76
 
83
77
  ## CI: `.github/workflows/lint-php.yml`
84
78
 
85
- ```yaml title=".github/workflows/lint-php.yml"
86
- name: Lint PHP
87
-
88
- on:
89
- push:
90
- branches:
91
- - dev
92
- - main
93
- paths:
94
- - '**/*.php'
95
- - 'composer.json'
96
- - 'composer.lock'
97
- - 'phpstan.neon'
98
- - 'phpstan.neon.dist'
99
- - 'psalm.xml'
100
- - '.github/workflows/lint-php.yml'
101
-
102
- pull_request:
103
- branches:
104
- - dev
105
- - main
106
-
107
- concurrency:
108
- group: ${{ github.ref }}-${{ github.workflow }}
109
- cancel-in-progress: true
110
-
111
- jobs:
112
- php:
113
- runs-on: ubuntu-latest
114
- permissions:
115
- contents: read
116
- steps:
117
- - uses: actions/checkout@v6
118
- with:
119
- persist-credentials: false
120
-
121
- - uses: ./.github/actions/setup-bun-deps
122
-
123
- - name: Install PHP
124
- uses: shivammathur/setup-php@v2
125
- with:
126
- php-version: '8.5'
127
-
128
- - name: Install Composer dependencies
129
- run: composer install --no-interaction --no-progress --prefer-dist
130
-
131
- - name: Lint PHP
132
- run: bun run lint-php
133
- ```
79
+ - Канон: [lint-php.yml.snippet.yml](./policy/lint_php_yml/template/lint-php.yml.snippet.yml)
@@ -1,19 +1,20 @@
1
- # Порт перевірки `lint-php.yml` з `npm/scripts/check-php.mjs` (php.mdc).
1
+ # Перевірка `lint-php.yml` (php.mdc).
2
2
  #
3
- # Запуск (локально):
4
- # conftest test .github/workflows/lint-php.yml -p npm/policy/php \
5
- # --namespace php.lint_php_yml
6
- #
7
- # Перевіряє: хоча б один крок `run` містить `bun run lint-php`. Універсальні
8
- # workflow-перевірки — у `ga.workflow_common`.
9
- #
10
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
11
- # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
12
- # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/lint-php.yml.snippet.yml.
5
+ # Маркер `run:` (bun run lint-php) збирається з template's php-job steps.
6
+ # Універсальні workflow-перевірки — у `ga.workflow_common`.
13
7
  package php.lint_php_yml
14
8
 
15
9
  import rego.v1
16
10
 
11
+ # Очікуваний `run:` маркер — конкатенація всіх run-блоків з template.
12
+ expected_run_blob := concat("\n", [r |
13
+ some step in data.template.snippet.jobs.php.steps
14
+ r := object.get(step, "run", "")
15
+ r != ""
16
+ ])
17
+
17
18
  all_run_text := concat("\n", [run_text |
18
19
  some job in object.get(input, "jobs", {})
19
20
  some step in object.get(job, "steps", [])
@@ -21,8 +22,9 @@ all_run_text := concat("\n", [run_text |
21
22
  ])
22
23
 
23
24
  deny contains msg if {
24
- not contains(all_run_text, "bun run lint-php")
25
- msg := "lint-php.yml: жоден крок run не містить `bun run lint-php` (php.mdc)"
25
+ expected_run_blob != ""
26
+ not contains(all_run_text, expected_run_blob)
27
+ msg := sprintf("lint-php.yml: жоден крок run не містить %q (php.mdc)", [expected_run_blob])
26
28
  }
27
29
 
28
30
  step_run_to_text(step) := step.run if is_string(step.run)
@@ -0,0 +1,47 @@
1
+ name: Lint PHP
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - dev
7
+ - main
8
+ paths:
9
+ - '**/*.php'
10
+ - 'composer.json'
11
+ - 'composer.lock'
12
+ - 'phpstan.neon'
13
+ - 'phpstan.neon.dist'
14
+ - 'psalm.xml'
15
+ - '.github/workflows/lint-php.yml'
16
+
17
+ pull_request:
18
+ branches:
19
+ - dev
20
+ - main
21
+
22
+ concurrency:
23
+ group: ${{ github.ref }}-${{ github.workflow }}
24
+ cancel-in-progress: true
25
+
26
+ jobs:
27
+ php:
28
+ runs-on: ubuntu-latest
29
+ permissions:
30
+ contents: read
31
+ steps:
32
+ - uses: actions/checkout@v6
33
+ with:
34
+ persist-credentials: false
35
+
36
+ - uses: ./.github/actions/setup-bun-deps
37
+
38
+ - name: Install PHP
39
+ uses: shivammathur/setup-php@v2
40
+ with:
41
+ php-version: '8.5'
42
+
43
+ - name: Install Composer dependencies
44
+ run: composer install --no-interaction --no-progress --prefer-dist
45
+
46
+ - name: Lint PHP
47
+ run: bun run lint-php
@@ -1,19 +1,16 @@
1
- # Порт перевірки `package.json` з `npm/scripts/check-php.mjs` (php.mdc).
1
+ # Перевірка `package.json` (php.mdc).
2
2
  #
3
- # Запуск (локально):
4
- # conftest test package.json -p npm/policy/php --namespace php.package_json
5
- #
6
- # Перевіряє: наявність скрипта `lint-php`. FS-перевірки (`composer.json`, наявність
7
- # `package.json` як такого) — у JS.
8
- #
9
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
10
- # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
11
- # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
3
+ # Канон надходить через --data: { "template": { "contains": ... } }
4
+ # Структура --data сформована з template/package.json.contains.json.
5
+ # FS-перевірки (`composer.json`, наявність `package.json` як такого) — у JS.
12
6
  package php.package_json
13
7
 
14
8
  import rego.v1
15
9
 
16
10
  deny contains msg if {
17
- not object.get(object.get(input, "scripts", {}), "lint-php", false)
18
- msg := "package.json: додай скрипт \"lint-php\" (php.mdc)"
11
+ some script_name, needles in data.template.contains.scripts
12
+ actual := object.get(object.get(input, "scripts", {}), script_name, "")
13
+ some needle in needles
14
+ not contains(actual, needle)
15
+ msg := sprintf("package.json: scripts.%s має містити %q (php.mdc)", [script_name, needle])
19
16
  }
@@ -0,0 +1,5 @@
1
+ {
2
+ "scripts": {
3
+ "lint-php": ["bun"]
4
+ }
5
+ }
@@ -1,21 +1,19 @@
1
- # Порт перевірки `lint-style.yml` з `npm/scripts/check-style-lint.mjs` (style-lint.mdc).
1
+ # Перевірка `lint-style.yml` (style-lint.mdc).
2
2
  #
3
- # Запуск (локально):
4
- # conftest test .github/workflows/lint-style.yml -p npm/policy/style_lint \
5
- # --namespace style_lint.lint_style_yml
6
- #
7
- # Перевіряє: хоча б один крок `run` містить `npx stylelint` (саме через npx, не
8
- # `bun run lint-style`). Універсальні workflow-перевірки (concurrency, заборонені
9
- # setup-bun/cache/install) — у `ga.workflow_common`.
10
- #
11
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
12
- # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
13
- # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/lint-style.yml.snippet.yml.
5
+ # Маркер `run:` (npx stylelint substring) збирається з template's stylelint-job steps.
6
+ # Універсальні workflow-перевірки — у `ga.workflow_common`.
14
7
  package style_lint.lint_style_yml
15
8
 
16
9
  import rego.v1
17
10
 
18
- # Усі тексти `run:` зі steps усіх jobs, склеєні в один blob — для substring-перевірки.
11
+ expected_run_blob := concat("\n", [r |
12
+ some step in data.template.snippet.jobs.stylelint.steps
13
+ r := object.get(step, "run", "")
14
+ r != ""
15
+ ])
16
+
19
17
  all_run_text := concat("\n", [run_text |
20
18
  some job in object.get(input, "jobs", {})
21
19
  some step in object.get(job, "steps", [])
@@ -23,11 +21,11 @@ all_run_text := concat("\n", [run_text |
23
21
  ])
24
22
 
25
23
  deny contains msg if {
26
- not contains(all_run_text, "npx stylelint")
27
- msg := "lint-style.yml: жоден крок run не містить `npx stylelint` (style-lint.mdc)"
24
+ expected_run_blob != ""
25
+ not contains(all_run_text, expected_run_blob)
26
+ msg := sprintf("lint-style.yml: жоден крок run не містить %q (style-lint.mdc)", [expected_run_blob])
28
27
  }
29
28
 
30
- # Текст `run:` як один рядок: підтримує string і array форми (YAML).
31
29
  step_run_to_text(step) := step.run if is_string(step.run)
32
30
 
33
31
  else := concat("\n", [s | some s in step.run]) if is_array(step.run)
@@ -0,0 +1,39 @@
1
+ name: StyleLint
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - dev
7
+ - main
8
+ paths:
9
+ - '**/*.css'
10
+ - '**/*.scss'
11
+ - '**/*.vue'
12
+
13
+ pull_request:
14
+ branches:
15
+ - dev
16
+ - main
17
+ paths:
18
+ - '**/*.css'
19
+ - '**/*.scss'
20
+ - '**/*.vue'
21
+
22
+ concurrency:
23
+ group: ${{ github.ref }}-${{ github.workflow }}
24
+ cancel-in-progress: true
25
+
26
+ jobs:
27
+ stylelint:
28
+ runs-on: ubuntu-latest
29
+ permissions:
30
+ contents: read
31
+ steps:
32
+ - uses: actions/checkout@v6
33
+ with:
34
+ persist-credentials: false
35
+
36
+ - uses: ./.github/actions/setup-bun-deps
37
+
38
+ - name: StyleLint
39
+ run: npx stylelint '**/*.{css,scss,vue}' --fix
@@ -1,49 +1,41 @@
1
- # Порт перевірок `package.json` з `npm/scripts/check-style-lint.mjs` (style-lint.mdc).
1
+ # Порт перевірок `package.json` (style-lint.mdc).
2
2
  #
3
- # Запуск (локально):
4
- # conftest test package.json -p npm/policy/style_lint --namespace style_lint.package_json
3
+ # Канон надходить через --data: { "template": { "contains": ..., "snippet": ... } }
4
+ # Структура --data сформована з template/package.json.{contains,snippet}.json.
5
+ # FS-альтернативи (`.stylelintrc.*` файли) + `.stylelintignore` — у JS.
5
6
  #
6
- # Перевіряє: наявність скрипта `lint-style` з `npx stylelint`, `@nitra/stylelint-config`
7
- # у `devDependencies`, поле `stylelint.extends == "@nitra/stylelint-config"`. FS-частина
8
- # (зовнішні `.stylelintrc.*` як альтернатива полю; `.stylelintignore`) лишається у JS.
9
- #
10
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
11
- # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
12
- # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
7
+ # Логіка, що ЛИШАЄТЬСЯ у rego (inverse не виноситься у template):
8
+ # - `@nitra/stylelint-config` має бути у devDependencies (presence-check).
13
9
  package style_lint.package_json
14
10
 
15
11
  import rego.v1
16
12
 
17
- # ── deny: lint-style скрипт ───────────────────────────────────────────────
13
+ # ── deny: substring requirements у scripts (contains) ────────────────────
18
14
 
19
15
  deny contains msg if {
20
- not object.get(object.get(input, "scripts", {}), "lint-style", false)
21
- msg := "package.json не містить скрипт \"lint-style\" (style-lint.mdc)"
16
+ some script_name, needles in data.template.contains.scripts
17
+ actual := object.get(object.get(input, "scripts", {}), script_name, "")
18
+ some needle in needles
19
+ not contains(actual, needle)
20
+ msg := sprintf("package.json: scripts.%s має містити %q (style-lint.mdc)", [script_name, needle])
22
21
  }
23
22
 
23
+ # ── deny: 2-level snippet walker (для stylelint.extends, якщо поле є) ────
24
+
24
25
  deny contains msg if {
25
- lint_style := object.get(object.get(input, "scripts", {}), "lint-style", "")
26
- lint_style != ""
27
- not contains(lint_style, "npx stylelint")
28
- msg := sprintf("lint-style має викликати stylelint через npx (зараз: %q) (style-lint.mdc)", [lint_style])
26
+ some section, expected_inner in data.template.snippet
27
+ cfg := object.get(input, section, null)
28
+ is_object(cfg)
29
+ some leaf_key, expected_value in expected_inner
30
+ actual := object.get(cfg, leaf_key, null)
31
+ actual != expected_value
32
+ msg := sprintf("package.json: %s.%s має бути %q (style-lint.mdc)", [section, leaf_key, expected_value])
29
33
  }
30
34
 
31
- # ── deny: @nitra/stylelint-config у devDependencies ───────────────────────
35
+ # ── deny: @nitra/stylelint-config у devDependencies (inverse) ────────────
32
36
 
33
37
  deny contains msg if {
34
38
  dev := object.get(input, "devDependencies", {})
35
39
  not "@nitra/stylelint-config" in object.keys(dev)
36
40
  msg := "@nitra/stylelint-config відсутній — bun add -d @nitra/stylelint-config (style-lint.mdc)"
37
41
  }
38
-
39
- # ── deny: поле stylelint.extends = "@nitra/stylelint-config" ──────────────
40
- #
41
- # JS-перевірка дозволяє альтернативу: окремий файл `.stylelintrc.*`. Цю частину
42
- # перевіряємо в JS (FS-вибірка); тут — лише структурна валідація поля, якщо воно є.
43
-
44
- deny contains msg if {
45
- cfg := object.get(input, "stylelint", null)
46
- is_object(cfg)
47
- object.get(cfg, "extends", null) != "@nitra/stylelint-config"
48
- msg := "package.json: stylelint.extends має бути \"@nitra/stylelint-config\" (style-lint.mdc)"
49
- }
@@ -0,0 +1,5 @@
1
+ {
2
+ "scripts": {
3
+ "lint-style": ["npx stylelint"]
4
+ }
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "stylelint": {
3
+ "extends": "@nitra/stylelint-config"
4
+ }
5
+ }
@@ -0,0 +1 @@
1
+ { "recommendations": ["stylelint.vscode-stylelint"] }