Checks if git is installed and has a user configured

Signed-off-by: Leonardo Maier <leonarmaier@gmail.com>
This commit is contained in:
Leonardo Maier
2022-09-20 17:31:40 -03:00
parent 7c6306fc8a
commit 099ce48eef
5 changed files with 118 additions and 46 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
---
'@backstage/create-app': minor
'@backstage/create-app': patch
---
Initializes a git repository when creating an app using @packages/create-app
@@ -33,6 +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 createTemporaryAppFolderMock = jest.spyOn(
tasks,
'createTemporaryAppFolderTask',
@@ -57,6 +58,7 @@ describe('command entrypoint', () => {
name: 'MyApp',
dbType: 'PostgreSQL',
});
checkForGitSetup.mockResolvedValue(true);
});
afterEach(() => {
@@ -88,4 +90,11 @@ describe('command entrypoint', () => {
await createApp(cmd);
expect(buildAppMock).not.toHaveBeenCalled();
});
it('should not call `initGitRepository` when `isGitConfigured` is false', async () => {
const cmd = {} as unknown as Command;
checkForGitSetup.mockResolvedValue(false);
await createApp(cmd);
expect(initGitRepositoryMock).not.toHaveBeenCalled();
});
});
+38 -28
View File
@@ -29,37 +29,41 @@ import {
moveAppTask,
templatingTask,
initGitRepository,
checkForGitSetup,
} from './lib/tasks';
export default async (opts: OptionValues): Promise<void> => {
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
const answers: Answers = await inquirer.prompt([
{
type: 'input',
name: 'name',
message: chalk.blue('Enter a name for the app [required]'),
validate: (value: any) => {
if (!value) {
return chalk.red('Please enter a name for the app');
} else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) {
return chalk.red(
'App name must be lowercase and contain only letters, digits, and dashes.',
);
}
return true;
const answers: Answers = await inquirer.prompt(
[
{
type: 'input',
name: 'name',
message: chalk.blue('Enter a name for the app [required]'),
validate: (value: any) => {
if (!value) {
return chalk.red('Please enter a name for the app');
} else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) {
return chalk.red(
'App name must be lowercase and contain only letters, digits, and dashes.',
);
}
return true;
},
when: (a: Answers) => {
const envName = process.env.BACKSTAGE_APP_NAME;
if (envName) {
a.name = envName;
return false;
}
return true;
},
},
when: (a: Answers) => {
const envName = process.env.BACKSTAGE_APP_NAME;
if (envName) {
a.name = envName;
return false;
}
return true;
},
},
]);
],
{ defaultBranch: 'master' },
);
const templateDir = paths.resolveOwn('templates/default-app');
const tempDir = resolvePath(os.tmpdir(), answers.name);
@@ -74,14 +78,18 @@ export default async (opts: OptionValues): Promise<void> => {
Task.log('Creating the app...');
try {
const isGitConfigured = await checkForGitSetup();
if (opts.path) {
// Template directly to specified path
Task.section('Checking that supplied path exists');
await checkPathExistsTask(appDir);
Task.section('Initializing git repository');
await initGitRepository(appDir, answers);
if (isGitConfigured) {
Task.section('Initializing git repository');
await initGitRepository(appDir, answers);
}
Task.section('Preparing files');
await templatingTask(templateDir, opts.path, answers);
@@ -94,8 +102,10 @@ export default async (opts: OptionValues): Promise<void> => {
Task.section('Creating a temporary app directory');
await createTemporaryAppFolderTask(tempDir);
Task.section('Initializing git repository');
await initGitRepository(tempDir, answers);
if (isGitConfigured) {
Task.section('Initializing git repository');
await initGitRepository(tempDir, answers);
}
Task.section('Preparing files');
await templatingTask(templateDir, tempDir, answers);
+28
View File
@@ -22,6 +22,7 @@ import {
Task,
buildAppTask,
checkAppExistsTask,
checkForGitSetup,
checkPathExistsTask,
createTemporaryAppFolderTask,
initGitRepository,
@@ -348,6 +349,33 @@ describe('buildAppTask', () => {
});
});
describe('checkForGitSetup', () => {
it('should check if git package is installed and configured', async () => {
mockExec.mockImplementation((_command, callback) => {
callback(null, { stdout: 'main' }, 'standard error');
});
await checkForGitSetup();
expect(mockExec).toHaveBeenCalledTimes(3);
expect(mockExec).toHaveBeenNthCalledWith(
1,
'which git',
expect.any(Function),
);
expect(mockExec).toHaveBeenNthCalledWith(
2,
'git config user.name',
expect.any(Function),
);
expect(mockExec).toHaveBeenNthCalledWith(
3,
'git config user.email',
expect.any(Function),
);
});
});
describe('initGitRepository', () => {
it('should initialize a git repository at the given path', async () => {
const destinationDir = 'tmp/mockApp/';
+42 -17
View File
@@ -237,6 +237,33 @@ export async function moveAppTask(
});
}
/**
* Checks if git package is installed and git credentials exists
*
* @throws if `exec` fails
*/
export async function checkForGitSetup(): Promise<boolean> {
const runCmd = (cmd: string) =>
exec(cmd).catch(error => {
process.stdout.write(error.stderr);
process.stdout.write(error.stdout);
throw new Error(`Could not execute command ${chalk.cyan(cmd)}`);
});
try {
await runCmd('which git');
const [gitUsername, gitEmail] = await Promise.all([
runCmd('git config user.name'),
runCmd('git config user.email'),
]);
return Boolean(gitUsername.stdout?.trim() && gitEmail.stdout?.trim());
} catch (error) {
return false;
}
}
/**
* Initializes a git repository in the destination folder
*
@@ -245,25 +272,23 @@ export async function moveAppTask(
* @throws if `exec` fails
*/
export async function initGitRepository(dir: string, context: any) {
const runCmd = async (cmd: string) => {
let cmdResponse = { stdout: '', stderr: '' };
await Task.forItem('executing', cmd, async () => {
process.chdir(dir);
cmdResponse = await exec(cmd).catch(error => {
process.stdout.write(error.stderr);
process.stdout.write(error.stdout);
throw new Error(`Could not execute command ${chalk.cyan(cmd)}`);
});
const runCmd = (cmd: string) =>
exec(cmd).catch(error => {
process.stdout.write(error.stderr);
process.stdout.write(error.stdout);
throw new Error(`Could not execute command ${chalk.cyan(cmd)}`);
});
return cmdResponse;
};
await runCmd('git init');
await runCmd('git commit --allow-empty -m "Initial commit"');
await Task.forItem('init', 'git repository', async () => {
process.chdir(dir);
const defaultBranch = await runCmd('git branch --format="%(refname:short)"');
await runCmd('git init');
await runCmd('git commit --allow-empty -m "Initial commit"');
context.defaultBranch = defaultBranch.stdout
? defaultBranch.stdout.trim()
: 'master';
const defaultBranch = await runCmd(
'git branch --format="%(refname:short)"',
);
context.defaultBranch = defaultBranch.stdout?.trim() || 'master';
});
}