@interopio/desktop-cli
Version:
CLI tool for setting up, building and packaging io.Connect Desktop projects
248 lines • 12 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPackageWithElectronForge = createPackageWithElectronForge;
const logger_1 = require("../../utils/logger");
const core_1 = require("@electron-forge/core");
const path_1 = require("../../utils/path");
const fs_extra_1 = require("fs-extra");
const path_2 = require("path");
const config_service_1 = require("../config/config.service");
const file_1 = require("../../utils/file");
const fs_1 = require("fs");
const promises_1 = require("fs/promises");
const os_1 = require("os");
const child_process_1 = require("child_process");
const error_handler_1 = require("../../utils/error.handler");
const logger = logger_1.Logger.getInstance();
async function createPackageWithElectronForge() {
logger.info('Creating installer...');
// copy PathUtils.getIOCDDir() to PathUtils.getDistDir() in a folder called <name>-<platform>-<arch>/
const appPackageTempDir = await copyAppToDist();
// TODO review if need this at all, now that we are using electron forge
await ensureElectronVersionIsSet();
// If your app is already prepackaged & signed internally, tell Forge to skip packaging
const makeResults = await callForgeMake(appPackageTempDir);
await signArtifacts(makeResults);
await callForgePublish(appPackageTempDir);
}
async function callForgeMake(appPackageTempDir) {
let makeTargets = getTargetsFromConfig();
if (makeTargets.length === 0) {
throw new error_handler_1.CLIError('No makers configured in forge.config.js. Please configure at least one maker (e.g. Squirrel for Windows, DMG for macOS).', {
suggestions: [
'Add at least one maker to the makers array in forge.config.js',
'Refer to https://www.electronforge.io/config/makers for available makers and configuration examples'
]
});
}
const makeOptions = {
dir: appPackageTempDir,
skipPackage: true, // <— consume prepackaged app in out/<name>-<platform>-<arch>/
outDir: path_1.PathUtils.getDistDir(),
overrideTargets: makeTargets
};
const options = {
dir: appPackageTempDir,
makeOptions,
dryRun: true
};
// we call make by actually calling publish with dryRun :((
logger.debug('Calling publish (dry run) with make options:', JSON.stringify(options, null, 2));
await core_1.api.publish(options);
logger.debug("publish (dry run) done");
// read results from dist/makeResults.json
const makeResultsJSON = (0, path_2.join)(path_1.PathUtils.getDistDir(), 'makeResults.json');
if ((0, fs_extra_1.existsSync)(makeResultsJSON)) {
const makeResults = await (0, fs_extra_1.readJson)(makeResultsJSON);
logger.debug('Read artifacts from dist/makeResults.json successfully!');
return makeResults;
}
else {
throw new error_handler_1.CLIError('No makeResults found after make process. Please ensure at least one maker is configured correctly in forge.config.js.', {
suggestions: [
'Check the output above for any errors during the make process',
'Ensure that the makers in forge.config.js are configured correctly for your platform'
]
});
}
}
async function callForgePublish(appPackageTempDir) {
const publishers = getPublishersFromConfig();
if (publishers.length === 0) {
logger.info('No publishers configured in forge.config.js, skipping publish step');
return;
}
const config = {
dir: appPackageTempDir,
dryRunResume: true,
};
logger.debug(`Calling publish with config `, JSON.stringify(config, null, 2));
await core_1.api.publish(config);
logger.debug("Publish step completed.");
}
/**
* This function copies the prepackaged app to the dist directory in a platform-specific way
* This is needed because we skip forge's prepackaging step and we need to ensure that
* the app is in the correct location for the make process to find it.
*/
async function copyAppToDist() {
const projectDir = path_1.PathUtils.getIOCDDir();
const productNameToUseInTempDir = (0, os_1.platform)() === "win32" ? config_service_1.ConfigManager.config.productSlug : config_service_1.ConfigManager.config.productSlug;
let appPackageTempDir = (0, path_2.join)(path_1.PathUtils.getDistDir(), `${productNameToUseInTempDir}-${process.platform}-${process.arch}`);
if ((0, fs_extra_1.existsSync)(appPackageTempDir)) {
(0, fs_1.rmSync)(appPackageTempDir, { recursive: true, force: true });
}
if ((0, os_1.platform)() === "win32") {
// on windows copy the whole directory
logger.debug(`Copying prepackaged app exe from ${projectDir} to ${appPackageTempDir}`);
await (0, promises_1.cp)(projectDir, appPackageTempDir, { recursive: true });
logger.debug(`Copied prepackaged app to ${appPackageTempDir} successfully!`);
// copy license from root dir to outDir
const licenseSrc = (0, path_2.join)(path_1.PathUtils.getRootDir(), 'LICENSE');
const licenseDest = (0, path_2.join)(appPackageTempDir, 'LICENSE');
if (await file_1.FileUtils.exists(licenseSrc)) {
(0, fs_1.cpSync)(licenseSrc, licenseDest);
logger.debug('Copied LICENSE to packaged app');
}
}
else if ((0, os_1.platform)() === "darwin") {
await (0, fs_extra_1.ensureDir)(appPackageTempDir);
// on macOS copy the app bundle only
const source = path_1.PathUtils.getIOCDAppBundlePath();
const destination = (0, path_2.join)(appPackageTempDir, config_service_1.ConfigManager.config.productSlug + ".app");
logger.debug(`Copying prepackaged app bundle from ${source} to ${destination}`);
(0, child_process_1.execSync)(`ditto "${source}" "${destination}"`, { stdio: 'pipe' });
logger.debug(`Copied prepackaged app to ${appPackageTempDir} successfully!`);
}
return appPackageTempDir;
}
/**
* This function signs the installer artifacts produced by the make process.
* @param makeResults The results of the make process, containing the artifacts to sign.
*/
async function signArtifacts(makeResults) {
if ((0, os_1.platform)() === "win32") {
logger.debug("Signing installer files");
const signBinary = require("./windows.helper").signBinary;
// Sign all produced installer artifacts BEFORE publishing
for (const result of makeResults) {
for (const artifact of result.artifacts) {
// artifact is a full path to e.g. Setup.exe / .msi / .dmg / .zip
await signBinary(artifact);
}
}
logger.debug("Signed installer files successfully!");
}
else if ((0, os_1.platform)() === "darwin") {
logger.debug("Signing installer files");
// const signBinary = require("./macOS.helper").signBinary;
// for (const result of makeResults) {
// for (const artifact of result.artifacts) {
// // artifact is a full path to e.g. Setup.exe / .msi / .dmg / .zip
// // await signBinary(artifact);
// }
// }
logger.debug("Signed installer files successfully!");
}
}
/** Reads the target configurations from the forge.config.js file and enhances it with additional information */
function getTargetsFromConfig() {
const forgeConfigPath = (0, path_2.join)(path_1.PathUtils.getRootDir(), 'forge.config.js');
let overrideTargets = [];
if (!(0, fs_extra_1.existsSync)(forgeConfigPath)) {
return overrideTargets;
}
const forgeConfig = require(forgeConfigPath);
if (!forgeConfig.makers || forgeConfig.makers.length === 0) {
return overrideTargets;
}
overrideTargets = forgeConfig.makers.map((t) => {
const config = config_service_1.ConfigManager.config;
if (t.name === '@electron-forge/maker-squirrel') {
const ioAssetsDir = path_1.PathUtils.getIOCDAssetsDir();
let icon = (0, path_2.join)(ioAssetsDir, 'images', 'logo.ico');
return {
name: '@electron-forge/maker-squirrel',
config: {
// iconUrl: icon, // needs to be HTTPs; displayed in Control Panel ➡ Programs and Features.
exe: config.win.exe.exeName, // * The name of your app's main `.exe` file.
// keep this concise
setupIcon: icon,
setupExe: `${(0, path_2.basename)(config.win.exe.exeName).replace(".exe", "")}-Setup.exe`,
version: config.version,
// Optional: ensure Squirrel uses your short product name
name: config.productSlug, // used for package IDs
authors: config.company, // The `authors` value for the NuGet package metadata.
owners: config.company, // The `owners` value for the NuGet package metadata.
copyRight: config.copyright, // The `copyright` value for the NuGet package metadata.
title: config.productName, // used as install window title
description: config.productDescription, // used in Control Panel ➡ Programs and Features.
noMSI: true,
...t.config ?? {},
// Keep the nuspect template
nuspecTemplate: "config/win-build/template.nuspectemplate",
usePackageJson: false,
}
};
}
if (t.name === '@electron-forge/maker-dmg') {
let icon = (0, path_2.join)(path_1.PathUtils.getRootDir(), "modifications", "iocd", "assets", "images", "logo.icns");
return {
name: '@electron-forge/maker-dmg',
config: {
name: config.productName,
icon: icon,
overwrite: true,
title: `${config.productName}`,
...t.config ?? {},
}
};
}
return t;
});
return overrideTargets;
}
function getPublishersFromConfig() {
const forgeConfigPath = (0, path_2.join)(path_1.PathUtils.getRootDir(), 'forge.config.js');
let overrideTargets = [];
if (!(0, fs_extra_1.existsSync)(forgeConfigPath)) {
return overrideTargets;
}
const forgeConfig = require(forgeConfigPath);
if (!forgeConfig.publishers || forgeConfig.publishers.length === 0) {
return overrideTargets;
}
overrideTargets = forgeConfig.publishers.map((t) => {
return t;
});
return overrideTargets;
}
async function ensureElectronVersionIsSet() {
const electronVersion = await getElectronVersionFromComponent();
if (!electronVersion) {
throw new Error('Electron version not found in iocd component metadata');
}
logger.debug(`Using Electron version: ${electronVersion}`);
}
/** Reads the electron version from the iocd component metadata */
async function getElectronVersionFromComponent() {
try {
const resourcesDir = path_1.PathUtils.getIOCDResourcesDir();
const metadataPath = (0, path_2.join)(resourcesDir, 'metadata.json');
if (!(await file_1.FileUtils.exists(metadataPath))) {
logger.debug('No iocd component metadata found, skipping electron version detection');
return null;
}
const metadata = await (0, fs_extra_1.readJson)(metadataPath);
if (metadata && metadata.electronVersion) {
return metadata.electronVersion;
}
logger.debug('No electron version found in iocd component metadata');
return null;
}
catch (error) {
logger.debug(`Failed to read electron version from component: ${error instanceof Error ? error.message : String(error)}`);
return null;
}
}
//# sourceMappingURL=electronForge.js.map