UNPKG

@interopio/desktop-cli

Version:

CLI tool for setting up, building and packaging io.Connect Desktop projects

248 lines 12 kB
"use strict"; 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