diff --git a/bin/linter.mjs b/bin/linter.mjs index bd9a976ca944..901384f8a95c 100755 --- a/bin/linter.mjs +++ b/bin/linter.mjs @@ -63,56 +63,79 @@ function runGit(args, options = {}) { }); } +/** + * Helper to get modified/added TypeScript files against a given git ref. + * Uses merge-base when possible to compare against the common ancestor (e.g. when base and branch have both moved). + */ +function getDiffFiles(ref) { + let diffTarget = ref; + try { + const mergeBase = runGit(['merge-base', ref, 'HEAD']).trim(); + if (mergeBase) { + diffTarget = mergeBase; + } + } catch { + // If merge-base fails (e.g. shallow clone or invalid ref), fall back to using ref directly + } + + const output = runGit([ + 'diff', + '--name-only', + '--diff-filter=ACMRT', + diffTarget, + '--', + '*.ts', + ]); + return output + .split('\n') + .map(f => f.trim()) + .filter(f => f.length > 0 && existsSync(f)); +} + /** * Returns a list of changed TypeScript files comparing against target branches/references. */ function getChangedFiles() { - const base = process.env.GITHUB_BASE_REF || 'main'; - const refsToTry = [ - base, - `upstream/${base}`, - `origin/${base}`, - 'FETCH_HEAD', - 'HEAD~1', - 'HEAD^', - ]; + const isCI = Boolean(process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITHUB_BASE_REF); + + let refsToTry = []; + let isStrictCI = false; + + if (isCI) { + const baseRef = process.env.GITHUB_BASE_REF; + if (!baseRef) { + throw new Error('Running in CI but GITHUB_BASE_REF environment variable is not set.'); + } + refsToTry = baseRef.startsWith('origin/') ? [baseRef] : [`origin/${baseRef}`, baseRef]; + isStrictCI = true; + } else { + let currentBranch = ''; + try { + currentBranch = runGit(['rev-parse', '--abbrev-ref', 'HEAD']).trim(); + } catch { + // Continue with fallback refs if branch detection fails + } + + refsToTry = (currentBranch === 'main') + ? ['origin/main', 'upstream/main', 'HEAD~1'] + : ['upstream/main', 'origin/main', 'main']; + } for (const ref of refsToTry) { try { - const output = runGit([ - 'diff', - '--name-only', - '--diff-filter=ACMRT', - ref, - '--', - '*.ts', - ]); - return output - .split('\n') - .map(f => f.trim()) - .filter(f => f.length > 0 && existsSync(f)); + const files = getDiffFiles(ref); + console.log(`Comparing against base reference: ${ref}`); + return files; } catch { - // Continue to the next fallback ref + // Continue to next ref if this one fails/does not exist } } - // Fallback to checking uncommitted working tree changes against HEAD if all specific refs fail - try { - const output = runGit([ - 'diff', - '--name-only', - '--diff-filter=ACMRT', - 'HEAD', - '--', - '*.ts', - ]); - return output - .split('\n') - .map(f => f.trim()) - .filter(f => f.length > 0 && existsSync(f)); - } catch { - return []; + if (isStrictCI) { + throw new Error(`Failed to determine changed files against GITHUB_BASE_REF '${refsToTry[0]}' in CI.`); } + + throw new Error(`Failed to determine changed files. Tried refs: ${refsToTry.join(', ')}`); } // --- ESLint Checker ---