mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-10 14:06:30 +00:00
Merge e03b4117a3
into b7f96b2ea1
This commit is contained in:
commit
3f4695bdbd
8 changed files with 90 additions and 35 deletions
|
@ -1998,6 +1998,13 @@ const options: RenovateOptions[] = [
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: 'automergeComment',
|
default: 'automergeComment',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'automergeFailureComment',
|
||||||
|
description:
|
||||||
|
'If an error occurs while automerging, a comment will be created to signal the user. Only used if `automergeFailureComment=on-error`.',
|
||||||
|
type: 'string',
|
||||||
|
default: 'never',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'ignoreTests',
|
name: 'ignoreTests',
|
||||||
description: 'Set to `true` to enable automerging without tests.',
|
description: 'Set to `true` to enable automerging without tests.',
|
||||||
|
|
|
@ -34,6 +34,7 @@ export interface RenovateSharedConfig {
|
||||||
addLabels?: string[];
|
addLabels?: string[];
|
||||||
autoReplaceGlobalMatch?: boolean;
|
autoReplaceGlobalMatch?: boolean;
|
||||||
automerge?: boolean;
|
automerge?: boolean;
|
||||||
|
autoMergeFailureComment?: string;
|
||||||
automergeSchedule?: string[];
|
automergeSchedule?: string[];
|
||||||
automergeStrategy?: MergeStrategy;
|
automergeStrategy?: MergeStrategy;
|
||||||
branchName?: string;
|
branchName?: string;
|
||||||
|
|
|
@ -467,6 +467,7 @@ describe('modules/platform/github/index', () => {
|
||||||
isArchived: false,
|
isArchived: false,
|
||||||
nameWithOwner: repository,
|
nameWithOwner: repository,
|
||||||
autoMergeAllowed: true,
|
autoMergeAllowed: true,
|
||||||
|
automergeFailureComment: 'never',
|
||||||
hasIssuesEnabled: true,
|
hasIssuesEnabled: true,
|
||||||
mergeCommitAllowed: true,
|
mergeCommitAllowed: true,
|
||||||
rebaseMergeAllowed: true,
|
rebaseMergeAllowed: true,
|
||||||
|
@ -2798,6 +2799,18 @@ describe('modules/platform/github/index', () => {
|
||||||
platformPrOptions: { usePlatformAutomerge: true },
|
platformPrOptions: { usePlatformAutomerge: true },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const prConfigComment: CreatePRConfig = {
|
||||||
|
sourceBranch: 'some-branch',
|
||||||
|
targetBranch: 'dev',
|
||||||
|
prTitle: 'The Title',
|
||||||
|
prBody: 'Hello world',
|
||||||
|
labels: ['deps', 'renovate'],
|
||||||
|
platformPrOptions: {
|
||||||
|
usePlatformAutomerge: true,
|
||||||
|
automergeFailureComment: 'on-error',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const mockScope = async (repoOpts: any = {}): Promise<httpMock.Scope> => {
|
const mockScope = async (repoOpts: any = {}): Promise<httpMock.Scope> => {
|
||||||
const scope = httpMock.scope(githubApiHost);
|
const scope = httpMock.scope(githubApiHost);
|
||||||
initRepoMock(scope, 'some/repo', repoOpts);
|
initRepoMock(scope, 'some/repo', repoOpts);
|
||||||
|
@ -2975,6 +2988,16 @@ describe('modules/platform/github/index', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle GraphQL errors with automergeFailureComment', async () => {
|
||||||
|
const scope = await mockScope({ automergeFailureComment: 'on-error' });
|
||||||
|
scope
|
||||||
|
.post('/repos/some/repo/issues/123/comments')
|
||||||
|
.reply(200)
|
||||||
|
.post('/graphql')
|
||||||
|
.reply(200, graphqlAutomergeErrorResp);
|
||||||
|
await expect(github.createPr(prConfigComment)).toResolve();
|
||||||
|
});
|
||||||
|
|
||||||
it('should handle REST API errors', async () => {
|
it('should handle REST API errors', async () => {
|
||||||
const scope = await mockScope();
|
const scope = await mockScope();
|
||||||
scope.post('/graphql').reply(500);
|
scope.post('/graphql').reply(500);
|
||||||
|
|
|
@ -1697,13 +1697,21 @@ async function tryPrAutomerge(
|
||||||
{ prNumber, errors: res.errors },
|
{ prNumber, errors: res.errors },
|
||||||
'GitHub-native automerge: fail',
|
'GitHub-native automerge: fail',
|
||||||
);
|
);
|
||||||
|
if (platformPrOptions.automergeFailureComment === 'on-error') {
|
||||||
|
logger.warn('This automerge request failed');
|
||||||
|
await addComment(
|
||||||
|
prNumber,
|
||||||
|
'The Automerge Request for this PR has failed. Please attend.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`GitHub-native automerge: success...PrNo: ${prNumber}`);
|
logger.debug(`GitHub-native automerge: success...PrNo: ${prNumber}`);
|
||||||
} catch (err) /* istanbul ignore next: missing test #22198 */ {
|
} catch (err) /* istanbul ignore next: missing test #22198 */ {
|
||||||
logger.warn({ prNumber, err }, 'GitHub-native automerge: REST API error');
|
logger.warn({ prNumber, err }, 'GitHub-native automerge: REST API error');
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates PR and returns PR number
|
// Creates PR and returns PR number
|
||||||
|
|
|
@ -100,6 +100,7 @@ export interface Issue {
|
||||||
export type PlatformPrOptions = {
|
export type PlatformPrOptions = {
|
||||||
autoApprove?: boolean;
|
autoApprove?: boolean;
|
||||||
automergeStrategy?: MergeStrategy;
|
automergeStrategy?: MergeStrategy;
|
||||||
|
automergeFailureComment?: string;
|
||||||
azureWorkItemId?: number;
|
azureWorkItemId?: number;
|
||||||
bbUseDefaultReviewers?: boolean;
|
bbUseDefaultReviewers?: boolean;
|
||||||
bbAutoResolvePrTasks?: boolean;
|
bbAutoResolvePrTasks?: boolean;
|
||||||
|
|
|
@ -57,6 +57,7 @@ export function getPlatformPrOptions(
|
||||||
return {
|
return {
|
||||||
autoApprove: !!config.autoApprove,
|
autoApprove: !!config.autoApprove,
|
||||||
automergeStrategy: config.automergeStrategy,
|
automergeStrategy: config.automergeStrategy,
|
||||||
|
automergeFailureComment: config.automergeFailureComment,
|
||||||
azureWorkItemId: config.azureWorkItemId ?? 0,
|
azureWorkItemId: config.azureWorkItemId ?? 0,
|
||||||
bbAutoResolvePrTasks: !!config.bbAutoResolvePrTasks,
|
bbAutoResolvePrTasks: !!config.bbAutoResolvePrTasks,
|
||||||
bbUseDefaultReviewers: !!config.bbUseDefaultReviewers,
|
bbUseDefaultReviewers: !!config.bbUseDefaultReviewers,
|
||||||
|
|
|
@ -116,6 +116,7 @@ export interface BranchConfig
|
||||||
LegacyAdminConfig,
|
LegacyAdminConfig,
|
||||||
PlatformPrOptions {
|
PlatformPrOptions {
|
||||||
automergeComment?: string;
|
automergeComment?: string;
|
||||||
|
automergeFailureComment?: string;
|
||||||
automergeType?: string;
|
automergeType?: string;
|
||||||
automergedPreviously?: boolean;
|
automergedPreviously?: boolean;
|
||||||
baseBranch: string;
|
baseBranch: string;
|
||||||
|
|
|
@ -19,12 +19,12 @@ describe('documentation', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('website-documentation', () => {
|
describe('website-documentation', () => {
|
||||||
function getConfigOptionSubHeaders(
|
async function getConfigOptionSubHeaders(
|
||||||
file: string,
|
file: string,
|
||||||
configOption: string,
|
configOption: string,
|
||||||
): string[] {
|
): Promise<string[]> {
|
||||||
const subHeadings = [];
|
const subHeadings = [];
|
||||||
const content = fs.readFileSync(`docs/usage/${file}`, 'utf8');
|
const content = await fs.readFile(`docs/usage/${file}`, 'utf8');
|
||||||
const reg = regEx(`##\\s${configOption}[\\s\\S]+?\n##\\s`);
|
const reg = regEx(`##\\s${configOption}[\\s\\S]+?\n##\\s`);
|
||||||
const match = reg.exec(content);
|
const match = reg.exec(content);
|
||||||
const subHeadersMatch = match?.[0]?.matchAll(/\n###\s(?<child>\w+)\n/g);
|
const subHeadersMatch = match?.[0]?.matchAll(/\n###\s(?<child>\w+)\n/g);
|
||||||
|
@ -39,8 +39,8 @@ describe('documentation', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('docs/usage/configuration-options.md', () => {
|
describe('docs/usage/configuration-options.md', () => {
|
||||||
function getConfigHeaders(file: string): string[] {
|
async function getConfigHeaders(file: string): Promise<string[]> {
|
||||||
const content = fs.readFileSync(`docs/usage/${file}`, 'utf8');
|
const content = await fs.readFile(`docs/usage/${file}`, 'utf8');
|
||||||
const matches = content.match(/\n## (.*?)\n/g) ?? [];
|
const matches = content.match(/\n## (.*?)\n/g) ?? [];
|
||||||
return matches.map((match) => match.substring(4, match.length - 1));
|
return matches.map((match) => match.substring(4, match.length - 1));
|
||||||
}
|
}
|
||||||
|
@ -54,20 +54,20 @@ describe('documentation', () => {
|
||||||
.sort();
|
.sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
it('has doc headers sorted alphabetically', () => {
|
it('has doc headers sorted alphabetically', async () => {
|
||||||
expect(getConfigHeaders('configuration-options.md')).toEqual(
|
expect(await getConfigHeaders('configuration-options.md')).toEqual(
|
||||||
getConfigHeaders('configuration-options.md').sort(),
|
(await getConfigHeaders('configuration-options.md')).sort(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has headers for every required option', () => {
|
it('has headers for every required option', async () => {
|
||||||
expect(getConfigHeaders('configuration-options.md')).toEqual(
|
expect(await getConfigHeaders('configuration-options.md')).toEqual(
|
||||||
getRequiredConfigOptions(),
|
getRequiredConfigOptions(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
function getConfigSubHeaders(file: string): string[] {
|
async function getConfigSubHeaders(file: string): Promise<string[]> {
|
||||||
const content = fs.readFileSync(`docs/usage/${file}`, 'utf8');
|
const content = await fs.readFile(`docs/usage/${file}`, 'utf8');
|
||||||
const matches = content.match(/\n### (.*?)\n/g) ?? [];
|
const matches = content.match(/\n### (.*?)\n/g) ?? [];
|
||||||
return matches
|
return matches
|
||||||
.map((match) => match.substring(5, match.length - 1))
|
.map((match) => match.substring(5, match.length - 1))
|
||||||
|
@ -100,21 +100,26 @@ describe('documentation', () => {
|
||||||
return parentNames;
|
return parentNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
it('has headers for every required sub-option', () => {
|
it('has headers for every required sub-option', async () => {
|
||||||
expect(getConfigSubHeaders('configuration-options.md')).toEqual(
|
expect(await getConfigSubHeaders('configuration-options.md')).toEqual(
|
||||||
getRequiredConfigSubOptions(),
|
getRequiredConfigSubOptions(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([...getParentNames()])(
|
test.each([...getParentNames()])(
|
||||||
'%s has sub-headers sorted alphabetically',
|
'%s has sub-headers sorted alphabetically',
|
||||||
(parentName: string) => {
|
async (parentName: string) => {
|
||||||
expect(
|
expect(
|
||||||
getConfigOptionSubHeaders('configuration-options.md', parentName),
|
await getConfigOptionSubHeaders(
|
||||||
).toEqual(
|
|
||||||
getConfigOptionSubHeaders(
|
|
||||||
'configuration-options.md',
|
'configuration-options.md',
|
||||||
parentName,
|
parentName,
|
||||||
|
),
|
||||||
|
).toEqual(
|
||||||
|
(
|
||||||
|
await getConfigOptionSubHeaders(
|
||||||
|
'configuration-options.md',
|
||||||
|
parentName,
|
||||||
|
)
|
||||||
).sort(),
|
).sort(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -122,8 +127,8 @@ describe('documentation', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('docs/usage/self-hosted-configuration.md', () => {
|
describe('docs/usage/self-hosted-configuration.md', () => {
|
||||||
function getSelfHostedHeaders(file: string): string[] {
|
async function getSelfHostedHeaders(file: string): Promise<string[]> {
|
||||||
const content = fs.readFileSync(`docs/usage/${file}`, 'utf8');
|
const content = await fs.readFile(`docs/usage/${file}`, 'utf8');
|
||||||
const matches = content.match(/\n## (.*?)\n/g) ?? [];
|
const matches = content.match(/\n## (.*?)\n/g) ?? [];
|
||||||
return matches.map((match) => match.substring(4, match.length - 1));
|
return matches.map((match) => match.substring(4, match.length - 1));
|
||||||
}
|
}
|
||||||
|
@ -135,32 +140,40 @@ describe('documentation', () => {
|
||||||
.sort();
|
.sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
it('has headers sorted alphabetically', () => {
|
it('has headers sorted alphabetically', async () => {
|
||||||
expect(getSelfHostedHeaders('self-hosted-configuration.md')).toEqual(
|
expect(
|
||||||
getSelfHostedHeaders('self-hosted-configuration.md').sort(),
|
await getSelfHostedHeaders('self-hosted-configuration.md'),
|
||||||
|
).toEqual(
|
||||||
|
(await getSelfHostedHeaders('self-hosted-configuration.md')).sort(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has headers for every required option', () => {
|
it('has headers for every required option', async () => {
|
||||||
expect(getSelfHostedHeaders('self-hosted-configuration.md')).toEqual(
|
expect(
|
||||||
getRequiredSelfHostedOptions(),
|
await getSelfHostedHeaders('self-hosted-configuration.md'),
|
||||||
);
|
).toEqual(getRequiredSelfHostedOptions());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('docs/usage/self-hosted-experimental.md', () => {
|
describe('docs/usage/self-hosted-experimental.md', () => {
|
||||||
function getSelfHostedExperimentalConfigHeaders(file: string): string[] {
|
async function getSelfHostedExperimentalConfigHeaders(
|
||||||
const content = fs.readFileSync(`docs/usage/${file}`, 'utf8');
|
file: string,
|
||||||
|
): Promise<string[]> {
|
||||||
|
const content = await fs.readFile(`docs/usage/${file}`, 'utf8');
|
||||||
const matches = content.match(/\n## (.*?)\n/g) ?? [];
|
const matches = content.match(/\n## (.*?)\n/g) ?? [];
|
||||||
return matches.map((match) => match.substring(4, match.length - 1));
|
return matches.map((match) => match.substring(4, match.length - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
it('has headers sorted alphabetically', () => {
|
it('has headers sorted alphabetically', async () => {
|
||||||
expect(
|
expect(
|
||||||
getSelfHostedExperimentalConfigHeaders('self-hosted-experimental.md'),
|
await getSelfHostedExperimentalConfigHeaders(
|
||||||
).toEqual(
|
|
||||||
getSelfHostedExperimentalConfigHeaders(
|
|
||||||
'self-hosted-experimental.md',
|
'self-hosted-experimental.md',
|
||||||
|
),
|
||||||
|
).toEqual(
|
||||||
|
(
|
||||||
|
await getSelfHostedExperimentalConfigHeaders(
|
||||||
|
'self-hosted-experimental.md',
|
||||||
|
)
|
||||||
).sort(),
|
).sort(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue