diff --git a/.changeset/cyan-plants-dress.md b/.changeset/cyan-plants-dress.md new file mode 100644 index 0000000000..19ebc63e53 --- /dev/null +++ b/.changeset/cyan-plants-dress.md @@ -0,0 +1,5 @@ +--- +'example-backend': patch +--- + +Pass GitHub token into Scaffolder GitHub Preparer diff --git a/.changeset/pretty-cups-joke.md b/.changeset/pretty-cups-joke.md new file mode 100644 index 0000000000..49a1db2a75 --- /dev/null +++ b/.changeset/pretty-cups-joke.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Add authentication token to Scaffolder GitHub Preparer diff --git a/packages/backend/src/plugins/scaffolder.ts b/packages/backend/src/plugins/scaffolder.ts index c5b36ab8da..90667bfb16 100644 --- a/packages/backend/src/plugins/scaffolder.ts +++ b/packages/backend/src/plugins/scaffolder.ts @@ -47,13 +47,12 @@ export default async function createPlugin({ templaters.register('cra', craTemplater); const filePreparer = new FilePreparer(); - const githubPreparer = new GithubPreparer(); + const gitlabPreparer = new GitlabPreparer(config); const azurePreparer = new AzurePreparer(config); const preparers = new Preparers(); preparers.register('file', filePreparer); - preparers.register('github', githubPreparer); preparers.register('gitlab', gitlabPreparer); preparers.register('gitlab/api', gitlabPreparer); preparers.register('azure/api', azurePreparer); @@ -75,6 +74,10 @@ export default async function createPlugin({ token: githubToken, repoVisibility, }); + + const githubPreparer = new GithubPreparer({ token: githubToken }); + + preparers.register('github', githubPreparer); publishers.register('file', githubPublisher); publishers.register('github', githubPublisher); } catch (e) { diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.test.ts index 4409d815b5..babdd65c04 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.test.ts @@ -90,7 +90,6 @@ describe('GitHubPreparer', () => { {}, ); }); - it('return the temp directory with the path to the folder if it is specified', async () => { const preparer = new GithubPreparer(); mockEntity.spec.path = './template/test/1/2/3'; @@ -100,4 +99,20 @@ describe('GitHubPreparer', () => { /\/template\/test\/1\/2\/3$/, ); }); + it('calls the clone command with the token when provided', async () => { + const preparer = new GithubPreparer({ token: 'abc' }); + await preparer.prepare(mockEntity); + expect(mocks.Clone.clone).toHaveBeenNthCalledWith( + 1, + 'https://github.com/benjdlambert/backstage-graphql-template', + expect.any(String), + { + fetchOpts: { + callbacks: { + credentials: expect.any(Function), + }, + }, + }, + ); + }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.ts b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.ts index ba4adcabd9..49703c5616 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.ts @@ -21,11 +21,18 @@ import { parseLocationAnnotation } from '../helpers'; import { InputError } from '@backstage/backend-common'; import { PreparerBase } from './types'; import GitUriParser from 'git-url-parse'; -import { Clone } from 'nodegit'; +import { Clone, Cred } from 'nodegit'; export class GithubPreparer implements PreparerBase { + token?: string; + + constructor(params: { token?: string } = {}) { + this.token = params.token; + } + async prepare(template: TemplateEntityV1alpha1): Promise { const { protocol, location } = parseLocationAnnotation(template); + const { token } = this; if (protocol !== 'github') { throw new InputError( @@ -45,9 +52,19 @@ export class GithubPreparer implements PreparerBase { template.spec.path ?? '.', ); - await Clone.clone(repositoryCheckoutUrl, tempDir, { - // TODO(blam): Maybe need some auth here? - }); + const cloneOptions = token + ? { + fetchOpts: { + callbacks: { + credentials() { + return Cred.userpassPlaintextNew(token, 'x-oauth-basic'); + }, + }, + }, + } + : {}; + + await Clone.clone(repositoryCheckoutUrl, tempDir, cloneOptions); return path.resolve(tempDir, templateDirectory); }