cli-node: refactor git into GitUtils
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { runGit, listChangedFiles } from './git';
|
||||
import { runGit, GitUtils } from './GitUtils';
|
||||
|
||||
describe('runGit', () => {
|
||||
it('runs a git command', async () => {
|
||||
@@ -43,10 +43,14 @@ describe('runGit', () => {
|
||||
|
||||
describe('listChangedFiles', () => {
|
||||
it('requires a ref', async () => {
|
||||
await expect(listChangedFiles('')).rejects.toThrow('ref is required');
|
||||
await expect(GitUtils.listChangedFiles('')).rejects.toThrow(
|
||||
'ref is required',
|
||||
);
|
||||
});
|
||||
|
||||
it('should return something', async () => {
|
||||
await expect(listChangedFiles('HEAD')).resolves.toEqual(expect.any(Array));
|
||||
await expect(GitUtils.listChangedFiles('HEAD')).resolves.toEqual(
|
||||
expect.any(Array),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -15,11 +15,8 @@
|
||||
*/
|
||||
|
||||
import { assertError, ForwardedError } from '@backstage/errors';
|
||||
import { execFile as execFileCb } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
import { paths } from '../paths';
|
||||
|
||||
const execFile = promisify(execFileCb);
|
||||
import { execFile } from '../run';
|
||||
|
||||
/**
|
||||
* Run a git command, trimming the output splitting it into lines.
|
||||
@@ -43,44 +40,55 @@ export async function runGit(...args: string[]) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sorted list of all files that have changed since the merge base
|
||||
* of the provided `ref` and HEAD, as well as all files that are not tracked by git.
|
||||
* Utilities for working with git.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export async function listChangedFiles(ref: string) {
|
||||
if (!ref) {
|
||||
throw new Error('ref is required');
|
||||
export class GitUtils {
|
||||
/**
|
||||
* Returns a sorted list of all files that have changed since the merge base
|
||||
* of the provided `ref` and HEAD, as well as all files that are not tracked by git.
|
||||
*/
|
||||
static async listChangedFiles(ref: string) {
|
||||
if (!ref) {
|
||||
throw new Error('ref is required');
|
||||
}
|
||||
|
||||
let diffRef = ref;
|
||||
try {
|
||||
const [base] = await runGit('merge-base', 'HEAD', ref);
|
||||
diffRef = base;
|
||||
} catch {
|
||||
// silently fall back to using the ref directly if merge base is not available
|
||||
}
|
||||
|
||||
const tracked = await runGit('diff', '--name-only', diffRef);
|
||||
const untracked = await runGit(
|
||||
'ls-files',
|
||||
'--others',
|
||||
'--exclude-standard',
|
||||
);
|
||||
|
||||
return Array.from(new Set([...tracked, ...untracked]));
|
||||
}
|
||||
|
||||
let diffRef = ref;
|
||||
try {
|
||||
const [base] = await runGit('merge-base', 'HEAD', ref);
|
||||
diffRef = base;
|
||||
} catch {
|
||||
// silently fall back to using the ref directly if merge base is not available
|
||||
/**
|
||||
* Returns the contents of a file at a specific ref.
|
||||
*/
|
||||
static async readFileAtRef(path: string, ref: string) {
|
||||
let showRef = ref;
|
||||
try {
|
||||
const [base] = await runGit('merge-base', 'HEAD', ref);
|
||||
showRef = base;
|
||||
} catch {
|
||||
// silently fall back to using the ref directly if merge base is not available
|
||||
}
|
||||
|
||||
const { stdout } = await execFile('git', ['show', `${showRef}:${path}`], {
|
||||
shell: true,
|
||||
cwd: paths.targetRoot,
|
||||
maxBuffer: 1024 * 1024 * 50,
|
||||
});
|
||||
return stdout;
|
||||
}
|
||||
|
||||
const tracked = await runGit('diff', '--name-only', diffRef);
|
||||
const untracked = await runGit('ls-files', '--others', '--exclude-standard');
|
||||
|
||||
return Array.from(new Set([...tracked, ...untracked]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of a file at a specific ref.
|
||||
*/
|
||||
export async function readFileAtRef(path: string, ref: string) {
|
||||
let showRef = ref;
|
||||
try {
|
||||
const [base] = await runGit('merge-base', 'HEAD', ref);
|
||||
showRef = base;
|
||||
} catch {
|
||||
// silently fall back to using the ref directly if merge base is not available
|
||||
}
|
||||
|
||||
const { stdout } = await execFile('git', ['show', `${showRef}:${path}`], {
|
||||
shell: true,
|
||||
cwd: paths.targetRoot,
|
||||
maxBuffer: 1024 * 1024 * 50,
|
||||
});
|
||||
return stdout;
|
||||
}
|
||||
@@ -14,4 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { listChangedFiles, readFileAtRef } from './git';
|
||||
export { GitUtils } from './GitUtils';
|
||||
|
||||
@@ -19,3 +19,5 @@
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export * from './git';
|
||||
|
||||
@@ -18,16 +18,10 @@ import { resolve as resolvePath } from 'path';
|
||||
import { getPackages } from '@manypkg/get-packages';
|
||||
import { PackageGraph } from './PackageGraph';
|
||||
import { Lockfile } from './Lockfile';
|
||||
import { listChangedFiles, readFileAtRef } from '../git';
|
||||
import { GitUtils } from '../git';
|
||||
|
||||
jest.mock('../git');
|
||||
|
||||
const mockListChangedFiles = listChangedFiles as jest.MockedFunction<
|
||||
typeof listChangedFiles
|
||||
>;
|
||||
const mockReadFileAtRef = readFileAtRef as jest.MockedFunction<
|
||||
typeof readFileAtRef
|
||||
>;
|
||||
const mockListChangedFiles = jest.spyOn(GitUtils, 'listChangedFiles');
|
||||
const mockReadFileAtRef = jest.spyOn(GitUtils, 'readFileAtRef');
|
||||
|
||||
jest.mock('../paths', () => ({
|
||||
paths: {
|
||||
|
||||
@@ -18,7 +18,7 @@ import path from 'path';
|
||||
import { getPackages, Package } from '@manypkg/get-packages';
|
||||
import { paths } from '../paths';
|
||||
import { PackageRole } from '../role';
|
||||
import { listChangedFiles, readFileAtRef } from '../git';
|
||||
import { GitUtils } from '../git';
|
||||
import { Lockfile } from './Lockfile';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
|
||||
@@ -214,7 +214,7 @@ export class PackageGraph extends Map<string, PackageGraphNode> {
|
||||
ref: string;
|
||||
analyzeLockfile?: boolean;
|
||||
}) {
|
||||
const changedFiles = await listChangedFiles(options.ref);
|
||||
const changedFiles = await GitUtils.listChangedFiles(options.ref);
|
||||
|
||||
const dirMap = new Map(
|
||||
Array.from(this.values()).map(pkg => [
|
||||
@@ -265,7 +265,7 @@ export class PackageGraph extends Map<string, PackageGraphNode> {
|
||||
paths.resolveTargetRoot('yarn.lock'),
|
||||
);
|
||||
otherLockfile = Lockfile.parse(
|
||||
await readFileAtRef('yarn.lock', options.ref),
|
||||
await GitUtils.readFileAtRef('yarn.lock', options.ref),
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
|
||||
Reference in New Issue
Block a user