feat(scaffolder-backend-module-gitlab): add gitlab:user:info action (#32556)
Signed-off-by: Jellyfrog <Jellyfrog@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-gitlab': patch
|
||||
---
|
||||
|
||||
Added new `gitlab:user:info` scaffolder action that retrieves information about a GitLab user. The action can fetch either the current authenticated user or a specific user by ID.
|
||||
@@ -140,6 +140,28 @@ export const createGitlabRepoPushAction: (options: {
|
||||
'v2'
|
||||
>;
|
||||
|
||||
// @public
|
||||
export const createGitlabUserInfoAction: (options: {
|
||||
integrations: ScmIntegrationRegistry;
|
||||
}) => TemplateAction<
|
||||
{
|
||||
repoUrl: string;
|
||||
token?: string | undefined;
|
||||
userId?: number | undefined;
|
||||
},
|
||||
{
|
||||
id: number;
|
||||
username: string;
|
||||
name: string;
|
||||
state: string;
|
||||
webUrl: string;
|
||||
email?: string | undefined;
|
||||
createdAt?: string | undefined;
|
||||
publicEmail?: string | undefined;
|
||||
},
|
||||
'v2'
|
||||
>;
|
||||
|
||||
// @public
|
||||
export function createPublishGitlabAction(options: {
|
||||
integrations: ScmIntegrationRegistry;
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils';
|
||||
import { createGitlabUserInfoAction } from './gitlabUserInfo';
|
||||
import { examples } from './gitlabUserInfo.examples';
|
||||
import yaml from 'yaml';
|
||||
import { mockServices } from '@backstage/backend-test-utils';
|
||||
|
||||
const mockGitlabClient = {
|
||||
Users: {
|
||||
show: jest.fn(),
|
||||
showCurrentUser: jest.fn(),
|
||||
},
|
||||
};
|
||||
jest.mock('@gitbeaker/rest', () => ({
|
||||
Gitlab: class {
|
||||
constructor() {
|
||||
return mockGitlabClient;
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
describe('gitlab:user:info examples', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const config = mockServices.rootConfig({
|
||||
data: {
|
||||
integrations: {
|
||||
gitlab: [
|
||||
{
|
||||
host: 'gitlab.com',
|
||||
token: 'sample-token',
|
||||
apiBaseUrl: 'https://gitlab.com/api/v4',
|
||||
},
|
||||
{
|
||||
host: 'gitlab.example.com',
|
||||
token: 'example-token',
|
||||
apiBaseUrl: 'https://gitlab.example.com/api/v4',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
const integrations = ScmIntegrations.fromConfig(config);
|
||||
|
||||
const action = createGitlabUserInfoAction({ integrations });
|
||||
|
||||
it(`should ${examples[0].description}`, async () => {
|
||||
const input = yaml.parse(examples[0].example).steps[0].input;
|
||||
|
||||
const mockContext = createMockActionContext({
|
||||
input,
|
||||
workspacePath: '/tmp/workspace',
|
||||
});
|
||||
|
||||
mockGitlabClient.Users.showCurrentUser.mockResolvedValue({
|
||||
id: 1,
|
||||
username: 'currentuser',
|
||||
name: 'Current User',
|
||||
state: 'active',
|
||||
web_url: 'https://gitlab.com/currentuser',
|
||||
});
|
||||
|
||||
await action.handler(mockContext);
|
||||
|
||||
expect(mockGitlabClient.Users.showCurrentUser).toHaveBeenCalled();
|
||||
expect(mockContext.output).toHaveBeenCalledWith('id', 1);
|
||||
expect(mockContext.output).toHaveBeenCalledWith('username', 'currentuser');
|
||||
});
|
||||
|
||||
it(`should ${examples[1].description}`, async () => {
|
||||
const input = yaml.parse(examples[1].example).steps[0].input;
|
||||
|
||||
const mockContext = createMockActionContext({
|
||||
input,
|
||||
workspacePath: '/tmp/workspace',
|
||||
});
|
||||
|
||||
mockGitlabClient.Users.show.mockResolvedValue({
|
||||
id: 12345,
|
||||
username: 'user12345',
|
||||
name: 'User 12345',
|
||||
state: 'active',
|
||||
web_url: 'https://gitlab.com/user12345',
|
||||
});
|
||||
|
||||
await action.handler(mockContext);
|
||||
|
||||
expect(mockGitlabClient.Users.show).toHaveBeenCalledWith(12345);
|
||||
expect(mockContext.output).toHaveBeenCalledWith('id', 12345);
|
||||
});
|
||||
|
||||
it(`should ${examples[2].description}`, async () => {
|
||||
const input = yaml.parse(examples[2].example).steps[0].input;
|
||||
|
||||
const mockContext = createMockActionContext({
|
||||
input,
|
||||
workspacePath: '/tmp/workspace',
|
||||
});
|
||||
|
||||
mockGitlabClient.Users.show.mockResolvedValue({
|
||||
id: 12345,
|
||||
username: 'user12345',
|
||||
name: 'User 12345',
|
||||
state: 'active',
|
||||
web_url: 'https://gitlab.com/user12345',
|
||||
});
|
||||
|
||||
await action.handler(mockContext);
|
||||
|
||||
expect(mockGitlabClient.Users.show).toHaveBeenCalledWith(12345);
|
||||
expect(mockContext.output).toHaveBeenCalledWith('id', 12345);
|
||||
});
|
||||
|
||||
it(`should ${examples[3].description}`, async () => {
|
||||
const input = yaml.parse(examples[3].example).steps[0].input;
|
||||
|
||||
const mockContext = createMockActionContext({
|
||||
input,
|
||||
workspacePath: '/tmp/workspace',
|
||||
});
|
||||
|
||||
mockGitlabClient.Users.show.mockResolvedValue({
|
||||
id: 12345,
|
||||
username: 'user12345',
|
||||
name: 'User 12345',
|
||||
state: 'active',
|
||||
web_url: 'https://gitlab.example.com/user12345',
|
||||
});
|
||||
|
||||
await action.handler(mockContext);
|
||||
|
||||
expect(mockGitlabClient.Users.show).toHaveBeenCalledWith(12345);
|
||||
expect(mockContext.output).toHaveBeenCalledWith('id', 12345);
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'webUrl',
|
||||
'https://gitlab.example.com/user12345',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { TemplateExample } from '@backstage/plugin-scaffolder-node';
|
||||
import yaml from 'yaml';
|
||||
|
||||
export const examples: TemplateExample[] = [
|
||||
{
|
||||
description: 'Get current authenticated user information',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
id: 'gitlabUserInfo',
|
||||
name: 'Get Current User Info',
|
||||
action: 'gitlab:user:info',
|
||||
input: {
|
||||
repoUrl: 'gitlab.com?repo=repo&owner=owner',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description: 'Get user information by user ID',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
id: 'gitlabUserInfo',
|
||||
name: 'Get User Info',
|
||||
action: 'gitlab:user:info',
|
||||
input: {
|
||||
repoUrl: 'gitlab.com?repo=repo&owner=owner',
|
||||
userId: 12345,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description: 'Get user information with a custom token',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
id: 'gitlabUserInfo',
|
||||
name: 'Get User Info',
|
||||
action: 'gitlab:user:info',
|
||||
input: {
|
||||
repoUrl: 'gitlab.com?repo=repo&owner=owner',
|
||||
token: '${{ secrets.GITLAB_TOKEN }}',
|
||||
userId: 12345,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description: 'Get user information from a self-hosted GitLab instance',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
id: 'gitlabUserInfo',
|
||||
name: 'Get User Info',
|
||||
action: 'gitlab:user:info',
|
||||
input: {
|
||||
repoUrl: 'gitlab.example.com?repo=repo&owner=owner',
|
||||
userId: 12345,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils';
|
||||
import { createGitlabUserInfoAction } from './gitlabUserInfo';
|
||||
import { mockServices } from '@backstage/backend-test-utils';
|
||||
|
||||
const mockGitlabClient = {
|
||||
Users: {
|
||||
show: jest.fn(),
|
||||
showCurrentUser: jest.fn(),
|
||||
},
|
||||
};
|
||||
jest.mock('@gitbeaker/rest', () => ({
|
||||
Gitlab: class {
|
||||
constructor() {
|
||||
return mockGitlabClient;
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
describe('gitlab:user:info', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const config = mockServices.rootConfig({
|
||||
data: {
|
||||
integrations: {
|
||||
gitlab: [
|
||||
{
|
||||
host: 'gitlab.com',
|
||||
token: 'myIntegrationsToken',
|
||||
apiBaseUrl: 'https://gitlab.com/api/v4',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
const integrations = ScmIntegrations.fromConfig(config);
|
||||
|
||||
const action = createGitlabUserInfoAction({ integrations });
|
||||
|
||||
it('should return current user info when no userId is specified', async () => {
|
||||
const mockContext = createMockActionContext({
|
||||
input: {
|
||||
repoUrl: 'gitlab.com?repo=repo&owner=owner',
|
||||
},
|
||||
workspacePath: '/tmp/workspace',
|
||||
});
|
||||
|
||||
mockGitlabClient.Users.showCurrentUser.mockResolvedValue({
|
||||
id: 1,
|
||||
username: 'johndoe',
|
||||
name: 'John Doe',
|
||||
state: 'active',
|
||||
web_url: 'https://gitlab.com/johndoe',
|
||||
email: 'john@example.com',
|
||||
created_at: '2020-01-01T00:00:00Z',
|
||||
public_email: 'john.public@example.com',
|
||||
});
|
||||
|
||||
await action.handler(mockContext);
|
||||
|
||||
expect(mockGitlabClient.Users.showCurrentUser).toHaveBeenCalled();
|
||||
expect(mockGitlabClient.Users.show).not.toHaveBeenCalled();
|
||||
|
||||
expect(mockContext.output).toHaveBeenCalledWith('id', 1);
|
||||
expect(mockContext.output).toHaveBeenCalledWith('username', 'johndoe');
|
||||
expect(mockContext.output).toHaveBeenCalledWith('name', 'John Doe');
|
||||
expect(mockContext.output).toHaveBeenCalledWith('state', 'active');
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'webUrl',
|
||||
'https://gitlab.com/johndoe',
|
||||
);
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'email',
|
||||
'john@example.com',
|
||||
);
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'createdAt',
|
||||
'2020-01-01T00:00:00Z',
|
||||
);
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'publicEmail',
|
||||
'john.public@example.com',
|
||||
);
|
||||
});
|
||||
|
||||
it('should return user info when userId is specified', async () => {
|
||||
const mockContext = createMockActionContext({
|
||||
input: {
|
||||
repoUrl: 'gitlab.com?repo=repo&owner=owner',
|
||||
userId: 123,
|
||||
},
|
||||
workspacePath: '/tmp/workspace',
|
||||
});
|
||||
|
||||
mockGitlabClient.Users.show.mockResolvedValue({
|
||||
id: 123,
|
||||
username: 'janedoe',
|
||||
name: 'Jane Doe',
|
||||
state: 'active',
|
||||
web_url: 'https://gitlab.com/janedoe',
|
||||
});
|
||||
|
||||
await action.handler(mockContext);
|
||||
|
||||
expect(mockGitlabClient.Users.show).toHaveBeenCalledWith(123);
|
||||
expect(mockGitlabClient.Users.showCurrentUser).not.toHaveBeenCalled();
|
||||
|
||||
expect(mockContext.output).toHaveBeenCalledWith('id', 123);
|
||||
expect(mockContext.output).toHaveBeenCalledWith('username', 'janedoe');
|
||||
expect(mockContext.output).toHaveBeenCalledWith('name', 'Jane Doe');
|
||||
expect(mockContext.output).toHaveBeenCalledWith('state', 'active');
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'webUrl',
|
||||
'https://gitlab.com/janedoe',
|
||||
);
|
||||
});
|
||||
|
||||
it('should work with a custom token', async () => {
|
||||
const mockContext = createMockActionContext({
|
||||
input: {
|
||||
repoUrl: 'gitlab.com?repo=repo&owner=owner',
|
||||
token: 'custom-oauth-token',
|
||||
},
|
||||
workspacePath: '/tmp/workspace',
|
||||
});
|
||||
|
||||
mockGitlabClient.Users.showCurrentUser.mockResolvedValue({
|
||||
id: 1,
|
||||
username: 'johndoe',
|
||||
name: 'John Doe',
|
||||
state: 'active',
|
||||
web_url: 'https://gitlab.com/johndoe',
|
||||
});
|
||||
|
||||
await action.handler(mockContext);
|
||||
|
||||
expect(mockGitlabClient.Users.showCurrentUser).toHaveBeenCalled();
|
||||
expect(mockContext.output).toHaveBeenCalledWith('id', 1);
|
||||
});
|
||||
|
||||
it('should handle minimal user response', async () => {
|
||||
const mockContext = createMockActionContext({
|
||||
input: {
|
||||
repoUrl: 'gitlab.com?repo=repo&owner=owner',
|
||||
userId: 456,
|
||||
},
|
||||
workspacePath: '/tmp/workspace',
|
||||
});
|
||||
|
||||
mockGitlabClient.Users.show.mockResolvedValue({
|
||||
id: 456,
|
||||
username: 'minimaluser',
|
||||
name: 'Minimal User',
|
||||
state: 'blocked',
|
||||
web_url: 'https://gitlab.com/minimaluser',
|
||||
});
|
||||
|
||||
await action.handler(mockContext);
|
||||
|
||||
expect(mockContext.output).toHaveBeenCalledWith('id', 456);
|
||||
expect(mockContext.output).toHaveBeenCalledWith('username', 'minimaluser');
|
||||
expect(mockContext.output).toHaveBeenCalledWith('name', 'Minimal User');
|
||||
expect(mockContext.output).toHaveBeenCalledWith('state', 'blocked');
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'webUrl',
|
||||
'https://gitlab.com/minimaluser',
|
||||
);
|
||||
// Optional fields should not be output if not present
|
||||
expect(mockContext.output).not.toHaveBeenCalledWith(
|
||||
'email',
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error when the API call fails', async () => {
|
||||
const mockContext = createMockActionContext({
|
||||
input: {
|
||||
repoUrl: 'gitlab.com?repo=repo&owner=owner',
|
||||
userId: 999,
|
||||
},
|
||||
workspacePath: '/tmp/workspace',
|
||||
});
|
||||
|
||||
mockGitlabClient.Users.show.mockRejectedValue(new Error('User not found'));
|
||||
|
||||
await expect(action.handler(mockContext)).rejects.toThrow(
|
||||
'Failed to retrieve GitLab user info: Error: User not found',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { InputError } from '@backstage/errors';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import { examples } from './gitlabUserInfo.examples';
|
||||
import { getClient, parseRepoUrl } from '../util';
|
||||
import { getErrorMessage } from './helpers';
|
||||
|
||||
/**
|
||||
* Creates a `gitlab:user:info` Scaffolder action.
|
||||
*
|
||||
* @param options - Templating configuration.
|
||||
* @public
|
||||
*/
|
||||
export const createGitlabUserInfoAction = (options: {
|
||||
integrations: ScmIntegrationRegistry;
|
||||
}) => {
|
||||
const { integrations } = options;
|
||||
|
||||
return createTemplateAction({
|
||||
id: 'gitlab:user:info',
|
||||
description:
|
||||
'Retrieves information about a GitLab user or the current authenticated user',
|
||||
examples,
|
||||
schema: {
|
||||
input: {
|
||||
repoUrl: z =>
|
||||
z.string({
|
||||
description: `Accepts the format 'gitlab.com?repo=project_name&owner=group_name' where 'project_name' is the repository name and 'group_name' is a group or username`,
|
||||
}),
|
||||
token: z =>
|
||||
z
|
||||
.string({
|
||||
description: 'The token to use for authorization to GitLab',
|
||||
})
|
||||
.optional(),
|
||||
userId: z =>
|
||||
z
|
||||
.number({
|
||||
description:
|
||||
'User ID. If not provided, returns current authenticated user',
|
||||
})
|
||||
.optional(),
|
||||
},
|
||||
output: {
|
||||
id: z =>
|
||||
z.number({
|
||||
description: 'The user ID',
|
||||
}),
|
||||
username: z =>
|
||||
z.string({
|
||||
description: 'The username',
|
||||
}),
|
||||
name: z =>
|
||||
z.string({
|
||||
description: 'The display name',
|
||||
}),
|
||||
state: z =>
|
||||
z.string({
|
||||
description: 'User state (active, blocked, etc.)',
|
||||
}),
|
||||
webUrl: z =>
|
||||
z.string({
|
||||
description: 'URL to user profile',
|
||||
}),
|
||||
email: z =>
|
||||
z
|
||||
.string({
|
||||
description:
|
||||
'Email address (only available for current user or admins)',
|
||||
})
|
||||
.optional(),
|
||||
createdAt: z =>
|
||||
z
|
||||
.string({
|
||||
description: 'User creation date',
|
||||
})
|
||||
.optional(),
|
||||
publicEmail: z =>
|
||||
z
|
||||
.string({
|
||||
description: 'Public email address',
|
||||
})
|
||||
.optional(),
|
||||
},
|
||||
},
|
||||
async handler(ctx) {
|
||||
try {
|
||||
const { repoUrl, token, userId } = ctx.input;
|
||||
|
||||
const { host } = parseRepoUrl(repoUrl, integrations);
|
||||
const api = getClient({ host, integrations, token });
|
||||
|
||||
const userInfo =
|
||||
userId !== undefined
|
||||
? await api.Users.show(userId)
|
||||
: await api.Users.showCurrentUser();
|
||||
|
||||
ctx.output('id', userInfo.id);
|
||||
ctx.output('username', userInfo.username);
|
||||
ctx.output('name', userInfo.name);
|
||||
ctx.output('state', userInfo.state);
|
||||
ctx.output('webUrl', userInfo.web_url as string);
|
||||
|
||||
if (userInfo.email) {
|
||||
ctx.output('email', userInfo.email as string);
|
||||
}
|
||||
if (userInfo.created_at) {
|
||||
ctx.output('createdAt', userInfo.created_at as string);
|
||||
}
|
||||
if (userInfo.public_email) {
|
||||
ctx.output('publicEmail', userInfo.public_email as string);
|
||||
}
|
||||
} catch (error: any) {
|
||||
throw new InputError(
|
||||
`Failed to retrieve GitLab user info: ${getErrorMessage(error)}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -23,4 +23,5 @@ export * from './gitlabProjectAccessTokenCreate';
|
||||
export * from './gitlabProjectDeployTokenCreate';
|
||||
export * from './gitlabProjectVariableCreate';
|
||||
export * from './gitlabRepoPush';
|
||||
export * from './gitlabUserInfo';
|
||||
export { IssueType, IssueStateEvent } from '../commonGitlabConfig';
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
createGitlabProjectDeployTokenAction,
|
||||
createGitlabProjectVariableAction,
|
||||
createGitlabRepoPushAction,
|
||||
createGitlabUserInfoAction,
|
||||
createPublishGitlabAction,
|
||||
createPublishGitlabMergeRequestAction,
|
||||
createTriggerGitlabPipelineAction,
|
||||
@@ -60,6 +61,7 @@ export const gitlabModule = createBackendModule({
|
||||
createGitlabProjectDeployTokenAction({ integrations }),
|
||||
createGitlabProjectVariableAction({ integrations }),
|
||||
createGitlabRepoPushAction({ integrations }),
|
||||
createGitlabUserInfoAction({ integrations }),
|
||||
editGitlabIssueAction({ integrations }),
|
||||
createPublishGitlabAction({ config, integrations }),
|
||||
createPublishGitlabMergeRequestAction({ integrations }),
|
||||
|
||||
Reference in New Issue
Block a user