Skip to content

Commit 317a330

Browse files
committed
fix: Asar: true failing on Windows for electron-builder 5.x
Closes #482
1 parent b091a13 commit 317a330

File tree

7 files changed

+119
-88
lines changed

7 files changed

+119
-88
lines changed

src/asarUtil.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { AsarFileInfo, listPackage, statFile, AsarOptions, AsarFileMetadata, createPackageFromFiles } from "asar"
2+
import { statOrNull } from "./util"
3+
import { Glob } from "glob"
4+
import { lstat, remove } from "fs-extra-p"
5+
import { Promise as BluebirdPromise } from "bluebird"
6+
import * as path from "path"
7+
8+
//noinspection JSUnusedLocalSymbols
9+
const __awaiter = require("./awaiter")
10+
11+
export async function createAsarArchive(src: string, resourcesPath: string, options: AsarOptions): Promise<any> {
12+
// dot: true as in the asar by default by we use glob default - do not copy hidden files
13+
let glob: Glob | null = null
14+
const files = (await new BluebirdPromise<Array<string>>((resolve, reject) => {
15+
glob = new Glob("**/*", {
16+
cwd: src,
17+
}, (error, matches) => {
18+
if (error == null) {
19+
resolve(matches)
20+
}
21+
else {
22+
reject(error)
23+
}
24+
})
25+
})).map(it => path.join(src, it))
26+
27+
const metadata: { [key: string]: AsarFileMetadata; } = {}
28+
29+
// https://github.com/electron-userland/electron-builder/issues/482#issuecomment-225100630
30+
const stats = await BluebirdPromise.map(files, it => {
31+
const systemIndependentPath = process.platform === "win32" ? it.replace(/\\/g, "/") : it
32+
if (glob!.symlinks[systemIndependentPath]) {
33+
// asar doesn't use stat for link
34+
metadata[it] = {
35+
type: "link",
36+
}
37+
return null
38+
}
39+
40+
const cachedType = glob!.cache[systemIndependentPath]
41+
if (cachedType == null || cachedType === "FILE") {
42+
const stat = glob!.statCache[systemIndependentPath]
43+
return stat == null ? lstat(it) : <any>stat
44+
}
45+
else {
46+
// asar doesn't use stat for dir
47+
metadata[it] = {
48+
type: "directory",
49+
}
50+
}
51+
return null
52+
})
53+
54+
for (let i = 0, n = files.length; i < n; i++) {
55+
const stat = stats[i]
56+
if (stat != null) {
57+
metadata[files[i]] = {
58+
type: stat.isFile() ? "file" : (stat.isDirectory() ? "directory" : "link"),
59+
stat: stat,
60+
}
61+
}
62+
}
63+
64+
await BluebirdPromise.promisify(createPackageFromFiles)(src, path.join(resourcesPath, "app.asar"), files, metadata, options)
65+
await remove(src)
66+
}
67+
68+
export async function checkFileInPackage(asarFile: string, relativeFile: string) {
69+
let stat: AsarFileInfo | null
70+
try {
71+
stat = statFile(asarFile, relativeFile)
72+
}
73+
catch (e) {
74+
const fileStat = await statOrNull(asarFile)
75+
if (fileStat == null) {
76+
throw new Error(`File "${asarFile}" does not exist. Seems like a wrong configuration.`)
77+
}
78+
79+
try {
80+
listPackage(asarFile)
81+
}
82+
catch (e) {
83+
throw new Error(`File "${asarFile}" is corrupted: ${e}`)
84+
}
85+
86+
// asar throws error on access to undefined object (info.link)
87+
stat = null
88+
}
89+
90+
if (stat == null) {
91+
throw new Error(`Application entry file "${relativeFile}" in the "${asarFile}" does not exist. Seems like a wrong configuration.`)
92+
}
93+
if (stat.size === 0) {
94+
throw new Error(`Application entry file "${relativeFile}" in the "${asarFile}" is corrupted: size 0`)
95+
}
96+
}

