From 7df6179b7f4f17343ce52469a6dbd9f1ad52db13 Mon Sep 17 00:00:00 2001 From: Paul Mowat Date: Fri, 13 Dec 2024 16:12:35 +0000 Subject: [PATCH] feat: adding requiredLinearHistory property Signed-off-by: Paul Mowat --- .changeset/chatty-files-sniff.md | 5 +++ .../report.api.md | 4 ++ .../src/actions/gitHelpers.ts | 3 ++ .../src/actions/github.test.ts | 24 +++++++++++ .../src/actions/github.ts | 4 ++ .../githubBranchProtection.examples.test.ts | 40 +++++++++++++++++++ .../githubBranchProtection.examples.ts | 21 ++++++++++ .../actions/githubBranchProtection.test.ts | 4 ++ .../src/actions/githubBranchProtection.ts | 4 ++ .../src/actions/githubRepoCreate.ts | 2 + .../src/actions/githubRepoPush.test.ts | 12 ++++++ .../src/actions/githubRepoPush.ts | 4 ++ .../src/actions/helpers.ts | 2 + .../src/actions/inputProperties.ts | 7 ++++ 14 files changed, 136 insertions(+) create mode 100644 .changeset/chatty-files-sniff.md diff --git a/.changeset/chatty-files-sniff.md b/.changeset/chatty-files-sniff.md new file mode 100644 index 0000000000..11eebbb93b --- /dev/null +++ b/.changeset/chatty-files-sniff.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-github': patch +--- + +adding requiredLinearHistory property for branch protection settings diff --git a/plugins/scaffolder-backend-module-github/report.api.md b/plugins/scaffolder-backend-module-github/report.api.md index 547c61768f..7acde180b1 100644 --- a/plugins/scaffolder-backend-module-github/report.api.md +++ b/plugins/scaffolder-backend-module-github/report.api.md @@ -79,6 +79,7 @@ export function createGithubBranchProtectionAction(options: { requiredConversationResolution?: boolean | undefined; requireLastPushApproval?: boolean | undefined; requiredCommitSigning?: boolean | undefined; + requiredLinearHistory?: boolean | undefined; token?: string | undefined; }, JsonObject @@ -270,6 +271,7 @@ export function createGithubRepoCreateAction(options: { } | undefined; requireCommitSigning?: boolean | undefined; + requiredLinearHistory?: boolean | undefined; customProperties?: | { [key: string]: string; @@ -317,6 +319,7 @@ export function createGithubRepoPushAction(options: { sourcePath?: string | undefined; token?: string | undefined; requiredCommitSigning?: boolean | undefined; + requiredLinearHistory?: boolean | undefined; requireLastPushApproval?: boolean | undefined; }, JsonObject @@ -430,6 +433,7 @@ export function createPublishGithubAction(options: { } | undefined; requiredCommitSigning?: boolean | undefined; + requiredLinearHistory?: boolean | undefined; customProperties?: | { [key: string]: string; diff --git a/plugins/scaffolder-backend-module-github/src/actions/gitHelpers.ts b/plugins/scaffolder-backend-module-github/src/actions/gitHelpers.ts index 9989a80724..c720690ee6 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/gitHelpers.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/gitHelpers.ts @@ -43,6 +43,7 @@ type BranchProtectionOptions = { enforceAdmins?: boolean; dismissStaleReviews?: boolean; requiredCommitSigning?: boolean; + requiredLinearHistory?: boolean; }; export const enableBranchProtectionOnDefaultRepoBranch = async ({ @@ -62,6 +63,7 @@ export const enableBranchProtectionOnDefaultRepoBranch = async ({ enforceAdmins = true, dismissStaleReviews = false, requiredCommitSigning = false, + requiredLinearHistory = false, }: BranchProtectionOptions): Promise => { const tryOnce = async () => { try { @@ -93,6 +95,7 @@ export const enableBranchProtectionOnDefaultRepoBranch = async ({ require_last_push_approval: requireLastPushApproval, }, required_conversation_resolution: requiredConversationResolution, + required_linear_history: requiredLinearHistory, }); if (requiredCommitSigning) { diff --git a/plugins/scaffolder-backend-module-github/src/actions/github.test.ts b/plugins/scaffolder-backend-module-github/src/actions/github.test.ts index 3af922eb4f..f2f36e263a 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/github.test.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/github.test.ts @@ -1097,6 +1097,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); await action.handler({ @@ -1126,6 +1127,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); await action.handler({ @@ -1154,6 +1156,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); await action.handler({ @@ -1181,6 +1184,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); }); @@ -1269,6 +1273,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); await action.handler({ @@ -1302,6 +1307,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); await action.handler({ @@ -1335,6 +1341,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); await action.handler({ @@ -1370,6 +1377,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); await action.handler({ @@ -1405,6 +1413,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); await action.handler({ @@ -1440,6 +1449,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); }); it('should call enableBranchProtectionOnDefaultRepoBranch with the correct values of bypassPullRequestAllowances', async () => { @@ -1472,6 +1482,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); await action.handler({ @@ -1503,6 +1514,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); await action.handler({ @@ -1534,6 +1546,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); await action.handler({ @@ -1565,6 +1578,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); await action.handler({ @@ -1600,6 +1614,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); await action.handler({ @@ -1635,6 +1650,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, }); }); @@ -1664,6 +1680,11 @@ describe('publish:github', () => { defaultValue: false, overrideValue: true, }, + { + inputProperty: 'requiredLinearHistory', + defaultValue: false, + overrideValue: true, + }, { inputProperty: 'protectEnforceAdmins', defaultValue: true, @@ -1712,6 +1733,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, [octokitParameter || inputProperty]: defaultValue, }); @@ -1740,6 +1762,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, [octokitParameter || inputProperty]: overrideValue, }); @@ -1768,6 +1791,7 @@ describe('publish:github', () => { enforceAdmins: true, dismissStaleReviews: false, requiredCommitSigning: false, + requiredLinearHistory: false, [octokitParameter || inputProperty]: defaultValue, }); }, diff --git a/plugins/scaffolder-backend-module-github/src/actions/github.ts b/plugins/scaffolder-backend-module-github/src/actions/github.ts index 982c68d80f..6d8e0046c2 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/github.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/github.ts @@ -115,6 +115,7 @@ export function createPublishGithubAction(options: { includeClaimKeys?: string[]; }; requiredCommitSigning?: boolean; + requiredLinearHistory?: boolean; customProperties?: { [key: string]: string }; }>({ id: 'publish:github', @@ -165,6 +166,7 @@ export function createPublishGithubAction(options: { secrets: inputProps.secrets, oidcCustomization: inputProps.oidcCustomization, requiredCommitSigning: inputProps.requiredCommitSigning, + requiredLinearHistory: inputProps.requiredLinearHistory, customProperties: inputProps.customProperties, }, }, @@ -217,6 +219,7 @@ export function createPublishGithubAction(options: { token: providedToken, customProperties, requiredCommitSigning = false, + requiredLinearHistory = false, } = ctx.input; const octokitOptions = await getOctokitOptions({ @@ -289,6 +292,7 @@ export function createPublishGithubAction(options: { gitAuthorEmail, dismissStaleReviews, requiredCommitSigning, + requiredLinearHistory, ); ctx.output('commitHash', commitResult?.commitHash); diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.examples.test.ts b/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.examples.test.ts index 5443d7304c..14ebc9a1cd 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.examples.test.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.examples.test.ts @@ -98,6 +98,7 @@ describe('github:branch-protection:create', () => { require_last_push_approval: false, }, required_conversation_resolution: false, + required_linear_history: false, }); expect( mockOctokit.rest.repos.createCommitSignatureProtection, @@ -130,6 +131,7 @@ describe('github:branch-protection:create', () => { require_last_push_approval: false, }, required_conversation_resolution: false, + required_linear_history: false, }); expect( mockOctokit.rest.repos.createCommitSignatureProtection, @@ -162,6 +164,44 @@ describe('github:branch-protection:create', () => { require_last_push_approval: true, }, required_conversation_resolution: true, + required_linear_history: false, + }); + expect( + mockOctokit.rest.repos.createCommitSignatureProtection, + ).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + branch: 'master', + }); + }); + + it('should create branch protection with params and require linear history', async () => { + const input = yaml.parse(examples[3].example).steps[0].input; + const ctx = Object.assign({}, mockContext, { input }); + await action.handler(ctx); + + expect(mockOctokit.rest.repos.updateBranchProtection).toHaveBeenCalledWith({ + mediaType: { + previews: ['luke-cage-preview'], + }, + owner: 'owner', + repo: 'repo', + branch: 'master', + required_status_checks: { + strict: true, + contexts: ['test'], + }, + restrictions: null, + enforce_admins: true, + required_pull_request_reviews: { + required_approving_review_count: 1, + require_code_owner_reviews: true, + bypass_pull_request_allowances: undefined, + dismiss_stale_reviews: true, + require_last_push_approval: true, + }, + required_conversation_resolution: true, + required_linear_history: true, }); expect( mockOctokit.rest.repos.createCommitSignatureProtection, diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.examples.ts b/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.examples.ts index 4d0eb253ef..12761a2b70 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.examples.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.examples.ts @@ -67,4 +67,25 @@ export const examples: TemplateExample[] = [ ], }), }, + { + description: `GitHub Branch Protection and required linear history on default branch.`, + example: yaml.stringify({ + steps: [ + { + action: 'github:branch-protection:create', + name: 'Setup Branch Protection', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + requireCodeOwnerReviews: true, + requiredStatusCheckContexts: ['test'], + dismissStaleReviews: true, + requireLastPushApproval: true, + requiredConversationResolution: true, + requiredCommitSigning: true, + requiredLinearHistory: true, + }, + }, + ], + }), + }, ]; diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.test.ts b/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.test.ts index d4eac30b81..8e672af725 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.test.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.test.ts @@ -96,6 +96,7 @@ describe('github:branch-protection:create', () => { require_last_push_approval: false, }, required_conversation_resolution: false, + required_linear_history: false, }); expect( mockOctokit.rest.repos.createCommitSignatureProtection, @@ -132,6 +133,7 @@ describe('github:branch-protection:create', () => { require_last_push_approval: false, }, required_conversation_resolution: false, + required_linear_history: false, }); expect( mockOctokit.rest.repos.createCommitSignatureProtection, @@ -167,6 +169,7 @@ describe('github:branch-protection:create', () => { requiredConversationResolution: true, requireLastPushApproval: true, requiredCommitSigning: true, + requiredLinearHistory: true, }, }); @@ -199,6 +202,7 @@ describe('github:branch-protection:create', () => { require_last_push_approval: true, }, required_conversation_resolution: true, + required_linear_history: true, }); expect( mockOctokit.rest.repos.createCommitSignatureProtection, diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.ts b/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.ts index 4ab847c98e..a7555d3af1 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubBranchProtection.ts @@ -62,6 +62,7 @@ export function createGithubBranchProtectionAction(options: { requiredConversationResolution?: boolean; requireLastPushApproval?: boolean; requiredCommitSigning?: boolean; + requiredLinearHistory?: boolean; token?: string; }>({ id: 'github:branch-protection:create', @@ -90,6 +91,7 @@ export function createGithubBranchProtectionAction(options: { inputProps.requiredConversationResolution, requireLastPushApproval: inputProps.requireLastPushApproval, requiredCommitSigning: inputProps.requiredCommitSigning, + requiredLinearHistory: inputProps.requiredLinearHistory, token: inputProps.token, }, }, @@ -109,6 +111,7 @@ export function createGithubBranchProtectionAction(options: { requiredConversationResolution = false, requireLastPushApproval = false, requiredCommitSigning = false, + requiredLinearHistory = false, token: providedToken, } = ctx.input; @@ -147,6 +150,7 @@ export function createGithubBranchProtectionAction(options: { enforceAdmins, dismissStaleReviews, requiredCommitSigning, + requiredLinearHistory, }); }, }); diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.ts b/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.ts index 1c3d035ee3..61dea32c35 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.ts @@ -100,6 +100,7 @@ export function createGithubRepoCreateAction(options: { includeClaimKeys?: string[]; }; requireCommitSigning?: boolean; + requiredLinearHistory?: boolean; customProperties?: { [key: string]: string }; }>({ id: 'github:repo:create', @@ -140,6 +141,7 @@ export function createGithubRepoCreateAction(options: { secrets: inputProps.secrets, oidcCustomization: inputProps.oidcCustomization, requiredCommitSigning: inputProps.requiredCommitSigning, + requiredLinearHistory: inputProps.requiredLinearHistory, customProperties: inputProps.customProperties, }, }, diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubRepoPush.test.ts b/plugins/scaffolder-backend-module-github/src/actions/githubRepoPush.test.ts index d0ee4bfa12..9ff8cd123c 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubRepoPush.test.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubRepoPush.test.ts @@ -331,6 +331,7 @@ describe('github:repo:push', () => { bypassPullRequestAllowances: undefined, requiredApprovingReviewCount: 1, requiredCommitSigning: false, + requiredLinearHistory: false, restrictions: undefined, }); @@ -359,6 +360,7 @@ describe('github:repo:push', () => { bypassPullRequestAllowances: undefined, requiredApprovingReviewCount: 1, requiredCommitSigning: false, + requiredLinearHistory: false, restrictions: undefined, }); @@ -387,6 +389,7 @@ describe('github:repo:push', () => { bypassPullRequestAllowances: undefined, requiredApprovingReviewCount: 1, requiredCommitSigning: false, + requiredLinearHistory: false, restrictions: undefined, }); @@ -415,6 +418,7 @@ describe('github:repo:push', () => { bypassPullRequestAllowances: undefined, requiredApprovingReviewCount: 1, requiredCommitSigning: false, + requiredLinearHistory: false, restrictions: undefined, }); }); @@ -464,6 +468,11 @@ describe('github:repo:push', () => { defaultValue: false, overrideValue: true, }, + { + inputProperty: 'requiredLinearHistory', + defaultValue: false, + overrideValue: true, + }, { inputProperty: 'protectEnforceAdmins', defaultValue: true, @@ -508,6 +517,7 @@ describe('github:repo:push', () => { bypassPullRequestAllowances: undefined, requiredApprovingReviewCount: 1, requiredCommitSigning: false, + requiredLinearHistory: false, restrictions: undefined, [octokitParameter || inputProperty]: defaultValue, }); @@ -536,6 +546,7 @@ describe('github:repo:push', () => { bypassPullRequestAllowances: undefined, requiredApprovingReviewCount: 1, requiredCommitSigning: false, + requiredLinearHistory: false, restrictions: undefined, [octokitParameter || inputProperty]: overrideValue, }); @@ -564,6 +575,7 @@ describe('github:repo:push', () => { bypassPullRequestAllowances: undefined, requiredApprovingReviewCount: 1, requiredCommitSigning: false, + requiredLinearHistory: false, restrictions: undefined, [octokitParameter || inputProperty]: defaultValue, }); diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubRepoPush.ts b/plugins/scaffolder-backend-module-github/src/actions/githubRepoPush.ts index a4fa7673c6..62624057c8 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubRepoPush.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubRepoPush.ts @@ -75,6 +75,7 @@ export function createGithubRepoPushAction(options: { sourcePath?: string; token?: string; requiredCommitSigning?: boolean; + requiredLinearHistory?: boolean; requireLastPushApproval?: boolean; }>({ id: 'github:repo:push', @@ -106,6 +107,7 @@ export function createGithubRepoPushAction(options: { sourcePath: inputProps.sourcePath, token: inputProps.token, requiredCommitSigning: inputProps.requiredCommitSigning, + requiredLinearHistory: inputProps.requiredLinearHistory, }, }, output: { @@ -137,6 +139,7 @@ export function createGithubRepoPushAction(options: { requireLastPushApproval = false, token: providedToken, requiredCommitSigning = false, + requiredLinearHistory = false, } = ctx.input; const { owner, repo } = parseRepoUrl(repoUrl, integrations); @@ -185,6 +188,7 @@ export function createGithubRepoPushAction(options: { gitAuthorEmail, dismissStaleReviews, requiredCommitSigning, + requiredLinearHistory, ); ctx.output('remoteUrl', remoteUrl); diff --git a/plugins/scaffolder-backend-module-github/src/actions/helpers.ts b/plugins/scaffolder-backend-module-github/src/actions/helpers.ts index b28c35105c..9bba0ca359 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/helpers.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/helpers.ts @@ -371,6 +371,7 @@ export async function initRepoPushAndProtect( gitAuthorEmail?: string, dismissStaleReviews?: boolean, requiredCommitSigning?: boolean, + requiredLinearHistory?: boolean, ): Promise<{ commitHash: string }> { const gitAuthorInfo = { name: gitAuthorName @@ -416,6 +417,7 @@ export async function initRepoPushAndProtect( enforceAdmins: protectEnforceAdmins, dismissStaleReviews: dismissStaleReviews, requiredCommitSigning: requiredCommitSigning, + requiredLinearHistory: requiredLinearHistory, }); } catch (e) { assertError(e); diff --git a/plugins/scaffolder-backend-module-github/src/actions/inputProperties.ts b/plugins/scaffolder-backend-module-github/src/actions/inputProperties.ts index 4a52c1d69f..2c86cf492e 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/inputProperties.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/inputProperties.ts @@ -270,6 +270,12 @@ const requiredCommitSigning = { description: `Require commit signing so that you must sign commits on this branch.`, }; +const requiredLinearHistory = { + title: 'Require linear history', + type: 'boolean', + description: `Prevent merge commits from being pushed to matching branches.`, +}; + const repoVariables = { title: 'Repository Variables', description: `Variables attached to the repository`, @@ -346,6 +352,7 @@ export { sourcePath }; export { token }; export { topics }; export { requiredCommitSigning }; +export { requiredLinearHistory }; export { repoVariables }; export { secrets }; export { oidcCustomization };