Fix for Issue ##21762

Signed-off-by: Jason Froehlich <jfroehli@redhat.com>
This commit is contained in:
Jason Froehlich
2023-12-15 16:01:55 -05:00
committed by blam
parent 086294bda1
commit 9e64c026ef
4 changed files with 314 additions and 4 deletions
@@ -15,7 +15,11 @@
*/
import { Git, getVoidLogger } from '@backstage/backend-common';
import { commitAndPushRepo, initRepoAndPush } from './gitHelpers';
import {
commitAndPushRepo,
initRepoAndPush,
commitAndPushBranch,
} from './gitHelpers';
jest.mock('@backstage/backend-common', () => ({
Git: {
@@ -29,10 +33,14 @@ jest.mock('@backstage/backend-common', () => ({
fetch: jest.fn(),
addRemote: jest.fn(),
push: jest.fn(),
clone: jest.fn(),
}),
},
getVoidLogger: jest.requireActual('@backstage/backend-common').getVoidLogger,
}));
jest.mock('fs-extra', () => ({
cpSync: jest.fn(),
}));
const mockedGit = Git.fromAuth({
logger: getVoidLogger(),
@@ -303,3 +311,142 @@ describe('commitAndPushRepo', () => {
});
});
});
describe('commitAndPushBranch', () => {
afterEach(() => {
jest.clearAllMocks();
});
describe('with minimal parameters', () => {
beforeEach(async () => {
await commitAndPushBranch({
tempDir: '/tmp/repo/dir/',
dir: '/test/repo/dir/',
remoteUrl: 'git@github.com:test/repo.git',
auth: {
username: 'test-user',
password: 'test-password',
},
commitMessage: 'commit message',
gitAuthorInfo: {
name: 'Scaffolder',
email: 'scaffolder@backstage.io',
},
logger: getVoidLogger(),
});
});
it('clone the repo', () => {
expect(mockedGit.clone).toHaveBeenCalledWith({
url: 'git@github.com:test/repo.git',
dir: '/tmp/repo/dir/',
});
});
it('fetch the branches', () => {
expect(mockedGit.fetch).toHaveBeenCalledWith({
dir: '/tmp/repo/dir/',
});
});
it('checkout the branch', () => {
expect(mockedGit.checkout).toHaveBeenCalledWith({
dir: '/tmp/repo/dir/',
ref: 'master',
});
});
it('stages all files in the repo', () => {
expect(mockedGit.add).toHaveBeenCalledWith({
dir: '/tmp/repo/dir/',
filepath: '.',
});
});
it('creates an commit', () => {
expect(mockedGit.commit).toHaveBeenCalledWith({
dir: '/tmp/repo/dir/',
message: 'commit message',
author: {
name: 'Scaffolder',
email: 'scaffolder@backstage.io',
},
committer: {
name: 'Scaffolder',
email: 'scaffolder@backstage.io',
},
});
});
it('pushes to the remote', () => {
expect(mockedGit.push).toHaveBeenCalledWith({
dir: '/tmp/repo/dir/',
remote: 'origin',
remoteRef: 'refs/heads/master',
});
});
});
it('with token', async () => {
await commitAndPushBranch({
tempDir: '/tmp/repo/dir/',
dir: '/test/repo/dir/',
remoteUrl: 'git@github.com:test/repo.git',
auth: {
token: 'test-token',
},
commitMessage: 'commit message',
logger: getVoidLogger(),
});
expect(mockedGit.clone).toHaveBeenCalledWith({
dir: '/tmp/repo/dir/',
url: 'git@github.com:test/repo.git',
});
});
it('allows overriding the default branch', async () => {
await commitAndPushBranch({
tempDir: '/tmp/repo/dir/',
dir: '/test/repo/dir/',
branch: 'trunk',
remoteUrl: 'git@github.com:test/repo.git',
auth: {
username: 'test-user',
password: 'test-password',
},
commitMessage: 'commit message',
logger: getVoidLogger(),
});
expect(mockedGit.checkout).toHaveBeenCalledWith({
dir: '/tmp/repo/dir/',
ref: 'trunk',
});
});
it('allows overriding the remoteRef', async () => {
await commitAndPushBranch({
tempDir: '/tmp/repo/dir/',
dir: '/test/repo/dir/',
gitAuthorInfo: {
name: 'Custom Scaffolder Author',
email: 'scaffolder@example.org',
},
remoteUrl: 'git@github.com:test/repo.git',
auth: {
username: 'test-user',
password: 'test-password',
},
remoteRef: 'ABC123',
commitMessage: 'commit message',
logger: getVoidLogger(),
});
expect(mockedGit.push).toHaveBeenCalledWith({
dir: '/tmp/repo/dir/',
remote: 'origin',
remoteRef: 'ABC123',
});
});
});
@@ -16,6 +16,7 @@
import { Git } from '@backstage/backend-common';
import { Logger } from 'winston';
import fs from 'fs-extra';
/**
* @public
@@ -134,3 +135,68 @@ export async function commitAndPushRepo(input: {
return { commitHash };
}
export async function commitAndPushBranch({
tempDir,
dir,
remoteUrl,
auth,
logger,
commitMessage,
gitAuthorInfo,
branch = 'master',
remoteRef,
}: {
tempDir: string;
dir: string;
remoteUrl: string;
// For use cases where token has to be used with Basic Auth
// it has to be provided as password together with a username
// which may be a fixed value defined by the provider.
auth: { username: string; password: string } | { token: string };
logger: Logger;
commitMessage: string;
gitAuthorInfo?: { name?: string; email?: string };
branch?: string;
remoteRef?: string;
}): Promise<{ commitHash: string }> {
const git = Git.fromAuth({
...auth,
logger,
});
await git.clone({ url: remoteUrl, dir: tempDir });
await git.fetch({ dir: tempDir });
await git.checkout({ dir: tempDir, ref: branch });
// copy files
fs.cpSync(dir, tempDir, {
recursive: true,
filter: path => {
return !(path.indexOf('.git') > -1);
},
});
await git.add({ dir: tempDir, filepath: '.' });
// use provided info if possible, otherwise use fallbacks
const authorInfo = {
name: gitAuthorInfo?.name ?? 'Scaffolder',
email: gitAuthorInfo?.email ?? 'scaffolder@backstage.io',
};
const commitHash = await git.commit({
dir: tempDir,
message: commitMessage,
author: authorInfo,
committer: authorInfo,
});
await git.push({
dir: tempDir,
remote: 'origin',
remoteRef: remoteRef ?? `refs/heads/${branch}`,
});
return { commitHash };
}
+5 -1
View File
@@ -25,5 +25,9 @@ export {
} from './executeShellCommand';
export { fetchContents, fetchFile } from './fetch';
export { type ActionContext, type TemplateAction } from './types';
export { initRepoAndPush, commitAndPushRepo } from './gitHelpers';
export {
initRepoAndPush,
commitAndPushRepo,
commitAndPushBranch,
} from './gitHelpers';
export { parseRepoUrl, getRepoSourceDirectory } from './util';