pr-checkmate 3.0.4 → 3.1.0
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/dist/{cli.js → cjs/cli.js} +30 -30
- package/dist/{config → cjs/config}/index.d.ts +2 -2
- package/dist/{config → cjs/config}/index.js +8 -7
- package/dist/{config → cjs/config}/loader.d.ts +1 -1
- package/dist/{config → cjs/config}/loader.js +8 -8
- package/dist/{core → cjs/core}/checks/dependencies/banned-imports.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/dependencies/banned-imports.js +10 -10
- package/dist/{core → cjs/core}/checks/dependencies/circular-deps.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/dependencies/circular-deps.js +27 -27
- package/dist/{core → cjs/core}/checks/dependencies/dependency-check.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/dependencies/dependency-check.js +17 -17
- package/dist/{core → cjs/core}/checks/dependencies/grype-scan.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/dependencies/grype-scan.js +18 -18
- package/dist/{core → cjs/core}/checks/dependencies/license-check.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/dependencies/license-check.js +12 -12
- package/dist/{core → cjs/core}/checks/dependencies/npm-audit.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/dependencies/npm-audit.js +14 -14
- package/dist/{core → cjs/core}/checks/dependencies/outdated-deps.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/dependencies/outdated-deps.js +14 -14
- package/dist/{core → cjs/core}/checks/dependencies/vuln-scan.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/dependencies/vuln-scan.js +14 -14
- package/dist/{core → cjs/core}/checks/findings.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/findings.js +16 -16
- package/dist/{core → cjs/core}/checks/git/custom-rules.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/git/custom-rules.js +19 -19
- package/dist/{core → cjs/core}/checks/git/diff-smell.d.ts +2 -2
- package/dist/{core → cjs/core}/checks/git/diff-smell.js +13 -13
- package/dist/{core → cjs/core}/checks/git/large-files.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/git/large-files.js +15 -15
- package/dist/{core → cjs/core}/checks/git/leftover-debug.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/git/leftover-debug.js +13 -13
- package/dist/{core → cjs/core}/checks/git/lockfile-drift.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/git/lockfile-drift.js +12 -12
- package/dist/{core → cjs/core}/checks/git/merge-conflict.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/git/merge-conflict.js +15 -15
- package/dist/{core → cjs/core}/checks/git/missing-tests.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/git/missing-tests.js +15 -15
- package/dist/{core → cjs/core}/checks/git/todo-fixme.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/git/todo-fixme.js +11 -11
- package/dist/{core → cjs/core}/checks/languages/cpp-format.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/languages/cpp-format.js +18 -18
- package/dist/{core → cjs/core}/checks/languages/external-checks.d.ts +9 -9
- package/dist/{core → cjs/core}/checks/languages/external-checks.js +10 -10
- package/dist/{core → cjs/core}/checks/languages/external-runner.d.ts +2 -2
- package/dist/{core → cjs/core}/checks/languages/external-runner.js +21 -21
- package/dist/{core → cjs/core}/checks/languages/go-format.d.ts +1 -1
- package/dist/cjs/core/checks/languages/go-format.js +202 -0
- package/dist/cjs/core/checks/languages/lint.d.ts +2 -0
- package/dist/{core → cjs/core}/checks/languages/lint.js +21 -21
- package/dist/{core → cjs/core}/checks/languages/prettier.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/languages/prettier.js +18 -18
- package/dist/{core → cjs/core}/checks/languages/python-format.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/languages/python-format.js +14 -14
- package/dist/{core → cjs/core}/checks/languages/python-lint.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/languages/python-lint.js +13 -13
- package/dist/{core → cjs/core}/checks/languages/python-typecheck.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/languages/python-typecheck.js +29 -29
- package/dist/{core → cjs/core}/checks/languages/typecheck.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/languages/typecheck.js +13 -13
- package/dist/{core → cjs/core}/checks/pr/commitlint.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/pr/commitlint.js +12 -12
- package/dist/{core → cjs/core}/checks/pr/pr-body.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/pr/pr-body.js +8 -8
- package/dist/{core → cjs/core}/checks/pr/pr-size-check.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/pr/pr-size-check.js +10 -10
- package/dist/{core → cjs/core}/checks/pr/pr-title-lint.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/pr/pr-title-lint.js +8 -8
- package/dist/{core → cjs/core}/checks/quality/case-collision.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/quality/case-collision.js +7 -7
- package/dist/{core → cjs/core}/checks/quality/config-validation.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/quality/config-validation.js +9 -9
- package/dist/{core → cjs/core}/checks/quality/coverage.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/quality/coverage.js +4 -4
- package/dist/{core → cjs/core}/checks/quality/dead-code.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/quality/dead-code.js +23 -23
- package/dist/{core → cjs/core}/checks/quality/duplicate-check.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/quality/duplicate-check.js +22 -22
- package/dist/{core → cjs/core}/checks/quality/license-header.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/quality/license-header.js +16 -16
- package/dist/{core → cjs/core}/checks/quality/markdown-lint.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/quality/markdown-lint.js +17 -17
- package/dist/{core → cjs/core}/checks/quality/package-size.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/quality/package-size.js +12 -12
- package/dist/{core → cjs/core}/checks/quality/spellcheck.d.ts +2 -2
- package/dist/{core → cjs/core}/checks/quality/spellcheck.js +18 -18
- package/dist/{core → cjs/core}/checks/quality/symlinks.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/quality/symlinks.js +8 -8
- package/dist/{core → cjs/core}/checks/quality/yaml-lint.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/quality/yaml-lint.js +12 -12
- package/dist/{core → cjs/core}/checks/security/diff-security.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/security/diff-security.js +15 -15
- package/dist/{core → cjs/core}/checks/security/dockerfile-security.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/security/dockerfile-security.js +16 -16
- package/dist/{core → cjs/core}/checks/security/migration-safety.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/security/migration-safety.js +17 -17
- package/dist/{core → cjs/core}/checks/security/security.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/security/security.js +21 -21
- package/dist/{core → cjs/core}/checks/security/sensitive-file-guard.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/security/sensitive-file-guard.js +14 -14
- package/dist/{core → cjs/core}/checks/security/workflow-security.d.ts +2 -2
- package/dist/{core → cjs/core}/checks/security/workflow-security.js +14 -14
- package/dist/{core → cjs/core}/checks/tool-install.d.ts +1 -1
- package/dist/{core → cjs/core}/checks/tool-install.js +2 -2
- package/dist/{core → cjs/core}/context.d.ts +1 -1
- package/dist/{core → cjs/core}/context.js +16 -16
- package/dist/cjs/core/index.d.ts +4 -0
- package/dist/{core → cjs/core}/index.js +7 -7
- package/dist/{core → cjs/core}/install/cache.js +2 -2
- package/dist/{core → cjs/core}/install/doctor.d.ts +4 -4
- package/dist/{core → cjs/core}/install/doctor.js +34 -34
- package/dist/{core → cjs/core}/install/ensure.d.ts +2 -2
- package/dist/{core → cjs/core}/install/ensure.js +15 -14
- package/dist/{core → cjs/core}/install/lock.js +4 -4
- package/dist/{core → cjs/core}/install/prepare.d.ts +4 -4
- package/dist/{core → cjs/core}/install/prepare.js +13 -13
- package/dist/{core → cjs/core}/install/profiles.d.ts +2 -2
- package/dist/{core → cjs/core}/install/reuse.d.ts +1 -1
- package/dist/{core → cjs/core}/install/reuse.js +4 -4
- package/dist/{core → cjs/core}/registry.d.ts +1 -1
- package/dist/cjs/core/registry.js +112 -0
- package/dist/{core → cjs/core}/reporters.d.ts +1 -1
- package/dist/{core → cjs/core}/reporters.js +28 -28
- package/dist/{core → cjs/core}/run-checks.d.ts +2 -2
- package/dist/{core → cjs/core}/run-checks.js +31 -31
- package/dist/{core → cjs/core}/targets.d.ts +1 -1
- package/dist/{core → cjs/core}/targets.js +20 -20
- package/dist/{core → cjs/core}/types.d.ts +3 -3
- package/dist/cjs/index.d.ts +12 -0
- package/dist/{index.js → cjs/index.js} +18 -18
- package/dist/{init.js → cjs/init.js} +68 -56
- package/dist/{interfaces → cjs/interfaces}/index.d.ts +3 -3
- package/dist/cjs/package.json +1 -0
- package/dist/{scripts → cjs/scripts}/copy-configs.js +22 -7
- package/dist/{scripts → cjs/scripts}/generate-install-manifests.js +2 -2
- package/dist/{scripts → cjs/scripts}/publish-github.js +4 -4
- package/dist/{utils → cjs/utils}/detect-language.js +12 -12
- package/dist/cjs/utils/execa.js +46 -0
- package/dist/{utils → cjs/utils}/git.js +2 -2
- package/dist/{utils → cjs/utils}/http.js +2 -2
- package/dist/cjs/utils/index.d.ts +15 -0
- package/dist/{utils → cjs/utils}/index.js +13 -13
- package/dist/{utils → cjs/utils}/own-deps.d.ts +2 -0
- package/dist/{utils → cjs/utils}/own-deps.js +1 -0
- package/dist/{utils → cjs/utils}/package-version.js +2 -1
- package/dist/{utils → cjs/utils}/resolve-bin.js +2 -1
- package/dist/{utils → cjs/utils}/update-notifier.js +4 -4
- package/dist/esm/cli.d.ts +2 -0
- package/dist/esm/cli.js +137 -0
- package/dist/esm/config/constants.d.ts +153 -0
- package/dist/esm/config/constants.js +360 -0
- package/dist/esm/config/index.d.ts +40 -0
- package/dist/esm/config/index.js +60 -0
- package/dist/esm/config/loader.d.ts +6 -0
- package/dist/esm/config/loader.js +191 -0
- package/dist/esm/config/pr-checkmate-workflow-docker.yml +79 -0
- package/dist/esm/config/pr-checkmate-workflow.yml +92 -0
- package/dist/esm/core/checks/dependencies/banned-imports.d.ts +2 -0
- package/dist/esm/core/checks/dependencies/banned-imports.js +70 -0
- package/dist/esm/core/checks/dependencies/circular-deps.d.ts +2 -0
- package/dist/esm/core/checks/dependencies/circular-deps.js +155 -0
- package/dist/esm/core/checks/dependencies/dependency-check.d.ts +43 -0
- package/dist/esm/core/checks/dependencies/dependency-check.js +174 -0
- package/dist/esm/core/checks/dependencies/grype-scan.d.ts +22 -0
- package/dist/esm/core/checks/dependencies/grype-scan.js +112 -0
- package/dist/esm/core/checks/dependencies/license-check.d.ts +2 -0
- package/dist/esm/core/checks/dependencies/license-check.js +61 -0
- package/dist/esm/core/checks/dependencies/npm-audit.d.ts +6 -0
- package/dist/esm/core/checks/dependencies/npm-audit.js +80 -0
- package/dist/esm/core/checks/dependencies/outdated-deps.d.ts +2 -0
- package/dist/esm/core/checks/dependencies/outdated-deps.js +66 -0
- package/dist/esm/core/checks/dependencies/vuln-scan.d.ts +2 -0
- package/dist/esm/core/checks/dependencies/vuln-scan.js +54 -0
- package/dist/esm/core/checks/findings.d.ts +299 -0
- package/dist/esm/core/checks/findings.js +701 -0
- package/dist/esm/core/checks/git/custom-rules.d.ts +2 -0
- package/dist/esm/core/checks/git/custom-rules.js +106 -0
- package/dist/esm/core/checks/git/diff-smell.d.ts +19 -0
- package/dist/esm/core/checks/git/diff-smell.js +69 -0
- package/dist/esm/core/checks/git/large-files.d.ts +2 -0
- package/dist/esm/core/checks/git/large-files.js +112 -0
- package/dist/esm/core/checks/git/leftover-debug.d.ts +2 -0
- package/dist/esm/core/checks/git/leftover-debug.js +89 -0
- package/dist/esm/core/checks/git/lockfile-drift.d.ts +2 -0
- package/dist/esm/core/checks/git/lockfile-drift.js +114 -0
- package/dist/esm/core/checks/git/merge-conflict.d.ts +2 -0
- package/dist/esm/core/checks/git/merge-conflict.js +117 -0
- package/dist/esm/core/checks/git/missing-tests.d.ts +2 -0
- package/dist/esm/core/checks/git/missing-tests.js +105 -0
- package/dist/esm/core/checks/git/todo-fixme.d.ts +7 -0
- package/dist/esm/core/checks/git/todo-fixme.js +80 -0
- package/dist/esm/core/checks/languages/cpp-format.d.ts +7 -0
- package/dist/esm/core/checks/languages/cpp-format.js +91 -0
- package/dist/esm/core/checks/languages/external-checks.d.ts +77 -0
- package/dist/esm/core/checks/languages/external-checks.js +197 -0
- package/dist/esm/core/checks/languages/external-runner.d.ts +119 -0
- package/dist/esm/core/checks/languages/external-runner.js +138 -0
- package/dist/esm/core/checks/languages/go-format.d.ts +8 -0
- package/dist/{core → esm/core}/checks/languages/go-format.js +29 -35
- package/dist/esm/core/checks/languages/lint.d.ts +2 -0
- package/dist/esm/core/checks/languages/lint.js +92 -0
- package/dist/esm/core/checks/languages/prettier.d.ts +2 -0
- package/dist/esm/core/checks/languages/prettier.js +85 -0
- package/dist/esm/core/checks/languages/python-format.d.ts +7 -0
- package/dist/esm/core/checks/languages/python-format.js +61 -0
- package/dist/esm/core/checks/languages/python-lint.d.ts +9 -0
- package/dist/esm/core/checks/languages/python-lint.js +55 -0
- package/dist/esm/core/checks/languages/python-typecheck.d.ts +14 -0
- package/dist/esm/core/checks/languages/python-typecheck.js +184 -0
- package/dist/esm/core/checks/languages/ruff-shared.d.ts +10 -0
- package/dist/esm/core/checks/languages/ruff-shared.js +35 -0
- package/dist/esm/core/checks/languages/typecheck.d.ts +2 -0
- package/dist/esm/core/checks/languages/typecheck.js +61 -0
- package/dist/esm/core/checks/pr/commitlint.d.ts +2 -0
- package/dist/esm/core/checks/pr/commitlint.js +102 -0
- package/dist/esm/core/checks/pr/pr-body.d.ts +2 -0
- package/dist/esm/core/checks/pr/pr-body.js +43 -0
- package/dist/esm/core/checks/pr/pr-size-check.d.ts +2 -0
- package/dist/esm/core/checks/pr/pr-size-check.js +42 -0
- package/dist/esm/core/checks/pr/pr-title-lint.d.ts +2 -0
- package/dist/esm/core/checks/pr/pr-title-lint.js +31 -0
- package/dist/esm/core/checks/quality/case-collision.d.ts +8 -0
- package/dist/esm/core/checks/quality/case-collision.js +39 -0
- package/dist/esm/core/checks/quality/config-validation.d.ts +11 -0
- package/dist/esm/core/checks/quality/config-validation.js +188 -0
- package/dist/esm/core/checks/quality/coverage.d.ts +2 -0
- package/dist/esm/core/checks/quality/coverage.js +62 -0
- package/dist/esm/core/checks/quality/dead-code.d.ts +2 -0
- package/dist/esm/core/checks/quality/dead-code.js +124 -0
- package/dist/esm/core/checks/quality/duplicate-check.d.ts +2 -0
- package/dist/esm/core/checks/quality/duplicate-check.js +141 -0
- package/dist/esm/core/checks/quality/license-header.d.ts +7 -0
- package/dist/esm/core/checks/quality/license-header.js +81 -0
- package/dist/esm/core/checks/quality/markdown-lint.d.ts +2 -0
- package/dist/esm/core/checks/quality/markdown-lint.js +104 -0
- package/dist/esm/core/checks/quality/package-size.d.ts +36 -0
- package/dist/esm/core/checks/quality/package-size.js +196 -0
- package/dist/esm/core/checks/quality/spellcheck.d.ts +77 -0
- package/dist/esm/core/checks/quality/spellcheck.js +296 -0
- package/dist/esm/core/checks/quality/symlinks.d.ts +16 -0
- package/dist/esm/core/checks/quality/symlinks.js +57 -0
- package/dist/esm/core/checks/quality/yaml-lint.d.ts +20 -0
- package/dist/esm/core/checks/quality/yaml-lint.js +116 -0
- package/dist/esm/core/checks/security/diff-security.d.ts +2 -0
- package/dist/esm/core/checks/security/diff-security.js +354 -0
- package/dist/esm/core/checks/security/dockerfile-security.d.ts +2 -0
- package/dist/esm/core/checks/security/dockerfile-security.js +98 -0
- package/dist/esm/core/checks/security/migration-safety.d.ts +2 -0
- package/dist/esm/core/checks/security/migration-safety.js +137 -0
- package/dist/esm/core/checks/security/security.d.ts +63 -0
- package/dist/esm/core/checks/security/security.js +201 -0
- package/dist/esm/core/checks/security/sensitive-file-guard.d.ts +2 -0
- package/dist/esm/core/checks/security/sensitive-file-guard.js +160 -0
- package/dist/esm/core/checks/security/workflow-security.d.ts +12 -0
- package/dist/esm/core/checks/security/workflow-security.js +100 -0
- package/dist/esm/core/checks/tool-install.d.ts +102 -0
- package/dist/esm/core/checks/tool-install.js +66 -0
- package/dist/esm/core/commands.d.ts +27 -0
- package/dist/esm/core/commands.js +89 -0
- package/dist/esm/core/context.d.ts +62 -0
- package/dist/esm/core/context.js +241 -0
- package/dist/esm/core/index.d.ts +4 -0
- package/dist/esm/core/index.js +4 -0
- package/dist/esm/core/install/cache.d.ts +58 -0
- package/dist/esm/core/install/cache.js +142 -0
- package/dist/esm/core/install/doctor.d.ts +35 -0
- package/dist/esm/core/install/doctor.js +164 -0
- package/dist/esm/core/install/ensure.d.ts +77 -0
- package/dist/esm/core/install/ensure.js +269 -0
- package/dist/esm/core/install/lock.d.ts +34 -0
- package/dist/esm/core/install/lock.js +92 -0
- package/dist/esm/core/install/merge.d.ts +47 -0
- package/dist/esm/core/install/merge.js +85 -0
- package/dist/esm/core/install/prepare.d.ts +76 -0
- package/dist/esm/core/install/prepare.js +142 -0
- package/dist/esm/core/install/profiles.d.ts +78 -0
- package/dist/esm/core/install/profiles.js +192 -0
- package/dist/esm/core/install/reuse.d.ts +41 -0
- package/dist/esm/core/install/reuse.js +100 -0
- package/dist/esm/core/registry.d.ts +6 -0
- package/dist/esm/core/registry.js +109 -0
- package/dist/esm/core/reporters.d.ts +18 -0
- package/dist/esm/core/reporters.js +266 -0
- package/dist/esm/core/run-checks.d.ts +33 -0
- package/dist/esm/core/run-checks.js +293 -0
- package/dist/esm/core/targets.d.ts +140 -0
- package/dist/esm/core/targets.js +213 -0
- package/dist/esm/core/types.d.ts +195 -0
- package/dist/esm/core/types.js +38 -0
- package/dist/esm/index.d.ts +12 -0
- package/dist/esm/index.js +15 -0
- package/dist/esm/init.d.ts +38 -0
- package/dist/esm/init.js +421 -0
- package/dist/esm/interfaces/index.d.ts +432 -0
- package/dist/esm/interfaces/index.js +2 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/scripts/copy-configs.d.ts +1 -0
- package/dist/esm/scripts/copy-configs.js +64 -0
- package/dist/esm/scripts/generate-install-manifests.d.ts +39 -0
- package/dist/esm/scripts/generate-install-manifests.js +115 -0
- package/dist/esm/scripts/publish-github.d.ts +1 -0
- package/dist/esm/scripts/publish-github.js +45 -0
- package/dist/esm/utils/ci.d.ts +11 -0
- package/dist/esm/utils/ci.js +40 -0
- package/dist/esm/utils/detect-language.d.ts +48 -0
- package/dist/esm/utils/detect-language.js +234 -0
- package/dist/esm/utils/direct-deps.d.ts +21 -0
- package/dist/esm/utils/direct-deps.js +220 -0
- package/dist/esm/utils/execa.d.ts +22 -0
- package/dist/{utils → esm/utils}/execa.js +1 -4
- package/dist/esm/utils/git.d.ts +14 -0
- package/dist/esm/utils/git.js +21 -0
- package/dist/esm/utils/glob.d.ts +24 -0
- package/dist/esm/utils/glob.js +40 -0
- package/dist/esm/utils/http.d.ts +59 -0
- package/dist/esm/utils/http.js +115 -0
- package/dist/esm/utils/index.d.ts +15 -0
- package/dist/esm/utils/index.js +26 -0
- package/dist/esm/utils/logger.d.ts +65 -0
- package/dist/esm/utils/logger.js +157 -0
- package/dist/esm/utils/own-deps.d.ts +16 -0
- package/dist/esm/utils/own-deps.js +107 -0
- package/dist/esm/utils/package-version.d.ts +4 -0
- package/dist/esm/utils/package-version.js +38 -0
- package/dist/esm/utils/pool.d.ts +49 -0
- package/dist/esm/utils/pool.js +113 -0
- package/dist/esm/utils/resolve-bin.d.ts +67 -0
- package/dist/esm/utils/resolve-bin.js +156 -0
- package/dist/esm/utils/result-collector.d.ts +33 -0
- package/dist/esm/utils/result-collector.js +44 -0
- package/dist/esm/utils/update-notifier.d.ts +19 -0
- package/dist/esm/utils/update-notifier.js +108 -0
- package/install-manifests/core/package-lock.json +4 -4
- package/install-manifests/core/package.json +1 -1
- package/package.json +15 -13
- package/dist/core/checks/languages/lint.d.ts +0 -2
- package/dist/core/index.d.ts +0 -4
- package/dist/core/registry.js +0 -112
- package/dist/index.d.ts +0 -12
- package/dist/utils/index.d.ts +0 -15
- /package/dist/{cli.d.ts → cjs/cli.d.ts} +0 -0
- /package/dist/{config → cjs/config}/constants.d.ts +0 -0
- /package/dist/{config → cjs/config}/constants.js +0 -0
- /package/dist/{config → cjs/config}/pr-checkmate-workflow-docker.yml +0 -0
- /package/dist/{config → cjs/config}/pr-checkmate-workflow.yml +0 -0
- /package/dist/{core → cjs/core}/checks/languages/ruff-shared.d.ts +0 -0
- /package/dist/{core → cjs/core}/checks/languages/ruff-shared.js +0 -0
- /package/dist/{core → cjs/core}/commands.d.ts +0 -0
- /package/dist/{core → cjs/core}/commands.js +0 -0
- /package/dist/{core → cjs/core}/install/cache.d.ts +0 -0
- /package/dist/{core → cjs/core}/install/lock.d.ts +0 -0
- /package/dist/{core → cjs/core}/install/merge.d.ts +0 -0
- /package/dist/{core → cjs/core}/install/merge.js +0 -0
- /package/dist/{core → cjs/core}/install/profiles.js +0 -0
- /package/dist/{core → cjs/core}/types.js +0 -0
- /package/dist/{init.d.ts → cjs/init.d.ts} +0 -0
- /package/dist/{interfaces → cjs/interfaces}/index.js +0 -0
- /package/dist/{scripts → cjs/scripts}/copy-configs.d.ts +0 -0
- /package/dist/{scripts → cjs/scripts}/generate-install-manifests.d.ts +0 -0
- /package/dist/{scripts → cjs/scripts}/publish-github.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/ci.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/ci.js +0 -0
- /package/dist/{utils → cjs/utils}/detect-language.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/direct-deps.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/direct-deps.js +0 -0
- /package/dist/{utils → cjs/utils}/execa.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/git.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/glob.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/glob.js +0 -0
- /package/dist/{utils → cjs/utils}/http.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/logger.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/logger.js +0 -0
- /package/dist/{utils → cjs/utils}/package-version.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/pool.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/pool.js +0 -0
- /package/dist/{utils → cjs/utils}/resolve-bin.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/result-collector.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/result-collector.js +0 -0
- /package/dist/{utils → cjs/utils}/update-notifier.d.ts +0 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { isCiEnvironment, logger } from '../utils/index.js';
|
|
2
|
+
import { packageVersion } from '../utils/package-version.js';
|
|
3
|
+
import { Semaphore, resolveConcurrency, runPool, withTimeout } from '../utils/pool.js';
|
|
4
|
+
import { clearToolPaths } from '../utils/resolve-bin.js';
|
|
5
|
+
import { ResultCollector } from '../utils/result-collector.js';
|
|
6
|
+
import { buildContext } from './context.js';
|
|
7
|
+
import { prepareTooling, noTooling } from './install/prepare.js';
|
|
8
|
+
import { REGISTRY } from './registry.js';
|
|
9
|
+
import { report } from './reporters.js';
|
|
10
|
+
import { PHASES, } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Resolve which checks to run: the built-in registry, custom `opts.checks`, or a mix.
|
|
13
|
+
* - no custom checks → the built-in registry
|
|
14
|
+
* - `extendRegistry !== false` → registry + custom (a custom check overrides a
|
|
15
|
+
* built-in one sharing its name)
|
|
16
|
+
* - `extendRegistry === false` → only the provided custom checks
|
|
17
|
+
*/
|
|
18
|
+
function resolveChecks(opts) {
|
|
19
|
+
const custom = opts.checks ?? [];
|
|
20
|
+
if (opts.extendRegistry === false)
|
|
21
|
+
return custom;
|
|
22
|
+
if (custom.length === 0)
|
|
23
|
+
return REGISTRY;
|
|
24
|
+
const byName = new Map();
|
|
25
|
+
for (const c of REGISTRY)
|
|
26
|
+
byName.set(c.name, c);
|
|
27
|
+
for (const c of custom)
|
|
28
|
+
byName.set(c.name, c); // custom wins on name clash
|
|
29
|
+
return [...byName.values()];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Emit the report via a preset channel or a custom reporter function.
|
|
33
|
+
*
|
|
34
|
+
* Returns whether delivery succeeded so the caller can honour
|
|
35
|
+
* `reporter.onPostFailure`. A custom function owns its own error handling, so it
|
|
36
|
+
* is always treated as delivered.
|
|
37
|
+
*/
|
|
38
|
+
async function emitReport(reporter, result, cfg) {
|
|
39
|
+
if (typeof reporter === 'function') {
|
|
40
|
+
await reporter(result);
|
|
41
|
+
return { attempted: false, ok: true };
|
|
42
|
+
}
|
|
43
|
+
return report(reporter ?? 'stdout', result, cfg);
|
|
44
|
+
}
|
|
45
|
+
/** Apply only/skip filters to the resolved check set. */
|
|
46
|
+
function selectChecks(checks, opts) {
|
|
47
|
+
const only = opts.only && opts.only.length > 0 ? new Set(opts.only) : null;
|
|
48
|
+
const skip = new Set(opts.skip ?? []);
|
|
49
|
+
return checks.filter(c => {
|
|
50
|
+
if (skip.has(c.name))
|
|
51
|
+
return false;
|
|
52
|
+
if (only && !only.has(c.name))
|
|
53
|
+
return false;
|
|
54
|
+
return true;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function errMessage(err) {
|
|
58
|
+
return err instanceof Error ? err.message.split('\n')[0].slice(0, 160) : String(err);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Apply the universal `config.severity` override for a check. Only remaps
|
|
62
|
+
* between warn↔fail (pass/skip are never changed); `off` is handled earlier by
|
|
63
|
+
* excluding the check from the run entirely. This is what lets a repo turn any
|
|
64
|
+
* advisory check into a gate, or downgrade a blocking one, by name.
|
|
65
|
+
*/
|
|
66
|
+
export function applySeverity(name, status, ctx) {
|
|
67
|
+
const sev = ctx.config.severity?.[name];
|
|
68
|
+
if (sev === 'error' && status === 'warn')
|
|
69
|
+
return 'fail';
|
|
70
|
+
if (sev === 'warn' && status === 'fail')
|
|
71
|
+
return 'warn';
|
|
72
|
+
return status;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Whether a diff-only check's skip is worth a row in the report.
|
|
76
|
+
*
|
|
77
|
+
* The two cases look identical from inside the check and are opposites to the
|
|
78
|
+
* person reading the result:
|
|
79
|
+
*
|
|
80
|
+
* - The range is missing because the run FELL BACK to a full scan — a shallow
|
|
81
|
+
* clone, an unreachable PR base. Coverage was lost without anyone choosing
|
|
82
|
+
* that, and saying nothing is how it stays lost (ticket 057).
|
|
83
|
+
* - The full scan was ASKED FOR. `--full` means "look at everything now", so
|
|
84
|
+
* three rows explaining that three checks review a diff repeat what the reader
|
|
85
|
+
* just said and push the real findings further down.
|
|
86
|
+
*
|
|
87
|
+
* `always` and `never` remain absolute; only `auto` distinguishes them.
|
|
88
|
+
*/
|
|
89
|
+
export function shouldReportDiffSkip(scope, reportSkipped = 'auto') {
|
|
90
|
+
if (reportSkipped === 'never')
|
|
91
|
+
return false;
|
|
92
|
+
if (reportSkipped === 'always')
|
|
93
|
+
return true;
|
|
94
|
+
const askedForFullScan = scope?.kind === 'full' && !scope.degraded;
|
|
95
|
+
return !askedForFullScan;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Why a `requiresDiff` check could not run, phrased for the person reading the
|
|
99
|
+
* report. A full scan the client asked for is expected; one we fell back to is
|
|
100
|
+
* a configuration problem, and the message says which.
|
|
101
|
+
*/
|
|
102
|
+
function noDiffReason(ctx) {
|
|
103
|
+
const scope = ctx.scope;
|
|
104
|
+
if (scope?.degraded)
|
|
105
|
+
return `no diff range (${scope.reason}) — set fetch-depth: 0`;
|
|
106
|
+
return 'full scan — this check reviews a diff';
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Whether a skip came from the missing diff range, as opposed to any other reason
|
|
110
|
+
* a check can decline to run.
|
|
111
|
+
*
|
|
112
|
+
* `reportDegradedScope` counts these to say how much coverage a fallback to a full
|
|
113
|
+
* scan cost. It used to count every skip in the report, which already included
|
|
114
|
+
* unrelated ones ("mypy not installed"), and tooling skips would have added more —
|
|
115
|
+
* turning a number that means "checks you lost to a shallow clone" into "skips,
|
|
116
|
+
* roughly". Matched on the phrasing {@link noDiffReason} produces, which is the
|
|
117
|
+
* only thing that distinguishes them once a skip is just a status and a string.
|
|
118
|
+
*/
|
|
119
|
+
function isDiffSkip(details) {
|
|
120
|
+
return Boolean(details && /^no diff range|^full scan — this check reviews a diff/.test(details));
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Run one check: gate on `applies`, execute, translate throws into `fail`.
|
|
124
|
+
*
|
|
125
|
+
* `heavy` admits only a couple of expensive checks at a time, and the per-check
|
|
126
|
+
* timeout means one that never returns costs a warning rather than the run.
|
|
127
|
+
*/
|
|
128
|
+
async function runOne(check, ctx, collector, heavy, tooling) {
|
|
129
|
+
try {
|
|
130
|
+
// A diff-only check without a range is usually a hole in coverage rather
|
|
131
|
+
// than an irrelevance, so it is reported as a skip and the gap stays visible
|
|
132
|
+
// (ticket 057).
|
|
133
|
+
//
|
|
134
|
+
// Except when the whole-repository scan was ASKED FOR. `--full` means "look
|
|
135
|
+
// at everything now", and answering with three rows explaining that three
|
|
136
|
+
// checks review a diff tells the reader something they just said themselves.
|
|
137
|
+
// A degraded scope still reports: there the missing range is a symptom —
|
|
138
|
+
// a shallow clone, an unreachable base — and staying quiet about it is how
|
|
139
|
+
// coverage disappears unnoticed.
|
|
140
|
+
if (check.requiresDiff && !ctx.baseSha) {
|
|
141
|
+
if (shouldReportDiffSkip(ctx.scope, ctx.config.run?.reportSkipped) &&
|
|
142
|
+
(await check.applies(ctx))) {
|
|
143
|
+
collector.register(check.name, 'skip', noDiffReason(ctx));
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (!(await check.applies(ctx))) {
|
|
148
|
+
// A check that doesn't apply is irrelevant to this project/run — e.g. a
|
|
149
|
+
// language-specific check (SwiftLint, ktlint, Ruff, C++) in a repo of a
|
|
150
|
+
// different language, or a check disabled in config. Omit it from the report
|
|
151
|
+
// entirely rather than listing it as "not applicable": in a single-language
|
|
152
|
+
// repo those rows are pure noise and inflate the skipped count. Runtime skips
|
|
153
|
+
// from run() (e.g. "no relevant files changed") are still reported.
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
// Tooling is checked AFTER `applies`, so the row only appears for a check
|
|
157
|
+
// that would otherwise have run. A Ruff row in a TypeScript repository would
|
|
158
|
+
// be noise; a Ruff row in a Python repository whose toolchain never arrived
|
|
159
|
+
// is the whole point — ticket 057, applied to tooling instead of diffs.
|
|
160
|
+
const missing = tooling.unavailable.get(check.name);
|
|
161
|
+
if (missing) {
|
|
162
|
+
// Severity still has the last word, as it does for every other outcome: a
|
|
163
|
+
// client who made this check advisory does not get a hard failure out of
|
|
164
|
+
// `onMissingTool: "error"`, and one who promoted it does.
|
|
165
|
+
const status = applySeverity(check.name, tooling.fatal ? 'fail' : 'skip', ctx);
|
|
166
|
+
logger.warn(`[runChecks]: ${status === 'fail' ? '❌' : '⏭️'} ${check.name}: ${missing}`);
|
|
167
|
+
collector.register(check.name, status, `tooling unavailable — ${missing}`);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const started = Date.now();
|
|
171
|
+
const timeoutMs = ctx.config.run?.checkTimeoutMs ?? 300000;
|
|
172
|
+
const execute = () => withTimeout(check.run(ctx), timeoutMs, () => {
|
|
173
|
+
logger.warn(`[runChecks]: ⏱️ ${check.name} exceeded ${timeoutMs}ms`);
|
|
174
|
+
return { status: 'warn', details: `timed out after ${timeoutMs}ms` };
|
|
175
|
+
});
|
|
176
|
+
const outcome = check.weight === 'heavy' ? await heavy.run(execute) : await execute();
|
|
177
|
+
if (ctx.config.run?.timings) {
|
|
178
|
+
logger.info(`[runChecks]: ${check.name} took ${Date.now() - started}ms`);
|
|
179
|
+
}
|
|
180
|
+
collector.register(check.name, applySeverity(check.name, outcome.status, ctx), outcome.details);
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
// An unexpected throw in a blocking check fails the run; in informational/format
|
|
184
|
+
// phases it only warns — those phases never block, matching the legacy pipeline.
|
|
185
|
+
const base = check.phase === 'blocking' ? 'fail' : 'warn';
|
|
186
|
+
const status = applySeverity(check.name, base, ctx);
|
|
187
|
+
logger.error(`[runChecks]: ${status === 'fail' ? '❌' : '⚠️'} ${check.name} threw: ${err}`);
|
|
188
|
+
collector.register(check.name, status, errMessage(err));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Run all applicable checks and return a structured report.
|
|
193
|
+
*
|
|
194
|
+
* Never throws for check violations and never calls `process.exit` — callers
|
|
195
|
+
* (CLI, PRValidator) decide what a failure means. Phases run in order; checks
|
|
196
|
+
* within a phase run in parallel.
|
|
197
|
+
*/
|
|
198
|
+
export async function runChecks(opts = {}) {
|
|
199
|
+
const ctx = await buildContext(opts);
|
|
200
|
+
// `severity: { "<name>": "off" }` disables any check by name, universally —
|
|
201
|
+
// a superset of the per-check `enabled: false` flags.
|
|
202
|
+
const off = ctx.config.severity ?? {};
|
|
203
|
+
const selected = selectChecks(resolveChecks(opts), opts).filter(c => off[c.name] !== 'off');
|
|
204
|
+
const collector = new ResultCollector();
|
|
205
|
+
// Config Validation verifies that `severity` keys name real checks. It reads
|
|
206
|
+
// the list from here rather than importing the registry, which would make
|
|
207
|
+
// registry → check → registry a cycle.
|
|
208
|
+
ctx.checkNames = resolveChecks(opts).map(c => c.name);
|
|
209
|
+
// Before any check starts: make sure the tools they need are reachable, and
|
|
210
|
+
// learn which ones are not. Doing this per-check would fetch the same profile
|
|
211
|
+
// from several checks at once and leave each of them guessing on failure.
|
|
212
|
+
const tooling = await prepareToolingFor(ctx, selected);
|
|
213
|
+
const limit = resolveConcurrency(ctx.config.run?.concurrency);
|
|
214
|
+
const heavyLimit = Math.max(1, ctx.config.run?.heavyConcurrency ?? 1);
|
|
215
|
+
const heavy = new Semaphore(heavyLimit);
|
|
216
|
+
for (const phase of PHASES) {
|
|
217
|
+
const inPhase = selected.filter(c => c.phase === phase);
|
|
218
|
+
await runPool(inPhase.map(c => () => runOne(c, ctx, collector, heavy, tooling)), limit);
|
|
219
|
+
}
|
|
220
|
+
reportDegradedScope(ctx, collector);
|
|
221
|
+
const build = () => {
|
|
222
|
+
const summary = collector.getSummary();
|
|
223
|
+
return { results: collector.getResults(), summary, ok: summary.failed === 0 };
|
|
224
|
+
};
|
|
225
|
+
const result = build();
|
|
226
|
+
const posted = await emitReport(opts.reporter, result, ctx.config.reporter ?? {});
|
|
227
|
+
// A summary that never reached the PR can matter as much as a finding. The row
|
|
228
|
+
// is added after the report was rendered — the printed table is what the run
|
|
229
|
+
// actually found — and the returned report carries it so the exit code can.
|
|
230
|
+
const onFailure = ctx.config.reporter?.onPostFailure ?? 'warn';
|
|
231
|
+
if (posted.attempted && !posted.ok && onFailure !== 'off') {
|
|
232
|
+
collector.register('Reporting', onFailure === 'error' ? 'fail' : 'warn', posted.error);
|
|
233
|
+
return build();
|
|
234
|
+
}
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Prepare the tooling the selected checks need, or explain what is missing.
|
|
239
|
+
*
|
|
240
|
+
* Separate from `prepareTooling` itself so the failure of the *preparation* is
|
|
241
|
+
* never the failure of the *run*: fetching tooling is best-effort by nature —
|
|
242
|
+
* offline runners, read-only caches, a registry having a bad day — and a throw
|
|
243
|
+
* here would take down a run that could still have done most of its job. The
|
|
244
|
+
* checks that needed the tooling report it themselves, one row each.
|
|
245
|
+
*
|
|
246
|
+
* `install.strategy: "manual"` and a caller that supplied its own checks both
|
|
247
|
+
* arrive here; neither is special-cased, because a profile whose packages ship
|
|
248
|
+
* with us needs no preparation either way and the loop exits having done nothing.
|
|
249
|
+
*/
|
|
250
|
+
async function prepareToolingFor(ctx, selected) {
|
|
251
|
+
try {
|
|
252
|
+
return await prepareTooling({
|
|
253
|
+
cwd: ctx.cwd,
|
|
254
|
+
config: ctx.config,
|
|
255
|
+
// A hand-built context (the SDK, a test) carries no counts. Treating its
|
|
256
|
+
// languages as real rather than marker-only is the honest reading: a caller
|
|
257
|
+
// that names a language is asserting it, not guessing.
|
|
258
|
+
usage: ctx.usage ?? ctx.languages.map(lang => ({ lang, files: 1, marker: true })),
|
|
259
|
+
selected: selected.map(c => c.name),
|
|
260
|
+
version: packageVersion(),
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
logger.warn(`[install]: ⚠️ could not prepare tooling: ${errMessage(err)}`);
|
|
265
|
+
// Preparation may have registered roots before it threw. Leaving them behind
|
|
266
|
+
// would point resolution at a half-installed directory for the whole run.
|
|
267
|
+
clearToolPaths();
|
|
268
|
+
return noTooling();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Surface a scope that silently narrowed the run.
|
|
273
|
+
*
|
|
274
|
+
* Falling back to a full scan keeps the file-based checks working, but it drops
|
|
275
|
+
* every diff-only check — most of the security surface — and the report gives no
|
|
276
|
+
* sign of it. Outside CI a full scan is simply the default, so this stays quiet
|
|
277
|
+
* there; inside CI it means the range could not be built, which is a
|
|
278
|
+
* configuration problem the client should hear about.
|
|
279
|
+
*
|
|
280
|
+
* `run.onDegradedScope: "error"` turns it into a gate, for repos where losing
|
|
281
|
+
* the security checks silently is worse than a red build.
|
|
282
|
+
*/
|
|
283
|
+
function reportDegradedScope(ctx, collector) {
|
|
284
|
+
const mode = ctx.config.run?.onDegradedScope ?? 'warn';
|
|
285
|
+
if (mode === 'off' || !ctx.scope?.degraded || !isCiEnvironment())
|
|
286
|
+
return;
|
|
287
|
+
const dropped = collector
|
|
288
|
+
.getResults()
|
|
289
|
+
.filter(r => r.status === 'skip' && isDiffSkip(r.details)).length;
|
|
290
|
+
const detail = `${ctx.scope.reason}; ${dropped} diff-only check(s) did not run — set fetch-depth: 0`;
|
|
291
|
+
logger.error(`[runChecks]: ${mode === 'error' ? '❌' : '⚠️'} Degraded scope: ${detail}`);
|
|
292
|
+
collector.register('Scan Scope', mode === 'error' ? 'fail' : 'warn', detail);
|
|
293
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { type CheckContext, type CheckOutcome } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Drop files that live under any excluded directory (client `ignoreDirs` or the
|
|
4
|
+
* curated default), matched on any path segment so nesting depth doesn't matter.
|
|
5
|
+
* Applied to every check's file list so build/tooling/report dirs are never
|
|
6
|
+
* scanned, even when committed.
|
|
7
|
+
*
|
|
8
|
+
* `keepDirs` un-ignores specific segments for a single check — e.g. YAML lint
|
|
9
|
+
* keeps the CI dirs (`.github`, `.gitlab`, `.circleci`), which are ignored
|
|
10
|
+
* globally but hold the workflow files a YAML linter most needs to see.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Drop files the client excluded by path.
|
|
14
|
+
*
|
|
15
|
+
* Separate from `ignoreDirs`, which matches a directory *name* on any segment:
|
|
16
|
+
* that cannot express "this one file" and cannot express "this one path" —
|
|
17
|
+
* `ignoreDirs: ["generated"]` silences every `generated/` in the repository, not
|
|
18
|
+
* the one that is actually generated. Globs answer both, and because this sits
|
|
19
|
+
* in the one function every file-scanning check calls, a client writes the rule
|
|
20
|
+
* once instead of hunting for the right key on each check.
|
|
21
|
+
*
|
|
22
|
+
* Exported because three checks build their file list themselves and cannot go
|
|
23
|
+
* through the resolvers above — and `excludePaths` is documented as "the only
|
|
24
|
+
* exclusion that reaches every check", which was not true of them (ticket 080).
|
|
25
|
+
* Each has a real reason to skip `sourcePath` and `ignoreDirs`; none has one to
|
|
26
|
+
* ignore a client saying "do not look here".
|
|
27
|
+
*/
|
|
28
|
+
export declare function excludeClientPaths(files: string[], cwd: string): string[];
|
|
29
|
+
/**
|
|
30
|
+
* Lists the files a language check should operate on, matched by pathspec `globs`
|
|
31
|
+
* (e.g. `['*.py']`). Uses the PR diff range when available (delta mode), else
|
|
32
|
+
* scans all tracked files. Returns an empty array when nothing matches, and
|
|
33
|
+
* `null` when git itself is unusable (caller should `skip`).
|
|
34
|
+
*
|
|
35
|
+
* Only ACMR files are returned (Added/Copied/Modified/Renamed) — deletions are
|
|
36
|
+
* excluded. Results are additionally filtered to paths that still exist on disk,
|
|
37
|
+
* so a rename/move within the diff range (where the old path shows up but no
|
|
38
|
+
* longer exists) can never hand a missing path to a downstream tool like ESLint
|
|
39
|
+
* or Prettier, which crash hard on a non-existent pattern.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveTargetFiles(ctx: CheckContext, globs: string[], opts?: {
|
|
42
|
+
keepDirs?: string[];
|
|
43
|
+
}): Promise<string[] | null>;
|
|
44
|
+
/**
|
|
45
|
+
* Like {@link resolveTargetFiles} but restricts results to the configured
|
|
46
|
+
* `sourcePath`(s). Preserves the semantics of the old `sourcePath` full-scan
|
|
47
|
+
* fallback while gaining delta support on every platform via `ctx.baseSha`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveScopedTargetFiles(ctx: CheckContext, globs: string[], sourcePaths: string[]): Promise<string[] | null>;
|
|
50
|
+
/**
|
|
51
|
+
* The files to work on, or the outcome to return instead of working.
|
|
52
|
+
*
|
|
53
|
+
* One value rather than two, because the two-value shape is what kept going
|
|
54
|
+
* wrong. Every file-scanning check ends up writing the same triple —
|
|
55
|
+
*
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const targets = await resolveTargetFiles(ctx, globs);
|
|
58
|
+
* if (targets === null) return skip('git unavailable');
|
|
59
|
+
* if (targets.length === 0) return pass('no Kotlin files changed');
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* — and it was written out seventeen times, with nineteen exact copies of the
|
|
63
|
+
* middle line and seven copies of the comment explaining why the two cases are
|
|
64
|
+
* not the same (ticket 079). The point of collapsing it is not the four lines. It
|
|
65
|
+
* is that `null` means "git could not tell us" and `[]` means "git did and there
|
|
66
|
+
* is nothing", they are opposite answers, and a check that conflates them reports
|
|
67
|
+
* a clean pass over a tree it never opened — which is ticket 072, and before that
|
|
68
|
+
* ticket 067, and both were found in security checks, where it cost the most.
|
|
69
|
+
*
|
|
70
|
+
* Written this way round — an array or an outcome, not a nullable array — so the
|
|
71
|
+
* caller's one line is `if (!Array.isArray(targets)) return targets;` and there
|
|
72
|
+
* is no second case left to forget.
|
|
73
|
+
*
|
|
74
|
+
* Not exported: it is the return type of {@link resolveTargets} and every caller
|
|
75
|
+
* gets it by inference, so exporting it would only widen the public surface and
|
|
76
|
+
* show up as an unused export in our own Dead Code check.
|
|
77
|
+
*/
|
|
78
|
+
type TargetsOrOutcome = string[] | CheckOutcome;
|
|
79
|
+
/** How a check wants an empty file list phrased, and at which status. */
|
|
80
|
+
type EmptyResult = {
|
|
81
|
+
pass: string;
|
|
82
|
+
} | {
|
|
83
|
+
skip: string;
|
|
84
|
+
};
|
|
85
|
+
/** Options for {@link resolveTargets} beyond the globs themselves. */
|
|
86
|
+
interface ResolveOptions {
|
|
87
|
+
/**
|
|
88
|
+
* What no matching files means to this check, and in what words.
|
|
89
|
+
*
|
|
90
|
+
* Not defaulted, and that is deliberate: the two halves of the corpus disagree.
|
|
91
|
+
* A language check has genuinely nothing to do when its language is not in the
|
|
92
|
+
* diff, so `{ pass: 'no Kotlin files changed' }`. A whole-tree check that finds
|
|
93
|
+
* no files at all has not reviewed the repository, so `{ skip: 'no tracked
|
|
94
|
+
* files' }`. Picking one for the caller would make half of them wrong.
|
|
95
|
+
*/
|
|
96
|
+
onEmpty: EmptyResult;
|
|
97
|
+
/** Walk every tracked file instead of the PR diff range. */
|
|
98
|
+
wholeTree?: boolean;
|
|
99
|
+
/** Restrict the result to the configured `sourcePath`(s). */
|
|
100
|
+
scoped?: boolean;
|
|
101
|
+
/** Un-ignore specific directory segments, as in {@link resolveTargetFiles}. */
|
|
102
|
+
keepDirs?: string[];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Resolve a check's file list, or the outcome it should report instead.
|
|
106
|
+
*
|
|
107
|
+
* The one entry point for checks that operate on whole files. It wraps
|
|
108
|
+
* {@link resolveTargetFiles} and {@link listTrackedFiles} rather than replacing
|
|
109
|
+
* them — a check that needs the raw list (because it distinguishes the two cases
|
|
110
|
+
* itself, or scans in several passes) still calls those directly.
|
|
111
|
+
*/
|
|
112
|
+
export declare function resolveTargets(ctx: CheckContext, globs: string[], opts: ResolveOptions): Promise<TargetsOrOutcome>;
|
|
113
|
+
/**
|
|
114
|
+
* List ALL tracked files matching `globs` (never delta), scoped to
|
|
115
|
+
* `sourcePath`(s).
|
|
116
|
+
*
|
|
117
|
+
* For whole-repo analyses like circular-dependency detection that need the full
|
|
118
|
+
* graph, not just the PR diff. Using `git ls-files` means untracked
|
|
119
|
+
* `node_modules` is excluded by construction.
|
|
120
|
+
*
|
|
121
|
+
* `keepDirs` un-ignores segments for one caller, exactly as in
|
|
122
|
+
* {@link resolveTargetFiles}. It was missing here, and that made Workflow
|
|
123
|
+
* Security blind on the whole-tree path: `.github` is in the default ignore list,
|
|
124
|
+
* so `git ls-files .github/workflows/*.yml` returned the workflow and this
|
|
125
|
+
* function then dropped it. A blocking security check reported a clean pass over
|
|
126
|
+
* files it had never been handed — the surviving half of ticket 067, found by
|
|
127
|
+
* level 1 of the release regression plan.
|
|
128
|
+
*
|
|
129
|
+
* `null` when git could not answer, `[]` when it answered "nothing". The two are
|
|
130
|
+
* opposites to a caller and used to be the same value: `reject: false` keeps a
|
|
131
|
+
* failed git out of the catch below, so a directory git cannot read returned an
|
|
132
|
+
* empty list, and every caller read that as "nothing to review" and passed. On a
|
|
133
|
+
* directory holding real code that produced three green rows — Diff Security,
|
|
134
|
+
* Workflow Security, Diff Smells — over files none of them had opened
|
|
135
|
+
* (ticket 072).
|
|
136
|
+
*/
|
|
137
|
+
export declare function listTrackedFiles(ctx: CheckContext, globs: string[], sourcePaths: string[], opts?: {
|
|
138
|
+
keepDirs?: string[];
|
|
139
|
+
}): Promise<string[] | null>;
|
|
140
|
+
export {};
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getIgnoredDirs, getSourcePaths } from '../config/index.js';
|
|
4
|
+
import { loadProjectConfig } from '../config/loader.js';
|
|
5
|
+
import { execa } from '../utils/execa.js';
|
|
6
|
+
import { isUnderIgnoredDir, logger, matchesAnyGlob } from '../utils/index.js';
|
|
7
|
+
import { pass, skip } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Drop files that live under any excluded directory (client `ignoreDirs` or the
|
|
10
|
+
* curated default), matched on any path segment so nesting depth doesn't matter.
|
|
11
|
+
* Applied to every check's file list so build/tooling/report dirs are never
|
|
12
|
+
* scanned, even when committed.
|
|
13
|
+
*
|
|
14
|
+
* `keepDirs` un-ignores specific segments for a single check — e.g. YAML lint
|
|
15
|
+
* keeps the CI dirs (`.github`, `.gitlab`, `.circleci`), which are ignored
|
|
16
|
+
* globally but hold the workflow files a YAML linter most needs to see.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Drop files the client excluded by path.
|
|
20
|
+
*
|
|
21
|
+
* Separate from `ignoreDirs`, which matches a directory *name* on any segment:
|
|
22
|
+
* that cannot express "this one file" and cannot express "this one path" —
|
|
23
|
+
* `ignoreDirs: ["generated"]` silences every `generated/` in the repository, not
|
|
24
|
+
* the one that is actually generated. Globs answer both, and because this sits
|
|
25
|
+
* in the one function every file-scanning check calls, a client writes the rule
|
|
26
|
+
* once instead of hunting for the right key on each check.
|
|
27
|
+
*
|
|
28
|
+
* Exported because three checks build their file list themselves and cannot go
|
|
29
|
+
* through the resolvers above — and `excludePaths` is documented as "the only
|
|
30
|
+
* exclusion that reaches every check", which was not true of them (ticket 080).
|
|
31
|
+
* Each has a real reason to skip `sourcePath` and `ignoreDirs`; none has one to
|
|
32
|
+
* ignore a client saying "do not look here".
|
|
33
|
+
*/
|
|
34
|
+
export function excludeClientPaths(files, cwd) {
|
|
35
|
+
let patterns = [];
|
|
36
|
+
try {
|
|
37
|
+
patterns = loadProjectConfig(cwd).excludePaths ?? [];
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// A config that cannot be read is the loader's problem to report, not a
|
|
41
|
+
// reason to scan nothing.
|
|
42
|
+
}
|
|
43
|
+
return patterns.length > 0 ? files.filter(f => !matchesAnyGlob(f, patterns)) : files;
|
|
44
|
+
}
|
|
45
|
+
function excludeIgnoredDirs(files, cwd, keepDirs = []) {
|
|
46
|
+
const ignored = new Set(getIgnoredDirs(cwd));
|
|
47
|
+
for (const dir of keepDirs)
|
|
48
|
+
ignored.delete(dir);
|
|
49
|
+
if (ignored.size === 0)
|
|
50
|
+
return files;
|
|
51
|
+
return files.filter(f => !isUnderIgnoredDir(f, ignored));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Lists the files a language check should operate on, matched by pathspec `globs`
|
|
55
|
+
* (e.g. `['*.py']`). Uses the PR diff range when available (delta mode), else
|
|
56
|
+
* scans all tracked files. Returns an empty array when nothing matches, and
|
|
57
|
+
* `null` when git itself is unusable (caller should `skip`).
|
|
58
|
+
*
|
|
59
|
+
* Only ACMR files are returned (Added/Copied/Modified/Renamed) — deletions are
|
|
60
|
+
* excluded. Results are additionally filtered to paths that still exist on disk,
|
|
61
|
+
* so a rename/move within the diff range (where the old path shows up but no
|
|
62
|
+
* longer exists) can never hand a missing path to a downstream tool like ESLint
|
|
63
|
+
* or Prettier, which crash hard on a non-existent pattern.
|
|
64
|
+
*/
|
|
65
|
+
export async function resolveTargetFiles(ctx, globs, opts = {}) {
|
|
66
|
+
try {
|
|
67
|
+
const args = ctx.baseSha
|
|
68
|
+
? [
|
|
69
|
+
'diff',
|
|
70
|
+
'--name-only',
|
|
71
|
+
'--diff-filter=ACMR',
|
|
72
|
+
ctx.baseSha,
|
|
73
|
+
ctx.headSha ?? 'HEAD',
|
|
74
|
+
'--',
|
|
75
|
+
...globs,
|
|
76
|
+
]
|
|
77
|
+
: ['ls-files', ...globs];
|
|
78
|
+
const cacheKey = `t:${args.join('')}`;
|
|
79
|
+
let stdout = ctx.cache?.get(cacheKey);
|
|
80
|
+
const fromCache = stdout !== undefined;
|
|
81
|
+
if (stdout === undefined) {
|
|
82
|
+
// `reject: false` keeps a failed git out of the catch below, so the exit
|
|
83
|
+
// code is the only thing separating "git could not answer" from "nothing
|
|
84
|
+
// matched". They are opposites to the caller: every caller turns `null`
|
|
85
|
+
// into a skip with a reason and `[]` into a clean pass. Without this
|
|
86
|
+
// branch a directory git cannot read produced the second — seven checks
|
|
87
|
+
// reporting ✅ over code none of them had looked at, while the docstring
|
|
88
|
+
// above had promised `null` all along. Found by ticket 062's corpus.
|
|
89
|
+
//
|
|
90
|
+
// A pathspec that matches nothing still exits 0, so the ordinary empty
|
|
91
|
+
// result cannot be swallowed here.
|
|
92
|
+
const result = await execa('git', args, { cwd: ctx.cwd, reject: false });
|
|
93
|
+
if (result.exitCode !== 0) {
|
|
94
|
+
logger.warn(`[targets]: ⚠️ git could not list files (exit ${result.exitCode})`);
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
stdout = result.stdout;
|
|
98
|
+
ctx.cache?.set(cacheKey, stdout);
|
|
99
|
+
}
|
|
100
|
+
const files = excludeClientPaths(excludeIgnoredDirs(stdout
|
|
101
|
+
.split('\n')
|
|
102
|
+
.map(f => f.trim())
|
|
103
|
+
.filter(Boolean), ctx.cwd, opts.keepDirs), ctx.cwd).filter(f => fs.existsSync(path.join(ctx.cwd, f)));
|
|
104
|
+
// One line per actual git call, not per consumer. Several checks ask for the
|
|
105
|
+
// same globs in a run and were each answered from the cache, but the line was
|
|
106
|
+
// printed after the cache branch — so four checks sharing one lookup printed
|
|
107
|
+
// four identical `Delta: 1 file(s) for *` lines and the log implied four walks
|
|
108
|
+
// of the tree.
|
|
109
|
+
//
|
|
110
|
+
// Demoted rather than dropped on a cache hit: the count is computed AFTER
|
|
111
|
+
// filtering, and two callers with the same globs but different `keepDirs` get
|
|
112
|
+
// different lists from the same cached output. Losing the second line would
|
|
113
|
+
// hide a real difference, so it moves to `debug` instead of disappearing.
|
|
114
|
+
const line = `[targets]: ${ctx.baseSha ? '📂 Delta' : 'Full scan'}: ${files.length} file(s) for ${globs.join(', ')}`;
|
|
115
|
+
if (fromCache)
|
|
116
|
+
logger.debug(`${line} (cached)`);
|
|
117
|
+
else
|
|
118
|
+
logger.info(line);
|
|
119
|
+
return files;
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
logger.warn(`[targets]: ⚠️ Could not list files\n${err}`);
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Restrict a list of repo-relative paths to the configured `sourcePath`(s).
|
|
128
|
+
* A source path of `.` (or one that normalizes to empty) imposes no
|
|
129
|
+
* restriction, so the whole repo is considered.
|
|
130
|
+
*/
|
|
131
|
+
function scopeToSourcePaths(files, sourcePaths) {
|
|
132
|
+
const scopes = sourcePaths.map(p => p.replace(/^\.\/?/, '').replace(/\/+$/, '')).filter(Boolean);
|
|
133
|
+
if (scopes.length === 0)
|
|
134
|
+
return files;
|
|
135
|
+
return files.filter(f => scopes.some(s => f === s || f.startsWith(`${s}/`)));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Like {@link resolveTargetFiles} but restricts results to the configured
|
|
139
|
+
* `sourcePath`(s). Preserves the semantics of the old `sourcePath` full-scan
|
|
140
|
+
* fallback while gaining delta support on every platform via `ctx.baseSha`.
|
|
141
|
+
*/
|
|
142
|
+
export async function resolveScopedTargetFiles(ctx, globs, sourcePaths) {
|
|
143
|
+
const files = await resolveTargetFiles(ctx, globs);
|
|
144
|
+
if (files === null)
|
|
145
|
+
return null;
|
|
146
|
+
return scopeToSourcePaths(files, sourcePaths);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Resolve a check's file list, or the outcome it should report instead.
|
|
150
|
+
*
|
|
151
|
+
* The one entry point for checks that operate on whole files. It wraps
|
|
152
|
+
* {@link resolveTargetFiles} and {@link listTrackedFiles} rather than replacing
|
|
153
|
+
* them — a check that needs the raw list (because it distinguishes the two cases
|
|
154
|
+
* itself, or scans in several passes) still calls those directly.
|
|
155
|
+
*/
|
|
156
|
+
export async function resolveTargets(ctx, globs, opts) {
|
|
157
|
+
const listOpts = opts.keepDirs ? { keepDirs: opts.keepDirs } : {};
|
|
158
|
+
// Read once, and only when something will use it. A whole-tree walk is always
|
|
159
|
+
// scoped — that is what `listTrackedFiles` promises its callers — while a delta
|
|
160
|
+
// walk is scoped only on request.
|
|
161
|
+
const sourcePaths = opts.wholeTree || opts.scoped ? getSourcePaths(ctx.cwd) : [];
|
|
162
|
+
const files = opts.wholeTree
|
|
163
|
+
? await listTrackedFiles(ctx, globs, sourcePaths, listOpts)
|
|
164
|
+
: await resolveTargetFiles(ctx, globs, listOpts);
|
|
165
|
+
if (files === null)
|
|
166
|
+
return skip('git unavailable');
|
|
167
|
+
const scoped = opts.wholeTree ? files : scopeToSourcePaths(files, sourcePaths);
|
|
168
|
+
if (scoped.length > 0)
|
|
169
|
+
return scoped;
|
|
170
|
+
return 'pass' in opts.onEmpty ? pass(opts.onEmpty.pass) : skip(opts.onEmpty.skip);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* List ALL tracked files matching `globs` (never delta), scoped to
|
|
174
|
+
* `sourcePath`(s).
|
|
175
|
+
*
|
|
176
|
+
* For whole-repo analyses like circular-dependency detection that need the full
|
|
177
|
+
* graph, not just the PR diff. Using `git ls-files` means untracked
|
|
178
|
+
* `node_modules` is excluded by construction.
|
|
179
|
+
*
|
|
180
|
+
* `keepDirs` un-ignores segments for one caller, exactly as in
|
|
181
|
+
* {@link resolveTargetFiles}. It was missing here, and that made Workflow
|
|
182
|
+
* Security blind on the whole-tree path: `.github` is in the default ignore list,
|
|
183
|
+
* so `git ls-files .github/workflows/*.yml` returned the workflow and this
|
|
184
|
+
* function then dropped it. A blocking security check reported a clean pass over
|
|
185
|
+
* files it had never been handed — the surviving half of ticket 067, found by
|
|
186
|
+
* level 1 of the release regression plan.
|
|
187
|
+
*
|
|
188
|
+
* `null` when git could not answer, `[]` when it answered "nothing". The two are
|
|
189
|
+
* opposites to a caller and used to be the same value: `reject: false` keeps a
|
|
190
|
+
* failed git out of the catch below, so a directory git cannot read returned an
|
|
191
|
+
* empty list, and every caller read that as "nothing to review" and passed. On a
|
|
192
|
+
* directory holding real code that produced three green rows — Diff Security,
|
|
193
|
+
* Workflow Security, Diff Smells — over files none of them had opened
|
|
194
|
+
* (ticket 072).
|
|
195
|
+
*/
|
|
196
|
+
export async function listTrackedFiles(ctx, globs, sourcePaths, opts = {}) {
|
|
197
|
+
try {
|
|
198
|
+
const result = await execa('git', ['ls-files', ...globs], { cwd: ctx.cwd, reject: false });
|
|
199
|
+
if (result.exitCode !== 0) {
|
|
200
|
+
logger.warn(`[targets]: ⚠️ git could not list tracked files (exit ${result.exitCode})`);
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
const files = excludeClientPaths(excludeIgnoredDirs(result.stdout
|
|
204
|
+
.split('\n')
|
|
205
|
+
.map(f => f.trim())
|
|
206
|
+
.filter(Boolean), ctx.cwd, opts.keepDirs), ctx.cwd);
|
|
207
|
+
return scopeToSourcePaths(files, sourcePaths);
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
logger.warn(`[targets]: ⚠️ Could not list tracked files\n${err}`);
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|