feat(createBuiltinActions): adds bitbucket:pipelines:run action to built in actions

Signed-off-by: Matthew Prinold <matthewprinold@gmail.com>
This commit is contained in:
Matthew Prinold
2023-12-20 11:34:01 +00:00
parent 2db42a19df
commit e7fc793e89
4 changed files with 139 additions and 22 deletions
@@ -47,6 +47,14 @@ describe('bitbucket:pipelines:run', () => {
output: jest.fn(),
createTemporaryDirectory: jest.fn(),
};
const responseJson = {
repository: {
links: {
html: { href: 'https://bitbucket.org/test-workspace/test-repo-slug' },
},
},
build_number: 1,
};
const worker = setupServer();
setupRequestMockHandlers(worker);
@@ -71,7 +79,7 @@ describe('bitbucket:pipelines:run', () => {
return res(
ctx.status(201),
ctx.set('Content-Type', 'application/json'),
ctx.json({}),
ctx.json(responseJson),
);
},
),
@@ -103,7 +111,7 @@ describe('bitbucket:pipelines:run', () => {
return res(
ctx.status(201),
ctx.set('Content-Type', 'application/json'),
ctx.json({}),
ctx.json(responseJson),
);
},
),
@@ -137,7 +145,7 @@ describe('bitbucket:pipelines:run', () => {
return res(
ctx.status(201),
ctx.set('Content-Type', 'application/json'),
ctx.json({}),
ctx.json(responseJson),
);
},
),
@@ -173,7 +181,7 @@ describe('bitbucket:pipelines:run', () => {
return res(
ctx.status(201),
ctx.set('Content-Type', 'application/json'),
ctx.json({}),
ctx.json(responseJson),
);
},
),
@@ -212,7 +220,7 @@ describe('bitbucket:pipelines:run', () => {
return res(
ctx.status(201),
ctx.set('Content-Type', 'application/json'),
ctx.json({}),
ctx.json(responseJson),
);
},
),
@@ -253,7 +261,7 @@ describe('bitbucket:pipelines:run', () => {
return res(
ctx.status(201),
ctx.set('Content-Type', 'application/json'),
ctx.json({}),
ctx.json(responseJson),
);
},
),
@@ -45,6 +45,16 @@ describe('bitbucket:pipelines:run', () => {
output: jest.fn(),
createTemporaryDirectory: jest.fn(),
};
const workspace = 'test-workspace';
const repo_slug = 'test-repo-slug';
const responseJson = {
repository: {
links: {
html: { href: 'https://bitbucket.org/test-workspace/test-repo-slug' },
},
},
build_number: 1,
};
const worker = setupServer();
setupRequestMockHandlers(worker);
@@ -52,10 +62,27 @@ describe('bitbucket:pipelines:run', () => {
jest.clearAllMocks();
});
it('should call the bitbucket api for running a pipeline', async () => {
it('should throw if there is no integration credentials or token provided', async () => {
const configNoCreds = new ConfigReader({
integrations: {
bitbucketCloud: [],
},
});
const integrationsNoCreds = ScmIntegrations.fromConfig(configNoCreds);
const actionNoCreds = createBitbucketPipelinesRunAction({
integrations: integrationsNoCreds,
});
const testContext = Object.assign({}, mockContext, {
input: { workspace, repo_slug },
});
await expect(actionNoCreds.handler(testContext)).rejects.toThrow(
/Authorization has not been provided for Bitbucket Cloud/,
);
});
it('should call the bitbucket api for running a pipeline with integration credentials', async () => {
expect.assertions(1);
const workspace = 'test-workspace';
const repo_slug = 'test-repo-slug';
worker.use(
rest.post(
`https://api.bitbucket.org/2.0/repositories/${workspace}/${repo_slug}/pipelines`,
@@ -64,7 +91,7 @@ describe('bitbucket:pipelines:run', () => {
return res(
ctx.status(201),
ctx.set('Content-Type', 'application/json'),
ctx.json({}),
ctx.json(responseJson),
);
},
),
@@ -75,4 +102,55 @@ describe('bitbucket:pipelines:run', () => {
});
await action.handler(testContext);
});
it('should call the bitbucket api for running a pipeline with input token', async () => {
expect.assertions(1);
worker.use(
rest.post(
`https://api.bitbucket.org/2.0/repositories/${workspace}/${repo_slug}/pipelines`,
(req, res, ctx) => {
expect(req.headers.get('Authorization')).toBe('Bearer abc');
return res(
ctx.status(201),
ctx.set('Content-Type', 'application/json'),
ctx.json(responseJson),
);
},
),
);
const testContext = Object.assign({}, mockContext, {
input: { workspace, repo_slug, token: 'abc' },
});
await action.handler(testContext);
});
it('should call outputs with the correct data', async () => {
worker.use(
rest.post(
`https://api.bitbucket.org/2.0/repositories/${workspace}/${repo_slug}/pipelines`,
(_, res, ctx) => {
return res(
ctx.status(201),
ctx.set('Content-Type', 'application/json'),
ctx.json(responseJson),
);
},
),
);
const testContext = Object.assign({}, mockContext, {
input: { workspace, repo_slug, token: 'abc' },
});
await action.handler(testContext);
expect(testContext.output).toHaveBeenCalledWith('buildNumber', 1);
expect(testContext.output).toHaveBeenCalledWith(
'repoUrl',
'https://bitbucket.org/test-workspace/test-repo-slug',
);
expect(testContext.output).toHaveBeenCalledWith(
'pipelinesUrl',
'https://bitbucket.org/test-workspace/test-repo-slug/pipelines',
);
});
});
@@ -16,10 +16,9 @@
import { examples } from './bitbucketCloudPipelinesRun.examples';
import { createTemplateAction } from '@backstage/plugin-scaffolder-node';
import fetch from 'node-fetch';
import fetch, { Response } from 'node-fetch';
import * as inputProps from './inputProperties';
import { ScmIntegrationRegistry } from '@backstage/integration';
import { InputError } from '@backstage/errors';
import { getAuthorizationHeader } from './helpers';
const id = 'bitbucket:pipelines:run';
@@ -44,7 +43,7 @@ export const createBitbucketPipelinesRunAction = (options: {
schema: {
input: {
type: 'object',
required: ['workspace', 'repo_url'],
required: ['workspace', 'repo_slug'],
properties: {
workspace: inputProps.workspace,
repo_slug: inputProps.repo_slug,
@@ -52,24 +51,36 @@ export const createBitbucketPipelinesRunAction = (options: {
token: inputProps.token,
},
},
output: {
type: 'object',
properties: {
buildNumber: {
title: 'Build number',
type: 'number',
},
repoUrl: {
title: 'A URL to the pipeline repositry',
type: 'string',
},
repoContentsUrl: {
title: 'A URL to the pipeline',
type: 'string',
},
},
},
},
supportsDryRun: false,
async handler(ctx) {
const { workspace, repo_slug, body, token } = ctx.input;
const host = 'bitbucket.org';
const integrationConfig = integrations.bitbucketCloud.byHost(host);
if (!integrationConfig) {
throw new InputError(
`No matching integration configuration for host ${host}, please check your integrations config`,
);
}
const authorization = getAuthorizationHeader(
token ? { token } : integrationConfig.config,
token ? { token } : integrationConfig!.config,
);
let response: Response;
try {
const response = await fetch(
response = await fetch(
`https://api.bitbucket.org/2.0/repositories/${workspace}/${repo_slug}/pipelines`,
{
method: 'POST',
@@ -81,10 +92,26 @@ export const createBitbucketPipelinesRunAction = (options: {
body: JSON.stringify(body) ?? {},
},
);
const data = await response.json();
} catch (e) {
throw new Error(`Unable to run pipeline, ${e}`);
}
if (response.status !== 201) {
throw new Error(
`Unable to run pipeline, ${response.status} ${
response.statusText
}, ${await response.text()}`,
);
}
const responseObject = await response.json();
ctx.output('buildNumber', responseObject.build_number);
ctx.output('repoUrl', responseObject.repository.links.html.href);
ctx.output(
'pipelinesUrl',
`${responseObject.repository.links.html.href}/pipelines`,
);
},
});
};
@@ -60,6 +60,7 @@ import {
createPublishBitbucketCloudAction,
createPublishBitbucketServerAction,
createPublishBitbucketServerPullRequestAction,
createBitbucketPipelinesRunAction,
} from '@backstage/plugin-scaffolder-backend-module-bitbucket';
import {
@@ -223,6 +224,9 @@ export const createBuiltinActions = (
integrations,
githubCredentialsProvider,
}),
createBitbucketPipelinesRunAction({
integrations,
}),
];
return actions as TemplateAction[];