feat: add github:autolinks:create action
Signed-off-by: Rutuja Marathe <rutujasudam.marathe@factset.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend-module-github': patch
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
Add a new action for creating github-autolink references for a repository: `github:autolinks:create`
|
||||
@@ -31,6 +31,21 @@ export function createGithubActionsDispatchAction(options: {
|
||||
JsonObject
|
||||
>;
|
||||
|
||||
// @public
|
||||
export function createGithubAutolinksAction(options: {
|
||||
integrations: ScmIntegrations;
|
||||
githubCredentialsProvider?: GithubCredentialsProvider;
|
||||
}): TemplateAction<
|
||||
{
|
||||
repoUrl: string;
|
||||
keyPrefix: string;
|
||||
urlTemplate: string;
|
||||
isAlphanumeric?: boolean | undefined;
|
||||
token?: string | undefined;
|
||||
},
|
||||
JsonObject
|
||||
>;
|
||||
|
||||
// @public
|
||||
export function createGithubDeployKeyAction(options: {
|
||||
integrations: ScmIntegrationRegistry;
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2023 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 { getVoidLogger } from '@backstage/backend-common';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import {
|
||||
DefaultGithubCredentialsProvider,
|
||||
GithubCredentialsProvider,
|
||||
ScmIntegrations,
|
||||
} from '@backstage/integration';
|
||||
import { TemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import { PassThrough } from 'stream';
|
||||
import { createGithubAutolinksAction } from './githubAutolinks';
|
||||
import { examples } from './githubAutolinks.examples';
|
||||
import yaml from 'yaml';
|
||||
|
||||
const mockOctokit = {
|
||||
rest: {
|
||||
repos: {
|
||||
createAutolink: jest.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
jest.mock('octokit', () => ({
|
||||
Octokit: class {
|
||||
constructor() {
|
||||
return mockOctokit;
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
describe('github:autolinks:create', () => {
|
||||
const config = new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{ host: 'github.com', token: 'tokenlols' },
|
||||
{ host: 'ghe.github.com' },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const integrations = ScmIntegrations.fromConfig(config);
|
||||
let githubCredentialsProvider: GithubCredentialsProvider;
|
||||
let action: TemplateAction<any, any>;
|
||||
|
||||
it('should call the githubApis for creating autolink reference', async () => {
|
||||
const input = yaml.parse(examples[0].example).steps[0].input;
|
||||
githubCredentialsProvider =
|
||||
DefaultGithubCredentialsProvider.fromIntegrations(integrations);
|
||||
action = createGithubAutolinksAction({
|
||||
integrations,
|
||||
githubCredentialsProvider,
|
||||
});
|
||||
|
||||
mockOctokit.rest.repos.createAutolink.mockResolvedValue({
|
||||
data: {
|
||||
id: '1',
|
||||
},
|
||||
});
|
||||
await action.handler({
|
||||
input,
|
||||
workspacePath: 'lol',
|
||||
logger: getVoidLogger(),
|
||||
logStream: new PassThrough(),
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
});
|
||||
|
||||
expect(mockOctokit.rest.repos.createAutolink).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
key_prefix: 'TICKET-',
|
||||
url_template: 'https://example.com/TICKET?query=<num>',
|
||||
is_alphanumeric: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2023 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: 'GitHub alphanumric autolink reference',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
action: 'github:autolinks:create',
|
||||
name: 'Create an autolink reference',
|
||||
input: {
|
||||
repoUrl: 'github.com?repo=repo&owner=owner',
|
||||
keyPrefix: 'TICKET-',
|
||||
urlTemplate: 'https://example.com/TICKET?query=<num>',
|
||||
isAlphanumeric: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2023 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 { getVoidLogger } from '@backstage/backend-common';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import {
|
||||
DefaultGithubCredentialsProvider,
|
||||
GithubCredentialsProvider,
|
||||
ScmIntegrations,
|
||||
} from '@backstage/integration';
|
||||
import { TemplateAction } from '@backstage/plugin-scaffolder-node';
|
||||
import { PassThrough } from 'stream';
|
||||
import { createGithubAutolinksAction } from './githubAutolinks';
|
||||
|
||||
const mockOctokit = {
|
||||
rest: {
|
||||
repos: {
|
||||
createAutolink: jest.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
jest.mock('octokit', () => ({
|
||||
Octokit: class {
|
||||
constructor() {
|
||||
return mockOctokit;
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
describe('github:autolinks:create', () => {
|
||||
const config = new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{ host: 'github.com', token: 'tokenlols' },
|
||||
{ host: 'ghe.github.com' },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const integrations = ScmIntegrations.fromConfig(config);
|
||||
let githubCredentialsProvider: GithubCredentialsProvider;
|
||||
let action: TemplateAction<any, any>;
|
||||
const mockContext = {
|
||||
workspacePath: 'lol',
|
||||
logger: getVoidLogger(),
|
||||
logStream: new PassThrough(),
|
||||
output: jest.fn(),
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
};
|
||||
|
||||
it('should call the githubApis for creating alphanumeric autolink reference', async () => {
|
||||
githubCredentialsProvider =
|
||||
DefaultGithubCredentialsProvider.fromIntegrations(integrations);
|
||||
action = createGithubAutolinksAction({
|
||||
integrations,
|
||||
githubCredentialsProvider,
|
||||
});
|
||||
|
||||
mockOctokit.rest.repos.createAutolink.mockResolvedValue({
|
||||
data: {
|
||||
id: '1',
|
||||
},
|
||||
});
|
||||
await action.handler({
|
||||
input: {
|
||||
repoUrl: 'github.com?repo=repo&owner=owner',
|
||||
keyPrefix: 'TICKET-',
|
||||
urlTemplate: 'https://example.com/TICKET?query=<num>',
|
||||
},
|
||||
...mockContext,
|
||||
});
|
||||
|
||||
expect(mockOctokit.rest.repos.createAutolink).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
key_prefix: 'TICKET-',
|
||||
url_template: 'https://example.com/TICKET?query=<num>',
|
||||
});
|
||||
});
|
||||
|
||||
it('should call the githubApis for creating numeric autolink reference', async () => {
|
||||
githubCredentialsProvider =
|
||||
DefaultGithubCredentialsProvider.fromIntegrations(integrations);
|
||||
action = createGithubAutolinksAction({
|
||||
integrations,
|
||||
githubCredentialsProvider,
|
||||
});
|
||||
|
||||
mockOctokit.rest.repos.createAutolink.mockResolvedValue({
|
||||
data: {
|
||||
id: '1',
|
||||
},
|
||||
});
|
||||
await action.handler({
|
||||
input: {
|
||||
repoUrl: 'github.com?repo=repo&owner=owner',
|
||||
keyPrefix: 'TICKET-',
|
||||
urlTemplate: 'https://example.com/TICKET?query=<num>',
|
||||
isAlphanumeric: false,
|
||||
},
|
||||
...mockContext,
|
||||
});
|
||||
|
||||
expect(mockOctokit.rest.repos.createAutolink).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
key_prefix: 'TICKET-',
|
||||
url_template: 'https://example.com/TICKET?query=<num>',
|
||||
is_alphanumeric: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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 {
|
||||
GithubCredentialsProvider,
|
||||
ScmIntegrations,
|
||||
} from '@backstage/integration';
|
||||
import {
|
||||
createTemplateAction,
|
||||
parseRepoUrl,
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
import { Octokit } from 'octokit';
|
||||
import { examples } from './githubAutolinks.examples';
|
||||
import { getOctokitOptions } from './helpers';
|
||||
|
||||
/**
|
||||
* Create an autolink reference for a repository
|
||||
* @public
|
||||
*/
|
||||
export function createGithubAutolinksAction(options: {
|
||||
integrations: ScmIntegrations;
|
||||
githubCredentialsProvider?: GithubCredentialsProvider;
|
||||
}) {
|
||||
const { integrations, githubCredentialsProvider } = options;
|
||||
|
||||
return createTemplateAction<{
|
||||
repoUrl: string;
|
||||
keyPrefix: string;
|
||||
urlTemplate: string;
|
||||
isAlphanumeric?: boolean;
|
||||
token?: string;
|
||||
}>({
|
||||
id: 'github:autolinks:create',
|
||||
description: 'Create an autolink reference for a repository',
|
||||
examples,
|
||||
schema: {
|
||||
input: {
|
||||
type: 'object',
|
||||
required: ['repoUrl', 'keyPrefix', 'urlTemplate'],
|
||||
properties: {
|
||||
repoUrl: {
|
||||
title: 'Repository Location',
|
||||
description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`,
|
||||
type: 'string',
|
||||
},
|
||||
keyPrefix: {
|
||||
title: 'Key Prefix',
|
||||
description:
|
||||
'This prefix appended by certain characters will generate a link any time it is found in an issue, pull request, or commit.',
|
||||
type: 'string',
|
||||
},
|
||||
urlTemplate: {
|
||||
title: 'URL Template',
|
||||
description:
|
||||
'The URL must contain <num> for the reference number. <num> matches different characters depending on the value of isAlphanumeric.',
|
||||
type: 'string',
|
||||
},
|
||||
isAlphanumeric: {
|
||||
title: 'Alphanumeric',
|
||||
description:
|
||||
'Whether this autolink reference matches alphanumeric characters. If true, the <num> parameter of the url_template matches alphanumeric characters A-Z (case insensitive), 0-9, and -. If false, this autolink reference only matches numeric characters. Default: true',
|
||||
type: 'boolean',
|
||||
},
|
||||
token: {
|
||||
title: 'Authentication Token',
|
||||
type: 'string',
|
||||
description: 'The token to use for authorization to GitHub',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
async handler(ctx) {
|
||||
const { repoUrl, keyPrefix, urlTemplate, isAlphanumeric, token } =
|
||||
ctx.input;
|
||||
|
||||
ctx.logger.info(`Creating autolink reference for repo ${repoUrl}`);
|
||||
|
||||
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
|
||||
|
||||
if (!owner) {
|
||||
throw new InputError('Invalid repository owner provided in repoUrl');
|
||||
}
|
||||
|
||||
const client = new Octokit(
|
||||
await getOctokitOptions({
|
||||
integrations,
|
||||
repoUrl,
|
||||
credentialsProvider: githubCredentialsProvider,
|
||||
token,
|
||||
}),
|
||||
);
|
||||
|
||||
await client.rest.repos.createAutolink({
|
||||
owner,
|
||||
repo,
|
||||
key_prefix: keyPrefix,
|
||||
url_template: urlTemplate,
|
||||
is_alphanumeric: isAlphanumeric,
|
||||
});
|
||||
|
||||
ctx.logger.info(`Autolink reference created successfully`);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -27,3 +27,4 @@ export {
|
||||
type CreateGithubPullRequestActionOptions,
|
||||
} from './githubPullRequest';
|
||||
export { createPublishGithubAction } from './github';
|
||||
export { createGithubAutolinksAction } from './githubAutolinks';
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
} from './filesystem';
|
||||
import {
|
||||
createGithubActionsDispatchAction,
|
||||
createGithubAutolinksAction,
|
||||
createGithubDeployKeyAction,
|
||||
createGithubEnvironmentAction,
|
||||
createGithubIssuesLabelAction,
|
||||
@@ -218,6 +219,10 @@ export const createBuiltinActions = (
|
||||
createGithubDeployKeyAction({
|
||||
integrations,
|
||||
}),
|
||||
createGithubAutolinksAction({
|
||||
integrations,
|
||||
githubCredentialsProvider,
|
||||
}),
|
||||
];
|
||||
|
||||
return actions as TemplateAction[];
|
||||
|
||||
Reference in New Issue
Block a user