Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 27 additions & 12 deletions packages/utils/src/lib/git/git.int.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { mkdir, rm, stat, writeFile } from 'node:fs/promises';
import { rm, stat, writeFile } from 'node:fs/promises';
import path from 'node:path';
import { type SimpleGit, simpleGit } from 'simple-git';
import { initGitRepo, teardownTestFolder } from '@code-pushup/test-utils';
import {
initGitRepoWithRemote,
teardownTestFolder,
} from '@code-pushup/test-utils';
import { toUnixPath } from '../transform.js';
import {
getGitDefaultBranch,
getGitRoot,
guardAgainstLocalChanges,
safeCheckout,
Expand All @@ -12,11 +16,14 @@ import {

describe('git utils in a git repo', () => {
const baseDir = path.join(process.cwd(), 'tmp', 'git-tests');
const repoDir = path.join(baseDir, 'repo');
let emptyGit: SimpleGit;

beforeAll(async () => {
await mkdir(baseDir, { recursive: true });
emptyGit = await initGitRepo(simpleGit, { baseDir, baseBranch: 'master' });
emptyGit = await initGitRepoWithRemote(simpleGit, {
baseDir,
baseBranch: 'master',
});
});

afterAll(async () => {
Expand All @@ -25,13 +32,15 @@ describe('git utils in a git repo', () => {

describe('without a branch and commits', () => {
it('getGitRoot should return git root in a set up repo', async () => {
await expect(getGitRoot(emptyGit)).resolves.toMatch(/tmp\/git-tests$/);
await expect(getGitRoot(emptyGit)).resolves.toMatch(
/tmp\/git-tests\/repo$/,
);
});
});

describe('with a branch and commits clean', () => {
beforeAll(async () => {
await writeFile(path.join(baseDir, 'README.md'), '# hello-world\n');
await writeFile(path.join(repoDir, 'README.md'), '# hello-world\n');
await emptyGit.add('README.md');
await emptyGit.commit('Create README');

Expand All @@ -45,24 +54,24 @@ describe('git utils in a git repo', () => {
});

it('should find Git root', async () => {
await expect(getGitRoot(emptyGit)).resolves.toBe(toUnixPath(baseDir));
await expect(getGitRoot(emptyGit)).resolves.toBe(toUnixPath(repoDir));
});

it('should convert absolute path to relative Git path', async () => {
await expect(
toGitPath(path.join(baseDir, 'src', 'utils.ts'), emptyGit),
toGitPath(path.join(repoDir, 'src', 'utils.ts'), emptyGit),
).resolves.toBe('src/utils.ts');
});

it('should convert relative Windows path to relative Git path', async () => {
await expect(
toGitPath(String.raw`Backend\API\Startup.cs`, emptyGit),
).resolves.toBe('../../Backend/API/Startup.cs');
).resolves.toBe('../../../Backend/API/Startup.cs');
});

it('should keep relative Unix path as is (already a Git path)', async () => {
await expect(toGitPath('Backend/API/Startup.cs', emptyGit)).resolves.toBe(
'../../Backend/API/Startup.cs',
'../../../Backend/API/Startup.cs',
);
});

Expand All @@ -89,10 +98,10 @@ describe('git utils in a git repo', () => {
});

describe('with a branch and commits dirty', () => {
const newFilePath = path.join(baseDir, 'new-file.md');
const newFilePath = path.join(repoDir, 'new-file.md');

beforeAll(async () => {
await writeFile(path.join(baseDir, 'README.md'), '# hello-world\n');
await writeFile(path.join(repoDir, 'README.md'), '# hello-world\n');
await emptyGit.add('README.md');
await emptyGit.commit('Create README');

Expand Down Expand Up @@ -179,4 +188,10 @@ describe('git utils in a git repo', () => {
);
});
});

describe('getGitDefaultBranch', () => {
it('should resolve the default branch name from origin/HEAD', async () => {
await expect(getGitDefaultBranch(emptyGit)).resolves.toBe('master');
});
});
});
2 changes: 1 addition & 1 deletion packages/utils/src/lib/git/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function getGitRoot(git = simpleGit()): Promise<string> {

export async function getGitDefaultBranch(git = simpleGit()): Promise<string> {
try {
const head = await git.revparse('--abbrev-ref origin/HEAD');
const head = await git.revparse(['--abbrev-ref', 'origin/HEAD']);
return head.replace(/^origin\//, '');
} catch (error) {
logger.warn(
Expand Down
41 changes: 37 additions & 4 deletions testing/test-utils/src/lib/utils/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,50 @@ export async function initGitRepo(
baseBranch?: string;
},
): Promise<SimpleGit> {
const { baseDir, config, baseBranch } = opt;
const { email = 'john.doe@example.com', name = 'John Doe' } = config ?? {};
const { baseDir, baseBranch } = opt;
await mkdir(baseDir, { recursive: true });
const git = simpleGit(baseDir);
await git.init();
await git.branch(['-M', baseBranch ?? 'main']);
await configureGitUser(git, opt.config);
return git;
}

/** Like {@link initGitRepo}, but with a simulated remote origin. Working directory is `<baseDir>/repo`. */
export async function initGitRepoWithRemote(
simpleGit: SimpleGitFactory,
opt: {
baseDir: string;
config?: GitConfig;
baseBranch?: string;
},
): Promise<SimpleGit> {
const { baseDir, baseBranch = 'main' } = opt;
const originDir = path.join(baseDir, 'origin.git');
const repoDir = path.join(baseDir, 'repo');

await mkdir(originDir, { recursive: true });
await simpleGit(originDir).init(true, ['--initial-branch', baseBranch]);
await simpleGit(baseDir).clone(originDir, repoDir);

const git = simpleGit(repoDir);
await configureGitUser(git, opt.config);
await commitFile(git, { baseDir: repoDir });
await git.push('origin', baseBranch);
await git.remote(['set-head', 'origin', baseBranch]);

return git;
}

async function configureGitUser(
git: SimpleGit,
config?: GitConfig,
): Promise<void> {
const { email = 'john.doe@example.com', name = 'John Doe' } = config ?? {};
await git.addConfig('user.name', name);
await git.addConfig('user.email', email);
await git.addConfig('commit.gpgSign', 'false');
await git.addConfig('tag.gpgSign', 'false');
await git.branch(['-M', baseBranch ?? 'main']);
return git;
}

export async function commitFile(
Expand Down
Loading