-
Notifications
You must be signed in to change notification settings - Fork 262
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Allow @electron/windows-sign to take over Squirrel codesigning (#…
…501) * feat: Allow @electron/windows-sign to take over Squirrel codesigning * test: Remove test for Node 14 * fix: Tests, correct node version * fix: We actually need Node v20 * fix: Update @electron/windows-sign * build: Update node-orb * docs: Add documentation
- Loading branch information
1 parent
b133e78
commit 598aa70
Showing
11 changed files
with
559 additions
and
297 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import path from 'path'; | ||
import fs from 'fs-extra'; | ||
|
||
import { createTempDir } from '../../src/temp-utils'; | ||
|
||
export const FIXTURE_APP_DIR = path.join(__dirname, '../fixtures/app'); | ||
|
||
export async function createTempAppDirectory(): Promise<string> { | ||
const appDirectory = await createTempDir('electron-winstaller-ad-'); | ||
await fs.copy(FIXTURE_APP_DIR, appDirectory); | ||
return appDirectory; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
const fs = require('fs-extra'); | ||
const path = require('path'); | ||
|
||
module.exports = function(args) { | ||
console.log(...args); | ||
|
||
fs.appendFileSync(path.join(__dirname, 'hook.log'), `${JSON.stringify(args)}\n`); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import test from 'ava'; | ||
import path from 'path'; | ||
import { createTempDir } from '../src/temp-utils'; | ||
import fs from 'fs-extra'; | ||
import { createWindowsInstaller } from '../src'; | ||
import { createTempAppDirectory } from './helpers/helpers'; | ||
import { SignToolOptions } from '@electron/windows-sign'; | ||
import semver from 'semver'; | ||
|
||
const log = require('debug')('electron-windows-installer:spec'); | ||
|
||
if (process.platform === 'win32' && semver.gte(process.version, '20.0.0')) { | ||
test.serial('creates a signtool.exe and uses it to sign', async (t): Promise<void> => { | ||
|
||
const outputDirectory = await createTempDir('ei-'); | ||
const appDirectory = await createTempAppDirectory(); | ||
const hookLogPath = path.join(__dirname, './helpers/hook.log'); | ||
const hookModulePath = path.join(__dirname, './helpers/windowsSignHook.js'); | ||
const windowsSign: SignToolOptions = { hookModulePath }; | ||
const options = { appDirectory, outputDirectory, windowsSign }; | ||
|
||
// Reset | ||
await fs.remove(hookLogPath); | ||
|
||
// Test | ||
await createWindowsInstaller(options); | ||
|
||
log(`Verifying assertions on ${outputDirectory}`); | ||
log(JSON.stringify(await fs.readdir(outputDirectory))); | ||
|
||
const nupkgPath = path.join(outputDirectory, 'myapp-1.0.0-full.nupkg'); | ||
|
||
t.true(await fs.pathExists(nupkgPath)); | ||
t.true(await fs.pathExists(path.join(outputDirectory, 'MyAppSetup.exe'))); | ||
|
||
if (process.platform === 'win32') { | ||
t.true(await fs.pathExists(path.join(outputDirectory, 'MyAppSetup.msi'))); | ||
} | ||
|
||
log('Verifying Update.exe'); | ||
t.true(await fs.pathExists(path.join(appDirectory, 'Squirrel.exe'))); | ||
|
||
log('Verifying that our hook got to "sign" all files'); | ||
const hookLog = await fs.readFile(hookLogPath, { encoding: 'utf8' }); | ||
const filesLogged = hookLog.split('\n').filter(v => !!v.trim()).length; | ||
t.is(filesLogged, 8); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import type { createSeaSignTool as createSeaSignToolType } from '@electron/windows-sign'; | ||
import path from 'path'; | ||
import semver from 'semver'; | ||
import fs from 'fs-extra'; | ||
|
||
import { SquirrelWindowsOptions } from './options'; | ||
|
||
const VENDOR_PATH = path.join(__dirname, '..', 'vendor'); | ||
const ORIGINAL_SIGN_TOOL_PATH = path.join(VENDOR_PATH, 'signtool.exe'); | ||
const BACKUP_SIGN_TOOL_PATH = path.join(VENDOR_PATH, 'signtool-original.exe'); | ||
const SIGN_LOG_PATH = path.join(VENDOR_PATH, 'electron-windows-sign.log'); | ||
|
||
/** | ||
* This method uses @electron/windows-sign to create a fake signtool.exe | ||
* that can be called by Squirrel - but then just calls @electron/windows-sign | ||
* to actually perform the signing. | ||
* | ||
* That's useful for users who need a high degree of customization of the signing | ||
* process but still want to use @electron/windows-installer. | ||
*/ | ||
export async function createSignTool(options: SquirrelWindowsOptions): Promise<void> { | ||
if (!options.windowsSign) { | ||
throw new Error('Signtool should only be created if windowsSign options are set'); | ||
} | ||
|
||
const createSeaSignTool = await getCreateSeaSignTool(); | ||
|
||
await resetSignTool(); | ||
await fs.remove(SIGN_LOG_PATH); | ||
|
||
// Make a backup of signtool.exe | ||
await fs.copy(ORIGINAL_SIGN_TOOL_PATH, BACKUP_SIGN_TOOL_PATH, { overwrite: true }); | ||
|
||
// Create a new signtool.exe using @electron/windows-sign | ||
await createSeaSignTool({ | ||
path: ORIGINAL_SIGN_TOOL_PATH, | ||
windowsSign: options.windowsSign | ||
}); | ||
} | ||
|
||
/** | ||
* Ensure that signtool.exe is actually the "real" signtool.exe, not our | ||
* fake substitute. | ||
*/ | ||
export async function resetSignTool() { | ||
if (fs.existsSync(BACKUP_SIGN_TOOL_PATH)) { | ||
// Reset the backup of signtool.exe | ||
await fs.copy(BACKUP_SIGN_TOOL_PATH, ORIGINAL_SIGN_TOOL_PATH, { overwrite: true }); | ||
await fs.remove(BACKUP_SIGN_TOOL_PATH); | ||
} | ||
} | ||
|
||
/** | ||
* @electron/windows-installer only requires Node.js >= 8.0.0. | ||
* @electron/windows-sign requires Node.js >= 16.0.0. | ||
* @electron/windows-sign's "fake signtool.exe" feature requires | ||
* Node.js >= 20.0.0, the first version to contain the "single | ||
* executable" feature with proper support. | ||
* | ||
* Since this is overall a very niche feature and only benefits | ||
* consumers with rather advanced codesigning needs, we did not | ||
* want to make Node.js v18 a hard requirement for @electron/windows-installer. | ||
* | ||
* Instead, @electron/windows-sign is an optional dependency - and | ||
* if it didn't install, we'll throw a useful error here. | ||
* | ||
* @returns | ||
*/ | ||
async function getCreateSeaSignTool(): Promise<typeof createSeaSignToolType> { | ||
try { | ||
const { createSeaSignTool } = await import('@electron/windows-sign'); | ||
return createSeaSignTool; | ||
} catch(error) { | ||
let message = 'In order to use windowsSign options, @electron/windows-sign must be installed as a dependency.'; | ||
|
||
if (semver.lte(process.version, '20.0.0')) { | ||
message += ` You are currently using Node.js ${process.version}. Please upgrade to Node.js 19 or later and reinstall all dependencies to ensure that @electron/windows-sign is available.`; | ||
} else { | ||
message += ` ${error}`; | ||
} | ||
|
||
throw new Error(message); | ||
} | ||
} |
Oops, something went wrong.