diff --git a/.changeset/proud-jars-look.md b/.changeset/proud-jars-look.md new file mode 100644 index 0000000000..2bae44db66 --- /dev/null +++ b/.changeset/proud-jars-look.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Add a `topics` input to `publish:github` action that can be used to set topics on the repository upon creation. 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 e0e9efa479..1f27745c0c 100644 --- a/plugins/scaffolder-backend/src/scaffolder/__mocks__/@octokit/rest/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/__mocks__/@octokit/rest/index.ts @@ -19,6 +19,7 @@ export const mockGithubClient = { createInOrg: jest.fn(), createForAuthenticatedUser: jest.fn(), addCollaborator: jest.fn(), + replaceAllTopics: jest.fn(), }, users: { getByUsername: jest.fn(), 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 dc2f602778..3dae202643 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 @@ -341,6 +341,72 @@ describe('publish:github', () => { ]); }); + it('should add topics 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', + }, + }); + + mockGithubClient.repos.replaceAllTopics.mockResolvedValue({ + data: { + names: ['node.js'], + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + topics: ['node.js'], + }, + }); + + expect(mockGithubClient.repos.replaceAllTopics).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + names: ['node.js'], + }); + }); + + it('should lowercase topics 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', + }, + }); + + mockGithubClient.repos.replaceAllTopics.mockResolvedValue({ + data: { + names: ['backstage'], + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + topics: ['BACKSTAGE'], + }, + }); + + expect(mockGithubClient.repos.replaceAllTopics).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + names: ['backstage'], + }); + }); + 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 1938c3bf13..adf08b53c4 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -48,6 +48,7 @@ export function createPublishGithubAction(options: { sourcePath?: string; repoVisibility: 'private' | 'internal' | 'public'; collaborators: Collaborator[]; + topics?: string[]; }>({ id: 'publish:github', description: @@ -101,6 +102,13 @@ export function createPublishGithubAction(options: { }, }, }, + topics: { + title: 'Topics', + type: 'array', + items: { + type: 'string', + }, + }, }, }, output: { @@ -124,6 +132,7 @@ export function createPublishGithubAction(options: { access, repoVisibility = 'private', collaborators, + topics, } = ctx.input; const { owner, repo, host } = parseRepoUrl(repoUrl); @@ -217,6 +226,18 @@ export function createPublishGithubAction(options: { } } + if (topics) { + try { + await client.repos.replaceAllTopics({ + owner, + repo, + names: topics.map(t => t.toLowerCase()), + }); + } catch (e) { + ctx.logger.warn(`Skipping topics ${topics.join(' ')}, ${e.message}`); + } + } + const remoteUrl = newRepo.clone_url; const repoContentsUrl = `${newRepo.html_url}/blob/master`;