feat(core): move git commit from generators to create-nx-workspace (#11633)
* fix(core): move git commit to create-nx-workspace * fix(core): add git init to create-nx-plugin
This commit is contained in:
parent
657b2bff5a
commit
15d83258fe
@ -47,6 +47,26 @@ Choices: [nx, angular]
|
|||||||
|
|
||||||
CLI to power the Nx workspace
|
CLI to power the Nx workspace
|
||||||
|
|
||||||
|
### commit.email
|
||||||
|
|
||||||
|
Type: string
|
||||||
|
|
||||||
|
E-mail of the committer
|
||||||
|
|
||||||
|
### commit.message
|
||||||
|
|
||||||
|
Type: string
|
||||||
|
|
||||||
|
Default: Initial commit
|
||||||
|
|
||||||
|
Commit message
|
||||||
|
|
||||||
|
### commit.name
|
||||||
|
|
||||||
|
Type: string
|
||||||
|
|
||||||
|
Name of the committer
|
||||||
|
|
||||||
### defaultBase
|
### defaultBase
|
||||||
|
|
||||||
Type: string
|
Type: string
|
||||||
@ -95,6 +115,14 @@ Type: string
|
|||||||
|
|
||||||
Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular", "angular-nest", "react", "react-express", "react-native", "next", "nest", "express"]. To build your own see https://nx.dev/packages/nx-plugin#preset
|
Customizes the initial content of your workspace. Default presets include: ["apps", "empty", "core", "npm", "ts", "web-components", "angular", "angular-nest", "react", "react-express", "react-native", "next", "nest", "express"]. To build your own see https://nx.dev/packages/nx-plugin#preset
|
||||||
|
|
||||||
|
### skipGit
|
||||||
|
|
||||||
|
Type: boolean
|
||||||
|
|
||||||
|
Default: false
|
||||||
|
|
||||||
|
Skip initializing a git repository.
|
||||||
|
|
||||||
### style
|
### style
|
||||||
|
|
||||||
Type: string
|
Type: string
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
"name": "create-nx-workspace",
|
"name": "create-nx-workspace",
|
||||||
"id": "create-nx-workspace",
|
"id": "create-nx-workspace",
|
||||||
"file": "generated/cli/create-nx-workspace",
|
"file": "generated/cli/create-nx-workspace",
|
||||||
"content": "---\ntitle: 'create-nx-workspace - CLI command'\ndescription: 'Create a new Nx workspace'\n---\n\n# create-nx-workspace\n\nCreate a new Nx workspace\n\n## Usage\n\n```bash\ncreate-nx-workspace [name] [options]\n```\n\nInstall `create-nx-workspace` globally to invoke the command directly, or use `npx create-nx-workspace`, `yarn create nx-workspace`, or `pnpx create-nx-workspace`.\n\n## Options\n\n### allPrompts\n\nType: boolean\n\nDefault: false\n\nShow all prompts\n\n### appName\n\nType: string\n\nThe name of the application when a preset with pregenerated app is selected\n\n### ci\n\nType: string\n\nChoices: [github, circleci, azure]\n\nGenerate a CI workflow file\n\n### cli\n\nType: string\n\nChoices: [nx, angular]\n\nCLI to power the Nx workspace\n\n### defaultBase\n\nType: string\n\nDefault: main\n\nDefault base to use for new projects\n\n### help\n\nType: boolean\n\nShow help\n\n### interactive\n\nType: boolean\n\nEnable interactive mode with presets\n\n### name\n\nType: string\n\nWorkspace name (e.g. org name)\n\n### nxCloud\n\nType: boolean\n\nSet up distributed caching using Nx Cloud (It's free and doesn't require registration.)\n\n### packageManager\n\nType: string\n\nChoices: [npm, pnpm, yarn]\n\nDefault: npm\n\nPackage manager to use\n\n### preset\n\nType: string\n\nCustomizes the initial content of your workspace. Default presets include: [\"apps\", \"empty\", \"core\", \"npm\", \"ts\", \"web-components\", \"angular\", \"angular-nest\", \"react\", \"react-express\", \"react-native\", \"next\", \"nest\", \"express\"]. To build your own see https://nx.dev/packages/nx-plugin#preset\n\n### style\n\nType: string\n\nStyle option to be used when a preset with pregenerated app is selected\n\n### version\n\nType: boolean\n\nShow version number\n"
|
"content": "---\ntitle: 'create-nx-workspace - CLI command'\ndescription: 'Create a new Nx workspace'\n---\n\n# create-nx-workspace\n\nCreate a new Nx workspace\n\n## Usage\n\n```bash\ncreate-nx-workspace [name] [options]\n```\n\nInstall `create-nx-workspace` globally to invoke the command directly, or use `npx create-nx-workspace`, `yarn create nx-workspace`, or `pnpx create-nx-workspace`.\n\n## Options\n\n### allPrompts\n\nType: boolean\n\nDefault: false\n\nShow all prompts\n\n### appName\n\nType: string\n\nThe name of the application when a preset with pregenerated app is selected\n\n### ci\n\nType: string\n\nChoices: [github, circleci, azure]\n\nGenerate a CI workflow file\n\n### cli\n\nType: string\n\nChoices: [nx, angular]\n\nCLI to power the Nx workspace\n\n### commit.email\n\nType: string\n\nE-mail of the committer\n\n### commit.message\n\nType: string\n\nDefault: Initial commit\n\nCommit message\n\n### commit.name\n\nType: string\n\nName of the committer\n\n### defaultBase\n\nType: string\n\nDefault: main\n\nDefault base to use for new projects\n\n### help\n\nType: boolean\n\nShow help\n\n### interactive\n\nType: boolean\n\nEnable interactive mode with presets\n\n### name\n\nType: string\n\nWorkspace name (e.g. org name)\n\n### nxCloud\n\nType: boolean\n\nSet up distributed caching using Nx Cloud (It's free and doesn't require registration.)\n\n### packageManager\n\nType: string\n\nChoices: [npm, pnpm, yarn]\n\nDefault: npm\n\nPackage manager to use\n\n### preset\n\nType: string\n\nCustomizes the initial content of your workspace. Default presets include: [\"apps\", \"empty\", \"core\", \"npm\", \"ts\", \"web-components\", \"angular\", \"angular-nest\", \"react\", \"react-express\", \"react-native\", \"next\", \"nest\", \"express\"]. To build your own see https://nx.dev/packages/nx-plugin#preset\n\n### skipGit\n\nType: boolean\n\nDefault: false\n\nSkip initializing a git repository.\n\n### style\n\nType: string\n\nStyle option to be used when a preset with pregenerated app is selected\n\n### version\n\nType: boolean\n\nShow version number\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "init",
|
"name": "init",
|
||||||
|
|||||||
@ -79,28 +79,6 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
"skipGit": {
|
|
||||||
"description": "Skip initializing a git repository.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"alias": "g"
|
|
||||||
},
|
|
||||||
"commit": {
|
|
||||||
"description": "Initial repository commit information.",
|
|
||||||
"oneOf": [
|
|
||||||
{ "type": "boolean" },
|
|
||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"name": { "type": "string" },
|
|
||||||
"email": { "type": "string", "format": "email" },
|
|
||||||
"message": { "type": "string" }
|
|
||||||
},
|
|
||||||
"required": ["name", "email"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"packageManager": {
|
"packageManager": {
|
||||||
"description": "The package manager used to install dependencies.",
|
"description": "The package manager used to install dependencies.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -342,28 +320,6 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
"skipGit": {
|
|
||||||
"description": "Skip initializing a git repository.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"alias": "g"
|
|
||||||
},
|
|
||||||
"commit": {
|
|
||||||
"description": "Initial repository commit information.",
|
|
||||||
"oneOf": [
|
|
||||||
{ "type": "boolean" },
|
|
||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"name": { "type": "string" },
|
|
||||||
"email": { "type": "string", "format": "email" },
|
|
||||||
"message": { "type": "string" }
|
|
||||||
},
|
|
||||||
"required": ["name", "email"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"preset": {
|
"preset": {
|
||||||
"description": "What to create in the new workspace.",
|
"description": "What to create in the new workspace.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|||||||
@ -13,6 +13,7 @@ describe('create-nx-plugin', () => {
|
|||||||
it('should be able to create a plugin repo and run plugin e2e', () => {
|
it('should be able to create a plugin repo and run plugin e2e', () => {
|
||||||
const wsName = uniq('ws-plugin');
|
const wsName = uniq('ws-plugin');
|
||||||
const pluginName = uniq('plugin');
|
const pluginName = uniq('plugin');
|
||||||
|
|
||||||
runCreatePlugin(wsName, {
|
runCreatePlugin(wsName, {
|
||||||
packageManager,
|
packageManager,
|
||||||
pluginName,
|
pluginName,
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import { execSync } from 'child_process';
|
|||||||
import { removeSync } from 'fs-extra';
|
import { removeSync } from 'fs-extra';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { dirSync } from 'tmp';
|
import { dirSync } from 'tmp';
|
||||||
import { showNxWarning } from './shared';
|
import { initializeGitRepo, showNxWarning } from './shared';
|
||||||
import {
|
import {
|
||||||
detectInvokedPackageManager,
|
detectInvokedPackageManager,
|
||||||
PackageManager,
|
PackageManager,
|
||||||
@ -123,17 +123,6 @@ function updateWorkspace(workspaceName: string) {
|
|||||||
removeSync(path.join(workspaceName, 'libs'));
|
removeSync(path.join(workspaceName, 'libs'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function commitChanges(workspaceName) {
|
|
||||||
execSync('git add .', {
|
|
||||||
cwd: workspaceName,
|
|
||||||
stdio: 'ignore',
|
|
||||||
});
|
|
||||||
execSync('git commit --amend --no-edit', {
|
|
||||||
cwd: workspaceName,
|
|
||||||
stdio: 'ignore',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function determineWorkspaceName(parsedArgs: any): Promise<string> {
|
function determineWorkspaceName(parsedArgs: any): Promise<string> {
|
||||||
const workspaceName: string = parsedArgs._[2];
|
const workspaceName: string = parsedArgs._[2];
|
||||||
|
|
||||||
@ -215,7 +204,8 @@ determineWorkspaceName(parsedArgs).then((workspaceName) => {
|
|||||||
createWorkspace(tmpDir, packageManager, parsedArgs, workspaceName);
|
createWorkspace(tmpDir, packageManager, parsedArgs, workspaceName);
|
||||||
updateWorkspace(workspaceName);
|
updateWorkspace(workspaceName);
|
||||||
createNxPlugin(workspaceName, pluginName, packageManager, parsedArgs);
|
createNxPlugin(workspaceName, pluginName, packageManager, parsedArgs);
|
||||||
commitChanges(workspaceName);
|
return initializeGitRepo(workspaceName).then(() => {
|
||||||
showNxWarning(workspaceName);
|
showNxWarning(workspaceName);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { execSync } from 'child_process';
|
import { execSync, spawn, SpawnOptions } from 'child_process';
|
||||||
import { output } from '@nrwl/devkit';
|
import { output } from '@nrwl/devkit';
|
||||||
|
|
||||||
export function showNxWarning(workspaceName: string) {
|
export function showNxWarning(workspaceName: string) {
|
||||||
@ -21,3 +21,86 @@ export function showNxWarning(workspaceName: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Because we don't want to depend on @nrwl/workspace
|
||||||
|
* we duplicate the helper functions from @nrwl/workspace in this file.
|
||||||
|
*/
|
||||||
|
export function deduceDefaultBase(): string {
|
||||||
|
const nxDefaultBase = 'main';
|
||||||
|
try {
|
||||||
|
return (
|
||||||
|
execSync('git config --get init.defaultBranch').toString().trim() ||
|
||||||
|
nxDefaultBase
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
return nxDefaultBase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkGitVersion(): string | null {
|
||||||
|
try {
|
||||||
|
let gitVersionOutput = execSync('git --version').toString().trim();
|
||||||
|
return gitVersionOutput.match(/[0-9]+\.[0-9]+\.+[0-9]+/)[0];
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Because we don't want to depend on create-nx-workspace
|
||||||
|
* we duplicate the helper functions from create-nx-workspace in this file.
|
||||||
|
*/
|
||||||
|
export async function initializeGitRepo(directory: string) {
|
||||||
|
const execute = (args: ReadonlyArray<string>, ignoreErrorStream = false) => {
|
||||||
|
const errorStream = ignoreErrorStream ? 'ignore' : process.stderr;
|
||||||
|
const spawnOptions: SpawnOptions = {
|
||||||
|
stdio: [process.stdin, 'ignore', errorStream],
|
||||||
|
shell: true,
|
||||||
|
cwd: directory,
|
||||||
|
env: process.env,
|
||||||
|
};
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
spawn('git', args, spawnOptions).on('close', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const gitVersion = checkGitVersion();
|
||||||
|
if (!gitVersion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const insideRepo = await execute(
|
||||||
|
['rev-parse', '--is-inside-work-tree'],
|
||||||
|
true
|
||||||
|
).then(
|
||||||
|
() => true,
|
||||||
|
() => false
|
||||||
|
);
|
||||||
|
if (insideRepo) {
|
||||||
|
output.log({
|
||||||
|
title:
|
||||||
|
'Directory is already under version control. Skipping initialization of git.',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const defaultBase = deduceDefaultBase();
|
||||||
|
const [gitMajor, gitMinor] = gitVersion.split('.');
|
||||||
|
|
||||||
|
if (+gitMajor > 2 || (+gitMajor === 2 && +gitMinor >= 28)) {
|
||||||
|
await execute(['init', '-b', defaultBase]);
|
||||||
|
} else {
|
||||||
|
await execute(['init']);
|
||||||
|
await execute(['checkout', '-b', defaultBase]); // Git < 2.28 doesn't support -b on git init.
|
||||||
|
}
|
||||||
|
await execute(['add', '.']);
|
||||||
|
const message = 'Initial commit';
|
||||||
|
await execute(['commit', `-m "${message}"`]);
|
||||||
|
output.log({
|
||||||
|
title: 'Successfully initialized git.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -21,6 +21,8 @@ import { getFileName, stringifyCollection } from './utils';
|
|||||||
import { yargsDecorator } from './decorator';
|
import { yargsDecorator } from './decorator';
|
||||||
import chalk = require('chalk');
|
import chalk = require('chalk');
|
||||||
import { ciList } from './ci';
|
import { ciList } from './ci';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { initializeGitRepo } from './git';
|
||||||
|
|
||||||
type Arguments = {
|
type Arguments = {
|
||||||
name: string;
|
name: string;
|
||||||
@ -33,6 +35,12 @@ type Arguments = {
|
|||||||
packageManager: PackageManager;
|
packageManager: PackageManager;
|
||||||
defaultBase: string;
|
defaultBase: string;
|
||||||
ci: string;
|
ci: string;
|
||||||
|
skipGit: boolean;
|
||||||
|
commit: {
|
||||||
|
message: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Preset {
|
enum Preset {
|
||||||
@ -121,7 +129,7 @@ export const commandsObject: yargs.Argv<Arguments> = yargs
|
|||||||
.wrap(yargs.terminalWidth())
|
.wrap(yargs.terminalWidth())
|
||||||
.parserConfiguration({
|
.parserConfiguration({
|
||||||
'strip-dashed': true,
|
'strip-dashed': true,
|
||||||
'dot-notation': false,
|
'dot-notation': true,
|
||||||
})
|
})
|
||||||
.command(
|
.command(
|
||||||
// this is the default and only command
|
// this is the default and only command
|
||||||
@ -187,6 +195,25 @@ export const commandsObject: yargs.Argv<Arguments> = yargs
|
|||||||
defaultDescription: 'main',
|
defaultDescription: 'main',
|
||||||
describe: chalk.dim`Default base to use for new projects`,
|
describe: chalk.dim`Default base to use for new projects`,
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
})
|
||||||
|
.option('skipGit', {
|
||||||
|
describe: chalk.dim`Skip initializing a git repository.`,
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
alias: 'g',
|
||||||
|
})
|
||||||
|
.option('commit.name', {
|
||||||
|
describe: chalk.dim`Name of the committer`,
|
||||||
|
type: 'string',
|
||||||
|
})
|
||||||
|
.option('commit.email', {
|
||||||
|
describe: chalk.dim`E-mail of the committer`,
|
||||||
|
type: 'string',
|
||||||
|
})
|
||||||
|
.option('commit.message', {
|
||||||
|
describe: chalk.dim`Commit message`,
|
||||||
|
type: 'string',
|
||||||
|
default: 'Initial commit',
|
||||||
}),
|
}),
|
||||||
async (argv: yargs.ArgumentsCamelCase<Arguments>) => {
|
async (argv: yargs.ArgumentsCamelCase<Arguments>) => {
|
||||||
await main(argv).catch((error) => {
|
await main(argv).catch((error) => {
|
||||||
@ -218,6 +245,8 @@ async function main(parsedArgs: yargs.Arguments<Arguments>) {
|
|||||||
packageManager,
|
packageManager,
|
||||||
defaultBase,
|
defaultBase,
|
||||||
ci,
|
ci,
|
||||||
|
skipGit,
|
||||||
|
commit,
|
||||||
} = parsedArgs;
|
} = parsedArgs;
|
||||||
|
|
||||||
output.log({
|
output.log({
|
||||||
@ -230,7 +259,11 @@ async function main(parsedArgs: yargs.Arguments<Arguments>) {
|
|||||||
|
|
||||||
const tmpDir = await createSandbox(packageManager);
|
const tmpDir = await createSandbox(packageManager);
|
||||||
|
|
||||||
await createApp(tmpDir, name, packageManager as PackageManager, {
|
const directory = await createApp(
|
||||||
|
tmpDir,
|
||||||
|
name,
|
||||||
|
packageManager as PackageManager,
|
||||||
|
{
|
||||||
...parsedArgs,
|
...parsedArgs,
|
||||||
cli,
|
cli,
|
||||||
preset,
|
preset,
|
||||||
@ -238,7 +271,8 @@ async function main(parsedArgs: yargs.Arguments<Arguments>) {
|
|||||||
style,
|
style,
|
||||||
nxCloud,
|
nxCloud,
|
||||||
defaultBase,
|
defaultBase,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
let nxCloudInstallRes;
|
let nxCloudInstallRes;
|
||||||
if (nxCloud) {
|
if (nxCloud) {
|
||||||
@ -255,6 +289,16 @@ async function main(parsedArgs: yargs.Arguments<Arguments>) {
|
|||||||
nxCloud && nxCloudInstallRes.code === 0
|
nxCloud && nxCloudInstallRes.code === 0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (!skipGit) {
|
||||||
|
try {
|
||||||
|
await initializeGitRepo(directory, { defaultBase, commit });
|
||||||
|
} catch (e) {
|
||||||
|
output.error({
|
||||||
|
title: 'Could not initialize git repository',
|
||||||
|
bodyLines: [e.message],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
showNxWarning(name);
|
showNxWarning(name);
|
||||||
pointToTutorialAndCourse(preset as Preset);
|
pointToTutorialAndCourse(preset as Preset);
|
||||||
@ -745,7 +789,7 @@ async function createApp(
|
|||||||
name: string,
|
name: string,
|
||||||
packageManager: PackageManager,
|
packageManager: PackageManager,
|
||||||
parsedArgs: any
|
parsedArgs: any
|
||||||
) {
|
): Promise<string> {
|
||||||
const { _, cli, ...restArgs } = parsedArgs;
|
const { _, cli, ...restArgs } = parsedArgs;
|
||||||
|
|
||||||
// Ensure to use packageManager for args
|
// Ensure to use packageManager for args
|
||||||
@ -760,7 +804,8 @@ async function createApp(
|
|||||||
|
|
||||||
const command = `new ${name} ${args} --collection=@nrwl/workspace/generators.json --cli=${cli}`;
|
const command = `new ${name} ${args} --collection=@nrwl/workspace/generators.json --cli=${cli}`;
|
||||||
|
|
||||||
let nxWorkspaceRoot = `"${process.cwd().replace(/\\/g, '/')}"`;
|
const workingDir = process.cwd().replace(/\\/g, '/');
|
||||||
|
let nxWorkspaceRoot = `"${workingDir}"`;
|
||||||
|
|
||||||
// If path contains spaces there is a problem in Windows for npm@6.
|
// If path contains spaces there is a problem in Windows for npm@6.
|
||||||
// In this case we have to escape the wrapping quotes.
|
// In this case we have to escape the wrapping quotes.
|
||||||
@ -791,6 +836,7 @@ async function createApp(
|
|||||||
} finally {
|
} finally {
|
||||||
workspaceSetupSpinner.stop();
|
workspaceSetupSpinner.stop();
|
||||||
}
|
}
|
||||||
|
return join(workingDir, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setupNxCloud(name: string, packageManager: PackageManager) {
|
async function setupNxCloud(name: string, packageManager: PackageManager) {
|
||||||
|
|||||||
28
packages/create-nx-workspace/bin/git.spec.ts
Normal file
28
packages/create-nx-workspace/bin/git.spec.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { checkGitVersion } from './git';
|
||||||
|
import * as cp from 'child_process';
|
||||||
|
|
||||||
|
describe('checkGitVersion', () => {
|
||||||
|
const execSyncSpy = jest.spyOn(cp, 'execSync');
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with text before semver', () => {
|
||||||
|
execSyncSpy.mockReturnValue(Buffer.from(`git version 2.33.0`));
|
||||||
|
const result = checkGitVersion();
|
||||||
|
expect(result).toEqual('2.33.0');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with 4 digit versions', () => {
|
||||||
|
execSyncSpy.mockReturnValue(Buffer.from(`git version 2.33.0.5`));
|
||||||
|
const result = checkGitVersion();
|
||||||
|
expect(result).toEqual('2.33.0');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with msysgit versions', () => {
|
||||||
|
execSyncSpy.mockReturnValue(Buffer.from(`git version 1.8.3.msysgit.0`));
|
||||||
|
const result = checkGitVersion();
|
||||||
|
expect(result).toEqual('1.8.3');
|
||||||
|
});
|
||||||
|
});
|
||||||
89
packages/create-nx-workspace/bin/git.ts
Normal file
89
packages/create-nx-workspace/bin/git.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import { execSync, spawn, SpawnOptions } from 'child_process';
|
||||||
|
import { deduceDefaultBase } from './default-base';
|
||||||
|
import { output } from './output';
|
||||||
|
|
||||||
|
export function checkGitVersion(): string | null {
|
||||||
|
try {
|
||||||
|
let gitVersionOutput = execSync('git --version').toString().trim();
|
||||||
|
return gitVersionOutput.match(/[0-9]+\.[0-9]+\.+[0-9]+/)[0];
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function initializeGitRepo(
|
||||||
|
directory: string,
|
||||||
|
options: {
|
||||||
|
defaultBase: string;
|
||||||
|
commit: { message: string; name: string; email: string };
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
const execute = (args: ReadonlyArray<string>, ignoreErrorStream = false) => {
|
||||||
|
const outputStream = 'ignore';
|
||||||
|
const errorStream = ignoreErrorStream ? 'ignore' : process.stderr;
|
||||||
|
const spawnOptions: SpawnOptions = {
|
||||||
|
stdio: [process.stdin, outputStream, errorStream],
|
||||||
|
shell: true,
|
||||||
|
cwd: directory,
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
...(options.commit.name
|
||||||
|
? {
|
||||||
|
GIT_AUTHOR_NAME: options.commit.name,
|
||||||
|
GIT_COMMITTER_NAME: options.commit.name,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
...(options.commit.email
|
||||||
|
? {
|
||||||
|
GIT_AUTHOR_EMAIL: options.commit.email,
|
||||||
|
GIT_COMMITTER_EMAIL: options.commit.email,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
spawn('git', args, spawnOptions).on('close', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const gitVersion = checkGitVersion();
|
||||||
|
if (!gitVersion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const insideRepo = await execute(
|
||||||
|
['rev-parse', '--is-inside-work-tree'],
|
||||||
|
true
|
||||||
|
).then(
|
||||||
|
() => true,
|
||||||
|
() => false
|
||||||
|
);
|
||||||
|
if (insideRepo) {
|
||||||
|
output.log({
|
||||||
|
title:
|
||||||
|
'Directory is already under version control. Skipping initialization of git.',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const defaultBase = options.defaultBase || deduceDefaultBase();
|
||||||
|
const [gitMajor, gitMinor] = gitVersion.split('.');
|
||||||
|
|
||||||
|
if (+gitMajor > 2 || (+gitMajor === 2 && +gitMinor >= 28)) {
|
||||||
|
await execute(['init', '-b', defaultBase]);
|
||||||
|
} else {
|
||||||
|
await execute(['init']);
|
||||||
|
await execute(['checkout', '-b', defaultBase]); // Git < 2.28 doesn't support -b on git init.
|
||||||
|
}
|
||||||
|
await execute(['add', '.']);
|
||||||
|
if (options.commit) {
|
||||||
|
const message = options.commit.message || 'initial commit';
|
||||||
|
await execute(['commit', `-m "${message}"`]);
|
||||||
|
}
|
||||||
|
output.log({
|
||||||
|
title: 'Successfully initialized git.',
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -8,7 +8,6 @@ const defaultOptions: Omit<Schema, 'name' | 'directory' | 'appName'> = {
|
|||||||
cli: 'nx',
|
cli: 'nx',
|
||||||
preset: Preset.Apps,
|
preset: Preset.Apps,
|
||||||
skipInstall: false,
|
skipInstall: false,
|
||||||
skipGit: false,
|
|
||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
defaultBase: 'main',
|
defaultBase: 'main',
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,17 +13,10 @@ import {
|
|||||||
|
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import * as yargsParser from 'yargs-parser';
|
import * as yargsParser from 'yargs-parser';
|
||||||
import { spawn, SpawnOptions } from 'child_process';
|
import { spawn } from 'child_process';
|
||||||
|
|
||||||
import { gte } from 'semver';
|
|
||||||
|
|
||||||
import { workspaceGenerator } from '../workspace/workspace';
|
import { workspaceGenerator } from '../workspace/workspace';
|
||||||
import { nxVersion } from '../../utils/versions';
|
import { nxVersion } from '../../utils/versions';
|
||||||
import { Preset } from '../utils/presets';
|
import { Preset } from '../utils/presets';
|
||||||
import {
|
|
||||||
checkGitVersion,
|
|
||||||
deduceDefaultBase,
|
|
||||||
} from '../../utilities/default-base';
|
|
||||||
import { getNpmPackageVersion } from '../utils/get-npm-package-version';
|
import { getNpmPackageVersion } from '../utils/get-npm-package-version';
|
||||||
|
|
||||||
export interface Schema {
|
export interface Schema {
|
||||||
@ -33,11 +26,9 @@ export interface Schema {
|
|||||||
appName: string;
|
appName: string;
|
||||||
npmScope?: string;
|
npmScope?: string;
|
||||||
skipInstall?: boolean;
|
skipInstall?: boolean;
|
||||||
skipGit?: boolean;
|
|
||||||
style?: string;
|
style?: string;
|
||||||
nxCloud?: boolean;
|
nxCloud?: boolean;
|
||||||
preset: string;
|
preset: string;
|
||||||
commit?: { name: string; email: string; message?: string };
|
|
||||||
defaultBase: string;
|
defaultBase: string;
|
||||||
linter: 'tslint' | 'eslint';
|
linter: 'tslint' | 'eslint';
|
||||||
packageManager?: PackageManager;
|
packageManager?: PackageManager;
|
||||||
@ -116,76 +107,6 @@ function generatePreset(host: Tree, opts: NormalizedSchema) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initializeGitRepo(
|
|
||||||
host: Tree,
|
|
||||||
rootDirectory: string,
|
|
||||||
options: Schema
|
|
||||||
) {
|
|
||||||
const execute = (args: ReadonlyArray<string>, ignoreErrorStream = false) => {
|
|
||||||
const outputStream = 'ignore';
|
|
||||||
const errorStream = ignoreErrorStream ? 'ignore' : process.stderr;
|
|
||||||
const spawnOptions: SpawnOptions = {
|
|
||||||
stdio: [process.stdin, outputStream, errorStream],
|
|
||||||
shell: true,
|
|
||||||
cwd: join(host.root, rootDirectory),
|
|
||||||
env: {
|
|
||||||
...process.env,
|
|
||||||
...(options.commit.name
|
|
||||||
? {
|
|
||||||
GIT_AUTHOR_NAME: options.commit.name,
|
|
||||||
GIT_COMMITTER_NAME: options.commit.name,
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
...(options.commit.email
|
|
||||||
? {
|
|
||||||
GIT_AUTHOR_EMAIL: options.commit.email,
|
|
||||||
GIT_COMMITTER_EMAIL: options.commit.email,
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
|
||||||
spawn('git', args, spawnOptions).on('close', (code) => {
|
|
||||||
if (code === 0) {
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
reject(code);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const gitVersion = checkGitVersion();
|
|
||||||
if (!gitVersion) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const insideRepo = await execute(
|
|
||||||
['rev-parse', '--is-inside-work-tree'],
|
|
||||||
true
|
|
||||||
).then(
|
|
||||||
() => true,
|
|
||||||
() => false
|
|
||||||
);
|
|
||||||
if (insideRepo) {
|
|
||||||
console.info(
|
|
||||||
`Directory is already under version control. Skipping initialization of git.`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const defaultBase = options.defaultBase || deduceDefaultBase();
|
|
||||||
if (gte(gitVersion, '2.28.0')) {
|
|
||||||
await execute(['init', '-b', defaultBase]);
|
|
||||||
} else {
|
|
||||||
await execute(['init']);
|
|
||||||
await execute(['checkout', '-b', defaultBase]); // Git < 2.28 doesn't support -b on git init.
|
|
||||||
}
|
|
||||||
await execute(['add', '.']);
|
|
||||||
if (options.commit) {
|
|
||||||
const message = options.commit.message || 'initial commit';
|
|
||||||
await execute(['commit', `-m "${message}"`]);
|
|
||||||
}
|
|
||||||
console.info('Successfully initialized git.');
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function newGenerator(host: Tree, options: Schema) {
|
export async function newGenerator(host: Tree, options: Schema) {
|
||||||
if (
|
if (
|
||||||
options.skipInstall &&
|
options.skipInstall &&
|
||||||
@ -229,15 +150,6 @@ export async function newGenerator(host: Tree, options: Schema) {
|
|||||||
return async () => {
|
return async () => {
|
||||||
installPackagesTask(host, false, options.directory, options.packageManager);
|
installPackagesTask(host, false, options.directory, options.packageManager);
|
||||||
await generatePreset(host, options);
|
await generatePreset(host, options);
|
||||||
if (!options.skipGit) {
|
|
||||||
try {
|
|
||||||
await initializeGitRepo(host, options.directory, options);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(
|
|
||||||
`Could not initialize git repository. Error: ${e.message}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -39,37 +39,6 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
"skipGit": {
|
|
||||||
"description": "Skip initializing a git repository.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"alias": "g"
|
|
||||||
},
|
|
||||||
"commit": {
|
|
||||||
"description": "Initial repository commit information.",
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"name": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "email"
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["name", "email"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"preset": {
|
"preset": {
|
||||||
"description": "What to create in the new workspace.",
|
"description": "What to create in the new workspace.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|||||||
@ -62,35 +62,6 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
"skipGit": {
|
|
||||||
"description": "Skip initializing a git repository.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"alias": "g"
|
|
||||||
},
|
|
||||||
"commit": {
|
|
||||||
"description": "Initial repository commit information.",
|
|
||||||
"oneOf": [
|
|
||||||
{ "type": "boolean" },
|
|
||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"name": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "email"
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["name", "email"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"packageManager": {
|
"packageManager": {
|
||||||
"description": "The package manager used to install dependencies.",
|
"description": "The package manager used to install dependencies.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import * as cp from 'child_process';
|
import * as cp from 'child_process';
|
||||||
import { checkGitVersion, deduceDefaultBase } from './default-base';
|
import { deduceDefaultBase } from './default-base';
|
||||||
|
|
||||||
describe('deduceDefaultBase', () => {
|
describe('deduceDefaultBase', () => {
|
||||||
const execSyncSpy = jest.spyOn(cp, 'execSync');
|
const execSyncSpy = jest.spyOn(cp, 'execSync');
|
||||||
@ -29,29 +29,3 @@ describe('deduceDefaultBase', () => {
|
|||||||
expect(result).toEqual('some-other-default-branch');
|
expect(result).toEqual('some-other-default-branch');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('checkGitVersion', () => {
|
|
||||||
const execSyncSpy = jest.spyOn(cp, 'execSync');
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
jest.resetAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work with text before semver', () => {
|
|
||||||
execSyncSpy.mockReturnValue(Buffer.from(`git version 2.33.0`));
|
|
||||||
const result = checkGitVersion();
|
|
||||||
expect(result).toEqual('2.33.0');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work with 4 digit versions', () => {
|
|
||||||
execSyncSpy.mockReturnValue(Buffer.from(`git version 2.33.0.5`));
|
|
||||||
const result = checkGitVersion();
|
|
||||||
expect(result).toEqual('2.33.0');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work with msysgit versions', () => {
|
|
||||||
execSyncSpy.mockReturnValue(Buffer.from(`git version 1.8.3.msysgit.0`));
|
|
||||||
const result = checkGitVersion();
|
|
||||||
expect(result).toEqual('1.8.3');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
@ -11,12 +11,3 @@ export function deduceDefaultBase(): string {
|
|||||||
return nxDefaultBase;
|
return nxDefaultBase;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkGitVersion(): string | null {
|
|
||||||
try {
|
|
||||||
let gitVersionOutput = execSync('git --version').toString().trim();
|
|
||||||
return gitVersionOutput.match(/[0-9]+\.[0-9]+\.+[0-9]+/)[0];
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user