diff --git a/scripts/after-pack-hook.js b/scripts/after-pack-hook.js index f5a48e322f36..1a2f9a83a391 100644 --- a/scripts/after-pack-hook.js +++ b/scripts/after-pack-hook.js @@ -68,6 +68,9 @@ module.exports = async function (params) { // Build out the entry point and clean up prior to setting up v8 snapshots so that the state of the binary is correct await buildEntryPointAndCleanup(outputFolder) - await setupV8Snapshots({ cypressAppPath: params.appOutDir, integrityCheckSource: getIntegrityCheckSource(outputFolder) }) + await setupV8Snapshots({ + cypressAppPath: params.appOutDir, + integrityCheckSource: getIntegrityCheckSource(outputFolder), + }) } } diff --git a/scripts/binary/binary-cleanup.js b/scripts/binary/binary-cleanup.js index da78053941fe..0b985fe1de9e 100644 --- a/scripts/binary/binary-cleanup.js +++ b/scripts/binary/binary-cleanup.js @@ -110,7 +110,7 @@ const getDependencyPathsToKeep = async (buildAppDir) => { }) } - return [...Object.keys(esbuildResult.metafile.inputs), ...entryPoints] + return [...Object.keys(esbuildResult.metafile.inputs), ...entryPoints, 'package.json'] } const createServerEntryPointBundle = async (buildAppDir) => { @@ -143,25 +143,30 @@ const createServerEntryPointBundle = async (buildAppDir) => { 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}`) } const buildEntryPointAndCleanup = async (buildAppDir) => { - // 1. Retrieve all dependencies that still need to be kept in the binary. In theory, we could use the bundles generated here as single files within the binary, - // but for now, we just track on the dependencies that get pulled in - const keptDependencies = [...await getDependencyPathsToKeep(buildAppDir), 'package.json'] + const [keptDependencies, serverEntryPointBundleDependencies] = await Promise.all([ + // 1. Retrieve all dependencies that still need to be kept in the binary. In theory, we could use the bundles generated here as single files within the binary, + // but for now, we just track on the dependencies that get pulled in + getDependencyPathsToKeep(buildAppDir), + // 2. Create a bundle for the server entry point. This will be used to start the server in the binary. It returns the dependencies that are pulled in by this bundle that potentially can now be removed + createServerEntryPointBundle(buildAppDir), + ]) - // 2. Create the entry point bundle and then gather the dependencies that could potentially be removed from the binary due to being in the snapshot or in the entry point bundle + // 3. Gather the dependencies that could potentially be removed from the binary due to being in the snapshot or in the entry point bundle const potentiallyRemovedDependencies = [ ...snapshotMetadata.healthy, ...snapshotMetadata.deferred, ...snapshotMetadata.norewrite, - ...await createServerEntryPointBundle(buildAppDir), + ...serverEntryPointBundleDependencies, ] console.log(`potentially removing ${potentiallyRemovedDependencies.length} dependencies`) - // 3. Remove all dependencies that are in the snapshot but not in the list of kept dependencies from the binary + // 4. Remove all dependencies that are in the snapshot but not in the list of kept dependencies from the binary await Promise.all(potentiallyRemovedDependencies.map(async (dependency) => { const typeScriptlessDependency = dependency.replace(/\.ts$/, '.js') @@ -171,10 +176,10 @@ const buildEntryPointAndCleanup = async (buildAppDir) => { } })) - // 4. Consolidate dependencies that are safe to consolidate (`lodash` and `bluebird`) + // 5. Consolidate dependencies that are safe to consolidate (`lodash` and `bluebird`) await consolidateDeps({ projectBaseDir: buildAppDir }) - // 5. Remove various unnecessary files from the binary to further clean things up. Likely, there is additional work that can be done here + // 6. Remove various unnecessary files from the binary to further clean things up. Likely, there is additional work that can be done here await del([ // Remove test files path.join(buildAppDir, '**', 'test'), @@ -226,7 +231,7 @@ const buildEntryPointAndCleanup = async (buildAppDir) => { path.join(buildAppDir, '**', 'yarn.lock'), ], { force: true }) - // 6. Remove any empty directories as a result of the rest of the cleanup + // 7. Remove any empty directories as a result of the rest of the cleanup await removeEmptyDirectories(buildAppDir) } diff --git a/scripts/binary/binary-integrity-check-source.js b/scripts/binary/binary-integrity-check-source.js index 03a88ec26a3f..8a8abe70ac35 100644 --- a/scripts/binary/binary-integrity-check-source.js +++ b/scripts/binary/binary-integrity-check-source.js @@ -3,10 +3,9 @@ const captureStackTrace = Error.captureStackTrace const toString = Function.prototype.toString const callFn = Function.call -// eslint-disable-next-line no-unused-vars const stackIntegrityCheck = function stackIntegrityCheck (options) { const originalStackTraceLimit = OrigError.stackTraceLimit - const originalStackTrace = OrigError.prepareStackTrace + const originalPrepareStackTrace = OrigError.prepareStackTrace OrigError.stackTraceLimit = Infinity @@ -19,7 +18,7 @@ const stackIntegrityCheck = function stackIntegrityCheck (options) { captureStackTrace(tempError, arguments.callee) const stack = tempError.stack.filter((frame) => !frame.getFileName().startsWith('node:internal') && !frame.getFileName().startsWith('node:electron')) - OrigError.prepareStackTrace = originalStackTrace + OrigError.prepareStackTrace = originalPrepareStackTrace OrigError.stackTraceLimit = originalStackTraceLimit if (stack.length !== options.stackToMatch.length) { @@ -27,9 +26,8 @@ const stackIntegrityCheck = function stackIntegrityCheck (options) { } for (let index = 0; index < options.stackToMatch.length; index++) { - const expectedFunctionName = options.stackToMatch[index].functionName + const { functionName: expectedFunctionName, fileName: expectedFileName } = options.stackToMatch[index] const actualFunctionName = stack[index].getFunctionName() - const expectedFileName = options.stackToMatch[index].fileName const actualFileName = stack[index].getFileName() if (expectedFunctionName && actualFunctionName !== expectedFunctionName) { @@ -49,12 +47,14 @@ function validateToString () { } function validateElectron (electron) { + // 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 (toString.call(electron.app.getAppPath) !== 'function getAppPath() { [native code] }') { throw new Error(`Integrity check failed for toString.call(electron.app.getAppPath)`) } } 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 (toString.call(fs.readFileSync) !== `function(t,r){const n=splitPath(t);if(!n.isAsar)return g.apply(this,arguments);const{asarPath:i,filePath:a}=n,o=getOrCreateArchive(i);if(!o)throw createError("INVALID_ARCHIVE",{asarPath:i});const c=o.getFileInfo(a);if(!c)throw createError("NOT_FOUND",{asarPath:i,filePath:a});if(0===c.size)return r?"":s.Buffer.alloc(0);if(c.unpacked){const t=o.copyFileOut(a);return e.readFileSync(t,r)}if(r){if("string"==typeof r)r={encoding:r};else if("object"!=typeof r)throw new TypeError("Bad arguments")}else r={encoding:null};const{encoding:f}=r,l=s.Buffer.alloc(c.size),u=o.getFdAndValidateIntegrityLater();if(!(u>=0))throw createError("NOT_FOUND",{asarPath:i,filePath:a});return logASARAccess(i,a,c.offset),e.readSync(u,l,0,c.size,c.offset),validateBufferIntegrity(l,c.integrity),f?l.toString(f):l}`) { throw new Error(`Integrity check failed for toString.call(fs.readFileSync)`) }