diff --git a/src/signals/path-matchers.ts b/src/signals/path-matchers.ts index fd81f38a94..94251a2505 100644 --- a/src/signals/path-matchers.ts +++ b/src/signals/path-matchers.ts @@ -76,6 +76,19 @@ const CONFIG_FILE_NAMES: ReadonlySet = new Set([ ".gitignore", ".gitattributes", ".dockerignore", + // Dependency automation and local toolchain version pins. + "renovate.json", + "dependabot.yml", + ".tool-versions", + "mise.toml", + "lefthook.yml", + "lefthook.yaml", + ".pre-commit-config.yaml", + // Hosted CI pipeline definitions (single-file basenames). + ".gitlab-ci.yml", + "azure-pipelines.yml", + "buf.yaml", + "buf.gen.yaml", ]); // Filename prefixes that identify build, lint, test-runner, and environment config files. @@ -145,9 +158,12 @@ export function isDependencyManifestFile(path: string): boolean { * lower-effort than genuine source changes, so slop signals can weight them differently (#561). */ export function isConfigFile(path: string): boolean { + const norm = normalize(path); const base = basename(path); if (CONFIG_FILE_NAMES.has(base)) return true; if (CONFIG_FILE_PREFIXES.some((prefix) => base.startsWith(prefix))) return true; + if (/(^|\/)\.github\/workflows\/[^/]+\.(ya?ml)$/.test(norm)) return true; + if (/(^|\/)\.circleci\/config\.ya?ml$/.test(norm)) return true; if (/\.(config|rc)\.[a-z0-9]+$/i.test(base)) return true; // `.stylelintrc`-style: dot-prefixed name with no extension after "rc"; `custom.rc`: dotted rc extension. return base.endsWith(".rc") || /^\.[^.]+rc$/i.test(base); diff --git a/test/unit/path-matchers.test.ts b/test/unit/path-matchers.test.ts index f1ca4a7b56..3a6bb089b6 100644 --- a/test/unit/path-matchers.test.ts +++ b/test/unit/path-matchers.test.ts @@ -175,6 +175,32 @@ describe("isConfigFile", () => { } }); + it("matches automation, toolchain, and hosted CI config files", () => { + for (const path of [ + "renovate.json", + ".github/dependabot.yml", + ".tool-versions", + "mise.toml", + "lefthook.yml", + ".pre-commit-config.yaml", + ".gitlab-ci.yml", + "azure-pipelines.yml", + "buf.yaml", + "buf.gen.yaml", + ".github/workflows/ci.yml", + ".github/workflows/release.yaml", + ".circleci/config.yml", + ]) { + expect(isConfigFile(path)).toBe(true); + } + }); + + it("does not treat renovate-like source names as config", () => { + for (const path of ["src/renovate-helpers.ts", "docs/dependabot-notes.md"]) { + expect(isConfigFile(path)).toBe(false); + } + }); + it("matches config files by known filename prefix", () => { for (const path of ["tsconfig.build.json", "vitest.config.ts", ".env.local", ".eslintrc.json", ".prettierrc.js"]) { expect(isConfigFile(path)).toBe(true); @@ -212,6 +238,8 @@ describe("classifyChangedFile", () => { ["vitest.config.ts", "config"], ["wrangler.jsonc", "config"], ["turbo.json", "config"], + ["renovate.json", "config"], + [".github/workflows/ci.yml", "config"], ["test/unit/app.test.ts", "test"], ["README.md", "docs"], ["src/app.ts", "source"],