Patching Cypress CLI NPM module

How to patch fix installed NPM module to avoid waiting for an official bug fix.

Note: this blog post is an expanded version of the official documentation section "Patch Cypress" available at https://on.cypress.io/debugging.

If you discover a bug in an NPM module, you can open an issue and wait for a fix. Yet, sometimes the problem is quite small and waiting for a patch release seems like eternity. In this blog post I will show how to write a patch for Cypress NPM module yourself and apply it after installing the official NPM module. This allows you to avoid waiting for (sometimes delayed) official release.

Cypress CLI npm module is a small module installed first when you run the npm i cypress command. This module parses the input arguments, starts an XVFB session if necessary and runs the Cypress Electron-based binary application. Sometimes the CLI module itself has a bug, which breaks your CI execution. For example, issue 2181 "dest.end error crash during run exit on GitLab CI / Windows" crashes the CLI when the child process exits on some versions of Windows. This error was apparently introduced in v3.0.0 frustrating many users. On Windows, to get the terminal colors to work, we needed to pipe input and output streams from the CLI process to the spawned Electron process. Thus the released CLI code has the following code fragment:

1
2
3
4
5
6
7
8
9
10
11
12
function needsEverythingPipedDirectly() {
return isPlatform('win32')
}
function getStdio(needsXvfb) {
if (needsEverythingPipedDirectly()) {
// hmm, maybe it should be "inherit"?!!
return 'pipe';
}
// a little bit more logic for Linux / Mac
// ...
return 'inherit'
}

Imagine you are a user trying to run Cypress tests on Windows CI machine and hitting the error. All tests pass successfully and then ...

1
2
3
4
5
6
7
8
9
10
11
_stream_readable.js:511
dest.end();
^
TypeError: dest.end is not a function
at Socket.onend (_stream_readable.js:511:10)
at Socket.g (events.js:292:16)
at emitNone (events.js:91:20)
at Socket.emit (events.js:185:7)
at endReadableNT (_stream_readable.js:974:12)
at _combinedTickCallback (internal/process/next_tick.js:80:11)
at process._tickCallback (internal/process/next_tick.js:104:9)

You suspect that switching from pipe to inherit would fix YOUR problem, but the Cypress team still has not implemented this fix; because each version of Cypress should work on a variety of platforms. What can you do meanwhile? You can patch Cypress CLI module in your own project, implementing a temporary fix! Here is how to do this.

  1. Install the patch-package with
    1
    npm i -D patch-package
  2. Add the patching step to your CI after npm ci step
    1
    2
    3
    - run: npm ci
    # after installing NPM dependencies, patch any that need custom code
    - run: npx patch-package
    Alternatively, you can apply the patch during the npm post-install phase. In your package.json add the following:
    1
    2
    3
    4
    5
    {
    "scripts": {
    "postinstall": "patch-package"
    }
    }
    Now, let's edit the line causing the problem in your local node_modules folder.
  3. Open and edit the node_modules/cypress/lib/exec/spawn.js file
    1
    2
    3
    if (needsEverythingPipedDirectly()) {
    return 'inherit'; // changed from 'pipe'
    }
  4. Run the npx patch-package cypress command.
    1
    2
    3
    4
    5
    6
    $ npx patch-package cypress
    patch-package 6.1.2
    • Creating temporary folder
    • Installing [email protected] with npm
    • Diffing your files with clean files
    ✔ Created file patches/cypress+3.4.1.patch
    The above command has created a new file patches/cypress+3.4.1.patch
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ cat patches/cypress+3.4.1.patch
    diff --git a/node_modules/cypress/lib/exec/spawn.js b/node_modules/cypress/lib/exec/spawn.js
    index ed13727..19c1fae 100644
    --- a/node_modules/cypress/lib/exec/spawn.js
    +++ b/node_modules/cypress/lib/exec/spawn.js
    @@ -42,7 +42,7 @@ function needsEverythingPipedDirectly() {
    function getStdio(needsXvfb) {
    if (needsEverythingPipedDirectly()) {
    - return 'pipe';
    + return 'inherit';
    }
  5. Commit the new patches folder to Git and push to GitHub
  6. CI machine installs Cypress and other npm modules, then applies the patch and runs the tests. Everything is good now.