Skip to content

Commit 61ad478

Browse files
committed
execa v10
1 parent e6d3387 commit 61ad478

7 files changed

Lines changed: 72 additions & 125 deletions

File tree

packages/create-docusaurus/src/commands.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
// We use cross-spawn instead of spawn because of Windows compatibility issues.
99
// For example, "yarn" doesn't work on Windows, it requires "yarn.cmd"
10-
// Tools like execa() use cross-spawn under the hood, and "resolve" the command
1110
import crossSpawn from 'cross-spawn';
1211
import supportsColor from 'supports-color';
1312
import {

packages/docusaurus-utils/src/vcs/__tests__/gitUtils.test.ts

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {describe, expect, it} from 'vitest';
99
import fs from 'fs-extra';
1010
import path from 'path';
1111
import os from 'os';
12-
import execa from 'execa';
12+
import {execa, type Options, type Result} from 'execa';
1313

1414
import {
1515
FileNotTrackedError,
@@ -38,22 +38,13 @@ class Git {
3838
cwd: string;
3939
args: string[];
4040
cmd: string;
41-
options?: execa.Options;
42-
}): Promise<execa.ExecaReturnValue> {
43-
const res = await execa(cmd, args, {
41+
options?: Options;
42+
}): Promise<Result> {
43+
return execa(cmd, args, {
4444
cwd,
45-
silent: true,
4645
shell: true,
4746
...options,
4847
});
49-
if (res.exitCode !== 0) {
50-
throw new Error(
51-
`Git command failed with code ${res.exitCode}: ${cmd} ${args.join(
52-
' ',
53-
)}`,
54-
);
55-
}
56-
return res;
5748
}
5849

5950
static async initializeRepo(dir: string): Promise<Git> {
@@ -83,8 +74,8 @@ class Git {
8374
async runOptimisticGitCommand(
8475
cmd: string,
8576
args?: string[],
86-
options?: execa.Options,
87-
): Promise<execa.ExecaReturnValue> {
77+
options?: Options,
78+
): Promise<Result> {
8879
return Git.runOptimisticGitCommand({cwd: this.dir, cmd, args, options});
8980
}
9081

@@ -538,8 +529,10 @@ describe('submodules APIs', () => {
538529
[Error: Couldn't find the git superproject root directory
539530
Failure while running \`git rev-parse --show-superproject-working-tree\` from cwd="<HOME_DIR>"
540531
The command executed throws an error: Command failed with exit code 128: git rev-parse --show-superproject-working-tree
532+
541533
fatal: not a git repository (or any of the parent directories): .git]
542-
Cause: [Error: Command failed with exit code 128: git rev-parse --show-superproject-working-tree
534+
Cause: [ExecaError: Command failed with exit code 128: git rev-parse --show-superproject-working-tree
535+
543536
fatal: not a git repository (or any of the parent directories): .git]
544537
`);
545538
});

packages/docusaurus-utils/src/vcs/gitUtils.ts

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import path from 'path';
99
import fs from 'fs-extra';
1010
import os from 'os';
1111
import _ from 'lodash';
12-
import execa from 'execa';
12+
import {execa, execaSync} from 'execa';
1313
import PQueue from 'p-queue';
1414
import logger from '@docusaurus/logger';
1515

@@ -34,7 +34,7 @@ const GitCommandQueue = new PQueue({
3434

3535
const realHasGitFn = () => {
3636
try {
37-
return execa.sync('git', ['--version']).exitCode === 0;
37+
return execaSync('git', ['--version']).exitCode === 0;
3838
} catch {
3939
return false;
4040
}
@@ -164,12 +164,6 @@ export async function getFileCommitDate(
164164
);
165165
}))!;
166166

167-
if (result.exitCode !== 0) {
168-
throw new Error(
169-
`Failed to retrieve the git history for file "${file}" with exit code ${result.exitCode}: ${result.stderr}`,
170-
);
171-
}
172-
173167
// We only parse the output line starting with our "RESULT:" prefix
174168
// See why https://github.com/facebook/docusaurus/pull/10022
175169
const regex = includeAuthor
@@ -294,15 +288,6 @@ The command executed throws an error: ${error.message}`,
294288
);
295289
});
296290

297-
if (result.exitCode !== 0) {
298-
throw new Error(
299-
`${createErrorMessageBase()}
300-
The command returned exit code ${logger.code(result.exitCode)}: ${logger.subdue(
301-
result.stderr,
302-
)}`,
303-
);
304-
}
305-
306291
return fs.realpath.native(result.stdout.trim());
307292
}
308293

@@ -334,15 +319,6 @@ The command executed throws an error: ${error.message}`,
334319
);
335320
});
336321

337-
if (result.exitCode !== 0) {
338-
throw new Error(
339-
`${createErrorMessageBase()}
340-
The command returned exit code ${logger.code(result.exitCode)}: ${logger.subdue(
341-
result.stderr,
342-
)}`,
343-
);
344-
}
345-
346322
const output = result.stdout.trim();
347323
// this command only works when inside submodules
348324
// otherwise it doesn't return anything when we are inside the main repo
@@ -372,15 +348,6 @@ The command executed throws an error: ${error.message}`,
372348
);
373349
});
374350

375-
if (result.exitCode !== 0) {
376-
throw new Error(
377-
`${createErrorMessageBase()}
378-
The command returned exit code ${logger.code(result.exitCode)}: ${logger.subdue(
379-
result.stderr,
380-
)}`,
381-
);
382-
}
383-
384351
const output = result.stdout.trim();
385352

386353
if (!output) {
@@ -461,20 +428,13 @@ export async function getGitRepositoryFilesInfo(
461428
],
462429
{
463430
cwd,
464-
encoding: 'utf-8',
431+
encoding: 'utf8',
465432
// TODO use streaming to avoid a large buffer
466433
// See https://github.com/withastro/starlight/issues/3154
467434
maxBuffer: 20 * 1024 * 1024,
468435
},
469436
);
470437

471-
if (result.exitCode !== 0) {
472-
throw new Error(
473-
`Docusaurus failed to run the 'git log' to retrieve tracked files last update date/author.
474-
The command exited with code ${result.exitCode}: ${result.stderr}`,
475-
);
476-
}
477-
478438
const logLines = result.stdout.split('\n');
479439

480440
const now = Date.now();

packages/docusaurus/bin/beforeCli.mjs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,12 @@ export default async function beforeCli() {
112112
}
113113

114114
const yarnVersionResult = await execa`yarn --version`;
115-
if (yarnVersionResult.exitCode === 0) {
116-
const majorVersion = parseInt(
117-
yarnVersionResult.stdout?.trim().split('.')[0] ?? '',
118-
10,
119-
);
120-
if (!Number.isNaN(majorVersion)) {
121-
return majorVersion;
122-
}
115+
const majorVersion = parseInt(
116+
yarnVersionResult.stdout?.trim().split('.')[0] ?? '',
117+
10,
118+
);
119+
if (!Number.isNaN(majorVersion)) {
120+
return majorVersion;
123121
}
124122

125123
return undefined;

packages/docusaurus/src/commands/deploy.ts

Lines changed: 54 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import fs from 'fs-extra';
99
import path from 'path';
1010
import os from 'os';
1111
import logger from '@docusaurus/logger';
12-
import execa from 'execa';
12+
import {execa} from 'execa';
1313
import {hasSSHProtocol, buildSshUrl, buildHttpsUrl} from '@docusaurus/utils';
1414
import {loadContext, type LoadContextParams} from '../server/site';
1515
import {build} from './build/build';
@@ -30,14 +30,16 @@ const debugMode = !!process.env.DOCUSAURUS_DEPLOY_DEBUG;
3030

3131
// Log executed commands so that user can figure out mistakes on his own
3232
// for example: https://github.com/facebook/docusaurus/issues/3875
33-
function exec(cmd: string, options?: {log?: boolean; failfast?: boolean}) {
33+
async function exec(
34+
file: string,
35+
args: string[],
36+
options?: {log?: boolean; failfast?: boolean},
37+
) {
3438
const log = options?.log ?? true;
35-
const failfast = options?.failfast ?? false;
39+
const failfast = options?.failfast ?? true;
40+
const cmd = [file, ...args].join(' ');
3641
try {
37-
// TODO migrate to execa(file,[...args]) instead
38-
// Use async/await everything
39-
// Avoid execa.command: the args need to be escaped manually
40-
const result = execa.commandSync(cmd);
42+
const result = await execa(file, args, {reject: false});
4143
if (log || debugMode) {
4244
logger.info`code=${obfuscateGitPass(
4345
cmd,
@@ -48,7 +50,8 @@ function exec(cmd: string, options?: {log?: boolean; failfast?: boolean}) {
4850
}
4951
if (failfast && result.exitCode !== 0) {
5052
throw new Error(
51-
`Command returned unexpected exitCode ${result.exitCode}`,
53+
`Command returned unexpected exitCode ${result.exitCode}
54+
${result.stderr}`,
5255
);
5356
}
5457
return result;
@@ -63,14 +66,8 @@ In CWD code=${process.cwd()}`,
6366
}
6467
}
6568

66-
// Execa escape args and add necessary quotes automatically
67-
// When using Execa.command, the args containing spaces must be escaped manually
68-
function escapeArg(arg: string): string {
69-
return arg.replaceAll(' ', '\\ ');
70-
}
71-
72-
function hasGit() {
73-
return exec('git --version').exitCode === 0;
69+
async function hasGit() {
70+
return (await exec('git', ['--version'], {failfast: false})).exitCode === 0;
7471
}
7572

7673
export async function deploy(
@@ -93,26 +90,22 @@ This behavior can have SEO impacts and create relative link issues.
9390
}
9491

9592
logger.info('Deploy command invoked...');
96-
if (!hasGit()) {
93+
if (!(await hasGit())) {
9794
throw new Error('Git not installed or not added to PATH!');
9895
}
9996

10097
// Source repo is the repo from where the command is invoked
101-
const {stdout} = exec('git remote get-url origin', {
98+
const {stdout} = await exec('git', ['remote', 'get-url', 'origin'], {
10299
log: false,
103-
failfast: true,
104100
});
105101
const sourceRepoUrl = stdout.trim();
106102

107103
// The source branch; defaults to the currently checked out branch
108104
const sourceBranch =
109105
process.env.CURRENT_BRANCH ??
110-
exec('git rev-parse --abbrev-ref HEAD', {
111-
log: false,
112-
failfast: true,
113-
})
114-
?.stdout?.toString()
115-
.trim();
106+
(
107+
await exec('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {log: false})
108+
).stdout.trim();
116109

117110
const gitUser = process.env.GIT_USER;
118111

@@ -157,10 +150,7 @@ This behavior can have SEO impacts and create relative link issues.
157150
const isPullRequest =
158151
process.env.CI_PULL_REQUEST ?? process.env.CIRCLE_PULL_REQUEST;
159152
if (isPullRequest) {
160-
exec('echo "Skipping deploy on a pull request."', {
161-
log: false,
162-
failfast: true,
163-
});
153+
logger.info('Skipping deploy on a pull request.');
164154
process.exit(0);
165155
}
166156

@@ -225,7 +215,9 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
225215

226216
// Save the commit hash that triggers publish-gh-pages before checking
227217
// out to deployment branch.
228-
const currentCommit = exec('git rev-parse HEAD')?.stdout?.toString().trim();
218+
const currentCommit = (
219+
await exec('git', ['rev-parse', 'HEAD'])
220+
).stdout.trim();
229221

230222
const runDeploy = async (outputDirectory: string) => {
231223
const targetDirectory = cliOptions.targetDir ?? '.';
@@ -238,22 +230,26 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
238230
// Clones the repo into the temp folder and checks out the target branch.
239231
// If the branch doesn't exist, it creates a new one based on the
240232
// repository default branch.
241-
if (
242-
exec(
243-
`git clone --depth 1 --branch ${deploymentBranch} ${deploymentRepoURL} ${escapeArg(
244-
toPath,
245-
)}`,
246-
).exitCode !== 0
247-
) {
248-
exec(`git clone --depth 1 ${deploymentRepoURL} ${escapeArg(toPath)}`);
249-
exec(`git checkout -b ${deploymentBranch}`);
233+
const cloneResult = await exec(
234+
'git',
235+
[
236+
'clone',
237+
'--depth',
238+
'1',
239+
'--branch',
240+
deploymentBranch,
241+
deploymentRepoURL,
242+
toPath,
243+
],
244+
{failfast: false},
245+
);
246+
if (cloneResult.exitCode !== 0) {
247+
await exec('git', ['clone', '--depth', '1', deploymentRepoURL, toPath]);
248+
await exec('git', ['checkout', '-b', deploymentBranch]);
250249
}
251250

252251
// Clear out any existing contents in the target directory
253-
exec(`git rm -rf ${escapeArg(targetDirectory)}`, {
254-
log: false,
255-
failfast: true,
256-
});
252+
await exec('git', ['rm', '-rf', targetDirectory], {log: false});
257253

258254
const targetPath = path.join(toPath, targetDirectory);
259255
try {
@@ -262,32 +258,37 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
262258
logger.error`Copying build assets from path=${fromPath} to path=${targetPath} failed.`;
263259
throw err;
264260
}
265-
exec('git add --all', {failfast: true});
261+
await exec('git', ['add', '--all']);
266262

267263
const gitUserName = process.env.GIT_USER_NAME;
268264
if (gitUserName) {
269-
exec(`git config user.name ${escapeArg(gitUserName)}`, {failfast: true});
265+
await exec('git', ['config', 'user.name', gitUserName]);
270266
}
271267

272268
const gitUserEmail = process.env.GIT_USER_EMAIL;
273269
if (gitUserEmail) {
274-
exec(`git config user.email ${escapeArg(gitUserEmail)}`, {
275-
failfast: true,
276-
});
270+
await exec('git', ['config', 'user.email', gitUserEmail]);
277271
}
278272

279273
const commitMessage =
280274
process.env.CUSTOM_COMMIT_MESSAGE ??
281275
`Deploy website - based on ${currentCommit}`;
282-
const commitResults = exec(
283-
`git commit -m ${escapeArg(commitMessage)} --allow-empty`,
276+
// The commit might return a non-zero value when site is up to date.
277+
const commitResults = await exec(
278+
'git',
279+
['commit', '-m', commitMessage, '--allow-empty'],
280+
{failfast: false},
281+
);
282+
const pushResult = await exec(
283+
'git',
284+
['push', '--force', 'origin', deploymentBranch],
285+
{failfast: false},
284286
);
285-
if (exec(`git push --force origin ${deploymentBranch}`).exitCode !== 0) {
287+
if (pushResult.exitCode !== 0) {
286288
throw new Error(
287289
'Running "git push" command failed. Does the GitHub user account you are using have push access to the repository?',
288290
);
289291
} else if (commitResults.exitCode === 0) {
290-
// The commit might return a non-zero value when site is up to date.
291292
let websiteURL;
292293
if (githubHost === 'github.com') {
293294
websiteURL = projectName.includes('.github.io')
@@ -297,7 +298,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
297298
// GitHub enterprise hosting.
298299
websiteURL = `https://${githubHost}/pages/${organizationName}/${projectName}/`;
299300
}
300-
exec(`echo "Website is live at ${websiteURL}."`, {failfast: true});
301+
logger.success`Website is live at url=${websiteURL}.`;
301302
process.exit(0);
302303
}
303304
};

0 commit comments

Comments
 (0)