diff --git a/.changeset/little-lemons-hope.md b/.changeset/little-lemons-hope.md new file mode 100644 index 0000000000..e2a7f747d6 --- /dev/null +++ b/.changeset/little-lemons-hope.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +GitLab preparer uses the right token (primarily the same one as the publisher, falling back to the integrations token) diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index d5f5a5c56b..46abade7c7 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -32,6 +32,7 @@ "@backstage/backend-common": "^0.4.1", "@backstage/catalog-model": "^0.6.0", "@backstage/config": "^0.1.2", + "@backstage/integration": "^0.1.4", "@gitbeaker/core": "^25.2.0", "@gitbeaker/node": "^25.2.0", "@octokit/rest": "^18.0.0", diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/gitlab.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/gitlab.test.ts index 3d8d7faf7a..8712507026 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/gitlab.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/gitlab.test.ts @@ -42,7 +42,7 @@ const mockEntityWithProtocol = (protocol: string): TemplateEntityV1alpha1 => ({ name: 'graphql-starter', title: 'GraphQL Service', description: - 'A GraphQL starter template for backstage to get you up and running\nthe best pracices with GraphQL\n', + 'A GraphQL starter template for backstage to get you up and running\nthe best practices with GraphQL\n', uid: '9cf16bad-16e0-4213-b314-c4eec773c50b', etag: 'ZTkxMjUxMjUtYWY3Yi00MjU2LWFkYWMtZTZjNjU5ZjJhOWM2', generation: 1, @@ -89,16 +89,41 @@ describe('GitLabPreparer', () => { ); }); - it(`calls the clone command with the correct arguments if an access token is provided for a repository using the ${protocol} protocol`, async () => { + it(`calls the clone command with the correct arguments if an access token is provided in integrations for a repository using the ${protocol} protocol`, async () => { const preparer = new GitlabPreparer( new ConfigReader({ - catalog: { - processors: { - gitlabApi: { - privateToken: 'fake-token', + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'fake-token', }, + ], + }, + }), + ); + mockEntity = mockEntityWithProtocol(protocol); + await preparer.prepare(mockEntity, { logger: getVoidLogger() }); + expect(mocks.Clone.clone).toHaveBeenNthCalledWith( + 1, + 'https://gitlab.com/benjdlambert/backstage-graphql-template', + expect.any(String), + { + fetchOpts: { + callbacks: { + credentials: expect.anything(), }, }, + }, + ); + }); + + it(`calls the clone command with the correct arguments if an access token is provided in scaffolder for a repository using the ${protocol} protocol`, async () => { + const preparer = new GitlabPreparer( + new ConfigReader({ + scaffolder: { + gitlab: { api: { token: 'fake-token' } }, + }, }), ); mockEntity = mockEntityWithProtocol(protocol); diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/gitlab.ts b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/gitlab.ts index 605c8db2df..c2c2dcd3fd 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/gitlab.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/gitlab.ts @@ -13,24 +13,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import os from 'os'; -import fs from 'fs-extra'; -import path from 'path'; -import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; -import { parseLocationAnnotation } from '../helpers'; import { InputError } from '@backstage/backend-common'; -import { PreparerBase, PreparerOptions } from './types'; +import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; +import { Config } from '@backstage/config'; +import { + GitLabIntegrationConfig, + readGitLabIntegrationConfigs, +} from '@backstage/integration'; +import fs from 'fs-extra'; import GitUriParser from 'git-url-parse'; import { Clone, Cred } from 'nodegit'; -import { Config } from '@backstage/config'; +import os from 'os'; +import path from 'path'; +import { parseLocationAnnotation } from '../helpers'; +import { PreparerBase, PreparerOptions } from './types'; export class GitlabPreparer implements PreparerBase { - private readonly privateToken: string; + private readonly integrations: GitLabIntegrationConfig[]; + private readonly scaffolderToken: string | undefined; constructor(config: Config) { - this.privateToken = - config.getOptionalString('catalog.processors.gitlabApi.privateToken') ?? - ''; + this.integrations = readGitLabIntegrationConfigs( + config.getOptionalConfigArray('integrations.gitlab') ?? [], + ); + this.scaffolderToken = config.getOptionalString( + 'scaffolder.gitlab.api.token', + ); } async prepare( @@ -58,12 +66,12 @@ export class GitlabPreparer implements PreparerBase { template.spec.path ?? '.', ); - const options = this.privateToken + const token = this.getToken(parsedGitLocation.resource); + const options = token ? { fetchOpts: { callbacks: { - credentials: () => - Cred.userpassPlaintextNew('oauth2', this.privateToken), + credentials: () => Cred.userpassPlaintextNew('oauth2', token), }, }, } @@ -73,4 +81,11 @@ export class GitlabPreparer implements PreparerBase { return path.resolve(tempDir, templateDirectory); } + + private getToken(host: string): string | undefined { + return ( + this.scaffolderToken || + this.integrations.find(c => c.host === host)?.token + ); + } }