diff --git a/test/common/README.md b/test/common/README.md index fa78b2792ef6ac..d272d7f354f762 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -1048,11 +1048,14 @@ Application functionality. Skip the rest of the tests if single executable applications are not supported in the current configuration. -### `injectAndCodeSign(targetExecutable, resource)` +### `generateSEA(targetExecutable, sourceExecutable, seaBlob, verifyWorkflow)` -Uses Postect to inject the contents of the file at the path `resource` into -the target executable file at the path `targetExecutable` and ultimately code -sign the final binary. +Copy `sourceExecutable` to `targetExecutable`, use postject to inject `seaBlob` +into `targetExecutable` and sign it if necessary. + +If `verifyWorkflow` is false (default) and any of the steps fails, it skips the tests. +Otherwise, an error is thrown. On Windows, if the failure happens at signing, +the stderr string is attached to `error.stderr` in the `error` being thrown. ## tick Module diff --git a/test/common/sea.js b/test/common/sea.js index d57c9e4238d867..da97c52fa44ab1 100644 --- a/test/common/sea.js +++ b/test/common/sea.js @@ -3,8 +3,9 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); +const { inspect } = require('util'); -const { readFileSync } = require('fs'); +const { readFileSync, copyFileSync } = require('fs'); const { spawnSyncAndExitWithoutError, } = require('../common/child_process'); @@ -54,47 +55,75 @@ function skipIfSingleExecutableIsNotSupported() { } } -function injectAndCodeSign(targetExecutable, resource) { +function generateSEA(targetExecutable, sourceExecutable, seaBlob) { + try { + copyFileSync(sourceExecutable, targetExecutable); + } catch(e) { + const message = `Cannot copy ${sourceExecutable} to ${targetExecutable}: ${inspect(e)}`; + if (verifyWorkflow) { + throw new Error(message) + } + common.skip(message); + } + console.log(`Copied ${sourceExecutable} to ${targetExecutable}`); + const postjectFile = fixtures.path('postject-copy', 'node_modules', 'postject', 'dist', 'cli.js'); - spawnSyncAndExitWithoutError(process.execPath, [ - postjectFile, - targetExecutable, - 'NODE_SEA_BLOB', - resource, - '--sentinel-fuse', 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2', - ...process.platform === 'darwin' ? [ '--macho-segment-name', 'NODE_SEA' ] : [], - ], {}); + try { + spawnSyncAndExitWithoutError(process.execPath, [ + postjectFile, + targetExecutable, + 'NODE_SEA_BLOB', + seaBlob, + '--sentinel-fuse', 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2', + ...process.platform === 'darwin' ? [ '--macho-segment-name', 'NODE_SEA' ] : [], + ]); + } catch(e) { + const message = `Cannot inject ${seaBlob} into ${targetExecutable}: ${inspect(e)}`; + if (verifyWorkflow) { + throw new Error(message) + } + common.skip(message); + } + console.log(`Injected ${seaBlob} into ${targetExecutable}`); if (process.platform === 'darwin') { - spawnSyncAndExitWithoutError('codesign', [ '--sign', '-', targetExecutable ], {}); - spawnSyncAndExitWithoutError('codesign', [ '--verify', targetExecutable ], {}); + try { + spawnSyncAndExitWithoutError('codesign', [ '--sign', '-', targetExecutable ], {}); + spawnSyncAndExitWithoutError('codesign', [ '--verify', targetExecutable ], {}); + } catch(e) { + const message = `Cannot sign ${targetExecutable}: ${inspect(e)}`; + if (verifyWorkflow) { + throw new Error(message); + } + common.skip(message); + } + console.log(`Signed ${targetExecutable}`); } else if (process.platform === 'win32') { - let signtoolFound = false; try { spawnSyncAndExitWithoutError('where', [ 'signtool' ], {}); - signtoolFound = true; } catch (err) { - console.log(err.message); - } - if (signtoolFound) { - let certificatesFound = false; - let stderr; - try { - ({ stderr } = spawnSyncAndExitWithoutError('signtool', [ 'sign', '/fd', 'SHA256', targetExecutable ], {})); - certificatesFound = true; - } catch (err) { - if (!/SignTool Error: No certificates were found that met all the given criteria/.test(stderr)) { - throw err; - } + const message = `Cannot find signtool: ${inspect(e)}`; + if (verifyWorkflow) { + throw new Error(message); } - if (certificatesFound) { - spawnSyncAndExitWithoutError('signtool', 'verify', '/pa', 'SHA256', targetExecutable, {}); + common.skip(message); + } + let stderr; + try { + ({ stderr } = spawnSyncAndExitWithoutError('signtool', [ 'sign', '/fd', 'SHA256', targetExecutable ], {})); + spawnSyncAndExitWithoutError('signtool', 'verify', '/pa', 'SHA256', targetExecutable, {}); + } catch (err) { + const message = `Cannot sign ${targetExecutable}: ${inspect(e)}\n${stderr}`; + if (verifyWorkflow) { + throw new Error(message); } + common.skip(message); } + console.log(`Signed ${targetExecutable}`); } } module.exports = { skipIfSingleExecutableIsNotSupported, - injectAndCodeSign, + generateSEA, }; diff --git a/test/sequential/test-single-executable-application-assets-raw.js b/test/sequential/test-single-executable-application-assets-raw.js index 6f0a8a77486fb6..4122e8b323a569 100644 --- a/test/sequential/test-single-executable-application-assets-raw.js +++ b/test/sequential/test-single-executable-application-assets-raw.js @@ -3,7 +3,7 @@ const common = require('../common'); const { - injectAndCodeSign, + generateSEA, skipIfSingleExecutableIsNotSupported, } = require('../common/sea'); @@ -56,8 +56,7 @@ const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'se assert(existsSync(seaPrepBlob)); - copyFileSync(process.execPath, outputFile); - injectAndCodeSign(outputFile, seaPrepBlob); + generateSEA(outputFile, process.execPath, seaPrepBlob); spawnSyncAndExitWithoutError( outputFile, diff --git a/test/sequential/test-single-executable-application-assets.js b/test/sequential/test-single-executable-application-assets.js index 366b606e37ce64..ed10e076ced883 100644 --- a/test/sequential/test-single-executable-application-assets.js +++ b/test/sequential/test-single-executable-application-assets.js @@ -3,7 +3,7 @@ const common = require('../common'); const { - injectAndCodeSign, + generateSEA, skipIfSingleExecutableIsNotSupported, } = require('../common/sea'); @@ -109,8 +109,7 @@ const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'se assert(existsSync(seaPrepBlob)); - copyFileSync(process.execPath, outputFile); - injectAndCodeSign(outputFile, seaPrepBlob); + generateSEA(outputFile, process.execPath, seaPrepBlob); spawnSyncAndExitWithoutError( outputFile, diff --git a/test/sequential/test-single-executable-application-disable-experimental-sea-warning.js b/test/sequential/test-single-executable-application-disable-experimental-sea-warning.js index fdd0c23a26da3e..5b1912f1a99eb0 100644 --- a/test/sequential/test-single-executable-application-disable-experimental-sea-warning.js +++ b/test/sequential/test-single-executable-application-disable-experimental-sea-warning.js @@ -3,7 +3,7 @@ require('../common'); const { - injectAndCodeSign, + generateSEA, skipIfSingleExecutableIsNotSupported, } = require('../common/sea'); @@ -51,8 +51,7 @@ spawnSyncAndExitWithoutError( assert(existsSync(seaPrepBlob)); -copyFileSync(process.execPath, outputFile); -injectAndCodeSign(outputFile, seaPrepBlob); +generateSEA(outputFile, process.execPath, seaPrepBlob); spawnSyncAndExitWithoutError( outputFile, diff --git a/test/sequential/test-single-executable-application-empty.js b/test/sequential/test-single-executable-application-empty.js index 047685b0074aa9..d26d72e47dafdd 100644 --- a/test/sequential/test-single-executable-application-empty.js +++ b/test/sequential/test-single-executable-application-empty.js @@ -1,9 +1,9 @@ 'use strict'; -require('../common'); +const common = require('../common'); const { - injectAndCodeSign, + generateSEA, skipIfSingleExecutableIsNotSupported, } = require('../common/sea'); @@ -13,7 +13,7 @@ skipIfSingleExecutableIsNotSupported(); // script. const tmpdir = require('../common/tmpdir'); -const { copyFileSync, writeFileSync, existsSync } = require('fs'); +const { writeFileSync, existsSync } = require('fs'); const { spawnSyncAndExitWithoutError } = require('../common/child_process'); const assert = require('assert'); @@ -38,8 +38,20 @@ spawnSyncAndExitWithoutError( assert(existsSync(seaPrepBlob)); -copyFileSync(process.execPath, outputFile); -injectAndCodeSign(outputFile, seaPrepBlob); +// Verify the workflow. +try { + generateSEA(outputFile, process.execPath, seaPrepBlob, true); +} catch (e) { + if (/Cannot copy/.test(e.message)) { + common.skip(e.message); + } else if (common.isWindows) { + if (/Cannot sign/.test(e.message) || /Cannot find signtool/.test(e.message)) { + common.skip(e.message); + } + } + + throw e; +} spawnSyncAndExitWithoutError( outputFile, diff --git a/test/sequential/test-single-executable-application-snapshot-and-code-cache.js b/test/sequential/test-single-executable-application-snapshot-and-code-cache.js index 952003cf02c585..40ae2a4ed44d50 100644 --- a/test/sequential/test-single-executable-application-snapshot-and-code-cache.js +++ b/test/sequential/test-single-executable-application-snapshot-and-code-cache.js @@ -3,7 +3,7 @@ require('../common'); const { - injectAndCodeSign, + generateSEA, skipIfSingleExecutableIsNotSupported, } = require('../common/sea'); @@ -62,8 +62,7 @@ const outputFile = join(tmpdir.path, process.platform === 'win32' ? 'sea.exe' : assert(existsSync(seaPrepBlob)); - copyFileSync(process.execPath, outputFile); - injectAndCodeSign(outputFile, seaPrepBlob); + generateSEA(outputFile, process.execPath, seaPrepBlob); spawnSyncAndExitWithoutError( outputFile, diff --git a/test/sequential/test-single-executable-application-snapshot.js b/test/sequential/test-single-executable-application-snapshot.js index 402505a6122c74..5bdad24adc93ca 100644 --- a/test/sequential/test-single-executable-application-snapshot.js +++ b/test/sequential/test-single-executable-application-snapshot.js @@ -3,7 +3,7 @@ require('../common'); const { - injectAndCodeSign, + generateSEA, skipIfSingleExecutableIsNotSupported, } = require('../common/sea'); @@ -85,8 +85,7 @@ const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'se assert(existsSync(seaPrepBlob)); - copyFileSync(process.execPath, outputFile); - injectAndCodeSign(outputFile, seaPrepBlob); + generateSEA(outputFile, process.execPath, seaPrepBlob); spawnSyncAndExitWithoutError( outputFile, diff --git a/test/sequential/test-single-executable-application-use-code-cache.js b/test/sequential/test-single-executable-application-use-code-cache.js index af5f2855ed6318..151e631eda7ea8 100644 --- a/test/sequential/test-single-executable-application-use-code-cache.js +++ b/test/sequential/test-single-executable-application-use-code-cache.js @@ -3,7 +3,7 @@ require('../common'); const { - injectAndCodeSign, + generateSEA, skipIfSingleExecutableIsNotSupported, } = require('../common/sea'); @@ -56,8 +56,7 @@ spawnSyncAndExitWithoutError( assert(existsSync(seaPrepBlob)); -copyFileSync(process.execPath, outputFile); -injectAndCodeSign(outputFile, seaPrepBlob); +generateSEA(outputFile, process.execPath, seaPrepBlob); spawnSyncAndExitWithoutError( outputFile, diff --git a/test/sequential/test-single-executable-application.js b/test/sequential/test-single-executable-application.js index 6379dfd2ea4b6d..2e5c7c1e5d564a 100644 --- a/test/sequential/test-single-executable-application.js +++ b/test/sequential/test-single-executable-application.js @@ -3,7 +3,7 @@ require('../common'); const { - injectAndCodeSign, + generateSEA, skipIfSingleExecutableIsNotSupported, } = require('../common/sea'); @@ -50,8 +50,7 @@ spawnSyncAndExitWithoutError( assert(existsSync(seaPrepBlob)); -copyFileSync(process.execPath, outputFile); -injectAndCodeSign(outputFile, seaPrepBlob); +generateSEA(outputFile, process.execPath, seaPrepBlob); spawnSyncAndExitWithoutError( outputFile,