feat(scaffolder-gitlab): add maskedAndHidden option for project variables

Signed-off-by: hyfc <myloxyloto2012@gmail.com>
This commit is contained in:
hyfc
2026-02-12 15:29:07 +00:00
parent 5dd683fa0a
commit 5730c8e101
10 changed files with 146 additions and 24 deletions
@@ -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.
@@ -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: '*'
@@ -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"
@@ -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;
},
@@ -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,
@@ -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,
@@ -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: '*',
},
@@ -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,
},
},
],
}),
},
];
@@ -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,
});
+27 -20
View File
@@ -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"