diff --git a/package.json b/package.json index c6ba5dbb9b..8bbd1207fb 100644 --- a/package.json +++ b/package.json @@ -106,6 +106,7 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.3", "@types/react-router-dom": "5.1.7", + "@types/semver": "^7.3.8", "@types/tmp": "^0.2.0", "@types/yargs": "^15.0.5", "@typescript-eslint/eslint-plugin": "~4.31.1", diff --git a/packages/workspace/src/generators/init/init.ts b/packages/workspace/src/generators/init/init.ts index 6afc73450c..c79b789fde 100755 --- a/packages/workspace/src/generators/init/init.ts +++ b/packages/workspace/src/generators/init/init.ts @@ -24,6 +24,7 @@ import { readFileSync } from 'fs'; import { basename } from 'path'; import { serializeJson } from '../../utilities/fileutils'; import { resolveUserExistingPrettierConfig } from '../../utilities/prettier'; +import { deduceDefaultBase } from '../../utilities/default-base'; import { angularCliVersion, nxVersion, @@ -426,7 +427,7 @@ async function createAdditionalFiles(host: Tree, options: Schema) { serializeJson({ npmScope: options.npmScope, affected: { - defaultBase: `${options.defaultBase}` || 'master', + defaultBase: `${options.defaultBase}` || deduceDefaultBase(), }, implicitDependencies: { 'angular.json': '*', diff --git a/packages/workspace/src/generators/init/schema.json b/packages/workspace/src/generators/init/schema.json index 1a92b1e724..cc8c62fcd0 100644 --- a/packages/workspace/src/generators/init/schema.json +++ b/packages/workspace/src/generators/init/schema.json @@ -12,8 +12,7 @@ }, "defaultBase": { "type": "string", - "description": "Default base branch for affected.", - "default": "master" + "description": "Default base branch for affected." }, "skipInstall": { "type": "boolean", diff --git a/packages/workspace/src/generators/new/new.ts b/packages/workspace/src/generators/new/new.ts index 30f207a5bb..855ba26067 100644 --- a/packages/workspace/src/generators/new/new.ts +++ b/packages/workspace/src/generators/new/new.ts @@ -16,9 +16,15 @@ import { join } from 'path'; import * as yargsParser from 'yargs-parser'; import { spawn, SpawnOptions } from 'child_process'; +import { gte } from 'semver'; + import { workspaceGenerator } from '../workspace/workspace'; import { nxVersion } from '../../utils/versions'; import { Preset } from '../utils/presets'; +import { + checkGitVersion, + deduceDefaultBase, +} from '../../utilities/default-base'; export interface Schema { cli: 'nx' | 'angular'; @@ -110,11 +116,8 @@ async function initializeGitRepo( }); }); }; - const hasCommand = await execute(['--version']).then( - () => true, - () => false - ); - if (!hasCommand) { + const gitVersion = checkGitVersion(); + if (!gitVersion) { return; } const insideRepo = await execute( @@ -130,7 +133,13 @@ async function initializeGitRepo( ); return; } - await execute(['init']); + 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]); + } await execute(['add', '.']); if (options.commit) { const message = options.commit.message || 'initial commit'; diff --git a/packages/workspace/src/generators/new/schema.json b/packages/workspace/src/generators/new/schema.json index b7c4b59255..57be2c518f 100644 --- a/packages/workspace/src/generators/new/schema.json +++ b/packages/workspace/src/generators/new/schema.json @@ -31,8 +31,7 @@ }, "defaultBase": { "type": "string", - "description": "Default base branch for affected.", - "default": "master" + "description": "Default base branch for affected." }, "skipInstall": { "description": "Skip installing dependency packages.", diff --git a/packages/workspace/src/generators/workspace/schema.json b/packages/workspace/src/generators/workspace/schema.json index abd850a19f..f31c96f435 100644 --- a/packages/workspace/src/generators/workspace/schema.json +++ b/packages/workspace/src/generators/workspace/schema.json @@ -54,8 +54,7 @@ }, "defaultBase": { "type": "string", - "description": "Default base branch for affected.", - "default": "master" + "description": "Default base branch for affected." }, "skipInstall": { "description": "Skip installing dependency packages.", diff --git a/packages/workspace/src/generators/workspace/workspace.ts b/packages/workspace/src/generators/workspace/workspace.ts index 68478080c6..7c567d74b7 100644 --- a/packages/workspace/src/generators/workspace/workspace.ts +++ b/packages/workspace/src/generators/workspace/workspace.ts @@ -18,6 +18,7 @@ import { readFileSync } from 'fs'; import { join, join as pathJoin } from 'path'; import { reformattedWorkspaceJsonOrNull } from '@nrwl/tao/src/shared/workspace'; import { Preset } from '../utils/presets'; +import { deduceDefaultBase } from '../../utilities/default-base'; export const DEFAULT_NRWL_PRETTIER_CONFIG = { singleQuote: true, @@ -130,6 +131,7 @@ export async function workspaceGenerator(host: Tree, options: Schema) { if (!options.name) { throw new Error(`Invalid options, "name" is required.`); } + options = normalizeOptions(options); createFiles(host, options); createPrettierrc(host, options); if (options.cli === 'angular') { @@ -155,3 +157,11 @@ function addPropertyWithStableKeys(obj: any, key: string, value: string) { obj[k] = copy[k]; }); } + +function normalizeOptions(options: Schema) { + let defaultBase = options.defaultBase || deduceDefaultBase(); + return { + ...options, + defaultBase, + }; +} diff --git a/packages/workspace/src/utilities/default-base.spec.ts b/packages/workspace/src/utilities/default-base.spec.ts new file mode 100644 index 0000000000..4f069a176f --- /dev/null +++ b/packages/workspace/src/utilities/default-base.spec.ts @@ -0,0 +1,57 @@ +import * as cp from 'child_process'; +import { checkGitVersion, deduceDefaultBase } from './default-base'; + +describe('deduceDefaultBase', () => { + const execSyncSpy = jest.spyOn(cp, 'execSync'); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('should work when not set', () => { + execSyncSpy.mockReturnValue(Buffer.from('')); + const result = deduceDefaultBase(); + expect(result).toEqual('master'); + }); + + it('should work when set', () => { + execSyncSpy.mockReturnValue(Buffer.from('main')); + const result = deduceDefaultBase(); + expect(result).toEqual('main'); + }); + + it('should work with extra line terminators', () => { + execSyncSpy.mockReturnValue( + Buffer.from(`main + `) + ); + const result = deduceDefaultBase(); + expect(result).toEqual('main'); + }); +}); + +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'); + }); +}); diff --git a/packages/workspace/src/utilities/default-base.ts b/packages/workspace/src/utilities/default-base.ts new file mode 100644 index 0000000000..edc9d9ba5b --- /dev/null +++ b/packages/workspace/src/utilities/default-base.ts @@ -0,0 +1,23 @@ +import { execSync } from 'child_process'; + +// TODO (v13): Update to main +export function deduceDefaultBase(): string { + const nxDefaultBase = 'master'; + try { + return ( + execSync('git config --get init.defaultBranch').toString().trim() || + nxDefaultBase + ); + } catch { + 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; + } +} diff --git a/yarn.lock b/yarn.lock index 08c104a896..52fffcb1f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6291,6 +6291,11 @@ resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz#50bea0c3c2acc31c959c5b1e747798b3b3d06d4b" integrity sha512-tGomyEuzSC1H28y2zlW6XPCaDaXFaD6soTdb4GNdmte2qfHtrKqhy0ZFs4r/1hpazCfEZqeTSRLvSasmEx89uw== +"@types/semver@^7.3.8": + version "7.3.8" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.8.tgz#508a27995498d7586dcecd77c25e289bfaf90c59" + integrity sha512-D/2EJvAlCEtYFEYmmlGwbGXuK886HzyCc3nZX/tkFTQdEU8jZDAgiv08P162yB17y4ZXZoq7yFAnW4GDBb9Now== + "@types/serve-static@*": version "1.13.9" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.9.tgz#aacf28a85a05ee29a11fb7c3ead935ac56f33e4e"