UNPKG

vrrv-installer-builder

Version:

A complete solution to package and build a ready for distribution Electron app for MacOS, Windows and Linux with “auto update” support out of the box

199 lines (198 loc) 11.5 kB
"use strict"; const path = require("path"); const bluebird_1 = require("bluebird"); const fs_extra_p_1 = require("fs-extra-p"); const util_1 = require("../util/util"); const util_2 = require("../util/util"); const log_1 = require("../util/log"); const archiverUtil = require("archiver-utils"); const archiver = require("archiver"); //noinspection JSUnusedLocalSymbols const __awaiter = require("../util/awaiter"); function convertVersion(version) { const parts = version.split("-"); const mainVersion = parts.shift(); if (parts.length > 0) { return [mainVersion, parts.join("-").replace(/\./g, "")].join("-"); } else { return mainVersion; } } exports.convertVersion = convertVersion; function syncReleases(outputDirectory, options) { log_1.log("Sync releases to build delta package"); const args = prepareArgs(["-u", options.remoteReleases, "-r", outputDirectory], path.join(options.vendorPath, "SyncReleases.exe")); if (options.remoteToken) { args.push("-t", options.remoteToken); } return util_1.spawn(process.platform === "win32" ? path.join(options.vendorPath, "SyncReleases.exe") : "mono", args); } function buildInstaller(options, outputDirectory, setupExe, packager, appOutDir) { return __awaiter(this, void 0, void 0, function* () { const appUpdate = yield packager.getTempFile("Update.exe"); yield bluebird_1.Promise.all([fs_extra_p_1.copy(path.join(options.vendorPath, "Update.exe"), appUpdate).then(() => packager.sign(appUpdate)), bluebird_1.Promise.all([fs_extra_p_1.remove(`${ outputDirectory.replace(/\\/g, "/") }/*-full.nupkg`), fs_extra_p_1.remove(path.join(outputDirectory, "RELEASES"))]).then(() => fs_extra_p_1.ensureDir(outputDirectory))]); if (options.remoteReleases) { yield syncReleases(outputDirectory, options); } const embeddedArchiveFile = yield packager.getTempFile("setup.zip"); const embeddedArchive = archiver("zip", { zlib: { level: options.packageCompressionLevel == null ? 6 : options.packageCompressionLevel } }); const embeddedArchiveOut = fs_extra_p_1.createWriteStream(embeddedArchiveFile); const embeddedArchivePromise = new bluebird_1.Promise(function (resolve, reject) { embeddedArchive.on("error", reject); embeddedArchiveOut.on("close", resolve); }); embeddedArchive.pipe(embeddedArchiveOut); embeddedArchive.file(appUpdate, { name: "Update.exe" }); embeddedArchive.file(options.loadingGif ? path.resolve(options.loadingGif) : path.join(__dirname, "..", "..", "templates", "install-spinner.gif"), { name: "background.gif" }); const version = convertVersion(options.version); const packageName = `${ options.name }-${ version }-full.nupkg`; const nupkgPath = path.join(outputDirectory, packageName); const setupPath = path.join(outputDirectory, setupExe || `${ options.name || options.productName }Setup.exe`); yield bluebird_1.Promise.all([pack(options, appOutDir, appUpdate, nupkgPath, version, options.packageCompressionLevel), fs_extra_p_1.copy(path.join(options.vendorPath, "Setup.exe"), setupPath)]); embeddedArchive.file(nupkgPath, { name: packageName }); const releaseEntry = yield releasify(options, nupkgPath, outputDirectory, packageName); embeddedArchive.append(releaseEntry, { name: "RELEASES" }); embeddedArchive.finalize(); yield embeddedArchivePromise; const writeZipToSetup = path.join(options.vendorPath, "WriteZipToSetup.exe"); yield util_1.exec(process.platform === "win32" ? writeZipToSetup : "wine", prepareArgs([setupPath, embeddedArchiveFile], writeZipToSetup)); yield packager.signAndEditResources(setupPath); if (options.msi && process.platform === "win32") { const outFile = setupExe.replace(".exe", ".msi"); yield msi(options, nupkgPath, setupPath, outputDirectory, outFile); // rcedit can only edit .exe resources yield packager.sign(path.join(outputDirectory, outFile)); } }); } exports.buildInstaller = buildInstaller; function pack(options, directory, updateFile, outFile, version, packageCompressionLevel) { return __awaiter(this, void 0, void 0, function* () { const archive = archiver("zip", { zlib: { level: packageCompressionLevel == null ? 9 : packageCompressionLevel } }); const archiveOut = fs_extra_p_1.createWriteStream(outFile); const archivePromise = new bluebird_1.Promise(function (resolve, reject) { archive.on("error", reject); archiveOut.on("error", reject); archiveOut.on("close", resolve); }); archive.pipe(archiveOut); const author = options.authors || options.owners; const copyright = options.copyright || `Copyright © ${ new Date().getFullYear() } ${ author }`; const nuspecContent = `<?xml version="1.0"?> <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>${ options.appId }</id> <version>${ version }</version> <title>${ options.productName }</title> <authors>${ author }</authors> <owners>${ options.owners || options.authors }</owners> <iconUrl>${ options.iconUrl }</iconUrl> <requireLicenseAcceptance>false</requireLicenseAcceptance> <description>${ options.description }</description> <copyright>${ copyright }</copyright>${ options.extraMetadataSpecs || "" } </metadata> </package>`; util_2.debug(`Created NuSpec file:\n${ nuspecContent }`); archive.append(nuspecContent.replace(/\n/, "\r\n"), { name: `${ encodeURI(options.name).replace(/%5B/g, "[").replace(/%5D/g, "]") }.nuspec` }); //noinspection SpellCheckingInspection archive.append(`<?xml version="1.0" encoding="utf-8"?> <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"> <Relationship Type="http://schemas.microsoft.com/packaging/2010/07/manifest" Target="/${ options.name }.nuspec" Id="Re0" /> <Relationship Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="/package/services/metadata/core-properties/1.psmdcp" Id="Re1" /> </Relationships>`.replace(/\n/, "\r\n"), { name: ".rels", prefix: "_rels" }); //noinspection SpellCheckingInspection archive.append(`<?xml version="1.0" encoding="utf-8"?> <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"> <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" /> <Default Extension="nuspec" ContentType="application/octet" /> <Default Extension="pak" ContentType="application/octet" /> <Default Extension="asar" ContentType="application/octet" /> <Default Extension="bin" ContentType="application/octet" /> <Default Extension="dll" ContentType="application/octet" /> <Default Extension="exe" ContentType="application/octet" /> <Default Extension="dat" ContentType="application/octet" /> <Default Extension="psmdcp" ContentType="application/vnd.openxmlformats-package.core-properties+xml" /> <Override PartName="/lib/net45/LICENSE" ContentType="application/octet" /> <Default Extension="diff" ContentType="application/octet" /> <Default Extension="bsdiff" ContentType="application/octet" /> <Default Extension="shasum" ContentType="text/plain" /> </Types>`.replace(/\n/, "\r\n"), { name: "[Content_Types].xml" }); archive.append(`<?xml version="1.0" encoding="utf-8"?> <coreProperties xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.openxmlformats.org/package/2006/metadata/core-properties"> <dc:creator>${ author }</dc:creator> <dc:description>${ options.description }</dc:description> <dc:identifier>${ options.appId }</dc:identifier> <version>${ version }</version> <keywords/> <dc:title>${ options.productName }</dc:title> <lastModifiedBy>NuGet, Version=2.8.50926.602, Culture=neutral, PublicKeyToken=null;Microsoft Windows NT 6.2.9200.0;.NET Framework 4</lastModifiedBy> </coreProperties>`.replace(/\n/, "\r\n"), { name: "1.psmdcp", prefix: "package/services/metadata/core-properties" }); archive.file(updateFile, { name: "Update.exe", prefix: "lib/net45" }); encodedZip(archive, directory, "lib/net45"); yield archivePromise; }); } function releasify(options, nupkgPath, outputDirectory, packageName) { return __awaiter(this, void 0, void 0, function* () { const args = ["--releasify", nupkgPath, "--releaseDir", outputDirectory]; const out = (yield util_1.exec(process.platform === "win32" ? path.join(options.vendorPath, "Update.com") : "mono", prepareArgs(args, path.join(options.vendorPath, "Update-Mono.exe")), { maxBuffer: 4 * 1024000 })).trim(); if (util_2.debug.enabled) { util_2.debug(out); } const lines = out.split("\n"); for (let i = lines.length - 1; i > -1; i--) { const line = lines[i]; if (line.indexOf(packageName) !== -1) { return line.trim(); } } throw new Error("Invalid output, cannot find last release entry"); }); } function msi(options, nupkgPath, setupPath, outputDirectory, outFile) { return __awaiter(this, void 0, void 0, function* () { const args = ["--createMsi", nupkgPath, "--bootstrapperExe", setupPath]; yield util_1.exec(process.platform === "win32" ? path.join(options.vendorPath, "Update.com") : "mono", prepareArgs(args, path.join(options.vendorPath, "Update-Mono.exe"))); //noinspection SpellCheckingInspection yield util_1.exec(path.join(options.vendorPath, "candle.exe"), ["-nologo", "-ext", "WixNetFxExtension", "-out", "Setup.wixobj", "Setup.wxs"], { cwd: outputDirectory }); //noinspection SpellCheckingInspection yield util_1.exec(path.join(options.vendorPath, "light.exe"), ["-ext", "WixNetFxExtension", "-sval", "-out", outFile, "Setup.wixobj"], { cwd: outputDirectory }); //noinspection SpellCheckingInspection yield bluebird_1.Promise.all([fs_extra_p_1.unlink(path.join(outputDirectory, "Setup.wxs")), fs_extra_p_1.unlink(path.join(outputDirectory, "Setup.wixobj")), fs_extra_p_1.unlink(path.join(outputDirectory, outFile.replace(".msi", ".wixpdb"))).catch(e => util_2.debug(e.toString()))]); }); } function prepareArgs(args, exePath) { if (process.platform !== "win32") { args.unshift(exePath); } return args; } function encodedZip(archive, dir, prefix) { archiverUtil.walkdir(dir, function (error, files) { if (error) { archive.emit("error", error); return; } for (let file of files) { if (file.stats.isDirectory()) { continue; } // GBK file name encoding (or Non-English file name) caused a problem const entryData = { name: encodeURI(file.relative.replace(/\\/g, "/")).replace(/%5B/g, "[").replace(/%5D/g, "]"), prefix: prefix, stats: file.stats }; archive._append(file.path, entryData); } archive.finalize(); }); } //# sourceMappingURL=squirrelPack.js.map