Skip to content

Commit

Permalink
feat: execa
Browse files Browse the repository at this point in the history
  • Loading branch information
OzakIOne committed Jul 31, 2024
1 parent 95990c6 commit e102c29
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 93 deletions.
1 change: 1 addition & 0 deletions packages/create-docusaurus/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@docusaurus/logger": "3.4.0",
"@docusaurus/utils": "3.4.0",
"commander": "^5.1.0",
"execa": "5.1.1",
"fs-extra": "^11.1.1",
"lodash": "^4.17.21",
"prompts": "^2.4.2",
Expand Down
44 changes: 24 additions & 20 deletions packages/create-docusaurus/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {fileURLToPath} from 'url';
import path from 'path';
import _ from 'lodash';
import logger from '@docusaurus/logger';
import shell from 'shelljs';
import execa from 'execa';
import prompts, {type Choice} from 'prompts';
import supportsColor from 'supports-color';
import {escapeShellArg, askPreferredLanguage} from '@docusaurus/utils';
Expand Down Expand Up @@ -70,9 +70,9 @@ function findPackageManagerFromUserAgent(): PackageManager | undefined {
}

async function askForPackageManagerChoice(): Promise<PackageManager> {
const hasYarn = shell.exec('yarn --version', {silent: true}).code === 0;
const hasPnpm = shell.exec('pnpm --version', {silent: true}).code === 0;
const hasBun = shell.exec('bun --version', {silent: true}).code === 0;
const hasYarn = (await execa.command('yarn --version')).exitCode === 0;
const hasPnpm = (await execa.command('pnpm --version')).exitCode === 0;
const hasBun = (await execa.command('bun --version')).exitCode === 0;

if (!hasYarn && !hasPnpm && !hasBun) {
return 'npm';
Expand Down Expand Up @@ -533,7 +533,7 @@ export default async function init(
const gitCloneCommand = `${gitCommand} ${escapeShellArg(
source.url,
)} ${escapeShellArg(dest)}`;
if (shell.exec(gitCloneCommand).code !== 0) {
if (execa.command(gitCloneCommand).exitCode !== 0) {
logger.error`Cloning Git template failed!`;
process.exit(1);
}
Expand Down Expand Up @@ -583,24 +583,28 @@ export default async function init(
const cdpath = path.relative('.', dest);
const pkgManager = await getPackageManager(dest, cliOptions);
if (!cliOptions.skipInstall) {
shell.cd(dest);
await execa.command(`cd ${dest}`);

Check warning

Code scanning / CodeQL

Shell command built from environment values Medium

This shell command depends on an uncontrolled
absolute path
.

Check warning

Code scanning / CodeQL

Unsafe shell command constructed from library input Medium

This string concatenation which depends on
library input
is later used in a
shell command
.
This string concatenation which depends on
library input
is later used in a
shell command
.
logger.info`Installing dependencies with name=${pkgManager}...`;
// ...

if (
shell.exec(
pkgManager === 'yarn'
? 'yarn'
: pkgManager === 'bun'
? 'bun install'
: `${pkgManager} install --color always`,
{
env: {
...process.env,
// Force coloring the output, since the command is invoked by
// shelljs, which is not an interactive shell
...(supportsColor.stdout ? {FORCE_COLOR: '1'} : {}),
(
await execa.command(
pkgManager === 'yarn'
? 'yarn'
: pkgManager === 'bun'
? 'bun install'
: `${pkgManager} install --color always`,
{
env: {
...process.env,
// Force coloring the output, since the command is invoked by
// shelljs, which is not an interactive shell
...(supportsColor.stdout ? {FORCE_COLOR: '1'} : {}),
},
},
},
).code !== 0
)
).exitCode !== 0
) {
logger.error('Dependency installation failed.');
logger.info`The site directory has already been created, and you can retry by typing:
Expand Down
3 changes: 1 addition & 2 deletions packages/docusaurus-plugin-content-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@
"@types/js-yaml": "^4.0.5",
"@types/picomatch": "^2.3.0",
"commander": "^5.1.0",
"picomatch": "^2.3.1",
"shelljs": "^0.8.5"
"picomatch": "^2.3.1"
},
"peerDependencies": {
"react": "^18.0.0",
Expand Down
1 change: 1 addition & 0 deletions packages/docusaurus-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@docusaurus/utils-common": "3.4.0",
"@svgr/webpack": "^8.1.0",
"escape-string-regexp": "^4.0.0",
"execa": "5.1.1",
"file-loader": "^6.2.0",
"fs-extra": "^11.1.1",
"github-slugger": "^1.5.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import fs from 'fs-extra';
import path from 'path';
import {createTempRepo} from '@testing-utils/git';
import shell from 'shelljs';
// import execa from 'execa';
import {
getGitLastUpdate,
LAST_UPDATE_FALLBACK,
Expand Down Expand Up @@ -69,6 +70,7 @@ describe('getGitLastUpdate', () => {
});

it('git does not exist', async () => {
// TODO how to mock execa command ?
const mock = jest.spyOn(shell, 'which').mockImplementationOnce(() => null);
const consoleMock = jest
.spyOn(console, 'warn')
Expand Down
56 changes: 31 additions & 25 deletions packages/docusaurus-utils/src/gitUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@
import path from 'path';
import fs from 'fs-extra';
import _ from 'lodash';
import shell from 'shelljs'; // TODO replace with async-first version

const realHasGitFn = () => !!shell.which('git');
import execa from 'execa';

const realHasGitFn = async () => {
try {
await execa('git', ['--version']);
return true;
} catch (error) {
return false;
}
};

// The hasGit call is synchronous IO so we memoize it
// The user won't install Git in the middle of a build anyway...
Expand Down Expand Up @@ -123,30 +130,29 @@ export async function getFileCommitDate(
file,
)}"`;

const result = await new Promise<{
code: number;
stdout: string;
stderr: string;
}>((resolve) => {
shell.exec(
command,
{
// Setting cwd is important, see: https://github.com/facebook/docusaurus/pull/5048
cwd: path.dirname(file),
silent: true,
},
(code, stdout, stderr) => {
resolve({code, stdout, stderr});
},
);
});

if (result.code !== 0) {
throw new Error(
`Failed to retrieve the git history for file "${file}" with exit code ${result.code}: ${result.stderr}`,
);
async function executeCommand(cmd: string, filepath: string) {
try {
const {exitCode, stdout, stderr} = await execa(cmd, {
cwd: path.dirname(filepath),
shell: true,
});

if (exitCode !== 0) {
throw new Error(
`Failed to retrieve the git history for file "${file}" with exit code ${exitCode}: ${stderr}`,
);
}

return {code: exitCode, stdout, stderr};
} catch (error) {
console.error('Error executing command:', error);
throw error;
}
}

// Usage
const result = await executeCommand(command, file);

// We only parse the output line starting with our "RESULT:" prefix
// See why https://github.com/facebook/docusaurus/pull/10022
const regex = includeAuthor
Expand Down
1 change: 1 addition & 0 deletions packages/docusaurus/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"escape-html": "^1.0.3",
"eta": "^2.2.0",
"eval": "^0.1.8",
"execa": "5.1.1",
"file-loader": "^6.2.0",
"fs-extra": "^11.1.1",
"html-minifier-terser": "^7.2.0",
Expand Down
44 changes: 27 additions & 17 deletions packages/docusaurus/src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import fs from 'fs-extra';
import path from 'path';
import os from 'os';
import logger from '@docusaurus/logger';
import shell from 'shelljs';
import execa from 'execa';
import {hasSSHProtocol, buildSshUrl, buildHttpsUrl} from '@docusaurus/utils';
import {loadContext, type LoadContextParams} from '../server/site';
import {build} from './build';
Expand All @@ -32,8 +32,10 @@ function obfuscateGitPass(str: string) {
// for example: https://github.com/facebook/docusaurus/issues/3875
function shellExecLog(cmd: string) {
try {
const result = shell.exec(cmd);
logger.info`code=${obfuscateGitPass(cmd)} subdue=${`code: ${result.code}`}`;
const result = execa.command(cmd);
logger.info`code=${obfuscateGitPass(
cmd,
)} subdue=${`code: ${result.exitCode}`}`;
return result;
} catch (err) {
logger.error`code=${obfuscateGitPass(cmd)}`;
Expand Down Expand Up @@ -61,19 +63,21 @@ This behavior can have SEO impacts and create relative link issues.
}

logger.info('Deploy command invoked...');
if (!shell.which('git')) {
try {
await execa.command('git --version');
} catch (err) {
throw new Error('Git not installed or on the PATH!');
}

// Source repo is the repo from where the command is invoked
const sourceRepoUrl = shell
.exec('git remote get-url origin', {silent: true})
.stdout.trim();
// TODO silent
const {stdout} = await execa.command('git remote get-url origin');
const sourceRepoUrl = stdout.trim();

// The source branch; defaults to the currently checked out branch
const sourceBranch =
process.env.CURRENT_BRANCH ??
shell.exec('git rev-parse --abbrev-ref HEAD', {silent: true}).stdout.trim();
execa.command('git rev-parse --abbrev-ref HEAD')?.stdout?.toString().trim();

const gitUser = process.env.GIT_USER;

Expand Down Expand Up @@ -118,8 +122,8 @@ This behavior can have SEO impacts and create relative link issues.
const isPullRequest =
process.env.CI_PULL_REQUEST ?? process.env.CIRCLE_PULL_REQUEST;
if (isPullRequest) {
shell.echo('Skipping deploy on a pull request.');
shell.exit(0);
await execa.command('echo "Skipping deploy on a pull request."');
process.exit(0);
}

// github.io indicates organization repos that deploy via default branch. All
Expand Down Expand Up @@ -183,23 +187,25 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);

// Save the commit hash that triggers publish-gh-pages before checking
// out to deployment branch.
const currentCommit = shellExecLog('git rev-parse HEAD').stdout.trim();
const currentCommit = shellExecLog('git rev-parse HEAD')
?.stdout?.toString()
.trim();

const runDeploy = async (outputDirectory: string) => {
const targetDirectory = cliOptions.targetDir ?? '.';
const fromPath = outputDirectory;
const toPath = await fs.mkdtemp(
path.join(os.tmpdir(), `${projectName}-${deploymentBranch}`),
);
shell.cd(toPath);
await execa.command(`cd ${toPath}`);

// Clones the repo into the temp folder and checks out the target branch.
// If the branch doesn't exist, it creates a new one based on the
// repository default branch.
if (
shellExecLog(
`git clone --depth 1 --branch ${deploymentBranch} ${deploymentRepoURL} "${toPath}"`,
).code !== 0
).exitCode !== 0
) {
shellExecLog(`git clone --depth 1 ${deploymentRepoURL} "${toPath}"`);
shellExecLog(`git checkout -b ${deploymentBranch}`);
Expand Down Expand Up @@ -232,12 +238,12 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
`Deploy website - based on ${currentCommit}`;
const commitResults = shellExecLog(`git commit -m "${commitMessage}"`);
if (
shellExecLog(`git push --force origin ${deploymentBranch}`).code !== 0
shellExecLog(`git push --force origin ${deploymentBranch}`).exitCode !== 0
) {
throw new Error(
'Running "git push" command failed. Does the GitHub user account you are using have push access to the repository?',
);
} else if (commitResults.code === 0) {
} else if (commitResults.exitCode === 0) {
// The commit might return a non-zero value when site is up to date.
let websiteURL = '';
if (githubHost === 'github.com') {
Expand All @@ -248,8 +254,12 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
// GitHub enterprise hosting.
websiteURL = `https://${githubHost}/pages/${organizationName}/${projectName}/`;
}
shell.echo(`Website is live at "${websiteURL}".`);
shell.exit(0);
try {
await execa.command(`echo "Website is live at ${websiteURL}."`);
process.exit(0);
} catch (err) {
throw new Error(`Failed to execute command: ${err}`);
}
}
};

Expand Down
33 changes: 4 additions & 29 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7410,7 +7410,7 @@ [email protected]:
signal-exit "^3.0.3"
strip-final-newline "^2.0.0"

execa@^5.0.0:
execa@5.1.1, execa@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
Expand Down Expand Up @@ -15245,16 +15245,7 @@ string-length@^4.0.1:
char-regex "^1.0.2"
strip-ansi "^6.0.0"

"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand Down Expand Up @@ -15353,14 +15344,7 @@ stringify-object@^3.3.0:
is-obj "^1.0.1"
is-regexp "^1.0.0"

"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"

strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand Down Expand Up @@ -17025,7 +17009,7 @@ [email protected], workbox-window@^7.0.0:
"@types/trusted-types" "^2.0.2"
workbox-core "7.0.0"

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
Expand All @@ -17043,15 +17027,6 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^8.0.1, wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
Expand Down

0 comments on commit e102c29

Please sign in to comment.