mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-06 06:29:45 -06:00
chore(dependency): Upgrade Electron 27 (#28792)
* dependency: upgrades electron from 25 to 26 * bump cache run ci * fix docker img names * ref electron upgrade branch * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * debug * debug * debug * update search string for resize observer error swallow * debug * update integrity check * update electron readme with upgrade troubleshooting section * point to new publish binary workflow branch for electron 27 * update electron readme with locations of chromium & node versions for a given electron version * update node versions and docker image refs * update electron version to 27.1.3 * fix db nativeBinding arg * chore: updating v8 snapshot cache * install setuptools on mac when updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * run workflows on this branch run ci * require addon directly and pass to better-sqlite3 init; debug * rm debug * try loading better-sqlite with a more dynamic filename * bump electron version * bump electron version * bump electron version -- run ci * bump electron version -- run ci * bump electron version -- run ci * bump electron version -- run ci * bump electron version -- run ci * add a step to update workflows.yml to electron upgrade process * reduce retry limit on issue 1244 test to prevent circle from thinking tests have hanged * target main branch of binary publish workflow? run ci * Update .node-version -- run ci * Update CHANGELOG.md * Update module_api_spec.ts * point publish binary back to electron upgrade branch * Adds some logging re: cachedDataVersion * use precise electron version for better-sqlite3 for centos7 * Update CHANGELOG.md * chore: fix issue with bytenode (#28568) * fix jsc for 27 -- run ci * Update smoke.js * fix build * update electron upgrade steps * Update packages/electron/README.md Co-authored-by: Mike McCready <66998419+MikeMcC399@users.noreply.github.com> * Update cli/CHANGELOG.md Co-authored-by: Mike McCready <66998419+MikeMcC399@users.noreply.github.com> * fix DebugEmptyStates component test * try to fix downstream build -- run ci (#28649) Co-authored-by: Cacie Prins <cacieprins@users.noreply.github.com> * point to consolidated binary publish branch * revert webpack-preprocessor-awesome-typescript-loader update * revert certain system tests * increase padding for module api system test duration window * account for differing screenshot sizes * screenshot size differs locally vs ci * update protocol snapshots * Update after-pack-hook.js * fix flaky slideshow * correct the chromium version in changelog * use 18.17.1 internal images * workflow filters * fix trailing checkbox in electron readme * add solution to crashing better-sqlite3 in electron readme * Update packages/electron/README.md Co-authored-by: Bill Glesias <bglesias@gmail.com> * Update scripts/after-pack-hook.js Co-authored-by: Ryan Manuel <ryanm@cypress.io> * Update scripts/after-pack-hook.js Co-authored-by: Ryan Manuel <ryanm@cypress.io> * Update scripts/after-pack-hook.js Co-authored-by: Ryan Manuel <ryanm@cypress.io> * add branch to setup_should_persist_artifacts * debug app e2e test * bump cache * upgrade browsers-internal to chrome 121 * revert to chrome 118 images * bump cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * chore: updating v8 snapshot cache * bump cache --------- Co-authored-by: cypress-bot[bot] <+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Ryan Manuel <ryanm@cypress.io> Co-authored-by: Mike McCready <66998419+MikeMcC399@users.noreply.github.com> Co-authored-by: Bill Glesias <bglesias@gmail.com>
This commit is contained in:
@@ -6,10 +6,23 @@ const path = require('path')
|
||||
const { setupV8Snapshots } = require('@tooling/v8-snapshot')
|
||||
const { flipFuses, FuseVersion, FuseV1Options } = require('@electron/fuses')
|
||||
const { buildEntryPointAndCleanup, cleanupUnneededDependencies } = require('./binary/binary-cleanup')
|
||||
const { getIntegrityCheckSource, getBinaryEntryPointSource, getEncryptionFileSource, getCloudEnvironmentFileSource, validateEncryptionFile, getProtocolFileSource, validateCloudEnvironmentFile, validateProtocolFile } = require('./binary/binary-sources')
|
||||
const { getIntegrityCheckSource, getBinaryEntryPointSource, getBinaryByteNodeEntryPointSource, getEncryptionFileSource, getCloudEnvironmentFileSource, validateEncryptionFile, getProtocolFileSource, validateCloudEnvironmentFile, validateProtocolFile, getIndexJscHash, DUMMY_INDEX_JSC_HASH } = require('./binary/binary-sources')
|
||||
const verify = require('../cli/lib/tasks/verify')
|
||||
const execa = require('execa')
|
||||
const meta = require('./binary/meta')
|
||||
|
||||
const CY_ROOT_DIR = path.join(__dirname, '..')
|
||||
|
||||
const createJscFromCypress = async () => {
|
||||
const args = []
|
||||
|
||||
if (verify.needsSandbox()) {
|
||||
args.push('--no-sandbox')
|
||||
}
|
||||
|
||||
await execa(`${meta.buildAppExecutable()}`, args)
|
||||
}
|
||||
|
||||
module.exports = async function (params) {
|
||||
try {
|
||||
console.log('****************************')
|
||||
@@ -58,6 +71,7 @@ module.exports = async function (params) {
|
||||
}
|
||||
|
||||
if (!['1', 'true'].includes(process.env.DISABLE_SNAPSHOT_REQUIRE)) {
|
||||
const binaryByteNodeEntryPointSource = await getBinaryByteNodeEntryPointSource()
|
||||
const binaryEntryPointSource = await getBinaryEntryPointSource()
|
||||
const encryptionFilePath = path.join(CY_ROOT_DIR, 'packages/server/lib/cloud/encryption.ts')
|
||||
const encryptionFileSource = await getEncryptionFileSource(encryptionFilePath)
|
||||
@@ -76,6 +90,10 @@ module.exports = async function (params) {
|
||||
fs.writeFile(path.join(outputFolder, 'index.js'), binaryEntryPointSource),
|
||||
])
|
||||
|
||||
const integrityCheckSource = getIntegrityCheckSource(outputFolder)
|
||||
|
||||
await fs.writeFile(path.join(outputFolder, 'index.js'), binaryByteNodeEntryPointSource)
|
||||
|
||||
await Promise.all([
|
||||
validateEncryptionFile(encryptionFilePath),
|
||||
validateCloudEnvironmentFile(cloudEnvironmentFilePath),
|
||||
@@ -87,6 +105,7 @@ module.exports = async function (params) {
|
||||
exePathPerPlatform[os.platform()],
|
||||
{
|
||||
version: FuseVersion.V1,
|
||||
resetAdHocDarwinSignature: os.platform() === 'darwin' && os.arch() === 'arm64',
|
||||
[FuseV1Options.LoadBrowserProcessSpecificV8Snapshot]: true,
|
||||
[FuseV1Options.EnableNodeCliInspectArguments]: false,
|
||||
},
|
||||
@@ -99,7 +118,34 @@ module.exports = async function (params) {
|
||||
await cleanupUnneededDependencies(outputFolder)
|
||||
await setupV8Snapshots({
|
||||
cypressAppPath: params.appOutDir,
|
||||
integrityCheckSource: getIntegrityCheckSource(outputFolder),
|
||||
integrityCheckSource,
|
||||
})
|
||||
|
||||
// Use Cypress to create the JSC entry point file
|
||||
await createJscFromCypress()
|
||||
|
||||
const indexJscHash = await getIndexJscHash(outputFolder)
|
||||
|
||||
// This file is not needed at this point since it's been replaced by the jsc. So we remove it.
|
||||
await fs.remove(path.join(outputFolder, 'packages/server/index.js'))
|
||||
await fs.writeFile(path.join(outputFolder, 'index.js'), binaryEntryPointSource)
|
||||
|
||||
await flipFuses(
|
||||
exePathPerPlatform[os.platform()],
|
||||
{
|
||||
version: FuseVersion.V1,
|
||||
[FuseV1Options.LoadBrowserProcessSpecificV8Snapshot]: true,
|
||||
[FuseV1Options.EnableNodeCliInspectArguments]: false,
|
||||
},
|
||||
)
|
||||
|
||||
// Regenerate the v8 snapshots. This time, we replace the JSC hash with what we calculated earlier. We use the existing snapshot script to avoid having to go through the entire v8 snapshot process
|
||||
await setupV8Snapshots({
|
||||
cypressAppPath: params.appOutDir,
|
||||
useExistingSnapshotScript: true,
|
||||
updateSnapshotScriptContents: (contents) => {
|
||||
return contents.replace(DUMMY_INDEX_JSC_HASH, indexJscHash)
|
||||
},
|
||||
})
|
||||
} else {
|
||||
console.log(`value of DISABLE_SNAPSHOT_REQUIRE was ${process.env.DISABLE_SNAPSHOT_REQUIRE}. Skipping snapshot require...`)
|
||||
|
||||
13
scripts/binary/binary-byte-node-entry-point-source.js
Normal file
13
scripts/binary/binary-byte-node-entry-point-source.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const compile = async () => {
|
||||
const bytenode = await import('bytenode')
|
||||
const path = require('path')
|
||||
|
||||
await bytenode.compileFile({
|
||||
filename: path.join(__dirname, 'packages', 'server', 'index.js'),
|
||||
output: path.join(__dirname, 'packages', 'server', 'index.jsc'),
|
||||
})
|
||||
}
|
||||
|
||||
compile().then(() => {
|
||||
process.exit()
|
||||
})
|
||||
@@ -142,19 +142,8 @@ const createServerEntryPointBundle = async (buildAppDir) => {
|
||||
|
||||
await fs.copy(path.join(workingDir, 'index.js'), path.join(buildAppDir, 'packages', 'server', 'index.js'))
|
||||
|
||||
console.log(`compiling server entry point bundle to ${path.join(buildAppDir, 'packages', 'server', 'index.jsc')}`)
|
||||
|
||||
// Use bytenode to compile the entry point bundle. This will save time on the v8 compile step and ensure the integrity of the entry point
|
||||
const bytenode = await import('bytenode')
|
||||
|
||||
await bytenode.compileFile({
|
||||
filename: path.join(buildAppDir, 'packages', 'server', 'index.js'),
|
||||
output: path.join(buildAppDir, 'packages', 'server', 'index.jsc'),
|
||||
electron: true,
|
||||
})
|
||||
|
||||
// Convert these inputs to a relative file path. Note that these paths are posix paths.
|
||||
return [...Object.keys(esbuildResult.metafile.inputs)].map((input) => `./${input}`)
|
||||
return [...Object.keys(esbuildResult.metafile.inputs)].filter((input) => input !== 'packages/server/index.js').map((input) => `./${input}`)
|
||||
}
|
||||
|
||||
const buildEntryPointAndCleanup = async (buildAppDir) => {
|
||||
|
||||
@@ -94,7 +94,7 @@ function validateElectron (electron) {
|
||||
|
||||
function validateFs (fs) {
|
||||
// Hard coded function as this is electron code and there's not an easy way to get the function string at package time. If this fails on an updated version of electron, we'll need to update this.
|
||||
if (originalToString.call(fs.readFileSync) !== `function(e,s){const r=splitPath(e);if(!r.isAsar)return F.apply(this,arguments);const{asarPath:n,filePath:a}=r,o=getOrCreateArchive(n);if(!o)throw createError("INVALID_ARCHIVE",{asarPath:n});const c=o.getFileInfo(a);if(!c)throw createError("NOT_FOUND",{asarPath:n,filePath:a});if(0===c.size)return s?"":i.Buffer.alloc(0);if(c.unpacked){const e=o.copyFileOut(a);return t.readFileSync(e,s)}if(s){if("string"==typeof s)s={encoding:s};else if("object"!=typeof s)throw new TypeError("Bad arguments")}else s={encoding:null};const{encoding:f}=s,l=i.Buffer.alloc(c.size),u=o.getFdAndValidateIntegrityLater();if(!(u>=0))throw createError("NOT_FOUND",{asarPath:n,filePath:a});return logASARAccess(n,a,c.offset),t.readSync(u,l,0,c.size,c.offset),validateBufferIntegrity(l,c.integrity),f?l.toString(f):l}`) {
|
||||
if (originalToString.call(fs.readFileSync) !== `function(e,s){const r=splitPath(e);if(!r.isAsar)return F.apply(this,arguments);const{asarPath:n,filePath:i}=r,o=getOrCreateArchive(n);if(!o)throw createError("INVALID_ARCHIVE",{asarPath:n});const c=o.getFileInfo(i);if(!c)throw createError("NOT_FOUND",{asarPath:n,filePath:i});if(0===c.size)return s?"":a.Buffer.alloc(0);if(c.unpacked){const e=o.copyFileOut(i);return t.readFileSync(e,s)}if(s){if("string"==typeof s)s={encoding:s};else if("object"!=typeof s)throw new TypeError("Bad arguments")}else s={encoding:null};const{encoding:f}=s,l=a.Buffer.alloc(c.size),u=o.getFdAndValidateIntegrityLater();if(!(u>=0))throw createError("NOT_FOUND",{asarPath:n,filePath:i});return logASARAccess(n,i,c.offset),t.readSync(u,l,0,c.size,c.offset),validateBufferIntegrity(l,c.integrity),f?l.toString(f):l}`) {
|
||||
console.error(`Integrity check failed for toString.call(fs.readFileSync)`)
|
||||
throw new Error(integrityErrorMessage)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ const path = require('path')
|
||||
const esbuild = require('esbuild')
|
||||
|
||||
const escapeString = (string) => string.replaceAll(`\``, `\\\``).replaceAll(`$`, `\\$`)
|
||||
const secret = require('crypto').randomBytes(48).toString('hex')
|
||||
const DUMMY_INDEX_JSC_HASH = 'abcddcbaabcddcbaabcddcbaabcddcba'
|
||||
|
||||
function read (file) {
|
||||
const pathToFile = require.resolve(`./${file}`)
|
||||
@@ -24,22 +26,37 @@ const getBinaryEntryPointSource = async () => {
|
||||
return esbuildResult.outputFiles[0].text
|
||||
}
|
||||
|
||||
const getBinaryByteNodeEntryPointSource = async () => {
|
||||
const esbuildResult = await esbuild.build({
|
||||
entryPoints: [require.resolve('./binary-byte-node-entry-point-source.js')],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
write: false,
|
||||
minify: true,
|
||||
treeShaking: true,
|
||||
})
|
||||
|
||||
return esbuildResult.outputFiles[0].text
|
||||
}
|
||||
|
||||
const getIntegrityCheckSource = (baseDirectory) => {
|
||||
const fileSource = read('binary-integrity-check-source.js')
|
||||
const secret = require('crypto').randomBytes(48).toString('hex')
|
||||
|
||||
const mainIndexHash = crypto.createHmac('md5', secret).update(fs.readFileSync(path.join(baseDirectory, './index.js'), 'utf8')).digest('hex')
|
||||
const indexJscHash = crypto.createHmac('md5', secret).update(fs.readFileSync(path.join(baseDirectory, './packages/server/index.jsc'), 'utf8')).digest('hex')
|
||||
|
||||
return fileSource.split('\n').join(`\n `)
|
||||
.replaceAll('MAIN_INDEX_HASH', mainIndexHash)
|
||||
.replaceAll('INDEX_JSC_HASH', indexJscHash)
|
||||
.replaceAll('INDEX_JSC_HASH', DUMMY_INDEX_JSC_HASH)
|
||||
.replaceAll('HMAC_SECRET', secret)
|
||||
.replaceAll('CRYPTO_CREATE_HMAC_TO_STRING', escapeString(crypto.createHmac.toString()))
|
||||
.replaceAll('CRYPTO_HMAC_UPDATE_TO_STRING', escapeString(crypto.Hmac.prototype.update.toString()))
|
||||
.replaceAll('CRYPTO_HMAC_DIGEST_TO_STRING', escapeString(crypto.Hmac.prototype.digest.toString()))
|
||||
}
|
||||
|
||||
const getIndexJscHash = (baseDirectory) => {
|
||||
return crypto.createHmac('md5', secret).update(fs.readFileSync(path.join(baseDirectory, './packages/server/index.jsc'), 'utf8')).digest('hex')
|
||||
}
|
||||
|
||||
const getEncryptionFileSource = async (encryptionFilePath) => {
|
||||
const fileContents = await fs.readFile(encryptionFilePath, 'utf8')
|
||||
|
||||
@@ -102,6 +119,7 @@ const validateProtocolFile = async (protocolFilePath) => {
|
||||
|
||||
module.exports = {
|
||||
getBinaryEntryPointSource,
|
||||
getBinaryByteNodeEntryPointSource,
|
||||
getIntegrityCheckSource,
|
||||
getEncryptionFileSource,
|
||||
validateEncryptionFile,
|
||||
@@ -109,4 +127,6 @@ module.exports = {
|
||||
validateCloudEnvironmentFile,
|
||||
getProtocolFileSource,
|
||||
validateProtocolFile,
|
||||
getIndexJscHash,
|
||||
DUMMY_INDEX_JSC_HASH,
|
||||
}
|
||||
|
||||
@@ -8,17 +8,13 @@ import electron from '../../packages/electron'
|
||||
import la from 'lazy-ass'
|
||||
import { promisify } from 'util'
|
||||
import glob from 'glob'
|
||||
|
||||
import * as packages from './util/packages'
|
||||
import * as meta from './meta'
|
||||
import xvfb from '../../cli/lib/exec/xvfb'
|
||||
import smoke from './smoke'
|
||||
import { spawn, execSync } from 'child_process'
|
||||
import { transformRequires } from './util/transform-requires'
|
||||
import execa from 'execa'
|
||||
import { testStaticAssets } from './util/testStaticAssets'
|
||||
import performanceTracking from '../../system-tests/lib/performance'
|
||||
import verify from '../../cli/lib/tasks/verify'
|
||||
import * as electronBuilder from 'electron-builder'
|
||||
|
||||
const globAsync = promisify(glob)
|
||||
@@ -307,26 +303,6 @@ export async function packageElectronApp (options: BuildCypressAppOpts) {
|
||||
|
||||
console.log(stdout)
|
||||
|
||||
// runSmokeTests
|
||||
let usingXvfb = xvfb.isNeeded()
|
||||
|
||||
try {
|
||||
if (usingXvfb) {
|
||||
await xvfb.start()
|
||||
}
|
||||
|
||||
log(`#testExecutableVersion ${meta.buildAppExecutable()}`)
|
||||
await testExecutableVersion(meta.buildAppExecutable(), version)
|
||||
|
||||
const executablePath = meta.buildAppExecutable()
|
||||
|
||||
await smoke.test(executablePath, meta.buildAppDir())
|
||||
} finally {
|
||||
if (usingXvfb) {
|
||||
await xvfb.stop()
|
||||
}
|
||||
}
|
||||
|
||||
// verifyAppCanOpen
|
||||
if (platform === 'darwin' && !skipSigning) {
|
||||
const appFolder = meta.zipDir()
|
||||
@@ -417,26 +393,3 @@ async function testDistVersion (distDir: string, version: string) {
|
||||
|
||||
console.log('✅ using node --version works')
|
||||
}
|
||||
|
||||
async function testExecutableVersion (buildAppExecutable: string, version: string) {
|
||||
log('#testVersion')
|
||||
|
||||
console.log('testing built app executable version')
|
||||
console.log(`by calling: ${buildAppExecutable} --version`)
|
||||
|
||||
const args = ['--version']
|
||||
|
||||
if (verify.needsSandbox()) {
|
||||
args.push('--no-sandbox')
|
||||
}
|
||||
|
||||
const result = await execa(buildAppExecutable, args)
|
||||
|
||||
la(result.stdout, 'missing output when getting built version', result)
|
||||
|
||||
console.log('built app version', result.stdout)
|
||||
la(result.stdout.trim() === version.trim(), 'different version reported',
|
||||
result.stdout, 'from input version to build', version)
|
||||
|
||||
console.log('✅ using --version on the Cypress binary works')
|
||||
}
|
||||
|
||||
@@ -23,6 +23,17 @@ const uploadUtils = require('./util/upload')
|
||||
const { uploadArtifactToS3 } = require('./upload-build-artifact')
|
||||
const { moveBinaries } = require('./move-binaries')
|
||||
const { exec } = require('child_process')
|
||||
const xvfb = require('../../cli/lib/exec/xvfb')
|
||||
const smoke = require('./smoke')
|
||||
const verify = require('../../cli/lib/tasks/verify')
|
||||
const execa = require('execa')
|
||||
|
||||
const log = function (msg) {
|
||||
const time = new Date()
|
||||
const timeStamp = time.toLocaleTimeString()
|
||||
|
||||
console.log(timeStamp, chalk.yellow(msg), chalk.blue(meta.PLATFORM))
|
||||
}
|
||||
|
||||
const success = (str) => {
|
||||
return console.log(chalk.bgGreen(` ${chalk.black(str)} `))
|
||||
@@ -50,6 +61,29 @@ const askMissingOptions = function (properties = []) {
|
||||
return questionsRemain(pickedQuestions)
|
||||
}
|
||||
|
||||
async function testExecutableVersion (buildAppExecutable, version) {
|
||||
log('#testVersion')
|
||||
|
||||
console.log('testing built app executable version')
|
||||
console.log(`by calling: ${buildAppExecutable} --version`)
|
||||
|
||||
const args = ['--version']
|
||||
|
||||
if (verify.needsSandbox()) {
|
||||
args.push('--no-sandbox')
|
||||
}
|
||||
|
||||
const result = await execa(buildAppExecutable, args)
|
||||
|
||||
la(result.stdout, 'missing output when getting built version', result)
|
||||
|
||||
console.log('built app version', result.stdout)
|
||||
la(result.stdout.trim() === version.trim(), 'different version reported',
|
||||
result.stdout, 'from input version to build', version)
|
||||
|
||||
console.log('✅ using --version on the Cypress binary works')
|
||||
}
|
||||
|
||||
// hack for @packages/server modifying cwd
|
||||
process.chdir(cwd)
|
||||
|
||||
@@ -214,6 +248,38 @@ const deploy = {
|
||||
})
|
||||
},
|
||||
|
||||
async smoke (options) {
|
||||
console.log('#smoke')
|
||||
|
||||
if (options == null) {
|
||||
options = this.parseOptions(process.argv)
|
||||
}
|
||||
|
||||
debug('parsed build options %o', options)
|
||||
|
||||
await askMissingOptions(['version'])(options)
|
||||
|
||||
// runSmokeTests
|
||||
let usingXvfb = xvfb.isNeeded()
|
||||
|
||||
try {
|
||||
if (usingXvfb) {
|
||||
await xvfb.start()
|
||||
}
|
||||
|
||||
log(`#testExecutableVersion ${meta.buildAppExecutable()}`)
|
||||
await testExecutableVersion(meta.buildAppExecutable(), options.version)
|
||||
|
||||
const executablePath = meta.buildAppExecutable()
|
||||
|
||||
await smoke.test(executablePath, meta.buildAppDir())
|
||||
} finally {
|
||||
if (usingXvfb) {
|
||||
await xvfb.stop()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
zip (options) {
|
||||
console.log('#zip')
|
||||
if (!options) {
|
||||
|
||||
@@ -299,7 +299,7 @@ const runIntegrityTest = async function (buildAppExecutable, buildAppDir, e2e) {
|
||||
console.error(`extra keys in electron process: ${extraKeys}`)
|
||||
}
|
||||
|
||||
const allowList = ['regeneratorRuntime', '__core-js_shared__', 'getSnapshotResult', 'supportTypeScript']
|
||||
const allowList = ['regeneratorRuntime', '__core-js_shared__', 'getSnapshotResult', 'supportTypeScript', 'Iterator']
|
||||
|
||||
await testAlteringEntryPoint(`(${compareGlobals.toString()})()`, `extra keys in electron process: ${allowList}\nIntegrity check failed with expected stack length 9 but got 10`)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ const { getNextVersionForBinary } = require('../get-next-version')
|
||||
const { nextVersion } = await getNextVersionForBinary()
|
||||
|
||||
const body = JSON.stringify({
|
||||
branch: 'cacie/upgrade-electron-27',
|
||||
parameters: {
|
||||
temp_dir: os.tmpdir(),
|
||||
sha: process.env.CIRCLE_SHA1,
|
||||
|
||||
Reference in New Issue
Block a user