-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
AppImageUpdater.ts
118 lines (104 loc) · 4.35 KB
/
AppImageUpdater.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import { AllPublishOptions, newError } from "builder-util-runtime"
import { execFileSync, spawn } from "child_process"
import { chmod } from "fs-extra"
import { unlinkSync } from "fs"
import * as path from "path"
import { DownloadUpdateOptions } from "./AppUpdater"
import { BaseUpdater, InstallOptions } from "./BaseUpdater"
import { DifferentialDownloaderOptions } from "./differentialDownloader/DifferentialDownloader"
import { FileWithEmbeddedBlockMapDifferentialDownloader } from "./differentialDownloader/FileWithEmbeddedBlockMapDifferentialDownloader"
import { DOWNLOAD_PROGRESS } from "./main"
import { findFile } from "./providers/Provider"
export class AppImageUpdater extends BaseUpdater {
constructor(options?: AllPublishOptions | null, app?: any) {
super(options, app)
}
public isUpdaterActive(): boolean {
if (process.env["APPIMAGE"] == null) {
if (process.env["SNAP"] == null) {
this._logger.warn("APPIMAGE env is not defined, current application is not an AppImage")
} else {
this._logger.info("SNAP env is defined, updater is disabled")
}
return false
}
return super.isUpdaterActive()
}
/*** @private */
protected doDownloadUpdate(downloadUpdateOptions: DownloadUpdateOptions): Promise<Array<string>> {
const provider = downloadUpdateOptions.updateInfoAndProvider.provider
const fileInfo = findFile(provider.resolveFiles(downloadUpdateOptions.updateInfoAndProvider.info), "AppImage")!
return this.executeDownload({
fileExtension: "AppImage",
fileInfo,
downloadUpdateOptions,
task: async (updateFile, downloadOptions) => {
const oldFile = process.env["APPIMAGE"]!
if (oldFile == null) {
throw newError("APPIMAGE env is not defined", "ERR_UPDATER_OLD_FILE_NOT_FOUND")
}
let isDownloadFull = false
try {
const downloadOptions: DifferentialDownloaderOptions = {
newUrl: fileInfo.url,
oldFile,
logger: this._logger,
newFile: updateFile,
isUseMultipleRangeRequest: provider.isUseMultipleRangeRequest,
requestHeaders: downloadUpdateOptions.requestHeaders,
cancellationToken: downloadUpdateOptions.cancellationToken,
}
if (this.listenerCount(DOWNLOAD_PROGRESS) > 0) {
downloadOptions.onProgress = it => this.emit(DOWNLOAD_PROGRESS, it)
}
await new FileWithEmbeddedBlockMapDifferentialDownloader(fileInfo.info, this.httpExecutor, downloadOptions).download()
} catch (e) {
this._logger.error(`Cannot download differentially, fallback to full download: ${e.stack || e}`)
// during test (developer machine mac) we must throw error
isDownloadFull = process.platform === "linux"
}
if (isDownloadFull) {
await this.httpExecutor.download(fileInfo.url, updateFile, downloadOptions)
}
await chmod(updateFile, 0o755)
},
})
}
protected doInstall(options: InstallOptions): boolean {
const appImageFile = process.env["APPIMAGE"]!
if (appImageFile == null) {
throw newError("APPIMAGE env is not defined", "ERR_UPDATER_OLD_FILE_NOT_FOUND")
}
// https://stackoverflow.com/a/1712051/1910191
unlinkSync(appImageFile)
let destination: string
const existingBaseName = path.basename(appImageFile)
// https://github.com/electron-userland/electron-builder/issues/2964
// if no version in existing file name, it means that user wants to preserve current custom name
if (path.basename(options.installerPath) === existingBaseName || !/\d+\.\d+\.\d+/.test(existingBaseName)) {
// no version in the file name, overwrite existing
destination = appImageFile
} else {
destination = path.join(path.dirname(appImageFile), path.basename(options.installerPath))
}
execFileSync("mv", ["-f", options.installerPath, destination])
if (destination !== appImageFile) {
this.emit("appimage-filename-updated", destination)
}
const env: any = {
...process.env,
APPIMAGE_SILENT_INSTALL: "true",
}
if (options.isForceRunAfter) {
spawn(destination, [], {
detached: true,
stdio: "ignore",
env,
}).unref()
} else {
env.APPIMAGE_EXIT_AFTER_INSTALL = "true"
execFileSync(destination, [], { env })
}
return true
}
}