diff --git a/.changeset/silly-emus-remain.md b/.changeset/silly-emus-remain.md new file mode 100644 index 0000000000..4fe605080a --- /dev/null +++ b/.changeset/silly-emus-remain.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Add examples for `github:actions:dispatch` scaffolder actions. diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.examples.test.ts new file mode 100644 index 0000000000..714818ba31 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.examples.test.ts @@ -0,0 +1,134 @@ +/* + * 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 { + ScmIntegrations, + DefaultGithubCredentialsProvider, + GithubCredentialsProvider, +} from '@backstage/integration'; +import { ConfigReader } from '@backstage/config'; +import { getVoidLogger } from '@backstage/backend-common'; +import { TemplateAction } from '@backstage/plugin-scaffolder-node'; +import { PassThrough } from 'stream'; +import { createGithubActionsDispatchAction } from './githubActionsDispatch'; +import yaml from 'yaml'; +import { examples } from './githubActionsDispatch.examples'; + +const mockOctokit = { + rest: { + actions: { + createWorkflowDispatch: jest.fn(), + }, + }, +}; +jest.mock('octokit', () => ({ + Octokit: class { + constructor() { + return mockOctokit; + } + }, +})); + +describe('github:actions:dispatch', () => { + 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; + + const mockContext = { + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + workflowId: 'a-workflow-id', + branchOrTagName: 'main', + }, + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + beforeEach(() => { + jest.resetAllMocks(); + githubCredentialsProvider = + DefaultGithubCredentialsProvider.fromIntegrations(integrations); + action = createGithubActionsDispatchAction({ + integrations, + githubCredentialsProvider, + }); + }); + + it('should call the githubApis for creating WorkflowDispatch without an input object', async () => { + mockOctokit.rest.actions.createWorkflowDispatch.mockResolvedValue({ + data: { + foo: 'bar', + }, + }); + + const repoUrl = 'github.com?repo=repo&owner=owner'; + const workflowId = 'dispatch_workflow'; + const branchOrTagName = 'main'; + const ctx = Object.assign({}, mockContext, { + input: { repoUrl, workflowId, branchOrTagName }, + }); + await action.handler(ctx); + console.log(ctx); + + expect( + mockOctokit.rest.actions.createWorkflowDispatch, + ).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + workflow_id: workflowId, + ref: branchOrTagName, + }); + }); + + it('should call the githubApis for creating WorkflowDispatch with an input object', async () => { + mockOctokit.rest.actions.createWorkflowDispatch.mockResolvedValue({ + data: { + foo: 'bar', + }, + }); + + const repoUrl = 'github.com?repo=repo&owner=owner'; + const workflowId = 'dispatch_workflow'; + const branchOrTagName = 'main'; + const workflowInputs = yaml.parse(examples[1].example).steps[0].input + .workflowInputs; + const ctx = Object.assign({}, mockContext, { + input: { repoUrl, workflowId, branchOrTagName, workflowInputs }, + }); + await action.handler(ctx); + expect( + mockOctokit.rest.actions.createWorkflowDispatch, + ).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + workflow_id: workflowId, + ref: branchOrTagName, + inputs: workflowInputs, + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.examples.ts new file mode 100644 index 0000000000..33ad86dd89 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.examples.ts @@ -0,0 +1,74 @@ +/* + * 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 Action Workflow Without Inputs.', + example: yaml.stringify({ + steps: [ + { + action: 'github:actions:dispatch', + name: 'Dispatch Github Action Workflow', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + workflowId: 'WORKFLOW_ID', + branchOrTagName: 'main', + }, + }, + ], + }), + }, + { + description: 'GitHub Action Workflow With Inputs', + example: yaml.stringify({ + steps: [ + { + action: 'github:actions:dispatch', + name: 'Dispatch Github Action Workflow with inputs', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + workflowId: 'WORKFLOW_ID', + branchOrTagName: 'main', + workflowInputs: { + input1: 'value1', + input2: 'value2', + }, + }, + }, + ], + }), + }, + { + description: 'GitHub Action Workflow With Custom Token', + example: yaml.stringify({ + steps: [ + { + action: 'github:actions:dispatch', + name: 'Dispatch GitHub Action Workflow (custom token)', + input: { + repoUrl: 'github.com?repo=reponame&owner=owner', + workflowId: 'WORKFLOW_ID', + branchOrTagName: 'release-1.0', + token: '${{ secrets.MY_CUSTOM_TOKEN }}', + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.ts index cd1593ee9f..6194713914 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubActionsDispatch.ts @@ -23,6 +23,7 @@ import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; import { Octokit } from 'octokit'; import { parseRepoUrl } from '../publish/util'; import { getOctokitOptions } from './helpers'; +import { examples } from './githubActionsDispatch.examples'; /** * Creates a new action that dispatches a GitHub Action workflow for a given branch or tag. @@ -44,6 +45,7 @@ export function createGithubActionsDispatchAction(options: { id: 'github:actions:dispatch', description: 'Dispatches a GitHub Action workflow for a given branch or tag', + examples, schema: { input: { type: 'object', @@ -119,3 +121,5 @@ export function createGithubActionsDispatchAction(options: { }, }); } + +export { examples };