feat(nx-cloud): new cloud onboarding flow (#26262)
New Nx cloud onboarding flow.   
This commit is contained in:
parent
260562e484
commit
f8239debd0
@ -183,6 +183,14 @@ Type: `string`
|
||||
|
||||
Stylesheet type to be used with certain stacks
|
||||
|
||||
### useGitHub
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Default: `false`
|
||||
|
||||
Will you be using GitHub as your git hosting provider?
|
||||
|
||||
### version
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
@ -183,6 +183,14 @@ Type: `string`
|
||||
|
||||
Stylesheet type to be used with certain stacks
|
||||
|
||||
### useGitHub
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Default: `false`
|
||||
|
||||
Will you be using GitHub as your git hosting provider?
|
||||
|
||||
### version
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
@ -23,6 +23,11 @@
|
||||
"type": "boolean",
|
||||
"description": "Hide formatting logs",
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"github": {
|
||||
"type": "boolean",
|
||||
"description": "If the user will be using GitHub as their git hosting provider",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
||||
@ -13,12 +13,14 @@ import { yargsDecorator } from './decorator';
|
||||
import { getPackageNameFromThirdPartyPreset } from '../src/utils/preset/get-third-party-preset';
|
||||
import {
|
||||
determineDefaultBase,
|
||||
determineIfGitHubWillBeUsed,
|
||||
determineNxCloud,
|
||||
determinePackageManager,
|
||||
} from '../src/internal-utils/prompts';
|
||||
import {
|
||||
withAllPrompts,
|
||||
withGitOptions,
|
||||
withUseGitHub,
|
||||
withNxCloud,
|
||||
withOptions,
|
||||
withPackageManager,
|
||||
@ -183,6 +185,7 @@ export const commandsObject: yargs.Argv<Arguments> = yargs
|
||||
type: 'string',
|
||||
}),
|
||||
withNxCloud,
|
||||
withUseGitHub,
|
||||
withAllPrompts,
|
||||
withPackageManager,
|
||||
withGitOptions
|
||||
@ -280,13 +283,28 @@ async function normalizeArgsMiddleware(
|
||||
|
||||
const packageManager = await determinePackageManager(argv);
|
||||
const defaultBase = await determineDefaultBase(argv);
|
||||
if (process.env.NX_NEW_CLOUD_ONBOARDING === 'true') {
|
||||
const nxCloud =
|
||||
argv.skipGit === true ? 'skip' : await determineNxCloud(argv);
|
||||
const useGitHub =
|
||||
nxCloud === 'skip'
|
||||
? undefined
|
||||
: nxCloud === 'github' ||
|
||||
(await determineIfGitHubWillBeUsed(nxCloud));
|
||||
Object.assign(argv, {
|
||||
nxCloud,
|
||||
useGitHub,
|
||||
packageManager,
|
||||
defaultBase,
|
||||
});
|
||||
} else {
|
||||
const nxCloud = await determineNxCloud(argv);
|
||||
|
||||
Object.assign(argv, {
|
||||
nxCloud,
|
||||
packageManager,
|
||||
defaultBase,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
|
||||
@ -5,6 +5,7 @@ export interface CreateWorkspaceOptions {
|
||||
name: string; // Workspace name (e.g. org name)
|
||||
packageManager: PackageManager; // Package manager to use
|
||||
nxCloud: NxCloud; // Enable Nx Cloud
|
||||
useGitHub?: boolean; // Will you be using GitHub as your git hosting provider?
|
||||
/**
|
||||
* @description Enable interactive mode with presets
|
||||
* @default true
|
||||
|
||||
@ -21,6 +21,7 @@ export async function createWorkspace<T extends CreateWorkspaceOptions>(
|
||||
defaultBase = 'main',
|
||||
commit,
|
||||
cliName,
|
||||
useGitHub,
|
||||
} = options;
|
||||
|
||||
if (cliName) {
|
||||
@ -52,7 +53,12 @@ export async function createWorkspace<T extends CreateWorkspaceOptions>(
|
||||
|
||||
let nxCloudInstallRes;
|
||||
if (nxCloud !== 'skip') {
|
||||
nxCloudInstallRes = await setupNxCloud(directory, packageManager, nxCloud);
|
||||
nxCloudInstallRes = await setupNxCloud(
|
||||
directory,
|
||||
packageManager,
|
||||
nxCloud,
|
||||
useGitHub
|
||||
);
|
||||
|
||||
if (nxCloud !== 'yes') {
|
||||
await setupCI(
|
||||
|
||||
@ -22,6 +22,24 @@ export async function determineNxCloud(
|
||||
}
|
||||
}
|
||||
|
||||
export async function determineIfGitHubWillBeUsed(
|
||||
nxCloud: NxCloud
|
||||
): Promise<boolean> {
|
||||
if (nxCloud === 'yes' || nxCloud === 'circleci') {
|
||||
const reply = await enquirer.prompt<{ github: 'Yes' | 'No' }>([
|
||||
{
|
||||
name: 'github',
|
||||
message: 'Will you be using GitHub as your git hosting provider?',
|
||||
type: 'autocomplete',
|
||||
choices: [{ name: 'Yes' }, { name: 'No' }],
|
||||
initial: 0,
|
||||
},
|
||||
]);
|
||||
return reply.github === 'Yes';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function nxCloudPrompt(key: MessageKey): Promise<NxCloud> {
|
||||
const { message, choices, initial, fallback, footer, hint } =
|
||||
messages.getPrompt(key);
|
||||
|
||||
@ -15,6 +15,14 @@ export function withNxCloud<T = unknown>(argv: yargs.Argv<T>) {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function withUseGitHub<T = unknown>(argv: yargs.Argv<T>) {
|
||||
return argv.option('useGitHub', {
|
||||
describe: chalk.dim`Will you be using GitHub as your git hosting provider?`,
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
});
|
||||
}
|
||||
|
||||
export function withAllPrompts<T = unknown>(argv: yargs.Argv<T>) {
|
||||
return argv.option('allPrompts', {
|
||||
alias: 'a',
|
||||
|
||||
@ -9,15 +9,23 @@ export type NxCloud = 'yes' | 'github' | 'circleci' | 'skip';
|
||||
export async function setupNxCloud(
|
||||
directory: string,
|
||||
packageManager: PackageManager,
|
||||
nxCloud: NxCloud
|
||||
nxCloud: NxCloud,
|
||||
useGitHub?: boolean
|
||||
) {
|
||||
const nxCloudSpinner = ora(`Setting up Nx Cloud`).start();
|
||||
try {
|
||||
const pmc = getPackageManagerCommand(packageManager);
|
||||
const res = await execAndWait(
|
||||
`${pmc.exec} nx g nx:connect-to-nx-cloud --no-interactive --quiet`,
|
||||
process.env.NX_NEW_CLOUD_ONBOARDING === 'true'
|
||||
? `${
|
||||
pmc.exec
|
||||
} nx g nx:connect-to-nx-cloud --installationSource=create-nx-workspace ${
|
||||
useGitHub ? '--github' : ''
|
||||
} --no-interactive`
|
||||
: `${pmc.exec} nx g nx:connect-to-nx-cloud --no-interactive --quiet`,
|
||||
directory
|
||||
);
|
||||
|
||||
if (nxCloud !== 'yes') {
|
||||
nxCloudSpinner.succeed(
|
||||
'CI workflow with Nx Cloud has been generated successfully'
|
||||
|
||||
@ -2,6 +2,7 @@ import { output } from '../../utils/output';
|
||||
import { readNxJson } from '../../config/configuration';
|
||||
import { FsTree, flushChanges } from '../../generators/tree';
|
||||
import { connectToNxCloud } from '../../nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud';
|
||||
import { shortenedCloudUrl } from '../../nx-cloud/utilities/url-shorten';
|
||||
import { getNxCloudUrl, isNxCloudUsed } from '../../utils/nx-cloud-utils';
|
||||
import { runNxSync } from '../../utils/child-process';
|
||||
import { NxJsonConfiguration } from '../../config/nx-json';
|
||||
@ -51,7 +52,9 @@ export async function connectToNxCloudIfExplicitlyAsked(
|
||||
|
||||
export async function connectToNxCloudCommand(): Promise<boolean> {
|
||||
const nxJson = readNxJson();
|
||||
|
||||
if (isNxCloudUsed(nxJson)) {
|
||||
if (process.env.NX_NEW_CLOUD_ONBOARDING !== 'true') {
|
||||
output.log({
|
||||
title: '✔ This workspace already has Nx Cloud set up',
|
||||
bodyLines: [
|
||||
@ -59,6 +62,25 @@ export async function connectToNxCloudCommand(): Promise<boolean> {
|
||||
`- Login at ${getNxCloudUrl(nxJson)} to connect your repository`,
|
||||
],
|
||||
});
|
||||
} else {
|
||||
const token =
|
||||
process.env.NX_CLOUD_ACCESS_TOKEN || nxJson.nxCloudAccessToken;
|
||||
if (!token) {
|
||||
throw new Error(
|
||||
`Unable to authenticate. Either define accessToken in nx.json or set the NX_CLOUD_ACCESS_TOKEN env variable.`
|
||||
);
|
||||
}
|
||||
const connectCloudUrl = await shortenedCloudUrl('nx-connect', token);
|
||||
output.log({
|
||||
title: '✔ This workspace already has Nx Cloud set up',
|
||||
bodyLines: [
|
||||
'If you have not done so already, connect your workspace to your Nx Cloud account:',
|
||||
`- Connect with Nx Cloud at:
|
||||
|
||||
${connectCloudUrl}`,
|
||||
],
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -66,7 +88,7 @@ export async function connectToNxCloudCommand(): Promise<boolean> {
|
||||
const callback = await connectToNxCloud(tree, {});
|
||||
tree.lock();
|
||||
flushChanges(workspaceRoot, tree.listChanges());
|
||||
callback();
|
||||
await callback();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import { output } from '../../utils/output';
|
||||
import { getPackageManagerCommand } from '../../utils/package-manager';
|
||||
import { generateDotNxSetup } from './implementation/dot-nx/add-nx-scripts';
|
||||
import { runNxSync } from '../../utils/child-process';
|
||||
import { readJsonFile, writeJsonFile } from '../../utils/fileutils';
|
||||
import { readJsonFile } from '../../utils/fileutils';
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
import {
|
||||
addDepsToPackageJson,
|
||||
@ -22,7 +22,6 @@ import { globWithWorkspaceContext } from '../../utils/workspace-context';
|
||||
import { connectExistingRepoToNxCloudPrompt } from '../connect/connect-to-nx-cloud';
|
||||
import { addNxToNpmRepo } from './implementation/add-nx-to-npm-repo';
|
||||
import { addNxToMonorepo } from './implementation/add-nx-to-monorepo';
|
||||
import { join } from 'path';
|
||||
|
||||
export interface InitArgs {
|
||||
interactive: boolean;
|
||||
|
||||
@ -32,6 +32,7 @@ import {
|
||||
writeJsonFile,
|
||||
} from '../../utils/fileutils';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { commitChanges } from '../../utils/git-utils';
|
||||
import {
|
||||
ArrayPackageGroup,
|
||||
NxMigrationsConfiguration,
|
||||
@ -1573,32 +1574,6 @@ function getStringifiedPackageJsonDeps(root: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
function commitChanges(commitMessage: string): string | null {
|
||||
try {
|
||||
execSync('git add -A', { encoding: 'utf8', stdio: 'pipe' });
|
||||
execSync('git commit --no-verify -F -', {
|
||||
encoding: 'utf8',
|
||||
stdio: 'pipe',
|
||||
input: commitMessage,
|
||||
});
|
||||
} catch (err) {
|
||||
throw new Error(`Error committing changes:\n${err.stderr}`);
|
||||
}
|
||||
|
||||
return getLatestCommitSha();
|
||||
}
|
||||
|
||||
function getLatestCommitSha(): string | null {
|
||||
try {
|
||||
return execSync('git rev-parse HEAD', {
|
||||
encoding: 'utf8',
|
||||
stdio: 'pipe',
|
||||
}).trim();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function runNxMigration(
|
||||
root: string,
|
||||
collectionPath: string,
|
||||
|
||||
@ -6,6 +6,7 @@ import { readJson } from '../../../generators/utils/json';
|
||||
import { NxJsonConfiguration } from '../../../config/nx-json';
|
||||
import { readNxJson, updateNxJson } from '../../../generators/utils/nx-json';
|
||||
import { formatChangedFilesWithPrettierIfAvailable } from '../../../generators/internal-utils/format-changed-files-with-prettier-if-available';
|
||||
import { shortenedCloudUrl } from '../../utilities/url-shorten';
|
||||
|
||||
function printCloudConnectionDisabledMessage() {
|
||||
output.error({
|
||||
@ -72,7 +73,13 @@ async function createNxCloudWorkspace(
|
||||
return response.data;
|
||||
}
|
||||
|
||||
function printSuccessMessage(url: string) {
|
||||
async function printSuccessMessage(
|
||||
url: string,
|
||||
token: string,
|
||||
installationSource: string,
|
||||
github: boolean
|
||||
) {
|
||||
if (process.env.NX_NEW_CLOUD_ONBOARDING !== 'true') {
|
||||
let origin = 'https://nx.app';
|
||||
try {
|
||||
origin = new URL(url).origin;
|
||||
@ -86,12 +93,32 @@ function printSuccessMessage(url: string) {
|
||||
`- Login at ${origin} to connect your repository`,
|
||||
],
|
||||
});
|
||||
} else {
|
||||
const connectCloudUrl = await shortenedCloudUrl(
|
||||
installationSource,
|
||||
token,
|
||||
github
|
||||
);
|
||||
|
||||
output.note({
|
||||
title: `Your Nx Cloud workspace is ready.`,
|
||||
bodyLines: [
|
||||
`To claim it, connect it to your Nx Cloud account:`,
|
||||
`- Commit and push your changes.`,
|
||||
`- Create a pull request for the changes.`,
|
||||
`- Go to the following URL to connect your workspace to Nx Cloud:
|
||||
|
||||
${connectCloudUrl}`,
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
interface ConnectToNxCloudOptions {
|
||||
analytics?: boolean;
|
||||
installationSource?: string;
|
||||
hideFormatLogs?: boolean;
|
||||
github?: boolean;
|
||||
}
|
||||
|
||||
function addNxCloudOptionsToNxJson(
|
||||
@ -138,7 +165,13 @@ export async function connectToNxCloud(
|
||||
silent: schema.hideFormatLogs,
|
||||
});
|
||||
|
||||
return () => printSuccessMessage(r.url);
|
||||
return async () =>
|
||||
await printSuccessMessage(
|
||||
r.url,
|
||||
r.token,
|
||||
schema.installationSource,
|
||||
schema.github
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,11 @@
|
||||
"type": "boolean",
|
||||
"description": "Hide formatting logs",
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"github": {
|
||||
"type": "boolean",
|
||||
"description": "If the user will be using GitHub as their git hosting provider",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
||||
111
packages/nx/src/nx-cloud/utilities/url-shorten.ts
Normal file
111
packages/nx/src/nx-cloud/utilities/url-shorten.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import { logger } from '../../devkit-exports';
|
||||
import { getGithubSlugOrNull } from '../../utils/git-utils';
|
||||
|
||||
export async function shortenedCloudUrl(
|
||||
installationSource: string,
|
||||
accessToken: string,
|
||||
github?: boolean
|
||||
) {
|
||||
const githubSlug = getGithubSlugOrNull();
|
||||
|
||||
const apiUrl = removeTrailingSlash(
|
||||
process.env.NX_CLOUD_API || process.env.NRWL_API || `https://cloud.nx.app`
|
||||
);
|
||||
|
||||
const installationSupportsGitHub = await getInstallationSupportsGitHub(
|
||||
apiUrl
|
||||
);
|
||||
|
||||
const usesGithub =
|
||||
(githubSlug || github) &&
|
||||
(apiUrl.includes('cloud.nx.app') ||
|
||||
apiUrl.includes('eu.nx.app') ||
|
||||
installationSupportsGitHub);
|
||||
|
||||
const source = getSource(installationSource);
|
||||
|
||||
try {
|
||||
const response = await require('axios').post(
|
||||
`${apiUrl}/nx-cloud/onboarding`,
|
||||
{
|
||||
type: usesGithub ? 'GITHUB' : 'MANUAL',
|
||||
source,
|
||||
accessToken: usesGithub ? null : accessToken,
|
||||
selectedRepositoryName: githubSlug,
|
||||
}
|
||||
);
|
||||
|
||||
if (!response?.data || response.data.message) {
|
||||
throw new Error(
|
||||
response?.data?.message ?? 'Failed to shorten Nx Cloud URL'
|
||||
);
|
||||
}
|
||||
|
||||
return `${apiUrl}/connect/${response.data}`;
|
||||
} catch (e) {
|
||||
logger.verbose(`Failed to shorten Nx Cloud URL.
|
||||
${e}`);
|
||||
return getURLifShortenFailed(
|
||||
usesGithub,
|
||||
githubSlug,
|
||||
apiUrl,
|
||||
accessToken,
|
||||
source
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function removeTrailingSlash(apiUrl: string) {
|
||||
return apiUrl[apiUrl.length - 1] === '/' ? apiUrl.slice(0, -1) : apiUrl;
|
||||
}
|
||||
|
||||
function getSource(
|
||||
installationSource: string
|
||||
): 'nx-init' | 'nx-connect' | 'create-nx-workspace' | 'other' {
|
||||
if (installationSource.includes('nx-init')) {
|
||||
return 'nx-init';
|
||||
} else if (installationSource.includes('nx-connect')) {
|
||||
return 'nx-connect';
|
||||
} else if (installationSource.includes('create-nx-workspace')) {
|
||||
return 'create-nx-workspace';
|
||||
} else {
|
||||
return 'other';
|
||||
}
|
||||
}
|
||||
|
||||
function getURLifShortenFailed(
|
||||
usesGithub: boolean,
|
||||
githubSlug: string,
|
||||
apiUrl: string,
|
||||
accessToken: string,
|
||||
source: string
|
||||
) {
|
||||
if (usesGithub) {
|
||||
if (githubSlug) {
|
||||
return `${apiUrl}/setup/connect-workspace/vcs?provider=GITHUB&selectedRepositoryName=${encodeURIComponent(
|
||||
githubSlug
|
||||
)}&source=${source}`;
|
||||
} else {
|
||||
return `${apiUrl}/setup/connect-workspace/vcs?provider=GITHUB&source=${source}`;
|
||||
}
|
||||
}
|
||||
return `${apiUrl}/setup/connect-workspace/manual?accessToken=${accessToken}&source=${source}`;
|
||||
}
|
||||
|
||||
async function getInstallationSupportsGitHub(apiUrl: string): Promise<boolean> {
|
||||
try {
|
||||
const response = await require('axios').get(`${apiUrl}/vcs-integrations`);
|
||||
if (!response?.data || response.data.message) {
|
||||
throw new Error(
|
||||
response?.data?.message ?? 'Failed to shorten Nx Cloud URL'
|
||||
);
|
||||
}
|
||||
return !!response.data.github;
|
||||
} catch (e) {
|
||||
if (process.env.NX_VERBOSE_LOGGING) {
|
||||
logger.warn(`Failed to access vcs-integrations endpoint.
|
||||
${e}`);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
84
packages/nx/src/utils/git-utils.spec.ts
Normal file
84
packages/nx/src/utils/git-utils.spec.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import { extractUserAndRepoFromGitHubUrl } from './git-utils';
|
||||
|
||||
describe('extractUserAndRepoFromGitHubUrl', () => {
|
||||
describe('ssh cases', () => {
|
||||
it('should return the github user + repo info for origin', () => {
|
||||
expect(
|
||||
extractUserAndRepoFromGitHubUrl(
|
||||
`
|
||||
upstream git@github.com:upstream-user/repo-name.git (fetch)
|
||||
upstream git@github.com:upstream-user/repo-name.git (push)
|
||||
origin git@github.com:origin-user/repo-name.git (fetch)
|
||||
origin git@github.com:origin-user/repo-name.git (push)
|
||||
`
|
||||
)
|
||||
).toBe('origin-user/repo-name');
|
||||
});
|
||||
|
||||
it('should return the github user + repo info for the first one since no origin', () => {
|
||||
expect(
|
||||
extractUserAndRepoFromGitHubUrl(
|
||||
`
|
||||
upstream git@github.com:upstream-user/repo-name.git (fetch)
|
||||
upstream git@github.com:upstream-user/repo-name.git (push)
|
||||
other git@github.com:other-user/repo-name.git (fetch)
|
||||
other git@github.com:other-user/repo-name.git (push)
|
||||
`
|
||||
)
|
||||
).toBe('upstream-user/repo-name');
|
||||
});
|
||||
|
||||
it('should return null since no github', () => {
|
||||
expect(
|
||||
extractUserAndRepoFromGitHubUrl(
|
||||
`
|
||||
upstream git@random.com:upstream-user/repo-name.git (fetch)
|
||||
upstream git@random.com:upstream-user/repo-name.git (push)
|
||||
origin git@random.com:other-user/repo-name.git (fetch)
|
||||
origin git@random.com:other-user/repo-name.git (push)
|
||||
`
|
||||
)
|
||||
).toBe(null);
|
||||
});
|
||||
});
|
||||
describe('https cases', () => {
|
||||
it('should return the github user + repo info for origin', () => {
|
||||
expect(
|
||||
extractUserAndRepoFromGitHubUrl(
|
||||
`
|
||||
upstream https://github.com/upstream-user/repo-name.git (fetch)
|
||||
upstream https://github.com/upstream-user/repo-name.git (push)
|
||||
origin https://github.com/origin-user/repo-name.git (fetch)
|
||||
origin https://github.com/origin-user/repo-name.git (push)
|
||||
`
|
||||
)
|
||||
).toBe('origin-user/repo-name');
|
||||
});
|
||||
|
||||
it('should return the github user + repo info for the first one since no origin', () => {
|
||||
expect(
|
||||
extractUserAndRepoFromGitHubUrl(
|
||||
`
|
||||
upstream https://github.com/upstream-user/repo-name.git (fetch)
|
||||
upstream https://github.com/upstream-user/repo-name.git (push)
|
||||
other https://github.com/other-user/repo-name.git (fetch)
|
||||
other https://github.com/other-user/repo-name.git (push)
|
||||
`
|
||||
)
|
||||
).toBe('upstream-user/repo-name');
|
||||
});
|
||||
|
||||
it('should return null since no github', () => {
|
||||
expect(
|
||||
extractUserAndRepoFromGitHubUrl(
|
||||
`
|
||||
upstream https://other.com/upstream-user/repo-name.git (fetch)
|
||||
upstream https://other.com/upstream-user/repo-name.git (push)
|
||||
origin https://other.com/other-user/repo-name.git (fetch)
|
||||
origin https://other.com/other-user/repo-name.git (push)
|
||||
`
|
||||
)
|
||||
).toBe(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
73
packages/nx/src/utils/git-utils.ts
Normal file
73
packages/nx/src/utils/git-utils.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
export function getGithubSlugOrNull(): string | null {
|
||||
try {
|
||||
const gitRemote = execSync('git remote -v').toString();
|
||||
return extractUserAndRepoFromGitHubUrl(gitRemote);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function extractUserAndRepoFromGitHubUrl(
|
||||
gitRemotes: string
|
||||
): string | null {
|
||||
const regex =
|
||||
/^\s*(\w+)\s+(git@github\.com:|https:\/\/github\.com\/)([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)\.git/gm;
|
||||
let firstGitHubUrl: string | null = null;
|
||||
let match;
|
||||
|
||||
while ((match = regex.exec(gitRemotes)) !== null) {
|
||||
const remoteName = match[1];
|
||||
const url = match[2] + match[3] + '/' + match[4] + '.git';
|
||||
|
||||
if (remoteName === 'origin') {
|
||||
return parseGitHubUrl(url);
|
||||
}
|
||||
|
||||
if (!firstGitHubUrl) {
|
||||
firstGitHubUrl = url;
|
||||
}
|
||||
}
|
||||
|
||||
return firstGitHubUrl ? parseGitHubUrl(firstGitHubUrl) : null;
|
||||
}
|
||||
|
||||
function parseGitHubUrl(url: string): string | null {
|
||||
const sshPattern =
|
||||
/git@github\.com:([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)\.git/;
|
||||
const httpsPattern =
|
||||
/https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)\.git/;
|
||||
let match = url.match(sshPattern) || url.match(httpsPattern);
|
||||
|
||||
if (match) {
|
||||
return `${match[1]}/${match[2]}`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function commitChanges(commitMessage: string): string | null {
|
||||
try {
|
||||
execSync('git add -A', { encoding: 'utf8', stdio: 'pipe' });
|
||||
execSync('git commit --no-verify -F -', {
|
||||
encoding: 'utf8',
|
||||
stdio: 'pipe',
|
||||
input: commitMessage,
|
||||
});
|
||||
} catch (err) {
|
||||
throw new Error(`Error committing changes:\n${err.stderr}`);
|
||||
}
|
||||
|
||||
return getLatestCommitSha();
|
||||
}
|
||||
|
||||
export function getLatestCommitSha(): string | null {
|
||||
try {
|
||||
return execSync('git rev-parse HEAD', {
|
||||
encoding: 'utf8',
|
||||
stdio: 'pipe',
|
||||
}).trim();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user