From 546ed16c77f44727d5b09e1e7197182e1bd41de9 Mon Sep 17 00:00:00 2001 From: Zach Bloomquist Date: Thu, 14 Apr 2022 12:23:31 -0400 Subject: [PATCH] fix(cli): show additional mitigation steps for max path length error (#21047) --- cli/__snapshots__/errors_spec.js | 1 + cli/__snapshots__/unzip_spec.js | 20 +++++++++++++- cli/lib/errors.js | 8 ++++++ cli/lib/tasks/unzip.js | 29 +++++++++++++------- cli/test/lib/tasks/unzip_spec.js | 46 ++++++++++++++++++++++---------- 5 files changed, 79 insertions(+), 25 deletions(-) diff --git a/cli/__snapshots__/errors_spec.js b/cli/__snapshots__/errors_spec.js index e254269f2d30..e5ce248b8a71 100644 --- a/cli/__snapshots__/errors_spec.js +++ b/cli/__snapshots__/errors_spec.js @@ -32,6 +32,7 @@ exports['errors individual has the following errors 1'] = [ "childProcessKilled", "failedDownload", "failedUnzip", + "failedUnzipWindowsMaxPathLength", "incompatibleHeadlessFlags", "invalidCacheDirectory", "invalidCypressEnv", diff --git a/cli/__snapshots__/unzip_spec.js b/cli/__snapshots__/unzip_spec.js index 830187db819b..ec4edf9dc004 100644 --- a/cli/__snapshots__/unzip_spec.js +++ b/cli/__snapshots__/unzip_spec.js @@ -1,4 +1,4 @@ -exports['unzip error 1'] = ` +exports['lib/tasks/unzip throws when cannot unzip 1'] = ` Error: The Cypress App could not be unzipped. Search for an existing issue or open a GitHub issue at @@ -15,3 +15,21 @@ Platform: darwin-x64 (Foo-OsVersion) Cypress Version: 1.2.3 ` + +exports['lib/tasks/unzip throws max path length error when cannot unzip due to realpath ENOENT on windows 1'] = ` +Error: The Cypress App could not be unzipped. + +This is most likely because the maximum path length is being exceeded on your system. + +Read here for solutions to this problem: https://on.cypress.io/win-max-path-length-error + +---------- + +Error: failed + +---------- + +Platform: win32-x64 (Foo-OsVersion) +Cypress Version: 1.2.3 + +` diff --git a/cli/lib/errors.js b/cli/lib/errors.js index ca460ec41a37..397123d5da6e 100644 --- a/cli/lib/errors.js +++ b/cli/lib/errors.js @@ -58,6 +58,13 @@ const failedUnzip = { solution: genericErrorSolution, } +const failedUnzipWindowsMaxPathLength = { + description: 'The Cypress App could not be unzipped.', + solution: `This is most likely because the maximum path length is being exceeded on your system. + + Read here for solutions to this problem: https://on.cypress.io/win-max-path-length-error`, +} + const missingApp = (binaryDir) => { return { description: `No version of Cypress is installed in: ${chalk.cyan( @@ -404,6 +411,7 @@ module.exports = { unexpected, failedDownload, failedUnzip, + failedUnzipWindowsMaxPathLength, invalidCypressEnv, invalidCacheDirectory, CYPRESS_RUN_BINARY, diff --git a/cli/lib/tasks/unzip.js b/cli/lib/tasks/unzip.js index ce665dfcd738..5993bd2700a3 100644 --- a/cli/lib/tasks/unzip.js +++ b/cli/lib/tasks/unzip.js @@ -195,7 +195,11 @@ const unzip = ({ zipFilePath, installDir, progress }) => { }) } -const start = ({ zipFilePath, installDir, progress }) => { +function isMaybeWindowsMaxPathLengthError (err) { + return os.platform() === 'win32' && err.code === 'ENOENT' && err.syscall === 'realpath' +} + +const start = async ({ zipFilePath, installDir, progress }) => { la(is.unemptyString(installDir), 'missing installDir') if (!progress) { progress = { onProgress: () => { @@ -203,18 +207,23 @@ const start = ({ zipFilePath, installDir, progress }) => { } } } - return fs.pathExists(installDir) - .then((exists) => { - if (exists) { + try { + const installDirExists = await fs.pathExists(installDir) + + if (installDirExists) { debug('removing existing unzipped binary', installDir) - return fs.removeAsync(installDir) + await fs.removeAsync(installDir) } - }) - .then(() => { - return unzip({ zipFilePath, installDir, progress }) - }) - .catch(throwFormErrorText(errors.failedUnzip)) + + await unzip({ zipFilePath, installDir, progress }) + } catch (err) { + const errorTemplate = isMaybeWindowsMaxPathLengthError(err) ? + errors.failedUnzipWindowsMaxPathLength + : errors.failedUnzip + + await throwFormErrorText(errorTemplate)(err) + } } module.exports = { diff --git a/cli/test/lib/tasks/unzip_spec.js b/cli/test/lib/tasks/unzip_spec.js index c16cac91c3a5..286439f67010 100644 --- a/cli/test/lib/tasks/unzip_spec.js +++ b/cli/test/lib/tasks/unzip_spec.js @@ -30,26 +30,44 @@ describe('lib/tasks/unzip', function () { afterEach(function () { stdout.restore() + }) + + it('throws when cannot unzip', async function () { + try { + await unzip.start({ + zipFilePath: path.join('test', 'fixture', 'bad_example.zip'), + installDir, + }) + } catch (err) { + logger.error(err) - // return fs.removeAsync(installationDir) + return snapshot(normalize(this.stdout.toString())) + } + + throw new Error('should have failed') }) - it('throws when cannot unzip', function () { - const ctx = this + it('throws max path length error when cannot unzip due to realpath ENOENT on windows', async function () { + const err = new Error('failed') - return unzip - .start({ - zipFilePath: path.join('test', 'fixture', 'bad_example.zip'), - installDir, - }) - .then(() => { - throw new Error('should have failed') - }) - .catch((err) => { + err.code = 'ENOENT' + err.syscall = 'realpath' + + os.platform.returns('win32') + sinon.stub(fs, 'ensureDirAsync').rejects(err) + + try { + await unzip.start({ + zipFilePath: path.join('test', 'fixture', 'bad_example.zip'), + installDir, + }) + } catch (err) { logger.error(err) - snapshot('unzip error 1', normalize(ctx.stdout.toString())) - }) + return snapshot(normalize(this.stdout.toString())) + } + + throw new Error('should have failed') }) it('can really unzip', function () {