fix(ci): replace minimatch with path.matchesGlob in pr-template-policy (#3701)

Root cause: e50ad812 introduced `const { minimatch } = require('minimatch')`
in scripts/pr-template-policy.cjs, but minimatch is not listed in
package.json (neither dependencies nor devDependencies). The
.github/workflows/pr-template-format.yml workflow checks out main via
actions/checkout and runs the script directly with no `npm ci`/`npm install`
step. Because the workflow uses `pull_request_target` + plain checkout
(no `ref:`), every PR — including PRs that don't touch this file — invokes
main's broken script and the `Pull request template format` check fails
with `Cannot find module 'minimatch'`. This blocks every open PR's check.

Fix choice: replace minimatch with Node's built-in `path.matchesGlob`
(stable since Node 22, required engine is `>=22.0.0`). The only minimatch
usage was `minimatch(file, pattern, { matchBase: false, dot: true })` in
allPathsAreTooling, against simple glob patterns (`**`, `*`, `*.md`,
`requirements*.txt`, etc.) with no extglobs, brace-expansion alternation,
or negation. path.matchesGlob handles all required cases including dot
files, so no new dependency is needed and no workflow change is required.

Verified: all 25 existing tests in tests/pr-template-policy.test.cjs pass,
including the tooling carve-out, exempt-marker, and template-detection
suites. Direct script invocation with CHANGED_FILES=.github/workflows/...
produces the expected `skipped: tooling-paths` carve-out.

This unblocks every open PR's `Pull request template format` check.
This commit is contained in:
Tom Boucher
2026-05-18 12:41:01 -04:00
committed by GitHub
parent e50ad8127f
commit 1645bb5ffd

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env node
const { minimatch } = require('minimatch');
const { matchesGlob } = require('path');
const TRUSTED_AUTHOR_ASSOCIATIONS = new Set([
'CONTRIBUTOR',
@@ -145,7 +145,7 @@ function matchingTemplate(body) {
function allPathsAreTooling(changedFiles, allowlist) {
if (!Array.isArray(changedFiles) || changedFiles.length === 0) return false;
return changedFiles.every((file) =>
allowlist.some((pattern) => minimatch(file, pattern, { matchBase: false, dot: true })),
allowlist.some((pattern) => matchesGlob(file, pattern)),
);
}