diff --git a/.changeset/better-eggs-slide.md b/.changeset/better-eggs-slide.md new file mode 100644 index 0000000000..cf3856bb86 --- /dev/null +++ b/.changeset/better-eggs-slide.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +--- + +Allow setting optional description on group creation diff --git a/plugins/scaffolder-backend-module-gitlab/report.api.md b/plugins/scaffolder-backend-module-gitlab/report.api.md index 2ff099d469..311c871429 100644 --- a/plugins/scaffolder-backend-module-gitlab/report.api.md +++ b/plugins/scaffolder-backend-module-gitlab/report.api.md @@ -22,6 +22,7 @@ export const createGitlabGroupEnsureExistsAction: (options: { } )[]; token?: string | undefined; + description?: string | undefined; }, { groupId?: number | undefined; diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.test.ts index 449c5980a4..766348841a 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.test.ts @@ -75,7 +75,7 @@ describe('gitlab:group:ensureExists', () => { expect(mockGitlabClient.Groups.create).toHaveBeenCalledWith( 'group1', 'group1', - {}, + { description: 'This is a top-level group' }, ); expect(mockContext.output).toHaveBeenCalledWith('groupId', 3); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.ts index f379bd38ec..967c0c55e7 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.ts @@ -18,7 +18,7 @@ import yaml from 'yaml'; export const examples: TemplateExample[] = [ { - description: 'Creating a group at the top level', + description: 'Creating a group at the top level, with a description', example: yaml.stringify({ steps: [ { @@ -28,6 +28,7 @@ export const examples: TemplateExample[] = [ input: { repoUrl: 'gitlab.com', path: ['group1'], + description: 'This is a top-level group', }, }, ], diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.test.ts index 8e52ee999e..2e1373b075 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.test.ts @@ -86,16 +86,73 @@ describe('gitlab:group:ensureExists', () => { input: { repoUrl: 'gitlab.com?repo=repo&owner=owner', path: ['foo', 'bar'], + description: 'my cool group', }, }); expect(mockGitlabClient.Groups.create).toHaveBeenCalledWith('bar', 'bar', { + description: 'my cool group', parentId: 2, }); expect(mockContext.output).toHaveBeenCalledWith('groupId', 3); }); + it('should only apply description to the last created group', async () => { + mockGitlabClient.Groups.search.mockResolvedValue([]); + + mockGitlabClient.Groups.create + .mockResolvedValueOnce({ + id: 1, + full_path: 'foo', + }) + .mockResolvedValueOnce({ + id: 2, + full_path: 'foo/bar', + }) + .mockResolvedValueOnce({ + id: 3, + full_path: 'foo/bar/baz', + }); + + await action.handler({ + ...mockContext, + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + path: ['foo', 'bar', 'baz'], + description: 'only for innermost group', + }, + }); + + expect(mockGitlabClient.Groups.create).toHaveBeenCalledTimes(3); + + // First group: no description + expect(mockGitlabClient.Groups.create).toHaveBeenNthCalledWith( + 1, + 'foo', + 'foo', + {}, + ); + + // Second group: no description + expect(mockGitlabClient.Groups.create).toHaveBeenNthCalledWith( + 2, + 'bar', + 'bar', + { parentId: 1 }, + ); + + // Third (last) group: has description + expect(mockGitlabClient.Groups.create).toHaveBeenNthCalledWith( + 3, + 'baz', + 'baz', + { parentId: 2, description: 'only for innermost group' }, + ); + + expect(mockContext.output).toHaveBeenCalledWith('groupId', 3); + }); + it('should create a new group from pathstring if it does not exists', async () => { mockGitlabClient.Groups.search.mockResolvedValue([ { diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.ts index c3d4fd74bf..3c56f94446 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.ts @@ -16,7 +16,7 @@ import { ScmIntegrationRegistry } from '@backstage/integration'; import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; -import { GroupSchema } from '@gitbeaker/rest'; +import { GroupSchema, CreateGroupOptions } from '@gitbeaker/rest'; import { getClient, parseRepoUrl } from '../util'; import { examples } from './gitlabGroupEnsureExists.examples'; @@ -47,6 +47,12 @@ export const createGitlabGroupEnsureExistsAction = (options: { description: 'The token to use for authorization to GitLab', }) .optional(), + description: z => + z + .string({ + description: 'The group’s description', + }) + .optional(), path: z => z .array( @@ -77,8 +83,7 @@ export const createGitlabGroupEnsureExistsAction = (options: { ctx.output('groupId', 42); return; } - - const { token, repoUrl, path } = ctx.input; + const { token, repoUrl, path, description } = ctx.input; const { host } = parseRepoUrl(repoUrl, integrations); @@ -86,7 +91,10 @@ export const createGitlabGroupEnsureExistsAction = (options: { let currentPath: string | null = null; let parentId: number | null = null; - for (const { name, slug } of pathIterator(path)) { + const pathParts = [...pathIterator(path)]; + const lastIndex = pathParts.length - 1; + for (let i = 0; i < pathParts.length; i++) { + const { name, slug } = pathParts[i]; const fullPath: string = currentPath ? `${currentPath}/${slug}` : slug; const result = (await api.Groups.search( fullPath, @@ -101,17 +109,11 @@ export const createGitlabGroupEnsureExistsAction = (options: { key: `ensure.${name}.${slug}.${parentId}`, // eslint-disable-next-line no-loop-func fn: async () => { - return ( - await api.Groups.create( - name, - slug, - parentId - ? { - parentId: parentId, - } - : {}, - ) - )?.id; + const groupOptions: CreateGroupOptions = { + ...(parentId ? { parentId } : {}), + ...(description && i === lastIndex ? { description } : {}), + }; + return (await api.Groups.create(name, slug, groupOptions))?.id; }, }); } else {