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.
@@ -1,53 +1,27 @@
1
1
  "use strict";
2
2
  /**
3
- * WebDriverAgent (WDA) Preparation Script
3
+ * WebDriverAgent (WDA) Preparation Module
4
4
  *
5
- * This script builds, signs, and packages WebDriverAgent for iOS devices.
6
- * It can be run as a CLI command: `node prepare-wda`
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
- * Options:
9
- * --mobile-provisioning-file, -m Path to the mobile provisioning file
10
- * --wda-project-path, -p Path to WebDriverAgent xcode project
11
- * --platform Platform type: ios, tvos, or both (default: ios)
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 modules dynamically
63
+ * Load required build dependencies dynamically
92
64
  */
93
- async function loadDependencies() {
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
- // eslint-disable-next-line @typescript-eslint/no-var-requires
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
- console.error('โŒ Missing required dependencies for prepare-wda command.');
110
- console.error('Please install them:');
111
- console.error(' npm install applesign archiver ios-mobileprovision-finder listr rxjs @inquirer/prompts');
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 (interactive selection if not provided)
83
+ * Get mobile provisioning file path from UUID
117
84
  */
118
- async function getMobileProvisioningFile(provision, select, mobileProvisioningFile) {
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 prompt = await select({
141
- message: 'Select the mobileprovision to use for signing',
142
- choices: provisioningFiles.map(file => ({
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
- freeBundleID = bundleIdName?.find(d => d.uuid === prompt) || null;
161
- return path_1.default.join(await getProvisioningProfilePath(), `${prompt}.mobileprovision`);
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, logger, _bundleId) {
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 && freeBundleID) {
192
- buildCommand += ` PRODUCT_BUNDLE_IDENTIFIER=${freeBundleID.name.replace(/^\s+|\s+$/g, '')}`;
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
- console.error(error);
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, observer) {
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
- observer.next(`Zipped ${archive.pointer()} total bytes`);
212
- observer.next(`Archive has been written to ${outputZipPath}`);
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
- console.log('๐ŸŽ Starting WebDriverAgent preparation...\n');
294
- const { Applesign, archiver, provision, Listr, Observable, select, input } = await loadDependencies();
295
- const mobileProvisioningFile = await getMobileProvisioningFile(provision, select, options.mobileProvisioningFile);
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
- // Ask user for custom output filename
298
- const defaultFileName = platform === 'tvos' ? 'wda-resign_tvos' : 'wda-resign';
299
- customOutputFileName = await input({
300
- message: 'Enter output filename (without .ipa extension):',
301
- default: defaultFileName,
302
- });
303
- const tasks = new Listr([
304
- {
305
- title: '๐Ÿ” Searching for WebDriverAgent.xcodeproj',
306
- task: async (context, task) => {
307
- context.wdaProjectPath = await getWdaProject(options.wdaProjectPath);
308
- task.title = `Found WebDriverAgent.xcodeproj at: ${context.wdaProjectPath}`;
309
- },
310
- },
311
- {
312
- title: '๐Ÿ—๏ธ Building WebDriverAgent',
313
- task: (context, task) => {
314
- return new Observable((observer) => {
315
- buildWebDriverAgent(context.wdaProjectPath, observer.next.bind(observer))
316
- .then(wdaAppPath => {
317
- context.wdaAppPath = wdaAppPath;
318
- task.title = 'Successfully built WebDriverAgent';
319
- observer.complete();
320
- })
321
- .catch((err) => {
322
- observer.error(err);
323
- });
324
- });
325
- },
326
- },
327
- {
328
- title: '๐Ÿ“ฆ Preparing WebDriverAgent IPA',
329
- task: (context) => {
330
- return new Observable((observer) => {
331
- const wdaBuildPath = path_1.default.join(context.wdaProjectPath, WDA_BUILD_PATH);
332
- const payloadDirectory = path_1.default.join(wdaBuildPath, 'Payload');
333
- observer.next('Removing framework directory');
334
- const frameworksPath = `${context.wdaAppPath}/Frameworks`;
335
- if (fs_1.default.existsSync(frameworksPath)) {
336
- fs_1.default.readdirSync(frameworksPath).forEach(f => fs_1.default.rmSync(`${frameworksPath}/${f}`, { recursive: true }));
337
- }
338
- const infoPlistPath = path_1.default.join(context.wdaAppPath, 'Info.plist');
339
- const bundleId = freeBundleID?.name.replace(/^\s+|\s+$/g, '') || '';
340
- // Read and update Info.plist
341
- if (fs_1.default.existsSync(infoPlistPath) && bundleId) {
342
- let infoPlistContent = fs_1.default.readFileSync(infoPlistPath, 'utf8');
343
- infoPlistContent = infoPlistContent.replace(/<key>CFBundleIdentifier<\/key>\n\s*<string>(.*?)<\/string>/, `<key>CFBundleIdentifier</key>\n<string>${bundleId}</string>`);
344
- fs_1.default.writeFileSync(infoPlistPath, infoPlistContent, 'utf8');
345
- }
346
- observer.next('Creating Payload directory');
347
- execAsync(`mkdir -p ${payloadDirectory}`)
348
- .then(() => {
349
- observer.next('Payload directory created successfully');
350
- })
351
- .then(() => {
352
- observer.next('๐Ÿšš Moving .app file to Payload directory...');
353
- return execAsync(`mv ${context.wdaAppPath} ${payloadDirectory}`);
354
- })
355
- .then(() => {
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
- // Check if hub is running
425
- const hubRunning = await isHubRunning(hubUrl);
426
- if (!hubRunning) {
427
- console.log('โš ๏ธ Hub server is not running or not reachable');
428
- console.log(` Hub URL: ${hubUrl}\n`);
429
- console.log(`๐Ÿ“ WDA IPA file created locally: ${generatedIpaPath}\n`);
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
- // Offer to upload to hub
433
- console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”');
434
- console.log('๐ŸŒ Hub server detected! Upload WDA to hub?');
435
- console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n');
436
- const shouldUpload = await select({
437
- message: 'Would you like to upload WDA to the hub?',
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
- // Get bundle ID from free account or prompt
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
- if (!generatedIpaPath || !fs_1.default.existsSync(generatedIpaPath)) {
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
- console.log('โœ… WDA uploaded successfully to hub!');
467
- if (result.wdaId) {
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
- console.log(`โŒ Failed to upload WDA: ${result.message}\n`);
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
- console.log(`โŒ Upload error: ${uploadError.message}\n`);
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
- // Print post-installation instructions
490
- console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”');
491
- console.log('๐Ÿ“ฑ Next Steps: Install and Trust WebDriverAgent on your device');
492
- console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n');
493
- console.log('1๏ธโƒฃ Install the signed WDA IPA on your iOS device:');
494
- console.log(' Using Xcode:');
495
- console.log(' โ€ข Open Xcode โ†’ Window โ†’ Devices and Simulators');
496
- console.log(' โ€ข Select your device โ†’ Click "+" under "Installed Apps"');
497
- console.log(` โ€ข Select: ${generatedIpaPath}\n`);
498
- console.log(' Using xcrun devicectl (recommended):');
499
- console.log(' $ xcrun devicectl device install app --device <udid> <path-to-wda.ipa>\n');
500
- console.log('2๏ธโƒฃ Trust the developer certificate on your iOS device:');
501
- console.log(' โ€ข Go to Settings โ†’ General โ†’ VPN & Device Management');
502
- console.log(' โ€ข Under "Developer App", tap on your developer/team name');
503
- console.log(' โ€ข Tap "Trust <Developer Name>" and confirm\n');
504
- console.log('3๏ธโƒฃ Verify WDA is installed:');
505
- console.log(' โ€ข Look for "WebDriverAgentRunner" app on your device');
506
- console.log(' โ€ข The app icon may be blank/white - this is normal\n');
507
- // Check if WDA was uploaded to hub (generatedIpaPath will contain WDA ID if uploaded)
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
- console.log('๐Ÿ’ก Tip: For free Apple Developer accounts, the certificate expires');
536
- console.log(' after 7 days. You will need to re-sign and reinstall WDA.\n');
537
- console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”');
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