@nitra/cursor 1.8.71 → 1.8.73
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/mdc/k8s.mdc +24 -0
- package/package.json +1 -1
- package/scripts/check-abie.mjs +58 -64
- package/scripts/check-bun.mjs +4 -7
- package/scripts/check-docker.mjs +5 -8
- package/scripts/check-ga.mjs +5 -8
- package/scripts/check-js-format.mjs +4 -7
- package/scripts/check-js-lint.mjs +4 -7
- package/scripts/check-js-pino.mjs +10 -12
- package/scripts/check-k8s.mjs +5 -8
- package/scripts/check-nginx-default-tpl.mjs +5 -8
- package/scripts/check-npm-module.mjs +4 -7
- package/scripts/check-style-lint.mjs +4 -7
- package/scripts/check-text.mjs +4 -7
- package/scripts/check-vue.mjs +17 -19
- package/scripts/run-docker.mjs +5 -8
- package/scripts/utils/check-reporter.mjs +27 -0
package/mdc/k8s.mdc
CHANGED
|
@@ -206,6 +206,30 @@ patches:
|
|
|
206
206
|
value: dev
|
|
207
207
|
```
|
|
208
208
|
|
|
209
|
+
4. Якщо в kustomization.yaml є remove разом з add на однаковий path:
|
|
210
|
+
|
|
211
|
+
```yaml title="overlay/kustomization.yaml (фрагмент)"
|
|
212
|
+
- op: remove
|
|
213
|
+
path: /spec/template/spec/nodeSelector
|
|
214
|
+
- op: add
|
|
215
|
+
path: /spec/template/spec/nodeSelector
|
|
216
|
+
value:
|
|
217
|
+
preem: "false"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
заміняй на replace:
|
|
221
|
+
|
|
222
|
+
```yaml title="overlay/kustomization.yaml (фрагмент)"
|
|
223
|
+
- target:
|
|
224
|
+
kind: Deployment
|
|
225
|
+
name: x
|
|
226
|
+
patch: |-
|
|
227
|
+
- op: replace
|
|
228
|
+
path: /spec/template/spec/nodeSelector
|
|
229
|
+
value:
|
|
230
|
+
preem: "false"
|
|
231
|
+
```
|
|
232
|
+
|
|
209
233
|
## Перевірка
|
|
210
234
|
|
|
211
235
|
**`npx @nitra/cursor check k8s`** — програмні критерії в **JSDoc на початку** **`npm/scripts/check-k8s.mjs`**. Якщо під **`k8s`** немає **`*.yaml`** — крок пропущено. Канон **`$schema`** для редактора — розділ **«Визначення схеми YAML`** нижче.
|
package/package.json
CHANGED
package/scripts/check-abie.mjs
CHANGED
|
@@ -32,7 +32,7 @@ import { dirname, join, relative } from 'node:path'
|
|
|
32
32
|
import { parseAllDocuments } from 'yaml'
|
|
33
33
|
|
|
34
34
|
import { pathHasK8sSegment, ruKustomizationHasHealthCheckDeletePatch } from './check-k8s.mjs'
|
|
35
|
-
import {
|
|
35
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
36
36
|
import { flattenWorkflowSteps, getStepUses, parseWorkflowYaml } from './utils/gha-workflow.mjs'
|
|
37
37
|
import { walkDir } from './utils/walkDir.mjs'
|
|
38
38
|
|
|
@@ -79,9 +79,9 @@ export function isAbieK8sBaseYamlPath(rel) {
|
|
|
79
79
|
/**
|
|
80
80
|
* Чи значення **`preem`** у base **Deployment** вважається «істинним» за abie.mdc (**true** або рядок **`true`** без урахування регістру).
|
|
81
81
|
* @param {unknown} v значення з YAML
|
|
82
|
-
* @returns {boolean}
|
|
82
|
+
* @returns {boolean} **true**, якщо значення вважається істинним за abie.mdc
|
|
83
83
|
*/
|
|
84
|
-
function
|
|
84
|
+
function isAbiePreemTruthy(v) {
|
|
85
85
|
if (v === true) {
|
|
86
86
|
return true
|
|
87
87
|
}
|
|
@@ -117,7 +117,7 @@ export function deploymentDocumentHasAbieBasePreemNodeSelector(obj) {
|
|
|
117
117
|
if (nodeSelector === null || typeof nodeSelector !== 'object' || Array.isArray(nodeSelector)) {
|
|
118
118
|
return false
|
|
119
119
|
}
|
|
120
|
-
return
|
|
120
|
+
return isAbiePreemTruthy(nodeSelector.preem)
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
/**
|
|
@@ -279,9 +279,10 @@ async function collectDeploymentDirs(root, yamlAbs, fail) {
|
|
|
279
279
|
* @param {string} root корінь репозиторію
|
|
280
280
|
* @param {string[]} yamlFilesAbs yaml під k8s
|
|
281
281
|
* @param {(msg: string) => void} fail callback
|
|
282
|
+
* @param {(msg: string) => void} passFn успішне повідомлення
|
|
282
283
|
* @returns {Promise<void>}
|
|
283
284
|
*/
|
|
284
|
-
async function ensureAbieBaseDeploymentPreemNodeSelector(root, yamlFilesAbs, fail) {
|
|
285
|
+
async function ensureAbieBaseDeploymentPreemNodeSelector(root, yamlFilesAbs, fail, passFn) {
|
|
285
286
|
const baseFiles = yamlFilesAbs.filter(abs => {
|
|
286
287
|
const rel = relative(root, abs).replaceAll('\\', '/') || abs
|
|
287
288
|
return isAbieK8sBaseYamlPath(rel)
|
|
@@ -311,24 +312,24 @@ async function ensureAbieBaseDeploymentPreemNodeSelector(root, yamlFilesAbs, fai
|
|
|
311
312
|
return
|
|
312
313
|
}
|
|
313
314
|
for (const doc of docs) {
|
|
314
|
-
if (doc.errors.length
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
315
|
+
if (doc.errors.length === 0) {
|
|
316
|
+
const obj = doc.toJSON()
|
|
317
|
+
if (isDeploymentDoc(obj)) {
|
|
318
|
+
anyBaseDeployment = true
|
|
319
|
+
if (!deploymentDocumentHasAbieBasePreemNodeSelector(obj)) {
|
|
320
|
+
fail(
|
|
321
|
+
`${rel}: Deployment у base: потрібен spec.template.spec.nodeSelector.preem: true (або 'true') — abie.mdc`
|
|
322
|
+
)
|
|
323
|
+
return
|
|
324
|
+
}
|
|
325
|
+
}
|
|
325
326
|
}
|
|
326
327
|
}
|
|
327
328
|
}
|
|
328
329
|
if (anyBaseDeployment) {
|
|
329
|
-
|
|
330
|
+
passFn('Deployment у …/base/…: nodeSelector.preem відповідає abie.mdc')
|
|
330
331
|
} else {
|
|
331
|
-
|
|
332
|
+
passFn('Немає Deployment у шляхах …/base/… — перевірку preem у base пропущено')
|
|
332
333
|
}
|
|
333
334
|
}
|
|
334
335
|
|
|
@@ -506,28 +507,25 @@ function collectNginxRunPatchStringsFromKustomizationDoc(doc) {
|
|
|
506
507
|
/** @type {string[]} */
|
|
507
508
|
const out = []
|
|
508
509
|
for (const p of patches) {
|
|
509
|
-
if (p
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
const patchStr = pr.patch
|
|
522
|
-
if (typeof patchStr === 'string' && patchStr.trim() !== '') {
|
|
523
|
-
out.push(patchStr)
|
|
510
|
+
if (p !== null && typeof p === 'object' && !Array.isArray(p)) {
|
|
511
|
+
const pr = /** @type {Record<string, unknown>} */ (p)
|
|
512
|
+
const target = pr.target
|
|
513
|
+
if (target !== null && typeof target === 'object' && !Array.isArray(target)) {
|
|
514
|
+
const tg = /** @type {Record<string, unknown>} */ (target)
|
|
515
|
+
if (tg.kind === 'HTTPRoute' && tg.name === 'nginx-run') {
|
|
516
|
+
const patchStr = pr.patch
|
|
517
|
+
if (typeof patchStr === 'string' && patchStr.trim() !== '') {
|
|
518
|
+
out.push(patchStr)
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
524
522
|
}
|
|
525
523
|
}
|
|
526
524
|
return out
|
|
527
525
|
}
|
|
528
526
|
|
|
529
527
|
/**
|
|
530
|
-
*
|
|
528
|
+
* Збирає всі inline **JSON6902**-фрагменти для **HTTPRoute/nginx-run** у **kustomization.yaml** (усі документи у файлі).
|
|
531
529
|
* @param {string} raw повний текст файлу
|
|
532
530
|
* @returns {string} текст для **`validateAbieNginxRunHttpRoutePatches`** (може бути порожнім)
|
|
533
531
|
*/
|
|
@@ -552,8 +550,8 @@ export function getCombinedNginxRunPatchTextFromKustomization(raw) {
|
|
|
552
550
|
}
|
|
553
551
|
|
|
554
552
|
/**
|
|
555
|
-
* Перевіряє
|
|
556
|
-
* @param {string} combined текст одного або кількох inline **patch
|
|
553
|
+
* Перевіряє сукупний текст patch(ів) **HTTPRoute/nginx-run** на відповідність abie.mdc.
|
|
554
|
+
* @param {string} combined текст одного або кількох inline **patch**, розділених символом нового рядка
|
|
557
555
|
* @param {'ua' | 'ru'} mode **ua** або **ru**
|
|
558
556
|
* @returns {string | null} повідомлення про помилку або **null**
|
|
559
557
|
*/
|
|
@@ -569,24 +567,21 @@ export function validateAbieNginxRunHttpRoutePatches(combined, mode) {
|
|
|
569
567
|
if (!markers.some(m => combined.includes(m))) {
|
|
570
568
|
return `HTTPRoute nginx-run: у value для /spec/hostnames має бути один із доменів abie (${markers.join(', ')}) — abie.mdc`
|
|
571
569
|
}
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
)
|
|
577
|
-
|
|
578
|
-
return `HTTPRoute nginx-run: потрібен replace path /spec/parentRefs/0/namespace з value ${ns} (abie.mdc)`
|
|
570
|
+
const namespaceOk =
|
|
571
|
+
mode === 'ua'
|
|
572
|
+
? /path:\s*\/spec\/parentRefs\/0\/namespace\b[\s\S]{0,200}?value:\s*['"]?ua['"]?(?:\s|$)/mu.test(combined)
|
|
573
|
+
: /path:\s*\/spec\/parentRefs\/0\/namespace\b[\s\S]{0,200}?value:\s*['"]?ru['"]?(?:\s|$)/mu.test(combined)
|
|
574
|
+
if (!namespaceOk) {
|
|
575
|
+
return `HTTPRoute nginx-run: потрібен replace path /spec/parentRefs/0/namespace з value ${mode} (abie.mdc)`
|
|
579
576
|
}
|
|
580
|
-
if (mode === 'ru') {
|
|
581
|
-
|
|
582
|
-
return 'HTTPRoute nginx-run (ru): потрібна анотація gwin.yandex.cloud/rules.http.upgradeTypes: websocket (abie.mdc)'
|
|
583
|
-
}
|
|
577
|
+
if (mode === 'ru' && !/gwin\.yandex\.cloud\/rules\.http\.upgradeTypes:\s*['"]?websocket['"]?/m.test(combined)) {
|
|
578
|
+
return 'HTTPRoute nginx-run (ru): потрібна анотація gwin.yandex.cloud/rules.http.upgradeTypes: websocket (abie.mdc)'
|
|
584
579
|
}
|
|
585
580
|
return null
|
|
586
581
|
}
|
|
587
582
|
|
|
588
583
|
/**
|
|
589
|
-
* Чи **kustomization** містить валідні для abie
|
|
584
|
+
* Чи **kustomization** містить валідні для abie записи **patch** для **HTTPRoute/nginx-run** (**ua** або **ru**).
|
|
590
585
|
* @param {string} raw повний текст **kustomization.yaml**
|
|
591
586
|
* @param {'ua' | 'ru'} mode overlay
|
|
592
587
|
* @returns {boolean} true, якщо **`validateAbieNginxRunHttpRoutePatches`** повертає **null**
|
|
@@ -780,9 +775,10 @@ async function ensureRuKustomizationHealthCheckDelete(root, yamlFilesAbs, health
|
|
|
780
775
|
* @param {string} root корінь репозиторію
|
|
781
776
|
* @param {string[]} yamlFilesAbs yaml під k8s
|
|
782
777
|
* @param {(msg: string) => void} fail callback
|
|
778
|
+
* @param {(msg: string) => void} passFn успішне повідомлення
|
|
783
779
|
* @returns {Promise<void>}
|
|
784
780
|
*/
|
|
785
|
-
async function ensureUaRuAbieNodeSelectorPatches(root, yamlFilesAbs, fail) {
|
|
781
|
+
async function ensureUaRuAbieNodeSelectorPatches(root, yamlFilesAbs, fail, passFn) {
|
|
786
782
|
const uaAbsList = yamlFilesAbs.filter(abs => isUaKustomizationPath(relative(root, abs).replaceAll('\\', '/') || abs))
|
|
787
783
|
if (uaAbsList.length === 0) {
|
|
788
784
|
fail(
|
|
@@ -806,7 +802,7 @@ async function ensureUaRuAbieNodeSelectorPatches(root, yamlFilesAbs, fail) {
|
|
|
806
802
|
)
|
|
807
803
|
return
|
|
808
804
|
}
|
|
809
|
-
|
|
805
|
+
passFn(`${rel}: nodeSelector patch (ua) відповідає abie.mdc`)
|
|
810
806
|
}
|
|
811
807
|
|
|
812
808
|
const ruAbsList = yamlFilesAbs.filter(abs => isRuKustomizationPath(relative(root, abs).replaceAll('\\', '/') || abs))
|
|
@@ -832,7 +828,7 @@ async function ensureUaRuAbieNodeSelectorPatches(root, yamlFilesAbs, fail) {
|
|
|
832
828
|
)
|
|
833
829
|
return
|
|
834
830
|
}
|
|
835
|
-
|
|
831
|
+
passFn(`${rel}: nodeSelector patch (ru) відповідає abie.mdc`)
|
|
836
832
|
}
|
|
837
833
|
}
|
|
838
834
|
|
|
@@ -841,9 +837,10 @@ async function ensureUaRuAbieNodeSelectorPatches(root, yamlFilesAbs, fail) {
|
|
|
841
837
|
* @param {string} root корінь репозиторію
|
|
842
838
|
* @param {string[]} yamlFilesAbs yaml під k8s
|
|
843
839
|
* @param {(msg: string) => void} fail callback
|
|
840
|
+
* @param {(msg: string) => void} passFn успішне повідомлення
|
|
844
841
|
* @returns {Promise<void>}
|
|
845
842
|
*/
|
|
846
|
-
async function ensureUaRuAbieHttpRoutePatches(root, yamlFilesAbs, fail) {
|
|
843
|
+
async function ensureUaRuAbieHttpRoutePatches(root, yamlFilesAbs, fail, passFn) {
|
|
847
844
|
const uaAbsList = yamlFilesAbs.filter(abs => isUaKustomizationPath(relative(root, abs).replaceAll('\\', '/') || abs))
|
|
848
845
|
if (uaAbsList.length === 0) {
|
|
849
846
|
fail(
|
|
@@ -867,7 +864,7 @@ async function ensureUaRuAbieHttpRoutePatches(root, yamlFilesAbs, fail) {
|
|
|
867
864
|
fail(`${rel}: ${v}`)
|
|
868
865
|
return
|
|
869
866
|
}
|
|
870
|
-
|
|
867
|
+
passFn(`${rel}: HTTPRoute nginx-run (ua) відповідає abie.mdc`)
|
|
871
868
|
}
|
|
872
869
|
|
|
873
870
|
const ruAbsList = yamlFilesAbs.filter(abs => isRuKustomizationPath(relative(root, abs).replaceAll('\\', '/') || abs))
|
|
@@ -893,7 +890,7 @@ async function ensureUaRuAbieHttpRoutePatches(root, yamlFilesAbs, fail) {
|
|
|
893
890
|
fail(`${rel}: ${v}`)
|
|
894
891
|
return
|
|
895
892
|
}
|
|
896
|
-
|
|
893
|
+
passFn(`${rel}: HTTPRoute nginx-run (ru) відповідає abie.mdc`)
|
|
897
894
|
}
|
|
898
895
|
}
|
|
899
896
|
|
|
@@ -902,17 +899,14 @@ async function ensureUaRuAbieHttpRoutePatches(root, yamlFilesAbs, fail) {
|
|
|
902
899
|
* @returns {Promise<number>} 0 — OK, 1 — є порушення
|
|
903
900
|
*/
|
|
904
901
|
export async function check() {
|
|
905
|
-
|
|
906
|
-
const fail =
|
|
907
|
-
console.log(` ❌ ${msg}`)
|
|
908
|
-
exitCode = 1
|
|
909
|
-
}
|
|
902
|
+
const reporter = createCheckReporter()
|
|
903
|
+
const { pass, fail } = reporter
|
|
910
904
|
|
|
911
905
|
const root = process.cwd()
|
|
912
906
|
const enabled = await isAbieRuleEnabled(root)
|
|
913
907
|
if (!enabled) {
|
|
914
908
|
pass(`Правило abie не увімкнено в ${CONFIG_FILE} (rules) — перевірку пропущено`)
|
|
915
|
-
return
|
|
909
|
+
return reporter.getExitCode()
|
|
916
910
|
}
|
|
917
911
|
|
|
918
912
|
pass('Правило abie увімкнено — виконуємо перевірки')
|
|
@@ -978,7 +972,7 @@ export async function check() {
|
|
|
978
972
|
}
|
|
979
973
|
}
|
|
980
974
|
pass('Є Deployment — перевіряємо base: spec.template.spec.nodeSelector.preem (abie.mdc)')
|
|
981
|
-
await ensureAbieBaseDeploymentPreemNodeSelector(root, yamlFiles, fail)
|
|
975
|
+
await ensureAbieBaseDeploymentPreemNodeSelector(root, yamlFiles, fail, pass)
|
|
982
976
|
} else {
|
|
983
977
|
pass('Немає Deployment у дереві k8s — перевірку hc.yaml пропущено')
|
|
984
978
|
}
|
|
@@ -988,10 +982,10 @@ export async function check() {
|
|
|
988
982
|
|
|
989
983
|
if (deploymentDirs.size > 0) {
|
|
990
984
|
pass('Є Deployment — перевіряємо nodeSelector у ua/ru kustomization (abie.mdc)')
|
|
991
|
-
await ensureUaRuAbieNodeSelectorPatches(root, yamlFiles, fail)
|
|
985
|
+
await ensureUaRuAbieNodeSelectorPatches(root, yamlFiles, fail, pass)
|
|
992
986
|
pass('Є Deployment — перевіряємо HTTPRoute nginx-run у ua/ru kustomization (abie.mdc)')
|
|
993
|
-
await ensureUaRuAbieHttpRoutePatches(root, yamlFiles, fail)
|
|
987
|
+
await ensureUaRuAbieHttpRoutePatches(root, yamlFiles, fail, pass)
|
|
994
988
|
}
|
|
995
989
|
|
|
996
|
-
return
|
|
990
|
+
return reporter.getExitCode()
|
|
997
991
|
}
|
package/scripts/check-bun.mjs
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { existsSync } from 'node:fs'
|
|
18
18
|
import { readFile } from 'node:fs/promises'
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Чи ім'я пакета дозволене в кореневих `devDependencies` за bun.mdc (лише `@cspell/*` та `@nitra/*`).
|
|
@@ -53,11 +53,8 @@ async function loadNCursorRules() {
|
|
|
53
53
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
54
54
|
*/
|
|
55
55
|
export async function check() {
|
|
56
|
-
|
|
57
|
-
const fail =
|
|
58
|
-
console.log(` ❌ ${msg}`)
|
|
59
|
-
exitCode = 1
|
|
60
|
-
}
|
|
56
|
+
const reporter = createCheckReporter()
|
|
57
|
+
const { pass, fail } = reporter
|
|
61
58
|
|
|
62
59
|
const forbidden = ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', '.yarnrc.yml']
|
|
63
60
|
for (const f of forbidden) {
|
|
@@ -164,5 +161,5 @@ export async function check() {
|
|
|
164
161
|
}
|
|
165
162
|
}
|
|
166
163
|
|
|
167
|
-
return
|
|
164
|
+
return reporter.getExitCode()
|
|
168
165
|
}
|
package/scripts/check-docker.mjs
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { basename } from 'node:path'
|
|
9
9
|
|
|
10
10
|
import { lintDockerfileWithHadolint, posixRel } from './utils/docker-hadolint.mjs'
|
|
11
|
-
import {
|
|
11
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
12
12
|
import { walkDir } from './utils/walkDir.mjs'
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -42,18 +42,15 @@ export async function findDockerfilePaths(root) {
|
|
|
42
42
|
* @returns {Promise<number>} 0 — все OK, 1 — є зауваження або помилка запуску
|
|
43
43
|
*/
|
|
44
44
|
export async function check() {
|
|
45
|
-
|
|
46
|
-
const fail =
|
|
47
|
-
console.log(` ❌ ${msg}`)
|
|
48
|
-
exitCode = 1
|
|
49
|
-
}
|
|
45
|
+
const reporter = createCheckReporter()
|
|
46
|
+
const { pass, fail } = reporter
|
|
50
47
|
|
|
51
48
|
const root = process.cwd()
|
|
52
49
|
const files = await findDockerfilePaths(root)
|
|
53
50
|
|
|
54
51
|
if (files.length === 0) {
|
|
55
52
|
pass('Немає Dockerfile / Containerfile — перевірку hadolint пропущено')
|
|
56
|
-
return
|
|
53
|
+
return reporter.getExitCode()
|
|
57
54
|
}
|
|
58
55
|
|
|
59
56
|
pass(`Знайдено файлів для hadolint: ${files.length}`)
|
|
@@ -69,5 +66,5 @@ export async function check() {
|
|
|
69
66
|
}
|
|
70
67
|
}
|
|
71
68
|
|
|
72
|
-
return
|
|
69
|
+
return reporter.getExitCode()
|
|
73
70
|
}
|
package/scripts/check-ga.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import { existsSync } from 'node:fs'
|
|
|
14
14
|
import { readdir, readFile } from 'node:fs/promises'
|
|
15
15
|
import { join } from 'node:path'
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
18
18
|
import {
|
|
19
19
|
anyRunStepIncludes,
|
|
20
20
|
eventPathsIncludeExact,
|
|
@@ -122,17 +122,14 @@ function verifyNoDirectBunOrCache(relPath, content, failFn, passFn) {
|
|
|
122
122
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
123
123
|
*/
|
|
124
124
|
export async function check() {
|
|
125
|
-
|
|
126
|
-
const fail =
|
|
127
|
-
console.log(` ❌ ${msg}`)
|
|
128
|
-
exitCode = 1
|
|
129
|
-
}
|
|
125
|
+
const reporter = createCheckReporter()
|
|
126
|
+
const { pass, fail } = reporter
|
|
130
127
|
|
|
131
128
|
const wfDir = '.github/workflows'
|
|
132
129
|
|
|
133
130
|
if (!existsSync(wfDir)) {
|
|
134
131
|
fail(`Директорія ${wfDir} не існує`)
|
|
135
|
-
return
|
|
132
|
+
return reporter.getExitCode()
|
|
136
133
|
}
|
|
137
134
|
|
|
138
135
|
const setupBunDepsAction = '.github/actions/setup-bun-deps/action.yml'
|
|
@@ -290,5 +287,5 @@ export async function check() {
|
|
|
290
287
|
}
|
|
291
288
|
}
|
|
292
289
|
|
|
293
|
-
return
|
|
290
|
+
return reporter.getExitCode()
|
|
294
291
|
}
|
|
@@ -6,18 +6,15 @@
|
|
|
6
6
|
import { existsSync } from 'node:fs'
|
|
7
7
|
import { readFile } from 'node:fs/promises'
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Перевіряє відповідність проєкту правилам js-format.mdc
|
|
13
13
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
14
14
|
*/
|
|
15
15
|
export async function check() {
|
|
16
|
-
|
|
17
|
-
const fail =
|
|
18
|
-
console.log(` ❌ ${msg}`)
|
|
19
|
-
exitCode = 1
|
|
20
|
-
}
|
|
16
|
+
const reporter = createCheckReporter()
|
|
17
|
+
const { pass, fail } = reporter
|
|
21
18
|
|
|
22
19
|
const expectedKeys = [
|
|
23
20
|
'arrowParens',
|
|
@@ -95,5 +92,5 @@ export async function check() {
|
|
|
95
92
|
if (pkg.prettier) fail('package.json містить поле "prettier" — видали його')
|
|
96
93
|
}
|
|
97
94
|
|
|
98
|
-
return
|
|
95
|
+
return reporter.getExitCode()
|
|
99
96
|
}
|
|
@@ -10,7 +10,7 @@ import { existsSync } from 'node:fs'
|
|
|
10
10
|
import { readFile } from 'node:fs/promises'
|
|
11
11
|
|
|
12
12
|
import { parseWorkflowYaml, verifyLintJsWorkflowStructure } from './utils/gha-workflow.mjs'
|
|
13
|
-
import {
|
|
13
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
14
14
|
|
|
15
15
|
/** Очікуваний локальний скрипт. */
|
|
16
16
|
export const CANONICAL_LINT_JS = 'bunx oxlint --fix && bunx eslint --fix . && bunx jscpd .'
|
|
@@ -41,11 +41,8 @@ export function isCanonicalLintJs(script) {
|
|
|
41
41
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
42
42
|
*/
|
|
43
43
|
export async function check() {
|
|
44
|
-
|
|
45
|
-
const fail =
|
|
46
|
-
console.log(` ❌ ${msg}`)
|
|
47
|
-
exitCode = 1
|
|
48
|
-
}
|
|
44
|
+
const reporter = createCheckReporter()
|
|
45
|
+
const { pass, fail } = reporter
|
|
49
46
|
|
|
50
47
|
let eslintPath = ''
|
|
51
48
|
if (existsSync('eslint.config.js')) {
|
|
@@ -242,5 +239,5 @@ export async function check() {
|
|
|
242
239
|
if (existsSync(dup)) fail(`Знайдено застарілий конфіг ESLint: ${dup} — видали, використовуй flat config`)
|
|
243
240
|
}
|
|
244
241
|
|
|
245
|
-
return
|
|
242
|
+
return reporter.getExitCode()
|
|
246
243
|
}
|
|
@@ -8,16 +8,17 @@ import { existsSync } from 'node:fs'
|
|
|
8
8
|
import { readFile } from 'node:fs/promises'
|
|
9
9
|
import { join } from 'node:path'
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
12
12
|
import { getMonorepoPackageRootDirs } from './utils/workspaces.mjs'
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Перевіряє відповідність правилам js-pino.mdc для одного workspace-пакета.
|
|
16
16
|
* @param {string} rootDir відносний шлях workspace (не `'.'`)
|
|
17
17
|
* @param {(msg: string) => void} fail функція зворотного виклику для реєстрації помилки перевірки
|
|
18
|
+
* @param {(msg: string) => void} passFn успішне повідомлення (як у check-reporter)
|
|
18
19
|
* @returns {Promise<void>} завершується після перевірок цього пакета
|
|
19
20
|
*/
|
|
20
|
-
async function checkWorkspacePackage(rootDir, fail) {
|
|
21
|
+
async function checkWorkspacePackage(rootDir, fail, passFn) {
|
|
21
22
|
const label = `[${rootDir}] `
|
|
22
23
|
const pkgPath = join(rootDir, 'package.json')
|
|
23
24
|
if (existsSync(pkgPath)) {
|
|
@@ -36,9 +37,9 @@ async function checkWorkspacePackage(rootDir, fail) {
|
|
|
36
37
|
if (existsSync(configmapPath)) {
|
|
37
38
|
const content = await readFile(configmapPath, 'utf8')
|
|
38
39
|
if (content.includes('OTEL_RESOURCE_ATTRIBUTES')) {
|
|
39
|
-
|
|
40
|
+
passFn(`${label}k8s/base/configmap.yaml містить OTEL_RESOURCE_ATTRIBUTES`)
|
|
40
41
|
if (content.includes('service.name=') && content.includes('service.namespace=')) {
|
|
41
|
-
|
|
42
|
+
passFn(`${label}OTEL_RESOURCE_ATTRIBUTES містить service.name та service.namespace`)
|
|
42
43
|
} else {
|
|
43
44
|
fail(`${label}OTEL_RESOURCE_ATTRIBUTES має містити service.name=<name>,service.namespace=<namespace>`)
|
|
44
45
|
}
|
|
@@ -53,23 +54,20 @@ async function checkWorkspacePackage(rootDir, fail) {
|
|
|
53
54
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
54
55
|
*/
|
|
55
56
|
export async function check() {
|
|
56
|
-
|
|
57
|
-
const fail =
|
|
58
|
-
console.log(` ❌ ${msg}`)
|
|
59
|
-
exitCode = 1
|
|
60
|
-
}
|
|
57
|
+
const reporter = createCheckReporter()
|
|
58
|
+
const { pass, fail } = reporter
|
|
61
59
|
|
|
62
60
|
const roots = await getMonorepoPackageRootDirs()
|
|
63
61
|
const workspaceRoots = roots.filter(r => r !== '.')
|
|
64
62
|
|
|
65
63
|
if (workspaceRoots.length === 0) {
|
|
66
64
|
pass('js-pino: немає workspace-пакетів у кореневому package.json — перевірку залежностей і k8s у пакетах пропущено')
|
|
67
|
-
return
|
|
65
|
+
return reporter.getExitCode()
|
|
68
66
|
}
|
|
69
67
|
|
|
70
68
|
for (const r of workspaceRoots) {
|
|
71
|
-
await checkWorkspacePackage(r, fail)
|
|
69
|
+
await checkWorkspacePackage(r, fail, pass)
|
|
72
70
|
}
|
|
73
71
|
|
|
74
|
-
return
|
|
72
|
+
return reporter.getExitCode()
|
|
75
73
|
}
|
package/scripts/check-k8s.mjs
CHANGED
|
@@ -51,7 +51,7 @@ import { basename, dirname, join, relative, resolve } from 'node:path'
|
|
|
51
51
|
|
|
52
52
|
import { parseAllDocuments } from 'yaml'
|
|
53
53
|
|
|
54
|
-
import {
|
|
54
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
55
55
|
import { walkDir } from './utils/walkDir.mjs'
|
|
56
56
|
|
|
57
57
|
/** Версія набору схем yannh — узгоджено з k8s.mdc */
|
|
@@ -1482,11 +1482,8 @@ async function ensureBaseKustomizationHasNamespace(root, yamlFiles, fail) {
|
|
|
1482
1482
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
1483
1483
|
*/
|
|
1484
1484
|
export async function check() {
|
|
1485
|
-
|
|
1486
|
-
const fail =
|
|
1487
|
-
console.log(` ❌ ${msg}`)
|
|
1488
|
-
exitCode = 1
|
|
1489
|
-
}
|
|
1485
|
+
const reporter = createCheckReporter()
|
|
1486
|
+
const { pass, fail } = reporter
|
|
1490
1487
|
|
|
1491
1488
|
const root = process.cwd()
|
|
1492
1489
|
|
|
@@ -1496,7 +1493,7 @@ export async function check() {
|
|
|
1496
1493
|
|
|
1497
1494
|
if (yamlFiles.length === 0) {
|
|
1498
1495
|
pass('Немає *.yaml під k8s — перевірку $schema пропущено')
|
|
1499
|
-
return
|
|
1496
|
+
return reporter.getExitCode()
|
|
1500
1497
|
}
|
|
1501
1498
|
|
|
1502
1499
|
pass(`YAML у k8s: ${yamlFiles.length} файл(ів)`)
|
|
@@ -1515,5 +1512,5 @@ export async function check() {
|
|
|
1515
1512
|
|
|
1516
1513
|
await ensureBaseKustomizationHasNamespace(root, yamlFiles, fail)
|
|
1517
1514
|
|
|
1518
|
-
return
|
|
1515
|
+
return reporter.getExitCode()
|
|
1519
1516
|
}
|
|
@@ -18,7 +18,7 @@ import { readdir, readFile, rename, unlink, writeFile } from 'node:fs/promises'
|
|
|
18
18
|
import { basename, dirname, join, relative } from 'node:path'
|
|
19
19
|
|
|
20
20
|
import { findDockerfilePaths } from './check-docker.mjs'
|
|
21
|
-
import {
|
|
21
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
22
22
|
import { walkDir } from './utils/walkDir.mjs'
|
|
23
23
|
|
|
24
24
|
/**
|
|
@@ -264,11 +264,8 @@ function dockerfileHasEnvsSubstTemplate(dockerfileContent) {
|
|
|
264
264
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
265
265
|
*/
|
|
266
266
|
export async function check() {
|
|
267
|
-
|
|
268
|
-
const fail =
|
|
269
|
-
console.log(` ❌ ${msg}`)
|
|
270
|
-
exitCode = 1
|
|
271
|
-
}
|
|
267
|
+
const reporter = createCheckReporter()
|
|
268
|
+
const { pass, fail } = reporter
|
|
272
269
|
|
|
273
270
|
const root = process.cwd()
|
|
274
271
|
|
|
@@ -284,7 +281,7 @@ export async function check() {
|
|
|
284
281
|
|
|
285
282
|
if (templates.length === 0) {
|
|
286
283
|
pass('Немає default.conf.template — перевірку nginx-default-tpl пропущено')
|
|
287
|
-
return
|
|
284
|
+
return reporter.getExitCode()
|
|
288
285
|
}
|
|
289
286
|
|
|
290
287
|
pass(`Знайдено default.conf.template: ${templates.length}`)
|
|
@@ -383,5 +380,5 @@ export async function check() {
|
|
|
383
380
|
fail('Очікується .vscode/settings.json з форматером nginx і formatOnSave (див. nginx-default-tpl.mdc)')
|
|
384
381
|
}
|
|
385
382
|
|
|
386
|
-
return
|
|
383
|
+
return reporter.getExitCode()
|
|
387
384
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { existsSync } from 'node:fs'
|
|
8
8
|
import { readFile, stat } from 'node:fs/promises'
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
11
11
|
import {
|
|
12
12
|
hasIdTokenWritePermission,
|
|
13
13
|
hasNpmPublishStepWithPackage,
|
|
@@ -21,11 +21,8 @@ import {
|
|
|
21
21
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
22
22
|
*/
|
|
23
23
|
export async function check() {
|
|
24
|
-
|
|
25
|
-
const fail =
|
|
26
|
-
console.log(` ❌ ${msg}`)
|
|
27
|
-
exitCode = 1
|
|
28
|
-
}
|
|
24
|
+
const reporter = createCheckReporter()
|
|
25
|
+
const { pass, fail } = reporter
|
|
29
26
|
|
|
30
27
|
if (existsSync('package.json')) {
|
|
31
28
|
pass('package.json існує')
|
|
@@ -114,5 +111,5 @@ export async function check() {
|
|
|
114
111
|
fail(`Відсутній ${publishWf} (npm-module.mdc: npm publish)`)
|
|
115
112
|
}
|
|
116
113
|
|
|
117
|
-
return
|
|
114
|
+
return reporter.getExitCode()
|
|
118
115
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { existsSync } from 'node:fs'
|
|
9
9
|
import { readFile } from 'node:fs/promises'
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
12
12
|
import { anyRunStepIncludesStylelint, parseWorkflowYaml } from './utils/gha-workflow.mjs'
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -16,11 +16,8 @@ import { anyRunStepIncludesStylelint, parseWorkflowYaml } from './utils/gha-work
|
|
|
16
16
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
17
17
|
*/
|
|
18
18
|
export async function check() {
|
|
19
|
-
|
|
20
|
-
const fail =
|
|
21
|
-
console.log(` ❌ ${msg}`)
|
|
22
|
-
exitCode = 1
|
|
23
|
-
}
|
|
19
|
+
const reporter = createCheckReporter()
|
|
20
|
+
const { pass, fail } = reporter
|
|
24
21
|
|
|
25
22
|
if (existsSync('package.json')) {
|
|
26
23
|
const pkg = JSON.parse(await readFile('package.json', 'utf8'))
|
|
@@ -105,5 +102,5 @@ export async function check() {
|
|
|
105
102
|
}
|
|
106
103
|
}
|
|
107
104
|
|
|
108
|
-
return
|
|
105
|
+
return reporter.getExitCode()
|
|
109
106
|
}
|
package/scripts/check-text.mjs
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { existsSync } from 'node:fs'
|
|
13
13
|
import { readFile } from 'node:fs/promises'
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
16
16
|
import { anyRunStepIncludes, parseWorkflowYaml } from './utils/gha-workflow.mjs'
|
|
17
17
|
|
|
18
18
|
/** Заголовок абзацу про апостроф у text.mdc / n-text.mdc. */
|
|
@@ -47,11 +47,8 @@ function verifyUkApostropheRuleParagraph(filePath, body, failFn, passFn) {
|
|
|
47
47
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
48
48
|
*/
|
|
49
49
|
export async function check() {
|
|
50
|
-
|
|
51
|
-
const fail =
|
|
52
|
-
console.log(` ❌ ${msg}`)
|
|
53
|
-
exitCode = 1
|
|
54
|
-
}
|
|
50
|
+
const reporter = createCheckReporter()
|
|
51
|
+
const { pass, fail } = reporter
|
|
55
52
|
|
|
56
53
|
const v8rIgnoreRequired = ['.vscode/extensions.json', '.vscode/settings.json']
|
|
57
54
|
if (existsSync('.v8rignore')) {
|
|
@@ -215,5 +212,5 @@ export async function check() {
|
|
|
215
212
|
}
|
|
216
213
|
}
|
|
217
214
|
|
|
218
|
-
return
|
|
215
|
+
return reporter.getExitCode()
|
|
219
216
|
}
|
package/scripts/check-vue.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { existsSync } from 'node:fs'
|
|
|
11
11
|
import { readFile } from 'node:fs/promises'
|
|
12
12
|
import { join, relative } from 'node:path'
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
15
15
|
import {
|
|
16
16
|
findForbiddenVueImportsInSourceFile,
|
|
17
17
|
isVueImportScanSourceFile,
|
|
@@ -53,9 +53,10 @@ function ukFilesCountPhrase(n) {
|
|
|
53
53
|
* Перевіряє залежності та vite.config одного Vue-пакета.
|
|
54
54
|
* @param {string} rootDir відносний шлях до пакета
|
|
55
55
|
* @param {(msg: string) => void} fail функція зворотного виклику для реєстрації помилки перевірки
|
|
56
|
+
* @param {(msg: string) => void} passFn успішне повідомлення (як у check-reporter)
|
|
56
57
|
* @returns {Promise<void>} завершується після перевірок залежностей, `vite.config` і сканування джерел на імпорти з `vue`
|
|
57
58
|
*/
|
|
58
|
-
async function checkVuePackage(rootDir, fail) {
|
|
59
|
+
async function checkVuePackage(rootDir, fail, passFn) {
|
|
59
60
|
const label = packageLabel(rootDir)
|
|
60
61
|
const prefix = `[${label}] `
|
|
61
62
|
|
|
@@ -66,7 +67,7 @@ async function checkVuePackage(rootDir, fail) {
|
|
|
66
67
|
const allDeps = { ...deps, ...devDeps }
|
|
67
68
|
|
|
68
69
|
if (deps.vue) {
|
|
69
|
-
|
|
70
|
+
passFn(`${prefix}vue в dependencies: ${deps.vue}`)
|
|
70
71
|
} else {
|
|
71
72
|
fail(`${prefix}vue відсутній в dependencies`)
|
|
72
73
|
}
|
|
@@ -74,7 +75,7 @@ async function checkVuePackage(rootDir, fail) {
|
|
|
74
75
|
if (devDeps.vite) {
|
|
75
76
|
const match = devDeps.vite.match(/(\d+)/)
|
|
76
77
|
if (match && Number(match[1]) >= 8) {
|
|
77
|
-
|
|
78
|
+
passFn(`${prefix}vite >= 8: ${devDeps.vite}`)
|
|
78
79
|
} else {
|
|
79
80
|
fail(`${prefix}vite має бути >= 8, знайдено: ${devDeps.vite}`)
|
|
80
81
|
}
|
|
@@ -83,25 +84,25 @@ async function checkVuePackage(rootDir, fail) {
|
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
if (devDeps['@vitejs/plugin-vue']) {
|
|
86
|
-
|
|
87
|
+
passFn(`${prefix}@vitejs/plugin-vue: ${devDeps['@vitejs/plugin-vue']}`)
|
|
87
88
|
} else {
|
|
88
89
|
fail(`${prefix}@vitejs/plugin-vue відсутній в devDependencies`)
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
if (allDeps['vue-macros']) {
|
|
92
|
-
|
|
93
|
+
passFn(`${prefix}vue-macros: ${allDeps['vue-macros']}`)
|
|
93
94
|
} else {
|
|
94
95
|
fail(`${prefix}vue-macros відсутній — bun add -d vue-macros`)
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
if (allDeps['unplugin-auto-import']) {
|
|
98
|
-
|
|
99
|
+
passFn(`${prefix}unplugin-auto-import присутній`)
|
|
99
100
|
} else {
|
|
100
101
|
fail(`${prefix}unplugin-auto-import відсутній — bun add -d unplugin-auto-import`)
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
if (allDeps['vite-plugin-vue-layouts-next']) {
|
|
104
|
-
|
|
105
|
+
passFn(`${prefix}vite-plugin-vue-layouts-next присутній`)
|
|
105
106
|
} else {
|
|
106
107
|
fail(`${prefix}vite-plugin-vue-layouts-next відсутній — bun add -d vite-plugin-vue-layouts-next`)
|
|
107
108
|
}
|
|
@@ -112,12 +113,12 @@ async function checkVuePackage(rootDir, fail) {
|
|
|
112
113
|
const relConfig = join(rootDir, viteConfig)
|
|
113
114
|
const content = await readFile(relConfig, 'utf8')
|
|
114
115
|
if (content.includes('VueMacros')) {
|
|
115
|
-
|
|
116
|
+
passFn(`${prefix}${viteConfig} використовує VueMacros`)
|
|
116
117
|
} else {
|
|
117
118
|
fail(`${prefix}${viteConfig} не містить VueMacros`)
|
|
118
119
|
}
|
|
119
120
|
if (content.includes('AutoImport')) {
|
|
120
|
-
|
|
121
|
+
passFn(`${prefix}${viteConfig} використовує AutoImport`)
|
|
121
122
|
} else {
|
|
122
123
|
fail(`${prefix}${viteConfig} не містить AutoImport`)
|
|
123
124
|
}
|
|
@@ -147,7 +148,7 @@ async function checkVuePackage(rootDir, fail) {
|
|
|
147
148
|
}
|
|
148
149
|
}
|
|
149
150
|
if (importViolations === 0) {
|
|
150
|
-
|
|
151
|
+
passFn(
|
|
151
152
|
`${prefix}немає заборонених value-імпортів з 'vue' у джерелах (проскановано ${ukFilesCountPhrase(sourcePaths.length)})`
|
|
152
153
|
)
|
|
153
154
|
}
|
|
@@ -158,11 +159,8 @@ async function checkVuePackage(rootDir, fail) {
|
|
|
158
159
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
159
160
|
*/
|
|
160
161
|
export async function check() {
|
|
161
|
-
|
|
162
|
-
const fail =
|
|
163
|
-
console.log(` ❌ ${msg}`)
|
|
164
|
-
exitCode = 1
|
|
165
|
-
}
|
|
162
|
+
const reporter = createCheckReporter()
|
|
163
|
+
const { pass, fail } = reporter
|
|
166
164
|
|
|
167
165
|
if (existsSync('.vscode/extensions.json')) {
|
|
168
166
|
const ext = JSON.parse(await readFile('.vscode/extensions.json', 'utf8'))
|
|
@@ -188,12 +186,12 @@ export async function check() {
|
|
|
188
186
|
|
|
189
187
|
if (vueRoots.length === 0) {
|
|
190
188
|
fail('vue не знайдено в dependencies жодного пакета (корінь репо та каталоги з кореневого workspaces)')
|
|
191
|
-
return
|
|
189
|
+
return reporter.getExitCode()
|
|
192
190
|
}
|
|
193
191
|
|
|
194
192
|
for (const r of vueRoots) {
|
|
195
|
-
await checkVuePackage(r, fail)
|
|
193
|
+
await checkVuePackage(r, fail, pass)
|
|
196
194
|
}
|
|
197
195
|
|
|
198
|
-
return
|
|
196
|
+
return reporter.getExitCode()
|
|
199
197
|
}
|
package/scripts/run-docker.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { basename } from 'node:path'
|
|
|
11
11
|
|
|
12
12
|
import { isRunAsCli } from './cli-entry.mjs'
|
|
13
13
|
import { lintDockerfileWithHadolint, posixRel } from './utils/docker-hadolint.mjs'
|
|
14
|
-
import {
|
|
14
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
15
15
|
import { walkDir } from './utils/walkDir.mjs'
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -44,18 +44,15 @@ export async function findLintDockerfilePaths(root) {
|
|
|
44
44
|
* @returns {Promise<number>} 0 — OK, 1 — зауваження або помилка
|
|
45
45
|
*/
|
|
46
46
|
async function main() {
|
|
47
|
-
|
|
48
|
-
const fail =
|
|
49
|
-
console.log(` ❌ ${msg}`)
|
|
50
|
-
exitCode = 1
|
|
51
|
-
}
|
|
47
|
+
const reporter = createCheckReporter()
|
|
48
|
+
const { pass, fail } = reporter
|
|
52
49
|
|
|
53
50
|
const root = process.cwd()
|
|
54
51
|
const files = await findLintDockerfilePaths(root)
|
|
55
52
|
|
|
56
53
|
if (files.length === 0) {
|
|
57
54
|
pass('lint-docker: немає Dockerfile / *.Dockerfile — hadolint пропущено')
|
|
58
|
-
return
|
|
55
|
+
return reporter.getExitCode()
|
|
59
56
|
}
|
|
60
57
|
|
|
61
58
|
pass(`lint-docker: файлів для hadolint: ${files.length}`)
|
|
@@ -71,7 +68,7 @@ async function main() {
|
|
|
71
68
|
}
|
|
72
69
|
}
|
|
73
70
|
|
|
74
|
-
return
|
|
71
|
+
return reporter.getExitCode()
|
|
75
72
|
}
|
|
76
73
|
|
|
77
74
|
if (isRunAsCli()) {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Спільний репортер для check-скриптів і lint-docker.
|
|
3
|
+
*
|
|
4
|
+
* Об’єднує вивід успіхів (`pass` з `pass.mjs`) і помилок з префіксом ❌; накопичує код виходу **1**,
|
|
5
|
+
* якщо хоча б раз викликано `fail`.
|
|
6
|
+
*
|
|
7
|
+
* Використовуй `getExitCode()` у `return`, а не деструктуризацію `exitCode` — геттер «знімається» один раз.
|
|
8
|
+
*/
|
|
9
|
+
import { pass } from './pass.mjs'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Створює пару `pass` / `fail` з накопиченням ненульового коду виходу.
|
|
13
|
+
* @returns {{ pass: typeof pass, fail: (msg: string) => void, getExitCode: () => number }}
|
|
14
|
+
*/
|
|
15
|
+
export function createCheckReporter() {
|
|
16
|
+
let exitCode = 0
|
|
17
|
+
return {
|
|
18
|
+
pass,
|
|
19
|
+
fail(msg) {
|
|
20
|
+
console.log(` ❌ ${msg}`)
|
|
21
|
+
exitCode = 1
|
|
22
|
+
},
|
|
23
|
+
getExitCode() {
|
|
24
|
+
return exitCode
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|