src/osxPackager.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ export default class OsXPackager extends PlatformPackager<OsXBuildOptions> {
6262
private static async findIdentity(certType: CertType, name?: string | null): Promise<string | null> {
6363
let identity = process.env.CSC_NAME || name
6464
if (isEmptyOrSpaces(identity)) {
65+
if (process.env.CSC_IDENTITY_AUTO_DISCOVERY === "false") {
66+
return null
67+
}
6568
return await findIdentity(certType)
6669
}
6770
else {

src/platformPackager.ts

Lines changed: 14 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import EventEmitter = NodeJS.EventEmitter
44
import { Promise as BluebirdPromise } from "bluebird"
55
import * as path from "path"
66
import { pack, ElectronPackagerOptions, userIgnoreFilter } from "electron-packager-tf"
7-
import { readdir, copy, unlink, lstat, remove, realpath } from "fs-extra-p"
7+
import { readdir, copy, unlink, remove, realpath } from "fs-extra-p"
88
import { statOrNull, use, warn, log, exec } from "./util"
99
import { Packager } from "./packager"
10-
import { listPackage, statFile, AsarFileMetadata, createPackageFromFiles, AsarOptions } from "asar"
10+
import { AsarOptions } from "asar"
1111
import { archiveApp } from "./targets/archive"
12-
import { Glob } from "glob"
1312
import { Minimatch } from "minimatch"
13+
import { checkFileInPackage, createAsarArchive } from "./asarUtil"
1414
import deepAssign = require("deep-assign")
1515

1616
//noinspection JSUnusedLocalSymbols
@@ -200,7 +200,7 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
200200
}
201201

202202
if (asarOptions != null) {
203-
await this.createAsarArchive(appPath, resourcesPath, asarOptions)
203+
await createAsarArchive(appPath, resourcesPath, asarOptions)
204204
}
205205
}
206206
await pack(options)
@@ -289,58 +289,6 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
289289
}
290290
}
291291

292-
private async createAsarArchive(src: string, resourcesPath: string, options: AsarOptions): Promise<any> {
293-
// dot: true as in the asar by default by we use glob default - do not copy hidden files
294-
let glob: Glob | null = null
295-
const files = (await new BluebirdPromise<Array<string>>((resolve, reject) => {
296-
glob = new Glob("**/*", {
297-
cwd: src,
298-
}, (error, matches) => {
299-
if (error == null) {
300-
resolve(matches)
301-
}
302-
else {
303-
reject(error)
304-
}
305-
})
306-
})).map(it => path.join(src, it))
307-
308-
const metadata: { [key: string]: AsarFileMetadata; } = {}
309-
310-
const stats = await BluebirdPromise.map(files, it => {
311-
if (glob!.symlinks[it]) {
312-
// asar doesn't use stat for link
313-
metadata[it] = {
314-
type: "link",
315-
}
316-
}
317-
else if (glob!.cache[it] === "FILE") {
318-
const stat = glob!.statCache[it]
319-
return stat == null ? lstat(it) : <any>stat
320-
}
321-
else {
322-
// asar doesn't use stat for dir
323-
metadata[it] = {
324-
type: "directory",
325-
}
326-
}
327-
return null
328-
})
329-
330-
for (let i = 0, n = files.length; i < n; i++) {
331-
const stat = stats[i]
332-
if (stat != null) {
333-
metadata[files[i]] = {
334-
type: "file",
335-
stat: stat,
336-
}
337-
}
338-
}
339-
340-
await BluebirdPromise.promisify(createPackageFromFiles)(src, path.join(resourcesPath, "app.asar"), files, metadata, options)
341-
await remove(src)
342-
}
343-
344292
private expandPattern(pattern: string, arch: Arch): string {
345293
return pattern
346294
.replace(/\$\{arch}/g, Arch[arch])
@@ -406,33 +354,19 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
406354
return path.join(appOutDir, `${this.appName}.app`, "Contents", "Resources")
407355
}
408356

