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
165 lines (164 loc) • 8.76 kB
JavaScript
;
const util_1 = require("./util/util");
const fs_extra_p_1 = require("fs-extra-p");
const httpRequest_1 = require("./util/httpRequest");
const path = require("path");
const promise_1 = require("./util/promise");
const bluebird_1 = require("bluebird");
const crypto_1 = require("crypto");
const os_1 = require("os");
//noinspection JSUnusedLocalSymbols
const __awaiter = require("./util/awaiter");
const appleCertificatePrefixes = ["Developer ID Application:", "3rd Party Mac Developer Application:", "Developer ID Installer:", "3rd Party Mac Developer Installer:"];
function downloadCertificate(urlOrBase64, tmpDir) {
if (urlOrBase64.startsWith("file://")) {
return bluebird_1.Promise.resolve(urlOrBase64.substring("file://".length));
}
return tmpDir.getTempFile(".p12").then(tempFile => (urlOrBase64.startsWith("https://") ? httpRequest_1.download(urlOrBase64, tempFile) : fs_extra_p_1.outputFile(tempFile, new Buffer(urlOrBase64, "base64"))).thenReturn(tempFile));
}
exports.downloadCertificate = downloadCertificate;
let bundledCertKeychainAdded = null;
// "Note that filename will not be searched to resolve the signing identity's certificate chain unless it is also on the user's keychain search list."
// but "security list-keychains" doesn't support add - we should 1) get current list 2) set new list - it is very bad http://stackoverflow.com/questions/10538942/add-a-keychain-to-search-list
// "overly complicated and introduces a race condition."
// https://github.com/electron-userland/electron-builder/issues/398
function createCustomCertKeychain() {
return __awaiter(this, void 0, void 0, function* () {
// copy to temp and then atomic rename to final path
const tmpKeychainPath = path.join(os_1.homedir(), ".cache", util_1.getTempName("electron_builder_root_certs"));
const keychainPath = path.join(os_1.homedir(), ".cache", "electron_builder_root_certs.keychain");
const results = yield bluebird_1.Promise.all([util_1.exec("security", ["list-keychains"]), fs_extra_p_1.copy(path.join(__dirname, "..", "certs", "root_certs.keychain"), tmpKeychainPath).then(() => fs_extra_p_1.rename(tmpKeychainPath, keychainPath))]);
const list = results[0].split("\n").map(it => {
let r = it.trim();
return r.substring(1, r.length - 1);
}).filter(it => it.length > 0);
if (!(list.indexOf(keychainPath) !== -1)) {
yield util_1.exec("security", ["list-keychains", "-d", "user", "-s", keychainPath].concat(list));
}
});
}
function createKeychain(tmpDir, cscLink, cscKeyPassword, cscILink, cscIKeyPassword) {
return __awaiter(this, void 0, void 0, function* () {
if (bundledCertKeychainAdded == null) {
bundledCertKeychainAdded = createCustomCertKeychain();
}
yield bundledCertKeychainAdded;
const keychainName = yield tmpDir.getTempFile(".keychain");
const certLinks = [cscLink];
if (cscILink != null) {
certLinks.push(cscILink);
}
const certPaths = new Array(certLinks.length);
const keychainPassword = crypto_1.randomBytes(8).toString("hex");
return yield promise_1.executeFinally(bluebird_1.Promise.all([bluebird_1.Promise.map(certLinks, (link, i) => downloadCertificate(link, tmpDir).then(it => certPaths[i] = it)), bluebird_1.Promise.mapSeries([["create-keychain", "-p", keychainPassword, keychainName], ["unlock-keychain", "-p", keychainPassword, keychainName], ["set-keychain-settings", "-t", "3600", "-u", keychainName]], it => util_1.exec("security", it))]).then(() => importCerts(keychainName, certPaths, [cscKeyPassword, cscIKeyPassword].filter(it => it != null))), () => promise_1.all(certPaths.map((it, index) => certLinks[index].startsWith("https://") ? fs_extra_p_1.deleteFile(it, true) : bluebird_1.Promise.resolve())));
});
}
exports.createKeychain = createKeychain;
function importCerts(keychainName, paths, keyPasswords) {
return __awaiter(this, void 0, void 0, function* () {
for (let i = 0; i < paths.length; i++) {
yield util_1.exec("security", ["import", paths[i], "-k", keychainName, "-T", "/usr/bin/codesign", "-T", "/usr/bin/productbuild", "-P", keyPasswords[i]]);
}
return {
keychainName: keychainName
};
});
}
function sign(path, name, keychain) {
const args = ["--deep", "--force", "--sign", name, path];
if (keychain != null) {
args.push("--keychain", keychain);
}
return util_1.exec("codesign", args);
}
exports.sign = sign;
exports.findIdentityRawResult = null;
function getValidIdentities(keychain) {
return __awaiter(this, void 0, void 0, function* () {
function addKeychain(args) {
if (keychain != null) {
args.push(keychain);
}
return args;
}
let result = exports.findIdentityRawResult;
if (result == null || keychain != null) {
// https://github.com/electron-userland/electron-builder/issues/481
// https://github.com/electron-userland/electron-builder/issues/535
result = bluebird_1.Promise.all([util_1.exec("security", addKeychain(["find-identity", "-v"])).then(it => it.trim().split("\n").filter(it => {
for (let prefix of appleCertificatePrefixes) {
if (it.indexOf(prefix) !== -1) {
return true;
}
}
return false;
})), util_1.exec("security", addKeychain(["find-identity", "-v", "-p", "codesigning"])).then(it => it.trim().split("\n"))]).then(it => {
const array = it[0].concat(it[1]).filter(it => !(it.indexOf("(Missing required extension)") !== -1) && !(it.indexOf("valid identities found") !== -1) && !(it.indexOf("iPhone ") !== -1) && !(it.indexOf("com.apple.idms.appleid.prd.") !== -1) && !(it.indexOf("Mac Developer:") !== -1)).map(it => it.substring(it.indexOf(")") + 1).trim());
return Array.from(new Set(array));
});
if (keychain == null) {
exports.findIdentityRawResult = result;
}
}
return result;
});
}
function _findIdentity(namePrefix, qualifier, keychain) {
return __awaiter(this, void 0, void 0, function* () {
// https://github.com/electron-userland/electron-builder/issues/484
//noinspection SpellCheckingInspection
const lines = yield getValidIdentities(keychain);
for (let line of lines) {
if (qualifier != null && !(line.indexOf(qualifier) !== -1)) {
continue;
}
if (line.indexOf(namePrefix) !== -1) {
return line.substring(line.indexOf('"') + 1, line.lastIndexOf('"'));
}
}
if (namePrefix === "Developer ID Application") {
// find non-Apple certificate
// https://github.com/electron-userland/electron-builder/issues/458
l: for (let line of lines) {
if (qualifier != null && !(line.indexOf(qualifier) !== -1)) {
continue;
}
for (let prefix of appleCertificatePrefixes) {
if (line.indexOf(prefix) !== -1) {
continue l;
}
}
return line.substring(line.indexOf('"') + 1, line.lastIndexOf('"'));
}
}
return null;
});
}
function findIdentity(certType, qualifier, keychain) {
return __awaiter(this, void 0, void 0, function* () {
let identity = process.env.CSC_NAME || qualifier;
if (util_1.isEmptyOrSpaces(identity)) {
if (keychain == null && process.env.CI == null && process.env.CSC_IDENTITY_AUTO_DISCOVERY === "false") {
return null;
}
return yield _findIdentity(certType, null, keychain);
} else {
identity = identity.trim();
for (let prefix of appleCertificatePrefixes) {
checkPrefix(identity, prefix);
}
const result = yield _findIdentity(certType, identity, keychain);
if (result == null) {
throw new Error(`Identity name "${ identity }" is specified, but no valid identity with this name in the keychain`);
}
return result;
}
});
}
exports.findIdentity = findIdentity;
function checkPrefix(name, prefix) {
if (name.startsWith(prefix)) {
throw new Error(`Please remove prefix "${ prefix }" from the specified name — appropriate certificate will be chosen automatically`);
}
}
//# sourceMappingURL=codeSign.js.map