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:
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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';
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user