409-
private async statFileInPackage(resourcesDir: string, packageFile: string, isAsar: boolean): Promise<any> {
410-
const relativeFile = path.relative(this.info.appDir, path.resolve(this.info.appDir, packageFile))
357+
private async checkFileInPackage(resourcesDir: string, file: string, isAsar: boolean) {
358+
const relativeFile = path.relative(this.info.appDir, path.resolve(this.info.appDir, file))
411359
if (isAsar) {
412-
try {
413-
return statFile(path.join(resourcesDir, "app.asar"), relativeFile) != null
414-
}
415-
catch (e) {
416-
const asarFile = path.join(resourcesDir, "app.asar")
417-
const fileStat = await statOrNull(asarFile)
418-
if (fileStat == null) {
419-
throw new Error(`File "${asarFile}" does not exist. Seems like a wrong configuration.`)
420-
}
421-
422-
try {
423-
listPackage(asarFile)
424-
}
425-
catch (e) {
426-
throw new Error(`File "${asarFile}" is corrupted: ${e}`)
427-
}
428-
429-
// asar throws error on access to undefined object (info.link)
430-
return false
431-
}
360+
await checkFileInPackage(path.join(resourcesDir, "app.asar"), relativeFile)
432361
}
433362
else {
434363
const outStat = await statOrNull(path.join(resourcesDir, "app", relativeFile))
435-
return outStat != null && outStat.isFile()
364+
if (outStat == null) {
365+
throw new Error(`Application entry file "${relativeFile}" does not exist. Seems like a wrong configuration.`)
366+
}
367+
else if (!outStat.isFile()) {
368+
throw new Error(`Application entry file "${relativeFile}" is not a file. Seems like a wrong configuration.`)
369+
}
436370
}
437371
}
438372

@@ -446,12 +380,8 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
446380
throw new Error(`Output directory "${appOutDir}" is not a directory. Seems like a wrong configuration.`)
447381
}
448382

449-
const resourcesDir = this.getResourcesDir(appOutDir)
450383
const mainFile = this.metadata.main || "index.js"
451-
const mainFileExists = await this.statFileInPackage(resourcesDir, mainFile, isAsar)
452-
if (!mainFileExists) {
453-
throw new Error(`Application entry file ${mainFile} could not be found in package. Seems like a wrong configuration.`)
454-
}
384+
await this.checkFileInPackage(this.getResourcesDir(appOutDir), mainFile, isAsar)
455385
}
456386

457387
protected async archiveApp(format: string, appOutDir: string, outFile: string): Promise<any> {

test/src/BuildTest.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ test("invalid main in the app package.json", t => t.throws(assertPack("test-app"
9494
tempDirCreated: projectDir => modifyPackageJson(projectDir, data => {
9595
data.main = "main.js"
9696
}, true)
97-
}), "Application entry file main.js could not be found in package. Seems like a wrong configuration."))
97+
}), /Application entry file "main.js" in the /))
9898

9999
test("invalid main in the app package.json (no asar)", t => t.throws(assertPack("test-app", allPlatforms(false), {
100100
tempDirCreated: projectDir => {
@@ -107,7 +107,7 @@ test("invalid main in the app package.json (no asar)", t => t.throws(assertPack(
107107
})
108108
])
109109
}
110-
}), "Application entry file main.js could not be found in package. Seems like a wrong configuration."))
110+
}), `Application entry file "main.js" does not exist. Seems like a wrong configuration.`))
111111

112112
test("main in the app package.json (no asar)", () => assertPack("test-app", allPlatforms(false), {
113113
tempDirCreated: projectDir => {

test/src/helpers/runTests.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ function runTests(): BluebirdPromise<any> {
155155
env: Object.assign({}, process.env, {
156156
NODE_PATH: path.join(testNodeModules, "electron-builder"),
157157
SKIP_WIN: skipWin,
158+
CSC_IDENTITY_AUTO_DISCOVERY: "false",
158159
}),
159160
shell: process.platform === "win32",
160161
stdio: "inherit"

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"node_modules/7zip-bin/index.d.ts",
5858
"node_modules/fs-extra-p/bluebird.d.ts",
5959
"node_modules/electron-osx-sign-tf/index.d.ts",
60+
"src/asarUtil.ts",
6061
"src/awaiter.ts",
6162
"src/build-cli.ts",
6263
"src/builder.ts",

typings/asar.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
declare module "asar" {
22
import { Stats } from "fs"
33

4-
interface Info {
4+
interface AsarFileInfo {
55
offset: number
66
size: number
77
}
@@ -19,7 +19,7 @@ declare module "asar" {
1919
export function listPackage(archive: string): Array<string>
2020

2121
// followLinks defaults to true
22-
export function statFile(archive: string, filename: string, followLinks?: boolean): Info | null
22+
export function statFile(archive: string, filename: string, followLinks?: boolean): AsarFileInfo | null
2323

2424
export function createPackageFromFiles(src: string, dest: string, filenames: Array<string>, metadata: { [key: string]: AsarFileMetadata;}, options: AsarOptions, callback: (error?: Error) => void): void
2525
}

0 commit comments

Comments
 (0)