farm-runner 1.1.11 โ 1.1.13
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/dist/src/bundle.js +1 -1
- package/dist/src/cli.js +1 -33
- package/dist/src/scripts/prepare-wda.d.ts +48 -11
- package/dist/src/scripts/prepare-wda.d.ts.map +1 -1
- package/dist/src/scripts/prepare-wda.js +203 -322
- package/dist/src/scripts/prepare-wda.js.map +1 -1
- package/package.json +7 -3
- package/dist/src/scripts/prepare-wda-cli.d.ts +0 -17
- package/dist/src/scripts/prepare-wda-cli.d.ts.map +0 -1
- package/dist/src/scripts/prepare-wda-cli.js +0 -77
- package/dist/src/scripts/prepare-wda-cli.js.map +0 -1
|
@@ -1,53 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* WebDriverAgent (WDA) Preparation
|
|
3
|
+
* WebDriverAgent (WDA) Preparation Module
|
|
4
4
|
*
|
|
5
|
-
* This
|
|
6
|
-
* It
|
|
5
|
+
* This module provides functionality to build, sign, and package WebDriverAgent for iOS devices.
|
|
6
|
+
* It is used via the WebSocket API from the hub UI, not as a CLI command.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* The prepareWda function is called with the following options:
|
|
9
|
+
* - provisioningUUID: UUID of the provisioning profile to use (REQUIRED)
|
|
10
|
+
* - isFreeAccount: Whether this is a free or enterprise account (REQUIRED)
|
|
11
|
+
* - outputFileName: Custom output filename without .ipa extension (optional, defaults to 'wda-resign')
|
|
12
|
+
* - uploadToHub: Whether to upload to hub after build (optional, defaults to true)
|
|
13
|
+
* - setAsDefault: Whether to set as default WDA (optional, defaults to false)
|
|
14
|
+
* - wdaProjectPath: Path to WebDriverAgent xcode project (optional, auto-detected if not provided)
|
|
15
|
+
* - platform: Platform type: ios, tvos, or both (optional, defaults to 'ios')
|
|
16
|
+
* - onProgress: Callback for progress updates (optional)
|
|
12
17
|
*/
|
|
13
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
-
if (k2 === undefined) k2 = k;
|
|
15
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
-
}
|
|
19
|
-
Object.defineProperty(o, k2, desc);
|
|
20
|
-
}) : (function(o, m, k, k2) {
|
|
21
|
-
if (k2 === undefined) k2 = k;
|
|
22
|
-
o[k2] = m[k];
|
|
23
|
-
}));
|
|
24
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
-
}) : function(o, v) {
|
|
27
|
-
o["default"] = v;
|
|
28
|
-
});
|
|
29
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
-
var ownKeys = function(o) {
|
|
31
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
-
var ar = [];
|
|
33
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
-
return ar;
|
|
35
|
-
};
|
|
36
|
-
return ownKeys(o);
|
|
37
|
-
};
|
|
38
|
-
return function (mod) {
|
|
39
|
-
if (mod && mod.__esModule) return mod;
|
|
40
|
-
var result = {};
|
|
41
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
-
__setModuleDefault(result, mod);
|
|
43
|
-
return result;
|
|
44
|
-
};
|
|
45
|
-
})();
|
|
46
18
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
47
19
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
48
20
|
};
|
|
49
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.getProvisioningProfilePath = getProvisioningProfilePath;
|
|
50
23
|
exports.prepareWda = prepareWda;
|
|
24
|
+
exports.listProvisioningProfiles = listProvisioningProfiles;
|
|
51
25
|
const child_process_1 = require("child_process");
|
|
52
26
|
const fs_1 = __importDefault(require("fs"));
|
|
53
27
|
const http_1 = __importDefault(require("http"));
|
|
@@ -57,13 +31,11 @@ const path_1 = __importDefault(require("path"));
|
|
|
57
31
|
const util_1 = __importDefault(require("util"));
|
|
58
32
|
const form_data_1 = __importDefault(require("form-data"));
|
|
59
33
|
const node_config_1 = require("../config/node.config");
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// Helper Functions
|
|
36
|
+
// ============================================================================
|
|
60
37
|
const execAsync = util_1.default.promisify(child_process_1.exec);
|
|
61
38
|
const WDA_BUILD_PATH = '/appium_wda_ios/Build/Products/Debug-iphoneos';
|
|
62
|
-
let bundleIdName = null;
|
|
63
|
-
let freeBundleID = null;
|
|
64
|
-
let isFreeAccount = false;
|
|
65
|
-
let customOutputFileName = null;
|
|
66
|
-
let generatedIpaPath = null;
|
|
67
39
|
/**
|
|
68
40
|
* Get Xcode major version
|
|
69
41
|
*/
|
|
@@ -88,9 +60,9 @@ async function getProvisioningProfilePath() {
|
|
|
88
60
|
}
|
|
89
61
|
}
|
|
90
62
|
/**
|
|
91
|
-
* Load required
|
|
63
|
+
* Load required build dependencies dynamically
|
|
92
64
|
*/
|
|
93
|
-
|
|
65
|
+
function loadDependencies() {
|
|
94
66
|
try {
|
|
95
67
|
// Dynamic imports to handle optional dependencies
|
|
96
68
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
@@ -99,29 +71,18 @@ async function loadDependencies() {
|
|
|
99
71
|
const archiver = require('archiver');
|
|
100
72
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
101
73
|
const { provision } = require('ios-mobileprovision-finder');
|
|
102
|
-
|
|
103
|
-
const Listr = require('listr');
|
|
104
|
-
const { Observable } = await Promise.resolve().then(() => __importStar(require('rxjs')));
|
|
105
|
-
const { select, input } = await Promise.resolve().then(() => __importStar(require('@inquirer/prompts')));
|
|
106
|
-
return { Applesign, archiver, provision, Listr, Observable, select, input };
|
|
74
|
+
return { Applesign, archiver, provision };
|
|
107
75
|
}
|
|
108
76
|
catch (error) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
process.exit(1);
|
|
77
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
78
|
+
throw new Error(`Missing required dependencies for WDA preparation: ${errorMessage}. ` +
|
|
79
|
+
`Please install: applesign archiver ios-mobileprovision-finder`);
|
|
113
80
|
}
|
|
114
81
|
}
|
|
115
82
|
/**
|
|
116
|
-
* Get mobile provisioning file
|
|
83
|
+
* Get mobile provisioning file path from UUID
|
|
117
84
|
*/
|
|
118
|
-
async function
|
|
119
|
-
if (mobileProvisioningFile) {
|
|
120
|
-
if (!fs_1.default.existsSync(mobileProvisioningFile) || !fs_1.default.statSync(mobileProvisioningFile).isFile()) {
|
|
121
|
-
throw new Error(`Mobile provisioning file ${mobileProvisioningFile} does not exist`);
|
|
122
|
-
}
|
|
123
|
-
return mobileProvisioningFile;
|
|
124
|
-
}
|
|
85
|
+
async function getMobileProvisioningFilePath(provision, provisioningUUID) {
|
|
125
86
|
const provisionFileDir = await getProvisioningProfilePath();
|
|
126
87
|
if (!fs_1.default.existsSync(provisionFileDir)) {
|
|
127
88
|
throw new Error(`Provisioning directory does not exist: ${provisionFileDir}`);
|
|
@@ -137,28 +98,16 @@ async function getMobileProvisioningFile(provision, select, mobileProvisioningFi
|
|
|
137
98
|
if (!provisioningFiles || !provisioningFiles.length) {
|
|
138
99
|
throw new Error('No mobileprovision file found on the machine');
|
|
139
100
|
}
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
value: file.UUID,
|
|
144
|
-
name: `${file.Name.split(':')[1] || file.Name} (Team: ${file.TeamName}) (${file.UUID})`,
|
|
145
|
-
})),
|
|
146
|
-
});
|
|
147
|
-
isFreeAccount = await select({
|
|
148
|
-
message: 'Is this a free or enterprise account provisioning profile?',
|
|
149
|
-
choices: [
|
|
150
|
-
{ value: true, name: 'Free Account' },
|
|
151
|
-
{ value: false, name: 'Enterprise Account' },
|
|
152
|
-
],
|
|
153
|
-
});
|
|
154
|
-
if (isFreeAccount) {
|
|
155
|
-
bundleIdName = provisioningFiles.map(file => ({
|
|
156
|
-
uuid: file.UUID,
|
|
157
|
-
name: `${file.Name.split(':')[1] || file.Name}`,
|
|
158
|
-
}));
|
|
101
|
+
const selectedProfile = provisioningFiles.find(file => file.UUID === provisioningUUID);
|
|
102
|
+
if (!selectedProfile) {
|
|
103
|
+
throw new Error(`Provisioning profile with UUID ${provisioningUUID} not found`);
|
|
159
104
|
}
|
|
160
|
-
|
|
161
|
-
|
|
105
|
+
// Use the actual file path from the profile, not a constructed path based on UUID
|
|
106
|
+
// The file on disk may have a different name than the UUID
|
|
107
|
+
return {
|
|
108
|
+
filePath: selectedProfile._filePath,
|
|
109
|
+
profile: selectedProfile,
|
|
110
|
+
};
|
|
162
111
|
}
|
|
163
112
|
/**
|
|
164
113
|
* Get WebDriverAgent project path
|
|
@@ -179,37 +128,40 @@ async function getWdaProject(wdaProjectPath) {
|
|
|
179
128
|
return path_1.default.dirname(projectPath);
|
|
180
129
|
}
|
|
181
130
|
catch (err) {
|
|
182
|
-
throw new Error('Unable to find WebDriverAgent project');
|
|
131
|
+
throw new Error('Unable to find WebDriverAgent project. Install Appium XCUITest driver first.');
|
|
183
132
|
}
|
|
184
133
|
}
|
|
185
134
|
/**
|
|
186
135
|
* Build WebDriverAgent
|
|
187
136
|
*/
|
|
188
|
-
async function buildWebDriverAgent(projectDir,
|
|
137
|
+
async function buildWebDriverAgent(projectDir, bundleId, isFreeAccount, onProgress) {
|
|
189
138
|
try {
|
|
190
139
|
let buildCommand = 'xcodebuild clean build-for-testing -project WebDriverAgent.xcodeproj -derivedDataPath appium_wda_ios -scheme WebDriverAgentRunner -destination generic/platform=iOS CODE_SIGNING_ALLOWED=NO';
|
|
191
|
-
if (isFreeAccount &&
|
|
192
|
-
buildCommand += ` PRODUCT_BUNDLE_IDENTIFIER=${
|
|
140
|
+
if (isFreeAccount && bundleId) {
|
|
141
|
+
buildCommand += ` PRODUCT_BUNDLE_IDENTIFIER=${bundleId.replace(/^\s+|\s+$/g, '')}`;
|
|
142
|
+
}
|
|
143
|
+
if (onProgress) {
|
|
144
|
+
onProgress(`Executing: ${buildCommand}`);
|
|
193
145
|
}
|
|
194
|
-
logger(buildCommand);
|
|
195
146
|
await execAsync(buildCommand, { cwd: projectDir, maxBuffer: 1024 * 1024 * 100 });
|
|
196
147
|
return `${projectDir}/${WDA_BUILD_PATH}/WebDriverAgentRunner-Runner.app`;
|
|
197
148
|
}
|
|
198
149
|
catch (error) {
|
|
199
|
-
|
|
200
|
-
throw new Error(`โ Error building WebDriverAgent: ${error?.message}`);
|
|
150
|
+
throw new Error(`Error building WebDriverAgent: ${error?.message}`);
|
|
201
151
|
}
|
|
202
152
|
}
|
|
203
153
|
/**
|
|
204
154
|
* Zip payload directory
|
|
205
155
|
*/
|
|
206
|
-
async function zipPayloadDirectory(archiver, outputZipPath, folderPath,
|
|
156
|
+
async function zipPayloadDirectory(archiver, outputZipPath, folderPath, onProgress) {
|
|
207
157
|
return new Promise((resolve, reject) => {
|
|
208
158
|
const output = fs_1.default.createWriteStream(outputZipPath);
|
|
209
159
|
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
210
160
|
output.on('close', () => {
|
|
211
|
-
|
|
212
|
-
|
|
161
|
+
if (onProgress) {
|
|
162
|
+
onProgress(`Zipped ${archive.pointer()} total bytes`);
|
|
163
|
+
onProgress(`Archive has been written to ${outputZipPath}`);
|
|
164
|
+
}
|
|
213
165
|
resolve();
|
|
214
166
|
});
|
|
215
167
|
archive.on('error', (err) => {
|
|
@@ -286,254 +238,183 @@ async function isHubRunning(hubUrl) {
|
|
|
286
238
|
});
|
|
287
239
|
});
|
|
288
240
|
}
|
|
241
|
+
// ============================================================================
|
|
242
|
+
// Main Export Function
|
|
243
|
+
// ============================================================================
|
|
289
244
|
/**
|
|
290
|
-
* Main function to prepare WDA
|
|
245
|
+
* Main function to prepare WDA (non-interactive, API-driven)
|
|
246
|
+
*
|
|
247
|
+
* This function is called via WebSocket from the hub UI. All parameters
|
|
248
|
+
* must be provided - there are no interactive prompts.
|
|
291
249
|
*/
|
|
292
250
|
async function prepareWda(options) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
251
|
+
const progress = options.onProgress || (() => { });
|
|
252
|
+
// Validate required parameters
|
|
253
|
+
if (!options.provisioningUUID) {
|
|
254
|
+
throw new Error('provisioningUUID is required');
|
|
255
|
+
}
|
|
256
|
+
if (options.isFreeAccount === undefined) {
|
|
257
|
+
throw new Error('isFreeAccount is required');
|
|
258
|
+
}
|
|
259
|
+
progress('init', 'Starting WebDriverAgent preparation...');
|
|
260
|
+
// Load dependencies
|
|
261
|
+
progress('deps', 'Loading build dependencies...');
|
|
262
|
+
const { Applesign, archiver, provision } = loadDependencies();
|
|
263
|
+
// Get provisioning profile info
|
|
264
|
+
progress('profile', 'Validating provisioning profile...');
|
|
265
|
+
const { filePath: mobileProvisioningFile, profile } = await getMobileProvisioningFilePath(provision, options.provisioningUUID);
|
|
296
266
|
const platform = options.platform || 'ios';
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
observer.next('Packing Payload directory...');
|
|
357
|
-
return zipPayloadDirectory(archiver, `${wdaBuildPath}/wda-resign.zip`, payloadDirectory, observer);
|
|
358
|
-
})
|
|
359
|
-
.then(() => observer.complete())
|
|
360
|
-
.catch((err) => {
|
|
361
|
-
observer.error(err);
|
|
362
|
-
});
|
|
363
|
-
});
|
|
364
|
-
},
|
|
365
|
-
},
|
|
366
|
-
{
|
|
367
|
-
title: 'โ๏ธ Signing WebDriverAgent IPA',
|
|
368
|
-
task: async (context, task) => {
|
|
369
|
-
const wdaBuildPath = path_1.default.join(context.wdaProjectPath, WDA_BUILD_PATH);
|
|
370
|
-
if (platform === 'both') {
|
|
371
|
-
const platforms = ['ios', 'tvos'];
|
|
372
|
-
const results = [];
|
|
373
|
-
for (const p of platforms) {
|
|
374
|
-
const wdaFileName = p === 'tvos' ? `${customOutputFileName}_tvos.ipa` : `${customOutputFileName}.ipa`;
|
|
375
|
-
const ipaPath = `${wdaBuildPath}/${wdaFileName}`;
|
|
376
|
-
const appleOptions = {
|
|
377
|
-
mobileprovision: mobileProvisioningFile,
|
|
378
|
-
outfile: ipaPath,
|
|
379
|
-
};
|
|
380
|
-
if (freeBundleID) {
|
|
381
|
-
appleOptions.bundleId = freeBundleID.name.replace(/^\s+|\s+$/g, '');
|
|
382
|
-
}
|
|
383
|
-
const as = new Applesign(appleOptions);
|
|
384
|
-
await as.signIPA(path_1.default.join(wdaBuildPath, 'wda-resign.zip'));
|
|
385
|
-
results.push(`${p}: ${ipaPath}`);
|
|
386
|
-
// Store the iOS IPA path for deployment
|
|
387
|
-
if (p === 'ios') {
|
|
388
|
-
generatedIpaPath = ipaPath;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
task.title = `Successfully signed WebDriverAgent files for both platforms: ${results.join(', ')}`;
|
|
392
|
-
}
|
|
393
|
-
else {
|
|
394
|
-
const wdaFileName = `${customOutputFileName}.ipa`;
|
|
395
|
-
const ipaPath = `${wdaBuildPath}/${wdaFileName}`;
|
|
396
|
-
const appleOptions = {
|
|
397
|
-
mobileprovision: mobileProvisioningFile,
|
|
398
|
-
outfile: ipaPath,
|
|
399
|
-
};
|
|
400
|
-
if (freeBundleID) {
|
|
401
|
-
appleOptions.bundleId = freeBundleID.name.replace(/^\s+|\s+$/g, '');
|
|
402
|
-
}
|
|
403
|
-
const as = new Applesign(appleOptions);
|
|
404
|
-
await as.signIPA(path_1.default.join(wdaBuildPath, 'wda-resign.zip'));
|
|
405
|
-
generatedIpaPath = ipaPath;
|
|
406
|
-
task.title = `Successfully signed WebDriverAgent file for ${platform}: ${ipaPath}`;
|
|
407
|
-
}
|
|
408
|
-
},
|
|
409
|
-
},
|
|
410
|
-
], { exitOnError: true });
|
|
411
|
-
await tasks.run();
|
|
412
|
-
console.log('\nโ
WebDriverAgent preparation complete!\n');
|
|
413
|
-
// Get hub URL and credentials from config
|
|
414
|
-
const config = (0, node_config_1.getMergedConfig)();
|
|
415
|
-
const hubUrl = config.hubUrl;
|
|
416
|
-
const accessKey = config.accessKey;
|
|
417
|
-
const secretToken = config.token;
|
|
418
|
-
if (!hubUrl || !accessKey || !secretToken) {
|
|
419
|
-
console.log('โ ๏ธ Hub URL or credentials not configured in node.config.json');
|
|
420
|
-
console.log(' Please configure hubUrl, accessKey, and token to upload WDA to hub.\n');
|
|
421
|
-
console.log(`๐ WDA IPA file created locally: ${generatedIpaPath}\n`);
|
|
267
|
+
const customOutputFileName = options.outputFileName || (platform === 'tvos' ? 'wda-resign_tvos' : 'wda-resign');
|
|
268
|
+
// Get bundle ID info for free accounts
|
|
269
|
+
let bundleId;
|
|
270
|
+
if (options.isFreeAccount) {
|
|
271
|
+
bundleId = profile.Name.split(':')[1] || profile.Name;
|
|
272
|
+
}
|
|
273
|
+
// Find WDA project
|
|
274
|
+
progress('project', 'Searching for WebDriverAgent.xcodeproj...');
|
|
275
|
+
const wdaProjectPath = await getWdaProject(options.wdaProjectPath);
|
|
276
|
+
progress('project', `Found WebDriverAgent.xcodeproj at: ${wdaProjectPath}`);
|
|
277
|
+
// Build WDA
|
|
278
|
+
progress('build', 'Building WebDriverAgent (this may take several minutes)...', 10);
|
|
279
|
+
const wdaAppPath = await buildWebDriverAgent(wdaProjectPath, bundleId, options.isFreeAccount, msg => progress('build', msg));
|
|
280
|
+
progress('build', 'Successfully built WebDriverAgent', 40);
|
|
281
|
+
// Prepare IPA
|
|
282
|
+
progress('package', 'Preparing WebDriverAgent IPA...');
|
|
283
|
+
const wdaBuildPath = path_1.default.join(wdaProjectPath, WDA_BUILD_PATH);
|
|
284
|
+
const payloadDirectory = path_1.default.join(wdaBuildPath, 'Payload');
|
|
285
|
+
// Remove framework directory
|
|
286
|
+
const frameworksPath = `${wdaAppPath}/Frameworks`;
|
|
287
|
+
if (fs_1.default.existsSync(frameworksPath)) {
|
|
288
|
+
fs_1.default.readdirSync(frameworksPath).forEach(f => fs_1.default.rmSync(`${frameworksPath}/${f}`, { recursive: true }));
|
|
289
|
+
}
|
|
290
|
+
// Update Info.plist if using free account
|
|
291
|
+
const infoPlistPath = path_1.default.join(wdaAppPath, 'Info.plist');
|
|
292
|
+
if (fs_1.default.existsSync(infoPlistPath) && bundleId) {
|
|
293
|
+
let infoPlistContent = fs_1.default.readFileSync(infoPlistPath, 'utf8');
|
|
294
|
+
infoPlistContent = infoPlistContent.replace(/<key>CFBundleIdentifier<\/key>\n\s*<string>(.*?)<\/string>/, `<key>CFBundleIdentifier</key>\n<string>${bundleId.replace(/^\s+|\s+$/g, '')}</string>`);
|
|
295
|
+
fs_1.default.writeFileSync(infoPlistPath, infoPlistContent, 'utf8');
|
|
296
|
+
}
|
|
297
|
+
// Create Payload directory and move .app file
|
|
298
|
+
await execAsync(`mkdir -p ${payloadDirectory}`);
|
|
299
|
+
await execAsync(`mv ${wdaAppPath} ${payloadDirectory}`);
|
|
300
|
+
progress('package', 'Created Payload directory', 50);
|
|
301
|
+
// Zip payload
|
|
302
|
+
await zipPayloadDirectory(archiver, `${wdaBuildPath}/wda-resign.zip`, payloadDirectory, msg => progress('package', msg));
|
|
303
|
+
progress('package', 'Packaged WebDriverAgent', 60);
|
|
304
|
+
// Sign IPA
|
|
305
|
+
progress('sign', 'Signing WebDriverAgent IPA...');
|
|
306
|
+
let generatedIpaPath;
|
|
307
|
+
if (platform === 'both') {
|
|
308
|
+
const platforms = ['ios', 'tvos'];
|
|
309
|
+
for (const p of platforms) {
|
|
310
|
+
const wdaFileName = p === 'tvos' ? `${customOutputFileName}_tvos.ipa` : `${customOutputFileName}.ipa`;
|
|
311
|
+
const ipaPath = `${wdaBuildPath}/${wdaFileName}`;
|
|
312
|
+
const appleOptions = {
|
|
313
|
+
mobileprovision: mobileProvisioningFile,
|
|
314
|
+
outfile: ipaPath,
|
|
315
|
+
};
|
|
316
|
+
if (bundleId) {
|
|
317
|
+
appleOptions.bundleId = bundleId.replace(/^\s+|\s+$/g, '');
|
|
318
|
+
}
|
|
319
|
+
const as = new Applesign(appleOptions);
|
|
320
|
+
await as.signIPA(path_1.default.join(wdaBuildPath, 'wda-resign.zip'));
|
|
321
|
+
if (p === 'ios') {
|
|
322
|
+
generatedIpaPath = ipaPath;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
progress('sign', `Signed WebDriverAgent for both platforms`, 80);
|
|
422
326
|
}
|
|
423
327
|
else {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
328
|
+
const wdaFileName = `${customOutputFileName}.ipa`;
|
|
329
|
+
generatedIpaPath = `${wdaBuildPath}/${wdaFileName}`;
|
|
330
|
+
const appleOptions = {
|
|
331
|
+
mobileprovision: mobileProvisioningFile,
|
|
332
|
+
outfile: generatedIpaPath,
|
|
333
|
+
};
|
|
334
|
+
if (bundleId) {
|
|
335
|
+
appleOptions.bundleId = bundleId.replace(/^\s+|\s+$/g, '');
|
|
336
|
+
}
|
|
337
|
+
const as = new Applesign(appleOptions);
|
|
338
|
+
await as.signIPA(path_1.default.join(wdaBuildPath, 'wda-resign.zip'));
|
|
339
|
+
progress('sign', `Signed WebDriverAgent for ${platform}`, 80);
|
|
340
|
+
}
|
|
341
|
+
// Upload to hub if requested (default: true)
|
|
342
|
+
const shouldUpload = options.uploadToHub !== false;
|
|
343
|
+
let wdaId;
|
|
344
|
+
if (shouldUpload) {
|
|
345
|
+
const config = (0, node_config_1.getMergedConfig)();
|
|
346
|
+
const hubUrl = config.hubUrl;
|
|
347
|
+
const accessKey = config.accessKey;
|
|
348
|
+
const secretToken = config.token;
|
|
349
|
+
if (!hubUrl || !accessKey || !secretToken) {
|
|
350
|
+
progress('upload', 'Hub credentials not configured, skipping upload');
|
|
430
351
|
}
|
|
431
352
|
else {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
choices: [
|
|
439
|
-
{ value: true, name: 'Yes, upload to hub' },
|
|
440
|
-
{ value: false, name: 'No, keep local only' },
|
|
441
|
-
],
|
|
442
|
-
});
|
|
443
|
-
if (shouldUpload) {
|
|
444
|
-
// Ask if this should be the default WDA
|
|
445
|
-
const setAsDefault = await select({
|
|
446
|
-
message: 'Set this as the default WDA for the platform?',
|
|
447
|
-
choices: [
|
|
448
|
-
{ value: true, name: 'Yes, set as default' },
|
|
449
|
-
{ value: false, name: 'No, upload as alternative' },
|
|
450
|
-
],
|
|
451
|
-
});
|
|
353
|
+
const hubRunning = await isHubRunning(hubUrl);
|
|
354
|
+
if (!hubRunning) {
|
|
355
|
+
progress('upload', 'Hub server not reachable, skipping upload');
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
progress('upload', 'Uploading WDA to hub...', 85);
|
|
452
359
|
const basicAuth = Buffer.from(`${accessKey}:${secretToken}`).toString('base64');
|
|
453
360
|
const authHeader = `Basic ${basicAuth}`;
|
|
454
|
-
|
|
455
|
-
const bundleId = freeBundleID?.name.replace(/^\s+|\s+$/g, '') || 'com.facebook.WebDriverAgentRunner';
|
|
456
|
-
console.log('\n๐ค Uploading WDA to hub...');
|
|
457
|
-
console.log(` Hub URL: ${hubUrl}`);
|
|
458
|
-
console.log(` Bundle ID: ${bundleId}`);
|
|
459
|
-
console.log(` Set as default: ${setAsDefault ? 'Yes' : 'No'}\n`);
|
|
361
|
+
const finalBundleId = bundleId?.replace(/^\s+|\s+$/g, '') || 'com.facebook.WebDriverAgentRunner';
|
|
460
362
|
try {
|
|
461
|
-
|
|
462
|
-
throw new Error('Generated IPA file not found');
|
|
463
|
-
}
|
|
464
|
-
const result = await uploadWdaToHub(hubUrl, authHeader, generatedIpaPath, bundleId, setAsDefault);
|
|
363
|
+
const result = await uploadWdaToHub(hubUrl, authHeader, generatedIpaPath, finalBundleId, options.setAsDefault ?? false);
|
|
465
364
|
if (result.success) {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
console.log(` WDA ID: ${result.wdaId}`);
|
|
469
|
-
console.log(` Set as default: ${setAsDefault ? 'Yes' : 'No'}\n`);
|
|
470
|
-
// Store WDA ID for later use
|
|
471
|
-
generatedIpaPath = result.wdaId; // Reuse variable to pass WDA ID
|
|
472
|
-
}
|
|
365
|
+
wdaId = result.wdaId;
|
|
366
|
+
progress('upload', `WDA uploaded successfully. ID: ${wdaId}`, 100);
|
|
473
367
|
}
|
|
474
368
|
else {
|
|
475
|
-
|
|
476
|
-
console.log(`๐ WDA IPA file is still available locally: ${generatedIpaPath}\n`);
|
|
369
|
+
progress('upload', `Upload failed: ${result.message}`);
|
|
477
370
|
}
|
|
478
371
|
}
|
|
479
372
|
catch (uploadError) {
|
|
480
|
-
|
|
481
|
-
console.log(`๐ WDA IPA file is still available locally: ${generatedIpaPath}\n`);
|
|
373
|
+
progress('upload', `Upload error: ${uploadError.message}`);
|
|
482
374
|
}
|
|
483
375
|
}
|
|
484
|
-
else {
|
|
485
|
-
console.log(`\n๐ WDA IPA file created locally: ${generatedIpaPath}\n`);
|
|
486
|
-
}
|
|
487
376
|
}
|
|
488
377
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
const isWdaId = generatedIpaPath && generatedIpaPath.length === 36 && generatedIpaPath.includes('-');
|
|
509
|
-
if (isWdaId) {
|
|
510
|
-
console.log('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
|
|
511
|
-
console.log('๐งช Use WDA in Your Test Automation');
|
|
512
|
-
console.log('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n');
|
|
513
|
-
console.log('Add this capability to your test configuration:\n');
|
|
514
|
-
console.log('JavaScript/WebdriverIO:');
|
|
515
|
-
console.log(' const capabilities = {');
|
|
516
|
-
console.log(" platformName: 'iOS',");
|
|
517
|
-
console.log(" 'appium:automationName': 'XCUITest',");
|
|
518
|
-
console.log(` 'df:wdaID': '${generatedIpaPath}',`);
|
|
519
|
-
console.log(" 'df:runsOn': 'real' // or 'simulated' for simulators");
|
|
520
|
-
console.log(' };\n');
|
|
521
|
-
console.log('Python:');
|
|
522
|
-
console.log(' desired_caps = {');
|
|
523
|
-
console.log(" 'platformName': 'iOS',");
|
|
524
|
-
console.log(" 'appium:automationName': 'XCUITest',");
|
|
525
|
-
console.log(` 'df:wdaID': '${generatedIpaPath}',`);
|
|
526
|
-
console.log(" 'df:runsOn': 'real' # or 'simulated' for simulators");
|
|
527
|
-
console.log(' }\n');
|
|
528
|
-
console.log('Java:');
|
|
529
|
-
console.log(' DesiredCapabilities capabilities = new DesiredCapabilities();');
|
|
530
|
-
console.log(' capabilities.setCapability("platformName", "iOS");');
|
|
531
|
-
console.log(' capabilities.setCapability("appium:automationName", "XCUITest");');
|
|
532
|
-
console.log(` capabilities.setCapability("df:wdaID", "${generatedIpaPath}");`);
|
|
533
|
-
console.log(' capabilities.setCapability("df:runsOn", "real"); // or "simulated"\n');
|
|
378
|
+
progress('complete', 'WebDriverAgent preparation complete!', 100);
|
|
379
|
+
return {
|
|
380
|
+
success: true,
|
|
381
|
+
message: 'WebDriverAgent preparation complete',
|
|
382
|
+
ipaPath: generatedIpaPath,
|
|
383
|
+
wdaId,
|
|
384
|
+
bundleId: bundleId?.replace(/^\s+|\s+$/g, ''),
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* List available provisioning profiles on this machine
|
|
389
|
+
* Used by the hub UI to populate the profile selection dropdown
|
|
390
|
+
*/
|
|
391
|
+
async function listProvisioningProfiles() {
|
|
392
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
393
|
+
const { provision } = require('ios-mobileprovision-finder');
|
|
394
|
+
const provisionFileDir = await getProvisioningProfilePath();
|
|
395
|
+
if (!fs_1.default.existsSync(provisionFileDir)) {
|
|
396
|
+
return { profiles: [] };
|
|
534
397
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
398
|
+
const files = fs_1.default
|
|
399
|
+
.readdirSync(provisionFileDir, { encoding: 'utf8' })
|
|
400
|
+
.filter(file => file.endsWith('.mobileprovision'));
|
|
401
|
+
const profiles = files
|
|
402
|
+
.map(file => {
|
|
403
|
+
const fullPath = path_1.default.join(provisionFileDir, file);
|
|
404
|
+
try {
|
|
405
|
+
const mp = provision.readFromFile(fullPath);
|
|
406
|
+
return {
|
|
407
|
+
uuid: mp.UUID,
|
|
408
|
+
name: mp.Name.split(':')[1] || mp.Name,
|
|
409
|
+
teamName: mp.TeamName,
|
|
410
|
+
filePath: fullPath,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
catch {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
})
|
|
417
|
+
.filter((profile) => profile !== null);
|
|
418
|
+
return { profiles };
|
|
538
419
|
}
|
|
539
420
|
//# sourceMappingURL=prepare-wda.js.map
|