Skip to content

Commit 6977557

Browse files
committed
feat: Squirrel.Windows doesn't escape " in the description
Closes #378
1 parent b71d2f3 commit 6977557

File tree

12 files changed

+80
-82
lines changed

12 files changed

+80
-82
lines changed

docs/Multi Platform Build.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ To build app in distributable format for Windows on Linux:
4949
sudo apt-get install mono-devel -y
5050
```
5151

52+
* Install zip.
53+
```
54+
apt-get install zip
55+
```
56+
5257
To build app in 32 bit from a machine with 64 bit:
5358

5459
```

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"bugs": "https://github.com/electron-userland/electron-builder/issues",
5757
"homepage": "https://github.com/electron-userland/electron-builder",
5858
"dependencies": {
59-
"7zip-bin": "^0.3.0",
59+
"7zip-bin": "^1.0.0",
6060
"asar": "^0.11.0",
6161
"bluebird": "^3.3.5",
6262
"chalk": "^1.1.3",
@@ -66,7 +66,7 @@
6666
"deep-assign": "^2.0.0",
6767
"electron-osx-sign-tf": "0.4.0-beta.0",
6868
"electron-packager-tf": "^7.0.2-beta.0",
69-
"electron-winstaller-fixed": "~2.6.2",
69+
"electron-winstaller-fixed": "~2.7.0",
7070
"fs-extra-p": "^1.0.1",
7171
"globby": "^4.0.0",
7272
"hosted-git-info": "^2.1.4",
@@ -106,7 +106,7 @@
106106
"should": "^8.3.1",
107107
"ts-babel": "^0.8.6",
108108
"tsconfig-glob": "^0.4.3",
109-
"tslint": "^3.9.0-dev.0",
109+
"tslint": "^3.10.0-dev.1",
110110
"typescript": "1.9.0-dev.20160511",
111111
"whitespace": "^2.0.0"
112112
},

