Refactor tasks to read git config first and then initialize git repo

Signed-off-by: Leonardo Maier <leonarmaier@gmail.com>
This commit is contained in:
Leonardo Maier
2022-09-23 12:15:56 -03:00
parent c3498947c2
commit b57a21a7fc
4 changed files with 92 additions and 55 deletions
+8 -4
View File
@@ -33,7 +33,7 @@ const checkPathExistsMock = jest.spyOn(tasks, 'checkPathExistsTask');
const templatingMock = jest.spyOn(tasks, 'templatingTask');
const checkAppExistsMock = jest.spyOn(tasks, 'checkAppExistsTask');
const initGitRepositoryMock = jest.spyOn(tasks, 'initGitRepository');
const checkForGitSetup = jest.spyOn(tasks, 'checkForGitSetup');
const readGitConfig = jest.spyOn(tasks, 'readGitConfig');
const createTemporaryAppFolderMock = jest.spyOn(
tasks,
'createTemporaryAppFolderTask',
@@ -58,7 +58,11 @@ describe('command entrypoint', () => {
name: 'MyApp',
dbType: 'PostgreSQL',
});
checkForGitSetup.mockResolvedValue(true);
readGitConfig.mockResolvedValue({
name: 'git-user',
email: 'git-email',
defaultBranch: 'git-default-branch',
});
});
afterEach(() => {
@@ -91,9 +95,9 @@ describe('command entrypoint', () => {
expect(buildAppMock).not.toHaveBeenCalled();
});
it('should not call `initGitRepository` when `isGitConfigured` is false', async () => {
it('should not call `initGitRepository` when `gitConfig` is undefined', async () => {
const cmd = {} as unknown as Command;
checkForGitSetup.mockResolvedValue(false);
readGitConfig.mockResolvedValue(undefined);
await createApp(cmd);
expect(initGitRepositoryMock).not.toHaveBeenCalled();
});
+17 -14
View File
@@ -29,9 +29,11 @@ import {
moveAppTask,
templatingTask,
initGitRepository,
checkForGitSetup,
readGitConfig,
} from './lib/tasks';
const DEFAULT_BRANCH = 'master';
export default async (opts: OptionValues): Promise<void> => {
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
@@ -78,7 +80,7 @@ export default async (opts: OptionValues): Promise<void> => {
Task.log('Creating the app...');
try {
const isGitConfigured = await checkForGitSetup();
const gitConfig = await readGitConfig();
if (opts.path) {
// Template directly to specified path
@@ -86,13 +88,11 @@ export default async (opts: OptionValues): Promise<void> => {
Task.section('Checking that supplied path exists');
await checkPathExistsTask(appDir);
if (isGitConfigured) {
Task.section('Initializing git repository');
await initGitRepository(appDir, answers);
}
Task.section('Preparing files');
await templatingTask(templateDir, opts.path, answers);
await templatingTask(templateDir, opts.path, {
...answers,
defaultBranch: gitConfig?.defaultBranch ?? DEFAULT_BRANCH,
});
} else {
// Template to temporary location, and then move files
@@ -102,18 +102,21 @@ export default async (opts: OptionValues): Promise<void> => {
Task.section('Creating a temporary app directory');
await createTemporaryAppFolderTask(tempDir);
if (isGitConfigured) {
Task.section('Initializing git repository');
await initGitRepository(tempDir, answers);
}
Task.section('Preparing files');
await templatingTask(templateDir, tempDir, answers);
await templatingTask(templateDir, tempDir, {
...answers,
defaultBranch: gitConfig?.defaultBranch ?? DEFAULT_BRANCH,
});
Task.section('Moving to final location');
await moveAppTask(tempDir, appDir, answers.name);
}
if (gitConfig) {
Task.section('Initializing git repository');
await initGitRepository(appDir);
}
if (!opts.skipInstall) {
Task.section('Building the app');
await buildAppTask(appDir);
+21 -20
View File
@@ -27,7 +27,7 @@ import {
moveAppTask,
templatingTask,
initGitRepository,
checkForGitSetup,
readGitConfig,
} from './tasks';
const commandExists = jest.fn();
@@ -286,17 +286,22 @@ describe('tasks', () => {
});
});
describe('checkForGitSetup', () => {
it('should return true if git package is installed and git credentials are set', async () => {
describe('readGitConfig', () => {
it('should return git config if git package is installed and git credentials are set', async () => {
mockExec.mockImplementation((_command, callback) => {
callback(null, { stdout: 'main' }, 'standard error');
});
commandExists.mockResolvedValue(true);
const isGitConfigured = await checkForGitSetup();
const gitConfig = await readGitConfig();
expect(isGitConfigured).toBe(true);
expect(gitConfig).toBeTruthy();
expect(gitConfig).toEqual({
name: 'main',
email: 'main',
defaultBranch: 'main',
});
expect(mockExec).toHaveBeenCalledWith(
'git config user.name',
expect.any(Function),
@@ -305,14 +310,19 @@ describe('tasks', () => {
'git config user.email',
expect.any(Function),
);
expect(mockExec).toHaveBeenCalledWith('git init', expect.any(Function));
expect(mockExec).toHaveBeenCalledWith(
'git commit --allow-empty -m "Initial commit"',
expect.any(Function),
);
});
it('should return false if git package is not installed', async () => {
commandExists.mockResolvedValue(false);
const isGitConfigured = await checkForGitSetup();
const gitConfig = await readGitConfig();
expect(isGitConfigured).toBe(false);
expect(gitConfig).toBeUndefined();
});
it('should return false if git package is installed but git credentials are not set', async () => {
@@ -322,9 +332,9 @@ describe('tasks', () => {
commandExists.mockResolvedValue(true);
const isGitConfigured = await checkForGitSetup();
const gitConfig = await readGitConfig();
expect(isGitConfigured).toBe(false);
expect(gitConfig).toBeUndefined();
expect(mockExec).toHaveBeenCalledWith(
'git config user.name',
expect.any(Function),
@@ -339,18 +349,14 @@ describe('tasks', () => {
describe('initGitRepository', () => {
it('should initialize a git repository at the given path', async () => {
const destinationDir = 'tmp/mockApp/';
const context = {
defaultBranch: '',
};
mockExec.mockImplementation((_command, callback) => {
callback(null, { stdout: 'main' }, 'standard error');
});
await initGitRepository(destinationDir, context);
await initGitRepository(destinationDir);
expect(context.defaultBranch).toBe('main');
expect(mockExec).toHaveBeenCalledTimes(3);
expect(mockExec).toHaveBeenCalledTimes(2);
expect(mockExec).toHaveBeenNthCalledWith(
1,
'git init',
@@ -361,11 +367,6 @@ describe('tasks', () => {
'git commit --allow-empty -m "Initial commit"',
expect.any(Function),
);
expect(mockExec).toHaveBeenNthCalledWith(
3,
'git branch --format="%(refname:short)"',
expect.any(Function),
);
});
});
});
+46 -17
View File
@@ -29,10 +29,17 @@ import { exec as execCb } from 'child_process';
import { packageVersions } from './versions';
import { promisify } from 'util';
import commandExists from 'command-exists';
import os from 'os';
const TASK_NAME_MAX_LENGTH = 14;
const exec = promisify(execCb);
export type GitConfig = {
name?: string;
email?: string;
defaultBranch?: string;
};
export class Task {
static log(name: string = '') {
process.stdout.write(`${chalk.green(name)}\n`);
@@ -239,11 +246,13 @@ export async function moveAppTask(
}
/**
* Checks if git package is installed and git credentials exists
* Read git configs by creating a temp folder and initializing a repo
*
* @throws if `exec` fails
*/
export async function checkForGitSetup(): Promise<boolean> {
export async function readGitConfig(): Promise<GitConfig | undefined> {
const tempDir = resolvePath(os.tmpdir(), 'git-temp-dir');
const runCmd = (cmd: string) =>
exec(cmd).catch(error => {
process.stdout.write(error.stderr);
@@ -251,26 +260,52 @@ export async function checkForGitSetup(): Promise<boolean> {
throw new Error(`Could not execute command ${chalk.cyan(cmd)}`);
});
const gitCommandExists = await commandExists('git');
const isGitAvailable = await commandExists('git').catch(() => false);
if (!gitCommandExists) return false;
if (!isGitAvailable) return;
const [gitUsername, gitEmail] = await Promise.all([
runCmd('git config user.name'),
runCmd('git config user.email'),
]);
try {
await fs.mkdir(tempDir);
return Boolean(gitUsername.stdout?.trim() && gitEmail.stdout?.trim());
process.chdir(tempDir);
const [gitUsername, gitEmail] = await Promise.all([
runCmd('git config user.name'),
runCmd('git config user.email'),
]);
const gitCredentials = Boolean(
gitUsername.stdout?.trim() && gitEmail.stdout?.trim(),
);
if (!gitCredentials) return;
await runCmd('git init');
await runCmd('git commit --allow-empty -m "Initial commit"');
const gitDefaultBranch = await runCmd(
'git branch --format="%(refname:short)"',
);
return {
name: gitUsername.stdout?.trim(),
email: gitEmail.stdout?.trim(),
defaultBranch: gitDefaultBranch.stdout?.trim(),
};
} catch (error) {
throw new Error(`Failed to read git config, ${error}`);
} finally {
await fs.rm(tempDir, { recursive: true });
}
}
/**
* Initializes a git repository in the destination folder
*
* @param dir - source path to initialize git repository in
* @param context - template parameters
* @throws if `exec` fails
*/
export async function initGitRepository(dir: string, context: any) {
export async function initGitRepository(dir: string) {
const runCmd = (cmd: string) =>
exec(cmd).catch(error => {
process.stdout.write(error.stderr);
@@ -283,11 +318,5 @@ export async function initGitRepository(dir: string, context: any) {
await runCmd('git init');
await runCmd('git commit --allow-empty -m "Initial commit"');
const defaultBranch = await runCmd(
'git branch --format="%(refname:short)"',
);
context.defaultBranch = defaultBranch.stdout?.trim() || 'master';
});
}