appium 2.0.0-beta.34 → 2.0.0-beta.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/lib/cli/driver-command.d.ts +3 -3
- package/build/lib/cli/driver-command.d.ts.map +1 -1
- package/build/lib/cli/driver-command.js +8 -8
- package/build/lib/cli/extension-command.d.ts +22 -17
- package/build/lib/cli/extension-command.d.ts.map +1 -1
- package/build/lib/cli/extension-command.js +35 -36
- package/build/lib/cli/plugin-command.d.ts +9 -15
- package/build/lib/cli/plugin-command.d.ts.map +1 -1
- package/build/lib/cli/plugin-command.js +8 -8
- package/build/tsconfig.tsbuildinfo +1 -1
- package/lib/cli/driver-command.js +45 -20
- package/lib/cli/extension-command.js +229 -123
- package/lib/cli/plugin-command.js +33 -18
- package/package.json +2 -2
- package/lib/appium-config.schema.json +0 -278
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
import _ from 'lodash';
|
|
4
4
|
import path from 'path';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
import {npm, fs, util, env} from '@appium/support';
|
|
6
|
+
import {log, spinWith, RingBuffer} from './utils';
|
|
7
|
+
import {SubProcess} from 'teen_process';
|
|
8
|
+
import {
|
|
9
|
+
INSTALL_TYPE_NPM,
|
|
10
|
+
INSTALL_TYPE_GIT,
|
|
11
|
+
INSTALL_TYPE_GITHUB,
|
|
12
|
+
INSTALL_TYPE_LOCAL,
|
|
13
|
+
} from '../extension/extension-config';
|
|
14
|
+
import {packageDidChange} from '../extension/package-changed';
|
|
11
15
|
|
|
12
16
|
const UPDATE_ALL = 'installed';
|
|
13
17
|
|
|
@@ -80,24 +84,31 @@ class ExtensionCommand {
|
|
|
80
84
|
* @return {Promise<ExtensionListData>} map of extension names to extension data
|
|
81
85
|
*/
|
|
82
86
|
async list ({showInstalled, showUpdates}) {
|
|
83
|
-
const lsMsg = `Listing ${showInstalled ? 'installed' : 'available'} ${
|
|
87
|
+
const lsMsg = `Listing ${showInstalled ? 'installed' : 'available'} ${
|
|
88
|
+
this.type
|
|
89
|
+
}s`;
|
|
84
90
|
const installedNames = Object.keys(this.config.installedExtensions);
|
|
85
91
|
const knownNames = Object.keys(this.knownExtensions);
|
|
86
|
-
const exts = [...installedNames, ...knownNames].reduce(
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
const exts = [...installedNames, ...knownNames].reduce(
|
|
93
|
+
(acc, name) => {
|
|
94
|
+
if (!acc[name]) {
|
|
95
|
+
if (installedNames.includes(name)) {
|
|
96
|
+
acc[name] = {
|
|
97
|
+
...this.config.installedExtensions[name],
|
|
98
|
+
installed: true,
|
|
99
|
+
};
|
|
100
|
+
} else if (!showInstalled) {
|
|
101
|
+
acc[name] = {pkgName: this.knownExtensions[name], installed: false};
|
|
102
|
+
}
|
|
92
103
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
104
|
+
return acc;
|
|
105
|
+
},
|
|
106
|
+
/**
|
|
107
|
+
* This accumulator contains either {@linkcode UninstalledExtensionLIstData} _or_
|
|
108
|
+
* {@linkcode InstalledExtensionListData} without upgrade information (which is added by the below code block)
|
|
109
|
+
* @type {Record<string,Partial<InstalledExtensionListData>|UninstalledExtensionListData>}
|
|
110
|
+
*/ ({})
|
|
111
|
+
);
|
|
101
112
|
|
|
102
113
|
// if we want to show whether updates are available, put that behind a spinner
|
|
103
114
|
await spinWith(this.isJsonOutput, lsMsg, async () => {
|
|
@@ -113,11 +124,12 @@ class ExtensionCommand {
|
|
|
113
124
|
const updates = await this.checkForExtensionUpdate(ext);
|
|
114
125
|
data.updateVersion = updates.safeUpdate;
|
|
115
126
|
data.unsafeUpdateVersion = updates.unsafeUpdate;
|
|
116
|
-
data.upToDate =
|
|
127
|
+
data.upToDate =
|
|
128
|
+
updates.safeUpdate === null && updates.unsafeUpdate === null;
|
|
117
129
|
}
|
|
118
130
|
});
|
|
119
131
|
|
|
120
|
-
const listData = /** @type {ExtensionListData} */(exts);
|
|
132
|
+
const listData = /** @type {ExtensionListData} */ (exts);
|
|
121
133
|
|
|
122
134
|
// if we're just getting the data, short circuit return here since we don't need to do any
|
|
123
135
|
// formatting logic
|
|
@@ -125,16 +137,20 @@ class ExtensionCommand {
|
|
|
125
137
|
return listData;
|
|
126
138
|
}
|
|
127
139
|
|
|
128
|
-
for (const [
|
|
129
|
-
name,
|
|
130
|
-
data
|
|
131
|
-
] of _.toPairs(listData)) {
|
|
140
|
+
for (const [name, data] of _.toPairs(listData)) {
|
|
132
141
|
let installTxt = ' [not installed]'.grey;
|
|
133
142
|
let updateTxt = '';
|
|
134
143
|
let upToDateTxt = '';
|
|
135
144
|
let unsafeUpdateTxt = '';
|
|
136
145
|
if (data.installed) {
|
|
137
|
-
const {
|
|
146
|
+
const {
|
|
147
|
+
installType,
|
|
148
|
+
installSpec,
|
|
149
|
+
updateVersion,
|
|
150
|
+
unsafeUpdateVersion,
|
|
151
|
+
version,
|
|
152
|
+
upToDate,
|
|
153
|
+
} = data;
|
|
138
154
|
let typeTxt;
|
|
139
155
|
switch (installType) {
|
|
140
156
|
case INSTALL_TYPE_GIT:
|
|
@@ -147,7 +163,9 @@ class ExtensionCommand {
|
|
|
147
163
|
default:
|
|
148
164
|
typeTxt = '(NPM)';
|
|
149
165
|
}
|
|
150
|
-
installTxt = `@${version.yellow} ${
|
|
166
|
+
installTxt = `@${version.yellow} ${
|
|
167
|
+
('[installed ' + typeTxt + ']').green
|
|
168
|
+
}`;
|
|
151
169
|
|
|
152
170
|
if (showUpdates) {
|
|
153
171
|
if (updateVersion) {
|
|
@@ -157,12 +175,15 @@ class ExtensionCommand {
|
|
|
157
175
|
upToDateTxt = ` [Up to date]`.green;
|
|
158
176
|
}
|
|
159
177
|
if (unsafeUpdateVersion) {
|
|
160
|
-
unsafeUpdateTxt =
|
|
178
|
+
unsafeUpdateTxt =
|
|
179
|
+
` [${unsafeUpdateVersion} available (potentially unsafe)]`.cyan;
|
|
161
180
|
}
|
|
162
181
|
}
|
|
163
182
|
}
|
|
164
183
|
|
|
165
|
-
console.log(
|
|
184
|
+
console.log(
|
|
185
|
+
`- ${name.yellow}${installTxt}${updateTxt}${upToDateTxt}${unsafeUpdateTxt}`
|
|
186
|
+
);
|
|
166
187
|
}
|
|
167
188
|
|
|
168
189
|
return listData;
|
|
@@ -174,33 +195,53 @@ class ExtensionCommand {
|
|
|
174
195
|
* @param {InstallArgs} args
|
|
175
196
|
* @return {Promise<ExtRecord<ExtType>>} map of all installed extension names to extension data
|
|
176
197
|
*/
|
|
177
|
-
async _install ({
|
|
198
|
+
async _install ({installSpec, installType, packageName}) {
|
|
199
|
+
/** @type {ExtensionFields<typeof this.type>} */
|
|
178
200
|
let extData;
|
|
179
|
-
let installSpec = ext;
|
|
180
201
|
|
|
181
|
-
if (
|
|
182
|
-
|
|
202
|
+
if (
|
|
203
|
+
packageName &&
|
|
204
|
+
[INSTALL_TYPE_LOCAL, INSTALL_TYPE_NPM].includes(installType)
|
|
205
|
+
) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`When using --source=${installType}, cannot also use --package`
|
|
208
|
+
);
|
|
183
209
|
}
|
|
184
210
|
|
|
185
|
-
if (
|
|
186
|
-
|
|
211
|
+
if (
|
|
212
|
+
!packageName &&
|
|
213
|
+
[INSTALL_TYPE_GIT, INSTALL_TYPE_GITHUB].includes(installType)
|
|
214
|
+
) {
|
|
215
|
+
throw new Error(
|
|
216
|
+
`When using --source=${installType}, must also use --package`
|
|
217
|
+
);
|
|
187
218
|
}
|
|
188
219
|
|
|
189
220
|
if (installType === INSTALL_TYPE_GITHUB) {
|
|
190
221
|
if (installSpec.split('/').length !== 2) {
|
|
191
|
-
throw new Error(
|
|
192
|
-
|
|
222
|
+
throw new Error(
|
|
223
|
+
`Github ${this.type} spec ${installSpec} appeared to be invalid; ` +
|
|
224
|
+
'it should be of the form <org>/<repo>'
|
|
225
|
+
);
|
|
193
226
|
}
|
|
194
|
-
extData = await this.installViaNpm({
|
|
227
|
+
extData = await this.installViaNpm({
|
|
228
|
+
installSpec,
|
|
229
|
+
pkgName: /** @type {string} */ (packageName),
|
|
230
|
+
});
|
|
195
231
|
} else if (installType === INSTALL_TYPE_GIT) {
|
|
196
232
|
// git urls can have '.git' at the end, but this is not necessary and would complicate the
|
|
197
233
|
// way we download and name directories, so we can just remove it
|
|
198
234
|
installSpec = installSpec.replace(/\.git$/, '');
|
|
199
|
-
extData = await this.installViaNpm({
|
|
235
|
+
extData = await this.installViaNpm({
|
|
236
|
+
installSpec,
|
|
237
|
+
pkgName: /** @type {string} */ (packageName),
|
|
238
|
+
});
|
|
200
239
|
} else {
|
|
201
240
|
let pkgName, pkgVer;
|
|
202
241
|
if (installType === INSTALL_TYPE_LOCAL) {
|
|
203
|
-
pkgName = path.isAbsolute(installSpec)
|
|
242
|
+
pkgName = path.isAbsolute(installSpec)
|
|
243
|
+
? installSpec
|
|
244
|
+
: path.resolve(installSpec);
|
|
204
245
|
} else {
|
|
205
246
|
// at this point we have either an npm package or an appium verified extension
|
|
206
247
|
// name or a local path. both of which will be installed via npm.
|
|
@@ -227,8 +268,9 @@ class ExtensionCommand {
|
|
|
227
268
|
// check it exists and get the correct package
|
|
228
269
|
const knownNames = Object.keys(this.knownExtensions);
|
|
229
270
|
if (!_.includes(knownNames, name)) {
|
|
230
|
-
const msg =
|
|
231
|
-
|
|
271
|
+
const msg =
|
|
272
|
+
`Could not resolve ${this.type}; are you sure it's in the list ` +
|
|
273
|
+
`of supported ${this.type}s? ${JSON.stringify(knownNames)}`;
|
|
232
274
|
throw new Error(msg);
|
|
233
275
|
}
|
|
234
276
|
pkgName = this.knownExtensions[name];
|
|
@@ -238,16 +280,18 @@ class ExtensionCommand {
|
|
|
238
280
|
}
|
|
239
281
|
}
|
|
240
282
|
|
|
241
|
-
extData = await this.installViaNpm({
|
|
283
|
+
extData = await this.installViaNpm({installSpec, pkgName, pkgVer});
|
|
242
284
|
}
|
|
243
285
|
|
|
244
|
-
const extName = extData[/** @type {string} */(`${this.type}Name`)];
|
|
245
|
-
delete extData[/** @type {string} */(`${this.type}Name`)];
|
|
286
|
+
const extName = extData[/** @type {string} */ (`${this.type}Name`)];
|
|
287
|
+
delete extData[/** @type {string} */ (`${this.type}Name`)];
|
|
246
288
|
|
|
247
289
|
if (this.config.isInstalled(extName)) {
|
|
248
|
-
throw new Error(
|
|
249
|
-
|
|
250
|
-
|
|
290
|
+
throw new Error(
|
|
291
|
+
`A ${this.type} named '${extName}' is already installed. ` +
|
|
292
|
+
`Did you mean to update? 'appium ${this.type} update'. See ` +
|
|
293
|
+
`installed ${this.type}s with 'appium ${this.type} list --installed'.`
|
|
294
|
+
);
|
|
251
295
|
}
|
|
252
296
|
|
|
253
297
|
const extManifest = {...extData, installType, installSpec};
|
|
@@ -269,19 +313,25 @@ class ExtensionCommand {
|
|
|
269
313
|
*
|
|
270
314
|
* @param {InstallViaNpmArgs} args
|
|
271
315
|
*/
|
|
272
|
-
async installViaNpm ({
|
|
316
|
+
async installViaNpm ({installSpec, pkgName, pkgVer}) {
|
|
273
317
|
const npmSpec = `${pkgName}${pkgVer ? '@' + pkgVer : ''}`;
|
|
274
|
-
const specMsg =
|
|
275
|
-
|
|
318
|
+
const specMsg =
|
|
319
|
+
npmSpec === installSpec ? '' : ` using NPM install spec '${npmSpec}'`;
|
|
320
|
+
const msg = `Installing '${installSpec}'${specMsg}`;
|
|
276
321
|
try {
|
|
277
|
-
const pkgJsonData = await spinWith(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
322
|
+
const pkgJsonData = await spinWith(
|
|
323
|
+
this.isJsonOutput,
|
|
324
|
+
msg,
|
|
325
|
+
async () =>
|
|
326
|
+
await npm.installPackage(this.config.appiumHome, pkgName, {
|
|
327
|
+
pkgVer,
|
|
328
|
+
})
|
|
329
|
+
);
|
|
330
|
+
return this.getExtensionFields(pkgJsonData, installSpec);
|
|
283
331
|
} catch (err) {
|
|
284
|
-
throw new Error(
|
|
332
|
+
throw new Error(
|
|
333
|
+
`Encountered an error when installing package: ${err.message}`
|
|
334
|
+
);
|
|
285
335
|
}
|
|
286
336
|
}
|
|
287
337
|
|
|
@@ -303,31 +353,38 @@ class ExtensionCommand {
|
|
|
303
353
|
* load as the main driver class, or to be able to detect incompatibilities between driver and
|
|
304
354
|
* appium versions.
|
|
305
355
|
*
|
|
306
|
-
* @param {ExtPackageJson<ExtType>} pkgJsonData - the package.json data for a driver module, as if it had been
|
|
307
|
-
*
|
|
356
|
+
* @param {ExtPackageJson<ExtType>} pkgJsonData - the package.json data for a driver module, as if it had been straightforwardly 'require'd
|
|
357
|
+
* @param {string} installSpec
|
|
308
358
|
* @returns {ExtensionFields<ExtType>}
|
|
309
359
|
*/
|
|
310
|
-
getExtensionFields (pkgJsonData) {
|
|
360
|
+
getExtensionFields (pkgJsonData, installSpec) {
|
|
311
361
|
if (!pkgJsonData.appium) {
|
|
312
|
-
throw new Error(
|
|
313
|
-
|
|
362
|
+
throw new Error(
|
|
363
|
+
`Installed driver did not have an 'appium' section in its ` +
|
|
364
|
+
`package.json file as expected`
|
|
365
|
+
);
|
|
314
366
|
}
|
|
315
367
|
const {appium, name, version} = pkgJsonData;
|
|
316
|
-
this.validateExtensionFields(appium);
|
|
368
|
+
this.validateExtensionFields(appium, installSpec);
|
|
317
369
|
/** @type {unknown} */
|
|
318
370
|
const result = {...appium, pkgName: name, version};
|
|
319
|
-
return /** @type {ExtensionFields<ExtType>} */(result);
|
|
371
|
+
return /** @type {ExtensionFields<ExtType>} */ (result);
|
|
320
372
|
}
|
|
321
373
|
|
|
322
374
|
/**
|
|
323
375
|
* For any package.json fields which a particular type of extension requires, validate the
|
|
324
376
|
* presence and form of those fields on the package.json data, throwing an error if anything is
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* For any `package.json` fields which a particular type of extension requires, validate the
|
|
380
|
+
* presence and form of those fields on the `package.json` data, throwing an error if anything is
|
|
325
381
|
* amiss.
|
|
326
382
|
*
|
|
327
|
-
* @param {ExtMetadata<ExtType>}
|
|
383
|
+
* @param {ExtMetadata<ExtType>} extMetadata - the data in the "appium" field of `package.json` for an extension
|
|
384
|
+
* @param {string} installSpec - Extension name/spec
|
|
328
385
|
*/
|
|
329
386
|
// eslint-disable-next-line no-unused-vars
|
|
330
|
-
validateExtensionFields (
|
|
387
|
+
validateExtensionFields (extMetadata, installSpec) {
|
|
331
388
|
throw new Error('Must be implemented in final class');
|
|
332
389
|
}
|
|
333
390
|
|
|
@@ -337,17 +394,22 @@ class ExtensionCommand {
|
|
|
337
394
|
* @param {UninstallOpts} opts
|
|
338
395
|
* @return {Promise<ExtRecord<ExtType>>} map of all installed extension names to extension data
|
|
339
396
|
*/
|
|
340
|
-
async _uninstall ({
|
|
341
|
-
if (!this.config.isInstalled(
|
|
342
|
-
throw new Error(
|
|
397
|
+
async _uninstall ({installSpec}) {
|
|
398
|
+
if (!this.config.isInstalled(installSpec)) {
|
|
399
|
+
throw new Error(
|
|
400
|
+
`Can't uninstall ${this.type} '${installSpec}'; it is not installed`
|
|
401
|
+
);
|
|
343
402
|
}
|
|
344
|
-
const installPath = this.config.getInstallPath(
|
|
403
|
+
const installPath = this.config.getInstallPath(installSpec);
|
|
345
404
|
try {
|
|
346
405
|
await fs.rimraf(installPath);
|
|
347
406
|
} finally {
|
|
348
|
-
await this.config.removeExtension(
|
|
407
|
+
await this.config.removeExtension(installSpec);
|
|
349
408
|
}
|
|
350
|
-
log(
|
|
409
|
+
log(
|
|
410
|
+
this.isJsonOutput,
|
|
411
|
+
`Successfully uninstalled ${this.type} '${installSpec}'`.green
|
|
412
|
+
);
|
|
351
413
|
return this.config.installedExtensions;
|
|
352
414
|
}
|
|
353
415
|
|
|
@@ -357,13 +419,17 @@ class ExtensionCommand {
|
|
|
357
419
|
* @param {ExtensionUpdateOpts} updateSpec
|
|
358
420
|
* @return {Promise<ExtensionUpdateResult>}
|
|
359
421
|
*/
|
|
360
|
-
async _update ({
|
|
361
|
-
const shouldUpdateAll =
|
|
422
|
+
async _update ({installSpec, unsafe}) {
|
|
423
|
+
const shouldUpdateAll = installSpec === UPDATE_ALL;
|
|
362
424
|
// if we're specifically requesting an update for an extension, make sure it's installed
|
|
363
|
-
if (!shouldUpdateAll && !this.config.isInstalled(
|
|
364
|
-
throw new Error(
|
|
425
|
+
if (!shouldUpdateAll && !this.config.isInstalled(installSpec)) {
|
|
426
|
+
throw new Error(
|
|
427
|
+
`The ${this.type} '${installSpec}' was not installed, so can't be updated`
|
|
428
|
+
);
|
|
365
429
|
}
|
|
366
|
-
const extsToUpdate = shouldUpdateAll
|
|
430
|
+
const extsToUpdate = shouldUpdateAll
|
|
431
|
+
? Object.keys(this.config.installedExtensions)
|
|
432
|
+
: [installSpec];
|
|
367
433
|
|
|
368
434
|
// 'errors' will have ext names as keys and error objects as values
|
|
369
435
|
/** @type {Record<string,Error>} */
|
|
@@ -376,24 +442,40 @@ class ExtensionCommand {
|
|
|
376
442
|
|
|
377
443
|
for (const e of extsToUpdate) {
|
|
378
444
|
try {
|
|
379
|
-
await spinWith(
|
|
380
|
-
|
|
381
|
-
|
|
445
|
+
await spinWith(
|
|
446
|
+
this.isJsonOutput,
|
|
447
|
+
`Checking if ${this.type} '${e}' is updatable`,
|
|
448
|
+
() => {
|
|
449
|
+
if (
|
|
450
|
+
this.config.installedExtensions[e].installType !==
|
|
451
|
+
INSTALL_TYPE_NPM
|
|
452
|
+
) {
|
|
453
|
+
throw new NotUpdatableError();
|
|
454
|
+
}
|
|
382
455
|
}
|
|
383
|
-
|
|
384
|
-
const update = await spinWith(
|
|
385
|
-
|
|
386
|
-
if
|
|
387
|
-
|
|
456
|
+
);
|
|
457
|
+
const update = await spinWith(
|
|
458
|
+
this.isJsonOutput,
|
|
459
|
+
`Checking if ${this.type} '${e}' needs an update`,
|
|
460
|
+
async () => {
|
|
461
|
+
const update = await this.checkForExtensionUpdate(e);
|
|
462
|
+
if (!(update.safeUpdate || update.unsafeUpdate)) {
|
|
463
|
+
throw new NoUpdatesAvailableError();
|
|
464
|
+
}
|
|
465
|
+
return update;
|
|
388
466
|
}
|
|
389
|
-
|
|
390
|
-
});
|
|
467
|
+
);
|
|
391
468
|
if (!unsafe && !update.safeUpdate) {
|
|
392
|
-
throw new Error(
|
|
393
|
-
|
|
394
|
-
|
|
469
|
+
throw new Error(
|
|
470
|
+
`The ${this.type} '${e}' has a major revision update ` +
|
|
471
|
+
`(${update.current} => ${update.unsafeUpdate}), which could include ` +
|
|
472
|
+
`breaking changes. If you want to apply this update, re-run with --unsafe`
|
|
473
|
+
);
|
|
395
474
|
}
|
|
396
|
-
const updateVer =
|
|
475
|
+
const updateVer =
|
|
476
|
+
unsafe && update.unsafeUpdate
|
|
477
|
+
? update.unsafeUpdate
|
|
478
|
+
: update.safeUpdate;
|
|
397
479
|
await spinWith(
|
|
398
480
|
this.isJsonOutput,
|
|
399
481
|
`Updating driver '${e}' from ${update.current} to ${updateVer}`,
|
|
@@ -407,12 +489,18 @@ class ExtensionCommand {
|
|
|
407
489
|
|
|
408
490
|
log(this.isJsonOutput, 'Update report:');
|
|
409
491
|
for (const [e, update] of _.toPairs(updates)) {
|
|
410
|
-
log(
|
|
492
|
+
log(
|
|
493
|
+
this.isJsonOutput,
|
|
494
|
+
`- ${this.type} ${e} updated: ${update.from} => ${update.to}`.green
|
|
495
|
+
);
|
|
411
496
|
}
|
|
412
497
|
for (const [e, err] of _.toPairs(errors)) {
|
|
413
498
|
if (err instanceof NotUpdatableError) {
|
|
414
|
-
log(
|
|
415
|
-
|
|
499
|
+
log(
|
|
500
|
+
this.isJsonOutput,
|
|
501
|
+
`- '${e}' was not installed via npm, so we could not check ` +
|
|
502
|
+
`for updates`.yellow
|
|
503
|
+
);
|
|
416
504
|
} else if (err instanceof NoUpdatesAvailableError) {
|
|
417
505
|
log(this.isJsonOutput, `- '${e}' had no updates available`.yellow);
|
|
418
506
|
} else {
|
|
@@ -436,8 +524,15 @@ class ExtensionCommand {
|
|
|
436
524
|
// this is a helper method, 'ext' is assumed to already be installed here, and of the npm
|
|
437
525
|
// install type
|
|
438
526
|
const {version, pkgName} = this.config.installedExtensions[ext];
|
|
439
|
-
let unsafeUpdate = await npm.getLatestVersion(
|
|
440
|
-
|
|
527
|
+
let unsafeUpdate = await npm.getLatestVersion(
|
|
528
|
+
this.config.appiumHome,
|
|
529
|
+
pkgName
|
|
530
|
+
);
|
|
531
|
+
let safeUpdate = await npm.getLatestSafeUpgradeVersion(
|
|
532
|
+
this.config.appiumHome,
|
|
533
|
+
pkgName,
|
|
534
|
+
version
|
|
535
|
+
);
|
|
441
536
|
if (!util.compareVersions(unsafeUpdate, '>', version)) {
|
|
442
537
|
// the latest version is not greater than the current version, so there's no possible update
|
|
443
538
|
unsafeUpdate = null;
|
|
@@ -458,16 +553,20 @@ class ExtensionCommand {
|
|
|
458
553
|
* Actually update an extension installed by NPM, using the NPM cli. And update the installation
|
|
459
554
|
* manifest.
|
|
460
555
|
*
|
|
461
|
-
* @param {string}
|
|
556
|
+
* @param {string} installSpec - name of extension to update
|
|
462
557
|
* @param {string} version - version string identifier to update extension to
|
|
463
558
|
* @returns {Promise<void>}
|
|
464
559
|
*/
|
|
465
|
-
async updateExtension (
|
|
466
|
-
const {pkgName} = this.config.installedExtensions[
|
|
467
|
-
await fs.rimraf(this.config.getInstallPath(
|
|
468
|
-
const extData = await this.installViaNpm({
|
|
469
|
-
|
|
470
|
-
|
|
560
|
+
async updateExtension (installSpec, version) {
|
|
561
|
+
const {pkgName} = this.config.installedExtensions[installSpec];
|
|
562
|
+
await fs.rimraf(this.config.getInstallPath(installSpec));
|
|
563
|
+
const extData = await this.installViaNpm({
|
|
564
|
+
installSpec,
|
|
565
|
+
pkgName,
|
|
566
|
+
pkgVer: version,
|
|
567
|
+
});
|
|
568
|
+
delete extData[/** @type {string} */ (`${this.type}Name`)];
|
|
569
|
+
await this.config.updateExtension(installSpec, extData);
|
|
471
570
|
}
|
|
472
571
|
|
|
473
572
|
/**
|
|
@@ -481,33 +580,37 @@ class ExtensionCommand {
|
|
|
481
580
|
* @param {RunOptions} opts
|
|
482
581
|
* @return {Promise<RunOutput>}
|
|
483
582
|
*/
|
|
484
|
-
async _run ({
|
|
485
|
-
if (!_.has(this.config.installedExtensions,
|
|
583
|
+
async _run ({installSpec, scriptName}) {
|
|
584
|
+
if (!_.has(this.config.installedExtensions, installSpec)) {
|
|
486
585
|
throw new Error(`please install the ${this.type} first`);
|
|
487
586
|
}
|
|
488
587
|
|
|
489
|
-
const extConfig = this.config.installedExtensions[
|
|
588
|
+
const extConfig = this.config.installedExtensions[installSpec];
|
|
490
589
|
|
|
491
590
|
// note: TS cannot understand that _.has() is a type guard
|
|
492
591
|
if (!extConfig.scripts) {
|
|
493
|
-
throw new Error(
|
|
494
|
-
|
|
592
|
+
throw new Error(
|
|
593
|
+
`The ${this.type} named '${installSpec}' does not contain the ` +
|
|
594
|
+
`"scripts" field underneath the "appium" field in its package.json`
|
|
595
|
+
);
|
|
495
596
|
}
|
|
496
597
|
|
|
497
598
|
const extScripts = extConfig.scripts;
|
|
498
599
|
|
|
499
600
|
if (!_.isPlainObject(extScripts)) {
|
|
500
|
-
throw new Error(
|
|
601
|
+
throw new Error(
|
|
602
|
+
`The ${this.type} named '${installSpec}' "scripts" field must be a plain object`
|
|
603
|
+
);
|
|
501
604
|
}
|
|
502
605
|
|
|
503
606
|
if (!_.has(extScripts, scriptName)) {
|
|
504
|
-
throw new Error(
|
|
607
|
+
throw new Error(
|
|
608
|
+
`The ${this.type} named '${installSpec}' does not support the script: '${scriptName}'`
|
|
609
|
+
);
|
|
505
610
|
}
|
|
506
611
|
|
|
507
|
-
const runner = new SubProcess(process.execPath, [
|
|
508
|
-
|
|
509
|
-
], {
|
|
510
|
-
cwd: this.config.getInstallPath(ext)
|
|
612
|
+
const runner = new SubProcess(process.execPath, [extScripts[scriptName]], {
|
|
613
|
+
cwd: this.config.getInstallPath(installSpec),
|
|
511
614
|
});
|
|
512
615
|
|
|
513
616
|
const output = new RingBuffer(50);
|
|
@@ -524,7 +627,10 @@ class ExtensionCommand {
|
|
|
524
627
|
log(this.isJsonOutput, `${scriptName} successfully ran`.green);
|
|
525
628
|
return {output: output.getBuff()};
|
|
526
629
|
} catch (err) {
|
|
527
|
-
log(
|
|
630
|
+
log(
|
|
631
|
+
this.isJsonOutput,
|
|
632
|
+
`Encountered an error when running '${scriptName}': ${err.message}`.red
|
|
633
|
+
);
|
|
528
634
|
return {error: err.message, output: output.getBuff()};
|
|
529
635
|
}
|
|
530
636
|
}
|
|
@@ -602,7 +708,7 @@ export {ExtensionCommand};
|
|
|
602
708
|
/**
|
|
603
709
|
* Options for {@linkcode ExtensionCommand._run}.
|
|
604
710
|
* @typedef RunOptions
|
|
605
|
-
* @property {string}
|
|
711
|
+
* @property {string} installSpec - name of the extension to run a script from
|
|
606
712
|
* @property {string} scriptName - name of the script to run
|
|
607
713
|
*/
|
|
608
714
|
|
|
@@ -617,7 +723,7 @@ export {ExtensionCommand};
|
|
|
617
723
|
/**
|
|
618
724
|
* Options for {@linkcode ExtensionCommand._update}.
|
|
619
725
|
* @typedef ExtensionUpdateOpts
|
|
620
|
-
* @property {string}
|
|
726
|
+
* @property {string} installSpec - the name of the extension to update
|
|
621
727
|
* @property {boolean} unsafe - if true, will perform unsafe updates past major revision boundaries
|
|
622
728
|
*/
|
|
623
729
|
|
|
@@ -638,7 +744,7 @@ export {ExtensionCommand};
|
|
|
638
744
|
/**
|
|
639
745
|
* Options for {@linkcode ExtensionCommand._uninstall}.
|
|
640
746
|
* @typedef UninstallOpts
|
|
641
|
-
* @property {string}
|
|
747
|
+
* @property {string} installSpec - the name or spec of an extension to uninstall
|
|
642
748
|
*/
|
|
643
749
|
|
|
644
750
|
/**
|
|
@@ -651,7 +757,7 @@ export {ExtensionCommand};
|
|
|
651
757
|
/**
|
|
652
758
|
* Options for {@linkcode ExtensionCommand.installViaNpm}
|
|
653
759
|
* @typedef InstallViaNpmArgs
|
|
654
|
-
* @property {string}
|
|
760
|
+
* @property {string} installSpec - the name or spec of an extension to install
|
|
655
761
|
* @property {string} pkgName - the NPM package name of the extension
|
|
656
762
|
* @property {string} [pkgVer] - the specific version of the NPM package
|
|
657
763
|
*/
|
|
@@ -667,7 +773,7 @@ export {ExtensionCommand};
|
|
|
667
773
|
/**
|
|
668
774
|
* Options for {@linkcode ExtensionCommand._install}
|
|
669
775
|
* @typedef InstallArgs
|
|
670
|
-
* @property {string}
|
|
776
|
+
* @property {string} installSpec - the name or spec of an extension to install
|
|
671
777
|
* @property {import('../../types/appium-manifest').InstallType} installType - how to install this extension. One of the INSTALL_TYPES
|
|
672
778
|
* @property {string} [packageName] - for git/github installs, the extension node package name
|
|
673
779
|
*/
|