diff --git a/app-config.yaml b/app-config.yaml index e3ab12587e..c0bea41b34 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -250,6 +250,11 @@ catalog: target: ../catalog-model/examples/acme-corp.yaml scaffolder: + # Use to customize commit author info used when new components are created + # git: + # author: + # name: Scaffolder + # email: scaffolder@backstage.io github: token: ${GITHUB_TOKEN} visibility: public # or 'internal' or 'private' diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 7528388a47..21c6e03b0f 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -116,6 +116,7 @@ export const createBuiltinActions: (options: { integrations: ScmIntegrations; catalogClient: CatalogApi; templaters: TemplaterBuilder; + config: Config; }) => TemplateAction[]; // @public (undocumented) @@ -155,11 +156,13 @@ export function createLegacyActions(options: Options): TemplateAction[]; // @public (undocumented) export function createPublishAzureAction(options: { integrations: ScmIntegrationRegistry; + config: Config; }): TemplateAction; // @public (undocumented) export function createPublishBitbucketAction(options: { integrations: ScmIntegrationRegistry; + config: Config; }): TemplateAction; // @public @@ -168,6 +171,7 @@ export function createPublishFileAction(): TemplateAction; // @public (undocumented) export function createPublishGithubAction(options: { integrations: ScmIntegrationRegistry; + config: Config; }): TemplateAction; // @public (undocumented) @@ -176,6 +180,7 @@ export const createPublishGithubPullRequestAction: ({ integrations, clientFactor // @public (undocumented) export function createPublishGitlabAction(options: { integrations: ScmIntegrationRegistry; + config: Config; }): TemplateAction; // @public (undocumented) diff --git a/plugins/scaffolder-backend/config.d.ts b/plugins/scaffolder-backend/config.d.ts index 971c3036d6..07602b649a 100644 --- a/plugins/scaffolder-backend/config.d.ts +++ b/plugins/scaffolder-backend/config.d.ts @@ -17,6 +17,15 @@ export interface Config { /** Configuration options for the scaffolder plugin */ scaffolder?: { + git?: { + /** + * The commit author info used when new components are created. + */ + author?: { + name?: string; + email?: string; + }; + }; github?: { [key: string]: string; /** diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts index 46b8db72b4..b3757571ff 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts @@ -17,6 +17,7 @@ import { UrlReader } from '@backstage/backend-common'; import { CatalogApi } from '@backstage/catalog-client'; import { ScmIntegrations } from '@backstage/integration'; +import { Config } from '@backstage/config'; import { TemplaterBuilder } from '../../stages'; import { createCatalogRegisterAction, @@ -41,8 +42,9 @@ export const createBuiltinActions = (options: { integrations: ScmIntegrations; catalogClient: CatalogApi; templaters: TemplaterBuilder; + config: Config; }) => { - const { reader, integrations, templaters, catalogClient } = options; + const { reader, integrations, templaters, catalogClient, config } = options; return [ createFetchPlainAction({ @@ -56,18 +58,22 @@ export const createBuiltinActions = (options: { }), createPublishGithubAction({ integrations, + config, }), createPublishGithubPullRequestAction({ integrations, }), createPublishGitlabAction({ integrations, + config, }), createPublishBitbucketAction({ integrations, + config, }), createPublishAzureAction({ integrations, + config, }), createDebugLogAction(), createCatalogRegisterAction({ catalogClient, integrations }), diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/azure.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/azure.test.ts index d3cb4ccafc..6a1c8fdc55 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/azure.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/azure.test.ts @@ -28,17 +28,17 @@ import { PassThrough } from 'stream'; import { initRepoAndPush } from '../../../stages/publish/helpers'; describe('publish:azure', () => { - const integrations = ScmIntegrations.fromConfig( - new ConfigReader({ - integrations: { - azure: [ - { host: 'dev.azure.com', token: 'tokenlols' }, - { host: 'myazurehostnotoken.com' }, - ], - }, - }), - ); - const action = createPublishAzureAction({ integrations }); + const config = new ConfigReader({ + integrations: { + azure: [ + { host: 'dev.azure.com', token: 'tokenlols' }, + { host: 'myazurehostnotoken.com' }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + const action = createPublishAzureAction({ integrations, config }); const mockContext = { input: { repoUrl: 'dev.azure.com?repo=repo&owner=owner&organization=org', @@ -187,6 +187,7 @@ describe('publish:azure', () => { defaultBranch: 'master', auth: { username: 'notempty', password: 'tokenlols' }, logger: mockContext.logger, + gitAuthorInfo: {}, }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/azure.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/azure.ts index eb1b0522c6..677abc3a08 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/azure.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/azure.ts @@ -21,11 +21,13 @@ import { GitRepositoryCreateOptions } from 'azure-devops-node-api/interfaces/Git import { getPersonalAccessTokenHandler, WebApi } from 'azure-devops-node-api'; import { getRepoSourceDirectory, parseRepoUrl } from './util'; import { createTemplateAction } from '../../createTemplateAction'; +import { Config } from '@backstage/config'; export function createPublishAzureAction(options: { integrations: ScmIntegrationRegistry; + config: Config; }) { - const { integrations } = options; + const { integrations, config } = options; return createTemplateAction<{ repoUrl: string; @@ -123,6 +125,11 @@ export function createPublishAzureAction(options: { // so it's just the base path I think const repoContentsUrl = remoteUrl; + const gitAuthorInfo = { + name: config.getOptionalString('scaffolder.git.author.name'), + email: config.getOptionalString('scaffolder.git.author.email'), + }; + await initRepoAndPush({ dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), remoteUrl, @@ -132,6 +139,7 @@ export function createPublishAzureAction(options: { password: integrationConfig.config.token, }, logger: ctx.logger, + gitAuthorInfo, }); ctx.output('remoteUrl', remoteUrl); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.test.ts index 16b84737a7..f173959aa0 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.test.ts @@ -26,27 +26,27 @@ import { PassThrough } from 'stream'; import { initRepoAndPush } from '../../../stages/publish/helpers'; describe('publish:bitbucket', () => { - const integrations = ScmIntegrations.fromConfig( - new ConfigReader({ - integrations: { - bitbucket: [ - { - host: 'bitbucket.org', - token: 'tokenlols', - }, - { - host: 'hosted.bitbucket.com', - token: 'thing', - apiBaseUrl: 'https://hosted.bitbucket.com/rest/api/1.0', - }, - { - host: 'notoken.bitbucket.com', - }, - ], - }, - }), - ); - const action = createPublishBitbucketAction({ integrations }); + const config = new ConfigReader({ + integrations: { + bitbucket: [ + { + host: 'bitbucket.org', + token: 'tokenlols', + }, + { + host: 'hosted.bitbucket.com', + token: 'thing', + apiBaseUrl: 'https://hosted.bitbucket.com/rest/api/1.0', + }, + { + host: 'notoken.bitbucket.com', + }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + const action = createPublishBitbucketAction({ integrations, config }); const mockContext = { input: { repoUrl: 'bitbucket.org?repo=repo&owner=owner', @@ -331,6 +331,7 @@ describe('publish:bitbucket', () => { defaultBranch: 'main', auth: { username: 'x-token-auth', password: 'tokenlols' }, logger: mockContext.logger, + gitAuthorInfo: {}, }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.ts index 295f4d1574..b05b51a7eb 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.ts @@ -23,6 +23,7 @@ import fetch from 'cross-fetch'; import { initRepoAndPush } from '../../../stages/publish/helpers'; import { createTemplateAction } from '../../createTemplateAction'; import { getRepoSourceDirectory, parseRepoUrl } from './util'; +import { Config } from '@backstage/config'; const createBitbucketCloudRepository = async (opts: { owner: string; @@ -184,8 +185,9 @@ const performEnableLFS = async (opts: { export function createPublishBitbucketAction(options: { integrations: ScmIntegrationRegistry; + config: Config; }) { - const { integrations } = options; + const { integrations, config } = options; return createTemplateAction<{ repoUrl: string; @@ -284,6 +286,11 @@ export function createPublishBitbucketAction(options: { apiBaseUrl, }); + const gitAuthorInfo = { + name: config.getOptionalString('scaffolder.git.author.name'), + email: config.getOptionalString('scaffolder.git.author.email'), + }; + await initRepoAndPush({ dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), remoteUrl, @@ -297,6 +304,7 @@ export function createPublishBitbucketAction(options: { }, defaultBranch, logger: ctx.logger, + gitAuthorInfo, }); if (enableLFS && host !== 'bitbucket.org') { diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts index 910fa27b46..058168d666 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts @@ -25,17 +25,17 @@ import { initRepoAndPush } from '../../../stages/publish/helpers'; import { when } from 'jest-when'; describe('publish:github', () => { - const integrations = ScmIntegrations.fromConfig( - new ConfigReader({ - integrations: { - github: [ - { host: 'github.com', token: 'tokenlols' }, - { host: 'ghe.github.com' }, - ], - }, - }), - ); - const action = createPublishGithubAction({ integrations }); + const config = new ConfigReader({ + integrations: { + github: [ + { host: 'github.com', token: 'tokenlols' }, + { host: 'ghe.github.com' }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + const action = createPublishGithubAction({ integrations, config }); const mockContext = { input: { repoUrl: 'github.com?repo=repo&owner=owner', @@ -178,6 +178,7 @@ describe('publish:github', () => { defaultBranch: 'master', auth: { username: 'x-access-token', password: 'tokenlols' }, logger: mockContext.logger, + gitAuthorInfo: {}, }); }); @@ -207,6 +208,7 @@ describe('publish:github', () => { defaultBranch: 'main', auth: { username: 'x-access-token', password: 'tokenlols' }, logger: mockContext.logger, + gitAuthorInfo: {}, }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts index 7dd790cb62..f14dd817b0 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -25,14 +25,16 @@ import { } from '../../../stages/publish/helpers'; import { getRepoSourceDirectory, parseRepoUrl } from './util'; import { createTemplateAction } from '../../createTemplateAction'; +import { Config } from '@backstage/config'; type Permission = 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; type Collaborator = { access: Permission; username: string }; export function createPublishGithubAction(options: { integrations: ScmIntegrationRegistry; + config: Config; }) { - const { integrations } = options; + const { integrations, config } = options; const credentialsProviders = new Map( integrations.github.list().map(integration => { @@ -248,6 +250,11 @@ export function createPublishGithubAction(options: { const remoteUrl = newRepo.clone_url; const repoContentsUrl = `${newRepo.html_url}/blob/${defaultBranch}`; + const gitAuthorInfo = { + name: config.getOptionalString('scaffolder.git.author.name'), + email: config.getOptionalString('scaffolder.git.author.email'), + }; + await initRepoAndPush({ dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), remoteUrl, @@ -257,6 +264,7 @@ export function createPublishGithubAction(options: { password: token, }, logger: ctx.logger, + gitAuthorInfo, }); try { diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.test.ts index 67064efde7..427056454c 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.test.ts @@ -24,24 +24,24 @@ import { PassThrough } from 'stream'; import { initRepoAndPush } from '../../../stages/publish/helpers'; describe('publish:gitlab', () => { - const integrations = ScmIntegrations.fromConfig( - new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'tokenlols', - apiBaseUrl: 'https://api.gitlab.com', - }, - { - host: 'hosted.gitlab.com', - apiBaseUrl: 'https://api.hosted.gitlab.com', - }, - ], - }, - }), - ); - const action = createPublishGitlabAction({ integrations }); + const config = new ConfigReader({ + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://api.gitlab.com', + }, + { + host: 'hosted.gitlab.com', + apiBaseUrl: 'https://api.hosted.gitlab.com', + }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + const action = createPublishGitlabAction({ integrations, config }); const mockContext = { input: { repoUrl: 'gitlab.com?repo=repo&owner=owner', @@ -166,6 +166,7 @@ describe('publish:gitlab', () => { remoteUrl: 'http://mockurl.git', auth: { username: 'oauth2', password: 'tokenlols' }, logger: mockContext.logger, + gitAuthorInfo: {}, }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts index 57c7fdf752..6d7384294d 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts @@ -20,11 +20,13 @@ import { Gitlab } from '@gitbeaker/node'; import { initRepoAndPush } from '../../../stages/publish/helpers'; import { getRepoSourceDirectory, parseRepoUrl } from './util'; import { createTemplateAction } from '../../createTemplateAction'; +import { Config } from '@backstage/config'; export function createPublishGitlabAction(options: { integrations: ScmIntegrationRegistry; + config: Config; }) { - const { integrations } = options; + const { integrations, config } = options; return createTemplateAction<{ repoUrl: string; @@ -121,6 +123,11 @@ export function createPublishGitlabAction(options: { const remoteUrl = (http_url_to_repo as string).replace(/\.git$/, ''); const repoContentsUrl = `${remoteUrl}/-/blob/master`; + const gitAuthorInfo = { + name: config.getOptionalString('scaffolder.git.author.name'), + email: config.getOptionalString('scaffolder.git.author.email'), + }; + await initRepoAndPush({ dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), remoteUrl: http_url_to_repo as string, @@ -130,6 +137,7 @@ export function createPublishGitlabAction(options: { password: integrationConfig.config.token, }, logger: ctx.logger, + gitAuthorInfo, }); ctx.output('remoteUrl', remoteUrl); diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/publish/helpers.ts b/plugins/scaffolder-backend/src/scaffolder/stages/publish/helpers.ts index dff0feb5f2..c974c466d4 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/publish/helpers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/publish/helpers.ts @@ -25,12 +25,14 @@ export async function initRepoAndPush({ auth, logger, defaultBranch = 'master', + gitAuthorInfo, }: { dir: string; remoteUrl: string; auth: { username: string; password: string }; logger: Logger; defaultBranch?: string; + gitAuthorInfo?: { name?: string; email?: string }; }): Promise { const git = Git.fromAuth({ username: auth.username, @@ -53,11 +55,17 @@ export async function initRepoAndPush({ await git.add({ dir, filepath }); } + // use provided info if possible, otherwise use fallbacks + const authorInfo = { + name: gitAuthorInfo?.name ?? 'Scaffolder', + email: gitAuthorInfo?.email ?? 'scaffolder@backstage.io', + }; + await git.commit({ dir, message: 'Initial commit', - author: { name: 'Scaffolder', email: 'scaffolder@backstage.io' }, - committer: { name: 'Scaffolder', email: 'scaffolder@backstage.io' }, + author: authorInfo, + committer: authorInfo, }); await git.addRemote({ diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index b0142d4b02..17304aefb1 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -129,6 +129,7 @@ export async function createRouter( catalogClient, templaters, reader, + config, }), ];