feat: allow signing git commits from scaffolder
adds support to sign git commits with configured PGP key in scaffolder actions. configuration can be done either by integration or by using the default signing key in the scaffolder config. note that this pgp-plugin is used for signing and that it is limited to using RSA keys and signatures made with SHA1 hashing algorithm. this change does not support github commit signing as it cannot be done the same way due to GitHub app and REST API being used. closes #25934 refers to #26333 Signed-off-by: Heikki Hellgren <heikki.hellgren@op.fi>
This commit is contained in:
committed by
Hellgren Heikki
parent
37416e06d6
commit
4f8b5b66a1
@@ -16,12 +16,12 @@
|
||||
|
||||
import { Git } from '../scm';
|
||||
import {
|
||||
commitAndPushRepo,
|
||||
initRepoAndPush,
|
||||
commitAndPushBranch,
|
||||
addFiles,
|
||||
createBranch,
|
||||
cloneRepo,
|
||||
commitAndPushBranch,
|
||||
commitAndPushRepo,
|
||||
createBranch,
|
||||
initRepoAndPush,
|
||||
} from './gitHelpers';
|
||||
import { mockServices } from '@backstage/backend-test-utils';
|
||||
import { loggerToWinstonLogger } from './loggerToWinstonLogger';
|
||||
@@ -224,6 +224,32 @@ describe('commitAndPushRepo', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('creates commit with signing key', async () => {
|
||||
await commitAndPushRepo({
|
||||
dir: '/test/repo/dir/',
|
||||
auth: {
|
||||
username: 'test-user',
|
||||
password: 'test-password',
|
||||
},
|
||||
logger: loggerToWinstonLogger(mockServices.logger.mock()),
|
||||
commitMessage: 'commit message',
|
||||
signingKey: 'test-signing-key',
|
||||
});
|
||||
expect(mockedGit.commit).toHaveBeenCalledWith({
|
||||
dir: '/test/repo/dir/',
|
||||
message: 'commit message',
|
||||
author: {
|
||||
name: 'Scaffolder',
|
||||
email: 'scaffolder@backstage.io',
|
||||
},
|
||||
committer: {
|
||||
name: 'Scaffolder',
|
||||
email: 'scaffolder@backstage.io',
|
||||
},
|
||||
signingKey: 'test-signing-key',
|
||||
});
|
||||
});
|
||||
|
||||
it('pushes to the remote', () => {
|
||||
expect(mockedGit.push).toHaveBeenCalledWith({
|
||||
dir: '/test/repo/dir/',
|
||||
|
||||
@@ -31,6 +31,7 @@ export async function initRepoAndPush(input: {
|
||||
defaultBranch?: string;
|
||||
commitMessage?: string;
|
||||
gitAuthorInfo?: { name?: string; email?: string };
|
||||
signingKey?: string;
|
||||
}): Promise<{ commitHash: string }> {
|
||||
const {
|
||||
dir,
|
||||
@@ -40,6 +41,7 @@ export async function initRepoAndPush(input: {
|
||||
defaultBranch = 'master',
|
||||
commitMessage = 'Initial commit',
|
||||
gitAuthorInfo,
|
||||
signingKey,
|
||||
} = input;
|
||||
const git = Git.fromAuth({
|
||||
...auth,
|
||||
@@ -64,6 +66,7 @@ export async function initRepoAndPush(input: {
|
||||
message: commitMessage,
|
||||
author: authorInfo,
|
||||
committer: authorInfo,
|
||||
signingKey,
|
||||
});
|
||||
|
||||
await git.push({
|
||||
@@ -89,6 +92,7 @@ export async function commitAndPushRepo(input: {
|
||||
gitAuthorInfo?: { name?: string; email?: string };
|
||||
branch?: string;
|
||||
remoteRef?: string;
|
||||
signingKey?: string;
|
||||
}): Promise<{ commitHash: string }> {
|
||||
const {
|
||||
dir,
|
||||
@@ -98,6 +102,7 @@ export async function commitAndPushRepo(input: {
|
||||
gitAuthorInfo,
|
||||
branch = 'master',
|
||||
remoteRef,
|
||||
signingKey,
|
||||
} = input;
|
||||
|
||||
const git = Git.fromAuth({
|
||||
@@ -120,6 +125,7 @@ export async function commitAndPushRepo(input: {
|
||||
message: commitMessage,
|
||||
author: authorInfo,
|
||||
committer: authorInfo,
|
||||
signingKey,
|
||||
});
|
||||
|
||||
await git.push({
|
||||
@@ -213,6 +219,7 @@ export async function commitAndPushBranch(options: {
|
||||
branch?: string;
|
||||
remoteRef?: string;
|
||||
remote?: string;
|
||||
signingKey?: string;
|
||||
}): Promise<{ commitHash: string }> {
|
||||
const {
|
||||
dir,
|
||||
@@ -223,6 +230,7 @@ export async function commitAndPushBranch(options: {
|
||||
branch = 'master',
|
||||
remoteRef,
|
||||
remote = 'origin',
|
||||
signingKey,
|
||||
} = options;
|
||||
const git = Git.fromAuth({
|
||||
...auth,
|
||||
@@ -240,6 +248,7 @@ export async function commitAndPushBranch(options: {
|
||||
message: commitMessage,
|
||||
author: authorInfo,
|
||||
committer: authorInfo,
|
||||
signingKey,
|
||||
});
|
||||
|
||||
await git.push({
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
jest.mock('isomorphic-git');
|
||||
jest.mock('isomorphic-git/http/node');
|
||||
jest.mock('fs-extra');
|
||||
jest.mock('@isomorphic-git/pgp-plugin', () => ({
|
||||
...jest.requireActual('@isomorphic-git/pgp-plugin'),
|
||||
pgp: {
|
||||
sign: jest.fn().mockResolvedValue({ signature: 'sign' }),
|
||||
},
|
||||
}));
|
||||
|
||||
import * as isomorphic from 'isomorphic-git';
|
||||
import { Git } from './git';
|
||||
@@ -140,8 +146,9 @@ describe('Git', () => {
|
||||
name: 'comitter',
|
||||
email: 'test@backstage.io',
|
||||
};
|
||||
const signingKey = 'test-signing-key';
|
||||
|
||||
await git.commit({ dir, message, author, committer });
|
||||
await git.commit({ dir, message, author, committer, signingKey });
|
||||
|
||||
expect(isomorphic.commit).toHaveBeenCalledWith({
|
||||
fs,
|
||||
@@ -149,6 +156,8 @@ describe('Git', () => {
|
||||
message,
|
||||
author,
|
||||
committer,
|
||||
signingKey,
|
||||
onSign: expect.any(Function),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,14 +15,16 @@
|
||||
*/
|
||||
|
||||
import git, {
|
||||
ProgressCallback,
|
||||
MergeResult,
|
||||
ReadCommitResult,
|
||||
AuthCallback,
|
||||
MergeResult,
|
||||
ProgressCallback,
|
||||
ReadCommitResult,
|
||||
} from 'isomorphic-git';
|
||||
import http from 'isomorphic-git/http/node';
|
||||
import fs from 'fs-extra';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
// @ts-ignore
|
||||
import { pgp } from '@isomorphic-git/pgp-plugin';
|
||||
|
||||
function isAuthCallbackOptions(
|
||||
options: StaticAuthOptions | AuthCallbackOptions,
|
||||
@@ -137,12 +139,21 @@ export class Git {
|
||||
message: string;
|
||||
author: { name: string; email: string };
|
||||
committer: { name: string; email: string };
|
||||
signingKey?: string;
|
||||
}): Promise<string> {
|
||||
const { dir, message, author, committer } = options;
|
||||
const { dir, message, author, committer, signingKey } = options;
|
||||
this.config.logger?.info(
|
||||
`Committing file to repo {dir=${dir},message=${message}}`,
|
||||
);
|
||||
return git.commit({ fs, dir, message, author, committer });
|
||||
return git.commit({
|
||||
fs,
|
||||
dir,
|
||||
message,
|
||||
author,
|
||||
committer,
|
||||
signingKey,
|
||||
onSign: signingKey ? pgp.sign : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
/** https://isomorphic-git.org/docs/en/clone */
|
||||
@@ -241,8 +252,9 @@ export class Git {
|
||||
ours?: string;
|
||||
author: { name: string; email: string };
|
||||
committer: { name: string; email: string };
|
||||
signingKey?: string;
|
||||
}): Promise<MergeResult> {
|
||||
const { dir, theirs, ours, author, committer } = options;
|
||||
const { dir, theirs, ours, author, committer, signingKey } = options;
|
||||
this.config.logger?.info(
|
||||
`Merging branch '${theirs}' into '${ours}' for repository {dir=${dir}}`,
|
||||
);
|
||||
@@ -255,6 +267,8 @@ export class Git {
|
||||
theirs,
|
||||
author,
|
||||
committer,
|
||||
signingKey,
|
||||
onSign: signingKey ? pgp.sign : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user