feat: Allow configuring branch up-to-date-ness

Allow users to configure whether GitHub branches from pull requests are required to be up to date when merging when configuring branch protections.

Signed-off-by: martincostello <martin@martincostello.com>
This commit is contained in:
martincostello
2022-10-05 13:55:53 +01:00
parent b2bec2e5a2
commit 17ff77154c
10 changed files with 95 additions and 3 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-scaffolder-backend': minor
---
Update the `github:publish` action to allow passing whether pull
requests must be up to date with the default branch before merging.
+3
View File
@@ -199,6 +199,7 @@ export function createGithubRepoCreateAction(options: {
allowAutoMerge?: boolean | undefined;
requireCodeOwnerReviews?: boolean | undefined;
requiredStatusCheckContexts?: string[] | undefined;
requireBranchesToBeUpToDate?: boolean | undefined;
repoVisibility?: 'internal' | 'private' | 'public' | undefined;
collaborators?:
| (
@@ -236,6 +237,7 @@ export function createGithubRepoPushAction(options: {
gitAuthorEmail?: string | undefined;
requireCodeOwnerReviews?: boolean | undefined;
requiredStatusCheckContexts?: string[] | undefined;
requireBranchesToBeUpToDate?: boolean | undefined;
sourcePath?: string | undefined;
token?: string | undefined;
}>;
@@ -366,6 +368,7 @@ export function createPublishGithubAction(options: {
sourcePath?: string | undefined;
requireCodeOwnerReviews?: boolean | undefined;
requiredStatusCheckContexts?: string[] | undefined;
requireBranchesToBeUpToDate?: boolean | undefined;
repoVisibility?: 'internal' | 'private' | 'public' | undefined;
collaborators?:
| (
@@ -53,6 +53,7 @@ export function createGithubRepoCreateAction(options: {
allowAutoMerge?: boolean;
requireCodeOwnerReviews?: boolean;
requiredStatusCheckContexts?: string[];
requireBranchesToBeUpToDate?: boolean;
repoVisibility?: 'private' | 'internal' | 'public';
collaborators?: Array<
| {
@@ -85,6 +86,7 @@ export function createGithubRepoCreateAction(options: {
access: inputProps.access,
requireCodeOwnerReviews: inputProps.requireCodeOwnerReviews,
requiredStatusCheckContexts: inputProps.requiredStatusCheckContexts,
requireBranchesToBeUpToDate: inputProps.requireBranchesToBeUpToDate,
repoVisibility: inputProps.repoVisibility,
deleteBranchOnMerge: inputProps.deleteBranchOnMerge,
allowMergeCommit: inputProps.allowMergeCommit,
@@ -283,6 +283,7 @@ describe('github:repo:push', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
@@ -302,6 +303,7 @@ describe('github:repo:push', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: true,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
@@ -321,6 +323,7 @@ describe('github:repo:push', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
});
@@ -343,6 +346,7 @@ describe('github:repo:push', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
@@ -362,6 +366,7 @@ describe('github:repo:push', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
@@ -381,11 +386,12 @@ describe('github:repo:push', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: false,
});
});
it('should call enableBranchProtectionOnDefaultRepoBranch with the correct values of requiredStatusCheckContexts', async () => {
it('should call enableBranchProtectionOnDefaultRepoBranch with the correct values of requiredStatusCheckContexts and requireBranchesToBeUpToDate', async () => {
mockOctokit.rest.repos.get.mockResolvedValue({
data: {
clone_url: 'https://github.com/clone/url.git',
@@ -403,6 +409,7 @@ describe('github:repo:push', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
@@ -411,6 +418,7 @@ describe('github:repo:push', () => {
input: {
...mockContext.input,
requiredStatusCheckContexts: ['statusCheck'],
requireBranchesToBeUpToDate: true,
},
});
@@ -422,6 +430,28 @@ describe('github:repo:push', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: ['statusCheck'],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
await action.handler({
...mockContext,
input: {
...mockContext.input,
requiredStatusCheckContexts: ['statusCheck'],
requireBranchesToBeUpToDate: false,
},
});
expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({
owner: 'owner',
client: mockOctokit,
repoName: 'repository',
logger: mockContext.logger,
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: ['statusCheck'],
requireBranchesToBeUpToDate: false,
enforceAdmins: true,
});
@@ -430,6 +460,7 @@ describe('github:repo:push', () => {
input: {
...mockContext.input,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
},
});
@@ -441,6 +472,7 @@ describe('github:repo:push', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
});
@@ -50,6 +50,7 @@ export function createGithubRepoPushAction(options: {
gitAuthorEmail?: string;
requireCodeOwnerReviews?: boolean;
requiredStatusCheckContexts?: string[];
requireBranchesToBeUpToDate?: boolean;
sourcePath?: string;
token?: string;
}>({
@@ -64,6 +65,7 @@ export function createGithubRepoPushAction(options: {
repoUrl: inputProps.repoUrl,
requireCodeOwnerReviews: inputProps.requireCodeOwnerReviews,
requiredStatusCheckContexts: inputProps.requiredStatusCheckContexts,
requireBranchesToBeUpToDate: inputProps.requireBranchesToBeUpToDate,
defaultBranch: inputProps.defaultBranch,
protectDefaultBranch: inputProps.protectDefaultBranch,
protectEnforceAdmins: inputProps.protectEnforceAdmins,
@@ -93,6 +95,7 @@ export function createGithubRepoPushAction(options: {
gitAuthorEmail,
requireCodeOwnerReviews = false,
requiredStatusCheckContexts = [],
requireBranchesToBeUpToDate = true,
token: providedToken,
} = ctx.input;
@@ -129,6 +132,7 @@ export function createGithubRepoPushAction(options: {
repo,
requireCodeOwnerReviews,
requiredStatusCheckContexts,
requireBranchesToBeUpToDate,
config,
ctx.logger,
gitCommitMessage,
@@ -248,6 +248,7 @@ export async function initRepoPushAndProtect(
repo: string,
requireCodeOwnerReviews: boolean,
requiredStatusCheckContexts: string[],
requireBranchesToBeUpToDate: boolean,
config: Config,
logger: any,
gitCommitMessage?: string,
@@ -290,6 +291,7 @@ export async function initRepoPushAndProtect(
defaultBranch,
requireCodeOwnerReviews,
requiredStatusCheckContexts,
requireBranchesToBeUpToDate,
enforceAdmins: protectEnforceAdmins,
});
} catch (e) {
@@ -47,6 +47,11 @@ const requiredStatusCheckContexts = {
type: 'string',
},
};
const requireBranchesToBeUpToDate = {
title: 'Require Branches To Be Up To Date?',
description: `Require branches to be up to date before merging. The default value is 'true'`,
type: 'boolean',
};
const repoVisibility = {
title: 'Repository Visibility',
type: 'string',
@@ -173,6 +178,7 @@ export { repoUrl };
export { repoVisibility };
export { requireCodeOwnerReviews };
export { requiredStatusCheckContexts };
export { requireBranchesToBeUpToDate };
export { sourcePath };
export { token };
export { topics };
@@ -185,6 +185,7 @@ type BranchProtectionOptions = {
logger: Logger;
requireCodeOwnerReviews: boolean;
requiredStatusCheckContexts?: string[];
requireBranchesToBeUpToDate?: boolean;
defaultBranch?: string;
enforceAdmins?: boolean;
};
@@ -196,6 +197,7 @@ export const enableBranchProtectionOnDefaultRepoBranch = async ({
logger,
requireCodeOwnerReviews,
requiredStatusCheckContexts = [],
requireBranchesToBeUpToDate = true,
defaultBranch = 'master',
enforceAdmins = true,
}: BranchProtectionOptions): Promise<void> => {
@@ -216,7 +218,7 @@ export const enableBranchProtectionOnDefaultRepoBranch = async ({
repo: repoName,
branch: defaultBranch,
required_status_checks: {
strict: true,
strict: requireBranchesToBeUpToDate,
contexts: requiredStatusCheckContexts,
},
restrictions: null,
@@ -669,6 +669,7 @@ describe('publish:github', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
@@ -688,6 +689,7 @@ describe('publish:github', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: true,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
@@ -707,6 +709,7 @@ describe('publish:github', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
});
@@ -732,6 +735,7 @@ describe('publish:github', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
@@ -751,6 +755,7 @@ describe('publish:github', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: false,
});
@@ -770,11 +775,12 @@ describe('publish:github', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
});
it('should call enableBranchProtectionOnDefaultRepoBranch with the correct values of requiredStatusCheckContexts', async () => {
it('should call enableBranchProtectionOnDefaultRepoBranch with the correct values of requiredStatusCheckContexts and requireBranchesToBeUpToDate', async () => {
mockOctokit.rest.users.getByUsername.mockResolvedValue({
data: { type: 'User' },
});
@@ -795,6 +801,7 @@ describe('publish:github', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
@@ -803,6 +810,7 @@ describe('publish:github', () => {
input: {
...mockContext.input,
requiredStatusCheckContexts: ['statusCheck'],
requireBranchesToBeUpToDate: true,
},
});
@@ -814,6 +822,28 @@ describe('publish:github', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: ['statusCheck'],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
await action.handler({
...mockContext,
input: {
...mockContext.input,
requiredStatusCheckContexts: ['statusCheck'],
requireBranchesToBeUpToDate: false,
},
});
expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({
owner: 'owner',
client: mockOctokit,
repoName: 'repo',
logger: mockContext.logger,
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: ['statusCheck'],
requireBranchesToBeUpToDate: false,
enforceAdmins: true,
});
@@ -833,6 +863,7 @@ describe('publish:github', () => {
defaultBranch: 'master',
requireCodeOwnerReviews: false,
requiredStatusCheckContexts: [],
requireBranchesToBeUpToDate: true,
enforceAdmins: true,
});
});
@@ -61,6 +61,7 @@ export function createPublishGithubAction(options: {
sourcePath?: string;
requireCodeOwnerReviews?: boolean;
requiredStatusCheckContexts?: string[];
requireBranchesToBeUpToDate?: boolean;
repoVisibility?: 'private' | 'internal' | 'public';
collaborators?: Array<
| {
@@ -94,6 +95,7 @@ export function createPublishGithubAction(options: {
access: inputProps.access,
requireCodeOwnerReviews: inputProps.requireCodeOwnerReviews,
requiredStatusCheckContexts: inputProps.requiredStatusCheckContexts,
requireBranchesToBeUpToDate: inputProps.requireBranchesToBeUpToDate,
repoVisibility: inputProps.repoVisibility,
defaultBranch: inputProps.defaultBranch,
protectDefaultBranch: inputProps.protectDefaultBranch,
@@ -128,6 +130,7 @@ export function createPublishGithubAction(options: {
access,
requireCodeOwnerReviews = false,
requiredStatusCheckContexts = [],
requireBranchesToBeUpToDate = true,
repoVisibility = 'private',
defaultBranch = 'master',
protectDefaultBranch = true,
@@ -193,6 +196,7 @@ export function createPublishGithubAction(options: {
repo,
requireCodeOwnerReviews,
requiredStatusCheckContexts,
requireBranchesToBeUpToDate,
config,
ctx.logger,
gitCommitMessage,