mirror of https://github.com/actions/checkout
Compare commits
2 Commits
43ff85c385
...
047af58489
| Author | SHA1 | Date |
|---|---|---|
|
|
047af58489 | |
|
|
3ff67abc5a |
|
|
@ -516,19 +516,29 @@ describe('timeout and retry configuration', () => {
|
||||||
jest.restoreAllMocks()
|
jest.restoreAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('setTimeout converts seconds to milliseconds', async () => {
|
it('setTimeout accepts valid values', async () => {
|
||||||
git = await createMockGit()
|
git = await createMockGit()
|
||||||
// Should not throw
|
|
||||||
git.setTimeout(30)
|
git.setTimeout(30)
|
||||||
git.setTimeout(0)
|
git.setTimeout(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('setTimeout rejects negative values', async () => {
|
||||||
|
git = await createMockGit()
|
||||||
|
expect(() => git.setTimeout(-1)).toThrow(/non-negative/)
|
||||||
|
})
|
||||||
|
|
||||||
it('setRetryConfig accepts valid parameters', async () => {
|
it('setRetryConfig accepts valid parameters', async () => {
|
||||||
git = await createMockGit()
|
git = await createMockGit()
|
||||||
// Should not throw
|
|
||||||
git.setRetryConfig(5, 2, 15)
|
git.setRetryConfig(5, 2, 15)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('setRetryConfig rejects min > max backoff', async () => {
|
||||||
|
git = await createMockGit()
|
||||||
|
expect(() => git.setRetryConfig(3, 20, 5)).toThrow(
|
||||||
|
/min seconds should be less than or equal to max seconds/
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it('fetch without timeout uses exec', async () => {
|
it('fetch without timeout uses exec', async () => {
|
||||||
git = await createMockGit()
|
git = await createMockGit()
|
||||||
// timeout defaults to 0 (disabled)
|
// timeout defaults to 0 (disabled)
|
||||||
|
|
@ -540,6 +550,28 @@ describe('timeout and retry configuration', () => {
|
||||||
const fetchCalls = mockExec.mock.calls.filter(
|
const fetchCalls = mockExec.mock.calls.filter(
|
||||||
(call: any[]) => (call[1] as string[]).includes('fetch')
|
(call: any[]) => (call[1] as string[]).includes('fetch')
|
||||||
)
|
)
|
||||||
expect(fetchCalls.length).toBeGreaterThan(0)
|
expect(fetchCalls).toHaveLength(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('fetch with timeout does not use exec', async () => {
|
||||||
|
git = await createMockGit()
|
||||||
|
// Short timeout and single attempt so the test completes quickly
|
||||||
|
git.setTimeout(1)
|
||||||
|
git.setRetryConfig(1, 0, 0)
|
||||||
|
|
||||||
|
mockExec.mockClear()
|
||||||
|
|
||||||
|
// fetch will use spawn path (which will fail/timeout since there's
|
||||||
|
// no real git repo), but we verify exec.exec was NOT called for fetch
|
||||||
|
try {
|
||||||
|
await git.fetch(['refs/heads/main'], {})
|
||||||
|
} catch {
|
||||||
|
// Expected: spawn will fail/timeout in test environment
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchCalls = mockExec.mock.calls.filter(
|
||||||
|
(call: any[]) => (call[1] as string[]).includes('fetch')
|
||||||
|
)
|
||||||
|
expect(fetchCalls).toHaveLength(0)
|
||||||
|
}, 10000)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -171,11 +171,14 @@ describe('input-helper tests', () => {
|
||||||
expect(settings.retryMaxBackoff).toBe(15)
|
expect(settings.retryMaxBackoff).toBe(15)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('clamps retry-max-backoff to min when less than min', async () => {
|
it('clamps retry-max-backoff to min when less than min and warns', async () => {
|
||||||
inputs['retry-min-backoff'] = '20'
|
inputs['retry-min-backoff'] = '20'
|
||||||
inputs['retry-max-backoff'] = '5'
|
inputs['retry-max-backoff'] = '5'
|
||||||
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
||||||
expect(settings.retryMaxBackoff).toBe(20)
|
expect(settings.retryMaxBackoff).toBe(20)
|
||||||
|
expect(core.warning).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining("'retry-max-backoff' (5) is less than 'retry-min-backoff' (20)")
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('defaults invalid timeout to 300 and warns', async () => {
|
it('defaults invalid timeout to 300 and warns', async () => {
|
||||||
|
|
|
||||||
|
|
@ -980,7 +980,10 @@ class GitCommandManager {
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
args.push('--recursive');
|
args.push('--recursive');
|
||||||
}
|
}
|
||||||
yield this.execGit(args);
|
const that = this;
|
||||||
|
yield this.networkRetryHelper.execute(() => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
yield that.execGit(args, { timeoutMs: that.timeoutMs });
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
submoduleUpdate(fetchDepth, recursive) {
|
submoduleUpdate(fetchDepth, recursive) {
|
||||||
|
|
@ -993,7 +996,10 @@ class GitCommandManager {
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
args.push('--recursive');
|
args.push('--recursive');
|
||||||
}
|
}
|
||||||
yield this.execGit(args);
|
const that = this;
|
||||||
|
yield this.networkRetryHelper.execute(() => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
yield that.execGit(args, { timeoutMs: that.timeoutMs });
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
submoduleStatus() {
|
submoduleStatus() {
|
||||||
|
|
@ -1241,13 +1247,16 @@ class GitCommandManager {
|
||||||
forceKillTimer.unref();
|
forceKillTimer.unref();
|
||||||
}
|
}
|
||||||
}, timeoutMs);
|
}, timeoutMs);
|
||||||
|
if (timer.unref) {
|
||||||
|
timer.unref();
|
||||||
|
}
|
||||||
child.on('close', (code) => {
|
child.on('close', (code) => {
|
||||||
if (settled)
|
if (settled)
|
||||||
return;
|
return;
|
||||||
settled = true;
|
settled = true;
|
||||||
cleanup();
|
cleanup();
|
||||||
if (timedOut) {
|
if (timedOut) {
|
||||||
reject(new Error(`Git operation timed out after ${timeoutMs / 1000} seconds: git ${args.slice(0, 3).join(' ')}...`));
|
reject(new Error(`Git operation timed out after ${timeoutMs / 1000} seconds: git ${args.slice(0, 5).join(' ')}...`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// null code means killed by signal (e.g. OOM killer, external SIGTERM)
|
// null code means killed by signal (e.g. OOM killer, external SIGTERM)
|
||||||
|
|
@ -2263,6 +2272,7 @@ function getInputs() {
|
||||||
result.retryMaxBackoff = 20;
|
result.retryMaxBackoff = 20;
|
||||||
}
|
}
|
||||||
if (result.retryMaxBackoff < result.retryMinBackoff) {
|
if (result.retryMaxBackoff < result.retryMinBackoff) {
|
||||||
|
core.warning(`'retry-max-backoff' (${result.retryMaxBackoff}) is less than 'retry-min-backoff' (${result.retryMinBackoff}). Using retry-min-backoff value for both.`);
|
||||||
result.retryMaxBackoff = result.retryMinBackoff;
|
result.retryMaxBackoff = result.retryMinBackoff;
|
||||||
}
|
}
|
||||||
core.debug(`retry max backoff = ${result.retryMaxBackoff}`);
|
core.debug(`retry max backoff = ${result.retryMaxBackoff}`);
|
||||||
|
|
|
||||||
|
|
@ -455,7 +455,10 @@ class GitCommandManager {
|
||||||
args.push('--recursive')
|
args.push('--recursive')
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.execGit(args)
|
const that = this
|
||||||
|
await this.networkRetryHelper.execute(async () => {
|
||||||
|
await that.execGit(args, {timeoutMs: that.timeoutMs})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async submoduleUpdate(fetchDepth: number, recursive: boolean): Promise<void> {
|
async submoduleUpdate(fetchDepth: number, recursive: boolean): Promise<void> {
|
||||||
|
|
@ -469,7 +472,10 @@ class GitCommandManager {
|
||||||
args.push('--recursive')
|
args.push('--recursive')
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.execGit(args)
|
const that = this
|
||||||
|
await this.networkRetryHelper.execute(async () => {
|
||||||
|
await that.execGit(args, {timeoutMs: that.timeoutMs})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async submoduleStatus(): Promise<boolean> {
|
async submoduleStatus(): Promise<boolean> {
|
||||||
|
|
@ -799,6 +805,9 @@ class GitCommandManager {
|
||||||
forceKillTimer.unref()
|
forceKillTimer.unref()
|
||||||
}
|
}
|
||||||
}, timeoutMs)
|
}, timeoutMs)
|
||||||
|
if (timer.unref) {
|
||||||
|
timer.unref()
|
||||||
|
}
|
||||||
|
|
||||||
child.on('close', (code: number | null) => {
|
child.on('close', (code: number | null) => {
|
||||||
if (settled) return
|
if (settled) return
|
||||||
|
|
@ -808,7 +817,7 @@ class GitCommandManager {
|
||||||
if (timedOut) {
|
if (timedOut) {
|
||||||
reject(
|
reject(
|
||||||
new Error(
|
new Error(
|
||||||
`Git operation timed out after ${timeoutMs / 1000} seconds: git ${args.slice(0, 3).join(' ')}...`
|
`Git operation timed out after ${timeoutMs / 1000} seconds: git ${args.slice(0, 5).join(' ')}...`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,9 @@ export async function getInputs(): Promise<IGitSourceSettings> {
|
||||||
result.retryMaxBackoff = 20
|
result.retryMaxBackoff = 20
|
||||||
}
|
}
|
||||||
if (result.retryMaxBackoff < result.retryMinBackoff) {
|
if (result.retryMaxBackoff < result.retryMinBackoff) {
|
||||||
|
core.warning(
|
||||||
|
`'retry-max-backoff' (${result.retryMaxBackoff}) is less than 'retry-min-backoff' (${result.retryMinBackoff}). Using retry-min-backoff value for both.`
|
||||||
|
)
|
||||||
result.retryMaxBackoff = result.retryMinBackoff
|
result.retryMaxBackoff = result.retryMinBackoff
|
||||||
}
|
}
|
||||||
core.debug(`retry max backoff = ${result.retryMaxBackoff}`)
|
core.debug(`retry max backoff = ${result.retryMaxBackoff}`)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue