diff --git a/packages/decap-cms-backend-git-gateway/src/implementation.ts b/packages/decap-cms-backend-git-gateway/src/implementation.ts index 25ab9d07f8d6..ead0c0adc25b 100644 --- a/packages/decap-cms-backend-git-gateway/src/implementation.ts +++ b/packages/decap-cms-backend-git-gateway/src/implementation.ts @@ -381,6 +381,7 @@ export default class GitGateway implements Implementation { return { name: userData.name, login: userData.email, + email: userData.email, avatar_url: userData.avatar_url, } as unknown as User; }); diff --git a/packages/decap-cms-backend-gitea/src/API.ts b/packages/decap-cms-backend-gitea/src/API.ts index 18b4f91fd4aa..ed743f6e2065 100644 --- a/packages/decap-cms-backend-gitea/src/API.ts +++ b/packages/decap-cms-backend-gitea/src/API.ts @@ -131,7 +131,7 @@ export default class API { static DEFAULT_COMMIT_MESSAGE = 'Automatically generated by Static CMS'; - user(): Promise<{ full_name: string; login: string; avatar_url: string }> { + user(): Promise<{ full_name: string; login: string; email: string; avatar_url: string }> { if (!this._userPromise) { this._userPromise = this.getUser(); } diff --git a/packages/decap-cms-backend-gitea/src/implementation.tsx b/packages/decap-cms-backend-gitea/src/implementation.tsx index 8584dffdf935..da05a1cc241c 100644 --- a/packages/decap-cms-backend-gitea/src/implementation.tsx +++ b/packages/decap-cms-backend-gitea/src/implementation.tsx @@ -183,6 +183,7 @@ export default class Gitea implements Implementation { return { name: user.full_name, login: user.login, + email: user.email, avatar_url: user.avatar_url, token: state.token as string, }; diff --git a/packages/decap-cms-backend-github/src/API.ts b/packages/decap-cms-backend-github/src/API.ts index 5120c0286195..13fb7327b692 100644 --- a/packages/decap-cms-backend-github/src/API.ts +++ b/packages/decap-cms-backend-github/src/API.ts @@ -253,13 +253,14 @@ export default class API { static DEFAULT_COMMIT_MESSAGE = 'Automatically generated by Decap CMS'; - user(): Promise<{ name: string; login: string }> { + user(): Promise<{ name: string; login: string; email?: string }> { if (!this._userPromise) { this._userPromise = this.getUser({ token: this.token }); } return this._userPromise.then(user => ({ name: user.name || 'Unknown', login: user.login, + email: user.email ?? undefined, })); } diff --git a/packages/decap-cms-core/index.d.ts b/packages/decap-cms-core/index.d.ts index 8c41d54f4450..6c47c278db2b 100644 --- a/packages/decap-cms-core/index.d.ts +++ b/packages/decap-cms-core/index.d.ts @@ -364,6 +364,7 @@ declare module 'decap-cms-core' { auth_type?: 'implicit' | 'pkce'; cms_label_prefix?: string; squash_merges?: boolean; + signoff_commits?: boolean; proxy_url?: string; commit_messages?: { create?: string; diff --git a/packages/decap-cms-core/src/backend.ts b/packages/decap-cms-core/src/backend.ts index 0025b26f4308..6e5f03d9e068 100644 --- a/packages/decap-cms-core/src/backend.ts +++ b/packages/decap-cms-core/src/backend.ts @@ -1178,6 +1178,7 @@ export class Backend { path, authorLogin: user.login, authorName: user.name, + authorEmail: user.email, }, user.useOpenAuthoring, ); @@ -1253,6 +1254,7 @@ export class Backend { path: file.path, authorLogin: user.login, authorName: user.name, + authorEmail: user.email, }, user.useOpenAuthoring, ), @@ -1279,6 +1281,7 @@ export class Backend { path, authorLogin: user.login, authorName: user.name, + authorEmail: user.email, }, user.useOpenAuthoring, ); @@ -1303,6 +1306,7 @@ export class Backend { path, authorLogin: user.login, authorName: user.name, + authorEmail: user.email, }, user.useOpenAuthoring, ); diff --git a/packages/decap-cms-core/src/lib/__tests__/formatters.spec.js b/packages/decap-cms-core/src/lib/__tests__/formatters.spec.js index efa44e5bd1f8..00bf5896b444 100644 --- a/packages/decap-cms-core/src/lib/__tests__/formatters.spec.js +++ b/packages/decap-cms-core/src/lib/__tests__/formatters.spec.js @@ -247,6 +247,27 @@ describe('formatters', () => { 'Ignoring unknown variable “author-email” in open authoring message template.', ); }); + + it('should return commit with trailer when signoff_commits is enabled', () => { + const collection = Map({ label_singular: 'Collection' }); + const config = { + backend: { + signoff_commits: true, + }, + }; + + expect( + commitMessageFormatter('create', config, { + slug: 'doc-slug', + path: 'file-path', + collection, + authorName: 'Test User', + authorEmail: 'test-user@example.org', + }), + ).toEqual( + 'Create Collection “doc-slug”\n\nSigned-off-by: Test User \n', + ); + }); }); describe('prepareSlug', () => { diff --git a/packages/decap-cms-core/src/lib/formatters.ts b/packages/decap-cms-core/src/lib/formatters.ts index a958cd148e14..239fe3f2f62a 100644 --- a/packages/decap-cms-core/src/lib/formatters.ts +++ b/packages/decap-cms-core/src/lib/formatters.ts @@ -44,16 +44,28 @@ type Options = { collection?: Collection; authorLogin?: string; authorName?: string; + authorEmail?: string; }; export function commitMessageFormatter( type: keyof typeof commitMessageTemplates, config: CmsConfig, - { slug, path, collection, authorLogin, authorName }: Options, + { slug, path, collection, authorLogin, authorName, authorEmail }: Options, isOpenAuthoring?: boolean, ) { const templates = { ...commitMessageTemplates, ...(config.backend.commit_messages || {}) }; + let trailers = ''; + if (config.backend.signoff_commits) { + if (!authorName) { + console.warn('Option signoff_commits is enabled, but author name is unknown'); + } else if (!authorEmail) { + console.warn('Option signoff_commits is enabled, but author email is unknown'); + } else { + trailers = `\n\nSigned-off-by: ${authorName} <${authorEmail}>\n`; + } + } + const commitMessage = templates[type].replace(variableRegex, (_, variable) => { switch (variable) { case 'slug': @@ -73,7 +85,7 @@ export function commitMessageFormatter( }); if (!isOpenAuthoring) { - return commitMessage; + return commitMessage + trailers; } const message = templates.openAuthoring.replace(variableRegex, (_, variable) => { @@ -90,7 +102,7 @@ export function commitMessageFormatter( } }); - return message; + return message + trailers; } export function prepareSlug(slug: string) { diff --git a/packages/decap-cms-core/src/types/redux.ts b/packages/decap-cms-core/src/types/redux.ts index 4587741e24a5..294d118f534b 100644 --- a/packages/decap-cms-core/src/types/redux.ts +++ b/packages/decap-cms-core/src/types/redux.ts @@ -377,6 +377,7 @@ export interface CmsBackend { auth_endpoint?: string; cms_label_prefix?: string; squash_merges?: boolean; + signoff_commits?: boolean; proxy_url?: string; commit_messages?: { create?: string; diff --git a/packages/decap-cms-lib-util/src/implementation.ts b/packages/decap-cms-lib-util/src/implementation.ts index 52539852bde8..3080bf99072f 100644 --- a/packages/decap-cms-lib-util/src/implementation.ts +++ b/packages/decap-cms-lib-util/src/implementation.ts @@ -90,6 +90,7 @@ export type Credentials = { token: string | {}; refresh_token?: string }; export type User = Credentials & { backendName?: string; login?: string; + email?: string; name: string; useOpenAuthoring?: boolean; };