From d622cfad17cda9e11e2ea173ecc249c89524ac91 Mon Sep 17 00:00:00 2001 From: "@pawelmitka" Date: Wed, 18 Aug 2021 17:02:30 +0200 Subject: [PATCH] feat(plugin-scaffolder-backend): provide option to require Code Owners in GitHub In order to make creation of a new repository more powerful option to enable 'Require review from Code Owners' has been added. The default behavior is that this option is disabled as with the current behavior. However, adding `requireCodeOwnersReview: true` in context input enables it. Signed-off-by: @pawelmitka --- .changeset/three-eggs-punch.md | 5 ++ .../src/scaffolder/actions/builtin/helpers.ts | 7 ++- .../actions/builtin/publish/github.test.ts | 62 ++++++++++++++++++- .../actions/builtin/publish/github.ts | 8 +++ 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 .changeset/three-eggs-punch.md diff --git a/.changeset/three-eggs-punch.md b/.changeset/three-eggs-punch.md new file mode 100644 index 0000000000..28dbd2439d --- /dev/null +++ b/.changeset/three-eggs-punch.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +GitHub branch protection option 'Require review from Code Owners' can be enabled by adding `requireCodeOwnersReview: true` in context input. diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts index 7a10046fd0..c1ad3adf3e 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts @@ -124,6 +124,7 @@ type BranchProtectionOptions = { owner: string; repoName: string; logger: Logger; + requireCodeOwnerReviews: boolean; defaultBranch?: string; }; @@ -132,6 +133,7 @@ export const enableBranchProtectionOnDefaultRepoBranch = async ({ client, owner, logger, + requireCodeOwnerReviews, defaultBranch = 'master', }: BranchProtectionOptions): Promise => { const tryOnce = async () => { @@ -153,7 +155,10 @@ export const enableBranchProtectionOnDefaultRepoBranch = async ({ required_status_checks: { strict: true, contexts: [] }, restrictions: null, enforce_admins: true, - required_pull_request_reviews: { required_approving_review_count: 1 }, + required_pull_request_reviews: { + required_approving_review_count: 1, + require_code_owner_reviews: requireCodeOwnerReviews, + }, }); } catch (e) { if ( diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts index 5e948b52b1..4461d227e8 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts @@ -22,7 +22,10 @@ import { ScmIntegrations } from '@backstage/integration'; import { ConfigReader } from '@backstage/config'; import { getVoidLogger } from '@backstage/backend-common'; import { PassThrough } from 'stream'; -import { initRepoAndPush } from '../helpers'; +import { + enableBranchProtectionOnDefaultRepoBranch, + initRepoAndPush, +} from '../helpers'; import { when } from 'jest-when'; describe('publish:github', () => { @@ -583,4 +586,61 @@ describe('publish:github', () => { 'https://github.com/html/url/blob/main', ); }); + + it('should call enableBranchProtectionOnDefaultRepoBranch with the correct values of requireCodeOwnerReviews', async () => { + mockGithubClient.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockGithubClient.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + name: 'repository', + }, + }); + + await action.handler(mockContext); + + expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ + owner: 'owner', + client: mockGithubClient, + repoName: 'repository', + logger: mockContext.logger, + defaultBranch: 'master', + requireCodeOwnerReviews: false, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + requireCodeOwnerReviews: true, + }, + }); + + expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ + owner: 'owner', + client: mockGithubClient, + repoName: 'repository', + logger: mockContext.logger, + defaultBranch: 'master', + requireCodeOwnerReviews: true, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + requireCodeOwnerReviews: false, + }, + }); + + expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ + owner: 'owner', + client: mockGithubClient, + repoName: 'repository', + logger: mockContext.logger, + defaultBranch: 'master', + requireCodeOwnerReviews: false, + }); + }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts index f767d6d38c..5a358822af 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -49,6 +49,7 @@ export function createPublishGithubAction(options: { access?: string; defaultBranch?: string; sourcePath?: string; + requireCodeOwnerReviews?: boolean; repoVisibility: 'private' | 'internal' | 'public'; collaborators: Collaborator[]; topics?: string[]; @@ -75,6 +76,11 @@ export function createPublishGithubAction(options: { description: `Sets an admin collaborator on the repository. Can either be a user reference different from 'owner' in 'repoUrl' or team reference, eg. 'org/team-name'`, type: 'string', }, + requireCodeOwnerReviews: { + title: + 'Require an approved review in PR including files with a designated Code Owner', + type: 'boolean', + }, repoVisibility: { title: 'Repository Visibility', type: 'string', @@ -138,6 +144,7 @@ export function createPublishGithubAction(options: { repoUrl, description, access, + requireCodeOwnerReviews = false, repoVisibility = 'private', defaultBranch = 'master', collaborators, @@ -283,6 +290,7 @@ export function createPublishGithubAction(options: { repoName: newRepo.name, logger: ctx.logger, defaultBranch, + requireCodeOwnerReviews, }); } catch (e) { ctx.logger.warn(