From 1d32be309be8807ee790d462e84e817872a1fa7c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 1 Apr 2021 18:56:06 +0100 Subject: [PATCH 1/6] allow collaborators on github creation Signed-off-by: Andrew Johnson --- .../create-react-app/template.yaml | 27 ++++++++- .../actions/builtin/publish/github.test.ts | 56 +++++++++++++++++++ .../actions/builtin/publish/github.ts | 32 +++++++++++ 3 files changed, 114 insertions(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml b/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml index 91f887ed0a..82fde335c5 100644 --- a/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml +++ b/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml @@ -19,6 +19,7 @@ spec: - component_id - use_typescript - description + - collaborators properties: component_id: title: Name @@ -39,4 +40,28 @@ spec: type: boolean description: Use Github Actions default: true - + collaborators: + title: Collaborators + description: Provide users with permissions + type: array + ui:options: + orderable: false + items: + type: object + required: + - username + - access + properties: + access: + type: string + description: The type of access for the user + default: pull + enum: + - push + - pull + - admin + - maintain + - triage + username: + type: string + description: The username or group 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 9880dda621..54282f76cd 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 @@ -232,6 +232,62 @@ describe('publish:github', () => { }); }); + it('should add multiple collaborators when provided', async () => { + mockGithubClient.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockGithubClient.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + collaborators: [ + { + access: 'pull', + username: 'robot-1', + }, + { + access: 'push', + username: 'robot-2', + }, + ], + }, + }); + + const commonProperties = { + org: 'owner', + owner: 'owner', + repo: 'repo', + }; + + expect( + mockGithubClient.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[1], + ).toEqual([ + { + ...commonProperties, + team_slug: 'robot-1', + permission: 'pull', + }, + ]); + + expect( + mockGithubClient.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[2], + ).toEqual([ + { + ...commonProperties, + team_slug: 'robot-2', + permission: 'push', + }, + ]); + }); + it('should call output with the remoteUrl and the repoContentsUrl', async () => { mockGithubClient.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, 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 70394b0039..8b09c644de 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -23,6 +23,9 @@ import { initRepoAndPush } from '../../../stages/publish/helpers'; import { getRepoSourceDirectory, parseRepoUrl } from './util'; import { createTemplateAction } from '../../createTemplateAction'; +type Permission = 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; +type Collaborator = { access: Permission; username: string }; + export function createPublishGithubAction(options: { integrations: ScmIntegrationRegistry; }) { @@ -41,6 +44,7 @@ export function createPublishGithubAction(options: { access?: string; sourcePath?: string; repoVisibility: 'private' | 'internal' | 'public'; + collaborators: Collaborator[]; }>({ id: 'publish:github', description: @@ -72,6 +76,21 @@ export function createPublishGithubAction(options: { 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the respository.', type: 'string', }, + collaborators: { + title: 'Collaborators', + type: 'array', + properties: { + access: { + title: 'The type of access for the user', + type: 'string', + enum: ['push', 'pull', 'admin', 'maintain', 'triage'], + }, + username: { + title: 'The username or group', + type: 'string', + }, + }, + }, }, }, output: { @@ -94,6 +113,7 @@ export function createPublishGithubAction(options: { description, access, repoVisibility = 'private', + collaborators, } = ctx.input; const { owner, repo, host } = parseRepoUrl(repoUrl); @@ -165,6 +185,18 @@ export function createPublishGithubAction(options: { }); } + if (collaborators) { + for (const { access, username } of collaborators) { + await client.teams.addOrUpdateRepoPermissionsInOrg({ + org: owner, + team_slug: username, + owner, + repo, + permission: access, + }); + } + } + const remoteUrl = data.clone_url; const repoContentsUrl = `${data.html_url}/blob/master`; From a376e3ee85d5c2584489ee98061358bfa196cf07 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 1 Apr 2021 19:06:09 +0100 Subject: [PATCH 2/6] changeset Signed-off-by: Andrew Johnson --- .changeset/tasty-apples-sell.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tasty-apples-sell.md diff --git a/.changeset/tasty-apples-sell.md b/.changeset/tasty-apples-sell.md new file mode 100644 index 0000000000..d24cd61e37 --- /dev/null +++ b/.changeset/tasty-apples-sell.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Adds a collaborator field to GitHub publish action for multiple users and access levels From 18a9669291f720216b078dfb47c97e915c6bad70 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 1 Apr 2021 19:09:36 +0100 Subject: [PATCH 3/6] make lint happy Signed-off-by: Andrew Johnson --- .../src/scaffolder/actions/builtin/publish/github.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 8b09c644de..225730bbad 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -186,13 +186,13 @@ export function createPublishGithubAction(options: { } if (collaborators) { - for (const { access, username } of collaborators) { + for (const { access: permission, username: team_slug } of collaborators) { await client.teams.addOrUpdateRepoPermissionsInOrg({ org: owner, - team_slug: username, + team_slug, owner, repo, - permission: access, + permission, }); } } From 97c5797f4ea55dc9edf6a2841924d05e3bedda0d Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 1 Apr 2021 19:17:13 +0100 Subject: [PATCH 4/6] prettier Signed-off-by: Andrew Johnson --- .../src/scaffolder/actions/builtin/publish/github.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 225730bbad..21481ec1dd 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -186,7 +186,10 @@ export function createPublishGithubAction(options: { } if (collaborators) { - for (const { access: permission, username: team_slug } of collaborators) { + for (const { + access: permission, + username: team_slug, + } of collaborators) { await client.teams.addOrUpdateRepoPermissionsInOrg({ org: owner, team_slug, From f7d71b6d9e47f3152d5dd739f521f4c3d4f3e208 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 6 Apr 2021 14:37:22 +0100 Subject: [PATCH 5/6] fixup Signed-off-by: Andrew Johnson --- .../actions/builtin/publish/github.ts | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) 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 21481ec1dd..eb2625ee04 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -78,16 +78,21 @@ export function createPublishGithubAction(options: { }, collaborators: { title: 'Collaborators', + description: 'Provide users with permissions', type: 'array', - properties: { - access: { - title: 'The type of access for the user', - type: 'string', - enum: ['push', 'pull', 'admin', 'maintain', 'triage'], - }, - username: { - title: 'The username or group', - type: 'string', + items: { + type: 'object', + required: ['username', 'access'], + properties: { + access: { + type: 'string', + description: 'The type of access for the user', + enum: ['push', 'pull', 'admin', 'maintain', 'triage'], + }, + username: { + type: 'string', + description: 'The username or group', + }, }, }, }, From 67b9602d14ebd1d57eb4aaa949656f6ccad6b96d Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 8 Apr 2021 15:45:37 +0100 Subject: [PATCH 6/6] don't throw on permission errors Signed-off-by: Andrew Johnson --- plugins/scaffolder-backend/package.json | 1 + .../actions/builtin/publish/github.test.ts | 53 +++++++++++++++++++ .../actions/builtin/publish/github.ts | 20 ++++--- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 69aced77b5..81ea9452ce 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -55,6 +55,7 @@ "handlebars": "^4.7.6", "helmet": "^4.0.0", "isomorphic-git": "^1.8.0", + "jest-when": "^3.1.0", "jsonschema": "^1.2.6", "knex": "^0.95.1", "luxon": "^1.26.0", 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 54282f76cd..dc2f602778 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,6 +22,7 @@ import { ConfigReader } from '@backstage/config'; import { getVoidLogger } from '@backstage/backend-common'; import { PassThrough } from 'stream'; import { initRepoAndPush } from '../../../stages/publish/helpers'; +import { when } from 'jest-when'; describe('publish:github', () => { const integrations = ScmIntegrations.fromConfig( @@ -288,6 +289,58 @@ describe('publish:github', () => { ]); }); + it('should ignore failures when adding multiple collaborators', async () => { + mockGithubClient.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockGithubClient.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + when(mockGithubClient.teams.addOrUpdateRepoPermissionsInOrg) + .calledWith({ + org: 'owner', + owner: 'owner', + repo: 'repo', + team_slug: 'robot-1', + permission: 'pull', + }) + .mockRejectedValueOnce(new Error('Something bad happened') as never); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + collaborators: [ + { + access: 'pull', + username: 'robot-1', + }, + { + access: 'push', + username: 'robot-2', + }, + ], + }, + }); + + expect( + mockGithubClient.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[2], + ).toEqual([ + { + org: 'owner', + owner: 'owner', + repo: 'repo', + team_slug: 'robot-2', + permission: 'push', + }, + ]); + }); + it('should call output with the remoteUrl and the repoContentsUrl', async () => { mockGithubClient.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, 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 eb2625ee04..e58f2c68aa 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -195,13 +195,19 @@ export function createPublishGithubAction(options: { access: permission, username: team_slug, } of collaborators) { - await client.teams.addOrUpdateRepoPermissionsInOrg({ - org: owner, - team_slug, - owner, - repo, - permission, - }); + try { + await client.teams.addOrUpdateRepoPermissionsInOrg({ + org: owner, + team_slug, + owner, + repo, + permission, + }); + } catch (e) { + ctx.logger.warn( + `Skipping ${permission} access for ${team_slug}, ${e.message}`, + ); + } } }