fix: Cap ci-filter deepen value and fall back to --unshallow (#32581)

Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
n8n-cat-bot[bot]
2026-06-18 13:42:58 +01:00
committed by GitHub
parent db1e21fecf
commit af563c69b2
2 changed files with 82 additions and 2 deletions
@@ -420,6 +420,76 @@ describe('getChangedFiles (shallow clone, stale base)', () => {
});
});
// --- getChangedFiles deepen cap (falls back to --unshallow) ---
describe('getChangedFiles (deepen cap falls back to --unshallow)', () => {
const builderDir = mkdtempSync(join(tmpdir(), 'ci-filter-build-cap-'));
const remoteDir = mkdtempSync(join(tmpdir(), 'ci-filter-remote-cap-'));
const repoDir = mkdtempSync(join(tmpdir(), 'ci-filter-cap-'));
const originalCwd = process.cwd();
const originalStep = process.env.CI_FILTER_DEEPEN_STEP;
const originalMax = process.env.CI_FILTER_MAX_DEEPEN;
const git = (args: string[], cwd: string) =>
execFileSync('git', args, { cwd, stdio: 'pipe' }).toString().trim();
before(() => {
execFileSync('git', ['init', '--bare', '-b', 'main', remoteDir], { stdio: 'pipe' });
git(['init', '-b', 'main'], builderDir);
git(['config', 'user.email', 'test@test.local'], builderDir);
git(['config', 'user.name', 'test'], builderDir);
git(['remote', 'add', 'origin', remoteDir], builderDir);
writeFileSync(join(builderDir, 'shared.ts'), 'shared\n');
git(['add', '.'], builderDir);
git(['commit', '-m', 'root'], builderDir);
git(['push', 'origin', 'main'], builderDir);
git(['checkout', '-b', 'pr-branch'], builderDir);
writeFileSync(join(builderDir, 'pr-only.ts'), 'pr\n');
git(['add', '.'], builderDir);
git(['commit', '-m', 'PR change'], builderDir);
git(['push', 'origin', 'pr-branch'], builderDir);
git(['checkout', 'main'], builderDir);
for (let i = 1; i <= 12; i++) {
writeFileSync(join(builderDir, 'shared.ts'), `shared\ndrift ${i}\n`);
git(['commit', '-am', `drift ${i}`], builderDir);
}
git(['push', 'origin', 'main'], builderDir);
git(
['clone', '--depth=1', '--branch', 'pr-branch', `file://${remoteDir}`, repoDir],
originalCwd,
);
git(['config', 'user.email', 'test@test.local'], repoDir);
git(['config', 'user.name', 'test'], repoDir);
// Tiny step + tiny cap so the loop hits the cap and falls back to --unshallow
// well before the merge base would otherwise be reachable by deepening.
process.env.CI_FILTER_DEEPEN_STEP = '1';
process.env.CI_FILTER_MAX_DEEPEN = '2';
process.chdir(repoDir);
});
after(() => {
process.chdir(originalCwd);
if (originalStep === undefined) delete process.env.CI_FILTER_DEEPEN_STEP;
else process.env.CI_FILTER_DEEPEN_STEP = originalStep;
if (originalMax === undefined) delete process.env.CI_FILTER_MAX_DEEPEN;
else process.env.CI_FILTER_MAX_DEEPEN = originalMax;
rmSync(builderDir, { recursive: true, force: true });
rmSync(remoteDir, { recursive: true, force: true });
rmSync(repoDir, { recursive: true, force: true });
});
it('resolves merge base via --unshallow once the cap is exceeded', () => {
assert.equal(git(['rev-parse', '--is-shallow-repository'], repoDir), 'true');
const changed = getChangedFiles('main');
assert.deepEqual(changed, ['pr-only.ts']);
// After --unshallow, the repo should no longer be shallow.
assert.equal(git(['rev-parse', '--is-shallow-repository'], repoDir), 'false');
});
});
// --- getChangedFiles unrelated histories ---
describe('getChangedFiles (unrelated histories)', () => {
+12 -2
View File
@@ -123,10 +123,15 @@ export function getChangedFiles(baseRef) {
* window (doubling each round) until the merge base resolves, or until the full
* history has been fetched — at which point no common ancestor means the
* histories are unrelated.
*
* Once the doubled step grows past the repo's history (capped well under
* git's signed int32 `--deepen` limit), switch to `--unshallow` instead of
* passing an ever-larger integer that git would reject.
*/
function fetchUntilMergeBase(baseRef) {
let step = Number(process.env.CI_FILTER_DEEPEN_STEP) || 200;
execSync(`git fetch --no-tags --prune --deepen=${step} origin ${baseRef}`, { stdio: 'pipe' });
const maxDeepen = Number(process.env.CI_FILTER_MAX_DEEPEN) || 20_000;
deepenFetch(baseRef, step, maxDeepen);
while (!hasReliableMergeBase()) {
if (!isShallow()) {
@@ -135,10 +140,15 @@ function fetchUntilMergeBase(baseRef) {
);
}
step *= 2;
execSync(`git fetch --no-tags --prune --deepen=${step} origin ${baseRef}`, { stdio: 'pipe' });
deepenFetch(baseRef, step, maxDeepen);
}
}
function deepenFetch(baseRef, step, maxDeepen) {
const flag = step > maxDeepen ? '--unshallow' : `--deepen=${step}`;
execSync(`git fetch --no-tags --prune ${flag} origin ${baseRef}`, { stdio: 'pipe' });
}
function isShallow() {
return (
execSync('git rev-parse --is-shallow-repository', { encoding: 'utf-8' }).trim() === 'true'