electron-updator 0.1.0 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +6 -7
- package/build/app-updator.js +246 -0
- package/build/common/constants.js +96 -0
- package/build/elelctron-app-adapter.js +33 -0
- package/build/index.js +10 -0
- package/build/libs/installer.exe +0 -0
- package/build/libs/unzip.exe +0 -0
- package/build/mac-updator.js +108 -0
- package/build/utils/download-file.js +74 -0
- package/build/utils/index.js +122 -0
- package/build/utils/install-macos-dmg.js +151 -0
- package/build/utils/sudo-prompt-exec.js +23 -0
- package/build/windows-updator.js +87 -0
- package/package.json +51 -21
- package/bin/electron-updator.js +0 -3
- package/index.js +0 -3
- package/lib/electron-updator.js +0 -1
package/README.md
CHANGED
@@ -8,21 +8,20 @@
|
|
8
8
|
|
9
9
|
[npm-image]: https://img.shields.io/npm/v/electron-updator.svg
|
10
10
|
[npm-url]: https://npmjs.org/package/electron-updator
|
11
|
-
[ci-image]: https://github.com/
|
12
|
-
[ci-url]: https://github.com/
|
13
|
-
[codecov-image]: https://img.shields.io/codecov/c/github/
|
14
|
-
[codecov-url]: https://codecov.io/gh/
|
11
|
+
[ci-image]: https://github.com/electron-modules/electron-updator/actions/workflows/ci.yml/badge.svg
|
12
|
+
[ci-url]: https://github.com/electron-modules/electron-updator/actions/workflows/ci.yml
|
13
|
+
[codecov-image]: https://img.shields.io/codecov/c/github/electron-modules/electron-updator.svg?logo=codecov
|
14
|
+
[codecov-url]: https://codecov.io/gh/electron-modules/electron-updator
|
15
15
|
[node-image]: https://img.shields.io/badge/node.js-%3E=_16-green.svg
|
16
16
|
[node-url]: http://nodejs.org/download/
|
17
17
|
[download-image]: https://img.shields.io/npm/dm/electron-updator.svg
|
18
18
|
[download-url]: https://npmjs.org/package/electron-updator
|
19
19
|
|
20
|
-
> electron
|
21
|
-
|
20
|
+
> electron updator
|
22
21
|
## Installment
|
23
22
|
|
24
23
|
```bash
|
25
|
-
$ npm i electron-updator --save
|
24
|
+
$ npm i electron-updator --save
|
26
25
|
```
|
27
26
|
|
28
27
|
## License
|
@@ -0,0 +1,246 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.AppUpdator = void 0;
|
4
|
+
const eventemitter3_1 = require("eventemitter3");
|
5
|
+
const constants_1 = require("./common/constants");
|
6
|
+
const download_file_1 = require("./utils/download-file");
|
7
|
+
const utils_1 = require("./utils");
|
8
|
+
const elelctron_app_adapter_1 = require("./elelctron-app-adapter");
|
9
|
+
class AppUpdator extends eventemitter3_1.EventEmitter {
|
10
|
+
constructor(options, app) {
|
11
|
+
super();
|
12
|
+
this.state = "idle" /* StateType.Idle */;
|
13
|
+
this.options = options;
|
14
|
+
this.logger = this._wrapLogger(options.logger);
|
15
|
+
this.app = app || new elelctron_app_adapter_1.ElectronAppAdapter();
|
16
|
+
this.startTimeStamp = new Date().getTime();
|
17
|
+
this.logger.info('AppUpdator#constructor');
|
18
|
+
this.availableUpdate = {
|
19
|
+
resourcePath: '',
|
20
|
+
latestAsarPath: '',
|
21
|
+
downloadTargetDir: '',
|
22
|
+
};
|
23
|
+
}
|
24
|
+
_wrapLogger(logger) {
|
25
|
+
if (!logger) {
|
26
|
+
logger = console;
|
27
|
+
}
|
28
|
+
const _logger = { ...logger };
|
29
|
+
const _wrap = (message, callback) => {
|
30
|
+
callback(`ElectronUpdator(${this.startTimeStamp})${message}`);
|
31
|
+
};
|
32
|
+
_logger.error = (message) => {
|
33
|
+
_wrap(message, logger.error);
|
34
|
+
};
|
35
|
+
_logger.info = (message) => {
|
36
|
+
_wrap(message, logger.info);
|
37
|
+
};
|
38
|
+
_logger.warn = (message) => {
|
39
|
+
_wrap(message, logger.warn);
|
40
|
+
};
|
41
|
+
return _logger;
|
42
|
+
}
|
43
|
+
setState(state) {
|
44
|
+
this.logger.info(`AppUpdator#setState${state}`);
|
45
|
+
this.state = state;
|
46
|
+
}
|
47
|
+
setFeedUrl(url) {
|
48
|
+
this.logger.info(`AppUpdator#setFeedUrl:url is ${url}`);
|
49
|
+
if (url && this.options) {
|
50
|
+
this.options.url = url;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
async checkForUpdates(executeType = constants_1.ExecuteType.Auto) {
|
54
|
+
this.logger.info(`AppUpdator#checkForUpdates:state is ${this.state}`);
|
55
|
+
if (executeType === constants_1.ExecuteType.User) {
|
56
|
+
if (this.state === "downloaded" /* StateType.Downloaded */) {
|
57
|
+
this.logger.info(`AppUpdator#checkForUpdates:UPDATE_DOWNLOADED`);
|
58
|
+
this.emit(constants_1.EventType.UPDATE_DOWNLOADED, {
|
59
|
+
executeType,
|
60
|
+
});
|
61
|
+
}
|
62
|
+
else {
|
63
|
+
this.logger.info(`AppUpdator#checkForUpdates:UPDATE_NOT_AVAILABLE`);
|
64
|
+
this.emit(constants_1.EventType.UPDATE_NOT_AVAILABLE, { updateInfo: this.updateInfo });
|
65
|
+
}
|
66
|
+
return;
|
67
|
+
}
|
68
|
+
this.setState("idle" /* StateType.Idle */);
|
69
|
+
try {
|
70
|
+
// 新一轮更新流程,更新 TimeStamp
|
71
|
+
this.startTimeStamp = new Date().getTime();
|
72
|
+
this.setState("checking-for-update" /* StateType.CheckingForUpdate */);
|
73
|
+
this.emit(constants_1.EventType.CHECKING_FOR_UPDATE);
|
74
|
+
const updateInfoResponse = await (0, utils_1.requestUpdateInfo)(this.options);
|
75
|
+
this.updateInfo = (this.options?.responseFormatter ? this.options?.responseFormatter(updateInfoResponse) : updateInfoResponse);
|
76
|
+
const needUpdate = this.options?.needUpdate(updateInfoResponse);
|
77
|
+
if (!needUpdate) {
|
78
|
+
this.logger.info(`updateInfo is ${JSON.stringify(this.updateInfo)},needUpdate is false`);
|
79
|
+
this.emit(constants_1.EventType.UPDATE_NOT_AVAILABLE, { updateInfo: this.updateInfo });
|
80
|
+
this.setState("idle" /* StateType.Idle */);
|
81
|
+
return;
|
82
|
+
}
|
83
|
+
this.logger.info(`AppUpdator#checkForUpdates:needUpdate is true`);
|
84
|
+
this.availableUpdate = this.doGetAvailableUpdateInfo(this.updateInfo);
|
85
|
+
if (!this.options?.autoDownload) {
|
86
|
+
this.logger.info(`AppUpdator#checkForUpdates:emit UPDATE_AVAILABLE`);
|
87
|
+
this.emit(constants_1.EventType.UPDATE_AVAILABLE, {
|
88
|
+
executeType,
|
89
|
+
updateInfo: this.updateInfo,
|
90
|
+
});
|
91
|
+
return;
|
92
|
+
}
|
93
|
+
this.downloadUpdate(executeType);
|
94
|
+
}
|
95
|
+
catch (e) {
|
96
|
+
e.customMessage = e.customMessage ? e.customMessage : `${constants_1.InstallResultType.CheckForUpdatesError}_${e.message}`;
|
97
|
+
this.logError(e);
|
98
|
+
this.setState("idle" /* StateType.Idle */);
|
99
|
+
}
|
100
|
+
}
|
101
|
+
async downloadUpdate(executeType = constants_1.ExecuteType.User) {
|
102
|
+
this.logger.info(`AppUpdator#downloadUpdate:executeType is ${executeType}`);
|
103
|
+
await this.downloadUpdateFile(this.updateInfo);
|
104
|
+
const result = await this.preCheck();
|
105
|
+
if (result.success) {
|
106
|
+
this.logger.info(`AppUpdator#downloadUpdate:emit UPDATE_DOWNLOADED`);
|
107
|
+
this.emit(constants_1.EventType.UPDATE_DOWNLOADED, {
|
108
|
+
executeType,
|
109
|
+
});
|
110
|
+
}
|
111
|
+
else {
|
112
|
+
this.logError(result.error);
|
113
|
+
this.setState("idle" /* StateType.Idle */);
|
114
|
+
}
|
115
|
+
}
|
116
|
+
async quitAndInstall() {
|
117
|
+
this.logger.info(`AppUpdator#quitAndInstall:state is ${this.state}`);
|
118
|
+
if (this.state !== "downloaded" /* StateType.Downloaded */) {
|
119
|
+
return;
|
120
|
+
}
|
121
|
+
this.setState("idle" /* StateType.Idle */);
|
122
|
+
try {
|
123
|
+
let result = { success: false };
|
124
|
+
if (this.updateInfo?.updateType === constants_1.UpdateType.Package) {
|
125
|
+
result = await this.doQuitAndInstallPackage();
|
126
|
+
}
|
127
|
+
else {
|
128
|
+
result = await this.doQuitAndInstallAsar();
|
129
|
+
}
|
130
|
+
if (result.success) {
|
131
|
+
this.logger.warn(`AppUpdator#quitAndInstall:install success`);
|
132
|
+
this.emit(constants_1.EventType.BEFORE_QUIT_FOR_UPDATE);
|
133
|
+
}
|
134
|
+
else {
|
135
|
+
result.message = `error: ${result.error?.message}`;
|
136
|
+
this.dispatchError(result.error);
|
137
|
+
}
|
138
|
+
}
|
139
|
+
catch (e) {
|
140
|
+
this.dispatchError(e);
|
141
|
+
}
|
142
|
+
}
|
143
|
+
async preCheckForAsar() {
|
144
|
+
this.logger.info(`AppUpdator#preCheckForAsar`);
|
145
|
+
return await this.unzip();
|
146
|
+
}
|
147
|
+
async preCheck() {
|
148
|
+
this.logger.info(`AppUpdator#preCheck`);
|
149
|
+
const { resourcePath } = this.availableUpdate;
|
150
|
+
if (this.state !== "downloaded" /* StateType.Downloaded */) {
|
151
|
+
return {
|
152
|
+
success: false,
|
153
|
+
error: new Error(`AppUpdator#preCheck:update status(${this.state}) error`),
|
154
|
+
};
|
155
|
+
}
|
156
|
+
// 清理老包
|
157
|
+
try {
|
158
|
+
this.logger.info(`AppUpdator#preCheck:cleanOldArchive`);
|
159
|
+
await (0, utils_1.cleanOldArchive)(resourcePath);
|
160
|
+
}
|
161
|
+
catch (e) {
|
162
|
+
this.logError(e);
|
163
|
+
}
|
164
|
+
let result = { success: true };
|
165
|
+
const { downloadTargetDir } = this.availableUpdate;
|
166
|
+
try {
|
167
|
+
const hasLatestFile = await (0, utils_1.existsAsync)(downloadTargetDir);
|
168
|
+
// 下载失败返回提示
|
169
|
+
if (!hasLatestFile) {
|
170
|
+
return {
|
171
|
+
success: false,
|
172
|
+
error: new Error('file is notfound'),
|
173
|
+
};
|
174
|
+
}
|
175
|
+
}
|
176
|
+
catch (e) {
|
177
|
+
return {
|
178
|
+
success: false,
|
179
|
+
error: e,
|
180
|
+
};
|
181
|
+
}
|
182
|
+
if (this.updateInfo?.updateType === constants_1.UpdateType.Package) {
|
183
|
+
result = await this.doPreCheckForPackage();
|
184
|
+
}
|
185
|
+
else {
|
186
|
+
result = await this.preCheckForAsar();
|
187
|
+
}
|
188
|
+
return result;
|
189
|
+
}
|
190
|
+
async downloadUpdateFile(updateInfo) {
|
191
|
+
if (this.state !== "checking-for-update" /* StateType.CheckingForUpdate */) {
|
192
|
+
throw new Error(`AppUpdator#downloadUpdateFile:update status(${this.state}) error`);
|
193
|
+
}
|
194
|
+
const { url, signature } = updateInfo.files[0];
|
195
|
+
const { downloadTargetDir } = this.availableUpdate;
|
196
|
+
this.setState("downloading" /* StateType.Downloading */);
|
197
|
+
try {
|
198
|
+
await (0, download_file_1.downloadFile)({
|
199
|
+
logger: this.logger,
|
200
|
+
url,
|
201
|
+
signature,
|
202
|
+
targetDir: downloadTargetDir,
|
203
|
+
emit: this.emit,
|
204
|
+
progressHandle: (data) => {
|
205
|
+
this.emit(constants_1.EventType.UPDATE_DOWNLOAD_PROGRESS, data);
|
206
|
+
},
|
207
|
+
});
|
208
|
+
this.logger.info(`AppUpdator#downloadUpdateFile:Downloaded`);
|
209
|
+
this.setState("downloaded" /* StateType.Downloaded */);
|
210
|
+
}
|
211
|
+
catch (e) {
|
212
|
+
this.setState("idle" /* StateType.Idle */);
|
213
|
+
e.customMessage = `${constants_1.InstallResultType.DownloadError}_${e.message}`;
|
214
|
+
this.logError(e);
|
215
|
+
}
|
216
|
+
}
|
217
|
+
async unzip() {
|
218
|
+
this.logger.info(`AppUpdator#unzip:start`);
|
219
|
+
try {
|
220
|
+
const result = await this.doUnzip();
|
221
|
+
if (!result.success) {
|
222
|
+
return result;
|
223
|
+
}
|
224
|
+
const { latestAsarPath } = this.availableUpdate;
|
225
|
+
const exist = await (0, utils_1.existFile)(latestAsarPath);
|
226
|
+
return Promise.resolve({ success: exist });
|
227
|
+
}
|
228
|
+
catch (error) {
|
229
|
+
error.customMessage = constants_1.InstallResultType.UpdateUnzipError;
|
230
|
+
return Promise.resolve({
|
231
|
+
success: false,
|
232
|
+
error,
|
233
|
+
});
|
234
|
+
}
|
235
|
+
}
|
236
|
+
dispatchError(e) {
|
237
|
+
this.logError(e);
|
238
|
+
this.emit(constants_1.EventType.ERROR, e.message);
|
239
|
+
}
|
240
|
+
logError(e) {
|
241
|
+
const message = (e.stack || e).toString();
|
242
|
+
this.logger.error(message);
|
243
|
+
}
|
244
|
+
}
|
245
|
+
exports.AppUpdator = AppUpdator;
|
246
|
+
//# sourceMappingURL=app-updator.js.map
|
@@ -0,0 +1,96 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.EventType = exports.DownloadProgressStatus = exports.ExecuteType = exports.UpdateType = exports.InstallResultType = exports.OldArchivePrefix = void 0;
|
4
|
+
/**
|
5
|
+
* 更新包备份前缀
|
6
|
+
*/
|
7
|
+
exports.OldArchivePrefix = 'old-';
|
8
|
+
/**
|
9
|
+
* 安装结果类型
|
10
|
+
*/
|
11
|
+
var InstallResultType;
|
12
|
+
(function (InstallResultType) {
|
13
|
+
InstallResultType["UpdateUnzipError"] = "update-unzip-error";
|
14
|
+
InstallResultType["DownloadError"] = "download-error";
|
15
|
+
InstallResultType["UpdateSuccess"] = "update-success";
|
16
|
+
InstallResultType["CheckForUpdatesError"] = "check-for-updates-error";
|
17
|
+
})(InstallResultType = exports.InstallResultType || (exports.InstallResultType = {}));
|
18
|
+
/**
|
19
|
+
* 更新类型
|
20
|
+
*/
|
21
|
+
var UpdateType;
|
22
|
+
(function (UpdateType) {
|
23
|
+
/** 全量更新 */
|
24
|
+
UpdateType["Package"] = "package";
|
25
|
+
/** 动态更新 */
|
26
|
+
UpdateType["Asar"] = "asar";
|
27
|
+
/** 更新包更新 */
|
28
|
+
UpdateType["Assets"] = "assets";
|
29
|
+
})(UpdateType = exports.UpdateType || (exports.UpdateType = {}));
|
30
|
+
/**
|
31
|
+
* 执行类型
|
32
|
+
*/
|
33
|
+
var ExecuteType;
|
34
|
+
(function (ExecuteType) {
|
35
|
+
/**
|
36
|
+
* 自动触发
|
37
|
+
*/
|
38
|
+
ExecuteType["User"] = "user";
|
39
|
+
/**
|
40
|
+
* 手动触发
|
41
|
+
*/
|
42
|
+
ExecuteType["Auto"] = "auto";
|
43
|
+
})(ExecuteType = exports.ExecuteType || (exports.ExecuteType = {}));
|
44
|
+
/**
|
45
|
+
* 执行类型
|
46
|
+
*/
|
47
|
+
var DownloadProgressStatus;
|
48
|
+
(function (DownloadProgressStatus) {
|
49
|
+
/**
|
50
|
+
* 开始
|
51
|
+
*/
|
52
|
+
DownloadProgressStatus["Begin"] = "begin";
|
53
|
+
/**
|
54
|
+
* 下载中
|
55
|
+
*/
|
56
|
+
DownloadProgressStatus["Downloading"] = "downloading";
|
57
|
+
/**
|
58
|
+
* 结束
|
59
|
+
*/
|
60
|
+
DownloadProgressStatus["End"] = "end";
|
61
|
+
})(DownloadProgressStatus = exports.DownloadProgressStatus || (exports.DownloadProgressStatus = {}));
|
62
|
+
/**
|
63
|
+
* 事件类型
|
64
|
+
*/
|
65
|
+
var EventType;
|
66
|
+
(function (EventType) {
|
67
|
+
/**
|
68
|
+
* 下载完成
|
69
|
+
*/
|
70
|
+
EventType["UPDATE_DOWNLOADED"] = "update-downloaded";
|
71
|
+
/**
|
72
|
+
* 下载进度
|
73
|
+
*/
|
74
|
+
EventType["UPDATE_DOWNLOAD_PROGRESS"] = "update-download-progress";
|
75
|
+
/**
|
76
|
+
* 用户调用 quitAndInstall 之后发出,可在次事件后完成关闭窗口、重启等操作
|
77
|
+
*/
|
78
|
+
EventType["BEFORE_QUIT_FOR_UPDATE"] = "before-quit-for-update";
|
79
|
+
/**
|
80
|
+
* 有可用更新
|
81
|
+
*/
|
82
|
+
EventType["UPDATE_AVAILABLE"] = "update-available";
|
83
|
+
/**
|
84
|
+
* 开始检查更新
|
85
|
+
*/
|
86
|
+
EventType["CHECKING_FOR_UPDATE"] = "checking-for-update";
|
87
|
+
/**
|
88
|
+
* 无可用更新
|
89
|
+
*/
|
90
|
+
EventType["UPDATE_NOT_AVAILABLE"] = "update-not-available";
|
91
|
+
/**
|
92
|
+
* 错误
|
93
|
+
*/
|
94
|
+
EventType["ERROR"] = "error";
|
95
|
+
})(EventType = exports.EventType || (exports.EventType = {}));
|
96
|
+
//# sourceMappingURL=constants.js.map
|
@@ -0,0 +1,33 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.ElectronAppAdapter = void 0;
|
4
|
+
const electron_1 = require("electron");
|
5
|
+
class ElectronAppAdapter {
|
6
|
+
constructor(app = electron_1.app) {
|
7
|
+
this.app = app;
|
8
|
+
}
|
9
|
+
get name() {
|
10
|
+
return this.app.getName();
|
11
|
+
}
|
12
|
+
get isPackaged() {
|
13
|
+
return this.app.isPackaged === true;
|
14
|
+
}
|
15
|
+
get userDataPath() {
|
16
|
+
return this.app.getPath('userData');
|
17
|
+
}
|
18
|
+
get exePath() {
|
19
|
+
return this.app.getPath('exe');
|
20
|
+
}
|
21
|
+
exit(code) {
|
22
|
+
this.app.exit(code);
|
23
|
+
}
|
24
|
+
relaunch(code) {
|
25
|
+
this.app.relaunch();
|
26
|
+
this.app.exit(code);
|
27
|
+
}
|
28
|
+
quit() {
|
29
|
+
this.app.quit();
|
30
|
+
}
|
31
|
+
}
|
32
|
+
exports.ElectronAppAdapter = ElectronAppAdapter;
|
33
|
+
//# sourceMappingURL=elelctron-app-adapter.js.map
|
package/build/index.js
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
const constants_1 = require("./common/constants");
|
4
|
+
const mac_updator_1 = require("./mac-updator");
|
5
|
+
const windows_updator_1 = require("./windows-updator");
|
6
|
+
exports.default = {
|
7
|
+
UpdateType: constants_1.UpdateType, EventType: constants_1.EventType, ExecuteType: constants_1.ExecuteType,
|
8
|
+
MacUpdator: mac_updator_1.MacUpdator, WindowsUpdator: windows_updator_1.WindowsUpdator, DownloadProgressStatus: constants_1.DownloadProgressStatus,
|
9
|
+
};
|
10
|
+
//# sourceMappingURL=index.js.map
|
Binary file
|
Binary file
|
@@ -0,0 +1,108 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.MacUpdator = void 0;
|
7
|
+
const path_1 = __importDefault(require("path"));
|
8
|
+
const app_updator_1 = require("./app-updator");
|
9
|
+
const constants_1 = require("./common/constants");
|
10
|
+
const install_macos_dmg_1 = __importDefault(require("./utils/install-macos-dmg"));
|
11
|
+
const utils_1 = require("./utils");
|
12
|
+
class MacUpdator extends app_updator_1.AppUpdator {
|
13
|
+
constructor(options) {
|
14
|
+
super(options);
|
15
|
+
}
|
16
|
+
doGetAvailableUpdateInfo(updateInfo) {
|
17
|
+
this.logger.info('MacUpdator#doGetAvailableUpdateInfo:start');
|
18
|
+
const exePath = this.app.exePath;
|
19
|
+
const resourcePath = path_1.default.resolve(exePath, '..', '..', 'Resources');
|
20
|
+
const latestAsarPath = path_1.default.resolve(resourcePath, 'latest.asar');
|
21
|
+
const latestAppPath = path_1.default.resolve(resourcePath, 'latest');
|
22
|
+
let downloadTargetDir = `${latestAsarPath}.zip`;
|
23
|
+
if (updateInfo.updateType === constants_1.UpdateType.Package) {
|
24
|
+
downloadTargetDir = `${latestAppPath}.dmg`;
|
25
|
+
}
|
26
|
+
return {
|
27
|
+
resourcePath,
|
28
|
+
downloadTargetDir,
|
29
|
+
latestAsarPath,
|
30
|
+
};
|
31
|
+
}
|
32
|
+
async doPreCheckForPackage() {
|
33
|
+
this.logger.info('MacUpdator#doPreCheckForPackage:start');
|
34
|
+
// Mac 全量安装前,先进行 dmg 安装检查
|
35
|
+
return await (0, install_macos_dmg_1.default)(this.options, this.logger, this.availableUpdate, this.updateInfo, true);
|
36
|
+
}
|
37
|
+
/**
|
38
|
+
* 资源解压
|
39
|
+
* @return
|
40
|
+
*/
|
41
|
+
async doUnzip() {
|
42
|
+
this.logger.info('MacUpdator#doUnzip:start');
|
43
|
+
const { resourcePath, downloadTargetDir } = this.availableUpdate;
|
44
|
+
try {
|
45
|
+
// 直接解压
|
46
|
+
await (0, utils_1.execAsync)(`unzip -o ${downloadTargetDir}`, {
|
47
|
+
cwd: resourcePath,
|
48
|
+
maxBuffer: 2 ** 28,
|
49
|
+
});
|
50
|
+
return {
|
51
|
+
success: true,
|
52
|
+
};
|
53
|
+
}
|
54
|
+
catch (error) {
|
55
|
+
return {
|
56
|
+
success: false,
|
57
|
+
error,
|
58
|
+
};
|
59
|
+
}
|
60
|
+
}
|
61
|
+
async doQuitAndInstallAsar() {
|
62
|
+
this.logger.info('MacUpdator#doQuitAndInstallAsar:start');
|
63
|
+
if (!this.availableUpdate) {
|
64
|
+
this.logger.error('MacUpdator#doQuitAndInstallAsar:not availableUpdate');
|
65
|
+
return Promise.resolve({ success: false });
|
66
|
+
}
|
67
|
+
const { resourcePath, latestAsarPath } = this.availableUpdate;
|
68
|
+
const oldAsarPath = path_1.default.resolve(resourcePath, `${constants_1.OldArchivePrefix}${new Date().getTime()}.asar`);
|
69
|
+
const currentAsarPath = path_1.default.resolve(resourcePath, 'app.asar');
|
70
|
+
try {
|
71
|
+
// 将老包改名 app.asar => old-xxxx.asar
|
72
|
+
if (await (0, utils_1.existsAsync)(currentAsarPath)) {
|
73
|
+
await (0, utils_1.renameAsync)(currentAsarPath, oldAsarPath);
|
74
|
+
}
|
75
|
+
}
|
76
|
+
catch (error) {
|
77
|
+
return {
|
78
|
+
success: false,
|
79
|
+
error,
|
80
|
+
};
|
81
|
+
}
|
82
|
+
try {
|
83
|
+
// 新包替换
|
84
|
+
await (0, utils_1.renameAsync)(latestAsarPath, currentAsarPath);
|
85
|
+
}
|
86
|
+
catch (error) {
|
87
|
+
// 替换出错,需要把老包还原
|
88
|
+
await (0, utils_1.renameAsync)(oldAsarPath, currentAsarPath);
|
89
|
+
return {
|
90
|
+
success: false,
|
91
|
+
error,
|
92
|
+
};
|
93
|
+
}
|
94
|
+
this.logger.warn('AppUpdator#quitAndInstall:install success');
|
95
|
+
this.emit(constants_1.EventType.BEFORE_QUIT_FOR_UPDATE);
|
96
|
+
// 重启应用
|
97
|
+
this.app.relaunch();
|
98
|
+
return {
|
99
|
+
success: true,
|
100
|
+
};
|
101
|
+
}
|
102
|
+
async doQuitAndInstallPackage() {
|
103
|
+
this.logger.info('AppUpdator#doQuitAndInstallPackage:start');
|
104
|
+
return await (0, install_macos_dmg_1.default)(this.options, this.logger, this.availableUpdate, this.updateInfo);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
exports.MacUpdator = MacUpdator;
|
108
|
+
//# sourceMappingURL=mac-updator.js.map
|
@@ -0,0 +1,74 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.downloadFile = void 0;
|
7
|
+
const urllib_1 = __importDefault(require("urllib"));
|
8
|
+
const constants_1 = require("../common/constants");
|
9
|
+
const _1 = require(".");
|
10
|
+
/**
|
11
|
+
* 文件下载
|
12
|
+
* @param updator 更新实例
|
13
|
+
* @param url 下载地址
|
14
|
+
* @param signature 下载签名
|
15
|
+
* @param targeDir 下载目录
|
16
|
+
* @param progressHandle 下载状态回调
|
17
|
+
* @return
|
18
|
+
*/
|
19
|
+
const downloadFile = async ({ logger, url, signature, targetDir, progressHandle }) => {
|
20
|
+
logger.info('downloadFile#downloadFile (start)');
|
21
|
+
const writeStream = (0, _1.createWriteStream)(targetDir);
|
22
|
+
let currentLength = 0;
|
23
|
+
progressHandle({
|
24
|
+
status: constants_1.DownloadProgressStatus.Begin,
|
25
|
+
});
|
26
|
+
return new Promise((resolve, reject) => {
|
27
|
+
urllib_1.default
|
28
|
+
.request(url, {
|
29
|
+
streaming: true,
|
30
|
+
followRedirect: true,
|
31
|
+
timeout: 10 * 60 * 1000,
|
32
|
+
})
|
33
|
+
.then((res) => {
|
34
|
+
const totalLength = res.headers['content-length'];
|
35
|
+
logger.info(`downloadFile#downloadFile (then),totalLength is ${totalLength}`);
|
36
|
+
res.res.on('data', (data) => {
|
37
|
+
try {
|
38
|
+
currentLength += data.length;
|
39
|
+
const progress = (currentLength / totalLength) * 100;
|
40
|
+
progressHandle({
|
41
|
+
status: constants_1.DownloadProgressStatus.Downloading,
|
42
|
+
progress,
|
43
|
+
url,
|
44
|
+
signature,
|
45
|
+
data,
|
46
|
+
});
|
47
|
+
writeStream.write(data);
|
48
|
+
}
|
49
|
+
catch (e) {
|
50
|
+
reject(e);
|
51
|
+
}
|
52
|
+
});
|
53
|
+
res.res.on('end', () => {
|
54
|
+
// 推迟调用 end(): https://stackoverflow.com/a/53878933
|
55
|
+
process.nextTick(() => writeStream.end());
|
56
|
+
try {
|
57
|
+
progressHandle({
|
58
|
+
status: constants_1.DownloadProgressStatus.End,
|
59
|
+
url,
|
60
|
+
signature,
|
61
|
+
});
|
62
|
+
logger.info('download file success, url:%s, to %s', url, targetDir);
|
63
|
+
resolve();
|
64
|
+
}
|
65
|
+
catch (e) {
|
66
|
+
reject(e);
|
67
|
+
}
|
68
|
+
});
|
69
|
+
res.res.on('error', reject);
|
70
|
+
});
|
71
|
+
});
|
72
|
+
};
|
73
|
+
exports.downloadFile = downloadFile;
|
74
|
+
//# sourceMappingURL=download-file.js.map
|
@@ -0,0 +1,122 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.getExecuteFile = exports.requestUpdateInfo = exports.existFile = exports.cleanOldArchive = exports.spawnAsync = exports.waitUntil = exports.sleep = exports.getMacOSAppPath = exports.sudoPrompt = exports.existsSync = exports.createWriteStream = exports.isMac = exports.isWin = exports.execAsync = exports.rimrafAsync = exports.readdirAsync = exports.existsAsync = exports.renameAsync = void 0;
|
7
|
+
const path_1 = __importDefault(require("path"));
|
8
|
+
const child_process_1 = require("child_process");
|
9
|
+
const util_1 = __importDefault(require("util"));
|
10
|
+
const urllib_1 = __importDefault(require("urllib"));
|
11
|
+
const sudo_prompt_alt_1 = __importDefault(require("sudo-prompt-alt"));
|
12
|
+
exports.sudoPrompt = sudo_prompt_alt_1.default;
|
13
|
+
const rimraf_alt_1 = __importDefault(require("rimraf-alt"));
|
14
|
+
const original_fs_1 = require("original-fs");
|
15
|
+
Object.defineProperty(exports, "existsSync", { enumerable: true, get: function () { return original_fs_1.existsSync; } });
|
16
|
+
Object.defineProperty(exports, "createWriteStream", { enumerable: true, get: function () { return original_fs_1.createWriteStream; } });
|
17
|
+
const constants_1 = require("../common/constants");
|
18
|
+
exports.renameAsync = util_1.default.promisify(original_fs_1.rename);
|
19
|
+
exports.existsAsync = util_1.default.promisify(original_fs_1.exists);
|
20
|
+
exports.readdirAsync = util_1.default.promisify(original_fs_1.readdir);
|
21
|
+
exports.rimrafAsync = util_1.default.promisify(rimraf_alt_1.default);
|
22
|
+
exports.execAsync = util_1.default.promisify(child_process_1.exec);
|
23
|
+
exports.isWin = process.platform === 'win32';
|
24
|
+
exports.isMac = process.platform === 'darwin';
|
25
|
+
const getMacOSAppPath = () => {
|
26
|
+
const sep = path_1.default.sep;
|
27
|
+
const execPath = process.execPath;
|
28
|
+
const contentPath = ['', 'Contents', 'MacOS'].join(sep);
|
29
|
+
return execPath.split(contentPath)[0];
|
30
|
+
};
|
31
|
+
exports.getMacOSAppPath = getMacOSAppPath;
|
32
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
33
|
+
exports.sleep = sleep;
|
34
|
+
const waitUntil = async (handle, options = { retryTime: 10, ms: 1000 }) => {
|
35
|
+
let retryTime = 0;
|
36
|
+
const p = async () => {
|
37
|
+
const isOk = handle();
|
38
|
+
if (!isOk) {
|
39
|
+
if (retryTime === options.retryTime) {
|
40
|
+
return false;
|
41
|
+
}
|
42
|
+
retryTime++;
|
43
|
+
await (0, exports.sleep)(options.ms);
|
44
|
+
return await p();
|
45
|
+
}
|
46
|
+
return true;
|
47
|
+
};
|
48
|
+
return await p();
|
49
|
+
};
|
50
|
+
exports.waitUntil = waitUntil;
|
51
|
+
const spawnAsync = (command, options) => {
|
52
|
+
let stdout = '';
|
53
|
+
let stderr = '';
|
54
|
+
const child = (0, child_process_1.spawn)(command, options);
|
55
|
+
child.stdout.on('data', (data) => {
|
56
|
+
stdout += data;
|
57
|
+
});
|
58
|
+
child.stderr.on('data', (data) => {
|
59
|
+
stderr += data;
|
60
|
+
});
|
61
|
+
return new Promise((resolve, reject) => {
|
62
|
+
child.on('error', reject);
|
63
|
+
child.on('close', (code) => {
|
64
|
+
if (code === 0) {
|
65
|
+
return resolve(stdout.toString());
|
66
|
+
}
|
67
|
+
const err = new Error(`child exited with code ${code}`);
|
68
|
+
err.code = code;
|
69
|
+
err.stderr = stderr.toString();
|
70
|
+
reject(err);
|
71
|
+
});
|
72
|
+
});
|
73
|
+
};
|
74
|
+
exports.spawnAsync = spawnAsync;
|
75
|
+
const cleanOldArchive = async (resourcePath) => {
|
76
|
+
const resourceArray = await (0, exports.readdirAsync)(resourcePath);
|
77
|
+
if (!resourceArray || !resourceArray.length) {
|
78
|
+
return;
|
79
|
+
}
|
80
|
+
const oldArchiveSource = resourceArray.filter((item) => item.startsWith(constants_1.OldArchivePrefix));
|
81
|
+
return await Promise.all(oldArchiveSource.map(async (file) => {
|
82
|
+
const filePath = path_1.default.join(resourcePath, file);
|
83
|
+
if (await (0, exports.existsAsync)(filePath)) {
|
84
|
+
await (0, exports.rimrafAsync)(filePath);
|
85
|
+
}
|
86
|
+
}));
|
87
|
+
};
|
88
|
+
exports.cleanOldArchive = cleanOldArchive;
|
89
|
+
const existFile = async (path) => {
|
90
|
+
const _existFile = await (0, exports.waitUntil)(() => (0, original_fs_1.existsSync)(path), {
|
91
|
+
ms: 1000,
|
92
|
+
retryTime: 30,
|
93
|
+
});
|
94
|
+
return _existFile;
|
95
|
+
};
|
96
|
+
exports.existFile = existFile;
|
97
|
+
const requestUpdateInfo = async (options) => {
|
98
|
+
const { url } = options;
|
99
|
+
if (!url) {
|
100
|
+
throw new Error('request url can\'t be empty');
|
101
|
+
}
|
102
|
+
let res = null;
|
103
|
+
try {
|
104
|
+
res = await urllib_1.default.request(url, {
|
105
|
+
dataType: 'json',
|
106
|
+
timeout: 10 * 1000,
|
107
|
+
});
|
108
|
+
}
|
109
|
+
catch (e) {
|
110
|
+
throw e;
|
111
|
+
}
|
112
|
+
if (!res || res.status !== 200) {
|
113
|
+
throw new Error(`request failure,status is ${res.status}`);
|
114
|
+
}
|
115
|
+
return res.data;
|
116
|
+
};
|
117
|
+
exports.requestUpdateInfo = requestUpdateInfo;
|
118
|
+
const getExecuteFile = (file) => {
|
119
|
+
return path_1.default.join(__dirname, '..', 'libs', file);
|
120
|
+
};
|
121
|
+
exports.getExecuteFile = getExecuteFile;
|
122
|
+
//# sourceMappingURL=index.js.map
|
@@ -0,0 +1,151 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
const os_1 = __importDefault(require("os"));
|
7
|
+
const path_1 = __importDefault(require("path"));
|
8
|
+
const _1 = require(".");
|
9
|
+
const index_1 = require("./index");
|
10
|
+
const _log = (logger, e) => {
|
11
|
+
const message = (e.stack || e).toString();
|
12
|
+
logger.error?.(message);
|
13
|
+
};
|
14
|
+
exports.default = async (updatorOptions, logger, availableUpdate, updateInfo, preCheck = false) => {
|
15
|
+
const { productName } = updatorOptions || {};
|
16
|
+
const { downloadTargetDir } = availableUpdate || {};
|
17
|
+
const appPath = (0, _1.getMacOSAppPath)();
|
18
|
+
const tmpPath = path_1.default.join(os_1.default.tmpdir(), String(new Date().getTime())); // 本地临时文件使用的目录
|
19
|
+
const volumesPath = path_1.default.join('/Volumes', updatorOptions.dmgTitleFormatter ? updatorOptions.dmgTitleFormatter(updatorOptions, updateInfo) : productName);
|
20
|
+
const volumesAppPath = path_1.default.join(volumesPath, `${productName}.app`);
|
21
|
+
// step 1: 检测是否支持 hdiutil;
|
22
|
+
try {
|
23
|
+
const res = (await (0, index_1.spawnAsync)('which', ['hdiutil']));
|
24
|
+
if (!res.includes('/bin/hdiutil')) {
|
25
|
+
throw new Error('hdiutil not found');
|
26
|
+
}
|
27
|
+
}
|
28
|
+
catch (error) {
|
29
|
+
if (preCheck) {
|
30
|
+
_log(logger, error);
|
31
|
+
}
|
32
|
+
return {
|
33
|
+
success: false,
|
34
|
+
error,
|
35
|
+
};
|
36
|
+
}
|
37
|
+
// step 2: eject 一次 volume 下的 app,兜底,避免上一次结束时执行失败
|
38
|
+
try {
|
39
|
+
await (0, index_1.spawnAsync)('hdiutil', ['eject', volumesPath]);
|
40
|
+
}
|
41
|
+
catch (e) {
|
42
|
+
// 如果前一次更新时 eject 成功,这里文件不存在会报错,是个正常的 case,不用处理上报
|
43
|
+
}
|
44
|
+
finally {
|
45
|
+
const volumeAppNotExist = await (0, _1.waitUntil)(() => !(0, index_1.existsSync)(volumesAppPath), {
|
46
|
+
ms: 300,
|
47
|
+
retryTime: 5,
|
48
|
+
});
|
49
|
+
if (!volumeAppNotExist) {
|
50
|
+
const error = new Error('volume not exists');
|
51
|
+
if (preCheck) {
|
52
|
+
_log(logger, error);
|
53
|
+
}
|
54
|
+
return {
|
55
|
+
success: false,
|
56
|
+
error,
|
57
|
+
};
|
58
|
+
}
|
59
|
+
}
|
60
|
+
// step 3: 执行 hdiutil attach,挂载 dmg
|
61
|
+
try {
|
62
|
+
await (0, index_1.spawnAsync)('hdiutil', ['attach', downloadTargetDir]);
|
63
|
+
}
|
64
|
+
catch (error) {
|
65
|
+
_log(logger, error);
|
66
|
+
}
|
67
|
+
finally {
|
68
|
+
// 判断有没有新的 dmg 文件,没有的话提示用户重新下载
|
69
|
+
const volumeAppExist = await (0, _1.waitUntil)(() => (0, index_1.existsSync)(volumesAppPath), {
|
70
|
+
ms: 300,
|
71
|
+
retryTime: 5,
|
72
|
+
});
|
73
|
+
if (!volumeAppExist) {
|
74
|
+
const error = new Error('attach fail');
|
75
|
+
if (preCheck) {
|
76
|
+
_log(logger, error);
|
77
|
+
}
|
78
|
+
return {
|
79
|
+
success: false,
|
80
|
+
error,
|
81
|
+
};
|
82
|
+
}
|
83
|
+
}
|
84
|
+
if (preCheck) {
|
85
|
+
// 如果是预检查,直接返回
|
86
|
+
try {
|
87
|
+
await (0, index_1.spawnAsync)('hdiutil', ['eject', volumesPath]);
|
88
|
+
}
|
89
|
+
catch (error) {
|
90
|
+
_log(logger, error);
|
91
|
+
}
|
92
|
+
return {
|
93
|
+
success: true,
|
94
|
+
};
|
95
|
+
}
|
96
|
+
// step 4: 将当前目录下的 app 移到临时目录,如果后续操作失败了兜底用
|
97
|
+
try {
|
98
|
+
await (0, index_1.spawnAsync)('mv', [appPath, tmpPath]);
|
99
|
+
}
|
100
|
+
catch (error) {
|
101
|
+
error.customMessage = 'step4 mv to tmp path error';
|
102
|
+
_log(logger, error);
|
103
|
+
}
|
104
|
+
finally {
|
105
|
+
// 看临时目录文件是否移动成功
|
106
|
+
const tmpPathExist = await (0, _1.waitUntil)(() => (0, index_1.existsSync)(tmpPath), {
|
107
|
+
ms: 300,
|
108
|
+
retryTime: 5,
|
109
|
+
});
|
110
|
+
if (!tmpPathExist) {
|
111
|
+
const error = new Error('cp to tmp path fail');
|
112
|
+
return {
|
113
|
+
success: false,
|
114
|
+
error,
|
115
|
+
};
|
116
|
+
}
|
117
|
+
}
|
118
|
+
// step 5: 将新的 app 文件移动到 Applications 目录下,如果失败 or 查不到移入文件,将临时目录下的文件移动回来
|
119
|
+
try {
|
120
|
+
await (0, index_1.spawnAsync)('cp', ['-R', volumesAppPath, appPath]);
|
121
|
+
}
|
122
|
+
catch (error) {
|
123
|
+
_log(logger, error);
|
124
|
+
}
|
125
|
+
finally {
|
126
|
+
const appExist = await (0, _1.waitUntil)(() => (0, index_1.existsSync)(appPath), {
|
127
|
+
ms: 300,
|
128
|
+
retryTime: 5,
|
129
|
+
});
|
130
|
+
// 查不到新移入的 dmg,将原 dmg 再移动回来
|
131
|
+
if (!appExist) {
|
132
|
+
const error = new Error('cp to app fail');
|
133
|
+
await (0, index_1.spawnAsync)('mv', [tmpPath, appPath]);
|
134
|
+
return {
|
135
|
+
success: false,
|
136
|
+
error,
|
137
|
+
};
|
138
|
+
}
|
139
|
+
}
|
140
|
+
// step 6: 执行 hdiutil eject,推出
|
141
|
+
try {
|
142
|
+
await (0, index_1.spawnAsync)('hdiutil', ['eject', volumesPath]);
|
143
|
+
}
|
144
|
+
catch (error) {
|
145
|
+
_log(logger, error);
|
146
|
+
}
|
147
|
+
return {
|
148
|
+
success: true,
|
149
|
+
};
|
150
|
+
};
|
151
|
+
//# sourceMappingURL=install-macos-dmg.js.map
|
@@ -0,0 +1,23 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.sudoPromptExec = void 0;
|
4
|
+
const index_1 = require("./index");
|
5
|
+
function sudoPromptExec(appUpdatorOptions, logger, shell) {
|
6
|
+
const options = {
|
7
|
+
name: appUpdatorOptions.productName,
|
8
|
+
};
|
9
|
+
return new Promise((resolve, reject) => {
|
10
|
+
logger.warn(`update#sudoPromptExec_shell_${shell}`);
|
11
|
+
index_1.sudoPrompt.exec(shell, options, (error, stdout, _) => {
|
12
|
+
if (error) {
|
13
|
+
reject(error);
|
14
|
+
logger.error(`update#sudoPromptExec_error_${error}`);
|
15
|
+
return;
|
16
|
+
}
|
17
|
+
resolve(stdout);
|
18
|
+
logger.warn(`update#sudoPromptExec_stdout_${stdout}`);
|
19
|
+
});
|
20
|
+
});
|
21
|
+
}
|
22
|
+
exports.sudoPromptExec = sudoPromptExec;
|
23
|
+
//# sourceMappingURL=sudo-prompt-exec.js.map
|
@@ -0,0 +1,87 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.WindowsUpdator = void 0;
|
7
|
+
const path_1 = __importDefault(require("path"));
|
8
|
+
const electron_1 = require("electron");
|
9
|
+
const constants_1 = require("./common/constants");
|
10
|
+
const sudo_prompt_exec_1 = require("./utils/sudo-prompt-exec");
|
11
|
+
const app_updator_1 = require("./app-updator");
|
12
|
+
const utils_1 = require("./utils");
|
13
|
+
class WindowsUpdator extends app_updator_1.AppUpdator {
|
14
|
+
doGetAvailableUpdateInfo(updateInfo) {
|
15
|
+
this.logger.info(`WindowsUpdator#doGetAvailableUpdateInfo:start`);
|
16
|
+
let resourcePath = path_1.default.resolve(this.app.userDataPath);
|
17
|
+
const latestAsarPath = path_1.default.resolve(resourcePath, 'latest.asar');
|
18
|
+
const latestAppPath = path_1.default.resolve(resourcePath, 'latest');
|
19
|
+
let downloadTargetDir = `${latestAsarPath}.zip`;
|
20
|
+
if (updateInfo.updateType === constants_1.UpdateType.Package) {
|
21
|
+
downloadTargetDir = `${latestAppPath}.exe`;
|
22
|
+
}
|
23
|
+
return {
|
24
|
+
resourcePath,
|
25
|
+
downloadTargetDir,
|
26
|
+
latestAsarPath,
|
27
|
+
};
|
28
|
+
}
|
29
|
+
async doPreCheckForPackage() {
|
30
|
+
this.logger.info(`WindowsUpdator#doPreCheckForPackage:start`);
|
31
|
+
// Windows 全量安装默认预检正常
|
32
|
+
return Promise.resolve({ success: true });
|
33
|
+
}
|
34
|
+
async doUnzip() {
|
35
|
+
this.logger.info(`WindowsUpdator#doUnzip:start`);
|
36
|
+
try {
|
37
|
+
const { downloadTargetDir, resourcePath } = this.availableUpdate;
|
38
|
+
const unzipExe = (0, utils_1.getExecuteFile)('unzip.exe');
|
39
|
+
const executeCommand = `"${unzipExe}" -o "${downloadTargetDir}" -d "${resourcePath}"`;
|
40
|
+
await (0, utils_1.execAsync)(executeCommand);
|
41
|
+
}
|
42
|
+
catch (error) {
|
43
|
+
return {
|
44
|
+
success: false,
|
45
|
+
error,
|
46
|
+
};
|
47
|
+
}
|
48
|
+
this.logger.info(`WindowsUpdator#doUnzip:success`);
|
49
|
+
return {
|
50
|
+
success: true,
|
51
|
+
};
|
52
|
+
}
|
53
|
+
async doQuitAndInstallPackage() {
|
54
|
+
this.logger.info(`WindowsUpdator#doQuitAndInstallPackage:success`);
|
55
|
+
const { downloadTargetDir } = this.availableUpdate;
|
56
|
+
try {
|
57
|
+
// Windows 全量安装
|
58
|
+
const shellOpen = electron_1.shell.openItem || electron_1.shell.openPath;
|
59
|
+
shellOpen(downloadTargetDir);
|
60
|
+
setTimeout(() => {
|
61
|
+
this.app.exit(0);
|
62
|
+
}, 30);
|
63
|
+
return Promise.resolve({ success: true });
|
64
|
+
}
|
65
|
+
catch (error) {
|
66
|
+
return Promise.resolve({ success: false, error });
|
67
|
+
}
|
68
|
+
}
|
69
|
+
async doQuitAndInstallAsar() {
|
70
|
+
this.logger.info(`WindowsUpdator#doQuitAndInstallAsar:start`);
|
71
|
+
const productName = this.options?.productName;
|
72
|
+
const { resourcePath } = this.availableUpdate;
|
73
|
+
const exePath = this.app.exePath;
|
74
|
+
const updateExePath = (0, utils_1.getExecuteFile)('installer.exe');
|
75
|
+
const targetPath = path_1.default.resolve(exePath, '..', 'resources');
|
76
|
+
const executeCommand = `"${updateExePath}" "${targetPath}" "${resourcePath}" "${productName}.exe" "${exePath}"`;
|
77
|
+
try {
|
78
|
+
await (0, sudo_prompt_exec_1.sudoPromptExec)(this.options, this.logger, executeCommand);
|
79
|
+
return Promise.resolve({ success: true });
|
80
|
+
}
|
81
|
+
catch (error) {
|
82
|
+
return Promise.resolve({ success: false, error });
|
83
|
+
}
|
84
|
+
}
|
85
|
+
}
|
86
|
+
exports.WindowsUpdator = WindowsUpdator;
|
87
|
+
//# sourceMappingURL=windows-updator.js.map
|
package/package.json
CHANGED
@@ -1,34 +1,64 @@
|
|
1
1
|
{
|
2
2
|
"name": "electron-updator",
|
3
|
-
"version": "0.1.
|
4
|
-
"description": "electron-updator",
|
5
|
-
"
|
6
|
-
|
7
|
-
"
|
3
|
+
"version": "0.1.2",
|
4
|
+
"description": "electron-updator is a software updator management solution for Electron applications, It is convenient to complete full software update and dynamic update.",
|
5
|
+
"scripts": {
|
6
|
+
"build": "sh ./build.sh",
|
7
|
+
"test": "egg-bin test test/**/index.test.ts",
|
8
|
+
"lint": "eslint --fix --quiet --ext .js,.jsx ./src",
|
9
|
+
"prepublishOnly": "npm run build",
|
10
|
+
"contributor": "git-contributor",
|
11
|
+
"dev": "ttsc -p tsconfig.json -watch"
|
8
12
|
},
|
13
|
+
"main": "./build",
|
14
|
+
"keywords": [
|
15
|
+
"electron",
|
16
|
+
"updator",
|
17
|
+
"auto-updator"
|
18
|
+
],
|
9
19
|
"files": [
|
10
|
-
"
|
11
|
-
"
|
20
|
+
"build/**/*.js",
|
21
|
+
"build/**/*.exe"
|
12
22
|
],
|
13
|
-
"main": "index.js",
|
14
23
|
"repository": {
|
15
24
|
"type": "git",
|
16
|
-
"url": "
|
25
|
+
"url": "https://github.com/electron-modules/electron-updator"
|
17
26
|
},
|
18
27
|
"dependencies": {
|
28
|
+
"lodash": "4.17.21",
|
29
|
+
"moment": "2.29.4",
|
30
|
+
"eventemitter3": "^4.0.0",
|
31
|
+
"rimraf-alt": "0.2.0",
|
32
|
+
"urllib": "2.34.1"
|
19
33
|
},
|
20
34
|
"devDependencies": {
|
21
|
-
"
|
22
|
-
"
|
23
|
-
"
|
24
|
-
"
|
25
|
-
"
|
26
|
-
"
|
27
|
-
|
28
|
-
|
29
|
-
"
|
30
|
-
"
|
31
|
-
"
|
35
|
+
"@applint/spec": "^1.2.0",
|
36
|
+
"@types/lodash": "^4.14.181",
|
37
|
+
"@types/mocha": "^10.0.1",
|
38
|
+
"@types/mock-require": "^2.0.1",
|
39
|
+
"@types/react-dom": "^17.0.16",
|
40
|
+
"@typescript-eslint/parser": "^5.19.0",
|
41
|
+
"babel-plugin-module-resolver": "^4.1.0",
|
42
|
+
"concurrently": "^5.3.0",
|
43
|
+
"copyfiles": "^2.4.1",
|
44
|
+
"cross-env": "^7.0.3",
|
45
|
+
"detect-port": "1",
|
46
|
+
"egg-bin": "^5.9.0",
|
47
|
+
"electron": "18",
|
48
|
+
"electron-windows": "18",
|
49
|
+
"eslint": "^7.32.0",
|
50
|
+
"git-contributor": "*",
|
51
|
+
"husky": "^7.0.4",
|
52
|
+
"mm": "^3.0.2",
|
53
|
+
"mock-require": "^3.0.3",
|
54
|
+
"prettier": "^2.6.2",
|
55
|
+
"prop-types": "^15.7.2",
|
56
|
+
"style-loader": "^1.1.2",
|
57
|
+
"ts-loader": "^9.2.8",
|
58
|
+
"ts-node": "^10.7.0",
|
59
|
+
"ttypescript": "^1.5.15",
|
60
|
+
"typescript": "^4.6.3",
|
61
|
+
"typescript-transform-paths": "^3.3.1"
|
32
62
|
},
|
33
63
|
"husky": {
|
34
64
|
"hooks": {
|
@@ -36,4 +66,4 @@
|
|
36
66
|
}
|
37
67
|
},
|
38
68
|
"license": "MIT"
|
39
|
-
}
|
69
|
+
}
|
package/bin/electron-updator.js
DELETED
package/index.js
DELETED
package/lib/electron-updator.js
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
'use strict';
|