export async function createSelfSignedCert(publisher: string) { const tmpDir = new TmpDir() const targetDir = process.cwd() const tempPrefix = path.join(await tmpDir.getTempDir({prefix: "self-signed-cert-creator"}), sanitizeFileName(publisher)) const cer = `${tempPrefix}.cer` const pvk = `${tempPrefix}.pvk` log.info(chalk.bold('When asked to enter a password ("Create Private Key Password"), please select "None".')) try { await ensureDir(path.dirname(tempPrefix)) const vendorPath = path.join(await getSignVendorPath(), "windows-10", process.arch) await exec(path.join(vendorPath, "makecert.exe"), ["-r", "-h", "0", "-n", `CN=${quoteString(publisher)}`, "-eku", "1.3.6.1.5.5.7.3.3", "-pe", "-sv", pvk, cer]) const pfx = path.join(targetDir, `${sanitizeFileName(publisher)}.pfx`) await unlinkIfExists(pfx) await exec(path.join(vendorPath, "pvk2pfx.exe"), ["-pvk", pvk, "-spc", cer, "-pfx", pfx]) log.info({file: pfx}, `created. Please see https://electron.build/code-signing how to use it to sign.`) const certLocation = "Cert:\\LocalMachine\\TrustedPeople" log.info({file: pfx, certLocation}, `importing. Operation will be succeed only if runned from root. Otherwise import file manually.`) await spawn("powershell.exe", ["Import-PfxCertificate", "-FilePath", `"${pfx}"`, "-CertStoreLocation", ""]) } finally { await tmpDir.cleanup() } }
async download(url: string, destination: string, options?: DownloadOptions | null): Promise<string> { if (options == null || !options.skipDirCreation) { await ensureDir(path.dirname(destination)) } if (this.httpsAgentPromise == null) { this.httpsAgentPromise = createAgent() } const agent = await this.httpsAgentPromise return await new BluebirdPromise<string>((resolve, reject) => { const parsedUrl = parseUrl(url) this.doDownload(configureRequestOptions({ hostname: parsedUrl.hostname, path: parsedUrl.path, headers: (options == null ? null : options.headers) || undefined, agent: agent, }), destination, 0, options || {}, (error: Error) => { if (error == null) { resolve(destination) } else { reject(error) } }) }) }
async download(url: string, destination: string, options: DownloadOptions = {cancellationToken: new CancellationToken()}): Promise<string> { if (!options.skipDirCreation) { await ensureDir(path.dirname(destination)) } if (this.httpsAgentPromise == null) { this.httpsAgentPromise = createAgent() } const agent = await this.httpsAgentPromise return await options.cancellationToken.createPromise<string>((resolve, reject, onCancel) => { const parsedUrl = parseUrl(url) this.doDownload(configureRequestOptions({ hostname: parsedUrl.hostname, path: parsedUrl.path, headers: options.headers || undefined, agent, }), destination, 0, options, (error: Error) => { if (error == null) { resolve(destination) } else { reject(error) } }, onCancel) }) }
async prepareWine(wineDir: string) { await emptyDir(wineDir) //noinspection SpellCheckingInspection const env = Object.assign({}, process.env, { WINEDLLOVERRIDES: "winemenubuilder.exe=d", WINEPREFIX: wineDir }) await exec("wineboot", ["--init"], {env: env}) // regedit often doesn't modify correctly let systemReg = await readFile(path.join(wineDir, "system.reg"), "utf8") systemReg = systemReg.replace('"CSDVersion"="Service Pack 3"', '"CSDVersion"=" "') systemReg = systemReg.replace('"CurrentBuildNumber"="2600"', '"CurrentBuildNumber"="10240"') systemReg = systemReg.replace('"CurrentVersion"="5.1"', '"CurrentVersion"="10.0"') systemReg = systemReg.replace('"ProductName"="Microsoft Windows XP"', '"ProductName"="Microsoft Windows 10"') systemReg = systemReg.replace('"CSDVersion"=dword:00000300', '"CSDVersion"=dword:00000000') await writeFile(path.join(wineDir, "system.reg"), systemReg) // remove links to host OS const desktopDir = path.join(this.userDir, "Desktop") await BluebirdPromise.all([ unlinkIfExists(desktopDir), unlinkIfExists(path.join(this.userDir, "My Documents")), unlinkIfExists(path.join(this.userDir, "My Music")), unlinkIfExists(path.join(this.userDir, "My Pictures")), unlinkIfExists(path.join(this.userDir, "My Videos")), ]) await ensureDir(desktopDir) return env }
await BluebirdPromise.each(dirToCreate.get(parentDir)!, (it): any => { if (dirToCreate.has(parentDir + path.sep + it)) { // already created return null } else { return ensureDir(base + path.sep + it) } })
//noinspection JSMethodCanBeStatic protected async doFlat(appPath: string, outFile: string, identity: Identity, keychain: string | n): Promise<any> { // productbuild doesn't created directory for out file await ensureDir(path.dirname(outFile)) const args = prepareProductBuildArgs(identity, keychain) args.push("--component", appPath, "/Applications") args.push(outFile) return await exec("productbuild", args) }
await BluebirdPromise.map(dirToCreate.keys(), async parentDir => { const base = unpackedDest + path.sep + parentDir await ensureDir(base) await BluebirdPromise.each(dirToCreate.get(parentDir)!, (it): any => { if (dirToCreate.has(parentDir + path.sep + it)) { // already created return null } else { return ensureDir(base + path.sep + it) } }) }, CONCURRENCY)
function doDownload(url: string, destination: string, redirectCount: number, callback: (error: Error) => void) { const ensureDirPromise = ensureDir(path.dirname(destination)) const parsedUrl = parseUrl(url) // user-agent must be specified, otherwise some host can return 401 unauthorised const request = https.request({ hostname: parsedUrl.hostname, path: parsedUrl.path, headers: { "User-Agent": "electron-builder" } }, (response: IncomingMessage) => { if (response.statusCode >= 400) { callback(new Error("Request error, status " + response.statusCode + ": " + response.statusMessage)) return } const redirectUrl = response.headers.location if (redirectUrl != null) { if (redirectCount < maxRedirects) { doDownload(redirectUrl, destination, redirectCount++, callback) } else { callback(new Error("Too many redirects (> " + maxRedirects + ")")) } return } ensureDirPromise .then(() => { const downloadStream = createWriteStream(destination) response.pipe(downloadStream) downloadStream.on("finish", () => downloadStream.close(callback)) }) .catch(callback) let ended = false response.on("end", () => { ended = true }) response.on("close", () => { if (!ended) { callback(new Error("Request aborted")) } }) }) addTimeOutHandler(request, callback) request.on("error", callback) request.end() }
export async function copyAppFiles(fileSet: FileSet, packager: Packager) { const metadata = fileSet.metadata const transformedFiles = fileSet.transformedFiles // search auto unpacked dir const unpackedDirs = new Set<string>() const taskManager = new AsyncTaskManager(packager.cancellationToken) const dirToCreateForUnpackedFiles = new Set<string>(unpackedDirs) const fileCopier = new FileCopier() const links: Array<Link> = [] for (let i = 0, n = fileSet.files.length; i < n; i++) { const file = fileSet.files[i] const stat = metadata.get(file) if (stat == null) { // dir continue } const relativePath = file.replace(fileSet.src, fileSet.destination) if (stat.isFile()) { const fileParent = path.dirname(file) // const dirNode = this.fs.getOrCreateNode(this.getRelativePath(fileParent)) const newData = transformedFiles == null ? null : transformedFiles[i] as string | Buffer if (newData != null) { transformedFiles[i] = null } if (!dirToCreateForUnpackedFiles.has(fileParent)) { dirToCreateForUnpackedFiles.add(fileParent) await ensureDir(fileParent.replace(fileSet.src, fileSet.destination)) } taskManager.addTask(copyFileOrData(fileCopier, newData, file, relativePath, stat)) if (taskManager.tasks.length > MAX_FILE_REQUESTS) { await taskManager.awaitTasks() } } else if (stat.isSymbolicLink()) { links.push({file: relativePath, link: await readlink(file)}) } } if (taskManager.tasks.length > MAX_FILE_REQUESTS) { await taskManager.awaitTasks() } if (links.length > 0) { BluebirdPromise.map(links, it => symlink(it.link, it.file), CONCURRENCY) } }
// http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/s3-example-creating-buckets.html async upload(task: UploadTask): Promise<any> { const fileName = path.basename(task.file) const cancellationToken = this.context.cancellationToken const target = (this.options.path == null ? "" : `${this.options.path}/`) + fileName if (process.env.__TEST_S3_PUBLISHER__ != null) { const testFile = path.join(process.env.__TEST_S3_PUBLISHER__!, target) await ensureDir(path.dirname(testFile)) await symlink(task.file, testFile) return } const s3Options: CreateMultipartUploadRequest = { Key: target, Bucket: this.getBucketName(), ContentType: mime.getType(task.file) || "application/octet-stream" } this.configureS3Options(s3Options) const contentLength = task.fileContent == null ? (await stat(task.file)).size : task.fileContent.length const uploader = new Uploader(new S3(this.createClientConfiguration()), s3Options, task.file, contentLength, task.fileContent) const progressBar = this.createProgressBar(fileName, uploader.contentLength) if (progressBar != null) { const callback = new ProgressCallback(progressBar) uploader.on("progress", () => { if (!cancellationToken.cancelled) { callback.update(uploader.loaded, uploader.contentLength) } }) } return await cancellationToken.createPromise((resolve, reject, onCancel) => { onCancel(() => uploader.abort()) uploader.upload() .then(() => { try { log.debug({provider: this.providerName, file: fileName, bucket: this.getBucketName()}, "uploaded") } finally { resolve() } }) .catch(reject) }) }