@hot-updater/apple-helper 0.25.8 → 0.25.10
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/index.cjs +371 -191
- package/dist/index.d.cts +21 -5
- package/dist/index.d.ts +21 -5
- package/dist/index.js +370 -192
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -37,6 +37,8 @@ let node_os = require("node:os");
|
|
|
37
37
|
node_os = __toESM(node_os);
|
|
38
38
|
let node_path = require("node:path");
|
|
39
39
|
node_path = __toESM(node_path);
|
|
40
|
+
let stream_promises = require("stream/promises");
|
|
41
|
+
stream_promises = __toESM(stream_promises);
|
|
40
42
|
|
|
41
43
|
//#region src/utils/cocoapods.ts
|
|
42
44
|
const installPodsIfNeeded = async (sourceDir) => {
|
|
@@ -102,26 +104,6 @@ const createRandomTmpDir = async (prefix = "apple-helper-") => {
|
|
|
102
104
|
return await (0, node_fs_promises.mkdtemp)((0, node_path.join)((0, node_os.tmpdir)(), prefix));
|
|
103
105
|
};
|
|
104
106
|
|
|
105
|
-
//#endregion
|
|
106
|
-
//#region src/utils/createXcodebuildLogger.ts
|
|
107
|
-
const createXcodebuildLogger = () => new __hot_updater_cli_tools.BuildLogger({
|
|
108
|
-
failurePatterns: ["BUILD FAILED", "ARCHIVE FAILED"],
|
|
109
|
-
importantLogPatterns: [
|
|
110
|
-
"error:",
|
|
111
|
-
"** BUILD FAILED **",
|
|
112
|
-
"** ARCHIVE FAILED **",
|
|
113
|
-
"The following build commands failed:"
|
|
114
|
-
],
|
|
115
|
-
progressMapping: [
|
|
116
|
-
[["[CP-User] [RN]Check rncore"], 10],
|
|
117
|
-
[["[CP-User] [Hermes] Replace Hermes"], 35],
|
|
118
|
-
[["[CP-User] [RN]Check FBReactNativeSpec"], 53],
|
|
119
|
-
[["React-FabricComponents"], 66],
|
|
120
|
-
[["[CP] Check Pods Manifest.lock"], 90],
|
|
121
|
-
[["BUILD SUCCEEDED", "ARCHIVE SUCCEEDED"], 100]
|
|
122
|
-
]
|
|
123
|
-
});
|
|
124
|
-
|
|
125
107
|
//#endregion
|
|
126
108
|
//#region src/utils/platform.ts
|
|
127
109
|
/**
|
|
@@ -202,56 +184,213 @@ const prettifyXcodebuildError = (error) => {
|
|
|
202
184
|
return /* @__PURE__ */ new Error(`Xcodebuild failed: ${error}`);
|
|
203
185
|
};
|
|
204
186
|
|
|
187
|
+
//#endregion
|
|
188
|
+
//#region src/utils/createXcodebuildLogger.ts
|
|
189
|
+
const createXcodebuildLogger = ({ logPrefix }) => new __hot_updater_cli_tools.BuildLogger({
|
|
190
|
+
logPrefix,
|
|
191
|
+
importantLogPatterns: [
|
|
192
|
+
"error:",
|
|
193
|
+
"** BUILD FAILED **",
|
|
194
|
+
"** ARCHIVE FAILED **",
|
|
195
|
+
"The following build commands failed:",
|
|
196
|
+
"note: Building targets in dependency order",
|
|
197
|
+
"note: Target dependency graph",
|
|
198
|
+
"Running script",
|
|
199
|
+
"[CP-User]",
|
|
200
|
+
"[CP] Check Pods Manifest.lock",
|
|
201
|
+
"Build Succeeded"
|
|
202
|
+
],
|
|
203
|
+
progressStages: [
|
|
204
|
+
["Building targets in dependency order", "Target dependency graph"],
|
|
205
|
+
["Write Auxiliary File", "[CP] Check Pods Manifest.lock"],
|
|
206
|
+
[
|
|
207
|
+
"Running script",
|
|
208
|
+
"[CP-User] [RN]Check rncore",
|
|
209
|
+
"[CP-User] [Hermes] Replace Hermes",
|
|
210
|
+
"[CP-User] [RN]Check FBReactNativeSpec"
|
|
211
|
+
],
|
|
212
|
+
[
|
|
213
|
+
"Processing",
|
|
214
|
+
"ProcessProductPackaging",
|
|
215
|
+
"ProcessProductPackagingDER"
|
|
216
|
+
],
|
|
217
|
+
["Compiling"],
|
|
218
|
+
["RegisterExecutionPolicyException"],
|
|
219
|
+
["Touching"],
|
|
220
|
+
["Building library", "Create Universal Binary"],
|
|
221
|
+
["Copying"],
|
|
222
|
+
[
|
|
223
|
+
"Build Succeeded",
|
|
224
|
+
"BUILD SUCCEEDED",
|
|
225
|
+
"ARCHIVE SUCCEEDED"
|
|
226
|
+
]
|
|
227
|
+
]
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region src/utils/runXcodebuildWithLogging.ts
|
|
232
|
+
let cachedXcbeautifyAvailability = null;
|
|
233
|
+
const TERMINATION_SIGNALS = ["SIGINT", "SIGTERM"];
|
|
234
|
+
const IGNORED_PIPELINE_ERROR_CODES = ["EPIPE", "ERR_STREAM_PREMATURE_CLOSE"];
|
|
235
|
+
const isXcbeautifyAvailable = async () => {
|
|
236
|
+
if (cachedXcbeautifyAvailability !== null) return cachedXcbeautifyAvailability;
|
|
237
|
+
try {
|
|
238
|
+
await (0, execa.execa)("which", ["xcbeautify"]);
|
|
239
|
+
cachedXcbeautifyAvailability = true;
|
|
240
|
+
return true;
|
|
241
|
+
} catch {
|
|
242
|
+
cachedXcbeautifyAvailability = false;
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
const createRawXcodebuildProcess = ({ args, sourceDir }) => {
|
|
247
|
+
return (0, execa.execa)("xcodebuild", args, {
|
|
248
|
+
cwd: sourceDir,
|
|
249
|
+
all: true,
|
|
250
|
+
env: {
|
|
251
|
+
...process.env,
|
|
252
|
+
NSUnbufferedIO: "YES"
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
const createXcbeautifyProcess = ({ cwd }) => {
|
|
257
|
+
return (0, execa.execa)("xcbeautify", [], {
|
|
258
|
+
cwd,
|
|
259
|
+
all: true
|
|
260
|
+
});
|
|
261
|
+
};
|
|
262
|
+
const getErrorCode = (error) => {
|
|
263
|
+
if (typeof error !== "object" || error === null || !("code" in error)) return "";
|
|
264
|
+
const { code } = error;
|
|
265
|
+
return typeof code === "string" ? code : "";
|
|
266
|
+
};
|
|
267
|
+
const shouldIgnorePipelineError = (error) => {
|
|
268
|
+
const errorCode = getErrorCode(error);
|
|
269
|
+
return IGNORED_PIPELINE_ERROR_CODES.includes(errorCode);
|
|
270
|
+
};
|
|
271
|
+
const createXcodebuildOutputPipingPromise = async ({ xcodebuildOutput, xcbeautifyInput }) => {
|
|
272
|
+
try {
|
|
273
|
+
return await (0, stream_promises.pipeline)(xcodebuildOutput, xcbeautifyInput);
|
|
274
|
+
} catch (error) {
|
|
275
|
+
if (shouldIgnorePipelineError(error)) return;
|
|
276
|
+
throw error;
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
const processOutputStream = async ({ outputStream, logger }) => {
|
|
280
|
+
if (!outputStream) return;
|
|
281
|
+
await logger.processStream(outputStream);
|
|
282
|
+
};
|
|
283
|
+
const runXcodebuildWithLogging = async ({ args, sourceDir, successMessage, failureMessage, logPrefix }) => {
|
|
284
|
+
const useXcbeautify = await isXcbeautifyAvailable();
|
|
285
|
+
const logger = createXcodebuildLogger({ logPrefix });
|
|
286
|
+
await logger.start();
|
|
287
|
+
const activeProcesses = [];
|
|
288
|
+
const terminateActiveProcesses = () => {
|
|
289
|
+
for (const activeProcess of activeProcesses) activeProcess.kill();
|
|
290
|
+
};
|
|
291
|
+
for (const signal of TERMINATION_SIGNALS) process.once(signal, terminateActiveProcesses);
|
|
292
|
+
try {
|
|
293
|
+
if (useXcbeautify) {
|
|
294
|
+
const xcodebuildProcess = createRawXcodebuildProcess({
|
|
295
|
+
args,
|
|
296
|
+
sourceDir
|
|
297
|
+
});
|
|
298
|
+
const xcbeautifyProcess = createXcbeautifyProcess({ cwd: sourceDir });
|
|
299
|
+
activeProcesses.push(xcodebuildProcess, xcbeautifyProcess);
|
|
300
|
+
const xcodebuildOutput = xcodebuildProcess.all ?? xcodebuildProcess.stdout;
|
|
301
|
+
const xcbeautifyInput = xcbeautifyProcess.stdin;
|
|
302
|
+
if (!xcodebuildOutput || !xcbeautifyInput) throw new Error("Failed to pipe xcodebuild output into xcbeautify");
|
|
303
|
+
const pipingPromise = createXcodebuildOutputPipingPromise({
|
|
304
|
+
xcodebuildOutput,
|
|
305
|
+
xcbeautifyInput
|
|
306
|
+
});
|
|
307
|
+
await processOutputStream({
|
|
308
|
+
logger,
|
|
309
|
+
outputStream: xcbeautifyProcess.all ?? xcbeautifyProcess.stdout
|
|
310
|
+
});
|
|
311
|
+
await Promise.all([
|
|
312
|
+
xcbeautifyProcess,
|
|
313
|
+
xcodebuildProcess,
|
|
314
|
+
pipingPromise
|
|
315
|
+
]);
|
|
316
|
+
} else {
|
|
317
|
+
const xcodebuildProcess = createRawXcodebuildProcess({
|
|
318
|
+
args,
|
|
319
|
+
sourceDir
|
|
320
|
+
});
|
|
321
|
+
activeProcesses.push(xcodebuildProcess);
|
|
322
|
+
await processOutputStream({
|
|
323
|
+
logger,
|
|
324
|
+
outputStream: xcodebuildProcess.all ?? xcodebuildProcess.stdout
|
|
325
|
+
});
|
|
326
|
+
await xcodebuildProcess;
|
|
327
|
+
}
|
|
328
|
+
logger.stop(successMessage);
|
|
329
|
+
} catch (error) {
|
|
330
|
+
logger.writeError(error);
|
|
331
|
+
logger.stop(failureMessage, false);
|
|
332
|
+
throw error;
|
|
333
|
+
} finally {
|
|
334
|
+
for (const signal of TERMINATION_SIGNALS) process.off(signal, terminateActiveProcesses);
|
|
335
|
+
await logger.close();
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
|
|
205
339
|
//#endregion
|
|
206
340
|
//#region src/builder/archiveXcodeProject.ts
|
|
207
|
-
const archiveXcodeProject = async ({ sourceDir, platform,
|
|
341
|
+
const archiveXcodeProject = async ({ sourceDir, platform, installPods, destination, extraParams, configuration, xcodeScheme, xcconfig, logPrefix }) => {
|
|
208
342
|
const xcodeProject = await parseXcodeProjectInfo(sourceDir);
|
|
209
|
-
if (
|
|
343
|
+
if (installPods) await installPodsIfNeeded(sourceDir);
|
|
210
344
|
const tmpDir = await createRandomTmpDir();
|
|
211
345
|
const archiveName = `${xcodeProject.name.replace(".xcworkspace", "").replace(".xcodeproj", "")}.xcarchive`;
|
|
212
346
|
const archivePath = path.default.join(tmpDir, archiveName);
|
|
213
347
|
const archiveArgs = prepareArchiveArgs({
|
|
214
348
|
archivePath,
|
|
215
349
|
platform,
|
|
216
|
-
schemeConfig,
|
|
217
350
|
sourceDir,
|
|
218
|
-
xcodeProject
|
|
351
|
+
xcodeProject,
|
|
352
|
+
configuration,
|
|
353
|
+
extraParams,
|
|
354
|
+
destination,
|
|
355
|
+
xcconfig,
|
|
356
|
+
xcodeScheme
|
|
219
357
|
});
|
|
220
358
|
__hot_updater_cli_tools.p.log.info(`Xcode Archive Settings:
|
|
221
359
|
Project ${xcodeProject.name}
|
|
222
|
-
Scheme ${
|
|
360
|
+
Scheme ${xcodeScheme}
|
|
223
361
|
Platform ${platform}
|
|
224
362
|
Command xcodebuild ${archiveArgs.join(" ")}
|
|
225
363
|
`);
|
|
226
|
-
const logger = createXcodebuildLogger();
|
|
227
|
-
logger.start(`${xcodeProject.name} (Archive)`);
|
|
228
364
|
try {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
365
|
+
await runXcodebuildWithLogging({
|
|
366
|
+
args: archiveArgs,
|
|
367
|
+
failureMessage: "Archive failed",
|
|
368
|
+
logPrefix,
|
|
369
|
+
sourceDir,
|
|
370
|
+
successMessage: "Archive completed successfully"
|
|
371
|
+
});
|
|
232
372
|
return { archivePath };
|
|
233
373
|
} catch (error) {
|
|
234
|
-
logger.stop("Archive failed", false);
|
|
235
374
|
throw prettifyXcodebuildError(error);
|
|
236
375
|
}
|
|
237
376
|
};
|
|
238
|
-
const prepareArchiveArgs = ({ archivePath, platform,
|
|
377
|
+
const prepareArchiveArgs = ({ xcodeScheme, archivePath, platform, sourceDir, xcodeProject, configuration = "Release", xcconfig, extraParams, destination = [] }) => {
|
|
239
378
|
const args = [
|
|
240
379
|
xcodeProject.isWorkspace ? "-workspace" : "-project",
|
|
241
380
|
path.default.join(sourceDir, xcodeProject.name),
|
|
242
381
|
"-scheme",
|
|
243
|
-
|
|
382
|
+
xcodeScheme,
|
|
244
383
|
"-configuration",
|
|
245
|
-
|
|
384
|
+
configuration,
|
|
246
385
|
"archive",
|
|
247
386
|
"-archivePath",
|
|
248
387
|
archivePath,
|
|
249
388
|
`HOT_UPDATER_MIN_BUNDLE_ID=${(0, __hot_updater_plugin_core.generateMinBundleId)()}`
|
|
250
389
|
];
|
|
251
|
-
if (
|
|
252
|
-
if (
|
|
390
|
+
if (xcconfig) args.push("-xcconfig", xcconfig);
|
|
391
|
+
if (extraParams) args.push(...extraParams);
|
|
253
392
|
const resolvedDestinations = resolveDestinations({
|
|
254
|
-
destinations:
|
|
393
|
+
destinations: destination,
|
|
255
394
|
useGeneric: true
|
|
256
395
|
});
|
|
257
396
|
if (resolvedDestinations.length === 0) resolvedDestinations.push(getDefaultDestination({
|
|
@@ -259,209 +398,94 @@ const prepareArchiveArgs = ({ archivePath, platform, schemeConfig, sourceDir, xc
|
|
|
259
398
|
platform,
|
|
260
399
|
useGeneric: true
|
|
261
400
|
}));
|
|
262
|
-
for (const destination of resolvedDestinations) args.push("-destination", destination);
|
|
401
|
+
for (const destination$1 of resolvedDestinations) args.push("-destination", destination$1);
|
|
263
402
|
return args;
|
|
264
403
|
};
|
|
265
404
|
|
|
266
|
-
//#endregion
|
|
267
|
-
//#region src/builder/exportXcodeArchive.ts
|
|
268
|
-
const exportXcodeArchive = async ({ archivePath, sourceDir, exportExtraParams, exportOptionsPlist }) => {
|
|
269
|
-
const exportPath = await createRandomTmpDir();
|
|
270
|
-
const exportArgs = prepareExportArgs({
|
|
271
|
-
archivePath,
|
|
272
|
-
exportPath,
|
|
273
|
-
exportOptionsPlist,
|
|
274
|
-
exportExtraParams
|
|
275
|
-
});
|
|
276
|
-
const spinner = __hot_updater_cli_tools.p.spinner();
|
|
277
|
-
spinner.start("Exporting archive to IPA");
|
|
278
|
-
try {
|
|
279
|
-
await (0, execa.execa)("xcodebuild", exportArgs, { cwd: sourceDir });
|
|
280
|
-
spinner.stop("Archive exported successfully");
|
|
281
|
-
return { exportPath };
|
|
282
|
-
} catch (error) {
|
|
283
|
-
spinner.stop("Export failed");
|
|
284
|
-
throw prettifyXcodebuildError(error);
|
|
285
|
-
}
|
|
286
|
-
};
|
|
287
|
-
const prepareExportArgs = ({ exportPath, archivePath, exportExtraParams, exportOptionsPlist }) => {
|
|
288
|
-
const args = [
|
|
289
|
-
"-exportArchive",
|
|
290
|
-
"-archivePath",
|
|
291
|
-
archivePath,
|
|
292
|
-
"-exportPath",
|
|
293
|
-
exportPath
|
|
294
|
-
];
|
|
295
|
-
if (exportExtraParams) args.push(...exportExtraParams);
|
|
296
|
-
args.push("-exportOptionsPlist", exportOptionsPlist);
|
|
297
|
-
return args;
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
//#endregion
|
|
301
|
-
//#region src/utils/assertXcodebuildExist.ts
|
|
302
|
-
const assertXcodebuildExist = async () => {
|
|
303
|
-
try {
|
|
304
|
-
await (0, execa.execa)("which", ["xcodebuild"]);
|
|
305
|
-
} catch {
|
|
306
|
-
__hot_updater_cli_tools.p.log.error(`${__hot_updater_cli_tools.colors.blueBright("xcodebuild")} resolve failed. Ensure that xcode is installed on your machine or xcodebuild is included in your path`);
|
|
307
|
-
process.exit(1);
|
|
308
|
-
}
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
//#endregion
|
|
312
|
-
//#region src/utils/enrichNativeBuildIosScheme.ts
|
|
313
|
-
const resolveExportOptionsPlist = (exportOptionsPlist) => {
|
|
314
|
-
const resolvedExportOptionsPlist = exportOptionsPlist ? path.default.isAbsolute(exportOptionsPlist) ? exportOptionsPlist : path.default.resolve((0, __hot_updater_cli_tools.getCwd)(), exportOptionsPlist) : void 0;
|
|
315
|
-
if (resolvedExportOptionsPlist && !fs.default.existsSync(resolvedExportOptionsPlist)) {
|
|
316
|
-
__hot_updater_cli_tools.p.log.error(`exportOptionsPlist doesn't exist in ${resolvedExportOptionsPlist}`);
|
|
317
|
-
process.exit(1);
|
|
318
|
-
}
|
|
319
|
-
return resolvedExportOptionsPlist;
|
|
320
|
-
};
|
|
321
|
-
const enrichNativeBuildIosScheme = async (scheme) => {
|
|
322
|
-
return {
|
|
323
|
-
platform: "ios",
|
|
324
|
-
installPods: true,
|
|
325
|
-
configuration: "Release",
|
|
326
|
-
destination: [],
|
|
327
|
-
...scheme,
|
|
328
|
-
exportOptionsPlist: resolveExportOptionsPlist(scheme.exportOptionsPlist)
|
|
329
|
-
};
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
//#endregion
|
|
333
|
-
//#region src/buildIos.ts
|
|
334
|
-
const buildIos = async ({ schemeConfig: _schemeConfig }) => {
|
|
335
|
-
await assertXcodebuildExist();
|
|
336
|
-
const iosProjectRoot = path.default.join((0, __hot_updater_cli_tools.getCwd)(), "ios");
|
|
337
|
-
const schemeConfig = await enrichNativeBuildIosScheme(_schemeConfig);
|
|
338
|
-
const buildDirectory = await createRandomTmpDir();
|
|
339
|
-
const archiveDir = path.default.join(buildDirectory, "archive");
|
|
340
|
-
const exportDir = path.default.join(buildDirectory, "export");
|
|
341
|
-
await fs.default.promises.mkdir(archiveDir, { recursive: true });
|
|
342
|
-
const { archivePath } = await archiveXcodeProject({
|
|
343
|
-
platform: schemeConfig.platform,
|
|
344
|
-
schemeConfig,
|
|
345
|
-
sourceDir: iosProjectRoot
|
|
346
|
-
});
|
|
347
|
-
const finalArchivePath = path.default.join(archiveDir, path.default.basename(archivePath));
|
|
348
|
-
await fs.default.promises.cp(archivePath, finalArchivePath, { recursive: true });
|
|
349
|
-
const appFromArchive = extractAppFromXcarchive(finalArchivePath);
|
|
350
|
-
if (appFromArchive) {
|
|
351
|
-
await fs.default.promises.cp(appFromArchive, path.default.join(archiveDir, path.default.basename(appFromArchive)), { recursive: true });
|
|
352
|
-
__hot_updater_cli_tools.p.log.success(".app extracted from .xcarchive");
|
|
353
|
-
}
|
|
354
|
-
if (schemeConfig.exportOptionsPlist) {
|
|
355
|
-
await fs.default.promises.mkdir(exportDir, { recursive: true });
|
|
356
|
-
const { exportPath } = await exportXcodeArchive({
|
|
357
|
-
archivePath,
|
|
358
|
-
exportExtraParams: schemeConfig.exportExtraParams,
|
|
359
|
-
exportOptionsPlist: schemeConfig.exportOptionsPlist,
|
|
360
|
-
sourceDir: iosProjectRoot
|
|
361
|
-
});
|
|
362
|
-
await fs.default.promises.cp(exportPath, exportDir, { recursive: true });
|
|
363
|
-
const ipaFile = fs.default.readdirSync(exportDir).find((file) => file.endsWith(".ipa"));
|
|
364
|
-
if (ipaFile) return {
|
|
365
|
-
buildDirectory,
|
|
366
|
-
buildArtifactPath: path.default.join(exportDir, ipaFile)
|
|
367
|
-
};
|
|
368
|
-
throw new Error("IPA file not found after export");
|
|
369
|
-
}
|
|
370
|
-
return {
|
|
371
|
-
buildDirectory,
|
|
372
|
-
buildArtifactPath: finalArchivePath
|
|
373
|
-
};
|
|
374
|
-
};
|
|
375
|
-
const extractAppFromXcarchive = (archivePath) => {
|
|
376
|
-
const productsPath = path.default.join(archivePath, "Products", "Applications");
|
|
377
|
-
if (!fs.default.existsSync(productsPath)) return;
|
|
378
|
-
const appFile = fs.default.readdirSync(productsPath).find((file) => file.endsWith(".app"));
|
|
379
|
-
if (appFile) return path.default.join(productsPath, appFile);
|
|
380
|
-
__hot_updater_cli_tools.p.log.warn("Failed to extract .app from xcarchive");
|
|
381
|
-
};
|
|
382
|
-
|
|
383
405
|
//#endregion
|
|
384
406
|
//#region src/builder/buildXcodeProject.ts
|
|
385
|
-
const buildXcodeProject = async ({ sourceDir, platform,
|
|
407
|
+
const buildXcodeProject = async ({ sourceDir, platform, xcodeScheme, configuration, deviceType, logPrefix, destination = [], useGenericDestination = false, installPods, extraParams }) => {
|
|
386
408
|
const xcodeProject = await parseXcodeProjectInfo(sourceDir);
|
|
387
|
-
if (installPods
|
|
409
|
+
if (installPods) await installPodsIfNeeded(sourceDir);
|
|
388
410
|
const derivedDataPath = await createRandomTmpDir();
|
|
411
|
+
const resolvedDestinations = resolveDestinations({
|
|
412
|
+
destinations: destination,
|
|
413
|
+
useGeneric: useGenericDestination
|
|
414
|
+
});
|
|
415
|
+
if (resolvedDestinations.length === 0) resolvedDestinations.push(getDefaultDestination({
|
|
416
|
+
deviceType,
|
|
417
|
+
platform,
|
|
418
|
+
useGeneric: useGenericDestination
|
|
419
|
+
}));
|
|
389
420
|
const buildArgs = prepareBuildArgs({
|
|
390
421
|
configuration,
|
|
391
422
|
derivedDataPath,
|
|
392
423
|
deviceType,
|
|
424
|
+
resolvedDestinations,
|
|
393
425
|
extraParams,
|
|
394
426
|
platform,
|
|
395
|
-
|
|
427
|
+
xcodeScheme,
|
|
396
428
|
sourceDir,
|
|
397
|
-
udid,
|
|
398
429
|
xcodeProject
|
|
399
430
|
});
|
|
400
431
|
__hot_updater_cli_tools.p.log.info(`Xcode Build Settings:
|
|
401
432
|
Project ${xcodeProject.name}
|
|
402
|
-
Scheme ${
|
|
433
|
+
Scheme ${xcodeScheme}
|
|
403
434
|
Configuration ${configuration}
|
|
404
435
|
Platform ${platform}
|
|
405
436
|
Device Type ${deviceType}
|
|
406
437
|
Command xcodebuild ${buildArgs.join(" ")}
|
|
407
438
|
`);
|
|
408
|
-
const logger = createXcodebuildLogger();
|
|
409
|
-
logger.start(`${xcodeProject.name} (Build)`);
|
|
410
439
|
try {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
440
|
+
await runXcodebuildWithLogging({
|
|
441
|
+
args: buildArgs,
|
|
442
|
+
sourceDir,
|
|
443
|
+
logPrefix,
|
|
444
|
+
successMessage: "Build completed successfully",
|
|
445
|
+
failureMessage: "Build failed"
|
|
446
|
+
});
|
|
414
447
|
return await getBuildSettings({
|
|
415
448
|
configuration,
|
|
416
449
|
derivedDataPath,
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
scheme,
|
|
450
|
+
resolvedDestinations,
|
|
451
|
+
xcodeScheme,
|
|
420
452
|
sourceDir,
|
|
421
|
-
udid,
|
|
422
453
|
xcodeProject
|
|
423
454
|
});
|
|
424
455
|
} catch (error) {
|
|
425
|
-
logger.stop("Build failed", false);
|
|
426
456
|
throw prettifyXcodebuildError(error);
|
|
427
457
|
}
|
|
428
458
|
};
|
|
429
|
-
const prepareBuildArgs = ({ xcodeProject, sourceDir, platform,
|
|
459
|
+
const prepareBuildArgs = ({ xcodeProject, sourceDir, platform, xcodeScheme, configuration, deviceType, resolvedDestinations, derivedDataPath, extraParams }) => {
|
|
430
460
|
const sdk = deviceType === "simulator" ? platformConfigs[platform].simulatorSdk : platformConfigs[platform].deviceSdk;
|
|
431
|
-
const destination = udid ? `id=${udid}` : deviceType === "simulator" ? platformConfigs[platform].simulatorDestination : platformConfigs[platform].deviceDestination;
|
|
432
461
|
const args = [
|
|
433
462
|
xcodeProject.isWorkspace ? "-workspace" : "-project",
|
|
434
463
|
path.default.join(sourceDir, xcodeProject.name),
|
|
435
464
|
"-scheme",
|
|
436
|
-
|
|
465
|
+
xcodeScheme,
|
|
437
466
|
"-configuration",
|
|
438
467
|
configuration,
|
|
439
468
|
"-sdk",
|
|
440
469
|
sdk,
|
|
441
|
-
"-destination",
|
|
442
|
-
destination,
|
|
443
470
|
"-derivedDataPath",
|
|
444
471
|
derivedDataPath,
|
|
445
472
|
`HOT_UPDATER_MIN_BUNDLE_ID=${(0, __hot_updater_plugin_core.generateMinBundleId)()}`,
|
|
446
473
|
"build"
|
|
447
474
|
];
|
|
448
475
|
if (extraParams) args.push(...extraParams);
|
|
476
|
+
for (const dest of resolvedDestinations) args.push("-destination", dest);
|
|
449
477
|
return args;
|
|
450
478
|
};
|
|
451
|
-
const getBuildSettings = async ({ xcodeProject, sourceDir,
|
|
452
|
-
const
|
|
453
|
-
const destination = udid ? `id=${udid}` : deviceType === "simulator" ? platformConfigs[platform].simulatorDestination : platformConfigs[platform].deviceDestination;
|
|
479
|
+
const getBuildSettings = async ({ xcodeProject, sourceDir, xcodeScheme, configuration, resolvedDestinations, derivedDataPath }) => {
|
|
480
|
+
const destinationArgs = resolvedDestinations.flatMap((dest) => ["-destination", dest]);
|
|
454
481
|
const { stdout: buildSettings } = await (0, execa.execa)("xcodebuild", [
|
|
455
482
|
xcodeProject.isWorkspace ? "-workspace" : "-project",
|
|
456
483
|
xcodeProject.name,
|
|
457
484
|
"-scheme",
|
|
458
|
-
|
|
485
|
+
xcodeScheme,
|
|
459
486
|
"-configuration",
|
|
460
487
|
configuration,
|
|
461
|
-
|
|
462
|
-
sdk,
|
|
463
|
-
"-destination",
|
|
464
|
-
destination,
|
|
488
|
+
...destinationArgs,
|
|
465
489
|
"-derivedDataPath",
|
|
466
490
|
derivedDataPath,
|
|
467
491
|
"-showBuildSettings",
|
|
@@ -483,6 +507,137 @@ const getBuildSettings = async ({ xcodeProject, sourceDir, platform, scheme, con
|
|
|
483
507
|
};
|
|
484
508
|
};
|
|
485
509
|
|
|
510
|
+
//#endregion
|
|
511
|
+
//#region src/builder/exportXcodeArchive.ts
|
|
512
|
+
const exportXcodeArchive = async ({ archivePath, sourceDir, exportExtraParams, exportOptionsPlist, logPrefix }) => {
|
|
513
|
+
const exportPath = await createRandomTmpDir();
|
|
514
|
+
const exportArgs = prepareExportArgs({
|
|
515
|
+
archivePath,
|
|
516
|
+
exportPath,
|
|
517
|
+
exportOptionsPlist,
|
|
518
|
+
exportExtraParams
|
|
519
|
+
});
|
|
520
|
+
try {
|
|
521
|
+
await runXcodebuildWithLogging({
|
|
522
|
+
args: exportArgs,
|
|
523
|
+
sourceDir,
|
|
524
|
+
successMessage: "Archive exported successfully",
|
|
525
|
+
failureMessage: "Export failed",
|
|
526
|
+
logPrefix
|
|
527
|
+
});
|
|
528
|
+
return { exportPath };
|
|
529
|
+
} catch (error) {
|
|
530
|
+
throw prettifyXcodebuildError(error);
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
const prepareExportArgs = ({ exportPath, archivePath, exportExtraParams, exportOptionsPlist }) => {
|
|
534
|
+
const args = [
|
|
535
|
+
"-exportArchive",
|
|
536
|
+
"-archivePath",
|
|
537
|
+
archivePath,
|
|
538
|
+
"-exportPath",
|
|
539
|
+
exportPath
|
|
540
|
+
];
|
|
541
|
+
if (exportExtraParams) args.push(...exportExtraParams);
|
|
542
|
+
args.push("-exportOptionsPlist", exportOptionsPlist);
|
|
543
|
+
return args;
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
//#endregion
|
|
547
|
+
//#region src/utils/assertXcodebuildExist.ts
|
|
548
|
+
const assertXcodebuildExist = async () => {
|
|
549
|
+
try {
|
|
550
|
+
await (0, execa.execa)("which", ["xcodebuild"]);
|
|
551
|
+
} catch {
|
|
552
|
+
__hot_updater_cli_tools.p.log.error(`${__hot_updater_cli_tools.colors.blueBright("xcodebuild")} resolve failed. Ensure that xcode is installed on your machine or xcodebuild is included in your path`);
|
|
553
|
+
process.exit(1);
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
//#endregion
|
|
558
|
+
//#region src/buildIos.ts
|
|
559
|
+
const buildIos = async ({ schemeConfig }) => {
|
|
560
|
+
await assertXcodebuildExist();
|
|
561
|
+
const iosProjectRoot = path.default.join((0, __hot_updater_cli_tools.getCwd)(), "ios");
|
|
562
|
+
const buildDirectory = await createRandomTmpDir();
|
|
563
|
+
const archiveDir = path.default.join(buildDirectory, "archive");
|
|
564
|
+
const exportDir = path.default.join(buildDirectory, "export");
|
|
565
|
+
await fs.default.promises.mkdir(archiveDir, { recursive: true });
|
|
566
|
+
if (schemeConfig.simulator) {
|
|
567
|
+
if (schemeConfig.destination.length) {
|
|
568
|
+
__hot_updater_cli_tools.p.log.error("The scheme option 'simulator' is not compatible with the 'destination' option.");
|
|
569
|
+
process.exit(1);
|
|
570
|
+
}
|
|
571
|
+
const appDir = path.default.join(buildDirectory, "app");
|
|
572
|
+
await fs.default.promises.mkdir(appDir, { recursive: true });
|
|
573
|
+
const { appPath } = await buildXcodeProject({
|
|
574
|
+
sourceDir: iosProjectRoot,
|
|
575
|
+
logPrefix: `ios-${schemeConfig.hotUpdaterSchemeName}-build`,
|
|
576
|
+
platform: schemeConfig.platform,
|
|
577
|
+
xcodeScheme: schemeConfig.scheme,
|
|
578
|
+
configuration: schemeConfig.configuration,
|
|
579
|
+
deviceType: "simulator",
|
|
580
|
+
installPods: schemeConfig.installPods,
|
|
581
|
+
extraParams: schemeConfig.extraParams,
|
|
582
|
+
useGenericDestination: true
|
|
583
|
+
});
|
|
584
|
+
const finalAppPath = path.default.join(appDir, path.default.basename(appPath));
|
|
585
|
+
await fs.default.promises.cp(appPath, finalAppPath, { recursive: true });
|
|
586
|
+
__hot_updater_cli_tools.p.log.success(`.app file created: ${path.default.basename(appPath)}`);
|
|
587
|
+
return {
|
|
588
|
+
buildDirectory,
|
|
589
|
+
buildArtifactPath: finalAppPath
|
|
590
|
+
};
|
|
591
|
+
} else {
|
|
592
|
+
const { archivePath } = await archiveXcodeProject({
|
|
593
|
+
platform: schemeConfig.platform,
|
|
594
|
+
sourceDir: iosProjectRoot,
|
|
595
|
+
xcconfig: schemeConfig.xcconfig,
|
|
596
|
+
xcodeScheme: schemeConfig.scheme,
|
|
597
|
+
destination: schemeConfig.destination,
|
|
598
|
+
extraParams: schemeConfig.extraParams,
|
|
599
|
+
configuration: schemeConfig.configuration,
|
|
600
|
+
installPods: schemeConfig.installPods,
|
|
601
|
+
logPrefix: `ios-${schemeConfig.hotUpdaterSchemeName}-build-archive`
|
|
602
|
+
});
|
|
603
|
+
const finalArchivePath = path.default.join(archiveDir, path.default.basename(archivePath));
|
|
604
|
+
await fs.default.promises.cp(archivePath, finalArchivePath, { recursive: true });
|
|
605
|
+
const appFromArchive = extractAppFromXcarchive(finalArchivePath);
|
|
606
|
+
if (appFromArchive) {
|
|
607
|
+
await fs.default.promises.cp(appFromArchive, path.default.join(archiveDir, path.default.basename(appFromArchive)), { recursive: true });
|
|
608
|
+
__hot_updater_cli_tools.p.log.success(".app extracted from .xcarchive");
|
|
609
|
+
}
|
|
610
|
+
if (schemeConfig.exportOptionsPlist) {
|
|
611
|
+
await fs.default.promises.mkdir(exportDir, { recursive: true });
|
|
612
|
+
const { exportPath } = await exportXcodeArchive({
|
|
613
|
+
archivePath,
|
|
614
|
+
exportExtraParams: schemeConfig.exportExtraParams,
|
|
615
|
+
exportOptionsPlist: schemeConfig.exportOptionsPlist,
|
|
616
|
+
sourceDir: iosProjectRoot,
|
|
617
|
+
logPrefix: `ios-${schemeConfig.hotUpdaterSchemeName}-build-export`
|
|
618
|
+
});
|
|
619
|
+
await fs.default.promises.cp(exportPath, exportDir, { recursive: true });
|
|
620
|
+
const ipaFile = fs.default.readdirSync(exportDir).find((file) => file.endsWith(".ipa"));
|
|
621
|
+
if (ipaFile) return {
|
|
622
|
+
buildDirectory,
|
|
623
|
+
buildArtifactPath: path.default.join(exportDir, ipaFile)
|
|
624
|
+
};
|
|
625
|
+
throw new Error("IPA file not found after export");
|
|
626
|
+
}
|
|
627
|
+
return {
|
|
628
|
+
buildDirectory,
|
|
629
|
+
buildArtifactPath: finalArchivePath
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
const extractAppFromXcarchive = (archivePath) => {
|
|
634
|
+
const productsPath = path.default.join(archivePath, "Products", "Applications");
|
|
635
|
+
if (!fs.default.existsSync(productsPath)) return;
|
|
636
|
+
const appFile = fs.default.readdirSync(productsPath).find((file) => file.endsWith(".app"));
|
|
637
|
+
if (appFile) return path.default.join(productsPath, appFile);
|
|
638
|
+
__hot_updater_cli_tools.p.log.warn("Failed to extract .app from xcarchive");
|
|
639
|
+
};
|
|
640
|
+
|
|
486
641
|
//#endregion
|
|
487
642
|
//#region src/runner/deviceRunner.ts
|
|
488
643
|
const installAndLaunchOnDevice = async ({ device, appPath, options }) => {
|
|
@@ -768,10 +923,9 @@ const Device = {
|
|
|
768
923
|
|
|
769
924
|
//#endregion
|
|
770
925
|
//#region src/runIos.ts
|
|
771
|
-
const runIos = async ({ schemeConfig
|
|
926
|
+
const runIos = async ({ schemeConfig, runOption }) => {
|
|
772
927
|
const { interactive, device: deviceOption } = runOption;
|
|
773
928
|
const iosProjectPath = path.default.join((0, __hot_updater_cli_tools.getCwd)(), "ios");
|
|
774
|
-
const schemeConfig = await enrichNativeBuildIosScheme(_schemeConfig);
|
|
775
929
|
const device = await Device.selectTargetDevice({
|
|
776
930
|
platform: "ios",
|
|
777
931
|
deviceOption,
|
|
@@ -789,12 +943,13 @@ const runIos = async ({ schemeConfig: _schemeConfig, runOption }) => {
|
|
|
789
943
|
const result$1 = await buildXcodeProject({
|
|
790
944
|
sourceDir: iosProjectPath,
|
|
791
945
|
platform: schemeConfig.platform,
|
|
792
|
-
|
|
946
|
+
xcodeScheme: schemeConfig.scheme,
|
|
793
947
|
configuration: schemeConfig.configuration,
|
|
794
948
|
deviceType: selectedDevice.type,
|
|
795
|
-
|
|
949
|
+
destination: [{ id: selectedDevice.udid }],
|
|
796
950
|
installPods: schemeConfig.installPods,
|
|
797
|
-
extraParams: schemeConfig.extraParams
|
|
951
|
+
extraParams: schemeConfig.extraParams,
|
|
952
|
+
logPrefix: `ios-${schemeConfig.hotUpdaterSchemeName}-run`
|
|
798
953
|
});
|
|
799
954
|
const runnerOptions$1 = {
|
|
800
955
|
bundleIdentifier: schemeConfig.bundleIdentifier,
|
|
@@ -814,12 +969,13 @@ const runIos = async ({ schemeConfig: _schemeConfig, runOption }) => {
|
|
|
814
969
|
const result = await buildXcodeProject({
|
|
815
970
|
sourceDir: iosProjectPath,
|
|
816
971
|
platform: schemeConfig.platform,
|
|
817
|
-
|
|
972
|
+
xcodeScheme: schemeConfig.scheme,
|
|
818
973
|
configuration: schemeConfig.configuration,
|
|
819
974
|
deviceType: device.type,
|
|
820
|
-
|
|
975
|
+
destination: [{ id: device.udid }],
|
|
821
976
|
installPods: schemeConfig.installPods,
|
|
822
|
-
extraParams: schemeConfig.extraParams
|
|
977
|
+
extraParams: schemeConfig.extraParams,
|
|
978
|
+
logPrefix: `ios-${schemeConfig.hotUpdaterSchemeName}-run`
|
|
823
979
|
});
|
|
824
980
|
const runnerOptions = {
|
|
825
981
|
bundleIdentifier: schemeConfig.bundleIdentifier,
|
|
@@ -843,6 +999,30 @@ const runIos = async ({ schemeConfig: _schemeConfig, runOption }) => {
|
|
|
843
999
|
return result;
|
|
844
1000
|
};
|
|
845
1001
|
|
|
1002
|
+
//#endregion
|
|
1003
|
+
//#region src/utils/enrichNativeBuildIosScheme.ts
|
|
1004
|
+
const resolveExportOptionsPlist = (exportOptionsPlist) => {
|
|
1005
|
+
const resolvedExportOptionsPlist = exportOptionsPlist ? path.default.isAbsolute(exportOptionsPlist) ? exportOptionsPlist : path.default.resolve((0, __hot_updater_cli_tools.getCwd)(), exportOptionsPlist) : void 0;
|
|
1006
|
+
if (resolvedExportOptionsPlist && !fs.default.existsSync(resolvedExportOptionsPlist)) {
|
|
1007
|
+
__hot_updater_cli_tools.p.log.error(`exportOptionsPlist doesn't exist in ${resolvedExportOptionsPlist}`);
|
|
1008
|
+
process.exit(1);
|
|
1009
|
+
}
|
|
1010
|
+
return resolvedExportOptionsPlist;
|
|
1011
|
+
};
|
|
1012
|
+
const enrichNativeBuildIosScheme = async ({ scheme, hotUpdaterSchemeName }) => {
|
|
1013
|
+
return {
|
|
1014
|
+
platform: "ios",
|
|
1015
|
+
installPods: false,
|
|
1016
|
+
configuration: "Release",
|
|
1017
|
+
destination: [],
|
|
1018
|
+
simulator: false,
|
|
1019
|
+
...scheme,
|
|
1020
|
+
exportOptionsPlist: resolveExportOptionsPlist(scheme.exportOptionsPlist),
|
|
1021
|
+
hotUpdaterSchemeName
|
|
1022
|
+
};
|
|
1023
|
+
};
|
|
1024
|
+
|
|
846
1025
|
//#endregion
|
|
847
1026
|
exports.buildIos = buildIos;
|
|
1027
|
+
exports.enrichNativeBuildIosScheme = enrichNativeBuildIosScheme;
|
|
848
1028
|
exports.runIos = runIos;
|