Skip to content

Commit

Permalink
feat: add git timeout (#14914)
Browse files Browse the repository at this point in the history
  • Loading branch information
hasanwhitesource committed Apr 17, 2022
1 parent da6ba64 commit 9270eba
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 3 deletions.
6 changes: 6 additions & 0 deletions docs/usage/self-hosted-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,12 @@ Before the first commit in a repository, Renovate will:
The `git` commands are run locally in the cloned repo instead of globally.
This reduces the chance of unintended consequences with global Git configs on shared systems.

## gitTimeout

To handle the case where the underlying Git processes appear to hang, configure the timeout with the number of milliseconds to wait after last received content on either `stdOut` or `stdErr` streams before sending a `SIGINT` kill message.

The value must be between `2000` and `6000` (milliseconds) inclusive.

## gitUrl

Override the default resolution for Git remote, e.g. to switch GitLab from HTTPS to SSH-based.
Expand Down
1 change: 1 addition & 0 deletions lib/config/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class GlobalConfig {
'migratePresets',
'privateKey',
'privateKeyOld',
'gitTimeout',
];

private static config: RepoGlobalConfig = {};
Expand Down
8 changes: 8 additions & 0 deletions lib/config/options/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,14 @@ const options: RenovateOptions[] = [
subType: 'string',
stage: 'repository',
},
{
name: 'gitTimeout',
description:
'Configure the timeout with a number of milliseconds to wait for a git task',
type: 'integer',
globalOnly: true,
default: 10000,
},
{
name: 'enabledManagers',
description:
Expand Down
1 change: 1 addition & 0 deletions lib/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export interface RepoGlobalConfig {
dockerUser?: string;
dryRun?: DryRunConfig;
executionTimeout?: number;
gitTimeout?: number;
exposeAllEnv?: boolean;
githubTokenWarn?: boolean;
migratePresets?: Record<string, string>;
Expand Down
13 changes: 13 additions & 0 deletions lib/config/validation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -739,5 +739,18 @@ describe('config/validation', () => {
},
]);
});

it.each([
[
'invalid value',
{
gitTimeout: 'string',
},
],
['out of range', { gitTimeout: 1000 }],
])('checks invalid git timeout values', async (_case, config) => {
const { errors } = await configValidation.validateConfig(config);
expect(errors).toHaveLength(1);
});
});
});
13 changes: 13 additions & 0 deletions lib/config/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,19 @@ export async function validateConfig(
});
continue;
}
if (key === 'gitTimeout') {
if (!is.number(val)) {
errors.push({
topic: 'Config Error',
message: `GitTimeout must be set to an integer value`,
});
} else if (!(val >= 2000 && val <= 60000)) {
errors.push({
topic: 'Config Error',
message: `GitTimeout value must be within 2 to 60 seconds`,
});
}
}
if (parentPath && topLevelObjects.includes(key)) {
errors.push({
topic: 'Configuration Error',
Expand Down
10 changes: 7 additions & 3 deletions lib/modules/manager/git-submodules/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ async function getUrl(
const headRefRe = regEx(/ref: refs\/heads\/(?<branch>\w+)\s/);

async function getDefaultBranch(subModuleUrl: string): Promise<string> {
const val = await Git().listRemote(['--symref', subModuleUrl, 'HEAD']);
const val = await Git(simpleGitConfig()).listRemote([
'--symref',
subModuleUrl,
'HEAD',
]);
return headRefRe.exec(val)?.groups?.branch ?? 'master';
}

Expand All @@ -46,7 +50,7 @@ async function getBranch(
subModuleUrl: string
): Promise<string> {
return (
(await Git().raw([
(await Git(simpleGitConfig()).raw([
'config',
'--file',
gitModulesPath,
Expand Down Expand Up @@ -91,7 +95,7 @@ export default async function extractPackageFile(
config: ExtractConfig
): Promise<PackageFile | null> {
const { localDir } = GlobalConfig.get();
const git = Git(localDir);
const git = Git(localDir, simpleGitConfig());
const gitModulesPath = upath.join(localDir, fileName);

const depNames = await getModules(git, gitModulesPath);
Expand Down
18 changes: 18 additions & 0 deletions lib/util/git/config.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
import { GlobalConfig } from '../../config/global';
import { simpleGitConfig } from './config';

describe('util/git/config', () => {
beforeEach(() => {
GlobalConfig.reset();
});

it('uses "close" events, ignores "exit" events from child processes', () => {
expect(simpleGitConfig()).toEqual({
completion: { onClose: true, onExit: false },
timeout: {
block: 10000,
},
});
});

it('uses timeout value from GlobalConfig', () => {
GlobalConfig.set({ gitTimeout: 50000 });
expect(simpleGitConfig()).toEqual({
completion: { onClose: true, onExit: false },
timeout: {
block: 50000,
},
});
});
});
4 changes: 4 additions & 0 deletions lib/util/git/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import is from '@sindresorhus/is';
import type { SimpleGitOptions } from 'simple-git';
import { GlobalConfig } from '../../config/global';
import type { GitNoVerifyOption } from './types';

let noVerify: GitNoVerifyOption[] = ['push', 'commit'];
Expand All @@ -22,5 +23,8 @@ export function simpleGitConfig(): Partial<SimpleGitOptions> {
onClose: true,
onExit: false,
},
timeout: {
block: GlobalConfig.get('gitTimeout') ?? 10000,
},
};
}

0 comments on commit 9270eba

Please sign in to comment.