@7n/rules 1.20.0 → 1.20.1
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 +7 -0
- package/package.json +1 -1
- package/rules/bun/licensee/docs/fix-licensee.md +1 -2
- package/rules/bun/licensee/docs/main.md +1 -1
- package/rules/bun/licensee/fix-licensee.mjs +3 -4
- package/rules/bun/licensee/main.mjs +7 -7
- package/rules/docker/lib/docker-hadolint.mjs +6 -8
- package/rules/docker/lib/docs/docker-hadolint.md +1 -1
- package/rules/docker/lint/docs/main.md +1 -1
- package/rules/docker/lint/main.mjs +1 -1
- package/rules/image-compress/check/docs/fix-check.md +1 -2
- package/rules/image-compress/check/docs/main.md +1 -1
- package/rules/image-compress/check/fix-check.mjs +27 -22
- package/rules/image-compress/check/main.mjs +14 -14
- package/rules/js/jscpd_duplicates/docs/main.md +1 -1
- package/rules/js/jscpd_duplicates/main.mjs +7 -8
- package/rules/k8s/kubeconform/docs/main.md +1 -1
- package/rules/k8s/kubeconform/main.mjs +1 -1
- package/rules/k8s/manifests/main.mjs +52 -39
- package/rules/php/cs_fixer/docs/main.md +1 -2
- package/rules/php/cs_fixer/main.mjs +8 -7
- package/rules/php/phpcs/docs/main.md +1 -2
- package/rules/php/phpcs/main.mjs +12 -11
- package/rules/php/project/docs/main.md +1 -2
- package/rules/php/project/main.mjs +19 -16
- package/rules/python/lib/docs/uv-run.md +1 -2
- package/rules/python/lib/uv-run.mjs +12 -8
- package/rules/python/mypy/docs/main.md +1 -1
- package/rules/python/mypy/main.mjs +9 -8
- package/rules/python/project/docs/main.md +1 -2
- package/rules/python/project/main.mjs +33 -23
- package/rules/python/ruff/docs/fix-ruff.md +1 -2
- package/rules/python/ruff/docs/main.md +1 -1
- package/rules/python/ruff/fix-ruff.mjs +12 -10
- package/rules/python/ruff/main.mjs +13 -12
- package/rules/rego/conftest_verify/docs/main.md +1 -2
- package/rules/rego/conftest_verify/main.mjs +12 -10
- package/rules/rego/lib/docs/run-external-tool.md +1 -1
- package/rules/rego/lib/run-external-tool.mjs +10 -7
- package/rules/rego/opa_check/docs/main.md +1 -1
- package/rules/rego/opa_check/main.mjs +3 -3
- package/rules/rego/regal/docs/main.md +1 -1
- package/rules/rego/regal/main.mjs +3 -3
- package/rules/rust/check/docs/fix-check.md +1 -1
- package/rules/rust/check/docs/main.md +1 -1
- package/rules/rust/check/fix-check.mjs +13 -12
- package/rules/rust/check/main.mjs +19 -13
- package/rules/security/scan/docs/main.md +1 -1
- package/rules/security/scan/main.mjs +9 -9
- package/rules/style/lint/docs/fix-lint.md +1 -1
- package/rules/style/lint/docs/main.md +1 -1
- package/rules/style/lint/fix-lint.mjs +8 -8
- package/rules/style/lint/main.mjs +6 -6
- package/rules/text/cspell-fix/docs/fix-worker.md +1 -1
- package/rules/text/cspell-fix/docs/main.md +1 -1
- package/rules/text/cspell-fix/fix-worker.mjs +1 -1
- package/rules/text/cspell-fix/main.mjs +14 -15
- package/rules/text/oxfmt/docs/fix-oxfmt.md +1 -1
- package/rules/text/oxfmt/docs/main.md +1 -1
- package/rules/text/oxfmt/fix-oxfmt.mjs +3 -3
- package/rules/text/oxfmt/main.mjs +6 -5
- package/rules/text/run-dotenv-linter/docs/fix-run-dotenv-linter.md +1 -2
- package/rules/text/run-dotenv-linter/docs/main.md +1 -1
- package/rules/text/run-dotenv-linter/fix-run-dotenv-linter.mjs +2 -2
- package/rules/text/run-dotenv-linter/main.mjs +19 -24
- package/rules/text/run-shellcheck/docs/fix-run-shellcheck.md +1 -2
- package/rules/text/run-shellcheck/docs/main.md +1 -1
- package/rules/text/run-shellcheck/fix-run-shellcheck.mjs +3 -3
- package/rules/text/run-shellcheck/main.mjs +46 -57
- package/rules/text/run-v8r/docs/main.md +1 -1
- package/rules/text/run-v8r/main.mjs +31 -26
- package/scripts/lib/lint-surface/blocking-inventory.mjs +12 -40
- package/scripts/lib/lint-surface/docs/blocking-inventory.md +1 -2
- package/scripts/utils/docs/spawn-async.md +1 -2
- package/scripts/utils/spawn-async.mjs +4 -1
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -3,9 +3,8 @@ type: JS Module
|
|
|
3
3
|
title: fix-licensee.mjs
|
|
4
4
|
resource: npm/rules/bun/licensee/fix-licensee.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 5c27ef32
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
tier: local-min
|
|
9
8
|
score: 100
|
|
10
9
|
issues: judge:inaccurate:0.99
|
|
11
10
|
judgeModel: openai-codex/gpt-5.4-mini
|
|
@@ -4,23 +4,22 @@
|
|
|
4
4
|
* вручну). Реальні `license-violation` (файл є, але залежності не проходять) — НЕ
|
|
5
5
|
* T0-фікс: потребують людського рішення про ліцензійну політику, не regen.
|
|
6
6
|
*/
|
|
7
|
-
import { spawnSync } from 'node:child_process'
|
|
8
7
|
import { join } from 'node:path'
|
|
9
8
|
|
|
10
9
|
import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
|
|
10
|
+
import { spawnAsync } from '../../../scripts/utils/spawn-async.mjs'
|
|
11
11
|
|
|
12
12
|
/** @type {import('../../../scripts/lib/lint-surface/types.mjs').T0Pattern[]} */
|
|
13
13
|
export const patterns = [
|
|
14
14
|
{
|
|
15
15
|
id: 'bun-licensee-config-init',
|
|
16
16
|
test: violations => violations.some(v => v.reason === 'licensee-config-missing'),
|
|
17
|
-
apply: (violations, ctx) => {
|
|
17
|
+
apply: async (violations, ctx) => {
|
|
18
18
|
const bun = resolveCmd('bun')
|
|
19
19
|
if (!bun) return { touchedFiles: [] }
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
await spawnAsync(bun, ['x', 'licensee', '--init', '--production', '--quiet'], {
|
|
22
22
|
cwd: ctx.cwd,
|
|
23
|
-
encoding: 'utf8',
|
|
24
23
|
shell: false
|
|
25
24
|
})
|
|
26
25
|
|
|
@@ -4,17 +4,17 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { existsSync } from 'node:fs'
|
|
6
6
|
import { join } from 'node:path'
|
|
7
|
-
import { spawnSync } from 'node:child_process'
|
|
8
7
|
|
|
9
8
|
import { createViolationReporter } from '../../../scripts/lib/lint-surface/violation-reporter.mjs'
|
|
10
9
|
import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
|
|
10
|
+
import { spawnAsync } from '../../../scripts/utils/spawn-async.mjs'
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Detector bun/licensee: ліцензії npm-залежностей через `licensee` (read-only).
|
|
14
14
|
* @param {import('../../../scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст lint-прогону
|
|
15
|
-
* @returns {import('../../../scripts/lib/lint-surface/types.mjs').LintResult} результат detector-а
|
|
15
|
+
* @returns {Promise<import('../../../scripts/lib/lint-surface/types.mjs').LintResult>} результат detector-а
|
|
16
16
|
*/
|
|
17
|
-
export function lint(ctx) {
|
|
17
|
+
export async function lint(ctx) {
|
|
18
18
|
const reporter = createViolationReporter(ctx)
|
|
19
19
|
const { fail } = reporter
|
|
20
20
|
const cwd = ctx.cwd
|
|
@@ -40,8 +40,8 @@ export function lint(ctx) {
|
|
|
40
40
|
// of undefined (reading 'localeCompare')" — спостережено з @npmcli/arborist на
|
|
41
41
|
// bun-based node_modules) завжди йде у stderr через die(); легітимний звіт про
|
|
42
42
|
// порушення — лише у stdout. Канал розрізняє crash від реального порушення.
|
|
43
|
-
const r =
|
|
44
|
-
if (r.
|
|
43
|
+
const r = await spawnAsync(bun, ['x', 'licensee', '--production', '--errors-only'], { cwd, shell: false })
|
|
44
|
+
if (r.exitCode !== 0) {
|
|
45
45
|
const stderr = (r.stderr ?? '').trim().slice(0, 2000)
|
|
46
46
|
if (stderr) {
|
|
47
47
|
// Crash самого тула — НЕ підтверджене ліцензійне порушення: fail-open
|
|
@@ -54,7 +54,7 @@ export function lint(ctx) {
|
|
|
54
54
|
level: 'warn',
|
|
55
55
|
message:
|
|
56
56
|
`lint-bun: licensee — інструмент завершився з помилкою, це НЕ підтверджене ліцензійне порушення ` +
|
|
57
|
-
`(код ${r.
|
|
57
|
+
`(код ${r.exitCode}, bun.mdc). Ймовірна причина — несумісність @npmcli/arborist з деревом bun install. ` +
|
|
58
58
|
`Перевір вручну: \`bunx licensee --production\`.\n${stderr}`
|
|
59
59
|
}
|
|
60
60
|
]
|
|
@@ -62,7 +62,7 @@ export function lint(ctx) {
|
|
|
62
62
|
}
|
|
63
63
|
const stdout = (r.stdout ?? '').trim().slice(0, 2000)
|
|
64
64
|
const detail = stdout ? `\n${stdout}` : ''
|
|
65
|
-
fail(`lint-bun: licensee — порушення ліцензій (код ${r.
|
|
65
|
+
fail(`lint-bun: licensee — порушення ліцензій (код ${r.exitCode}, bun.mdc)${detail}`, 'license-violation')
|
|
66
66
|
}
|
|
67
67
|
return reporter.result()
|
|
68
68
|
}
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* прибрано — hadolint ставиться як **нативний бінарник**, без `docker run`.
|
|
7
7
|
* Використовується `../js/lint.mjs` (правило `n-rules lint docker` / check-docker).
|
|
8
8
|
*/
|
|
9
|
-
import { spawnSync } from 'node:child_process'
|
|
10
9
|
import { relative, sep } from 'node:path'
|
|
11
10
|
|
|
12
11
|
import { ensureTool } from '../../../scripts/lib/ensure-tool.mjs'
|
|
12
|
+
import { spawnAsync } from '../../../scripts/utils/spawn-async.mjs'
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Відносний шлях від root з прямими слешами (стабільний вивід незалежно від OS).
|
|
@@ -27,9 +27,9 @@ export function posixRel(root, absPath) {
|
|
|
27
27
|
* чи не вдався — повертаємо `ok: false` з підказкою (без `docker run`).
|
|
28
28
|
* @param {string} root корінь репозиторію
|
|
29
29
|
* @param {string} absPath абсолютний шлях до Dockerfile
|
|
30
|
-
* @returns {{ ok: boolean, stdout: string, stderr: string, via: string }} результат перевірки hadolint
|
|
30
|
+
* @returns {Promise<{ ok: boolean, stdout: string, stderr: string, via: string }>} результат перевірки hadolint
|
|
31
31
|
*/
|
|
32
|
-
export function lintDockerfileWithHadolint(root, absPath) {
|
|
32
|
+
export async function lintDockerfileWithHadolint(root, absPath) {
|
|
33
33
|
const rel = posixRel(root, absPath)
|
|
34
34
|
let hadolintPath
|
|
35
35
|
try {
|
|
@@ -46,13 +46,11 @@ export function lintDockerfileWithHadolint(root, absPath) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
const local =
|
|
50
|
-
cwd: root
|
|
51
|
-
encoding: 'utf8',
|
|
52
|
-
maxBuffer: 10 * 1024 * 1024
|
|
49
|
+
const local = await spawnAsync(hadolintPath, [rel], {
|
|
50
|
+
cwd: root
|
|
53
51
|
})
|
|
54
52
|
return {
|
|
55
|
-
ok: local.
|
|
53
|
+
ok: local.exitCode === 0,
|
|
56
54
|
stdout: local.stdout ?? '',
|
|
57
55
|
stderr: local.stderr ?? '',
|
|
58
56
|
via: 'hadolint'
|
|
@@ -351,7 +351,7 @@ async function checkDockerfile(reporter, root, abs) {
|
|
|
351
351
|
const nginxUserHint = getNginxUnprivilegedUserHint(content)
|
|
352
352
|
if (nginxUserHint) fail(`${rel} (nginx non-root): ${nginxUserHint}`)
|
|
353
353
|
|
|
354
|
-
const { ok, stdout, stderr, via } = lintDockerfileWithHadolint(root, abs)
|
|
354
|
+
const { ok, stdout, stderr, via } = await lintDockerfileWithHadolint(root, abs)
|
|
355
355
|
const tail = (stdout + stderr).trim()
|
|
356
356
|
if (!ok) {
|
|
357
357
|
const detail = tail ? `:\n${tail}` : ''
|
|
@@ -3,9 +3,8 @@ type: JS Module
|
|
|
3
3
|
title: fix-check.mjs
|
|
4
4
|
resource: npm/rules/image-compress/check/fix-check.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 0c9ec731
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
tier: local-min
|
|
9
8
|
score: 100
|
|
10
9
|
issues: judge:inaccurate:0.98
|
|
11
10
|
judgeModel: openai-codex/gpt-5.4-mini
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* Detector лише ЗВІТУЄ `needsCompression` (read-only contract); мутація — тут, до LLM-ladder:
|
|
5
5
|
* стиснення — бінарна трансформація, не текстовий diff, тож LLM-фікс тут апріорі недоречний.
|
|
6
6
|
*/
|
|
7
|
-
import { spawnSync } from 'node:child_process'
|
|
8
7
|
import { env } from 'node:process'
|
|
9
8
|
|
|
10
9
|
import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
|
|
10
|
+
import { spawnAsync } from '../../../scripts/utils/spawn-async.mjs'
|
|
11
11
|
|
|
12
12
|
const MINIFY_PACKAGE_NAME = '@nitra/minify-image'
|
|
13
13
|
const JSON_MAX_BUFFER = 20 * 1024 * 1024
|
|
@@ -17,37 +17,42 @@ const JSON_MAX_BUFFER = 20 * 1024 * 1024
|
|
|
17
17
|
* Best-effort: відсутній npx / помилка / ненульовий код — лог-варн без падіння (re-detect
|
|
18
18
|
* наступного кроку покаже справжній стан).
|
|
19
19
|
* @param {string} cwd корінь репозиторію
|
|
20
|
-
* @returns {string[]} абсолютні шляхи стиснутих файлів (порожньо при помилці/no-op)
|
|
20
|
+
* @returns {Promise<string[]>} абсолютні шляхи стиснутих файлів (порожньо при помилці/no-op)
|
|
21
21
|
*/
|
|
22
|
-
function runCompression(cwd) {
|
|
22
|
+
async function runCompression(cwd) {
|
|
23
23
|
const npxPath = resolveCmd('npx')
|
|
24
24
|
if (!npxPath) {
|
|
25
25
|
console.log(` ⚠️ 'npx' не знайдено в PATH — пропускаємо стиснення зображень`)
|
|
26
26
|
return []
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
let writeResult
|
|
30
|
+
try {
|
|
31
|
+
writeResult = await spawnAsync(npxPath, [MINIFY_PACKAGE_NAME, '--src=.', '--write'], {
|
|
32
|
+
cwd,
|
|
33
|
+
env,
|
|
34
|
+
maxBuffer: JSON_MAX_BUFFER
|
|
35
|
+
})
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.log(` ⚠️ не вдалося запустити \`npx ${MINIFY_PACKAGE_NAME} --write\`: ${error.message}`)
|
|
37
38
|
return []
|
|
38
39
|
}
|
|
39
|
-
if (typeof writeResult.
|
|
40
|
-
console.log(` ⚠️ \`npx ${MINIFY_PACKAGE_NAME} --write\` завершився з кодом ${writeResult.
|
|
40
|
+
if (typeof writeResult.exitCode === 'number' && writeResult.exitCode !== 0) {
|
|
41
|
+
console.log(` ⚠️ \`npx ${MINIFY_PACKAGE_NAME} --write\` завершився з кодом ${writeResult.exitCode}`)
|
|
41
42
|
return []
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
let jsonResult
|
|
46
|
+
try {
|
|
47
|
+
jsonResult = await spawnAsync(npxPath, [MINIFY_PACKAGE_NAME, '--src=.', '--json'], {
|
|
48
|
+
cwd,
|
|
49
|
+
env,
|
|
50
|
+
maxBuffer: JSON_MAX_BUFFER
|
|
51
|
+
})
|
|
52
|
+
} catch {
|
|
53
|
+
return []
|
|
54
|
+
}
|
|
55
|
+
if (jsonResult.exitCode !== 0) return []
|
|
51
56
|
|
|
52
57
|
try {
|
|
53
58
|
const report = JSON.parse(jsonResult.stdout)
|
|
@@ -63,8 +68,8 @@ export const patterns = [
|
|
|
63
68
|
{
|
|
64
69
|
id: 'image-compress-write',
|
|
65
70
|
test: violations => violations.some(v => v.reason === 'needs-compression'),
|
|
66
|
-
apply: (violations, ctx) => {
|
|
67
|
-
const compressed = runCompression(ctx.cwd)
|
|
71
|
+
apply: async (violations, ctx) => {
|
|
72
|
+
const compressed = await runCompression(ctx.cwd)
|
|
68
73
|
if (compressed.length === 0) return { touchedFiles: [] }
|
|
69
74
|
|
|
70
75
|
for (const absPath of compressed) ctx.recordWrite?.(absPath)
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* `.n-minify-image.tsv` (`@nitra/minify-image --json`). Стиснення (`--write`) — окремий
|
|
4
4
|
* fix, не в detector-і.
|
|
5
5
|
*/
|
|
6
|
-
import { spawnSync } from 'node:child_process'
|
|
7
|
-
|
|
8
6
|
import { createViolationReporter } from '../../../scripts/lib/lint-surface/violation-reporter.mjs'
|
|
7
|
+
import { spawnAsync } from '../../../scripts/utils/spawn-async.mjs'
|
|
9
8
|
|
|
10
9
|
const JSON_MAX_BUFFER = 20 * 1024 * 1024
|
|
11
10
|
|
|
@@ -20,27 +19,28 @@ function parseMinifyJson(stdout) {
|
|
|
20
19
|
/**
|
|
21
20
|
* Detector image-compress/check: \@nitra/minify-image --json (read-only).
|
|
22
21
|
* @param {import('../../../scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст lint-прогону
|
|
23
|
-
* @returns {import('../../../scripts/lib/lint-surface/types.mjs').LintResult} результат detector-а
|
|
22
|
+
* @returns {Promise<import('../../../scripts/lib/lint-surface/types.mjs').LintResult>} результат detector-а
|
|
24
23
|
*/
|
|
25
|
-
export function lint(ctx) {
|
|
24
|
+
export async function lint(ctx) {
|
|
26
25
|
const reporter = createViolationReporter(ctx)
|
|
27
26
|
const { fail } = reporter
|
|
28
27
|
const cwd = ctx.cwd
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
let r
|
|
30
|
+
try {
|
|
31
|
+
r = await spawnAsync('npx', ['@nitra/minify-image', '--src=.', '--json'], {
|
|
32
|
+
cwd,
|
|
33
|
+
env: process.env,
|
|
34
|
+
maxBuffer: JSON_MAX_BUFFER
|
|
35
|
+
})
|
|
36
|
+
} catch (error) {
|
|
37
|
+
fail(`image-compress: не вдалося запустити npx @nitra/minify-image --json: ${error.message}`, 'tool-error')
|
|
38
38
|
return reporter.result()
|
|
39
39
|
}
|
|
40
|
-
if (r.
|
|
40
|
+
if (r.exitCode !== 0) {
|
|
41
41
|
const detail = [r.stdout, r.stderr].filter(Boolean).join('\n').trim()
|
|
42
42
|
const detailSuffix = detail ? `:\n${detail}` : ''
|
|
43
|
-
fail(`image-compress: @nitra/minify-image --json завершився з кодом ${r.
|
|
43
|
+
fail(`image-compress: @nitra/minify-image --json завершився з кодом ${r.exitCode}${detailSuffix}`, 'tool-error')
|
|
44
44
|
return reporter.result()
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
* Кожен клон → одне порушення (anchored на `firstFile`), із посиланням на `secondFile`
|
|
4
4
|
* у `data`. JSON-звіт пишеться у системний tmp (поза репо) — дерево не мутується.
|
|
5
5
|
*/
|
|
6
|
-
import { spawnSync } from 'node:child_process'
|
|
7
6
|
import { mkdtempSync, readFileSync, rmSync } from 'node:fs'
|
|
8
7
|
import { tmpdir } from 'node:os'
|
|
9
8
|
import { join } from 'node:path'
|
|
10
9
|
|
|
10
|
+
import { spawnAsync } from '../../../scripts/utils/spawn-async.mjs'
|
|
11
|
+
|
|
11
12
|
/**
|
|
12
13
|
* @typedef {{ name: string, start: number, end: number }} JscpdFileRef
|
|
13
14
|
* @typedef {{ firstFile: JscpdFileRef, secondFile: JscpdFileRef, lines: number, format: string }} JscpdClone
|
|
@@ -38,18 +39,16 @@ function cloneToViolation(clone) {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
/**
|
|
41
|
-
* Detector js/jscpd_duplicates: дублікати коду через `jscpd` (read-only).
|
|
42
|
+
* Detector js/jscpd_duplicates: дублікати коду через `jscpd` (read-only). Async (не блокує
|
|
43
|
+
* event loop) — детектор може виконуватись у parallel lane `detectAll()` (ADR 260716-1354).
|
|
42
44
|
* @param {import('../../../scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст лінту
|
|
43
|
-
* @returns {import('../../../scripts/lib/lint-surface/types.mjs').LintResult} перелік порушень
|
|
45
|
+
* @returns {Promise<import('../../../scripts/lib/lint-surface/types.mjs').LintResult>} перелік порушень
|
|
44
46
|
*/
|
|
45
|
-
export function lint(ctx) {
|
|
47
|
+
export async function lint(ctx) {
|
|
46
48
|
const cwd = ctx.cwd
|
|
47
49
|
const outDir = mkdtempSync(join(tmpdir(), 'jscpd-'))
|
|
48
50
|
try {
|
|
49
|
-
const r =
|
|
50
|
-
cwd,
|
|
51
|
-
encoding: 'utf8'
|
|
52
|
-
})
|
|
51
|
+
const r = await spawnAsync('bunx', ['jscpd', '.', '--reporters', 'json', '--output', outDir, '--silent'], { cwd })
|
|
53
52
|
let report
|
|
54
53
|
try {
|
|
55
54
|
report = JSON.parse(readFileSync(join(outDir, 'jscpd-report.json'), 'utf8'))
|
|
@@ -32,7 +32,7 @@ export async function lint(ctx) {
|
|
|
32
32
|
}
|
|
33
33
|
if (targets.length === 0) return reporter.result()
|
|
34
34
|
|
|
35
|
-
const code = runKubeconform(targets, ctx.verbose === true)
|
|
35
|
+
const code = await runKubeconform(targets, ctx.verbose === true)
|
|
36
36
|
if (code !== 0 && code !== 127) fail('kubeconform знайшов невалідні маніфести (k8s.mdc)', 'kubeconform')
|
|
37
37
|
|
|
38
38
|
return reporter.result()
|
|
@@ -3,7 +3,6 @@ import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'no
|
|
|
3
3
|
import { readFile, readdir, stat, writeFile } from 'node:fs/promises'
|
|
4
4
|
import { tmpdir } from 'node:os'
|
|
5
5
|
import { basename, dirname, join, relative, resolve } from 'node:path'
|
|
6
|
-
import { spawnSync } from 'node:child_process'
|
|
7
6
|
import { fileURLToPath } from 'node:url'
|
|
8
7
|
|
|
9
8
|
import { isSeq, parse, parseAllDocuments, parseDocument, stringify } from 'yaml'
|
|
@@ -13,6 +12,7 @@ import { loadCursorIgnorePaths } from '../../../scripts/lib/load-cursor-config.m
|
|
|
13
12
|
import { runConftestBatch } from '../../../scripts/lib/run-conftest-batch.mjs'
|
|
14
13
|
import { ensureTool } from '../../../scripts/lib/ensure-tool.mjs'
|
|
15
14
|
import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
|
|
15
|
+
import { spawnAsync } from '../../../scripts/utils/spawn-async.mjs'
|
|
16
16
|
import { walkDir } from '../../../scripts/utils/walkDir.mjs'
|
|
17
17
|
|
|
18
18
|
/** Версія набору схем yannh — узгоджено з k8s.mdc */
|
|
@@ -6798,9 +6798,9 @@ const DATREE_CRD_SCHEMA_LOCATION =
|
|
|
6798
6798
|
* Прогоняє `kubeconform` по k8s-каталогах (read-only валідація схем).
|
|
6799
6799
|
* @param {string[]} dirs абсолютні шляхи k8s-коренів.
|
|
6800
6800
|
* @param {boolean} [verbose] показати повний нативний вивід kubeconform (`stdio: 'inherit'`).
|
|
6801
|
-
* @returns {number} exit-код процесу (127 якщо тул відсутній).
|
|
6801
|
+
* @returns {Promise<number>} exit-код процесу (127 якщо тул відсутній).
|
|
6802
6802
|
*/
|
|
6803
|
-
export function runKubeconform(dirs, verbose = false) {
|
|
6803
|
+
export async function runKubeconform(dirs, verbose = false) {
|
|
6804
6804
|
const args = [
|
|
6805
6805
|
'-summary',
|
|
6806
6806
|
'-kubernetes-version',
|
|
@@ -6813,12 +6813,16 @@ export function runKubeconform(dirs, verbose = false) {
|
|
|
6813
6813
|
...dirs
|
|
6814
6814
|
]
|
|
6815
6815
|
const kubeconformPath = ensureTool('kubeconform')
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6816
|
+
try {
|
|
6817
|
+
const r = await spawnAsync(kubeconformPath, args, { stdio: verbose ? 'inherit' : 'pipe' })
|
|
6818
|
+
return r.exitCode ?? 1
|
|
6819
|
+
} catch (error) {
|
|
6820
|
+
if (error && error.code === 'ENOENT') {
|
|
6821
|
+
console.error('kubeconform не знайдено в PATH. Встанови з https://github.com/yannh/kubeconform#readme')
|
|
6822
|
+
return 127
|
|
6823
|
+
}
|
|
6824
|
+
return 1
|
|
6820
6825
|
}
|
|
6821
|
-
return r.status ?? 1
|
|
6822
6826
|
}
|
|
6823
6827
|
|
|
6824
6828
|
/**
|
|
@@ -6866,14 +6870,17 @@ export async function findKustomizationDirs(dir) {
|
|
|
6866
6870
|
* @param {string} kubectlPath шлях до бінарника kubectl.
|
|
6867
6871
|
* @param {string} dir каталог з kustomization.
|
|
6868
6872
|
* @param {boolean} [verbose] показати stderr тула напряму (`stdio: 'inherit'`).
|
|
6869
|
-
* @returns {{ status: number, stdout:
|
|
6873
|
+
* @returns {Promise<{ status: number, stdout: string }>} exit-код і зібраний YAML.
|
|
6870
6874
|
*/
|
|
6871
|
-
function runKustomizeBuild(kubectlPath, dir, verbose = false) {
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6875
|
+
async function runKustomizeBuild(kubectlPath, dir, verbose = false) {
|
|
6876
|
+
try {
|
|
6877
|
+
const r = await spawnAsync(kubectlPath, ['kustomize', dir], {
|
|
6878
|
+
stdio: ['ignore', 'pipe', verbose ? 'inherit' : 'pipe']
|
|
6879
|
+
})
|
|
6880
|
+
return { status: r.exitCode ?? 1, stdout: r.stdout ?? '' }
|
|
6881
|
+
} catch {
|
|
6882
|
+
return { status: 1, stdout: '' }
|
|
6883
|
+
}
|
|
6877
6884
|
}
|
|
6878
6885
|
|
|
6879
6886
|
/**
|
|
@@ -6882,19 +6889,22 @@ function runKustomizeBuild(kubectlPath, dir, verbose = false) {
|
|
|
6882
6889
|
* @param {string|Buffer} manifest вміст маніфеста для сканування.
|
|
6883
6890
|
* @param {string[]} exceptionsArgs додаткові CLI-аргументи (`--exceptions ...`).
|
|
6884
6891
|
* @param {boolean} [verbose] показати повний нативний вивід kubescape (`stdio: 'inherit'`).
|
|
6885
|
-
* @returns {{ status: number, enoent: boolean }} exit-код і прапорець відсутнього тула.
|
|
6892
|
+
* @returns {Promise<{ status: number, enoent: boolean }>} exit-код і прапорець відсутнього тула.
|
|
6886
6893
|
*/
|
|
6887
|
-
function runKubescapeManifest(kubescapePath, manifest, exceptionsArgs, verbose = false) {
|
|
6894
|
+
async function runKubescapeManifest(kubescapePath, manifest, exceptionsArgs, verbose = false) {
|
|
6888
6895
|
const dir = mkdtempSync(join(tmpdir(), 'nitra-cursor-k8s-'))
|
|
6889
6896
|
const file = join(dir, 'manifest.yaml')
|
|
6890
6897
|
try {
|
|
6891
6898
|
writeFileSync(file, manifest)
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6899
|
+
try {
|
|
6900
|
+
const r = await spawnAsync(kubescapePath, ['scan', file, '--severity-threshold', 'high', ...exceptionsArgs], {
|
|
6901
|
+
stdio: verbose ? 'inherit' : 'pipe'
|
|
6902
|
+
})
|
|
6903
|
+
return { status: r.exitCode ?? 1, enoent: false }
|
|
6904
|
+
} catch (error) {
|
|
6905
|
+
const enoent = Boolean(error && error.code === 'ENOENT')
|
|
6906
|
+
return { status: 1, enoent }
|
|
6907
|
+
}
|
|
6898
6908
|
} finally {
|
|
6899
6909
|
rmSync(dir, { recursive: true, force: true })
|
|
6900
6910
|
}
|
|
@@ -6906,19 +6916,22 @@ function runKubescapeManifest(kubescapePath, manifest, exceptionsArgs, verbose =
|
|
|
6906
6916
|
* @param {string} dir каталог для сканування.
|
|
6907
6917
|
* @param {string[]} exceptionsArgs додаткові CLI-аргументи (`--exceptions ...`).
|
|
6908
6918
|
* @param {boolean} [verbose] показати повний нативний вивід kubescape (`stdio: 'inherit'`) і хід сканування.
|
|
6909
|
-
* @returns {number} exit-код (127 якщо тул відсутній).
|
|
6919
|
+
* @returns {Promise<number>} exit-код (127 якщо тул відсутній).
|
|
6910
6920
|
*/
|
|
6911
|
-
function scanRawK8sDir(kubescapePath, dir, exceptionsArgs, verbose = false) {
|
|
6921
|
+
async function scanRawK8sDir(kubescapePath, dir, exceptionsArgs, verbose = false) {
|
|
6912
6922
|
if (verbose) console.log(`run-k8s: kubescape scan ${dir} (без kustomization — сирий dir-скан)`)
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6923
|
+
try {
|
|
6924
|
+
const r = await spawnAsync(kubescapePath, ['scan', dir, '--severity-threshold', 'high', ...exceptionsArgs], {
|
|
6925
|
+
stdio: verbose ? 'inherit' : 'pipe'
|
|
6926
|
+
})
|
|
6927
|
+
return r.exitCode ?? 1
|
|
6928
|
+
} catch (error) {
|
|
6929
|
+
if (error && error.code === 'ENOENT') {
|
|
6930
|
+
console.error(KUBESCAPE_MISSING_HINT)
|
|
6931
|
+
return 127
|
|
6932
|
+
}
|
|
6933
|
+
return 1
|
|
6920
6934
|
}
|
|
6921
|
-
return r.status ?? 1
|
|
6922
6935
|
}
|
|
6923
6936
|
|
|
6924
6937
|
/**
|
|
@@ -6928,14 +6941,14 @@ function scanRawK8sDir(kubescapePath, dir, exceptionsArgs, verbose = false) {
|
|
|
6928
6941
|
* @param {string[]} kdirs каталоги з kustomization.
|
|
6929
6942
|
* @param {string[]} exceptionsArgs додаткові CLI-аргументи (`--exceptions ...`).
|
|
6930
6943
|
* @param {boolean} [verbose] показати повний нативний вивід kubectl/kubescape і хід сканування.
|
|
6931
|
-
* @returns {number} 0 якщо всі чисті, інакше перший ненульовий exit-код (127 — тул відсутній).
|
|
6944
|
+
* @returns {Promise<number>} 0 якщо всі чисті, інакше перший ненульовий exit-код (127 — тул відсутній).
|
|
6932
6945
|
*/
|
|
6933
|
-
function scanKustomizeK8sDirs(kubectlPath, kubescapePath, kdirs, exceptionsArgs, verbose = false) {
|
|
6946
|
+
async function scanKustomizeK8sDirs(kubectlPath, kubescapePath, kdirs, exceptionsArgs, verbose = false) {
|
|
6934
6947
|
for (const kdir of kdirs) {
|
|
6935
6948
|
if (verbose) console.log(`run-k8s: kubectl kustomize ${kdir} | kubescape scan <tmp>`)
|
|
6936
|
-
const build = runKustomizeBuild(kubectlPath, kdir, verbose)
|
|
6949
|
+
const build = await runKustomizeBuild(kubectlPath, kdir, verbose)
|
|
6937
6950
|
if (build.status !== 0) return build.status
|
|
6938
|
-
const ks = runKubescapeManifest(kubescapePath, build.stdout, exceptionsArgs, verbose)
|
|
6951
|
+
const ks = await runKubescapeManifest(kubescapePath, build.stdout, exceptionsArgs, verbose)
|
|
6939
6952
|
if (ks.enoent) {
|
|
6940
6953
|
console.error(KUBESCAPE_MISSING_HINT)
|
|
6941
6954
|
return 127
|
|
@@ -6960,7 +6973,7 @@ async function runKubescape(dirs, root, verbose = false) {
|
|
|
6960
6973
|
for (const d of dirs) {
|
|
6961
6974
|
const kdirs = await findKustomizationDirs(d)
|
|
6962
6975
|
if (kdirs.length === 0) {
|
|
6963
|
-
const rawStatus = scanRawK8sDir(kubescapePath, d, exceptionsArgs, verbose)
|
|
6976
|
+
const rawStatus = await scanRawK8sDir(kubescapePath, d, exceptionsArgs, verbose)
|
|
6964
6977
|
if (rawStatus !== 0) return rawStatus
|
|
6965
6978
|
continue
|
|
6966
6979
|
}
|
|
@@ -6971,7 +6984,7 @@ async function runKubescape(dirs, root, verbose = false) {
|
|
|
6971
6984
|
return 127
|
|
6972
6985
|
}
|
|
6973
6986
|
}
|
|
6974
|
-
const buildStatus = scanKustomizeK8sDirs(kubectlPath, kubescapePath, kdirs, exceptionsArgs, verbose)
|
|
6987
|
+
const buildStatus = await scanKustomizeK8sDirs(kubectlPath, kubescapePath, kdirs, exceptionsArgs, verbose)
|
|
6975
6988
|
if (buildStatus !== 0) return buildStatus
|
|
6976
6989
|
}
|
|
6977
6990
|
return 0
|
|
@@ -3,9 +3,8 @@ type: JS Module
|
|
|
3
3
|
title: main.mjs
|
|
4
4
|
resource: npm/rules/php/cs_fixer/main.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: de8de231
|
|
7
7
|
model: omlx/gemma-4-e4b-it-OptiQ-4bit
|
|
8
|
-
tier: local-min
|
|
9
8
|
score: 100
|
|
10
9
|
issues: judge:inaccurate:0.98
|
|
11
10
|
judgeModel: openai-codex/gpt-5.4-mini
|