Skip to content

Commit

Permalink
feat: Add a vendor directory option to use a custom vendor (#526)
Browse files Browse the repository at this point in the history
* update nuget to v6.10.1

* add vendor directory option

* delete .vscode

* restore the nuget.exe

* add default vendor option

---------

Co-authored-by: beyondkmp <beyondkmkp@gmail.com>
  • Loading branch information
beyondkmp and beyondkmp authored Jul 23, 2024
1 parent 88d5b8b commit a18e3c6
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 93 deletions.
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function checkIfCommandExists(command: string): Promise<boolean> {
/**
* This package's main function, which creates a Squirrel.Windows executable
* installer and optionally code-signs the output.
*
*
* @param options Options for installer generation and signing
* @see {@link https://github.com/Squirrel/Squirrel.Windows | Squirrel.Windows}
*/
Expand Down Expand Up @@ -74,7 +74,7 @@ export async function createWindowsInstaller(options: SquirrelWindowsOptions): P
let { appDirectory, outputDirectory, loadingGif } = options;
outputDirectory = path.resolve(outputDirectory || 'installer');

const vendorPath = path.join(__dirname, '..', 'vendor');
const vendorPath = options.vendorDirectory || path.join(__dirname, '..', 'vendor');
const vendorUpdate = path.join(vendorPath, 'Squirrel.exe');
const appUpdate = path.join(appDirectory, 'Squirrel.exe');

Expand Down
18 changes: 11 additions & 7 deletions src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export interface SquirrelWindowsOptions {
* The folder path of your Electron app
*/
appDirectory: string;
/**
* The folder path of Squirrel windows
*/
vendorDirectory?: string;
/**
* The folder path to create the .exe installer in.
*
Expand All @@ -23,7 +27,7 @@ export interface SquirrelWindowsOptions {
nuspecTemplate?: string;
/**
* The local path to a `.gif` file to display during install.
*
*
* @defaultValue the bundled {@link https://github.com/electron/windows-installer/blob/main/resources/install-spinner.gif | install-spinner.gif}
*/
loadingGif?: string;
Expand Down Expand Up @@ -92,7 +96,7 @@ export interface SquirrelWindowsOptions {
name?: string;
/**
* The path to an Authenticode Code Signing Certificate.
*
*
* This is a legacy parameter provided for backwards compatibility.
* For more comprehensive support of various codesigning scenarios
* like EV certificates, see the
Expand All @@ -101,7 +105,7 @@ export interface SquirrelWindowsOptions {
certificateFile?: string;
/**
* The password to decrypt the certificate given in `certificateFile`
*
*
* This is a legacy parameter provided for backwards compatibility.
* For more comprehensive support of various codesigning scenarios
* like EV certificates, see the
Expand All @@ -113,7 +117,7 @@ export interface SquirrelWindowsOptions {
*
* Overrides {@link SquirrelWindowsOptions.certificateFile | certificateFile}
* and {@link SquirrelWindowsOptions.certificatePassword | certificatePassword}`.
*
*
* This is a legacy parameter provided for backwards compatibility.
* For more comprehensive support of various codesigning scenarios
* like EV certificates, see the
Expand Down Expand Up @@ -186,13 +190,13 @@ export interface SquirrelWindowsOptions {

/**
* Requires Node.js 18 or newer.
*
*
* Sign your app with `@electron/windows-sign`, allowing for full customization
* of the code-signing process - and supports more complicated scenarios like
* cloud-hosted EV certificates, custom sign pipelines, and per-file overrides.
* It also supports all existing "simple" codesigning scenarios, including
* just passing a certificate file and password.
*
* just passing a certificate file and password.
*
* @see {@link https://github.com/electron/windows-sign | @electron/windows-sign documentation}
* for all possible configuration options.
*/
Expand Down
173 changes: 89 additions & 84 deletions src/sign.ts
Original file line number Diff line number Diff line change
@@ -1,84 +1,89 @@
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);
}
}
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';

let VENDOR_PATH: string;
let ORIGINAL_SIGN_TOOL_PATH: string;
let BACKUP_SIGN_TOOL_PATH: string;
let SIGN_LOG_PATH: string;

/**
* 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');
}

VENDOR_PATH = options.vendorDirectory || path.join(__dirname, '..', 'vendor');
ORIGINAL_SIGN_TOOL_PATH = path.join(VENDOR_PATH, 'signtool.exe');
BACKUP_SIGN_TOOL_PATH = path.join(VENDOR_PATH, 'signtool-original.exe');
SIGN_LOG_PATH = path.join(VENDOR_PATH, 'electron-windows-sign.log');

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);
}
}

0 comments on commit a18e3c6

Please sign in to comment.