diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index ddd5fec59..48c0c9764 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -2,12 +2,12 @@ on: pull_request: {} push: branches: - - main - - master + - main + - master paths: - - .github/workflows/semgrep.yml + - .github/workflows/semgrep.yml schedule: - - cron: '0 0 * * 0' + - cron: '0 0 * * 0' name: Semgrep jobs: semgrep: @@ -18,5 +18,5 @@ jobs: container: image: returntocorp/semgrep@sha256:98c2572fced2474539fd27cab3207ebd8e95e4e7aab4c3b381fdc5e2641d9941 # latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - run: semgrep ci + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - run: semgrep ci diff --git a/.remarkrc.mjs b/.remarkrc.mjs index 885b8f84a..4d9e19929 100644 --- a/.remarkrc.mjs +++ b/.remarkrc.mjs @@ -72,14 +72,39 @@ const naturalLanguage = unified().use([ // tagline. Ignoring a phrase here is by its text, not by the rule id // the reporter prints, so multi-word entries keep their spaces. ignore: [ - 'accomplish', 'additional', 'address', 'aggregate', 'attempt', - 'contains', 'ensure', 'equivalent', 'establish', 'function', - 'identical', 'identify', 'immediately', 'inception', 'indicate', - 'interface', 'maintain', 'multiple', 'portion', 'request', - 'require', 'subsequent', 'type', + 'accomplish', + 'additional', + 'address', + 'aggregate', + 'attempt', + 'contains', + 'ensure', + 'equivalent', + 'establish', + 'function', + 'identical', + 'identify', + 'immediately', + 'inception', + 'indicate', + 'interface', + 'maintain', + 'multiple', + 'portion', + 'request', + 'require', + 'subsequent', + 'type', // Wordiness the house style tolerates. - 'all of', 'appropriate', 'however', 'it is', 'it is essential', - 'one particular', 'overall', 'there are', 'there is', + 'all of', + 'appropriate', + 'however', + 'it is', + 'it is essential', + 'one particular', + 'overall', + 'there are', + 'there is', ], }, ], diff --git a/build/utils.mts b/build/utils.mts index 69aba9ed0..481c41db7 100644 --- a/build/utils.mts +++ b/build/utils.mts @@ -32,22 +32,60 @@ const expandDirPattern = (pattern: string) => pattern.endsWith('/') ? `${pattern}**` : pattern; /** - * Matches files by glob pattern, `globby`-style. Two of globby's conveniences - * that `fs.promises.glob` lacks are reproduced here: `!`-prefixed patterns act - * as exclusions (the native API takes those as a separate option), and only - * files are returned (the native API yields directories alongside them). + * Widens a pattern so that wildcards also match dot-prefixed names. Neither + * `*` nor `**` will do so on its own, which quietly kept every dot-config + * file out of the checks: `**` walked past `.github/`, and `*.mjs` never saw + * `.remarkrc.mjs`. Brace alternation is the way back in -- one alternative + * for descending through a dot directory, one for the dot file itself. + * + * A dot directory nested inside another (`.a/.b/`) is past what the syntax + * can express; there is none here, and one would have to be named outright. + * @param {string} pattern The glob pattern to widen. + * @returns {string[]} Patterns which between them match what the one did, dot names included. + */ +const expandDotPattern = (pattern: string) => { + const segments = pattern.split('/'); + const widened = segments + .map((segment, index) => { + if (segment === '**') return '{**,**/.*/**}'; + + // Only the basename decides whether a match is a dot file; a wildcard + // in the middle of the path is a directory name, covered above. + const isBasename = index === segments.length - 1; + + return isBasename && segment.startsWith('*') ? `{,.}${segment}` : segment; + }) + .join('/'); + + // A pattern whose own tail is `**` -- which is what naming a directory + // expands to -- has no basename segment to have been widened, so the dot + // files directly beneath it need a pattern of their own. + return segments.at(-1) === '**' + ? [widened, `${segments.slice(0, -1).join('/')}/**/{,.}*`] + : [widened]; +}; + +/** + * Matches files by glob pattern, `globby`-style. Three of globby's + * conveniences that `fs.promises.glob` lacks are reproduced here: + * `!`-prefixed patterns act as exclusions (the native API takes those as a + * separate option), wildcards match dot-prefixed names, and only files are + * returned (the native API yields directories alongside them). * @param {string | string[]} patterns Glob patterns to include, optionally mixed with `!`-prefixed patterns to exclude. * @returns {Promise} The matched file paths, relative to the cwd. */ export async function glob(patterns: string | string[]) { const include = []; - const exclude = []; + // Matching dot names is what puts `.git/` in reach of a plain `**`, and no + // task has any business reading it. Excluded directories are pruned whole, + // dot entries included, so callers need not widen their own exclusions. + const exclude = ['.git/**']; for (const pattern of [patterns].flat()) { if (pattern.startsWith('!')) { exclude.push(expandDirPattern(pattern.slice(1))); } else { - include.push(expandDirPattern(pattern)); + include.push(...expandDotPattern(expandDirPattern(pattern))); } }