pr-checkmate 3.0.4 → 3.2.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/{core → cjs/core}/checks/languages/go-format.js +21 -21
- 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/cjs/core/checks/quality/markdown-lint.js +147 -0
- 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/profiles.js +1 -1
- 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/{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/esm/core/checks/languages/go-format.js +163 -0
- 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 +141 -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/esm/utils/execa.js +10 -0
- 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/install-manifests/duplicate/package-lock.json +0 -12
- package/install-manifests/js/package-lock.json +17 -41
- package/install-manifests/js/package.json +1 -1
- package/install-manifests/markdown/package-lock.json +1 -492
- package/install-manifests/markdown/package.json +1 -1
- package/package.json +17 -15
- package/dist/core/checks/languages/lint.d.ts +0 -2
- package/dist/core/checks/quality/markdown-lint.js +0 -110
- 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}/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}/execa.js +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,31 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { logger } from '../../../utils/index.js';
|
|
3
|
+
import { pass, warn, skip } from '../../types.js';
|
|
4
|
+
const CONVENTIONAL_PREFIX = /^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?!?: .+/;
|
|
5
|
+
export const prTitleLintCheck = {
|
|
6
|
+
name: 'PR Title',
|
|
7
|
+
phase: 'informational',
|
|
8
|
+
applies() {
|
|
9
|
+
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
10
|
+
return Boolean(eventPath && fs.existsSync(eventPath));
|
|
11
|
+
},
|
|
12
|
+
async run() {
|
|
13
|
+
let event;
|
|
14
|
+
try {
|
|
15
|
+
event = JSON.parse(fs.readFileSync(process.env.GITHUB_EVENT_PATH, 'utf-8'));
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
logger.warn(`[PR Title]: ⚠️ Could not read GitHub event\n${err}`);
|
|
19
|
+
return skip('event unreadable');
|
|
20
|
+
}
|
|
21
|
+
const pr = event.pull_request;
|
|
22
|
+
if (!pr?.title)
|
|
23
|
+
return skip('no PR title');
|
|
24
|
+
const title = pr.title;
|
|
25
|
+
if (!CONVENTIONAL_PREFIX.test(title)) {
|
|
26
|
+
logger.warn(`[PR Title]: ⚠️ Not Conventional Commits: "${title}"`);
|
|
27
|
+
return warn('title does not follow Conventional Commits');
|
|
28
|
+
}
|
|
29
|
+
return pass();
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Check } from '../../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Flags tracked files whose paths differ only by case (e.g. `Foo.ts` vs
|
|
4
|
+
* `foo.ts`). Such pairs coexist on Linux (case-sensitive) but collide on macOS/
|
|
5
|
+
* Windows and in many Docker layers, silently breaking checkouts and builds.
|
|
6
|
+
* Whole-repo scan (not delta), scoped to `sourcePath` and honouring `ignoreDirs`.
|
|
7
|
+
*/
|
|
8
|
+
export declare const caseCollisionCheck: Check;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { logger } from '../../../utils/index.js';
|
|
2
|
+
import { resolveTargets } from '../../targets.js';
|
|
3
|
+
import { pass, warn } from '../../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Flags tracked files whose paths differ only by case (e.g. `Foo.ts` vs
|
|
6
|
+
* `foo.ts`). Such pairs coexist on Linux (case-sensitive) but collide on macOS/
|
|
7
|
+
* Windows and in many Docker layers, silently breaking checkouts and builds.
|
|
8
|
+
* Whole-repo scan (not delta), scoped to `sourcePath` and honouring `ignoreDirs`.
|
|
9
|
+
*/
|
|
10
|
+
export const caseCollisionCheck = {
|
|
11
|
+
name: 'Case Collision',
|
|
12
|
+
phase: 'informational',
|
|
13
|
+
applies(ctx) {
|
|
14
|
+
return ctx.config.caseCollision?.enabled !== false;
|
|
15
|
+
},
|
|
16
|
+
async run(ctx) {
|
|
17
|
+
const files = await resolveTargets(ctx, ['*'], {
|
|
18
|
+
wholeTree: true,
|
|
19
|
+
onEmpty: { skip: 'no tracked files' },
|
|
20
|
+
});
|
|
21
|
+
if (!Array.isArray(files))
|
|
22
|
+
return files;
|
|
23
|
+
const byLower = new Map();
|
|
24
|
+
for (const f of files) {
|
|
25
|
+
const key = f.toLowerCase();
|
|
26
|
+
let set = byLower.get(key);
|
|
27
|
+
if (!set) {
|
|
28
|
+
set = new Set();
|
|
29
|
+
byLower.set(key, set);
|
|
30
|
+
}
|
|
31
|
+
set.add(f);
|
|
32
|
+
}
|
|
33
|
+
const collisions = [...byLower.values()].filter(set => set.size > 1);
|
|
34
|
+
if (collisions.length === 0)
|
|
35
|
+
return pass();
|
|
36
|
+
collisions.forEach(set => logger.warn(`[Case Collision]: ⚠️ ${[...set].join(' ↔ ')}`));
|
|
37
|
+
return warn(`${collisions.length} case-collision(s) — paths differing only by case`);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Check } from '../../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The closest real name to `input`, when one is close enough to be a typo.
|
|
4
|
+
*
|
|
5
|
+
* "unknown check" alone leaves someone rereading their config for the missing
|
|
6
|
+
* character; naming the intended check ends the search. The threshold scales
|
|
7
|
+
* with length so short names need an exact-ish match and long ones tolerate a
|
|
8
|
+
* slip or two.
|
|
9
|
+
*/
|
|
10
|
+
export declare function suggestName(input: string, candidates: string[]): string | null;
|
|
11
|
+
export declare const configValidationCheck: Check;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { KNOWN_CONFIG_KEYS } from '../../../config/index.js';
|
|
4
|
+
import { pass, skip, warn } from '../../types.js';
|
|
5
|
+
import { severityEmitter } from '../findings.js';
|
|
6
|
+
const VALID_SEVERITIES = ['error', 'warn', 'off'];
|
|
7
|
+
const VALID_FAIL_ON = ['error', 'warn', 'never'];
|
|
8
|
+
/** Edit distance, capped: we only care whether two names are one or two typos apart. */
|
|
9
|
+
function distance(a, b) {
|
|
10
|
+
const rows = Array.from({ length: a.length + 1 }, (_, i) => [i, ...Array(b.length).fill(0)]);
|
|
11
|
+
for (let j = 0; j <= b.length; j++)
|
|
12
|
+
rows[0][j] = j;
|
|
13
|
+
for (let i = 1; i <= a.length; i++) {
|
|
14
|
+
for (let j = 1; j <= b.length; j++) {
|
|
15
|
+
const cost = a[i - 1].toLowerCase() === b[j - 1].toLowerCase() ? 0 : 1;
|
|
16
|
+
rows[i][j] = Math.min(rows[i - 1][j] + 1, rows[i][j - 1] + 1, rows[i - 1][j - 1] + cost);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return rows[a.length][b.length];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The closest real name to `input`, when one is close enough to be a typo.
|
|
23
|
+
*
|
|
24
|
+
* "unknown check" alone leaves someone rereading their config for the missing
|
|
25
|
+
* character; naming the intended check ends the search. The threshold scales
|
|
26
|
+
* with length so short names need an exact-ish match and long ones tolerate a
|
|
27
|
+
* slip or two.
|
|
28
|
+
*/
|
|
29
|
+
export function suggestName(input, candidates) {
|
|
30
|
+
let best = null;
|
|
31
|
+
for (const name of candidates) {
|
|
32
|
+
const d = distance(input, name);
|
|
33
|
+
if (!best || d < best.d)
|
|
34
|
+
best = { name, d };
|
|
35
|
+
}
|
|
36
|
+
const limit = Math.max(2, Math.floor(input.length / 4));
|
|
37
|
+
return best && best.d <= limit ? best.name : null;
|
|
38
|
+
}
|
|
39
|
+
/** Describe a bad value without dumping an entire nested object into the report. */
|
|
40
|
+
function shortValue(value) {
|
|
41
|
+
const text = JSON.stringify(value);
|
|
42
|
+
return text === undefined ? String(value) : text.slice(0, 40);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Validate the `severity` map: values must be a known level, and — more
|
|
46
|
+
* importantly — keys must name a check that exists.
|
|
47
|
+
*
|
|
48
|
+
* A typo here is the worst kind of silent failure in the whole config. `severity`
|
|
49
|
+
* is how a client promotes an advisory check to a gate, so `"Diff Securty":
|
|
50
|
+
* "error"` reads as "this now blocks merges", does nothing at all, and leaves a
|
|
51
|
+
* green pipeline that looks like proof it worked.
|
|
52
|
+
*
|
|
53
|
+
* The misspelling above is the example, so it is muted here rather than added to
|
|
54
|
+
* the dictionary — a global entry would wave through every real "Securty" typo
|
|
55
|
+
* in the repo, which is the exact failure this function exists to catch.
|
|
56
|
+
* cspell:ignore Securty
|
|
57
|
+
*/
|
|
58
|
+
function validateSeverity(json, checkNames) {
|
|
59
|
+
const issues = [];
|
|
60
|
+
const sev = json.severity;
|
|
61
|
+
if (!sev || typeof sev !== 'object')
|
|
62
|
+
return issues;
|
|
63
|
+
for (const [name, val] of Object.entries(sev)) {
|
|
64
|
+
if (!VALID_SEVERITIES.includes(val)) {
|
|
65
|
+
issues.push(`severity["${name}"] must be error|warn|off (got ${shortValue(val)})`);
|
|
66
|
+
}
|
|
67
|
+
if (!checkNames.includes(name)) {
|
|
68
|
+
const hint = suggestName(name, checkNames);
|
|
69
|
+
issues.push(`severity["${name}"] names no check${hint ? ` — did you mean "${hint}"?` : ''}` +
|
|
70
|
+
' (it has no effect)');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return issues;
|
|
74
|
+
}
|
|
75
|
+
/** `failOn` and `severityOverrides` are new and easy to mistype; check them too. */
|
|
76
|
+
function validateFailOn(json) {
|
|
77
|
+
const issues = [];
|
|
78
|
+
const check = (where, block) => {
|
|
79
|
+
if (!block || typeof block !== 'object')
|
|
80
|
+
return;
|
|
81
|
+
const { failOn, severityOverrides } = block;
|
|
82
|
+
if (failOn !== undefined && !VALID_FAIL_ON.includes(failOn)) {
|
|
83
|
+
issues.push(`${where}.failOn must be error|warn|never (got ${shortValue(failOn)})`);
|
|
84
|
+
}
|
|
85
|
+
if (severityOverrides && typeof severityOverrides === 'object') {
|
|
86
|
+
for (const [label, level] of Object.entries(severityOverrides)) {
|
|
87
|
+
if (!VALID_SEVERITIES.includes(level)) {
|
|
88
|
+
issues.push(`${where}.severityOverrides["${label}"] must be error|warn|off (got ${shortValue(level)})`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
check('severityDefaults', json.severityDefaults);
|
|
94
|
+
for (const key of Object.keys(json)) {
|
|
95
|
+
if (key.startsWith('//') || key === 'severityDefaults')
|
|
96
|
+
continue;
|
|
97
|
+
check(key, json[key]);
|
|
98
|
+
}
|
|
99
|
+
return issues;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Validates pr-checkmate.json itself: unknown top-level keys (typos like
|
|
103
|
+
* `mergeConfict` that silently do nothing), invalid values in `severity` and
|
|
104
|
+
* `failOn`, and names in `severity` that match no check. Reads the raw file so it
|
|
105
|
+
* checks what the user actually wrote, not the merged defaults. `//`-comment keys
|
|
106
|
+
* and `$schema` are ignored.
|
|
107
|
+
*/
|
|
108
|
+
/**
|
|
109
|
+
* Report a `sourcePath` that points nowhere.
|
|
110
|
+
*
|
|
111
|
+
* Every scoped check restricts itself to these directories, so a path that does
|
|
112
|
+
* not exist silences them — and silences them as a PASS. Measured on a fixture
|
|
113
|
+
* with `"sourcePath": "src"` and the code in `app/`: a real conflict marker in
|
|
114
|
+
* `app/conflict.py` went unreported while Merge Conflict, a blocking check,
|
|
115
|
+
* printed ✅. Nothing else in the run mentioned that most of the repository had
|
|
116
|
+
* not been looked at.
|
|
117
|
+
*
|
|
118
|
+
* `init` defaults to `.` and recommends it, so this is not the common case — it
|
|
119
|
+
* is what happens when someone picks `src` at setup, or moves the code later, and
|
|
120
|
+
* that is precisely when nobody is looking for it.
|
|
121
|
+
*/
|
|
122
|
+
function validateSourcePath(json, cwd) {
|
|
123
|
+
const raw = json.sourcePath;
|
|
124
|
+
if (raw === undefined)
|
|
125
|
+
return [];
|
|
126
|
+
const paths = (Array.isArray(raw) ? raw : [raw]).filter((p) => typeof p === 'string' && p.trim().length > 0);
|
|
127
|
+
return paths
|
|
128
|
+
.filter(p => {
|
|
129
|
+
const normalised = p.replace(/^\.\/?/, '').replace(/\/+$/, '');
|
|
130
|
+
// `.` and `./` mean the whole repository and always exist.
|
|
131
|
+
if (normalised === '')
|
|
132
|
+
return false;
|
|
133
|
+
return !fs.existsSync(path.join(cwd, normalised));
|
|
134
|
+
})
|
|
135
|
+
.map(p => `sourcePath "${p}" does not exist — every scoped check silently reviews nothing, and reports a pass`);
|
|
136
|
+
}
|
|
137
|
+
export const configValidationCheck = {
|
|
138
|
+
name: 'Config Validation',
|
|
139
|
+
phase: 'informational',
|
|
140
|
+
applies(ctx) {
|
|
141
|
+
return ctx.config.configValidation?.enabled !== false;
|
|
142
|
+
},
|
|
143
|
+
async run(ctx) {
|
|
144
|
+
const cfg = ctx.config.configValidation ?? {};
|
|
145
|
+
const file = path.join(ctx.cwd, 'pr-checkmate.json');
|
|
146
|
+
let raw;
|
|
147
|
+
try {
|
|
148
|
+
raw = fs.readFileSync(file, 'utf-8');
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return skip('no pr-checkmate.json');
|
|
152
|
+
}
|
|
153
|
+
let json;
|
|
154
|
+
try {
|
|
155
|
+
json = JSON.parse(raw);
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
const msg = err instanceof Error ? err.message.split('\n')[0] : String(err);
|
|
159
|
+
return warn(`pr-checkmate.json is not valid JSON: ${msg}`);
|
|
160
|
+
}
|
|
161
|
+
const issues = [];
|
|
162
|
+
const allowed = new Set(cfg.allowUnknownKeys ?? []);
|
|
163
|
+
// Absent only for a hand-built context; then there is nothing to verify names
|
|
164
|
+
// against, and claiming a name is wrong would be worse than staying quiet.
|
|
165
|
+
const checkNames = ctx.checkNames ?? [];
|
|
166
|
+
for (const key of Object.keys(json)) {
|
|
167
|
+
if (key.startsWith('//') || key === '$schema' || allowed.has(key))
|
|
168
|
+
continue;
|
|
169
|
+
if (!KNOWN_CONFIG_KEYS.has(key)) {
|
|
170
|
+
const hint = suggestName(key, [...KNOWN_CONFIG_KEYS]);
|
|
171
|
+
issues.push(`unknown key "${key}"${hint ? ` — did you mean "${hint}"?` : ''}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (cfg.validateCheckNames !== false && checkNames.length > 0) {
|
|
175
|
+
issues.push(...validateSeverity(json, checkNames));
|
|
176
|
+
}
|
|
177
|
+
issues.push(...validateFailOn(json));
|
|
178
|
+
issues.push(...validateSourcePath(json, ctx.cwd));
|
|
179
|
+
if (issues.length === 0)
|
|
180
|
+
return pass();
|
|
181
|
+
// Advisory by default: a config problem is worth knowing about, but a repo
|
|
182
|
+
// that wants a broken config to stop the build sets `severity: "error"`.
|
|
183
|
+
const sev = severityEmitter(cfg.severity, 'warn');
|
|
184
|
+
issues.slice(0, 10).forEach(i => sev.log(`[Config Validation]: ${sev.icon} ${i}`));
|
|
185
|
+
const sample = issues.slice(0, 3).join('; ');
|
|
186
|
+
return sev.emit(`${issues.length} config issue(s): ${sample}${issues.length > 3 ? ' …' : ''}`);
|
|
187
|
+
},
|
|
188
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pass } from '../../types.js';
|
|
4
|
+
import { severityEmitter } from '../findings.js';
|
|
5
|
+
/**
|
|
6
|
+
* Common coverage-report locations across languages/tools. Presence of any one
|
|
7
|
+
* means the test step produced coverage. Reports are usually git-ignored, so we
|
|
8
|
+
* probe the working tree with fs, not git. A client with a non-standard path
|
|
9
|
+
* sets `coverage.reportPath`.
|
|
10
|
+
*/
|
|
11
|
+
const DEFAULT_REPORTS = [
|
|
12
|
+
'coverage/lcov.info', // JS/TS (lcov), C++
|
|
13
|
+
'lcov.info',
|
|
14
|
+
'coverage/coverage-final.json', // istanbul
|
|
15
|
+
'coverage/coverage-summary.json',
|
|
16
|
+
'coverage/cobertura-coverage.xml', // Cobertura (JS, Python)
|
|
17
|
+
'coverage/clover.xml',
|
|
18
|
+
'coverage.xml', // Python (pytest-cov / coverage.py)
|
|
19
|
+
'coverage.out', // Go
|
|
20
|
+
'build/reports/jacoco/test/jacocoTestReport.xml', // Kotlin/Java (Gradle)
|
|
21
|
+
'target/site/jacoco/jacoco.xml', // Java (Maven)
|
|
22
|
+
];
|
|
23
|
+
export const coverageCheck = {
|
|
24
|
+
name: 'Coverage',
|
|
25
|
+
phase: 'informational',
|
|
26
|
+
/**
|
|
27
|
+
* Opt-in: only runs when a repo explicitly enables it (`coverage.enabled:
|
|
28
|
+
* true`). Presence-warning by default everywhere would be pure noise in repos
|
|
29
|
+
* that don't collect coverage — so a client turns it on to say "we produce
|
|
30
|
+
* coverage", and the check then guards against the report silently going
|
|
31
|
+
* missing (a broken `--coverage` flag, tests not running, etc.).
|
|
32
|
+
*/
|
|
33
|
+
applies(ctx) {
|
|
34
|
+
return ctx.config.coverage?.enabled === true;
|
|
35
|
+
},
|
|
36
|
+
async run(ctx) {
|
|
37
|
+
const cfg = ctx.config.coverage;
|
|
38
|
+
const candidates = cfg?.reportPath
|
|
39
|
+
? Array.isArray(cfg.reportPath)
|
|
40
|
+
? cfg.reportPath
|
|
41
|
+
: [cfg.reportPath]
|
|
42
|
+
: DEFAULT_REPORTS;
|
|
43
|
+
const found = candidates.find(rel => {
|
|
44
|
+
try {
|
|
45
|
+
// `isFile()` as well as the size: `statSync` reports a non-zero size for
|
|
46
|
+
// a directory too, so a path that happens to be one would read as a
|
|
47
|
+
// report. Non-empty matters because a zero-byte report is what a test
|
|
48
|
+
// run that died halfway leaves behind — the case this check exists for.
|
|
49
|
+
const stat = fs.statSync(path.join(ctx.cwd, rel));
|
|
50
|
+
return stat.isFile() && stat.size > 0;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
if (found)
|
|
57
|
+
return pass(`coverage report found: ${found}`);
|
|
58
|
+
const sev = severityEmitter(cfg?.severity, 'warn');
|
|
59
|
+
sev.log(`[Coverage]: ${sev.icon} no coverage report found (looked for ${candidates.length} location(s), e.g. ${candidates.slice(0, 3).join(', ')})`);
|
|
60
|
+
return sev.emit('no coverage report found — is the test step running with coverage enabled?');
|
|
61
|
+
},
|
|
62
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { DEAD_CODE_IGNORE_DEFAULTS } from '../../../config/constants.js';
|
|
4
|
+
import { execa } from '../../../utils/execa.js';
|
|
5
|
+
import { logger, resolvePackageBin } from '../../../utils/index.js';
|
|
6
|
+
import { clientToolPaths } from '../../install/reuse.js';
|
|
7
|
+
import { pass, warn, skip } from '../../types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Resolve ts-unused-exports, preferring the consumer project's own copy (so a
|
|
10
|
+
* repo pinned to a different major keeps its own behaviour) and falling back to
|
|
11
|
+
* the copy bundled with pr-checkmate — the order `lint`/`typecheck` use for
|
|
12
|
+
* eslint/tsc, and `circular-deps` uses for dpdm.
|
|
13
|
+
*
|
|
14
|
+
* Returns null instead of throwing: an unresolvable analyzer is a skip, never a
|
|
15
|
+
* failed PR. Previously this always resolved with no `paths`, which only ever
|
|
16
|
+
* searched pr-checkmate's own bundle — a repository whose only copy of
|
|
17
|
+
* ts-unused-exports is its own dependency (ours does not declare it as a bundled
|
|
18
|
+
* dependency) hit `Cannot resolve binary` on every run.
|
|
19
|
+
*/
|
|
20
|
+
function resolveTsUnusedExports(ctx) {
|
|
21
|
+
// No configuration of its own — it reads the module graph via tsconfig — so
|
|
22
|
+
// the version check is the right question here and `clientOwnsConfig` stays
|
|
23
|
+
// off, same as dpdm.
|
|
24
|
+
for (const options of [{ paths: clientToolPaths('ts-unused-exports', ctx) }, undefined]) {
|
|
25
|
+
try {
|
|
26
|
+
return resolvePackageBin('ts-unused-exports', 'bin/ts-unused-exports', options);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Try the next location; both failing is a skip, handled by the caller.
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
export const deadCodeCheck = {
|
|
35
|
+
name: 'Dead Code',
|
|
36
|
+
phase: 'informational',
|
|
37
|
+
weight: 'heavy',
|
|
38
|
+
applies(ctx) {
|
|
39
|
+
if (ctx.config.deadCode?.enabled === false)
|
|
40
|
+
return false;
|
|
41
|
+
return ctx.languages.includes('typescript');
|
|
42
|
+
},
|
|
43
|
+
async run(ctx) {
|
|
44
|
+
const tsConfigPath = path.join(ctx.cwd, 'tsconfig.json');
|
|
45
|
+
if (!fs.existsSync(tsConfigPath)) {
|
|
46
|
+
return skip('no tsconfig.json found');
|
|
47
|
+
}
|
|
48
|
+
const bin = resolveTsUnusedExports(ctx);
|
|
49
|
+
if (!bin) {
|
|
50
|
+
return skip('ts-unused-exports could not be resolved');
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
// Run the full analysis unfiltered: ignore patterns are applied to the
|
|
54
|
+
// *report* below, not passed to ts-unused-exports' own `--ignoreFiles`.
|
|
55
|
+
// That flag drops files from analysis entirely, so a file's imports stop
|
|
56
|
+
// counting as usage — ignoring a barrel or entry point would then cascade
|
|
57
|
+
// and flag everything it re-exports. Filtering the output instead keeps
|
|
58
|
+
// every usage edge intact and only hides the exempted files' own reports.
|
|
59
|
+
const result = await execa(process.execPath, [bin, tsConfigPath], {
|
|
60
|
+
cwd: ctx.cwd,
|
|
61
|
+
reject: false,
|
|
62
|
+
});
|
|
63
|
+
const output = [result.stdout, result.stderr].filter(Boolean).join('\n').trim();
|
|
64
|
+
if (!output || result.exitCode === 0) {
|
|
65
|
+
logger.info('[Dead Code]: ✅ No unused exports found');
|
|
66
|
+
return pass();
|
|
67
|
+
}
|
|
68
|
+
const ignore = compileIgnorePatterns(ctx.config.deadCode?.ignoreFiles ?? DEAD_CODE_IGNORE_DEFAULTS);
|
|
69
|
+
// ts-unused-exports prints one `path: sym1, sym2` line per file, preceded
|
|
70
|
+
// by a `N modules with unused exports` summary line we skip.
|
|
71
|
+
const parsed = output
|
|
72
|
+
.split('\n')
|
|
73
|
+
.map(line => line.trim().match(/^(.+\.[cm]?tsx?): (.+)$/))
|
|
74
|
+
.filter((match) => match !== null)
|
|
75
|
+
.map(match => ({
|
|
76
|
+
file: match[1],
|
|
77
|
+
symbols: match[2]
|
|
78
|
+
.split(',')
|
|
79
|
+
.map(sym => sym.trim())
|
|
80
|
+
.filter(Boolean),
|
|
81
|
+
}));
|
|
82
|
+
// The tool said something and none of it was a report. That is not a clean
|
|
83
|
+
// repository — it is an answer this check cannot read, and calling it a
|
|
84
|
+
// pass is how Circular Deps stayed green on every repository for its whole
|
|
85
|
+
// existence while grepping for console wording that had changed. Same
|
|
86
|
+
// resolution as Grype Scan: say so rather than invent a verdict.
|
|
87
|
+
if (parsed.length === 0) {
|
|
88
|
+
logger.warn(`[Dead Code]: ⚠️ ts-unused-exports produced no parseable output:\n${output}`);
|
|
89
|
+
return warn('ts-unused-exports produced no parseable output');
|
|
90
|
+
}
|
|
91
|
+
const entries = parsed.filter(entry => !ignore.some(re => re.test(entry.file)));
|
|
92
|
+
if (entries.length === 0) {
|
|
93
|
+
logger.info('[Dead Code]: ✅ No unused exports found');
|
|
94
|
+
return pass();
|
|
95
|
+
}
|
|
96
|
+
const fileCount = entries.length;
|
|
97
|
+
const exportCount = entries.reduce((sum, entry) => sum + entry.symbols.length, 0);
|
|
98
|
+
logger.warn(`[Dead Code]: ⚠️ ${exportCount} unused export(s) across ${fileCount} file(s):`);
|
|
99
|
+
for (const entry of entries) {
|
|
100
|
+
logger.warn(`${entry.file}: ${entry.symbols.join(', ')}`);
|
|
101
|
+
}
|
|
102
|
+
return warn(`${exportCount} unused export(s) in ${fileCount} file(s)`);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
logger.warn(`[Dead Code]: ⚠️ Could not run dead code check\n${err}`);
|
|
106
|
+
return skip('ts-unused-exports unavailable');
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Compile `deadCode.ignoreFiles` regex sources, skipping any that fail to parse
|
|
112
|
+
* (a bad pattern warns rather than aborting the whole check).
|
|
113
|
+
*/
|
|
114
|
+
function compileIgnorePatterns(patterns) {
|
|
115
|
+
return (patterns ?? []).filter(Boolean).flatMap(pattern => {
|
|
116
|
+
try {
|
|
117
|
+
return [new RegExp(pattern)];
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
logger.warn(`[Dead Code]: ⚠️ Ignoring invalid ignoreFiles pattern: ${pattern}`);
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { CONFIG_FILE_PATTERNS, DUPLICATE_CODE_FORMATS, DUPLICATE_IGNORE_DEFAULTS, } from '../../../config/constants.js';
|
|
5
|
+
import { getSourcePaths } from '../../../config/index.js';
|
|
6
|
+
import { execa } from '../../../utils/execa.js';
|
|
7
|
+
import { logger, resolvePackageBin } from '../../../utils/index.js';
|
|
8
|
+
import { resolveScopedTargetFiles } from '../../targets.js';
|
|
9
|
+
import { pass, warn, skip } from '../../types.js';
|
|
10
|
+
import { logSample } from '../findings.js';
|
|
11
|
+
/**
|
|
12
|
+
* The files to hand jscpd, per language.
|
|
13
|
+
*
|
|
14
|
+
* This map is why the check reads `ctx.languages` at all. It used to pass a fixed
|
|
15
|
+
* `*.ts,*.tsx,*.js,*.jsx` and gate itself on TypeScript/JavaScript, so a Python
|
|
16
|
+
* repository got `⏭️ Duplicate Code: no relevant files` — not because it had no
|
|
17
|
+
* duplication, but because nothing ever looked. The formats list in `constants.ts`
|
|
18
|
+
* named Python and C++ all along, which made the report's own claim untrue.
|
|
19
|
+
*
|
|
20
|
+
* Extensions mirror `detect-language.ts` so a language that decides the check runs
|
|
21
|
+
* is a language whose files it actually scans, with one deliberate addition: `.c`
|
|
22
|
+
* and `.h` under `cpp`, since a C project detects as `cpp` and the `c` format is
|
|
23
|
+
* one jscpd supports.
|
|
24
|
+
*/
|
|
25
|
+
const LANGUAGE_GLOBS = {
|
|
26
|
+
typescript: ['*.ts', '*.tsx'],
|
|
27
|
+
javascript: ['*.js', '*.jsx', '*.mjs', '*.cjs'],
|
|
28
|
+
python: ['*.py'],
|
|
29
|
+
cpp: ['*.cpp', '*.cc', '*.cxx', '*.c++', '*.hpp', '*.hh', '*.hxx', '*.c', '*.h'],
|
|
30
|
+
swift: ['*.swift'],
|
|
31
|
+
kotlin: ['*.kt', '*.kts', '*.java'],
|
|
32
|
+
go: ['*.go'],
|
|
33
|
+
rust: ['*.rs'],
|
|
34
|
+
csharp: ['*.cs'],
|
|
35
|
+
ruby: ['*.rb'],
|
|
36
|
+
php: ['*.php'],
|
|
37
|
+
};
|
|
38
|
+
/** The scan globs for one run: every detected language jscpd can tokenise. */
|
|
39
|
+
function globsFor(languages) {
|
|
40
|
+
return [...new Set(languages.flatMap(lang => LANGUAGE_GLOBS[lang] ?? []))];
|
|
41
|
+
}
|
|
42
|
+
// Always excluded regardless of client config — scanning these never yields
|
|
43
|
+
// meaningful production duplication. Config files (eslint/*.config.*) are data,
|
|
44
|
+
// not logic, and are commonly shipped in two formats (e.g. .eslintrc + flat
|
|
45
|
+
// config), so their repetition is expected rather than a code smell.
|
|
46
|
+
const BASE_IGNORE = ['node_modules/**', 'dist/**', '.git/**', ...CONFIG_FILE_PATTERNS];
|
|
47
|
+
/** `<path>:<start>-<end>`, repo-relative, for one end of a clone. */
|
|
48
|
+
function locate(cwd, at) {
|
|
49
|
+
const name = at?.name ?? '';
|
|
50
|
+
const rel = name ? path.relative(cwd, name) || path.basename(name) : '<unknown file>';
|
|
51
|
+
return `${rel}:${at?.start ?? '?'}-${at?.end ?? '?'}`;
|
|
52
|
+
}
|
|
53
|
+
/** One reportable line per clone: both ends, and how big it is. */
|
|
54
|
+
function describe(cwd, clone) {
|
|
55
|
+
const size = clone.lines === undefined ? '' : ` (${clone.lines} lines)`;
|
|
56
|
+
return `${locate(cwd, clone.firstFile)} ↔ ${locate(cwd, clone.secondFile)}${size}`;
|
|
57
|
+
}
|
|
58
|
+
export const duplicateCheck = {
|
|
59
|
+
name: 'Duplicate Code',
|
|
60
|
+
phase: 'informational',
|
|
61
|
+
weight: 'heavy',
|
|
62
|
+
applies(ctx) {
|
|
63
|
+
if (ctx.config.duplicate?.enabled === false)
|
|
64
|
+
return false;
|
|
65
|
+
return globsFor(ctx.languages).length > 0;
|
|
66
|
+
},
|
|
67
|
+
async run(ctx) {
|
|
68
|
+
const minLines = ctx.config.duplicate?.minLines ?? 5;
|
|
69
|
+
const minTokens = ctx.config.duplicate?.minTokens ?? 50;
|
|
70
|
+
const targetPaths = await resolveScopedTargetFiles(ctx, globsFor(ctx.languages), getSourcePaths(ctx.cwd));
|
|
71
|
+
if (targetPaths === null)
|
|
72
|
+
return skip('git unavailable');
|
|
73
|
+
if (targetPaths.length === 0)
|
|
74
|
+
return skip('no relevant files');
|
|
75
|
+
const jscpdBin = resolvePackageBin('jscpd', 'run-jscpd.js');
|
|
76
|
+
// Client-configurable exclusions (pr-checkmate.json `duplicate.ignore`),
|
|
77
|
+
// defaulting to test files/folders, appended to the always-on base ignores.
|
|
78
|
+
const configuredIgnore = ctx.config.duplicate?.ignore ?? DUPLICATE_IGNORE_DEFAULTS;
|
|
79
|
+
const ignore = [...BASE_IGNORE, ...configuredIgnore].join(',');
|
|
80
|
+
// Client-configurable formats (pr-checkmate.json `duplicate.formats`),
|
|
81
|
+
// defaulting to source-code formats so data files (yaml/json/markdown) are
|
|
82
|
+
// not scanned. Clients can add them back explicitly.
|
|
83
|
+
const formats = (ctx.config.duplicate?.formats ?? DUPLICATE_CODE_FORMATS).join(',');
|
|
84
|
+
// The JSON reporter writes a file, so it needs somewhere to write it. Outside
|
|
85
|
+
// the repository deliberately: with no `--output`, jscpd creates `report/`
|
|
86
|
+
// in the client's working tree, and a check that leaves a directory behind is
|
|
87
|
+
// a check that shows up in the next commit.
|
|
88
|
+
const reportDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pr-checkmate-jscpd-'));
|
|
89
|
+
try {
|
|
90
|
+
// `--silent` keeps jscpd's own console output away from the client entirely.
|
|
91
|
+
// That is what retired `stripJscpdPromo` — six string markers matching the
|
|
92
|
+
// funding lines jscpd v5 prints after its report, each good until the next
|
|
93
|
+
// release of somebody else's package.
|
|
94
|
+
const result = await execa(process.execPath, [
|
|
95
|
+
jscpdBin,
|
|
96
|
+
'--min-lines',
|
|
97
|
+
String(minLines),
|
|
98
|
+
'--min-tokens',
|
|
99
|
+
String(minTokens),
|
|
100
|
+
'--reporters',
|
|
101
|
+
'json',
|
|
102
|
+
'--output',
|
|
103
|
+
reportDir,
|
|
104
|
+
'--absolute',
|
|
105
|
+
'--silent',
|
|
106
|
+
'--format',
|
|
107
|
+
formats,
|
|
108
|
+
'--ignore',
|
|
109
|
+
ignore,
|
|
110
|
+
...targetPaths,
|
|
111
|
+
], { cwd: ctx.cwd, reject: false });
|
|
112
|
+
const reportPath = path.join(reportDir, 'jscpd-report.json');
|
|
113
|
+
if (!fs.existsSync(reportPath)) {
|
|
114
|
+
// No report and nothing to read it from: jscpd did not get as far as
|
|
115
|
+
// writing one. Its stderr is the only thing that can say why, and a skip
|
|
116
|
+
// is the honest outcome — an empty report would read as "no duplication".
|
|
117
|
+
logger.warn(`[Duplicate Code]: ⚠️ jscpd wrote no report (exit ${result.exitCode ?? 'killed'})` +
|
|
118
|
+
`${result.stderr ? `\n${result.stderr}` : ''}`);
|
|
119
|
+
return skip('jscpd produced no report');
|
|
120
|
+
}
|
|
121
|
+
const report = JSON.parse(fs.readFileSync(reportPath, 'utf-8'));
|
|
122
|
+
const clones = report.duplicates ?? [];
|
|
123
|
+
// jscpd's own count, not a regex over its prose. `output.match(/(\d+)\s+clone/i)`
|
|
124
|
+
// read the number out of an English sentence, so a reworded summary upstream
|
|
125
|
+
// would have reported zero duplication on a repository full of it — and
|
|
126
|
+
// reported it as a pass.
|
|
127
|
+
const cloneCount = report.statistics?.total?.clones ?? clones.length;
|
|
128
|
+
if (cloneCount === 0)
|
|
129
|
+
return pass();
|
|
130
|
+
logSample('Duplicate Code', `${cloneCount} duplicate block(s):`, clones.map(clone => describe(ctx.cwd, clone)));
|
|
131
|
+
return warn(`${cloneCount} duplicate block(s)`);
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
logger.warn(`[Duplicate Code]: ⚠️ Could not run duplicate check\n${err}`);
|
|
135
|
+
return skip('jscpd unavailable');
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
fs.rmSync(reportDir, { recursive: true, force: true });
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Check } from '../../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Requires a license/copyright header (a configured regex) in the first N lines
|
|
4
|
+
* of newly ADDED source files. Opt-in — needs `licenseHeader.pattern`; does
|
|
5
|
+
* nothing until a repo configures one. Advisory (warn).
|
|
6
|
+
*/
|
|
7
|
+
export declare const licenseHeaderCheck: Check;
|