mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-13 02:09:52 -06:00
Closes Mesa/GLX related warnings shown when running Cypress #29521 The primary Cypress process can emit benign warnings related to Mesa/GLX when running in certain Linux environments or containers. These warnings are related to graphics drivers and X11 display settings, but are not necessary for Cypress to execute correctly. This PR suppresses these warnings from stdout, similar to other benign Electron warnings: - `error: XDG_RUNTIME_DIR is invalid or not set in the environment.` - `MESA: error: ZINK: failed to choose pdev` - `glx: failed to create drisw screen` 1. Execute Cypress tests in run mode on a Linux machine or container with minimal graphics drivers installed 2. Execute Cypress tests in run mode in a Docker container using a basic Linux image (e.g. ubuntu:latest) 3. Verify the suppressed warnings no longer appear in the terminal output Users will no longer see benign graphics-related warnings in their terminal output when running Cypress in Linux environments with minimal graphics support. This reduces noise in the terminal output while not affecting any actual test functionality. - [x] Have tests been added/updated? - [NA] Has a PR for user-facing changes been opened in cypress-documentation? - [NA] Have API changes been updated in the type definitions? fix linting change word for CI Update cli/CHANGELOG.md Going with the suggesting. Co-authored-by: Mike McCready <66998419+MikeMcC399@users.noreply.github.com> fix linting update readme with another fixed issue Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
406 lines
15 KiB
JavaScript
406 lines
15 KiB
JavaScript
const _ = require('lodash')
|
|
const os = require('os')
|
|
const cp = require('child_process')
|
|
const path = require('path')
|
|
const Promise = require('bluebird')
|
|
const debug = require('debug')('cypress:cli')
|
|
const debugVerbose = require('debug')('cypress-verbose:cli')
|
|
|
|
const util = require('../util')
|
|
const state = require('../tasks/state')
|
|
const xvfb = require('./xvfb')
|
|
const verify = require('../tasks/verify')
|
|
const errors = require('../errors')
|
|
const readline = require('readline')
|
|
|
|
const isXlibOrLibudevRe = /^(?:Xlib|libudev)/
|
|
const isHighSierraWarningRe = /\*\*\* WARNING/
|
|
const isRenderWorkerRe = /\.RenderWorker-/
|
|
|
|
// Chromium (which Electron uses) always makes several attempts to connect to the system dbus.
|
|
// This works fine in most desktop environments, but in a docker container, there is no dbus service
|
|
// and Chromium emits several error lines, similar to these:
|
|
|
|
// [1957:0406/160550.146820:ERROR:bus.cc(392)] Failed to connect to the bus: Failed to connect to socket /var/run/dbus/system_bus_socket: No such file or directory
|
|
// [1957:0406/160550.147994:ERROR:bus.cc(392)] Failed to connect to the bus: Address does not contain a colon
|
|
|
|
// These warnings are absolutely harmless. Failure to connect to dbus means that electron won't be able to access the user's
|
|
// credential wallet (none exists in a docker container) and won't show up in the system tray (again, none exists).
|
|
// Failure to connect is expected and normal here, but users frequently misidentify these errors as the cause of their problems.
|
|
|
|
// https://github.com/cypress-io/cypress/issues/19299
|
|
const isDbusWarning = /Failed to connect to the bus:/
|
|
|
|
// Electron began logging these on self-signed certs with 17.0.0-alpha.4.
|
|
// Once this is fixed upstream this regex can be removed: https://github.com/electron/electron/issues/34583
|
|
// Sample:
|
|
// [3801:0606/152837.383892:ERROR:cert_verify_proc_builtin.cc(681)] CertVerifyProcBuiltin for www.googletagmanager.com failed:
|
|
// ----- Certificate i=0 (OU=Cypress Proxy Server Certificate,O=Cypress Proxy CA,L=Internet,ST=Internet,C=Internet,CN=www.googletagmanager.com) -----
|
|
// ERROR: No matching issuer found
|
|
const isCertVerifyProcBuiltin = /(^\[.*ERROR:cert_verify_proc_builtin\.cc|^----- Certificate i=0 \(OU=Cypress Proxy|^ERROR: No matching issuer found$)/
|
|
|
|
/**
|
|
* Electron logs benign warnings about Vulkan when run on hosts that do not have a GPU. This is coming from the primary Electron process,
|
|
* and not the browser being used for tests.
|
|
* Samples:
|
|
* Warning: loader_scanned_icd_add: Driver /usr/lib/x86_64-linux-gnu/libvulkan_intel.so supports Vulkan 1.2, but only supports loader interface version 4. Interface version 5 or newer required to support this version of Vulkan (Policy #LDP_DRIVER_7)
|
|
* Warning: loader_scanned_icd_add: Driver /usr/lib/x86_64-linux-gnu/libvulkan_lvp.so supports Vulkan 1.1, but only supports loader interface version 4. Interface version 5 or newer required to support this version of Vulkan (Policy #LDP_DRIVER_7)
|
|
* Warning: loader_scanned_icd_add: Driver /usr/lib/x86_64-linux-gnu/libvulkan_radeon.so supports Vulkan 1.2, but only supports loader interface version 4. Interface version 5 or newer required to support this verison of Vulkan (Policy #LDP_DRIVER_7)
|
|
* Warning: Layer VK_LAYER_MESA_device_select uses API version 1.2 which is older than the application specified API version of 1.3. May cause issues.
|
|
*/
|
|
|
|
const isHostVulkanDriverWarning = /^Warning:.+(#LDP_DRIVER_7|VK_LAYER_MESA_device_select).+/
|
|
|
|
/**
|
|
* Electron logs benign warnings about Vulkan when run in docker containers whose host does not have a GPU. This is coming from the primary
|
|
* Electron process, and not the browser being used for tests.
|
|
* Sample:
|
|
* Warning: vkCreateInstance: Found no drivers!
|
|
* Warning: vkCreateInstance failed with VK_ERROR_INCOMPATIBLE_DRIVER
|
|
* at CheckVkSuccessImpl (../../third_party/dawn/src/dawn/native/vulkan/VulkanError.cpp:88)
|
|
* at CreateVkInstance (../../third_party/dawn/src/dawn/native/vulkan/BackendVk.cpp:458)
|
|
* at Initialize (../../third_party/dawn/src/dawn/native/vulkan/BackendVk.cpp:344)
|
|
* at Create (../../third_party/dawn/src/dawn/native/vulkan/BackendVk.cpp:266)
|
|
* at operator() (../../third_party/dawn/src/dawn/native/vulkan/BackendVk.cpp:521)
|
|
*/
|
|
|
|
const isContainerVulkanDriverWarning = /^Warning: vkCreateInstance/
|
|
|
|
const isContainerVulkanStack = /^\s*at (CheckVkSuccessImpl|CreateVkInstance|Initialize|Create|operator).+(VulkanError|BackendVk).cpp/
|
|
|
|
/**
|
|
* In Electron 32.0.0 a new debug scenario log message started appearing when iframes navigate to about:blank. This is a benign message.
|
|
* https://github.com/electron/electron/issues/44368
|
|
* Sample:
|
|
* [78887:1023/114920.074882:ERROR:debug_utils.cc(14)] Hit debug scenario: 4
|
|
*/
|
|
const isDebugScenario4 = /^\[[^\]]+debug_utils\.cc[^\]]+\] Hit debug scenario: 4/
|
|
|
|
/**
|
|
* In Electron 32.0.0 a new EGL driver message started appearing when running on Linux. This is a benign message.
|
|
* https://github.com/electron/electron/issues/43415
|
|
* Sample:
|
|
* [78887:1023/114920.074882:ERROR:gl_display.cc(14)] EGL Driver message (Error) eglQueryDeviceAttribEXT: Bad attribute.
|
|
*/
|
|
const isEGLDriverMessage = /^\[[^\]]+gl_display\.cc[^\]]+\] EGL Driver message \(Error\) eglQueryDeviceAttribEXT: Bad attribute\./
|
|
|
|
/**
|
|
* Mesa/GLX related warnings that occur in certain Linux environments without proper GPU support
|
|
* or when running in containers. These are benign warnings that don't affect functionality.
|
|
* Samples:
|
|
* error: XDG_RUNTIME_DIR is invalid or not set in the environment.
|
|
* MESA: error: ZINK: failed to choose pdev
|
|
* glx: failed to create drisw screen
|
|
*/
|
|
const isXdgRuntimeError = /^error: XDG_RUNTIME_DIR is invalid or not set/
|
|
const isMesaZinkError = /^MESA: error: ZINK: failed to choose pdev/
|
|
const isGlxDriverError = /^glx: failed to create drisw screen/
|
|
|
|
const GARBAGE_WARNINGS = [
|
|
isXlibOrLibudevRe,
|
|
isHighSierraWarningRe,
|
|
isRenderWorkerRe,
|
|
isDbusWarning,
|
|
isCertVerifyProcBuiltin,
|
|
isHostVulkanDriverWarning,
|
|
isContainerVulkanDriverWarning,
|
|
isContainerVulkanStack,
|
|
isDebugScenario4,
|
|
isEGLDriverMessage,
|
|
isXdgRuntimeError,
|
|
isMesaZinkError,
|
|
isGlxDriverError,
|
|
]
|
|
|
|
const isGarbageLineWarning = (str) => {
|
|
return _.some(GARBAGE_WARNINGS, (re) => {
|
|
return re.test(str)
|
|
})
|
|
}
|
|
|
|
function isPlatform (platform) {
|
|
return os.platform() === platform
|
|
}
|
|
|
|
function needsStderrPiped (needsXvfb) {
|
|
return _.some([
|
|
isPlatform('darwin'),
|
|
|
|
(needsXvfb && isPlatform('linux')),
|
|
|
|
util.isPossibleLinuxWithIncorrectDisplay(),
|
|
])
|
|
}
|
|
|
|
function needsEverythingPipedDirectly () {
|
|
return isPlatform('win32')
|
|
}
|
|
|
|
function getStdio (needsXvfb) {
|
|
if (needsEverythingPipedDirectly()) {
|
|
return 'pipe'
|
|
}
|
|
|
|
// https://github.com/cypress-io/cypress/issues/921
|
|
// https://github.com/cypress-io/cypress/issues/1143
|
|
// https://github.com/cypress-io/cypress/issues/1745
|
|
if (needsStderrPiped(needsXvfb)) {
|
|
// returning pipe here so we can massage stderr
|
|
// and remove garbage from Xlib and libuv
|
|
// due to starting the Xvfb process on linux
|
|
return ['inherit', 'inherit', 'pipe']
|
|
}
|
|
|
|
return 'inherit'
|
|
}
|
|
|
|
module.exports = {
|
|
isGarbageLineWarning,
|
|
|
|
start (args, options = {}) {
|
|
const needsXvfb = xvfb.isNeeded()
|
|
let executable = state.getPathToExecutable(state.getBinaryDir())
|
|
|
|
if (util.getEnv('CYPRESS_RUN_BINARY')) {
|
|
executable = path.resolve(util.getEnv('CYPRESS_RUN_BINARY'))
|
|
}
|
|
|
|
debug('needs to start own Xvfb?', needsXvfb)
|
|
|
|
// Always push cwd into the args
|
|
// which additionally acts as a signal to the
|
|
// binary that it was invoked through the NPM module
|
|
args = args || []
|
|
if (typeof args === 'string') {
|
|
args = [args]
|
|
}
|
|
|
|
args = [...args, '--cwd', process.cwd(), '--userNodePath', process.execPath, '--userNodeVersion', process.versions.node]
|
|
|
|
_.defaults(options, {
|
|
dev: false,
|
|
env: process.env,
|
|
detached: false,
|
|
stdio: getStdio(needsXvfb),
|
|
})
|
|
|
|
const spawn = (overrides = {}) => {
|
|
return new Promise((resolve, reject) => {
|
|
_.defaults(overrides, {
|
|
onStderrData: false,
|
|
})
|
|
|
|
const { onStderrData } = overrides
|
|
const envOverrides = util.getEnvOverrides(options)
|
|
const electronArgs = []
|
|
const node11WindowsFix = isPlatform('win32')
|
|
|
|
let startScriptPath
|
|
|
|
if (options.dev) {
|
|
executable = 'node'
|
|
// if we're in dev then reset
|
|
// the launch cmd to be 'npm run dev'
|
|
startScriptPath = path.resolve(__dirname, '..', '..', '..', 'scripts', 'start.js'),
|
|
|
|
debug('in dev mode the args became %o', args)
|
|
}
|
|
|
|
if (!options.dev && verify.needsSandbox()) {
|
|
electronArgs.push('--no-sandbox')
|
|
}
|
|
|
|
// strip dev out of child process options
|
|
/**
|
|
* @type {import('child_process').ForkOptions}
|
|
*/
|
|
let stdioOptions = _.pick(options, 'env', 'detached', 'stdio')
|
|
|
|
// figure out if we're going to be force enabling or disabling colors.
|
|
// also figure out whether we should force stdout and stderr into thinking
|
|
// it is a tty as opposed to a pipe.
|
|
stdioOptions.env = _.extend({}, stdioOptions.env, envOverrides)
|
|
|
|
if (node11WindowsFix) {
|
|
stdioOptions = _.extend({}, stdioOptions, { windowsHide: false })
|
|
}
|
|
|
|
if (util.isPossibleLinuxWithIncorrectDisplay()) {
|
|
// make sure we use the latest DISPLAY variable if any
|
|
debug('passing DISPLAY', process.env.DISPLAY)
|
|
stdioOptions.env.DISPLAY = process.env.DISPLAY
|
|
}
|
|
|
|
if (stdioOptions.env.ELECTRON_RUN_AS_NODE) {
|
|
// Since we are running electron as node, we need to add an entry point file.
|
|
startScriptPath = path.join(state.getBinaryPkgPath(path.dirname(executable)), '..', 'index.js')
|
|
} else {
|
|
// Start arguments with "--" so Electron knows these are OUR
|
|
// arguments and does not try to sanitize them. Otherwise on Windows
|
|
// an url in one of the arguments crashes it :(
|
|
// https://github.com/cypress-io/cypress/issues/5466
|
|
args = [...electronArgs, '--', ...args]
|
|
}
|
|
|
|
if (startScriptPath) {
|
|
args.unshift(startScriptPath)
|
|
}
|
|
|
|
if (process.env.CYPRESS_INTERNAL_DEV_DEBUG) {
|
|
args.unshift(process.env.CYPRESS_INTERNAL_DEV_DEBUG)
|
|
}
|
|
|
|
debug('spawn args %o %o', args, _.omit(stdioOptions, 'env'))
|
|
debug('spawning Cypress with executable: %s', executable)
|
|
|
|
const child = cp.spawn(executable, args, stdioOptions)
|
|
|
|
function resolveOn (event) {
|
|
return function (code, signal) {
|
|
debug('child event fired %o', { event, code, signal })
|
|
|
|
if (code === null) {
|
|
const errorObject = errors.errors.childProcessKilled(event, signal)
|
|
|
|
return errors.getError(errorObject).then(reject)
|
|
}
|
|
|
|
resolve(code)
|
|
}
|
|
}
|
|
|
|
child.on('close', resolveOn('close'))
|
|
child.on('exit', resolveOn('exit'))
|
|
child.on('error', reject)
|
|
|
|
if (isPlatform('win32')) {
|
|
const rl = readline.createInterface({
|
|
input: process.stdin,
|
|
output: process.stdout,
|
|
})
|
|
|
|
// on windows, SIGINT does not propagate to the child process when ctrl+c is pressed
|
|
// this makes sure all nested processes are closed(ex: firefox inside the server)
|
|
rl.on('SIGINT', function () {
|
|
let kill = require('tree-kill')
|
|
|
|
kill(child.pid, 'SIGINT')
|
|
})
|
|
}
|
|
|
|
// if stdio options is set to 'pipe', then
|
|
// we should set up pipes:
|
|
// process STDIN (read stream) => child STDIN (writeable)
|
|
// child STDOUT => process STDOUT
|
|
// child STDERR => process STDERR with additional filtering
|
|
if (child.stdin) {
|
|
debug('piping process STDIN into child STDIN')
|
|
process.stdin.pipe(child.stdin)
|
|
}
|
|
|
|
if (child.stdout) {
|
|
debug('piping child STDOUT to process STDOUT')
|
|
child.stdout.pipe(process.stdout)
|
|
}
|
|
|
|
// if this is defined then we are manually piping for linux
|
|
// to filter out the garbage
|
|
if (child.stderr) {
|
|
debug('piping child STDERR to process STDERR')
|
|
child.stderr.on('data', (data) => {
|
|
const str = data.toString()
|
|
|
|
// bail if this is warning line garbage
|
|
if (isGarbageLineWarning(str)) {
|
|
debugVerbose(str)
|
|
|
|
return
|
|
}
|
|
|
|
// if we have a callback and this explicitly returns
|
|
// false then bail
|
|
if (onStderrData && onStderrData(str)) {
|
|
return
|
|
}
|
|
|
|
// else pass it along!
|
|
process.stderr.write(data)
|
|
})
|
|
}
|
|
|
|
// https://github.com/cypress-io/cypress/issues/1841
|
|
// https://github.com/cypress-io/cypress/issues/5241
|
|
// In some versions of node, it will throw on windows
|
|
// when you close the parent process after piping
|
|
// into the child process. unpiping does not seem
|
|
// to have any effect. so we're just catching the
|
|
// error here and not doing anything.
|
|
process.stdin.on('error', (err) => {
|
|
if (['EPIPE', 'ENOTCONN'].includes(err.code)) {
|
|
return
|
|
}
|
|
|
|
throw err
|
|
})
|
|
|
|
if (stdioOptions.detached) {
|
|
child.unref()
|
|
}
|
|
})
|
|
}
|
|
|
|
const spawnInXvfb = () => {
|
|
return xvfb
|
|
.start()
|
|
.then(userFriendlySpawn)
|
|
.finally(xvfb.stop)
|
|
}
|
|
|
|
const userFriendlySpawn = (linuxWithDisplayEnv) => {
|
|
debug('spawning, should retry on display problem?', Boolean(linuxWithDisplayEnv))
|
|
|
|
let brokenGtkDisplay
|
|
|
|
const overrides = {}
|
|
|
|
if (linuxWithDisplayEnv) {
|
|
_.extend(overrides, {
|
|
electronLogging: true,
|
|
onStderrData (str) {
|
|
// if we receive a broken pipe anywhere
|
|
// then we know that's why cypress exited early
|
|
if (util.isBrokenGtkDisplay(str)) {
|
|
brokenGtkDisplay = true
|
|
}
|
|
},
|
|
})
|
|
}
|
|
|
|
return spawn(overrides)
|
|
.then((code) => {
|
|
if (code !== 0 && brokenGtkDisplay) {
|
|
util.logBrokenGtkDisplayWarning()
|
|
|
|
return spawnInXvfb()
|
|
}
|
|
|
|
return code
|
|
})
|
|
// we can format and handle an error message from the code above
|
|
// prevent wrapping error again by using "known: undefined" filter
|
|
.catch({ known: undefined }, errors.throwFormErrorText(errors.errors.unexpected))
|
|
}
|
|
|
|
if (needsXvfb) {
|
|
return spawnInXvfb()
|
|
}
|
|
|
|
// if we are on linux and there's already a DISPLAY
|
|
// set, then we may need to rerun cypress after
|
|
// spawning our own Xvfb server
|
|
const linuxWithDisplayEnv = util.isPossibleLinuxWithIncorrectDisplay()
|
|
|
|
return userFriendlySpawn(linuxWithDisplayEnv)
|
|
},
|
|
}
|