appium-ios-device 3.1.9 → 3.1.11
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/CHANGELOG.md +12 -0
- package/build/lib/afc/index.d.ts +1 -1
- package/build/lib/afc/index.d.ts.map +1 -1
- package/build/lib/afc/index.js +9 -9
- package/build/lib/afc/index.js.map +1 -1
- package/build/lib/afc/protocol.d.ts.map +1 -1
- package/build/lib/afc/protocol.js +16 -16
- package/build/lib/afc/protocol.js.map +1 -1
- package/build/lib/afc/streams.d.ts.map +1 -1
- package/build/lib/afc/streams.js.map +1 -1
- package/build/lib/afc/transformer/afcdecoder.d.ts.map +1 -1
- package/build/lib/afc/transformer/afcdecoder.js.map +1 -1
- package/build/lib/afc/transformer/afcencoder.d.ts.map +1 -1
- package/build/lib/afc/transformer/afcencoder.js.map +1 -1
- package/build/lib/base-service.d.ts.map +1 -1
- package/build/lib/base-service.js.map +1 -1
- package/build/lib/house-arrest/index.d.ts.map +1 -1
- package/build/lib/house-arrest/index.js.map +1 -1
- package/build/lib/imagemounter/index.d.ts.map +1 -1
- package/build/lib/imagemounter/index.js +4 -4
- package/build/lib/imagemounter/index.js.map +1 -1
- package/build/lib/imagemounter/utils/list_developer_image.d.ts.map +1 -1
- package/build/lib/imagemounter/utils/list_developer_image.js +3 -3
- package/build/lib/imagemounter/utils/list_developer_image.js.map +1 -1
- package/build/lib/installation-proxy/index.d.ts +11 -11
- package/build/lib/installation-proxy/index.d.ts.map +1 -1
- package/build/lib/installation-proxy/index.js +19 -17
- package/build/lib/installation-proxy/index.js.map +1 -1
- package/build/lib/instrument/headers.d.ts.map +1 -1
- package/build/lib/instrument/headers.js +7 -7
- package/build/lib/instrument/headers.js.map +1 -1
- package/build/lib/instrument/index.d.ts +1 -1
- package/build/lib/instrument/index.d.ts.map +1 -1
- package/build/lib/instrument/index.js +13 -7
- package/build/lib/instrument/index.js.map +1 -1
- package/build/lib/instrument/transformer/dtx-decode.d.ts.map +1 -1
- package/build/lib/instrument/transformer/dtx-decode.js +9 -3
- package/build/lib/instrument/transformer/dtx-decode.js.map +1 -1
- package/build/lib/instrument/transformer/dtx-encode.d.ts.map +1 -1
- package/build/lib/instrument/transformer/dtx-encode.js +1 -1
- package/build/lib/instrument/transformer/dtx-encode.js.map +1 -1
- package/build/lib/instrument/transformer/nskeyed.d.ts.map +1 -1
- package/build/lib/instrument/transformer/nskeyed.js +16 -9
- package/build/lib/instrument/transformer/nskeyed.js.map +1 -1
- package/build/lib/lockdown/index.d.ts +5 -5
- package/build/lib/lockdown/index.d.ts.map +1 -1
- package/build/lib/lockdown/index.js +9 -10
- package/build/lib/lockdown/index.js.map +1 -1
- package/build/lib/logger.js.map +1 -1
- package/build/lib/mcinstall/index.d.ts.map +1 -1
- package/build/lib/mcinstall/index.js +14 -6
- package/build/lib/mcinstall/index.js.map +1 -1
- package/build/lib/notification-proxy/index.d.ts.map +1 -1
- package/build/lib/notification-proxy/index.js +2 -2
- package/build/lib/notification-proxy/index.js.map +1 -1
- package/build/lib/plist-service/index.d.ts.map +1 -1
- package/build/lib/plist-service/index.js.map +1 -1
- package/build/lib/plist-service/transformer/plist-service-decoder.d.ts.map +1 -1
- package/build/lib/plist-service/transformer/plist-service-decoder.js.map +1 -1
- package/build/lib/plist-service/transformer/plist-service-encoder.d.ts.map +1 -1
- package/build/lib/plist-service/transformer/plist-service-encoder.js.map +1 -1
- package/build/lib/services.d.ts.map +1 -1
- package/build/lib/services.js +3 -3
- package/build/lib/services.js.map +1 -1
- package/build/lib/simulatelocation/index.d.ts.map +1 -1
- package/build/lib/simulatelocation/index.js.map +1 -1
- package/build/lib/ssl-helper.js +3 -3
- package/build/lib/ssl-helper.js.map +1 -1
- package/build/lib/syslog/index.d.ts +1 -1
- package/build/lib/syslog/index.d.ts.map +1 -1
- package/build/lib/syslog/index.js +1 -1
- package/build/lib/syslog/index.js.map +1 -1
- package/build/lib/syslog/transformer/syslog-decoder.d.ts.map +1 -1
- package/build/lib/syslog/transformer/syslog-decoder.js +4 -2
- package/build/lib/syslog/transformer/syslog-decoder.js.map +1 -1
- package/build/lib/testmanagerd/index.d.ts.map +1 -1
- package/build/lib/testmanagerd/index.js.map +1 -1
- package/build/lib/usbmux/index.d.ts.map +1 -1
- package/build/lib/usbmux/index.js +16 -13
- package/build/lib/usbmux/index.js.map +1 -1
- package/build/lib/usbmux/transformer/usbmux-decoder.d.ts.map +1 -1
- package/build/lib/usbmux/transformer/usbmux-decoder.js +1 -1
- package/build/lib/usbmux/transformer/usbmux-decoder.js.map +1 -1
- package/build/lib/usbmux/transformer/usbmux-encoder.d.ts.map +1 -1
- package/build/lib/usbmux/transformer/usbmux-encoder.js +1 -1
- package/build/lib/usbmux/transformer/usbmux-encoder.js.map +1 -1
- package/build/lib/util/transformer/length-based-splitter.d.ts.map +1 -1
- package/build/lib/util/transformer/length-based-splitter.js +9 -4
- package/build/lib/util/transformer/length-based-splitter.js.map +1 -1
- package/build/lib/util/transformer/stream-logger.d.ts.map +1 -1
- package/build/lib/util/transformer/stream-logger.js +4 -3
- package/build/lib/util/transformer/stream-logger.js.map +1 -1
- package/build/lib/util/uuid/parse.js.map +1 -1
- package/build/lib/util/uuid/stringify.js.map +1 -1
- package/build/lib/util/uuid/validate.d.ts.map +1 -1
- package/build/lib/util/uuid/validate.js.map +1 -1
- package/build/lib/utilities.d.ts.map +1 -1
- package/build/lib/utilities.js +12 -12
- package/build/lib/utilities.js.map +1 -1
- package/build/lib/webinspector/index.d.ts.map +1 -1
- package/build/lib/webinspector/index.js +11 -9
- package/build/lib/webinspector/index.js.map +1 -1
- package/build/lib/webinspector/transformer/webinspector-decoder.d.ts.map +1 -1
- package/build/lib/webinspector/transformer/webinspector-decoder.js.map +1 -1
- package/build/lib/webinspector/transformer/webinspector-encoder.d.ts.map +1 -1
- package/build/lib/webinspector/transformer/webinspector-encoder.js.map +1 -1
- package/build/lib/xctest.d.ts.map +1 -1
- package/build/lib/xctest.js +3 -6
- package/build/lib/xctest.js.map +1 -1
- package/lib/afc/index.js +49 -43
- package/lib/afc/protocol.js +53 -50
- package/lib/afc/streams.js +7 -9
- package/lib/afc/transformer/afcdecoder.js +8 -10
- package/lib/afc/transformer/afcencoder.js +7 -10
- package/lib/base-service.js +2 -4
- package/lib/constants.js +1 -1
- package/lib/house-arrest/index.js +16 -14
- package/lib/imagemounter/index.js +107 -104
- package/lib/imagemounter/utils/list_developer_image.js +115 -100
- package/lib/installation-proxy/index.js +31 -27
- package/lib/instrument/headers.js +51 -40
- package/lib/instrument/index.js +32 -21
- package/lib/instrument/transformer/dtx-decode.js +30 -16
- package/lib/instrument/transformer/dtx-encode.js +6 -8
- package/lib/instrument/transformer/nskeyed.js +40 -21
- package/lib/lockdown/index.js +44 -35
- package/lib/logger.js +1 -1
- package/lib/mcinstall/index.js +20 -13
- package/lib/notification-proxy/index.js +18 -17
- package/lib/plist-service/index.js +13 -14
- package/lib/plist-service/transformer/plist-service-decoder.js +6 -7
- package/lib/plist-service/transformer/plist-service-encoder.js +6 -7
- package/lib/services.js +45 -29
- package/lib/simulatelocation/index.js +4 -5
- package/lib/ssl-helper.js +6 -7
- package/lib/syslog/index.js +7 -8
- package/lib/syslog/transformer/syslog-decoder.js +11 -10
- package/lib/testmanagerd/index.js +10 -7
- package/lib/usbmux/index.js +52 -41
- package/lib/usbmux/transformer/usbmux-decoder.js +8 -10
- package/lib/usbmux/transformer/usbmux-encoder.js +10 -8
- package/lib/util/transformer/length-based-splitter.js +62 -24
- package/lib/util/transformer/stream-logger.js +14 -11
- package/lib/util/uuid/parse.ts +2 -2
- package/lib/util/uuid/stringify.ts +1 -1
- package/lib/util/uuid/validate.ts +2 -1
- package/lib/utilities.js +39 -28
- package/lib/webinspector/index.js +59 -46
- package/lib/webinspector/transformer/webinspector-decoder.js +22 -11
- package/lib/webinspector/transformer/webinspector-encoder.js +6 -8
- package/lib/xctest.js +284 -245
- package/package.json +4 -2
|
@@ -1,124 +1,127 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import {BaseServicePlist} from '../base-service';
|
|
2
|
+
import {fs} from '@appium/support';
|
|
3
3
|
import B from 'bluebird';
|
|
4
|
-
import {
|
|
5
|
-
const {
|
|
4
|
+
import {log} from '../logger';
|
|
5
|
+
const {lstat, readFile, createReadStream} = fs;
|
|
6
6
|
|
|
7
7
|
const MOBILE_IMAGE_MOUNTER_SERVICE_NAME = 'com.apple.mobile.mobile_image_mounter';
|
|
8
8
|
const FILE_TYPE_IMAGE = 'image';
|
|
9
9
|
const FILE_TYPE_SIGNATURE = 'signature';
|
|
10
10
|
|
|
11
11
|
function checkIfError(ret) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
if (ret.Error) {
|
|
13
|
+
throw new Error(ret.Error);
|
|
14
|
+
}
|
|
15
|
+
return ret;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
async function assertIsFile(filePath, fileType) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
throw err;
|
|
28
|
-
}
|
|
29
|
-
if (fileStat.isDirectory()) {
|
|
30
|
-
throw new Error(`The provided ${fileType} path is expected to be a file, but a directory was given: ${filePath}`);
|
|
19
|
+
/** @type {import('fs').Stats | undefined} */
|
|
20
|
+
let fileStat;
|
|
21
|
+
try {
|
|
22
|
+
fileStat = await lstat(filePath);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
if (/** @type {NodeJS.ErrnoException} */ (err).code === 'ENOENT') {
|
|
25
|
+
throw new Error(`The provided ${fileType} path does not exist: ${filePath}`);
|
|
31
26
|
}
|
|
32
|
-
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
29
|
+
if (fileStat.isDirectory()) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`The provided ${fileType} path is expected to be a file, but a directory was given: ${filePath}`,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return fileStat;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
class ImageMounter extends BaseServicePlist {
|
|
38
|
+
/**
|
|
39
|
+
* Lookup for mounted images.
|
|
40
|
+
* @param {string} imageType Type of image, `Developer` by default.
|
|
41
|
+
* @returns {Promise<Buffer[]>} Signature of each mounted image.
|
|
42
|
+
*/
|
|
43
|
+
async lookup(imageType = 'Developer') {
|
|
44
|
+
const ret = await this._plistService.sendPlistAndReceive({
|
|
45
|
+
Command: 'LookupImage',
|
|
46
|
+
ImageType: imageType,
|
|
47
|
+
});
|
|
48
|
+
return checkIfError(ret).ImageSignature || [];
|
|
49
|
+
}
|
|
36
50
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const ret = await this._plistService.sendPlistAndReceive({
|
|
44
|
-
Command: 'LookupImage',
|
|
45
|
-
ImageType: imageType
|
|
46
|
-
});
|
|
47
|
-
return checkIfError(ret).ImageSignature || [];
|
|
48
|
-
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if developer image is mounted.
|
|
53
|
+
*/
|
|
54
|
+
async isDeveloperImageMounted() {
|
|
55
|
+
return (await this.lookup()).length > 0;
|
|
56
|
+
}
|
|
49
57
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Mount image for device.
|
|
60
|
+
* @param {string} imageFilePath The file path of image.
|
|
61
|
+
* @param {string} imageSignatureFilePath The signature file path of given `DeveloperDiskImage.dmg`
|
|
62
|
+
* @param {string} imageType Type of image, `Developer` by default.
|
|
63
|
+
*/
|
|
64
|
+
async mount(imageFilePath, imageSignatureFilePath, imageType = 'Developer') {
|
|
65
|
+
//check file stats
|
|
66
|
+
const [imageFileStat] = await B.all([
|
|
67
|
+
assertIsFile(imageFilePath, FILE_TYPE_IMAGE),
|
|
68
|
+
assertIsFile(imageSignatureFilePath, FILE_TYPE_SIGNATURE),
|
|
69
|
+
]);
|
|
70
|
+
//read signature
|
|
71
|
+
const signature = await readFile(imageSignatureFilePath);
|
|
72
|
+
const mountedImages = await this.lookup(imageType);
|
|
73
|
+
if (mountedImages.find((mountedSignature) => signature.equals(mountedSignature))) {
|
|
74
|
+
log.info(
|
|
75
|
+
`An image with same signature of ${imageSignatureFilePath} is mounted. Doing nothing here`,
|
|
76
|
+
);
|
|
77
|
+
return;
|
|
55
78
|
}
|
|
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
|
-
ImageSignature: signature,
|
|
81
|
-
ImageSize: imageSize,
|
|
82
|
-
ImageType: imageType
|
|
83
|
-
});
|
|
84
|
-
if (checkIfError(receiveBytesResult).Status !== 'ReceiveBytesAck') {
|
|
85
|
-
const errMsg = `Unexpected return from ${MOBILE_IMAGE_MOUNTER_SERVICE_NAME} on sending ReceiveBytes`;
|
|
86
|
-
throw new Error(`${errMsg}: ${JSON.stringify(receiveBytesResult)}`);
|
|
87
|
-
}
|
|
88
|
-
//push image to device
|
|
89
|
-
const stream = createReadStream(imageFilePath);
|
|
90
|
-
try {
|
|
91
|
-
await new B((resolve, reject) => {
|
|
92
|
-
stream.on('end', resolve);
|
|
93
|
-
stream.on('error', reject);
|
|
94
|
-
stream.on('data', async (data) => {
|
|
95
|
-
try {
|
|
96
|
-
await this._plistService._socketClient.write(data);
|
|
97
|
-
} catch (e) {
|
|
98
|
-
stream.emit('error', e);
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
} finally {
|
|
103
|
-
stream.close();
|
|
104
|
-
}
|
|
105
|
-
const pushImageResult = await this._plistService.receivePlist();
|
|
106
|
-
if (checkIfError(pushImageResult).Status !== 'Complete') {
|
|
107
|
-
const errMsg = `Unexpected return from ${MOBILE_IMAGE_MOUNTER_SERVICE_NAME} on pushing image file`;
|
|
108
|
-
throw new Error(`${errMsg}: ${JSON.stringify(pushImageResult)}`);
|
|
109
|
-
}
|
|
110
|
-
//mount image
|
|
111
|
-
const mountResult = await this._plistService.sendPlistAndReceive({
|
|
112
|
-
Command: 'MountImage',
|
|
113
|
-
ImagePath: '/private/var/mobile/Media/PublicStaging/staging.dimag',
|
|
114
|
-
ImageSignature: signature,
|
|
115
|
-
ImageType: imageType
|
|
79
|
+
//notify device
|
|
80
|
+
const imageSize = imageFileStat.size;
|
|
81
|
+
const receiveBytesResult = await this._plistService.sendPlistAndReceive({
|
|
82
|
+
Command: 'ReceiveBytes',
|
|
83
|
+
ImageSignature: signature,
|
|
84
|
+
ImageSize: imageSize,
|
|
85
|
+
ImageType: imageType,
|
|
86
|
+
});
|
|
87
|
+
if (checkIfError(receiveBytesResult).Status !== 'ReceiveBytesAck') {
|
|
88
|
+
const errMsg = `Unexpected return from ${MOBILE_IMAGE_MOUNTER_SERVICE_NAME} on sending ReceiveBytes`;
|
|
89
|
+
throw new Error(`${errMsg}: ${JSON.stringify(receiveBytesResult)}`);
|
|
90
|
+
}
|
|
91
|
+
//push image to device
|
|
92
|
+
const stream = createReadStream(imageFilePath);
|
|
93
|
+
try {
|
|
94
|
+
await new B((resolve, reject) => {
|
|
95
|
+
stream.on('end', resolve);
|
|
96
|
+
stream.on('error', reject);
|
|
97
|
+
stream.on('data', async (data) => {
|
|
98
|
+
try {
|
|
99
|
+
await this._plistService._socketClient.write(data);
|
|
100
|
+
} catch (e) {
|
|
101
|
+
stream.emit('error', e);
|
|
102
|
+
}
|
|
116
103
|
});
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
104
|
+
});
|
|
105
|
+
} finally {
|
|
106
|
+
stream.close();
|
|
107
|
+
}
|
|
108
|
+
const pushImageResult = await this._plistService.receivePlist();
|
|
109
|
+
if (checkIfError(pushImageResult).Status !== 'Complete') {
|
|
110
|
+
const errMsg = `Unexpected return from ${MOBILE_IMAGE_MOUNTER_SERVICE_NAME} on pushing image file`;
|
|
111
|
+
throw new Error(`${errMsg}: ${JSON.stringify(pushImageResult)}`);
|
|
112
|
+
}
|
|
113
|
+
//mount image
|
|
114
|
+
const mountResult = await this._plistService.sendPlistAndReceive({
|
|
115
|
+
Command: 'MountImage',
|
|
116
|
+
ImagePath: '/private/var/mobile/Media/PublicStaging/staging.dimag',
|
|
117
|
+
ImageSignature: signature,
|
|
118
|
+
ImageType: imageType,
|
|
119
|
+
});
|
|
120
|
+
if (mountResult.DetailedError?.includes('is already mounted at /Developer')) {
|
|
121
|
+
log.info('DeveloperImage was already mounted');
|
|
122
|
+
return;
|
|
122
123
|
}
|
|
124
|
+
checkIfError(mountResult);
|
|
125
|
+
}
|
|
123
126
|
}
|
|
124
|
-
export {
|
|
127
|
+
export {ImageMounter, MOBILE_IMAGE_MOUNTER_SERVICE_NAME};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import {zip, net, env, fs} from '@appium/support';
|
|
3
|
+
import {join as joinPath} from 'node:path';
|
|
4
4
|
import axios from 'axios';
|
|
5
|
-
import {
|
|
5
|
+
import {log} from '../../logger';
|
|
6
6
|
|
|
7
|
-
const {
|
|
7
|
+
const {exists, readdir, mkdir, rimraf} = fs;
|
|
8
8
|
/**
|
|
9
9
|
* @typedef {Object} GithubTreeObject
|
|
10
10
|
* @property {string} path
|
|
@@ -46,40 +46,42 @@ const DEVELOPER_IMAGE_SIGNATURE_FILE_NAME = 'DeveloperDiskImage.dmg.signature';
|
|
|
46
46
|
* @returns {Promise<GithubTreeObject[] | undefined>} file list under target folder
|
|
47
47
|
*/
|
|
48
48
|
async function listGithubImageList(githubImageOption) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
const {githubRepo, subFolderList = [], branch = 'master'} = githubImageOption;
|
|
50
|
+
const initialUrl = `https://api.github.com/repos/${githubRepo}/git/trees/${branch}`;
|
|
51
|
+
const repoUrl = `https://github.com/${githubRepo}/`;
|
|
52
|
+
const fileList = [];
|
|
53
|
+
let curUrl = initialUrl;
|
|
54
|
+
/**
|
|
55
|
+
* @type {GithubTreeObject[] | undefined}
|
|
56
|
+
*/
|
|
57
|
+
for (let i = 0; i <= subFolderList.length; i++) {
|
|
58
|
+
const res = await axios.get(curUrl);
|
|
54
59
|
/**
|
|
55
|
-
* @type {
|
|
60
|
+
* @type {GithubTreeResponse}
|
|
56
61
|
*/
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const treeList = body?.tree;
|
|
64
|
-
if (!treeList) {
|
|
65
|
-
throw new Error(`Failed on looking up ${fileList.join('/')} under ${repoUrl}: ${JSON.stringify(body)}`);
|
|
66
|
-
}
|
|
67
|
-
if (i === subFolderList.length) {
|
|
68
|
-
return treeList;
|
|
69
|
-
}
|
|
70
|
-
const entry = subFolderList[i];
|
|
71
|
-
const nextItem = _.find(treeList, (item) => item.path === entry);
|
|
72
|
-
if (!nextItem) {
|
|
73
|
-
const errMsg = `Unable to find ${entry} under ${fileList.join('/')} in ${repoUrl}`;
|
|
74
|
-
throw new Error(`${errMsg}: ${JSON.stringify(treeList)}`);
|
|
75
|
-
}
|
|
76
|
-
if (nextItem.type !== 'tree') {
|
|
77
|
-
const errMsg = `${entry} under ${fileList.join('/')} in ${repoUrl} is expected to be a tree`;
|
|
78
|
-
throw new Error(`${errMsg}, got ${nextItem.type} instead.`);
|
|
79
|
-
}
|
|
80
|
-
fileList.push(entry);
|
|
81
|
-
curUrl = nextItem.url;
|
|
62
|
+
const body = res.data;
|
|
63
|
+
const treeList = body?.tree;
|
|
64
|
+
if (!treeList) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Failed on looking up ${fileList.join('/')} under ${repoUrl}: ${JSON.stringify(body)}`,
|
|
67
|
+
);
|
|
82
68
|
}
|
|
69
|
+
if (i === subFolderList.length) {
|
|
70
|
+
return treeList;
|
|
71
|
+
}
|
|
72
|
+
const entry = subFolderList[i];
|
|
73
|
+
const nextItem = _.find(treeList, (item) => item.path === entry);
|
|
74
|
+
if (!nextItem) {
|
|
75
|
+
const errMsg = `Unable to find ${entry} under ${fileList.join('/')} in ${repoUrl}`;
|
|
76
|
+
throw new Error(`${errMsg}: ${JSON.stringify(treeList)}`);
|
|
77
|
+
}
|
|
78
|
+
if (nextItem.type !== 'tree') {
|
|
79
|
+
const errMsg = `${entry} under ${fileList.join('/')} in ${repoUrl} is expected to be a tree`;
|
|
80
|
+
throw new Error(`${errMsg}, got ${nextItem.type} instead.`);
|
|
81
|
+
}
|
|
82
|
+
fileList.push(entry);
|
|
83
|
+
curUrl = nextItem.url;
|
|
84
|
+
}
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
/**
|
|
@@ -89,19 +91,19 @@ async function listGithubImageList(githubImageOption) {
|
|
|
89
91
|
* or `undefined` if no such file exists
|
|
90
92
|
*/
|
|
91
93
|
async function findDeveloperImageFromDirectory(entry) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
94
|
+
const fileList = await readdir(entry, {withFileTypes: true});
|
|
95
|
+
for (const subEntry of fileList) {
|
|
96
|
+
if (subEntry.name === DEVELOPER_IMAGE_FILE_NAME && subEntry.isFile()) {
|
|
97
|
+
return entry;
|
|
98
|
+
}
|
|
99
|
+
if (subEntry.isDirectory()) {
|
|
100
|
+
const fullPath = joinPath(entry, subEntry.name);
|
|
101
|
+
const subFolderResult = await findDeveloperImageFromDirectory(fullPath);
|
|
102
|
+
if (subFolderResult) {
|
|
103
|
+
return subFolderResult;
|
|
104
|
+
}
|
|
104
105
|
}
|
|
106
|
+
}
|
|
105
107
|
}
|
|
106
108
|
|
|
107
109
|
/**
|
|
@@ -120,33 +122,40 @@ async function findDeveloperImageFromDirectory(entry) {
|
|
|
120
122
|
* @throws If developer image is not found, or error while downloading or unzipping.
|
|
121
123
|
*/
|
|
122
124
|
async function findDeveloperImage(version, githubImageOption) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
125
|
+
const finalVersion = version.split('.').splice(0, 2).join('.');
|
|
126
|
+
const fileName = `${finalVersion}.zip`;
|
|
127
|
+
const DEFAULT_IMAGE_DIR = joinPath(await env.resolveAppiumHome(), DEFAULT_IMAGE_DIR_NAME);
|
|
128
|
+
const fullDownloadPath = joinPath(DEFAULT_IMAGE_DIR, fileName);
|
|
129
|
+
if (!(await exists(DEFAULT_IMAGE_DIR))) {
|
|
130
|
+
await mkdir(DEFAULT_IMAGE_DIR, {recursive: true});
|
|
131
|
+
}
|
|
132
|
+
if (!(await exists(fullDownloadPath))) {
|
|
133
|
+
await searchAndDownloadDeveloperImageFromGithub(
|
|
134
|
+
finalVersion,
|
|
135
|
+
fullDownloadPath,
|
|
136
|
+
githubImageOption,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
const decompressPath = joinPath(DEFAULT_IMAGE_DIR, finalVersion);
|
|
140
|
+
/** @type {string | undefined} */
|
|
141
|
+
let developerImageParentFolder;
|
|
142
|
+
if (await exists(decompressPath)) {
|
|
143
|
+
developerImageParentFolder = await findDeveloperImageFromDirectory(decompressPath);
|
|
144
|
+
}
|
|
145
|
+
if (!developerImageParentFolder) {
|
|
146
|
+
await zip.extractAllTo(fullDownloadPath, decompressPath);
|
|
147
|
+
developerImageParentFolder = await findDeveloperImageFromDirectory(decompressPath);
|
|
148
|
+
}
|
|
149
|
+
if (!developerImageParentFolder) {
|
|
150
|
+
throw new Error(`Unable to find unzipped developer image in ${decompressPath}`);
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
developerImage: joinPath(developerImageParentFolder, DEVELOPER_IMAGE_FILE_NAME),
|
|
154
|
+
developerImageSignature: joinPath(
|
|
155
|
+
developerImageParentFolder,
|
|
156
|
+
DEVELOPER_IMAGE_SIGNATURE_FILE_NAME,
|
|
157
|
+
),
|
|
158
|
+
};
|
|
150
159
|
}
|
|
151
160
|
|
|
152
161
|
/**
|
|
@@ -155,31 +164,37 @@ async function findDeveloperImage(version, githubImageOption) {
|
|
|
155
164
|
* @param {string} fullDownloadPath
|
|
156
165
|
* @param {ImageFromGithubRepo} githubImageOption
|
|
157
166
|
*/
|
|
158
|
-
async function searchAndDownloadDeveloperImageFromGithub(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
167
|
+
async function searchAndDownloadDeveloperImageFromGithub(
|
|
168
|
+
finalVersion,
|
|
169
|
+
fullDownloadPath,
|
|
170
|
+
githubImageOption,
|
|
171
|
+
) {
|
|
172
|
+
const fileNameRegExp = new RegExp(`${_.escapeRegExp(finalVersion)}(\\(([\\w_|.()])+\\))?.zip`);
|
|
173
|
+
const {githubRepo, subFolderList = [], branch = 'master'} = githubImageOption;
|
|
174
|
+
/** @type {string | undefined} */
|
|
175
|
+
let fileUrl;
|
|
176
|
+
const fileList = await listGithubImageList(githubImageOption);
|
|
177
|
+
if (!fileList) {
|
|
178
|
+
throw new Error(`Failed to list https://github.com/${githubRepo}`);
|
|
179
|
+
}
|
|
180
|
+
const targetFile = _.find(fileList, (item) => fileNameRegExp.test(item.path));
|
|
181
|
+
const splitter = subFolderList.length > 0 ? '/' : '';
|
|
182
|
+
const subFolderPath = `${splitter}${subFolderList.join('/')}${splitter}`;
|
|
183
|
+
if (targetFile) {
|
|
184
|
+
fileUrl = `https://raw.githubusercontent.com/${githubRepo}/${branch}${subFolderPath}${targetFile.path}`;
|
|
185
|
+
}
|
|
186
|
+
if (!fileUrl) {
|
|
187
|
+
throw new Error(`Failed to get developer image for iOS ${finalVersion}`);
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
log.info(
|
|
191
|
+
`Downloading developer image for ${finalVersion} to ${fullDownloadPath} from ${fileUrl}`,
|
|
192
|
+
);
|
|
193
|
+
await net.downloadFile(fileUrl, fullDownloadPath);
|
|
194
|
+
} catch (e) {
|
|
195
|
+
await rimraf(fullDownloadPath);
|
|
196
|
+
throw e;
|
|
197
|
+
}
|
|
183
198
|
}
|
|
184
199
|
|
|
185
|
-
export {
|
|
200
|
+
export {findDeveloperImage};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import {
|
|
2
|
+
import {BaseServicePlist} from '../base-service';
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
5
|
* https://github.com/tmothy20013/libimobiledevice-master/blob/ecd89b42021cf6f7efe9ad271b3bddc7dd4b281e/src/installation_proxy.c
|
|
@@ -14,11 +14,11 @@ class InstallationProxyService extends BaseServicePlist {
|
|
|
14
14
|
* @param {Object} clientOptions The extra options that wants to be passed to the installd
|
|
15
15
|
* @param {number} timeout [60000] The timeout between messages received from the phone as status updates
|
|
16
16
|
*/
|
|
17
|
-
async installApplication
|
|
17
|
+
async installApplication(path, clientOptions = {}, timeout = 60000) {
|
|
18
18
|
const request = {
|
|
19
19
|
Command: 'Install',
|
|
20
20
|
PackagePath: path,
|
|
21
|
-
ClientOptions: clientOptions
|
|
21
|
+
ClientOptions: clientOptions,
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
this._plistService.sendPlist(request);
|
|
@@ -34,11 +34,11 @@ class InstallationProxyService extends BaseServicePlist {
|
|
|
34
34
|
* @param {Object} clientOptions The extra options that wants to be passed to the installd
|
|
35
35
|
* @param {number} timeout [60000] The timeout between messages received from the phone as status updates
|
|
36
36
|
*/
|
|
37
|
-
async upgradeApplication
|
|
37
|
+
async upgradeApplication(path, clientOptions = {}, timeout = 60000) {
|
|
38
38
|
const request = {
|
|
39
39
|
Command: 'Upgrade',
|
|
40
40
|
PackagePath: path,
|
|
41
|
-
ClientOptions: clientOptions
|
|
41
|
+
ClientOptions: clientOptions,
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
this._plistService.sendPlist(request);
|
|
@@ -46,21 +46,21 @@ class InstallationProxyService extends BaseServicePlist {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
* @typedef {Object} ListApplicationOptions
|
|
50
|
+
*
|
|
51
|
+
* @property {string} applicationType of the which group you want to list. These can be User, System or leave it empty for both
|
|
52
|
+
* @property {Array} returnAttributes the fields which should be filtered and returned to the client. Leave this parameter empty if you don't want to filter
|
|
53
|
+
*/
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* Lists applications according to the opts and returns them as a map
|
|
57
57
|
* @param {Partial<ListApplicationOptions>} opts the listing options that wants to be passed
|
|
58
58
|
* @returns {Promise<Record<any, any>>} A map of the applications which the key is the bundleId
|
|
59
59
|
*/
|
|
60
|
-
async listApplications
|
|
60
|
+
async listApplications(opts = {}) {
|
|
61
61
|
const request = {
|
|
62
62
|
Command: 'Browse',
|
|
63
|
-
ClientOptions: {}
|
|
63
|
+
ClientOptions: {},
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
if (opts.applicationType) {
|
|
@@ -84,25 +84,27 @@ class InstallationProxyService extends BaseServicePlist {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
* @typedef {Object} LookupApplicationOptions
|
|
88
|
+
*
|
|
89
|
+
* @property {string} applicationType of the which group you want to list. These can be User, System or leave it empty for both
|
|
90
|
+
* @property {Array} returnAttributes the fields which should be filtered and returned to the client. Leave this parameter empty if you don't want to filter
|
|
91
|
+
* @property {string|Array} bundleIds Bundle Ids of the apps that should be searched
|
|
92
|
+
*/
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
95
|
* Lists applications according to the opts and returns them as a map
|
|
96
96
|
* @param {Partial<LookupApplicationOptions>} opts the lookup options that wants to be passed
|
|
97
97
|
* @returns {Promise<Record<any, any>>} A map of the applications which the key is the bundleId
|
|
98
98
|
*/
|
|
99
|
-
async lookupApplications
|
|
99
|
+
async lookupApplications(opts = {}) {
|
|
100
100
|
const request = {
|
|
101
101
|
Command: 'Lookup',
|
|
102
|
-
ClientOptions: {}
|
|
102
|
+
ClientOptions: {},
|
|
103
103
|
};
|
|
104
104
|
if (opts.bundleIds) {
|
|
105
|
-
request.ClientOptions.BundleIDs = _.isString(opts.bundleIds)
|
|
105
|
+
request.ClientOptions.BundleIDs = _.isString(opts.bundleIds)
|
|
106
|
+
? [opts.bundleIds]
|
|
107
|
+
: opts.bundleIds;
|
|
106
108
|
}
|
|
107
109
|
if (opts.applicationType) {
|
|
108
110
|
request.ClientOptions.ApplicationType = opts.applicationType;
|
|
@@ -118,7 +120,9 @@ class InstallationProxyService extends BaseServicePlist {
|
|
|
118
120
|
return message.LookupResult;
|
|
119
121
|
}
|
|
120
122
|
}
|
|
121
|
-
throw new Error(
|
|
123
|
+
throw new Error(
|
|
124
|
+
`Could not find LookupResult in the response: Response: ${JSON.stringify(messages)}`,
|
|
125
|
+
);
|
|
122
126
|
}
|
|
123
127
|
|
|
124
128
|
/**
|
|
@@ -126,17 +130,17 @@ class InstallationProxyService extends BaseServicePlist {
|
|
|
126
130
|
* @param {string} bundleId of the app that needs to be passed for uninstallation
|
|
127
131
|
* @param {number} timeout The timeout between messages received from the phone as status updates
|
|
128
132
|
*/
|
|
129
|
-
async uninstallApplication
|
|
133
|
+
async uninstallApplication(bundleId, timeout = 20000) {
|
|
130
134
|
const request = {
|
|
131
135
|
Command: 'Uninstall',
|
|
132
|
-
ApplicationIdentifier: bundleId
|
|
136
|
+
ApplicationIdentifier: bundleId,
|
|
133
137
|
};
|
|
134
138
|
|
|
135
139
|
this._plistService.sendPlist(request);
|
|
136
140
|
return await this._waitMessageCompletion(timeout);
|
|
137
141
|
}
|
|
138
142
|
|
|
139
|
-
async _waitMessageCompletion
|
|
143
|
+
async _waitMessageCompletion(timeout) {
|
|
140
144
|
let messages = [];
|
|
141
145
|
// Just added for safety. This shouldn't happen
|
|
142
146
|
for (let i = 0; i < Number.MAX_SAFE_INTEGER; i++) {
|
|
@@ -149,7 +153,7 @@ class InstallationProxyService extends BaseServicePlist {
|
|
|
149
153
|
return messages;
|
|
150
154
|
}
|
|
151
155
|
|
|
152
|
-
_isFinished
|
|
156
|
+
_isFinished(response) {
|
|
153
157
|
if (response.Error) {
|
|
154
158
|
throw new Error(`Unexpected data: ${JSON.stringify(response)}`);
|
|
155
159
|
}
|
|
@@ -161,5 +165,5 @@ class InstallationProxyService extends BaseServicePlist {
|
|
|
161
165
|
}
|
|
162
166
|
}
|
|
163
167
|
|
|
164
|
-
export {
|
|
168
|
+
export {InstallationProxyService, INSTALLATION_PROXY_SERVICE_NAME};
|
|
165
169
|
export default InstallationProxyService;
|