pr-checkmate 3.0.3 → 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/install-manifests/cpp/package-lock.json +4 -4
- package/install-manifests/cpp/package.json +1 -1
- package/package.json +16 -14
- 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,106 @@
|
|
|
1
|
+
import { execa } from '../../../utils/execa.js';
|
|
2
|
+
import { logger, matchesAnyGlob } from '../../../utils/index.js';
|
|
3
|
+
import { listTrackedFiles } from '../../targets.js';
|
|
4
|
+
import { pass, skip } from '../../types.js';
|
|
5
|
+
import { clientFindingOptions, emptyBuckets, mergeSeverityBuckets, reportSeverityBuckets, scanTrackedFiles, } from '../findings.js';
|
|
6
|
+
/**
|
|
7
|
+
* Compile the user's `customRules` into ready-to-run matchers. A rule with an
|
|
8
|
+
* invalid regex is skipped with a warning rather than crashing the run. `g`/`y`
|
|
9
|
+
* flags are stripped because `.test()` on a sticky/global regex is stateful and
|
|
10
|
+
* would skip matches across lines.
|
|
11
|
+
*/
|
|
12
|
+
function compileRules(ctx) {
|
|
13
|
+
const rules = ctx.config.customRules ?? [];
|
|
14
|
+
const compiled = [];
|
|
15
|
+
for (const r of rules) {
|
|
16
|
+
if (!r?.name || !r?.pattern)
|
|
17
|
+
continue;
|
|
18
|
+
let re;
|
|
19
|
+
try {
|
|
20
|
+
re = new RegExp(r.pattern, (r.flags ?? '').replace(/[gy]/g, ''));
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
logger.warn(`[Custom Rules]: ⚠️ invalid regex in rule "${r.name}" — skipped\n${err}`);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
compiled.push({
|
|
27
|
+
message: r.message ?? r.name,
|
|
28
|
+
re,
|
|
29
|
+
severity: r.severity === 'error' ? 'error' : 'warn',
|
|
30
|
+
include: r.include,
|
|
31
|
+
exclude: r.exclude,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return compiled;
|
|
35
|
+
}
|
|
36
|
+
const PREFIX = 'Custom Rules';
|
|
37
|
+
/** Parse the diff, bucketing rule matches on added lines by severity. */
|
|
38
|
+
function scanDiff(stdout, rules) {
|
|
39
|
+
var _a;
|
|
40
|
+
const errors = {};
|
|
41
|
+
const warnings = {};
|
|
42
|
+
let file = '';
|
|
43
|
+
for (const raw of stdout.split('\n')) {
|
|
44
|
+
if (raw.startsWith('+++ ')) {
|
|
45
|
+
const p = raw.slice(4).trim();
|
|
46
|
+
file = p === '/dev/null' ? '' : p.replace(/^b\//, '');
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (!raw.startsWith('+') || raw.startsWith('+++'))
|
|
50
|
+
continue;
|
|
51
|
+
if (raw.includes('pr-checkmate-ignore'))
|
|
52
|
+
continue;
|
|
53
|
+
const content = raw.slice(1);
|
|
54
|
+
for (const rule of rules) {
|
|
55
|
+
if (rule.include && !matchesAnyGlob(file, rule.include))
|
|
56
|
+
continue;
|
|
57
|
+
if (rule.exclude && matchesAnyGlob(file, rule.exclude))
|
|
58
|
+
continue;
|
|
59
|
+
if (rule.re.test(content)) {
|
|
60
|
+
const bucket = rule.severity === 'error' ? errors : warnings;
|
|
61
|
+
(bucket[_a = rule.message] ?? (bucket[_a] = [])).push(`${file}: ${content.trim().substring(0, 100)}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { errors, warnings };
|
|
66
|
+
}
|
|
67
|
+
export const customRulesCheck = {
|
|
68
|
+
name: 'Custom Rules',
|
|
69
|
+
phase: 'informational',
|
|
70
|
+
// No `requiresDiff`: a project rule describes what the code should look like,
|
|
71
|
+
// not what this PR changed (ticket 067). A repo adopting a rule wants to know
|
|
72
|
+
// where it is already violated.
|
|
73
|
+
applies(ctx) {
|
|
74
|
+
return (ctx.config.customRules?.length ?? 0) > 0;
|
|
75
|
+
},
|
|
76
|
+
async run(ctx) {
|
|
77
|
+
const rules = compileRules(ctx);
|
|
78
|
+
if (rules.length === 0)
|
|
79
|
+
return pass('no valid custom rules');
|
|
80
|
+
try {
|
|
81
|
+
const found = emptyBuckets();
|
|
82
|
+
const collect = (stdout) => mergeSeverityBuckets(found, scanDiff(stdout, rules));
|
|
83
|
+
if (ctx.baseSha) {
|
|
84
|
+
const { stdout } = await execa('git', ['diff', '--unified=0', '--diff-filter=ACMR', ctx.baseSha, ctx.headSha ?? 'HEAD'], { cwd: ctx.cwd });
|
|
85
|
+
collect(stdout);
|
|
86
|
+
}
|
|
87
|
+
else if (ctx.config.run?.fullScanBehaviour !== 'skip') {
|
|
88
|
+
const files = await listTrackedFiles(ctx, ['*'], ['.']);
|
|
89
|
+
if (files === null)
|
|
90
|
+
return skip('git unavailable');
|
|
91
|
+
await scanTrackedFiles(ctx, files, collect);
|
|
92
|
+
}
|
|
93
|
+
// A rule with severity "error" blocks the run (fail), regardless of phase.
|
|
94
|
+
return reportSeverityBuckets(found.errors, found.warnings, {
|
|
95
|
+
prefix: PREFIX,
|
|
96
|
+
errorSummary: n => `${n} error(s)`,
|
|
97
|
+
warningSummary: n => `${n} warning(s)`,
|
|
98
|
+
...clientFindingOptions(ctx),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
logger.warn(`[Custom Rules]: ⚠️ Could not analyze diff\n${err}`);
|
|
103
|
+
return skip('diff unavailable');
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Check } from '../../types.js';
|
|
2
|
+
import { DiffPattern } from '../findings.js';
|
|
3
|
+
/**
|
|
4
|
+
* Smells that are only smells in live code.
|
|
5
|
+
*
|
|
6
|
+
* `view: 'code'` blanks string literals before matching, which is what tells a
|
|
7
|
+
* `debugger` statement apart from the seven places an ESLint config names the
|
|
8
|
+
* rule that bans it — `'no-debugger': 'error'` was reported as a leftover
|
|
9
|
+
* `debugger` in every repo that configures the rule, this one included.
|
|
10
|
+
* Comment stripping (the default) covers the other half: prose *about* a
|
|
11
|
+
* `debugger` is not one.
|
|
12
|
+
*/
|
|
13
|
+
export declare const CODE_SMELLS: DiffPattern[];
|
|
14
|
+
/**
|
|
15
|
+
* Markers that live *in* comments, so this set is scanned with comment handling
|
|
16
|
+
* off — stripping comments first would leave these patterns nothing to match.
|
|
17
|
+
*/
|
|
18
|
+
export declare const COMMENT_SMELLS: DiffPattern[];
|
|
19
|
+
export declare const diffSmellCheck: Check;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { logger } from '../../../utils/index.js';
|
|
2
|
+
import { pass, skip } from '../../types.js';
|
|
3
|
+
import { bucketTotal, emptyBuckets, mergeSeverityBuckets, reportCountedFindings, scanDiffPatterns, reviewLines, } from '../findings.js';
|
|
4
|
+
/**
|
|
5
|
+
* Smells that are only smells in live code.
|
|
6
|
+
*
|
|
7
|
+
* `view: 'code'` blanks string literals before matching, which is what tells a
|
|
8
|
+
* `debugger` statement apart from the seven places an ESLint config names the
|
|
9
|
+
* rule that bans it — `'no-debugger': 'error'` was reported as a leftover
|
|
10
|
+
* `debugger` in every repo that configures the rule, this one included.
|
|
11
|
+
* Comment stripping (the default) covers the other half: prose *about* a
|
|
12
|
+
* `debugger` is not one.
|
|
13
|
+
*/
|
|
14
|
+
export const CODE_SMELLS = [
|
|
15
|
+
{
|
|
16
|
+
pattern: /^\+(?!\+).*\bconsole\.(log|error|warn|debug|info)\s*\(/,
|
|
17
|
+
label: 'console.*()',
|
|
18
|
+
severity: 'warn',
|
|
19
|
+
view: 'code',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
pattern: /^\+(?!\+).*\bdebugger\b/,
|
|
23
|
+
label: 'debugger statement',
|
|
24
|
+
severity: 'warn',
|
|
25
|
+
view: 'code',
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
/**
|
|
29
|
+
* Markers that live *in* comments, so this set is scanned with comment handling
|
|
30
|
+
* off — stripping comments first would leave these patterns nothing to match.
|
|
31
|
+
*/
|
|
32
|
+
export const COMMENT_SMELLS = [
|
|
33
|
+
{ pattern: /^\+\s*\/\/\s*TODO\b/i, label: 'TODO comment', severity: 'warn' },
|
|
34
|
+
{ pattern: /^\+\s*\/\/\s*FIXME\b/i, label: 'FIXME comment', severity: 'warn' },
|
|
35
|
+
{ pattern: /^\+\s*\/\/\s*HACK\b/i, label: 'HACK comment', severity: 'warn' },
|
|
36
|
+
{ pattern: /^\+\s*\/\/\s*XXX\b/i, label: 'XXX marker', severity: 'warn' },
|
|
37
|
+
];
|
|
38
|
+
export const diffSmellCheck = {
|
|
39
|
+
name: 'Diff Smells',
|
|
40
|
+
phase: 'informational',
|
|
41
|
+
// No `requiresDiff`: a leftover `debugger` is leftover whether it arrived in
|
|
42
|
+
// this PR or an earlier one (ticket 067).
|
|
43
|
+
applies(ctx) {
|
|
44
|
+
return ctx.config.diffSmell?.enabled !== false;
|
|
45
|
+
},
|
|
46
|
+
async run(ctx) {
|
|
47
|
+
try {
|
|
48
|
+
const { ignorePaths } = ctx.config.diffSmell ?? {};
|
|
49
|
+
const found = emptyBuckets();
|
|
50
|
+
const reviewed = await reviewLines(ctx, ['*.ts', '*.tsx', '*.js', '*.jsx'], stdout => {
|
|
51
|
+
mergeSeverityBuckets(found, scanDiffPatterns(stdout, CODE_SMELLS, { ignorePaths }));
|
|
52
|
+
mergeSeverityBuckets(found, scanDiffPatterns(stdout, COMMENT_SMELLS, { comments: 'off', ignorePaths }));
|
|
53
|
+
});
|
|
54
|
+
// An unreadable file list is not "no smells" (ticket 072). Checked before
|
|
55
|
+
// the empty-bucket branch below, because that branch is what turned it into
|
|
56
|
+
// a green row.
|
|
57
|
+
if (!reviewed)
|
|
58
|
+
return skip('git unavailable');
|
|
59
|
+
// Every smell here is declared `warn`, so nothing reaches the error bucket.
|
|
60
|
+
if (bucketTotal(found.warnings) === 0)
|
|
61
|
+
return pass();
|
|
62
|
+
return reportCountedFindings(found.warnings, ctx, { prefix: 'Diff Smells' });
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
logger.warn(`[Diff Smells]: ⚠️ Could not analyze diff\n${err}`);
|
|
66
|
+
return skip('diff unavailable');
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { execa } from '../../../utils/execa.js';
|
|
4
|
+
import { logger, matchesAnyGlob } from '../../../utils/index.js';
|
|
5
|
+
import { resolveTargetFiles } from '../../targets.js';
|
|
6
|
+
import { pass, skip } from '../../types.js';
|
|
7
|
+
import { severityEmitter } from '../findings.js';
|
|
8
|
+
const DEFAULT_MAX_KB = 500;
|
|
9
|
+
/**
|
|
10
|
+
* Lockfiles are legitimately large and always generated, never accidental — so
|
|
11
|
+
* they're exempt from the size/binary guard by default (a 600 KB
|
|
12
|
+
* package-lock.json is normal, not bloat). Their changes are still surfaced by
|
|
13
|
+
* the Lockfile Drift and Sensitive Files checks. Merged with the client's
|
|
14
|
+
* `largeFiles.allow`.
|
|
15
|
+
*/
|
|
16
|
+
const LOCKFILE_ALLOW = [
|
|
17
|
+
'**/package-lock.json',
|
|
18
|
+
'**/npm-shrinkwrap.json',
|
|
19
|
+
'**/yarn.lock',
|
|
20
|
+
'**/pnpm-lock.yaml',
|
|
21
|
+
'**/bun.lockb',
|
|
22
|
+
'**/Cargo.lock',
|
|
23
|
+
'**/poetry.lock',
|
|
24
|
+
'**/uv.lock',
|
|
25
|
+
'**/Pipfile.lock',
|
|
26
|
+
'**/pdm.lock',
|
|
27
|
+
'**/Gemfile.lock',
|
|
28
|
+
'**/composer.lock',
|
|
29
|
+
'**/go.sum',
|
|
30
|
+
'**/Package.resolved',
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Parse `git diff --numstat` and collect files git reports as binary — those
|
|
34
|
+
* rows carry `-` in both the additions and deletions columns.
|
|
35
|
+
*/
|
|
36
|
+
function binaryPaths(numstat) {
|
|
37
|
+
const binary = new Set();
|
|
38
|
+
for (const line of numstat.split('\n')) {
|
|
39
|
+
if (!line)
|
|
40
|
+
continue;
|
|
41
|
+
const [add, del, ...rest] = line.split('\t');
|
|
42
|
+
if (add === '-' && del === '-')
|
|
43
|
+
binary.add(rest.join('\t'));
|
|
44
|
+
}
|
|
45
|
+
return binary;
|
|
46
|
+
}
|
|
47
|
+
export const largeFilesCheck = {
|
|
48
|
+
name: 'Large Files',
|
|
49
|
+
phase: 'informational',
|
|
50
|
+
// No `requiresDiff`: a committed 40 MB binary bloats every clone whether it
|
|
51
|
+
// landed in this PR or two years ago (ticket 067). `resolveTargetFiles` already
|
|
52
|
+
// falls back to `git ls-files` with no range, so the file list is right either
|
|
53
|
+
// way; only the binary-detection diff below needed a second path.
|
|
54
|
+
applies(ctx) {
|
|
55
|
+
return ctx.config.largeFiles?.enabled !== false;
|
|
56
|
+
},
|
|
57
|
+
async run(ctx) {
|
|
58
|
+
// resolveTargetFiles (not the scoped variant): a bloat guard should catch
|
|
59
|
+
// large files anywhere in the repo — a root-level asset is exactly the case
|
|
60
|
+
// a narrowed `sourcePath` would miss — but still skip `ignoreDirs`
|
|
61
|
+
// (dist/build/vendor), where large generated files are expected.
|
|
62
|
+
const files = await resolveTargetFiles(ctx, ['*']);
|
|
63
|
+
if (files === null)
|
|
64
|
+
return skip('git unavailable');
|
|
65
|
+
if (files.length === 0)
|
|
66
|
+
return pass();
|
|
67
|
+
const maxKb = ctx.config.largeFiles?.maxSizeKb ?? DEFAULT_MAX_KB;
|
|
68
|
+
const maxBytes = maxKb * 1024;
|
|
69
|
+
const allow = [...LOCKFILE_ALLOW, ...(ctx.config.largeFiles?.allow ?? [])];
|
|
70
|
+
if (!ctx.baseSha && ctx.config.run?.fullScanBehaviour === 'skip')
|
|
71
|
+
return pass();
|
|
72
|
+
let binary;
|
|
73
|
+
try {
|
|
74
|
+
// `--numstat` reports `-\t-` for binary files. With no range there is
|
|
75
|
+
// nothing to diff, so binary detection falls back to reading the bytes —
|
|
76
|
+
// the size check is what matters here, and it works either way.
|
|
77
|
+
const args = ctx.baseSha
|
|
78
|
+
? ['diff', '--numstat', '--diff-filter=ACM', ctx.baseSha, ctx.headSha ?? 'HEAD']
|
|
79
|
+
: null;
|
|
80
|
+
const { stdout } = args ? await execa('git', args, { cwd: ctx.cwd }) : { stdout: '' };
|
|
81
|
+
binary = binaryPaths(stdout);
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
logger.warn(`[Large Files]: ⚠️ Could not analyze diff\n${err}`);
|
|
85
|
+
return skip('diff unavailable');
|
|
86
|
+
}
|
|
87
|
+
const findings = [];
|
|
88
|
+
for (const file of files) {
|
|
89
|
+
if (matchesAnyGlob(file, allow))
|
|
90
|
+
continue;
|
|
91
|
+
let size;
|
|
92
|
+
try {
|
|
93
|
+
size = fs.statSync(path.join(ctx.cwd, file)).size;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
continue; // renamed/removed between diff and stat — skip
|
|
97
|
+
}
|
|
98
|
+
const reasons = [];
|
|
99
|
+
if (size > maxBytes)
|
|
100
|
+
reasons.push(`${Math.round(size / 1024)} KB > ${maxKb} KB`);
|
|
101
|
+
if (binary.has(file))
|
|
102
|
+
reasons.push('binary');
|
|
103
|
+
if (reasons.length > 0)
|
|
104
|
+
findings.push(`${file} (${reasons.join(', ')})`);
|
|
105
|
+
}
|
|
106
|
+
if (findings.length === 0)
|
|
107
|
+
return pass();
|
|
108
|
+
const sev = severityEmitter(ctx.config.largeFiles?.severity, 'warn');
|
|
109
|
+
findings.forEach(f => sev.log(`[Large Files]: ${sev.icon} ${f}`));
|
|
110
|
+
return sev.emit(`${findings.length} large/binary file(s) added`);
|
|
111
|
+
},
|
|
112
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { logger } from '../../../utils/index.js';
|
|
2
|
+
import { pass, skip } from '../../types.js';
|
|
3
|
+
import { bucketTotal, commentSyntaxFor, reportCountedFindings, reviewLines, stripInlineComment, } from '../findings.js';
|
|
4
|
+
/**
|
|
5
|
+
* Debug statements for languages NOT already covered by the Diff Smells check
|
|
6
|
+
* (which handles `console.*`/`debugger` for JS/TS). Kept separate so the two
|
|
7
|
+
* checks never double-report the same finding in a PR comment.
|
|
8
|
+
*/
|
|
9
|
+
const PYTHON_PATTERNS = [
|
|
10
|
+
{ pattern: /^\+(?!\+).*\bprint\s*\(/, label: 'print()' },
|
|
11
|
+
{ pattern: /^\+(?!\+).*\bpprint\s*\(/, label: 'pprint()' },
|
|
12
|
+
{ pattern: /^\+(?!\+).*\bbreakpoint\s*\(/, label: 'breakpoint()' },
|
|
13
|
+
{ pattern: /^\+(?!\+).*\bpdb\.set_trace\s*\(/, label: 'pdb.set_trace()' },
|
|
14
|
+
];
|
|
15
|
+
const KOTLIN_PATTERNS = [
|
|
16
|
+
{ pattern: /^\+(?!\+).*\bSystem\.(out|err)\.println\s*\(/, label: 'System.out/err.println()' },
|
|
17
|
+
// Bare println(...), but not the qualified System.out.println above — the
|
|
18
|
+
// negative lookbehind rejects a preceding `.` or identifier char.
|
|
19
|
+
{ pattern: /^\+(?!\+).*(?<![.\w])println\s*\(/, label: 'println()' },
|
|
20
|
+
];
|
|
21
|
+
/** `+++ b/path/to/file.py` — the header that says which file follows. */
|
|
22
|
+
const DIFF_FILE_HEADER = /^\+\+\+ b\/(.+)$/;
|
|
23
|
+
/**
|
|
24
|
+
* Bucket added-line matches by label, skipping lines opted out via directive.
|
|
25
|
+
*
|
|
26
|
+
* Comment stripping follows the file each hunk belongs to (ticket 059). It
|
|
27
|
+
* matters here as much as in Diff Security: `#` is a comment in Python but a
|
|
28
|
+
* private field in Kotlin/Java-adjacent syntax, and this check scans both.
|
|
29
|
+
*/
|
|
30
|
+
function collect(stdout, patterns, findings, mode = 'language') {
|
|
31
|
+
let syntax = 'both';
|
|
32
|
+
for (const line of stdout.split('\n')) {
|
|
33
|
+
const header = DIFF_FILE_HEADER.exec(line);
|
|
34
|
+
if (header) {
|
|
35
|
+
syntax = mode === 'language' ? commentSyntaxFor(header[1]) : 'both';
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
// Honour an inline opt-out in any comment style (`// pr-checkmate-ignore`,
|
|
39
|
+
// `# pr-checkmate-ignore`).
|
|
40
|
+
if (line.includes('pr-checkmate-ignore'))
|
|
41
|
+
continue;
|
|
42
|
+
// Match on the comment-stripped line so `# print(x)` in a comment isn't flagged.
|
|
43
|
+
const scanned = mode === 'off' ? line : stripInlineComment(line, syntax);
|
|
44
|
+
for (const { pattern, label } of patterns) {
|
|
45
|
+
if (pattern.test(scanned)) {
|
|
46
|
+
(findings[label] ?? (findings[label] = [])).push(line.slice(1).trim().substring(0, 100));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export const leftoverDebugCheck = {
|
|
52
|
+
name: 'Leftover Debug',
|
|
53
|
+
phase: 'informational',
|
|
54
|
+
// No `requiresDiff`: a stray `print()` is stray wherever it sits (ticket 067).
|
|
55
|
+
applies(ctx) {
|
|
56
|
+
return (ctx.config.leftoverDebug?.enabled !== false &&
|
|
57
|
+
(ctx.languages.includes('python') || ctx.languages.includes('kotlin')));
|
|
58
|
+
},
|
|
59
|
+
async run(ctx) {
|
|
60
|
+
try {
|
|
61
|
+
const findings = {};
|
|
62
|
+
const comments = ctx.config.leftoverDebug?.commentHandling ?? 'language';
|
|
63
|
+
// One failed language set fails the check — see todo-fixme, same reason.
|
|
64
|
+
let reviewed = true;
|
|
65
|
+
if (ctx.languages.includes('python')) {
|
|
66
|
+
reviewed =
|
|
67
|
+
(await reviewLines(ctx, ['*.py'], out => collect(out, PYTHON_PATTERNS, findings, comments))) && reviewed;
|
|
68
|
+
}
|
|
69
|
+
if (ctx.languages.includes('kotlin')) {
|
|
70
|
+
reviewed =
|
|
71
|
+
(await reviewLines(ctx, ['*.kt', '*.kts', '*.java'], out => collect(out, KOTLIN_PATTERNS, findings, comments))) && reviewed;
|
|
72
|
+
}
|
|
73
|
+
if (!reviewed)
|
|
74
|
+
return skip('git unavailable');
|
|
75
|
+
if (bucketTotal(findings) === 0)
|
|
76
|
+
return pass();
|
|
77
|
+
// Advisory by default; a repo may promote to a gate via
|
|
78
|
+
// `leftoverDebug.severity: 'error'`.
|
|
79
|
+
return reportCountedFindings(findings, ctx, {
|
|
80
|
+
prefix: 'Leftover Debug',
|
|
81
|
+
severity: ctx.config.leftoverDebug?.severity,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
logger.warn(`[Leftover Debug]: ⚠️ Could not analyze diff\n${err}`);
|
|
86
|
+
return skip('diff unavailable');
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { execa } from '../../../utils/execa.js';
|
|
4
|
+
import { logger } from '../../../utils/index.js';
|
|
5
|
+
import { excludeClientPaths } from '../../targets.js';
|
|
6
|
+
import { pass, skip } from '../../types.js';
|
|
7
|
+
import { severityEmitter } from '../findings.js';
|
|
8
|
+
/**
|
|
9
|
+
* Manifest → its lockfile(s). A change to the manifest's dependency set without
|
|
10
|
+
* a corresponding lockfile update is drift: CI resolves versions the author
|
|
11
|
+
* never locked. Matched by basename so monorepo sub-packages work too.
|
|
12
|
+
*/
|
|
13
|
+
const LOCK_PAIRS = [
|
|
14
|
+
{ manifest: 'package.json', locks: ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml'] },
|
|
15
|
+
{ manifest: 'pyproject.toml', locks: ['poetry.lock', 'uv.lock'] },
|
|
16
|
+
{ manifest: 'requirements.in', locks: ['requirements.txt'] },
|
|
17
|
+
{ manifest: 'go.mod', locks: ['go.sum'] },
|
|
18
|
+
{ manifest: 'Cargo.toml', locks: ['Cargo.lock'] },
|
|
19
|
+
{ manifest: 'Gemfile', locks: ['Gemfile.lock'] },
|
|
20
|
+
];
|
|
21
|
+
const DEP_KEYS = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
|
|
22
|
+
/** Extract only the dependency sections of a parsed package.json, or `{}`. */
|
|
23
|
+
function extractDeps(raw) {
|
|
24
|
+
try {
|
|
25
|
+
const json = JSON.parse(raw);
|
|
26
|
+
const deps = {};
|
|
27
|
+
for (const key of DEP_KEYS)
|
|
28
|
+
if (json[key])
|
|
29
|
+
deps[key] = json[key];
|
|
30
|
+
return deps;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* True when a package.json's dependency sections differ between base and head.
|
|
38
|
+
* Guards against false positives from `version`/`scripts`-only edits that don't
|
|
39
|
+
* need a lockfile refresh. A newly added manifest (no base version) counts as
|
|
40
|
+
* changed. Other ecosystems have no zero-dependency parser here, so they trigger
|
|
41
|
+
* on any manifest change.
|
|
42
|
+
*/
|
|
43
|
+
async function packageDepsChanged(ctx, manifestPath, base) {
|
|
44
|
+
let headDeps = {};
|
|
45
|
+
try {
|
|
46
|
+
headDeps = extractDeps(fs.readFileSync(path.join(ctx.cwd, manifestPath), 'utf-8'));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Head file unreadable (e.g. deleted) — treat as changed so it surfaces.
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
const { stdout, exitCode } = await execa('git', ['show', `${base}:${manifestPath}`], {
|
|
53
|
+
cwd: ctx.cwd,
|
|
54
|
+
reject: false,
|
|
55
|
+
});
|
|
56
|
+
const baseDeps = exitCode === 0 ? extractDeps(stdout) : {};
|
|
57
|
+
return JSON.stringify(headDeps) !== JSON.stringify(baseDeps);
|
|
58
|
+
}
|
|
59
|
+
export const lockfileDriftCheck = {
|
|
60
|
+
name: 'Lockfile Drift',
|
|
61
|
+
phase: 'informational',
|
|
62
|
+
requiresDiff: true,
|
|
63
|
+
applies(ctx) {
|
|
64
|
+
return ctx.config.lockfileDrift?.enabled !== false;
|
|
65
|
+
},
|
|
66
|
+
async run(ctx) {
|
|
67
|
+
try {
|
|
68
|
+
// Raw name-only diff — manifests/locks live at the repo root, outside any
|
|
69
|
+
// configured `sourcePath`, so this deliberately does NOT scope (same as
|
|
70
|
+
// the Sensitive Files guard). Scoping would hide the very files we check.
|
|
71
|
+
//
|
|
72
|
+
// `excludePaths` is still applied, below: not scoping is an argument about
|
|
73
|
+
// WHERE these files live, not a licence to ignore a client who named one
|
|
74
|
+
// (ticket 080). A vendored sub-project's lockfile is a real case.
|
|
75
|
+
const { stdout } = await execa('git', [
|
|
76
|
+
'diff',
|
|
77
|
+
'--name-only',
|
|
78
|
+
'--diff-filter=ACMRD',
|
|
79
|
+
ctx.baseSha,
|
|
80
|
+
ctx.headSha ?? 'HEAD',
|
|
81
|
+
], { cwd: ctx.cwd });
|
|
82
|
+
const changed = new Set(excludeClientPaths(stdout.split('\n').filter(Boolean), ctx.cwd));
|
|
83
|
+
const warnings = [];
|
|
84
|
+
for (const changedPath of changed) {
|
|
85
|
+
const pair = LOCK_PAIRS.find(p => p.manifest === path.basename(changedPath));
|
|
86
|
+
if (!pair)
|
|
87
|
+
continue;
|
|
88
|
+
// For package.json, only flag when the dependency set actually changed.
|
|
89
|
+
if (pair.manifest === 'package.json') {
|
|
90
|
+
if (!(await packageDepsChanged(ctx, changedPath, ctx.baseSha)))
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const dir = path.dirname(changedPath);
|
|
94
|
+
const lockPath = (lock) => (dir === '.' ? lock : `${dir}/${lock}`);
|
|
95
|
+
const presentLocks = pair.locks.filter(lock => fs.existsSync(path.join(ctx.cwd, lockPath(lock))));
|
|
96
|
+
if (presentLocks.length === 0)
|
|
97
|
+
continue; // no lockfile committed → nothing to drift
|
|
98
|
+
const anyLockChanged = presentLocks.some(lock => changed.has(lockPath(lock)));
|
|
99
|
+
if (!anyLockChanged) {
|
|
100
|
+
warnings.push(`${changedPath} changed but ${presentLocks.join(' / ')} not updated`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (warnings.length === 0)
|
|
104
|
+
return pass();
|
|
105
|
+
const sev = severityEmitter(ctx.config.lockfileDrift?.severity, 'warn');
|
|
106
|
+
warnings.forEach(w => sev.log(`[Lockfile Drift]: ${sev.icon} ${w}`));
|
|
107
|
+
return sev.emit(`${warnings.length} manifest(s) changed without a lockfile update`);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
logger.warn(`[Lockfile Drift]: ⚠️ Could not analyze diff\n${err}`);
|
|
111
|
+
return skip('diff unavailable');
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { getSourcePaths } from '../../../config/index.js';
|
|
2
|
+
import { execa } from '../../../utils/execa.js';
|
|
3
|
+
import { logger } from '../../../utils/index.js';
|
|
4
|
+
import { resolveScopedTargetFiles } from '../../targets.js';
|
|
5
|
+
import { pass, skip } from '../../types.js';
|
|
6
|
+
import { scanTrackedFiles, severityEmitter } from '../findings.js';
|
|
7
|
+
/**
|
|
8
|
+
* Git conflict markers, matched against the content of an added diff line
|
|
9
|
+
* (the leading `+` already stripped). Each is exactly seven characters at the
|
|
10
|
+
* start of the line, optionally followed by a space + branch/ref label
|
|
11
|
+
* (`=======` stands alone). `|||||||` is the diff3 base-section marker.
|
|
12
|
+
*/
|
|
13
|
+
const MARKER_START = /^<{7}(?: |$)/;
|
|
14
|
+
const MARKER_BASE = /^\|{7}(?: |$)/;
|
|
15
|
+
const MARKER_SEP = /^={7}$/;
|
|
16
|
+
const MARKER_END = /^>{7}(?: |$)/;
|
|
17
|
+
/** An angle marker (`<<<<<<<` / `>>>>>>>`) is unambiguous on its own. */
|
|
18
|
+
function isAngleMarker(content) {
|
|
19
|
+
return MARKER_START.test(content) || MARKER_END.test(content);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Parse a `git diff --unified=0` and return every added line that looks like a
|
|
23
|
+
* conflict marker, tracking the file and new-file line number for reporting.
|
|
24
|
+
*/
|
|
25
|
+
function scanDiff(diff) {
|
|
26
|
+
const hits = [];
|
|
27
|
+
let file = '';
|
|
28
|
+
let newLine = 0;
|
|
29
|
+
for (const raw of diff.split('\n')) {
|
|
30
|
+
if (raw.startsWith('+++ ')) {
|
|
31
|
+
// `+++ b/path` (or `+++ /dev/null` for deletions). Strip the `b/` prefix.
|
|
32
|
+
const path = raw.slice(4).trim();
|
|
33
|
+
file = path === '/dev/null' ? '' : path.replace(/^b\//, '');
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (raw.startsWith('---'))
|
|
37
|
+
continue; // old-file header, never a content line
|
|
38
|
+
if (raw.startsWith('@@')) {
|
|
39
|
+
// @@ -oldStart,oldLen +newStart,newLen @@ — reset the new-file counter.
|
|
40
|
+
const m = /\+(\d+)/.exec(raw);
|
|
41
|
+
newLine = m ? Number(m[1]) : 0;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (raw.startsWith('+')) {
|
|
45
|
+
const content = raw.slice(1);
|
|
46
|
+
if (file &&
|
|
47
|
+
(isAngleMarker(content) || MARKER_BASE.test(content) || MARKER_SEP.test(content))) {
|
|
48
|
+
hits.push({ file, line: newLine, angle: isAngleMarker(content) });
|
|
49
|
+
}
|
|
50
|
+
newLine += 1;
|
|
51
|
+
}
|
|
52
|
+
// Removed (`-`) lines don't advance the new-file counter; with --unified=0
|
|
53
|
+
// there are no context lines to account for.
|
|
54
|
+
}
|
|
55
|
+
return hits;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Keep every angle/base marker, but a bare `=======` only counts when the same
|
|
59
|
+
* file also carries an angle marker. A standalone `=======` is a legitimate
|
|
60
|
+
* markdown setext-heading underline; requiring a bracketing `<<<<<<<`/`>>>>>>>`
|
|
61
|
+
* in the file avoids that false positive while still catching real conflicts.
|
|
62
|
+
*/
|
|
63
|
+
function filterHits(hits) {
|
|
64
|
+
const filesWithAngle = new Set(hits.filter(h => h.angle).map(h => h.file));
|
|
65
|
+
return hits.filter(h => h.angle || filesWithAngle.has(h.file));
|
|
66
|
+
}
|
|
67
|
+
export const mergeConflictCheck = {
|
|
68
|
+
name: 'Merge Conflict',
|
|
69
|
+
phase: 'blocking',
|
|
70
|
+
// No `requiresDiff`: a committed conflict marker is broken code whether it
|
|
71
|
+
// arrived in this PR or survived an earlier bad merge (ticket 067). This is
|
|
72
|
+
// the check most worth running over a whole repository, since a marker that
|
|
73
|
+
// slipped through once stays there silently.
|
|
74
|
+
applies(ctx) {
|
|
75
|
+
return ctx.config.mergeConflict?.enabled !== false;
|
|
76
|
+
},
|
|
77
|
+
async run(ctx) {
|
|
78
|
+
// Resolve the scoped file set the canonical way (delta-aware, honouring the
|
|
79
|
+
// client's `ignoreDirs` and `sourcePath`), then diff only those files for
|
|
80
|
+
// content. `['*']` matches every path since conflicts are language-agnostic;
|
|
81
|
+
// scoping still keeps build/vendor/report dirs out, like every other check.
|
|
82
|
+
const files = await resolveScopedTargetFiles(ctx, ['*'], getSourcePaths(ctx.cwd));
|
|
83
|
+
if (files === null)
|
|
84
|
+
return skip('git unavailable');
|
|
85
|
+
if (files.length === 0)
|
|
86
|
+
return pass();
|
|
87
|
+
try {
|
|
88
|
+
const hits = [];
|
|
89
|
+
if (ctx.baseSha) {
|
|
90
|
+
const { stdout } = await execa('git', ['diff', '--unified=0', ctx.baseSha, ctx.headSha ?? 'HEAD', '--', ...files], { cwd: ctx.cwd });
|
|
91
|
+
hits.push(...scanDiff(stdout));
|
|
92
|
+
}
|
|
93
|
+
else if (ctx.config.run?.fullScanBehaviour !== 'skip') {
|
|
94
|
+
await scanTrackedFiles(ctx, files, asDiff => hits.push(...scanDiff(asDiff)));
|
|
95
|
+
}
|
|
96
|
+
const found = filterHits(hits);
|
|
97
|
+
if (found.length === 0)
|
|
98
|
+
return pass();
|
|
99
|
+
// Conflict markers are a hard error by default; a repo may downgrade to
|
|
100
|
+
// `warn` via `mergeConflict.severity`.
|
|
101
|
+
const sev = severityEmitter(ctx.config.mergeConflict?.severity, 'error');
|
|
102
|
+
for (const h of found) {
|
|
103
|
+
sev.log(`[Merge Conflict]: ${sev.icon} ${h.file}:${h.line}`);
|
|
104
|
+
}
|
|
105
|
+
const sample = found
|
|
106
|
+
.slice(0, 5)
|
|
107
|
+
.map(h => `${h.file}:${h.line}`)
|
|
108
|
+
.join(', ');
|
|
109
|
+
const more = found.length > 5 ? ` (+${found.length - 5} more)` : '';
|
|
110
|
+
return sev.emit(` unresolved conflict marker(s): ${sample}${more}`);
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
logger.warn(`[Merge Conflict]: ⚠️ Could not analyze diff\n${err}`);
|
|
114
|
+
return skip('diff unavailable');
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
};
|