src/cleanup.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ async function main() {
4646
async function isRecentlyUsed(dir: string) {
4747
try {
4848
const lastUsed = parseInt(await readFile(path.join(dir, ".lastUsed"), "utf8"), 10)
49-
if (!isNaN(lastUsed) && (Date.now() - lastUsed) < 3600000) {
49+
if (!isNaN(lastUsed) && (Date.now() - lastUsed) < (3600000 * 2)) {
5050
return true
5151
}
5252
}

src/linuxPackager.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,15 @@ export class LinuxPackager extends PlatformPackager<LinuxBuildOptions> {
6363
return [].concat(...await BluebirdPromise.all(promises))
6464
}
6565

66+
async pack(outDir: string, arch: string, postAsyncTasks: Array<Promise<any>>): Promise<any> {
67+
const appOutDir = this.computeAppOutDir(outDir, arch)
68+
await this.doPack(this.computePackOptions(outDir, arch), outDir, appOutDir, arch, this.customBuildOptions)
69+
70+
if (this.options.dist) {
71+
postAsyncTasks.push(this.packageInDistributableFormat(outDir, appOutDir, arch))
72+
}
73+
}
74+
6675
private async computeDesktop(tempDir: string): Promise<Array<string>> {
6776
const tempFile = path.join(tempDir, this.appName + ".desktop")
6877
await outputFile(tempFile, this.debOptions.desktop || `[Desktop Entry]

src/platformPackager.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,17 +105,11 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
105105
})
106106
}
107107

108-
pack(outDir: string, arch: string, postAsyncTasks: Array<Promise<any>>): Promise<any> {
109-
const appOutDir = this.computeAppOutDir(outDir, arch)
110-
return this.doPack(this.computePackOptions(outDir, arch), outDir, appOutDir, arch, this.customBuildOptions, postAsyncTasks)
111-
}
108+
abstract pack(outDir: string, arch: string, postAsyncTasks: Array<Promise<any>>): Promise<any>
112109

113-
protected async doPack(options: ElectronPackagerOptions, outDir: string, appOutDir: string, arch: string, customBuildOptions: DC, postAsyncTasks: Array<Promise<any>> | null = null) {
110+
protected async doPack(options: ElectronPackagerOptions, outDir: string, appOutDir: string, arch: string, customBuildOptions: DC) {
114111
await this.packApp(options, appOutDir)
115112
await this.copyExtraResources(appOutDir, arch, customBuildOptions)
116-
if (postAsyncTasks != null && this.options.dist) {
117-
postAsyncTasks.push(this.packageInDistributableFormat(outDir, appOutDir, arch))
118-
}
119113
}
120114

121115
protected computePackOptions(outDir: string, arch: string): ElectronPackagerOptions {
@@ -137,6 +131,7 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
137131
asar: true,
138132
overwrite: true,
139133
"app-version": version,
134+
"app-copyright": `Copyright © ${new Date().getFullYear()} ${this.metadata.author.name || this.appName}`,
140135
"build-version": buildVersion,
141136
tmpdir: false,
142137
"version-string": {
@@ -187,8 +182,6 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
187182
return await BluebirdPromise.map(await this.getExtraResources(arch, customBuildOptions), it => copy(path.join(this.projectDir, it), path.join(resourcesDir, it)))
188183
}
189184

190-
protected abstract packageInDistributableFormat(outDir: string, appOutDir: string, arch: string): Promise<any>
191-
192185
protected async computePackageUrl(): Promise<string | null> {
193186
const url = this.metadata.homepage || this.devMetadata.homepage
194187
if (url != null) {

src/winPackager.ts

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,16 @@ import { PlatformPackager, BuildInfo } from "./platformPackager"
44
import { Platform, WinBuildOptions } from "./metadata"
55
import * as path from "path"
66
import { log, statOrNull } from "./util"
7-
import { readFile, deleteFile, stat, rename, copy, emptyDir, writeFile, open, close, read } from "fs-extra-p"
7+
import { readFile, deleteFile, rename, copy, emptyDir, writeFile, open, close, read, move } from "fs-extra-p"
88
import { sign } from "signcode-tf"
9+
import ElectronPackagerOptions = ElectronPackager.ElectronPackagerOptions
910

1011
//noinspection JSUnusedLocalSymbols
1112
const __awaiter = require("./awaiter")
1213

1314
export class WinPackager extends PlatformPackager<WinBuildOptions> {
1415
certFilePromise: Promise<string | null>
1516

16-
extraNuGetFileSources: Promise<Array<string>> | null
17-
1817
loadingGifStat: Promise<string> | null
1918

2019
readonly iconPath: Promise<string>
@@ -56,32 +55,30 @@ export class WinPackager extends PlatformPackager<WinBuildOptions> {
5655
// we must check icon before pack because electron-packager uses icon and it leads to cryptic error message "spawn wine ENOENT"
5756
await this.iconPath
5857

58+
let appOutDir = this.computeAppOutDir(outDir, arch)
59+
const packOptions = this.computePackOptions(outDir, arch)
60+
5961
if (!this.options.dist) {
60-
return await super.pack(outDir, arch, postAsyncTasks)
62+
await this.doPack(packOptions, outDir, appOutDir, arch, this.customBuildOptions)
63+
return
6164
}
6265

63-
const appOutDir = this.computeAppOutDir(outDir, arch)
66+
const unpackedDir = path.join(outDir, `win${arch === "x64" ? "" : `-${arch}`}-unpacked`)
67+
const finalAppOut = path.join(outDir, `win${arch === "x64" ? "" : `-${arch}`}-unpacked`, "lib", "net45")
6468
const installerOut = computeDistOut(outDir, arch)
65-
log("Removing %s", installerOut)
69+
log("Removing %s and %s", path.relative(this.projectDir, installerOut), path.relative(this.projectDir, unpackedDir))
6670
await BluebirdPromise.all([
67-
this.packApp(this.computePackOptions(outDir, arch), appOutDir),
68-
emptyDir(installerOut)
71+
this.packApp(packOptions, appOutDir),
72+
emptyDir(installerOut),
73+
emptyDir(unpackedDir)
6974
])
7075

71-
const extraResources = await this.copyExtraResources(appOutDir, arch, this.customBuildOptions)
72-
if (extraResources.length > 0) {
73-
this.extraNuGetFileSources = BluebirdPromise.map(extraResources, file => {
74-
return stat(file)
75-
.then(it => {
76-
const relativePath = path.relative(appOutDir, file)
77-
const src = it.isDirectory() ? `${relativePath}${path.sep}**` : relativePath
78-
return `<file src="${src}" target="lib\\net45\\${relativePath.replace(/\//g, "\\")}"/>`
79-
})
80-
})
81-
}
76+
await move(appOutDir, finalAppOut)
77+
appOutDir = finalAppOut
8278

79+
await this.copyExtraResources(appOutDir, arch, this.customBuildOptions)
8380
if (this.options.dist) {
84-
postAsyncTasks.push(this.packageInDistributableFormat(outDir, appOutDir, arch))
81+
postAsyncTasks.push(this.packageInDistributableFormat(outDir, appOutDir, arch, packOptions))
8582
}
8683
}
8784

@@ -102,7 +99,7 @@ export class WinPackager extends PlatformPackager<WinBuildOptions> {
10299
}
103100
}
104101

105-
protected async computeEffectiveDistOptions(appOutDir: string, installerOutDir: string): Promise<any> {
102+
protected async computeEffectiveDistOptions(appOutDir: string, installerOutDir: string, packOptions: ElectronPackagerOptions): Promise<any> {
106103
let iconUrl = this.customBuildOptions.iconUrl || this.devMetadata.build.iconUrl
107104
if (iconUrl == null) {
108105
if (this.info.repositoryInfo != null) {
@@ -120,6 +117,13 @@ export class WinPackager extends PlatformPackager<WinBuildOptions> {
120117
checkConflictingOptions(this.customBuildOptions)
121118

122119
const projectUrl = await this.computePackageUrl()
120+
const rceditOptions = {
121+
"version-string": packOptions["version-string"],
122+
"file-version": packOptions["build-version"],
123+
"product-version": packOptions["app-version"],
124+
}
125+
rceditOptions["version-string"]!.LegalCopyright = packOptions["app-copyright"]
126+
123127
const options: any = Object.assign({
124128
name: this.metadata.name,
125129
productName: this.appName,
@@ -138,13 +142,14 @@ export class WinPackager extends PlatformPackager<WinBuildOptions> {
138142
skipUpdateIcon: true,
139143
usePackageJson: false,
140144
noMsi: true,
141-
extraFileSpecs: this.extraNuGetFileSources == null ? null : ("\n" + (await this.extraNuGetFileSources).join("\n")),
142145
extraMetadataSpecs: projectUrl == null ? null : `\n <projectUrl>${projectUrl}</projectUrl>`,
146+
copyright: packOptions["app-copyright"],
143147
sign: {
144148
name: this.appName,
145149
site: projectUrl,
146150
overwrite: true,
147-
}
151+
},
152+
rcedit: rceditOptions,
148153
}, this.customBuildOptions)
149154

150155
if (this.loadingGifStat != null) {
@@ -154,9 +159,9 @@ export class WinPackager extends PlatformPackager<WinBuildOptions> {
154159
return options
155160
}
156161

157-
async packageInDistributableFormat(outDir: string, appOutDir: string, arch: string): Promise<any> {
162+
async packageInDistributableFormat(outDir: string, appOutDir: string, arch: string, packOptions: ElectronPackagerOptions): Promise<any> {
158163
const installerOutDir = computeDistOut(outDir, arch)
159-
await require("electron-winstaller-fixed").createWindowsInstaller(await this.computeEffectiveDistOptions(appOutDir, installerOutDir))
164+
await require("electron-winstaller-fixed").createWindowsInstaller(await this.computeEffectiveDistOptions(appOutDir, installerOutDir, packOptions))
160165

161166
const version = this.metadata.version
162167
const archSuffix = arch === "x64" ? "" : ("-" + arch)
@@ -170,8 +175,8 @@ export class WinPackager extends PlatformPackager<WinBuildOptions> {
170175
}
171176

172177
const promises: Array<Promise<any>> = [
173-
rename(path.join(installerOutDir, "Setup.exe"), path.join(installerOutDir, `${this.appName}Setup-${version}${archSuffix}.exe`))
174-
.then(it => this.dispatchArtifactCreated(it, `${this.metadata.name}Setup-${version}${archSuffix}.exe`)),
178+
rename(path.join(installerOutDir, "Setup.exe"), path.join(installerOutDir, `${this.appName} Setup ${version}${archSuffix}.exe`))
179+
.then(it => this.dispatchArtifactCreated(it, `${this.metadata.name}-Setup-${version}${archSuffix}.exe`)),
175180
]
176181

177182
if (archSuffix === "") {
@@ -244,7 +249,7 @@ function isIco(buffer: Buffer): boolean {
244249
}
245250

246251
export function computeDistOut(outDir: string, arch: string): string {
247-
return path.join(outDir, "win" + (arch === "x64" ? "-x64" : ""))
252+
return path.join(outDir, "win" + (arch === "x64" ? "" : "-arch"))
248253
}
249254

250255
function checkConflictingOptions(options: any) {

test/fixtures/test-app-one/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"name": "TestApp",
44
"version": "1.1.0",
55
"homepage": "http://foo.example.com",
6-
"description": "Test Application",
6+
"description": "Test Application (test quite \" #378)",
77
"scripts": {
88
"start": "electron ."
99
},

test/fixtures/test-app/app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"private": true,
33
"name": "TestApp",
44
"version": "1.1.0",
5-
"description": "Test Application",
5+
"description": "Test Application (test quite \" #378)",
66
"author": "Foo Bar <foo@example.com>",
77
"license": "MIT",
88
"homepage": "http://foo.example.com",

test/src/BuildTest.ts

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import test from "./helpers/avaEx"
22
import { assertPack, modifyPackageJson, outDirName } from "./helpers/packTester"
3+
import { expectedWinContents } from "./helpers/expectedContents"
34
import { move, outputFile, outputJson } from "fs-extra-p"
45
import { Promise as BluebirdPromise } from "bluebird"
56
import * as path from "path"
67
import { assertThat } from "./helpers/fileAssert"
78
import { Platform, PackagerOptions } from "out"
9+
import pathSorter = require("path-sort")
810

911
//noinspection JSUnusedLocalSymbols
1012
const __awaiter = require("out/awaiter")
@@ -112,7 +114,9 @@ test("copy extra resource", async () => {
112114
await assertPack("test-app", {
113115
platform: [platform],
114116
// to check NuGet package
115-
dist: platform === Platform.WINDOWS
117+
dist: platform === Platform.WINDOWS,
118+
cscLink: null,
119+
cscInstallerLink: null,
116120
}, {
117121
tempDirCreated: (projectDir) => {
118122
return BluebirdPromise.all([
@@ -146,6 +150,9 @@ test("copy extra resource", async () => {
146150
if (platform === Platform.OSX) {
147151
resourcesDir = path.join(resourcesDir, "TestApp.app", "Contents", "Resources")
148152
}
153+
else if (platform === Platform.WINDOWS) {
154+
resourcesDir = path.join(projectDir, outDirName, "win-unpacked", "lib", "net45")
155+
}
149156
await assertThat(path.join(resourcesDir, "foo")).isDirectory()
150157
await assertThat(path.join(resourcesDir, "foo", "nameWithoutDot")).isFile()
151158
await assertThat(path.join(resourcesDir, "bar", "hello.txt")).isFile()
@@ -154,39 +161,14 @@ test("copy extra resource", async () => {
154161
await assertThat(path.join(resourcesDir, "platformSpecific")).isFile()
155162
await assertThat(path.join(resourcesDir, "ignoreMe.txt")).doesNotExist()
156163
},
157-
expectedContents: platform === Platform.WINDOWS ? [
158-
"lib/net45/content_resources_200_percent.pak",
159-
"lib/net45/content_shell.pak",
160-
"lib/net45/d3dcompiler_47.dll",
161-
"lib/net45/ffmpeg.dll",
162-
"lib/net45/icudtl.dat",
163-
"lib/net45/libEGL.dll",
164-
"lib/net45/libGLESv2.dll",
165-
"lib/net45/LICENSE",
166-
"lib/net45/LICENSES.chromium.html",
167-
"lib/net45/msvcp120.dll",
168-
"lib/net45/msvcr120.dll",
169-
"lib/net45/natives_blob.bin",
170-
"lib/net45/node.dll",
171-
"lib/net45/platformSpecific",
172-
"lib/net45/snapshot_blob.bin",
173-
"lib/net45/TestApp.exe",
174-
"lib/net45/ui_resources_200_percent.pak",
175-
"lib/net45/Update.exe",
176-
"lib/net45/vccorlib120.dll",
177-
"lib/net45/version",
178-
"lib/net45/xinput1_3.dll",
164+
expectedContents: platform === Platform.WINDOWS ? pathSorter(expectedWinContents.concat(
179165
"lib/net45/bar/hello.txt",
180166
"lib/net45/bar/x64.txt",
181167
"lib/net45/foo/nameWithoutDot",
182-
"lib/net45/locales/en-US.pak",
183-
"lib/net45/resources/app.asar",
184-
"lib/net45/resources/electron.asar",
185-
"lib/net45/win/x64.txt",
186-
"TestApp.nuspec",
187-
"[Content_Types].xml",
188-
"_rels/.rels"
189-
] : null,
168+
"lib/net45/platformSpecific",
169+
"lib/net45/win/",
170+
"lib/net45/win/x64.txt"
171+
)) : null,
190172
})
191173
}
192174
})

test/src/helpers/packTester.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ async function checkLinuxResult(projectDir: string, packager: Packager, packager
144144
Maintainer: "Foo Bar <foo@example.com>",
145145
Vendor: "Foo Bar <foo@example.com>",
146146
Package: "testapp",
147-
Description: " \n Test Application",
147+
Description: " \n Test Application (test quite \" #378)",
148148
})
149149
}
150150

@@ -206,7 +206,7 @@ async function checkWindowsResult(packager: Packager, packagerOptions: PackagerO
206206
function getWinExpected(archSuffix: string) {
207207
return [
208208
`RELEASES${archSuffix}`,
209-
`${productName}Setup-1.1.0${archSuffix}.exe`,
209+
`${productName} Setup 1.1.0${archSuffix}.exe`,
210210
`TestApp-1.1.0${archSuffix}-full.nupkg`,
211211
]
212212
}
@@ -225,7 +225,7 @@ async function checkWindowsResult(packager: Packager, packagerOptions: PackagerO
225225
if (archSuffix == "") {
226226
const expectedArtifactNames = expected.slice()
227227
expectedArtifactNames[1] = `TestAppSetup-1.1.0${archSuffix}.exe`
228-
assertThat(artifacts.map(it => it.artifactName).filter(it => it != null)).deepEqual([`TestAppSetup-1.1.0${archSuffix}.exe`])
228+
assertThat(artifacts.map(it => it.artifactName).filter(it => it != null)).deepEqual([`TestApp-Setup-1.1.0${archSuffix}.exe`])
229229
}
230230

231231
const packageFile = path.join(path.dirname(artifacts[0].file), `TestApp-1.1.0${archSuffix}-full.nupkg`)
@@ -238,7 +238,7 @@ async function checkWindowsResult(packager: Packager, packagerOptions: PackagerO
238238
const expectedContents = checkOptions == null || checkOptions.expectedContents == null ? expectedWinContents : checkOptions.expectedContents
239239
assertThat(files).deepEqual(expectedContents.map(it => {
240240
if (it === "lib/net45/TestApp.exe") {
241-
return `lib/net45/${productName.replace(/ /g, "%20")}.exe`
241+
return `lib/net45/${productName}.exe`
242242
}
243243
else {
244244
return it
@@ -260,7 +260,7 @@ async function checkWindowsResult(packager: Packager, packagerOptions: PackagerO
260260
<owners>Foo Bar</owners>
261261
<iconUrl>https://raw.githubusercontent.com/szwacz/electron-boilerplate/master/resources/windows/icon.ico</iconUrl>
262262
<requireLicenseAcceptance>false</requireLicenseAcceptance>
263-
<description>Test Application</description>
263+
<description>Test Application (test quite \" #378)</description>
264264
<copyright>Copyright © ${new Date().getFullYear()} Foo Bar</copyright>
265265
<projectUrl>http://foo.example.com</projectUrl>
266266
</metadata>

0 commit comments

Comments
 (0)