From 5730c8e1011c76c509cfe183f62db125170de947 Mon Sep 17 00:00:00 2001 From: hyfc Date: Thu, 12 Feb 2026 15:29:07 +0000 Subject: [PATCH] feat(scaffolder-gitlab): add maskedAndHidden option for project variables Signed-off-by: hyfc --- .changeset/add-masked-and-hidden-option.md | 5 ++ .../README.md | 1 + .../package.json | 4 +- .../report.api.md | 2 + .../src/actions/gitlab.test.ts | 45 +++++++++++++++++- .../src/actions/gitlab.ts | 5 +- ...tlabProjectVariableCreate.examples.test.ts | 32 +++++++++++++ .../gitlabProjectVariableCreate.examples.ts | 20 ++++++++ .../actions/gitlabProjectVariableCreate.ts | 9 ++++ yarn.lock | 47 +++++++++++-------- 10 files changed, 146 insertions(+), 24 deletions(-) create mode 100644 .changeset/add-masked-and-hidden-option.md diff --git a/.changeset/add-masked-and-hidden-option.md b/.changeset/add-masked-and-hidden-option.md new file mode 100644 index 0000000000..ad5c810a8e --- /dev/null +++ b/.changeset/add-masked-and-hidden-option.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +--- + +Added `maskedAndHidden` option to `gitlab:projectVariable:create` and `publish:gitlab` action to support creating GitLab project variables that are both masked and hidden. Updated gitbeaker to version 43.8.0 for proper type support. diff --git a/plugins/scaffolder-backend-module-gitlab/README.md b/plugins/scaffolder-backend-module-gitlab/README.md index 4542ec4198..1579f2e55b 100644 --- a/plugins/scaffolder-backend-module-gitlab/README.md +++ b/plugins/scaffolder-backend-module-gitlab/README.md @@ -117,6 +117,7 @@ spec: value: "${{ steps['gitlab-access-token'].output.access_token }}" variableType: 'env_var' masked: true + maskedAndHidden: false variableProtected: false raw: false environmentScope: '*' diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index d5a35090e9..f705312f01 100644 --- a/plugins/scaffolder-backend-module-gitlab/package.json +++ b/plugins/scaffolder-backend-module-gitlab/package.json @@ -50,8 +50,8 @@ "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", "@backstage/plugin-scaffolder-node": "workspace:^", - "@gitbeaker/requester-utils": "^41.2.0", - "@gitbeaker/rest": "^41.2.0", + "@gitbeaker/requester-utils": "^43.8.0", + "@gitbeaker/rest": "^43.8.0", "luxon": "^3.0.0", "yaml": "^2.0.0", "zod": "^3.25.76" diff --git a/plugins/scaffolder-backend-module-gitlab/report.api.md b/plugins/scaffolder-backend-module-gitlab/report.api.md index 6b59166118..519e3938e2 100644 --- a/plugins/scaffolder-backend-module-gitlab/report.api.md +++ b/plugins/scaffolder-backend-module-gitlab/report.api.md @@ -111,6 +111,7 @@ export const createGitlabProjectVariableAction: (options: { token?: string | undefined; variableProtected?: boolean | undefined; masked?: boolean | undefined; + maskedAndHidden?: boolean | undefined; raw?: boolean | undefined; environmentScope?: string | undefined; }, @@ -221,6 +222,7 @@ export function createPublishGitlabAction(options: { variable_type?: 'file' | 'env_var' | undefined; masked?: boolean | undefined; environment_scope?: string | undefined; + masked_and_hidden?: boolean | undefined; }[] | undefined; }, diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.test.ts index 1e3ac40d35..564075fe96 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.test.ts @@ -146,6 +146,19 @@ describe('publish:gitlab', () => { ], }, }); + const mockContextWithMaskedAndHiddenVariable = createMockActionContext({ + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + repoVisibility: 'private' as const, + projectVariables: [ + { + key: 'secret', + value: 'secret-value', + masked_and_hidden: true, + }, + ], + }, + }); beforeEach(() => { jest.resetAllMocks(); @@ -441,6 +454,36 @@ describe('publish:gitlab', () => { variableType: 'env_var', protected: true, masked: true, + masked_and_hidden: false, + raw: false, + environmentScope: '*', + }, + ); + }); + + it('should call the correct Gitlab APIs for variables with masked_and_hidden option', async () => { + mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ + id: 1234, + kind: 'group', + }); + mockGitlabClient.Groups.allProjects.mockResolvedValue([]); + mockGitlabClient.Projects.create.mockResolvedValue({ + id: 123456, + http_url_to_repo: 'http://mockurl.git', + }); + + await action.handler(mockContextWithMaskedAndHiddenVariable); + + expect(mockGitlabClient.ProjectVariables.create).toHaveBeenCalledWith( + 123456, + 'secret', + 'secret-value', + { + variableType: 'env_var', + protected: false, + masked: false, + masked_and_hidden: true, raw: false, environmentScope: '*', }, @@ -725,8 +768,8 @@ describe('publish:gitlab', () => { expect(mockGitlabClient.Users.showCurrentUser).toHaveBeenCalled(); expect(mockGitlabClient.ProjectMembers.add).toHaveBeenCalledWith( 123456, - 12345, 50, + { userId: 12345 }, ); expect(mockGitlabClient.Projects.create).toHaveBeenCalledWith({ namespaceId: 1234, diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.ts index 7f3f673260..d743dd06ac 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.ts @@ -206,6 +206,7 @@ export function createPublishGitlabAction(options: { variable_type: z.enum(['env_var', 'file']).optional(), protected: z.boolean().optional(), masked: z.boolean().optional(), + masked_and_hidden: z.boolean().optional(), raw: z.boolean().optional(), environment_scope: z.string().optional(), }), @@ -339,7 +340,7 @@ export function createPublishGitlabAction(options: { token: integrationConfig.config.token, }); - await adminClient.ProjectMembers.add(projectId, userId, 50); + await adminClient.ProjectMembers.add(projectId, 50, { userId }); } const remoteUrl = (http_url_to_repo as string).replace(/\.git$/, ''); @@ -435,6 +436,7 @@ export function createPublishGitlabAction(options: { 'env_var') as VariableType, protected: variable.protected ?? false, masked: variable.masked ?? false, + masked_and_hidden: variable.masked_and_hidden ?? false, raw: variable.raw ?? false, environment_scope: variable.environment_scope ?? '*', }); @@ -448,6 +450,7 @@ export function createPublishGitlabAction(options: { variableType: variableWithDefaults.variable_type, protected: variableWithDefaults.protected, masked: variableWithDefaults.masked, + masked_and_hidden: variableWithDefaults.masked_and_hidden, environmentScope: variableWithDefaults.environment_scope, description: variableWithDefaults.description, raw: variableWithDefaults.raw, diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabProjectVariableCreate.examples.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabProjectVariableCreate.examples.test.ts index 99b26682d8..be844f83e3 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabProjectVariableCreate.examples.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabProjectVariableCreate.examples.test.ts @@ -85,6 +85,7 @@ describe('gitlab:projectVariableAction: create examples', () => { variableType: 'env_var', // Correctly using variableType environmentScope: '*', masked: false, + masked_and_hidden: false, protected: false, raw: false, }, @@ -109,6 +110,7 @@ describe('gitlab:projectVariableAction: create examples', () => { variableType: 'file', // Correctly using variableType protected: false, masked: false, + masked_and_hidden: false, raw: false, environmentScope: '*', }, @@ -131,6 +133,7 @@ describe('gitlab:projectVariableAction: create examples', () => { 'my_value', { masked: false, + masked_and_hidden: false, raw: false, environmentScope: '*', variableType: 'env_var', // Correctly using variableType @@ -159,6 +162,7 @@ describe('gitlab:projectVariableAction: create examples', () => { environmentScope: '*', variableType: 'env_var', // Correctly using variableType masked: true, + masked_and_hidden: false, }, ); }); @@ -182,6 +186,7 @@ describe('gitlab:projectVariableAction: create examples', () => { environmentScope: '*', variableType: 'env_var', // Correctly using variableType masked: false, + masked_and_hidden: false, raw: true, }, ); @@ -205,6 +210,7 @@ describe('gitlab:projectVariableAction: create examples', () => { protected: false, variableType: 'env_var', // Correctly using variableType masked: false, + masked_and_hidden: false, raw: false, environmentScope: 'production', }, @@ -229,6 +235,32 @@ describe('gitlab:projectVariableAction: create examples', () => { protected: false, variableType: 'env_var', // Correctly using variableType masked: false, + masked_and_hidden: false, + raw: false, + environmentScope: '*', + }, + ); + }); + + it(`Should ${examples[7].description}`, async () => { + mockGitlabClient.ProjectVariables.create.mockResolvedValue({ + token: 'TOKEN', + }); + + await action.handler({ + ...mockContext, + input: yaml.parse(examples[7].example).steps[0].input, + }); + + expect(mockGitlabClient.ProjectVariables.create).toHaveBeenCalledWith( + '999', + 'SECRET_TOKEN', + 'super-secret-token', + { + protected: false, + variableType: 'env_var', + masked: false, + masked_and_hidden: true, raw: false, environmentScope: '*', }, diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabProjectVariableCreate.examples.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabProjectVariableCreate.examples.ts index 81d526f7c8..e1d040f028 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabProjectVariableCreate.examples.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabProjectVariableCreate.examples.ts @@ -157,4 +157,24 @@ export const examples: TemplateExample[] = [ ], }), }, + { + description: 'Create a GitLab project variable that is masked and hidden.', + example: yaml.stringify({ + steps: [ + { + id: 'createVariable', + action: 'gitlab:projectVariable:create', + name: 'Create GitLab Project Variable', + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: '999', + key: 'SECRET_TOKEN', + value: 'super-secret-token', + variableType: 'env_var', + maskedAndHidden: true, + }, + }, + ], + }), + }, ]; diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabProjectVariableCreate.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabProjectVariableCreate.ts index 824a8566f7..031ff028bb 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabProjectVariableCreate.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabProjectVariableCreate.ts @@ -78,6 +78,13 @@ export const createGitlabProjectVariableAction = (options: { }) .default(false) .optional(), + maskedAndHidden: z => + z + .boolean({ + description: 'Whether the variable is masked and hidden', + }) + .default(false) + .optional(), raw: z => z .boolean({ @@ -103,6 +110,7 @@ export const createGitlabProjectVariableAction = (options: { variableType, variableProtected = false, masked = false, + maskedAndHidden = false, raw = false, environmentScope = '*', token, @@ -119,6 +127,7 @@ export const createGitlabProjectVariableAction = (options: { variableType: variableType as VariableType, protected: variableProtected, masked, + masked_and_hidden: maskedAndHidden, raw, environmentScope, }); diff --git a/yarn.lock b/yarn.lock index a1c84f2f09..bc8dfcdf69 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6848,8 +6848,8 @@ __metadata: "@backstage/integration": "workspace:^" "@backstage/plugin-scaffolder-node": "workspace:^" "@backstage/plugin-scaffolder-node-test-utils": "workspace:^" - "@gitbeaker/requester-utils": "npm:^41.2.0" - "@gitbeaker/rest": "npm:^41.2.0" + "@gitbeaker/requester-utils": "npm:^43.8.0" + "@gitbeaker/rest": "npm:^43.8.0" luxon: "npm:^3.0.0" yaml: "npm:^2.0.0" zod: "npm:^3.25.76" @@ -9272,14 +9272,14 @@ __metadata: languageName: node linkType: hard -"@gitbeaker/core@npm:^41.3.0": - version: 41.3.0 - resolution: "@gitbeaker/core@npm:41.3.0" +"@gitbeaker/core@npm:^43.8.0": + version: 43.8.0 + resolution: "@gitbeaker/core@npm:43.8.0" dependencies: - "@gitbeaker/requester-utils": "npm:^41.3.0" - qs: "npm:^6.12.2" + "@gitbeaker/requester-utils": "npm:^43.8.0" + qs: "npm:^6.14.0" xcase: "npm:^2.0.1" - checksum: 10/db60bfedcd541bd9826f7292630109934a4c667c240e4e914cdb8034c7628a841ebef8701f3d8ed5ac7a64b67479ae7415049a45071b12e40787332653f0f99f + checksum: 10/1c935ffec246c854d7d543cc4ccb97271c322ea506c02ccf60774bffd42462c36cd3324d6876082da706d25e444878f76714746b1fbd77c202b0e3766ef5f4ae languageName: node linkType: hard @@ -9295,15 +9295,15 @@ __metadata: languageName: node linkType: hard -"@gitbeaker/requester-utils@npm:^41.2.0, @gitbeaker/requester-utils@npm:^41.3.0": - version: 41.3.0 - resolution: "@gitbeaker/requester-utils@npm:41.3.0" +"@gitbeaker/requester-utils@npm:^43.8.0": + version: 43.8.0 + resolution: "@gitbeaker/requester-utils@npm:43.8.0" dependencies: picomatch-browser: "npm:^2.2.6" - qs: "npm:^6.12.2" - rate-limiter-flexible: "npm:^4.0.1" + qs: "npm:^6.14.0" + rate-limiter-flexible: "npm:^8.0.1" xcase: "npm:^2.0.1" - checksum: 10/10e3d6a87368f7111493a86f17dc969f0e8abb1aae07ac577b204acc297efa1711bfb4d6ba1f8299273e2bc410a755bd329111943fc6337c573cdbb38ab5f899 + checksum: 10/529685994676068a18f4bf6329252c66a62c1ef160f6e9d4b3a916330189cc1628fb30c983625d38295cef2999438e14ecb611fcd8a484b7c479b6dcf29f7679 languageName: node linkType: hard @@ -9317,13 +9317,13 @@ __metadata: languageName: node linkType: hard -"@gitbeaker/rest@npm:^41.2.0": - version: 41.3.0 - resolution: "@gitbeaker/rest@npm:41.3.0" +"@gitbeaker/rest@npm:^43.8.0": + version: 43.8.0 + resolution: "@gitbeaker/rest@npm:43.8.0" dependencies: - "@gitbeaker/core": "npm:^41.3.0" - "@gitbeaker/requester-utils": "npm:^41.3.0" - checksum: 10/8f280919303e2a57f9cf7070cc3bb74966be73c24448ab6cdd59c7ca6ad9f79f34a1454fb47138fddf91cc743ba769ead42513e5efb06e2be9d40f1f92432cc7 + "@gitbeaker/core": "npm:^43.8.0" + "@gitbeaker/requester-utils": "npm:^43.8.0" + checksum: 10/a29493ee1e0e789a0fa4307108c616bb287f33a390262a57025461140a284ae1284918ff4d4e91d3351e61b050eea55ffe2ca7551ffcb6be29ae22c67136a463 languageName: node linkType: hard @@ -43765,6 +43765,13 @@ __metadata: languageName: node linkType: hard +"rate-limiter-flexible@npm:^8.0.1": + version: 8.3.0 + resolution: "rate-limiter-flexible@npm:8.3.0" + checksum: 10/9c8d7a3224e3a57fdf7da721dabdb4942eb7dd7b5f3aa4cd57e2185928d5842855a8dc2e1db5e0403987d369972d0567e98aade2c2daa71436ebdae704e5a8ff + languageName: node + linkType: hard + "raw-body@npm:^2.3.3, raw-body@npm:^2.4.1, raw-body@npm:~2.5.3": version: 2.5.3 resolution: "raw-body@npm:2.5.3"