diff --git a/plugins/scaffolder-backend/src/scaffolder/__mocks__/@octokit/rest/index.ts b/plugins/scaffolder-backend/src/scaffolder/__mocks__/@octokit/rest/index.ts index a302bc5931..e0e9efa479 100644 --- a/plugins/scaffolder-backend/src/scaffolder/__mocks__/@octokit/rest/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/__mocks__/@octokit/rest/index.ts @@ -18,10 +18,14 @@ export const mockGithubClient = { repos: { createInOrg: jest.fn(), createForAuthenticatedUser: jest.fn(), + addCollaborator: jest.fn(), }, users: { getByUsername: jest.fn(), }, + teams: { + addOrUpdateRepoPermissionsInOrg: jest.fn(), + }, }; export class Octokit { diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts index 718c01438c..86effd0ea5 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.test.ts @@ -30,6 +30,7 @@ const { mockGithubClient } = require('@octokit/rest') as { mockGithubClient: { repos: jest.Mocked; users: jest.Mocked; + teams: jest.Mocked; }; }; @@ -76,6 +77,7 @@ describe('GitHub Publisher', () => { values: { storePath: 'blam/test', owner: 'bob', + access: 'blam/team', }, directory: '/tmp/test', }); @@ -84,6 +86,15 @@ describe('GitHub Publisher', () => { org: 'blam', name: 'test', }); + expect( + mockGithubClient.teams.addOrUpdateRepoPermissionsInOrg, + ).toHaveBeenCalledWith({ + org: 'blam', + team_slug: 'team', + owner: 'blam', + repo: 'test', + permission: 'admin', + }); }); it('should use octokit to create a repo in the authed user if the organisation property is not set', async () => { @@ -102,6 +113,7 @@ describe('GitHub Publisher', () => { values: { storePath: 'blam/test', owner: 'bob', + access: 'blam', }, directory: '/tmp/test', }); @@ -111,14 +123,49 @@ describe('GitHub Publisher', () => { ).toHaveBeenCalledWith({ name: 'test', }); + expect(mockGithubClient.repos.addCollaborator).not.toHaveBeenCalled(); + }); + }); + + it('should invite other user in the authed user', async () => { + mockGithubClient.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + clone_url: 'mockclone', + }, + } as OctokitResponse); + mockGithubClient.users.getByUsername.mockResolvedValue({ + data: { + type: 'User', + }, + } as OctokitResponse); + + await publisher.publish({ + values: { + storePath: 'blam/test', + owner: 'bob', + access: 'bob', + }, + directory: '/tmp/test', + }); + + expect( + mockGithubClient.repos.createForAuthenticatedUser, + ).toHaveBeenCalledWith({ + name: 'test', + }); + expect(mockGithubClient.repos.addCollaborator).toHaveBeenCalledWith({ + owner: 'blam', + repo: 'test', + username: 'bob', + permission: 'admin', }); }); describe('publish: createGitDirectory', () => { const values = { - isOrg: true, storePath: 'blam/test', owner: 'lols', + access: 'lols', }; const mockDir = '/tmp/test/dir'; diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.ts index f3306e3662..09cb720ebe 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/github.ts @@ -44,6 +44,7 @@ export class GithubPublisher implements PublisherBase { values: RequiredTemplateValues & Record, ) { const [owner, name] = values.storePath.split('/'); + const access = values.access as string; const user = await this.client.users.getByUsername({ username: owner }); @@ -54,6 +55,27 @@ export class GithubPublisher implements PublisherBase { const { data } = await repoCreationPromise; + if (access.match(new RegExp(`${owner}/.+`))) { + const [, team] = access.split('/'); + await this.client.teams.addOrUpdateRepoPermissionsInOrg({ + org: owner, + team_slug: team, + owner, + repo: name, + permission: 'admin', + }); + } else { + // no need to add access if it's the person who own's the personal account + if (access !== owner) { + await this.client.repos.addCollaborator({ + owner, + repo: name, + username: access, + permission: 'admin', + }); + } + } + return data?.clone_url; } diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx index 1ed0504894..60f74e4cbe 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx @@ -54,7 +54,7 @@ const useTemplate = ( const OWNER_REPO_SCHEMA = { $schema: 'http://json-schema.org/draft-07/schema#' as const, - required: ['storePath', 'owner'], + required: ['storePath', 'owner', 'access'], properties: { owner: { type: 'string' as const, @@ -67,6 +67,11 @@ const OWNER_REPO_SCHEMA = { title: 'Store path', description: 'GitHub store path in org/repo format', }, + access: { + type: 'string' as const, + title: 'Access', + description: 'Who should have access, in user or owner/team format', + }, }, };