Merge pull request #17921 from ohjongsung/bitbucketServer-pullRequests
Add a scaffolder action that pull-requests for Bitbucket Server
This commit is contained in:
@@ -54,6 +54,7 @@ import {
|
||||
createPublishBitbucketAction,
|
||||
createPublishBitbucketCloudAction,
|
||||
createPublishBitbucketServerAction,
|
||||
createPublishBitbucketServerPullRequestAction,
|
||||
createPublishGerritAction,
|
||||
createPublishGerritReviewAction,
|
||||
createPublishGithubAction,
|
||||
@@ -164,6 +165,10 @@ export const createBuiltinActions = (
|
||||
integrations,
|
||||
config,
|
||||
}),
|
||||
createPublishBitbucketServerPullRequestAction({
|
||||
integrations,
|
||||
config,
|
||||
}),
|
||||
createPublishAzureAction({
|
||||
integrations,
|
||||
config,
|
||||
|
||||
+389
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
jest.mock('../helpers', () => {
|
||||
return {
|
||||
initRepoAndPush: jest.fn().mockResolvedValue({
|
||||
commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6',
|
||||
}),
|
||||
commitAndPushRepo: jest.fn().mockResolvedValue({
|
||||
commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6',
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
import { createPublishBitbucketServerPullRequestAction } from './bitbucketServerPullRequest';
|
||||
import { rest } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { setupRequestMockHandlers } from '@backstage/backend-test-utils';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { PassThrough } from 'stream';
|
||||
|
||||
describe('publish:bitbucketServer:pull-request', () => {
|
||||
const config = new ConfigReader({
|
||||
integrations: {
|
||||
bitbucketServer: [
|
||||
{
|
||||
host: 'hosted.bitbucket.com',
|
||||
token: 'thing',
|
||||
apiBaseUrl: 'https://hosted.bitbucket.com/rest/api/1.0',
|
||||
},
|
||||
{
|
||||
host: 'basic-auth.bitbucket.com',
|
||||
username: 'test-user',
|
||||
password: 'test-password',
|
||||
apiBaseUrl: 'https://basic-auth.bitbucket.com/rest/api/1.0',
|
||||
},
|
||||
{
|
||||
host: 'no-credentials.bitbucket.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const integrations = ScmIntegrations.fromConfig(config);
|
||||
const action = createPublishBitbucketServerPullRequestAction({
|
||||
integrations,
|
||||
config,
|
||||
});
|
||||
const mockContext = {
|
||||
input: {
|
||||
repoUrl: 'hosted.bitbucket.com?project=project&repo=repo',
|
||||
title: 'Add Scaffolder actions for Bitbucket Server',
|
||||
description:
|
||||
'I just made a Pull Request that Add Scaffolder actions for Bitbucket Server',
|
||||
targetBranch: 'master',
|
||||
sourceBranch: 'develop',
|
||||
},
|
||||
workspacePath: 'wsp',
|
||||
logger: getVoidLogger(),
|
||||
logStream: new PassThrough(),
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
};
|
||||
const responseOfBranches = {
|
||||
size: 3,
|
||||
limit: 25,
|
||||
isLastPage: true,
|
||||
values: [
|
||||
{
|
||||
id: 'refs/heads/master',
|
||||
displayId: 'master',
|
||||
type: 'BRANCH',
|
||||
latestCommit: 'b1041e3f9b071b3d5cacd6826b7549cd624418f1',
|
||||
latestChangeset: 'b1041e3f9b071b3d5cacd6826b7549cd624418f1',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
id: 'refs/heads/develop',
|
||||
displayId: 'develop',
|
||||
type: 'BRANCH',
|
||||
latestCommit: '98e21148205367aeb11c25a52eaca3c2945253fa',
|
||||
latestChangeset: '98e21148205367aeb11c25a52eaca3c2945253fa',
|
||||
isDefault: false,
|
||||
},
|
||||
{
|
||||
id: 'refs/heads/develop-2',
|
||||
displayId: 'develop-2',
|
||||
type: 'BRANCH',
|
||||
latestCommit: 'b1041e3f9b071b3d5cacd6826b7549cd624418f1',
|
||||
latestChangeset: 'b1041e3f9b071b3d5cacd6826b7549cd624418f1',
|
||||
isDefault: false,
|
||||
},
|
||||
],
|
||||
start: 0,
|
||||
};
|
||||
const responseOfPullRequests = {
|
||||
id: 19,
|
||||
version: 0,
|
||||
title: 'Test for bitbucket server pull-requests',
|
||||
description: 'Test for bitbucket server pull-requests',
|
||||
state: 'OPEN',
|
||||
open: true,
|
||||
closed: false,
|
||||
createdDate: 1684200289521,
|
||||
updatedDate: 1684200289521,
|
||||
fromRef: {
|
||||
id: 'refs/heads/develop',
|
||||
displayId: 'develop',
|
||||
latestCommit: '98e21148205367aeb11c25a52eaca3c2945253fa',
|
||||
type: 'BRANCH',
|
||||
repository: {
|
||||
slug: 'repo',
|
||||
id: 1812,
|
||||
name: 'repo',
|
||||
description: 'This is a test repo',
|
||||
hierarchyId: '1da8822903a9b11a27b8',
|
||||
scmId: 'git',
|
||||
state: 'AVAILABLE',
|
||||
statusMessage: 'Available',
|
||||
forkable: true,
|
||||
project: {},
|
||||
public: false,
|
||||
links: {},
|
||||
},
|
||||
},
|
||||
toRef: {
|
||||
id: 'refs/heads/master',
|
||||
displayId: 'master',
|
||||
latestCommit: 'b1041e3f9b071b3d5cacd6826b7549cd624418f1',
|
||||
type: 'BRANCH',
|
||||
repository: {
|
||||
slug: 'repo',
|
||||
id: 1812,
|
||||
name: 'repo',
|
||||
description: 'This is a test repo',
|
||||
hierarchyId: '1da8822903a9b11a27b8',
|
||||
scmId: 'git',
|
||||
state: 'AVAILABLE',
|
||||
statusMessage: 'Available',
|
||||
forkable: true,
|
||||
project: {},
|
||||
public: false,
|
||||
links: {},
|
||||
},
|
||||
},
|
||||
locked: false,
|
||||
author: {
|
||||
user: {
|
||||
name: 'test-user',
|
||||
emailAddress: 'test-user@sample.com',
|
||||
id: 2944,
|
||||
displayName: 'test-user',
|
||||
active: true,
|
||||
slug: 'test-user',
|
||||
type: 'NORMAL',
|
||||
links: {},
|
||||
},
|
||||
role: 'AUTHOR',
|
||||
approved: false,
|
||||
status: 'UNAPPROVED',
|
||||
},
|
||||
reviewers: [],
|
||||
participants: [],
|
||||
links: {
|
||||
self: [
|
||||
{
|
||||
href: 'https://hosted.bitbucket.com/projects/project/repos/repo/pull-requests/1',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const handlers = [
|
||||
rest.get(
|
||||
'https://hosted.bitbucket.com/rest/api/1.0/projects/project/repos/repo/branches',
|
||||
(_, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.set('Content-Type', 'application/json'),
|
||||
ctx.json(responseOfBranches),
|
||||
);
|
||||
},
|
||||
),
|
||||
rest.post(
|
||||
'https://hosted.bitbucket.com/rest/api/1.0/projects/project/repos/repo/pull-requests',
|
||||
(_, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(201),
|
||||
ctx.set('Content-Type', 'application/json'),
|
||||
ctx.json(responseOfPullRequests),
|
||||
);
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
const server = setupServer();
|
||||
setupRequestMockHandlers(server);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should throw an error when the repoUrl is not well formed', async () => {
|
||||
await expect(
|
||||
action.handler({
|
||||
...mockContext,
|
||||
input: {
|
||||
...mockContext.input,
|
||||
repoUrl: 'hosted.bitbucket.com?repo=repo',
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow(/missing project/);
|
||||
|
||||
await expect(
|
||||
action.handler({
|
||||
...mockContext,
|
||||
input: {
|
||||
...mockContext.input,
|
||||
repoUrl: 'hosted.bitbucket.com?project=project',
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow(/missing repo/);
|
||||
});
|
||||
|
||||
it('should throw if there is no integration config provided', async () => {
|
||||
await expect(
|
||||
action.handler({
|
||||
...mockContext,
|
||||
input: {
|
||||
...mockContext.input,
|
||||
repoUrl: 'missing.com?project=project&repo=repo',
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow(/No matching integration configuration/);
|
||||
});
|
||||
|
||||
it('should throw if there no credentials in the integration config that is returned', async () => {
|
||||
await expect(
|
||||
action.handler({
|
||||
...mockContext,
|
||||
input: {
|
||||
...mockContext.input,
|
||||
repoUrl: 'no-credentials.bitbucket.com?project=project&repo=repo',
|
||||
},
|
||||
}),
|
||||
).rejects.toThrow(
|
||||
/Authorization has not been provided for no-credentials.bitbucket.com/,
|
||||
);
|
||||
});
|
||||
|
||||
it('should call the correct APIs with token', async () => {
|
||||
expect.assertions(3);
|
||||
server.use(
|
||||
rest.get(
|
||||
'https://hosted.bitbucket.com/rest/api/1.0/projects/project/repos/repo/branches',
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('Authorization')).toBe('Bearer thing');
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.set('Content-Type', 'application/json'),
|
||||
ctx.json(responseOfBranches),
|
||||
);
|
||||
},
|
||||
),
|
||||
rest.post(
|
||||
'https://hosted.bitbucket.com/rest/api/1.0/projects/project/repos/repo/pull-requests',
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('Authorization')).toBe('Bearer thing');
|
||||
return res(
|
||||
ctx.status(201),
|
||||
ctx.set('Content-Type', 'application/json'),
|
||||
ctx.json(responseOfPullRequests),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input: {
|
||||
...mockContext.input,
|
||||
repoUrl: 'hosted.bitbucket.com?project=project&repo=repo',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should call the correct APIs with basic auth', async () => {
|
||||
expect.assertions(3);
|
||||
server.use(
|
||||
rest.get(
|
||||
'https://basic-auth.bitbucket.com/rest/api/1.0/projects/project/repos/repo/branches',
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('Authorization')).toBe(
|
||||
'Basic dGVzdC11c2VyOnRlc3QtcGFzc3dvcmQ=',
|
||||
);
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.set('Content-Type', 'application/json'),
|
||||
ctx.json(responseOfBranches),
|
||||
);
|
||||
},
|
||||
),
|
||||
rest.post(
|
||||
'https://basic-auth.bitbucket.com/rest/api/1.0/projects/project/repos/repo/pull-requests',
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('Authorization')).toBe(
|
||||
'Basic dGVzdC11c2VyOnRlc3QtcGFzc3dvcmQ=',
|
||||
);
|
||||
return res(
|
||||
ctx.status(201),
|
||||
ctx.set('Content-Type', 'application/json'),
|
||||
ctx.json(responseOfPullRequests),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input: {
|
||||
...mockContext.input,
|
||||
repoUrl: 'basic-auth.bitbucket.com?project=project&repo=repo',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should work if the token is provided through ctx.input', async () => {
|
||||
expect.assertions(3);
|
||||
const token = 'user-token';
|
||||
server.use(
|
||||
rest.get(
|
||||
'https://no-credentials.bitbucket.com/rest/api/1.0/projects/project/repos/repo/branches',
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('Authorization')).toBe(`Bearer ${token}`);
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.set('Content-Type', 'application/json'),
|
||||
ctx.json(responseOfBranches),
|
||||
);
|
||||
},
|
||||
),
|
||||
rest.post(
|
||||
'https://no-credentials.bitbucket.com/rest/api/1.0/projects/project/repos/repo/pull-requests',
|
||||
(req, res, ctx) => {
|
||||
expect(req.headers.get('Authorization')).toBe(`Bearer ${token}`);
|
||||
return res(
|
||||
ctx.status(201),
|
||||
ctx.set('Content-Type', 'application/json'),
|
||||
ctx.json(responseOfPullRequests),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input: {
|
||||
...mockContext.input,
|
||||
repoUrl: 'no-credentials.bitbucket.com?project=project&repo=repo',
|
||||
token: token,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should call outputs with the correct urls', async () => {
|
||||
server.use(...handlers);
|
||||
|
||||
await action.handler(mockContext);
|
||||
|
||||
expect(mockContext.output).toHaveBeenCalledWith(
|
||||
'pullRequestUrl',
|
||||
'https://hosted.bitbucket.com/projects/project/repos/repo/pull-requests/1',
|
||||
);
|
||||
});
|
||||
});
|
||||
+291
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright 2021 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 {
|
||||
getBitbucketServerRequestOptions,
|
||||
ScmIntegrationRegistry,
|
||||
} from '@backstage/integration';
|
||||
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import fetch, { RequestInit, Response } from 'node-fetch';
|
||||
import { parseRepoUrl } from './util';
|
||||
import { Config } from '@backstage/config';
|
||||
|
||||
const createPullRequest = async (opts: {
|
||||
project: string;
|
||||
repo: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
toRef: {
|
||||
id: string;
|
||||
displayId: string;
|
||||
type: string;
|
||||
latestCommit: string;
|
||||
latestChangeset: string;
|
||||
isDefault: boolean;
|
||||
};
|
||||
fromRef: {
|
||||
id: string;
|
||||
displayId: string;
|
||||
type: string;
|
||||
latestCommit: string;
|
||||
latestChangeset: string;
|
||||
isDefault: boolean;
|
||||
};
|
||||
authorization: string;
|
||||
apiBaseUrl: string;
|
||||
}) => {
|
||||
const {
|
||||
project,
|
||||
repo,
|
||||
title,
|
||||
description,
|
||||
toRef,
|
||||
fromRef,
|
||||
authorization,
|
||||
apiBaseUrl,
|
||||
} = opts;
|
||||
|
||||
let response: Response;
|
||||
const data: RequestInit = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
title: title,
|
||||
description: description,
|
||||
state: 'OPEN',
|
||||
open: true,
|
||||
closed: false,
|
||||
locked: true,
|
||||
toRef: toRef,
|
||||
fromRef: fromRef,
|
||||
}),
|
||||
headers: {
|
||||
Authorization: authorization,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
response = await fetch(
|
||||
`${apiBaseUrl}/projects/${encodeURIComponent(
|
||||
project,
|
||||
)}/repos/${encodeURIComponent(repo)}/pull-requests`,
|
||||
data,
|
||||
);
|
||||
} catch (e) {
|
||||
throw new Error(`Unable to create pull-reqeusts, ${e}`);
|
||||
}
|
||||
|
||||
if (response.status !== 201) {
|
||||
throw new Error(
|
||||
`Unable to create pull requests, ${response.status} ${
|
||||
response.statusText
|
||||
}, ${await response.text()}`,
|
||||
);
|
||||
}
|
||||
|
||||
const r = await response.json();
|
||||
return `${r.links.self[0].href}`;
|
||||
};
|
||||
const findBranches = async (opts: {
|
||||
project: string;
|
||||
repo: string;
|
||||
branchName: string;
|
||||
authorization: string;
|
||||
apiBaseUrl: string;
|
||||
}) => {
|
||||
const { project, repo, branchName, authorization, apiBaseUrl } = opts;
|
||||
|
||||
let response: Response;
|
||||
const options: RequestInit = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: authorization,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
response = await fetch(
|
||||
`${apiBaseUrl}/projects/${encodeURIComponent(
|
||||
project,
|
||||
)}/repos/${encodeURIComponent(
|
||||
repo,
|
||||
)}/branches?boostMatches=true&filterText=${encodeURIComponent(
|
||||
branchName,
|
||||
)}`,
|
||||
options,
|
||||
);
|
||||
} catch (e) {
|
||||
throw new Error(`Unable to get branches, ${e}`);
|
||||
}
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(
|
||||
`Unable to get branches, ${response.status} ${
|
||||
response.statusText
|
||||
}, ${await response.text()}`,
|
||||
);
|
||||
}
|
||||
|
||||
const r = await response.json();
|
||||
for (const object of r.values) {
|
||||
if (object.displayId === branchName) {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a BitbucketServer Pull Request action.
|
||||
* @public
|
||||
*/
|
||||
export function createPublishBitbucketServerPullRequestAction(options: {
|
||||
integrations: ScmIntegrationRegistry;
|
||||
config: Config;
|
||||
}) {
|
||||
const { integrations } = options;
|
||||
|
||||
return createTemplateAction<{
|
||||
repoUrl: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
targetBranch?: string;
|
||||
sourceBranch: string;
|
||||
token?: string;
|
||||
}>({
|
||||
id: 'publish:bitbucketServer:pull-request',
|
||||
schema: {
|
||||
input: {
|
||||
type: 'object',
|
||||
required: ['repoUrl', 'title', 'sourceBranch'],
|
||||
properties: {
|
||||
repoUrl: {
|
||||
title: 'Repository Location',
|
||||
type: 'string',
|
||||
},
|
||||
title: {
|
||||
title: 'Pull Request title',
|
||||
type: 'string',
|
||||
description: 'The title for the pull request',
|
||||
},
|
||||
description: {
|
||||
title: 'Pull Request Description',
|
||||
type: 'string',
|
||||
description: 'The description of the pull request',
|
||||
},
|
||||
targetBranch: {
|
||||
title: 'Target Branch',
|
||||
type: 'string',
|
||||
description: `Branch of repository to apply changes to. The default value is 'master'`,
|
||||
},
|
||||
sourceBranch: {
|
||||
title: 'Source Branch',
|
||||
type: 'string',
|
||||
description: 'Branch of repository to copy changes from',
|
||||
},
|
||||
token: {
|
||||
title: 'Authorization Token',
|
||||
type: 'string',
|
||||
description:
|
||||
'The token to use for authorization to BitBucket Server',
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
pullRequestUrl: {
|
||||
title: 'A URL to the pull request with the provider',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
async handler(ctx) {
|
||||
const {
|
||||
repoUrl,
|
||||
title,
|
||||
description,
|
||||
targetBranch = 'master',
|
||||
sourceBranch,
|
||||
} = ctx.input;
|
||||
|
||||
const { project, repo, host } = parseRepoUrl(repoUrl, integrations);
|
||||
|
||||
if (!project) {
|
||||
throw new InputError(
|
||||
`Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`,
|
||||
);
|
||||
}
|
||||
|
||||
const integrationConfig = integrations.bitbucketServer.byHost(host);
|
||||
if (!integrationConfig) {
|
||||
throw new InputError(
|
||||
`No matching integration configuration for host ${host}, please check your integrations config`,
|
||||
);
|
||||
}
|
||||
|
||||
const token = ctx.input.token ?? integrationConfig.config.token;
|
||||
|
||||
const authConfig = {
|
||||
...integrationConfig.config,
|
||||
...{ token },
|
||||
};
|
||||
|
||||
const reqOpts = getBitbucketServerRequestOptions(authConfig);
|
||||
const authorization = reqOpts.headers.Authorization;
|
||||
if (!authorization) {
|
||||
throw new Error(
|
||||
`Authorization has not been provided for ${integrationConfig.config.host}. Please add either (a) a user login auth token, or (b) a token input from the template or (c) username + password to the integration config.`,
|
||||
);
|
||||
}
|
||||
|
||||
const apiBaseUrl = integrationConfig.config.apiBaseUrl;
|
||||
|
||||
const toRef = await findBranches({
|
||||
project,
|
||||
repo,
|
||||
branchName: targetBranch,
|
||||
authorization,
|
||||
apiBaseUrl,
|
||||
});
|
||||
|
||||
const fromRef = await findBranches({
|
||||
project,
|
||||
repo,
|
||||
branchName: sourceBranch,
|
||||
authorization,
|
||||
apiBaseUrl,
|
||||
});
|
||||
|
||||
const pullRequestUrl = await createPullRequest({
|
||||
project,
|
||||
repo,
|
||||
title,
|
||||
description,
|
||||
toRef,
|
||||
fromRef,
|
||||
authorization,
|
||||
apiBaseUrl,
|
||||
});
|
||||
|
||||
ctx.output('pullRequestUrl', pullRequestUrl);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -18,6 +18,7 @@ export { createPublishAzureAction } from './azure';
|
||||
export { createPublishBitbucketAction } from './bitbucket';
|
||||
export { createPublishBitbucketCloudAction } from './bitbucketCloud';
|
||||
export { createPublishBitbucketServerAction } from './bitbucketServer';
|
||||
export { createPublishBitbucketServerPullRequestAction } from './bitbucketServerPullRequest';
|
||||
export { createPublishGerritAction } from './gerrit';
|
||||
export { createPublishGerritReviewAction } from './gerritReview';
|
||||
export { createPublishGithubAction } from './github';
|
||||
|
||||
Reference in New Issue
Block a user