diff --git a/.github/scripts/trunk-impacted-targets.js b/.github/scripts/trunk-impacted-targets.js index 4f4bf9de3cac..778461fbe882 100644 --- a/.github/scripts/trunk-impacted-targets.js +++ b/.github/scripts/trunk-impacted-targets.js @@ -174,10 +174,21 @@ function isTripwire(file) { return TRIPWIRE_MATCHERS.some((re) => re.test(file)) } +// Tool caches share the directory with the products, so a local run can pick up +// directories such as .ruff_cache, .pytest_cache, and __pycache__ as products and +// invent a lane for each. CI never sees them because a fresh checkout has only +// tracked directories and this job does not run Python, so this keeps a local run +// consistent with CI rather than fixing a live miscount. Dropping a real product +// would only ever widen, because an unrecognized product name falls through to +// ALL, so the filter is safe in the one direction it can be wrong. +function isProductDirectory(name) { + return !name.startsWith('.') && !name.startsWith('__') && name !== 'node_modules' +} + function listProducts(repoRoot) { return fs .readdirSync(path.join(repoRoot, 'products'), { withFileTypes: true }) - .filter((entry) => entry.isDirectory()) + .filter((entry) => entry.isDirectory() && isProductDirectory(entry.name)) .map((entry) => entry.name) .sort() } @@ -275,8 +286,7 @@ function compileContractMatcher(inputs) { if (include.length === 0) { return null } - return (relativePath) => - include.some((re) => re.test(relativePath)) && !exclude.some((re) => re.test(relativePath)) + return (relativePath) => include.some((re) => re.test(relativePath)) && !exclude.some((re) => re.test(relativePath)) } // Only products that narrow `backend:contract-check` in their own turbo.json get @@ -763,6 +773,7 @@ module.exports = { buildContext, compileContractMatcher, globToRegExp, + isProductDirectory, isTripwire, parseCrateDependencies, parseCrateName, diff --git a/.github/scripts/trunk-impacted-targets.test.js b/.github/scripts/trunk-impacted-targets.test.js index c366416e291d..69ba37bef0f5 100644 --- a/.github/scripts/trunk-impacted-targets.test.js +++ b/.github/scripts/trunk-impacted-targets.test.js @@ -14,6 +14,7 @@ const { computeTargets, compileContractMatcher, globToRegExp, + isProductDirectory, isTripwire, parseCrateDependencies, reverseClosure, @@ -301,6 +302,18 @@ test('product frontend and backend changes land in separate domains', () => { assert.deepEqual(computeTargets(['products/beta/backend/api.py'], CONTEXT), ['py:product:beta']) }) +// ruff and pytest leave caches next to the products, and a run that treats them +// as products invents a lane per cache. Nothing downstream rejects a nonsense +// target name, so the only symptom is a local run disagreeing with CI. +test('tool caches beside the products are not products', () => { + for (const name of ['.ruff_cache', '.pytest_cache', '__pycache__', 'node_modules']) { + assert.equal(isProductDirectory(name), false, name) + } + for (const name of ['surveys', 'error_tracking', 'desktop']) { + assert.equal(isProductDirectory(name), true, name) + } +}) + test('a product file that is neither backend nor frontend claims both domains', () => { const targets = computeTargets(['products/beta/mcp/tools.yaml'], CONTEXT) assert.equal(targets.includes('py:product:beta'), true)