@ai-development-environment/control-agent 0.0.97 → 0.0.98
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/control-agent.js +594 -113
- package/package.json +1 -1
package/dist/control-agent.js
CHANGED
|
@@ -3225,7 +3225,7 @@ var require_stream = __commonJS({
|
|
|
3225
3225
|
};
|
|
3226
3226
|
duplex._final = function(callback) {
|
|
3227
3227
|
if (ws.readyState === ws.CONNECTING) {
|
|
3228
|
-
ws.once("open", function
|
|
3228
|
+
ws.once("open", function open6() {
|
|
3229
3229
|
duplex._final(callback);
|
|
3230
3230
|
});
|
|
3231
3231
|
return;
|
|
@@ -3246,7 +3246,7 @@ var require_stream = __commonJS({
|
|
|
3246
3246
|
};
|
|
3247
3247
|
duplex._write = function(chunk, encoding, callback) {
|
|
3248
3248
|
if (ws.readyState === ws.CONNECTING) {
|
|
3249
|
-
ws.once("open", function
|
|
3249
|
+
ws.once("open", function open6() {
|
|
3250
3250
|
duplex._write(chunk, encoding, callback);
|
|
3251
3251
|
});
|
|
3252
3252
|
return;
|
|
@@ -4280,7 +4280,7 @@ async function saveConfig(config, path = configPath()) {
|
|
|
4280
4280
|
|
|
4281
4281
|
// src/graphql-client.ts
|
|
4282
4282
|
import { createReadStream } from "node:fs";
|
|
4283
|
-
import { rm, stat } from "node:fs/promises";
|
|
4283
|
+
import { open, rm, stat } from "node:fs/promises";
|
|
4284
4284
|
import { createHash } from "node:crypto";
|
|
4285
4285
|
import { Readable } from "node:stream";
|
|
4286
4286
|
|
|
@@ -4988,6 +4988,39 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
|
4988
4988
|
var wrapper_default = import_websocket.default;
|
|
4989
4989
|
|
|
4990
4990
|
// src/graphql-client.ts
|
|
4991
|
+
async function writeArtifactTransferBytes(writer, bytes, position) {
|
|
4992
|
+
let written = 0;
|
|
4993
|
+
while (written < bytes.byteLength) {
|
|
4994
|
+
const remaining = bytes.byteLength - written;
|
|
4995
|
+
const { bytesWritten } = await writer.write(
|
|
4996
|
+
bytes,
|
|
4997
|
+
written,
|
|
4998
|
+
remaining,
|
|
4999
|
+
position + written
|
|
5000
|
+
);
|
|
5001
|
+
if (!Number.isSafeInteger(bytesWritten) || bytesWritten <= 0 || bytesWritten > remaining) {
|
|
5002
|
+
throw new Error("Artifact transfer file write made no progress");
|
|
5003
|
+
}
|
|
5004
|
+
written += bytesWritten;
|
|
5005
|
+
}
|
|
5006
|
+
}
|
|
5007
|
+
async function artifactTransferRetryDelay(attempt, signal) {
|
|
5008
|
+
await new Promise((resolve12, reject) => {
|
|
5009
|
+
const timeout = setTimeout(
|
|
5010
|
+
() => {
|
|
5011
|
+
signal.removeEventListener("abort", abort);
|
|
5012
|
+
resolve12();
|
|
5013
|
+
},
|
|
5014
|
+
Math.min(4e3, 250 * 2 ** attempt)
|
|
5015
|
+
);
|
|
5016
|
+
const abort = () => {
|
|
5017
|
+
clearTimeout(timeout);
|
|
5018
|
+
reject(signal.reason ?? new Error("Artifact transfer was cancelled"));
|
|
5019
|
+
};
|
|
5020
|
+
if (signal.aborted) abort();
|
|
5021
|
+
else signal.addEventListener("abort", abort, { once: true });
|
|
5022
|
+
});
|
|
5023
|
+
}
|
|
4991
5024
|
var RUN_COMMAND_FIELDS = `
|
|
4992
5025
|
id runId agentId sequence type payload status error
|
|
4993
5026
|
run {
|
|
@@ -5257,6 +5290,205 @@ var AgentGraphQLClient = class {
|
|
|
5257
5290
|
);
|
|
5258
5291
|
}
|
|
5259
5292
|
}
|
|
5293
|
+
artifactTransferHeaders(values = {}) {
|
|
5294
|
+
return mergeHeaders(this.headers, {
|
|
5295
|
+
...this.credential ? { authorization: `Bearer ${this.credential}` } : {},
|
|
5296
|
+
...values
|
|
5297
|
+
});
|
|
5298
|
+
}
|
|
5299
|
+
async uploadBuildArtifactTransfer(input) {
|
|
5300
|
+
const information = await stat(input.path);
|
|
5301
|
+
const base = `${this.server}/api/agent/build-artifact-transfers/${encodeURIComponent(input.transferId)}`;
|
|
5302
|
+
const metadata = await fetch(base, {
|
|
5303
|
+
method: "POST",
|
|
5304
|
+
headers: this.artifactTransferHeaders({
|
|
5305
|
+
"content-type": "application/json"
|
|
5306
|
+
}),
|
|
5307
|
+
body: JSON.stringify({
|
|
5308
|
+
uploadLength: information.size,
|
|
5309
|
+
checksum: input.checksum,
|
|
5310
|
+
filename: input.filename,
|
|
5311
|
+
contentType: input.contentType
|
|
5312
|
+
}),
|
|
5313
|
+
signal: input.signal
|
|
5314
|
+
});
|
|
5315
|
+
if (!metadata.ok) {
|
|
5316
|
+
throw new Error(
|
|
5317
|
+
`Artifact transfer initialization failed: HTTP ${metadata.status} ${await metadata.text()}`
|
|
5318
|
+
);
|
|
5319
|
+
}
|
|
5320
|
+
const confirmedOffset = async () => {
|
|
5321
|
+
const response = await fetch(`${base}/upload`, {
|
|
5322
|
+
method: "HEAD",
|
|
5323
|
+
headers: this.artifactTransferHeaders(),
|
|
5324
|
+
signal: input.signal
|
|
5325
|
+
});
|
|
5326
|
+
if (!response.ok) {
|
|
5327
|
+
throw new Error(
|
|
5328
|
+
`Could not inspect artifact upload offset: HTTP ${response.status}`
|
|
5329
|
+
);
|
|
5330
|
+
}
|
|
5331
|
+
const offset = Number(response.headers.get("upload-offset"));
|
|
5332
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || offset > information.size) {
|
|
5333
|
+
throw new Error("Artifact relay returned an invalid upload offset");
|
|
5334
|
+
}
|
|
5335
|
+
return offset;
|
|
5336
|
+
};
|
|
5337
|
+
const handle = await open(input.path, "r");
|
|
5338
|
+
try {
|
|
5339
|
+
let offset = await confirmedOffset();
|
|
5340
|
+
const buffer = Buffer.allocUnsafe(16 * 1024 * 1024);
|
|
5341
|
+
while (offset < information.size) {
|
|
5342
|
+
if (input.signal.aborted) throw input.signal.reason;
|
|
5343
|
+
const length = Math.min(buffer.length, information.size - offset);
|
|
5344
|
+
const { bytesRead } = await handle.read(buffer, 0, length, offset);
|
|
5345
|
+
if (bytesRead !== length)
|
|
5346
|
+
throw new Error("Artifact changed while uploading");
|
|
5347
|
+
let attempt = 0;
|
|
5348
|
+
while (true) {
|
|
5349
|
+
let failure;
|
|
5350
|
+
try {
|
|
5351
|
+
const response = await fetch(`${base}/upload`, {
|
|
5352
|
+
method: "PATCH",
|
|
5353
|
+
headers: this.artifactTransferHeaders({
|
|
5354
|
+
"content-length": String(bytesRead),
|
|
5355
|
+
"content-type": "application/offset+octet-stream",
|
|
5356
|
+
"upload-offset": String(offset)
|
|
5357
|
+
}),
|
|
5358
|
+
body: buffer.subarray(0, bytesRead),
|
|
5359
|
+
signal: input.signal
|
|
5360
|
+
});
|
|
5361
|
+
if (response.ok) {
|
|
5362
|
+
const nextOffset = Number(response.headers.get("upload-offset"));
|
|
5363
|
+
if (Number.isSafeInteger(nextOffset) && nextOffset > offset && nextOffset <= information.size) {
|
|
5364
|
+
offset = nextOffset;
|
|
5365
|
+
break;
|
|
5366
|
+
}
|
|
5367
|
+
failure = new Error(
|
|
5368
|
+
"Artifact relay returned an invalid upload offset"
|
|
5369
|
+
);
|
|
5370
|
+
} else {
|
|
5371
|
+
failure = new Error(
|
|
5372
|
+
`Artifact chunk upload failed: HTTP ${response.status} ${await response.text()}`
|
|
5373
|
+
);
|
|
5374
|
+
}
|
|
5375
|
+
} catch (error) {
|
|
5376
|
+
if (input.signal.aborted) throw error;
|
|
5377
|
+
failure = error instanceof Error ? error : new Error(String(error));
|
|
5378
|
+
}
|
|
5379
|
+
if (++attempt >= 5) throw failure;
|
|
5380
|
+
await artifactTransferRetryDelay(attempt, input.signal);
|
|
5381
|
+
const resumedOffset = await confirmedOffset();
|
|
5382
|
+
if (resumedOffset !== offset) {
|
|
5383
|
+
offset = resumedOffset;
|
|
5384
|
+
break;
|
|
5385
|
+
}
|
|
5386
|
+
}
|
|
5387
|
+
}
|
|
5388
|
+
} finally {
|
|
5389
|
+
await handle.close();
|
|
5390
|
+
}
|
|
5391
|
+
const completion = await fetch(`${base}/complete`, {
|
|
5392
|
+
method: "POST",
|
|
5393
|
+
headers: this.artifactTransferHeaders(),
|
|
5394
|
+
signal: input.signal
|
|
5395
|
+
});
|
|
5396
|
+
if (!completion.ok) {
|
|
5397
|
+
throw new Error(
|
|
5398
|
+
`Artifact transfer completion failed: HTTP ${completion.status} ${await completion.text()}`
|
|
5399
|
+
);
|
|
5400
|
+
}
|
|
5401
|
+
}
|
|
5402
|
+
async downloadBuildArtifactTransfer(input) {
|
|
5403
|
+
const base = `${this.server}/api/agent/build-artifact-transfers/${encodeURIComponent(input.transferId)}`;
|
|
5404
|
+
let metadata;
|
|
5405
|
+
while (true) {
|
|
5406
|
+
const response = await fetch(base, {
|
|
5407
|
+
headers: this.artifactTransferHeaders(),
|
|
5408
|
+
signal: input.signal
|
|
5409
|
+
});
|
|
5410
|
+
if (!response.ok) {
|
|
5411
|
+
throw new Error(
|
|
5412
|
+
`Could not inspect artifact transfer: HTTP ${response.status} ${await response.text()}`
|
|
5413
|
+
);
|
|
5414
|
+
}
|
|
5415
|
+
metadata = await response.json();
|
|
5416
|
+
if (["READY", "DOWNLOADING", "SUCCEEDED"].includes(metadata.status))
|
|
5417
|
+
break;
|
|
5418
|
+
if (["FAILED", "CANCELLED", "EXPIRED"].includes(metadata.status)) {
|
|
5419
|
+
throw new Error(
|
|
5420
|
+
metadata.error || `Artifact transfer ${metadata.status.toLowerCase()}`
|
|
5421
|
+
);
|
|
5422
|
+
}
|
|
5423
|
+
await new Promise((resolve12) => setTimeout(resolve12, 1e3));
|
|
5424
|
+
}
|
|
5425
|
+
if (!Number.isSafeInteger(metadata.uploadLength) || !metadata.uploadLength || !metadata.checksum || !metadata.filename) {
|
|
5426
|
+
throw new Error("Artifact transfer metadata is incomplete");
|
|
5427
|
+
}
|
|
5428
|
+
let offset = 0;
|
|
5429
|
+
try {
|
|
5430
|
+
offset = (await stat(input.path)).size;
|
|
5431
|
+
} catch {
|
|
5432
|
+
}
|
|
5433
|
+
if (offset > metadata.uploadLength) {
|
|
5434
|
+
await rm(input.path, { force: true });
|
|
5435
|
+
offset = 0;
|
|
5436
|
+
}
|
|
5437
|
+
const handle = await open(input.path, offset === 0 ? "w" : "r+");
|
|
5438
|
+
try {
|
|
5439
|
+
while (offset < metadata.uploadLength) {
|
|
5440
|
+
const end = Math.min(
|
|
5441
|
+
metadata.uploadLength - 1,
|
|
5442
|
+
offset + 16 * 1024 * 1024 - 1
|
|
5443
|
+
);
|
|
5444
|
+
let bytes = null;
|
|
5445
|
+
let attempt = 0;
|
|
5446
|
+
while (!bytes) {
|
|
5447
|
+
let failure = new Error("Artifact range download failed");
|
|
5448
|
+
try {
|
|
5449
|
+
const response = await fetch(`${base}/download`, {
|
|
5450
|
+
headers: this.artifactTransferHeaders({
|
|
5451
|
+
range: `bytes=${offset}-${end}`
|
|
5452
|
+
}),
|
|
5453
|
+
signal: input.signal
|
|
5454
|
+
});
|
|
5455
|
+
if (response.status !== 206) {
|
|
5456
|
+
failure = new Error(
|
|
5457
|
+
`Artifact range download failed: HTTP ${response.status} ${await response.text()}`
|
|
5458
|
+
);
|
|
5459
|
+
} else {
|
|
5460
|
+
const received = Buffer.from(await response.arrayBuffer());
|
|
5461
|
+
if (received.length === end - offset + 1) bytes = received;
|
|
5462
|
+
else failure = new Error("Artifact range length did not match");
|
|
5463
|
+
}
|
|
5464
|
+
} catch (error) {
|
|
5465
|
+
if (input.signal.aborted) throw error;
|
|
5466
|
+
failure = error instanceof Error ? error : new Error(String(error));
|
|
5467
|
+
}
|
|
5468
|
+
if (bytes) break;
|
|
5469
|
+
if (++attempt >= 5) throw failure;
|
|
5470
|
+
await artifactTransferRetryDelay(attempt, input.signal);
|
|
5471
|
+
}
|
|
5472
|
+
await writeArtifactTransferBytes(handle, bytes, offset);
|
|
5473
|
+
offset += bytes.length;
|
|
5474
|
+
}
|
|
5475
|
+
await handle.sync();
|
|
5476
|
+
} finally {
|
|
5477
|
+
await handle.close();
|
|
5478
|
+
}
|
|
5479
|
+
const digest = createHash("sha256");
|
|
5480
|
+
for await (const chunk of createReadStream(input.path))
|
|
5481
|
+
digest.update(chunk);
|
|
5482
|
+
if (digest.digest("hex") !== metadata.checksum) {
|
|
5483
|
+
await rm(input.path, { force: true });
|
|
5484
|
+
throw new Error("Downloaded artifact checksum did not match");
|
|
5485
|
+
}
|
|
5486
|
+
return {
|
|
5487
|
+
filename: metadata.filename,
|
|
5488
|
+
contentType: metadata.contentType ?? "application/octet-stream",
|
|
5489
|
+
checksum: metadata.checksum
|
|
5490
|
+
};
|
|
5491
|
+
}
|
|
5260
5492
|
completeJob(jobId, status2, result, error) {
|
|
5261
5493
|
return this.request(
|
|
5262
5494
|
`mutation CompleteJob($jobId: ID!, $status: AgentJobStatus!, $result: JSON, $error: String) {
|
|
@@ -7423,6 +7655,9 @@ var IOS_BUILD_JOB_KIND = "ios.build.run";
|
|
|
7423
7655
|
var IOS_BUILD_DELETE_JOB_KIND = "ios.build.delete";
|
|
7424
7656
|
var IOS_ARTIFACT_DOWNLOAD_JOB_KIND = "ios.artifact.download";
|
|
7425
7657
|
var IOS_DEPLOY_JOB_KIND = "ios.build.deploy";
|
|
7658
|
+
var IOS_AGENT_RUN_DESTINATIONS_JOB_KIND = "ios.agentRunDestinations.inspect";
|
|
7659
|
+
var IOS_ARTIFACT_TRANSFER_UPLOAD_JOB_KIND = "ios.artifact.transfer.upload";
|
|
7660
|
+
var IOS_REMOTE_DEPLOY_JOB_KIND = "ios.build.remoteDeploy";
|
|
7426
7661
|
var IOS_EXPORT_JOB_KIND = "ios.archive.export";
|
|
7427
7662
|
var IOS_TEST_RESULTS_JOB_KIND = "ios.test-results.parse";
|
|
7428
7663
|
var IOS_COVERAGE_REPORT_JOB_KIND = "ios.coverage.generate";
|
|
@@ -7436,6 +7671,9 @@ var IOS_BUILD_JOB_KINDS = [
|
|
|
7436
7671
|
IOS_BUILD_DELETE_JOB_KIND,
|
|
7437
7672
|
IOS_ARTIFACT_DOWNLOAD_JOB_KIND,
|
|
7438
7673
|
IOS_DEPLOY_JOB_KIND,
|
|
7674
|
+
IOS_AGENT_RUN_DESTINATIONS_JOB_KIND,
|
|
7675
|
+
IOS_ARTIFACT_TRANSFER_UPLOAD_JOB_KIND,
|
|
7676
|
+
IOS_REMOTE_DEPLOY_JOB_KIND,
|
|
7439
7677
|
IOS_EXPORT_JOB_KIND,
|
|
7440
7678
|
IOS_TEST_RESULTS_JOB_KIND,
|
|
7441
7679
|
IOS_COVERAGE_REPORT_JOB_KIND,
|
|
@@ -7476,6 +7714,31 @@ var APPROVED_BUILD_SETTING_OVERRIDES = [
|
|
|
7476
7714
|
"MARKETING_VERSION",
|
|
7477
7715
|
"CURRENT_PROJECT_VERSION"
|
|
7478
7716
|
];
|
|
7717
|
+
function genericBuildDestinations(action) {
|
|
7718
|
+
if (!GENERIC_BUILD_DESTINATION_ACTIONS.includes(action)) return [];
|
|
7719
|
+
const physical = {
|
|
7720
|
+
type: "PHYSICAL_DEVICE",
|
|
7721
|
+
id: "generic-ios",
|
|
7722
|
+
name: "Any Physical iOS Device",
|
|
7723
|
+
platform: "iOS",
|
|
7724
|
+
osVersion: null,
|
|
7725
|
+
state: null,
|
|
7726
|
+
generic: true
|
|
7727
|
+
};
|
|
7728
|
+
if (action === "ARCHIVE") return [physical];
|
|
7729
|
+
return [
|
|
7730
|
+
{
|
|
7731
|
+
type: "SIMULATOR",
|
|
7732
|
+
id: "generic-ios-simulator",
|
|
7733
|
+
name: "Any iOS Simulator",
|
|
7734
|
+
platform: "iOS Simulator",
|
|
7735
|
+
osVersion: null,
|
|
7736
|
+
state: null,
|
|
7737
|
+
generic: true
|
|
7738
|
+
},
|
|
7739
|
+
physical
|
|
7740
|
+
];
|
|
7741
|
+
}
|
|
7479
7742
|
var DEFAULT_BUILD_ADVANCED_SETTINGS = {
|
|
7480
7743
|
packageResolution: "DEFAULT",
|
|
7481
7744
|
disablePackageRepositoryCache: false,
|
|
@@ -7592,6 +7855,12 @@ function parseBuildDestination(value) {
|
|
|
7592
7855
|
destination.generic,
|
|
7593
7856
|
"build destination.generic"
|
|
7594
7857
|
)
|
|
7858
|
+
},
|
|
7859
|
+
...destination.available === void 0 ? {} : {
|
|
7860
|
+
available: booleanValue2(
|
|
7861
|
+
destination.available,
|
|
7862
|
+
"build destination.available"
|
|
7863
|
+
)
|
|
7595
7864
|
}
|
|
7596
7865
|
};
|
|
7597
7866
|
}
|
|
@@ -7925,13 +8194,50 @@ function parseBuildArtifactDownloadPayload(value) {
|
|
|
7925
8194
|
)
|
|
7926
8195
|
};
|
|
7927
8196
|
}
|
|
7928
|
-
function
|
|
7929
|
-
const input = objectValue7(value, "build
|
|
8197
|
+
function parseBuildArtifactTransferUploadPayload(value) {
|
|
8198
|
+
const input = objectValue7(value, "build artifact transfer upload payload");
|
|
8199
|
+
const buildId = stringValue7(
|
|
8200
|
+
input.buildId,
|
|
8201
|
+
"build artifact transfer upload payload.buildId"
|
|
8202
|
+
);
|
|
8203
|
+
const artifactDirectory = safeAbsolutePath(
|
|
8204
|
+
input.artifactDirectory,
|
|
8205
|
+
"build artifact transfer upload payload.artifactDirectory"
|
|
8206
|
+
);
|
|
8207
|
+
if (artifactDirectory.split("/").at(-1) !== buildId) {
|
|
8208
|
+
throw new Error(
|
|
8209
|
+
"Build artifact transfer folder must end with the build ID"
|
|
8210
|
+
);
|
|
8211
|
+
}
|
|
8212
|
+
return {
|
|
8213
|
+
transferId: stringValue7(
|
|
8214
|
+
input.transferId,
|
|
8215
|
+
"build artifact transfer upload payload.transferId"
|
|
8216
|
+
),
|
|
8217
|
+
buildId,
|
|
8218
|
+
artifactDirectory,
|
|
8219
|
+
artifactRelativePath: safeRelativePath2(
|
|
8220
|
+
input.artifactRelativePath,
|
|
8221
|
+
"build artifact transfer upload payload.artifactRelativePath"
|
|
8222
|
+
)
|
|
8223
|
+
};
|
|
8224
|
+
}
|
|
8225
|
+
function parseBuildAgentRunDestinationsPayload(value) {
|
|
8226
|
+
const input = objectValue7(value, "agent run destinations payload");
|
|
8227
|
+
return {
|
|
8228
|
+
destinationType: enumValue3(
|
|
8229
|
+
input.destinationType,
|
|
8230
|
+
BUILD_DESTINATION_TYPES,
|
|
8231
|
+
"agent run destinations payload.destinationType"
|
|
8232
|
+
)
|
|
8233
|
+
};
|
|
8234
|
+
}
|
|
8235
|
+
function parseDeployments(input) {
|
|
7930
8236
|
if (!Array.isArray(input.deployments) || !input.deployments.length) {
|
|
7931
8237
|
throw new Error("build deployment payload.deployments must not be empty");
|
|
7932
8238
|
}
|
|
7933
|
-
const deployments = input.deployments.map((
|
|
7934
|
-
const deployment = objectValue7(
|
|
8239
|
+
const deployments = input.deployments.map((value, index) => {
|
|
8240
|
+
const deployment = objectValue7(value, `deployments[${index}]`);
|
|
7935
8241
|
return {
|
|
7936
8242
|
id: stringValue7(deployment.id, `deployments[${index}].id`),
|
|
7937
8243
|
destination: parseBuildDestination(deployment.destination)
|
|
@@ -7950,6 +8256,11 @@ function parseBuildDeploymentPayload(value) {
|
|
|
7950
8256
|
if (new Set(deployments.map(({ destination }) => destination.type)).size > 1) {
|
|
7951
8257
|
throw new Error("deployment destinations must use one destination type");
|
|
7952
8258
|
}
|
|
8259
|
+
return deployments;
|
|
8260
|
+
}
|
|
8261
|
+
function parseBuildDeploymentPayload(value) {
|
|
8262
|
+
const input = objectValue7(value, "build deployment payload");
|
|
8263
|
+
const deployments = parseDeployments(input);
|
|
7953
8264
|
return {
|
|
7954
8265
|
...worktreeIdentity(input),
|
|
7955
8266
|
buildId: stringValue7(input.buildId, "build deployment payload.buildId"),
|
|
@@ -7968,6 +8279,24 @@ function parseBuildDeploymentPayload(value) {
|
|
|
7968
8279
|
deployments
|
|
7969
8280
|
};
|
|
7970
8281
|
}
|
|
8282
|
+
function parseBuildRemoteDeploymentPayload(value) {
|
|
8283
|
+
const input = objectValue7(value, "remote build deployment payload");
|
|
8284
|
+
return {
|
|
8285
|
+
transferId: stringValue7(
|
|
8286
|
+
input.transferId,
|
|
8287
|
+
"remote build deployment payload.transferId"
|
|
8288
|
+
),
|
|
8289
|
+
buildId: stringValue7(
|
|
8290
|
+
input.buildId,
|
|
8291
|
+
"remote build deployment payload.buildId"
|
|
8292
|
+
),
|
|
8293
|
+
bundleIdentifier: stringValue7(
|
|
8294
|
+
input.bundleIdentifier,
|
|
8295
|
+
"remote build deployment payload.bundleIdentifier"
|
|
8296
|
+
),
|
|
8297
|
+
deployments: parseDeployments(input)
|
|
8298
|
+
};
|
|
8299
|
+
}
|
|
7971
8300
|
function parseBuildExportSettings(value) {
|
|
7972
8301
|
const input = objectValue7(value, "export settings");
|
|
7973
8302
|
const profiles = objectValue7(
|
|
@@ -9242,7 +9571,7 @@ import {
|
|
|
9242
9571
|
cp,
|
|
9243
9572
|
mkdir as mkdir2,
|
|
9244
9573
|
mkdtemp as mkdtemp2,
|
|
9245
|
-
open,
|
|
9574
|
+
open as open2,
|
|
9246
9575
|
readdir,
|
|
9247
9576
|
readFile as readFile2,
|
|
9248
9577
|
realpath as realpath2,
|
|
@@ -9637,7 +9966,7 @@ function command(executable, args, timeoutMs, signal, cwd, env = process.env) {
|
|
|
9637
9966
|
});
|
|
9638
9967
|
}
|
|
9639
9968
|
async function commandToFile(executable, args, timeoutMs, signal, cwd, env, destination) {
|
|
9640
|
-
const output = await
|
|
9969
|
+
const output = await open2(destination, "wx", 384);
|
|
9641
9970
|
try {
|
|
9642
9971
|
return await captureCommand({
|
|
9643
9972
|
command: executable,
|
|
@@ -10147,7 +10476,7 @@ var parseBuildSourceMetadata = async (payload, timeoutMs, signal) => {
|
|
|
10147
10476
|
headSha: input.headSha
|
|
10148
10477
|
};
|
|
10149
10478
|
};
|
|
10150
|
-
function simulatorDestinations(value) {
|
|
10479
|
+
function simulatorDestinations(value, includeUnavailable = false) {
|
|
10151
10480
|
if (!value || typeof value !== "object" || Array.isArray(value)) return [];
|
|
10152
10481
|
const devices = value.devices;
|
|
10153
10482
|
if (!devices || typeof devices !== "object" || Array.isArray(devices))
|
|
@@ -10160,7 +10489,8 @@ function simulatorDestinations(value) {
|
|
|
10160
10489
|
for (const raw of entries) {
|
|
10161
10490
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
10162
10491
|
const device = raw;
|
|
10163
|
-
|
|
10492
|
+
const available = device.isAvailable === true;
|
|
10493
|
+
if (!available && !includeUnavailable || typeof device.udid !== "string")
|
|
10164
10494
|
continue;
|
|
10165
10495
|
result.push({
|
|
10166
10496
|
type: "SIMULATOR",
|
|
@@ -10168,7 +10498,8 @@ function simulatorDestinations(value) {
|
|
|
10168
10498
|
name: typeof device.name === "string" ? device.name : device.udid,
|
|
10169
10499
|
platform: "iOS Simulator",
|
|
10170
10500
|
osVersion,
|
|
10171
|
-
state: typeof device.state === "string" ? device.state : null
|
|
10501
|
+
state: typeof device.state === "string" ? device.state : available ? null : "Unavailable",
|
|
10502
|
+
available
|
|
10172
10503
|
});
|
|
10173
10504
|
}
|
|
10174
10505
|
}
|
|
@@ -10188,7 +10519,7 @@ function nestedString(value, paths) {
|
|
|
10188
10519
|
}
|
|
10189
10520
|
return null;
|
|
10190
10521
|
}
|
|
10191
|
-
function physicalDestinations(value) {
|
|
10522
|
+
function physicalDestinations(value, includeUnavailable = false) {
|
|
10192
10523
|
if (!value || typeof value !== "object" || Array.isArray(value)) return [];
|
|
10193
10524
|
const result = value.result;
|
|
10194
10525
|
const devices = result && typeof result === "object" && !Array.isArray(result) ? result.devices : null;
|
|
@@ -10209,7 +10540,8 @@ function physicalDestinations(value) {
|
|
|
10209
10540
|
["connectionProperties", "tunnelState"],
|
|
10210
10541
|
["deviceProperties", "connectionState"]
|
|
10211
10542
|
]);
|
|
10212
|
-
|
|
10543
|
+
const available = !(state && /(disconnected|unavailable|not.?connected|offline)/i.test(state));
|
|
10544
|
+
if (!available && !includeUnavailable) {
|
|
10213
10545
|
return [];
|
|
10214
10546
|
}
|
|
10215
10547
|
return [
|
|
@@ -10222,37 +10554,13 @@ function physicalDestinations(value) {
|
|
|
10222
10554
|
["deviceProperties", "osVersionNumber"],
|
|
10223
10555
|
["deviceProperties", "osVersion"]
|
|
10224
10556
|
]),
|
|
10225
|
-
state
|
|
10557
|
+
state,
|
|
10558
|
+
available
|
|
10226
10559
|
}
|
|
10227
10560
|
];
|
|
10228
10561
|
});
|
|
10229
10562
|
}
|
|
10230
|
-
function
|
|
10231
|
-
if (!GENERIC_BUILD_DESTINATION_ACTIONS.includes(action)) return [];
|
|
10232
|
-
const physical = {
|
|
10233
|
-
type: "PHYSICAL_DEVICE",
|
|
10234
|
-
id: "generic-ios",
|
|
10235
|
-
name: "Any Physical iOS Device",
|
|
10236
|
-
platform: "iOS",
|
|
10237
|
-
osVersion: null,
|
|
10238
|
-
state: null,
|
|
10239
|
-
generic: true
|
|
10240
|
-
};
|
|
10241
|
-
if (action === "ARCHIVE") return [physical];
|
|
10242
|
-
return [
|
|
10243
|
-
{
|
|
10244
|
-
type: "SIMULATOR",
|
|
10245
|
-
id: "generic-ios-simulator",
|
|
10246
|
-
name: "Any iOS Simulator",
|
|
10247
|
-
platform: "iOS Simulator",
|
|
10248
|
-
osVersion: null,
|
|
10249
|
-
state: null,
|
|
10250
|
-
generic: true
|
|
10251
|
-
},
|
|
10252
|
-
physical
|
|
10253
|
-
];
|
|
10254
|
-
}
|
|
10255
|
-
async function listPhysicalDevices(timeoutMs, signal) {
|
|
10563
|
+
async function listPhysicalDevices(timeoutMs, signal, includeUnavailable = false) {
|
|
10256
10564
|
const directory = await mkdtemp2(join3(tmpdir2(), "ade-devices-"));
|
|
10257
10565
|
const output = join3(directory, "devices.json");
|
|
10258
10566
|
try {
|
|
@@ -10272,7 +10580,10 @@ async function listPhysicalDevices(timeoutMs, signal) {
|
|
|
10272
10580
|
signal
|
|
10273
10581
|
);
|
|
10274
10582
|
if (result.exitCode !== 0) return [];
|
|
10275
|
-
return physicalDestinations(
|
|
10583
|
+
return physicalDestinations(
|
|
10584
|
+
JSON.parse(await readFile2(output, "utf8")),
|
|
10585
|
+
includeUnavailable
|
|
10586
|
+
);
|
|
10276
10587
|
} finally {
|
|
10277
10588
|
await rm3(directory, { recursive: true, force: true });
|
|
10278
10589
|
}
|
|
@@ -10280,24 +10591,7 @@ async function listPhysicalDevices(timeoutMs, signal) {
|
|
|
10280
10591
|
var inspectBuildDestinations = async (payload, timeoutMs, signal) => {
|
|
10281
10592
|
const input = parseBuildDestinationsPayload(payload);
|
|
10282
10593
|
const folder = await validateWorktree(input, timeoutMs, signal);
|
|
10283
|
-
|
|
10284
|
-
const preflight = await command(
|
|
10285
|
-
"xcrun",
|
|
10286
|
-
[
|
|
10287
|
-
"xcodebuild",
|
|
10288
|
-
...sourceArguments(input.source, absolutePath),
|
|
10289
|
-
"-scheme",
|
|
10290
|
-
input.scheme,
|
|
10291
|
-
"-configuration",
|
|
10292
|
-
input.configuration,
|
|
10293
|
-
"-showBuildSettings",
|
|
10294
|
-
"-json"
|
|
10295
|
-
],
|
|
10296
|
-
Math.min(timeoutMs, 6e4),
|
|
10297
|
-
signal,
|
|
10298
|
-
folder
|
|
10299
|
-
);
|
|
10300
|
-
requireSuccess(preflight, "The saved scheme or configuration is unavailable");
|
|
10594
|
+
await validateSource(folder, input.source);
|
|
10301
10595
|
const [simulators, physical] = await Promise.all([
|
|
10302
10596
|
command(
|
|
10303
10597
|
"xcrun",
|
|
@@ -10323,7 +10617,7 @@ var inspectBuildRunDestinations = async (payload, timeoutMs, signal) => {
|
|
|
10323
10617
|
if (input.destinationType === "SIMULATOR") {
|
|
10324
10618
|
const simulators = await command(
|
|
10325
10619
|
"xcrun",
|
|
10326
|
-
["simctl", "list", "devices", "
|
|
10620
|
+
["simctl", "list", "devices", "-j"],
|
|
10327
10621
|
Math.min(timeoutMs, 3e4),
|
|
10328
10622
|
signal,
|
|
10329
10623
|
folder
|
|
@@ -10331,12 +10625,33 @@ var inspectBuildRunDestinations = async (payload, timeoutMs, signal) => {
|
|
|
10331
10625
|
requireSuccess(simulators, "Could not inspect available simulators");
|
|
10332
10626
|
return {
|
|
10333
10627
|
...successfulProcess,
|
|
10334
|
-
destinations: simulatorDestinations(JSON.parse(simulators.stdout))
|
|
10628
|
+
destinations: simulatorDestinations(JSON.parse(simulators.stdout), true)
|
|
10335
10629
|
};
|
|
10336
10630
|
}
|
|
10337
10631
|
return {
|
|
10338
10632
|
...successfulProcess,
|
|
10339
|
-
destinations: await listPhysicalDevices(timeoutMs, signal)
|
|
10633
|
+
destinations: await listPhysicalDevices(timeoutMs, signal, true)
|
|
10634
|
+
};
|
|
10635
|
+
};
|
|
10636
|
+
var inspectAgentBuildRunDestinations = async (payload, timeoutMs, signal) => {
|
|
10637
|
+
const input = parseBuildAgentRunDestinationsPayload(payload);
|
|
10638
|
+
if (input.destinationType === "SIMULATOR") {
|
|
10639
|
+
const simulators = await command(
|
|
10640
|
+
"xcrun",
|
|
10641
|
+
["simctl", "list", "devices", "-j"],
|
|
10642
|
+
Math.min(timeoutMs, 3e4),
|
|
10643
|
+
signal,
|
|
10644
|
+
process.cwd()
|
|
10645
|
+
);
|
|
10646
|
+
requireSuccess(simulators, "Could not inspect available simulators");
|
|
10647
|
+
return {
|
|
10648
|
+
...successfulProcess,
|
|
10649
|
+
destinations: simulatorDestinations(JSON.parse(simulators.stdout), true)
|
|
10650
|
+
};
|
|
10651
|
+
}
|
|
10652
|
+
return {
|
|
10653
|
+
...successfulProcess,
|
|
10654
|
+
destinations: await listPhysicalDevices(timeoutMs, signal, true)
|
|
10340
10655
|
};
|
|
10341
10656
|
};
|
|
10342
10657
|
function sensitiveValues(env) {
|
|
@@ -11457,7 +11772,10 @@ async function captureArtifacts(input, folder, signal, includeProducts = true) {
|
|
|
11457
11772
|
await artifact(input.artifactDirectory, "RUNNABLE_APP", destination, {
|
|
11458
11773
|
bundleIdentifier: settings.PRODUCT_BUNDLE_IDENTIFIER ?? null,
|
|
11459
11774
|
target: apps[0].target,
|
|
11460
|
-
destinationType: input.destination.type
|
|
11775
|
+
destinationType: input.destination.type,
|
|
11776
|
+
architectures: (settings.ARCHS ?? settings.VALID_ARCHS ?? "").split(/\s+/).filter(Boolean),
|
|
11777
|
+
minimumOSVersion: settings.IPHONEOS_DEPLOYMENT_TARGET ?? null,
|
|
11778
|
+
sdkName: settings.SDK_NAME ?? null
|
|
11461
11779
|
})
|
|
11462
11780
|
);
|
|
11463
11781
|
}
|
|
@@ -11536,6 +11854,7 @@ var runIosBuild = async (payload, timeoutMs, signal, onLog, context) => {
|
|
|
11536
11854
|
telemetry: input.telemetry ?? null
|
|
11537
11855
|
};
|
|
11538
11856
|
let buildResult = null;
|
|
11857
|
+
let preflightResult = null;
|
|
11539
11858
|
let coverageChanges = [];
|
|
11540
11859
|
let errorCode = null;
|
|
11541
11860
|
let error = null;
|
|
@@ -11561,29 +11880,50 @@ var runIosBuild = async (payload, timeoutMs, signal, onLog, context) => {
|
|
|
11561
11880
|
} catch (progressError) {
|
|
11562
11881
|
logger.emit("PREPARING", "SYSTEM", cleanError(progressError));
|
|
11563
11882
|
}
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
phase: "PRE_BUILD",
|
|
11573
|
-
source: script.preBuildScript,
|
|
11574
|
-
contextPath,
|
|
11575
|
-
hookContext: baseHookContext,
|
|
11883
|
+
if (!signal.aborted) {
|
|
11884
|
+
preflightResult = await runLoggedCommand({
|
|
11885
|
+
command: "xcrun",
|
|
11886
|
+
args: xcodeBuildSettingsArguments(input, folder),
|
|
11887
|
+
cwd: folder,
|
|
11888
|
+
env: xcodeEnvironment(),
|
|
11889
|
+
timeoutMs: Math.min(timeoutMs, 6e4),
|
|
11890
|
+
signal,
|
|
11576
11891
|
logger,
|
|
11577
|
-
|
|
11892
|
+
phase: "PREFLIGHT"
|
|
11578
11893
|
});
|
|
11579
|
-
|
|
11580
|
-
if (execution.causedBuildFailure) {
|
|
11894
|
+
if (preflightResult.exitCode !== 0 && !preflightResult.cancelled) {
|
|
11581
11895
|
failBuild = true;
|
|
11582
|
-
errorCode = "
|
|
11583
|
-
error =
|
|
11584
|
-
|
|
11896
|
+
errorCode = preflightResult.timedOut ? "TIMEOUT" : classifyFailure(preflightResult.output);
|
|
11897
|
+
error = cleanError(
|
|
11898
|
+
preflightResult.output || "The saved scheme, configuration, or destination is unavailable"
|
|
11899
|
+
);
|
|
11900
|
+
}
|
|
11901
|
+
}
|
|
11902
|
+
if (!failBuild && !signal.aborted) {
|
|
11903
|
+
for (const script of [...input.scripts].sort(
|
|
11904
|
+
(a, b) => a.position - b.position
|
|
11905
|
+
)) {
|
|
11906
|
+
if (!script.preBuildScript) continue;
|
|
11907
|
+
const execution = await runHook({
|
|
11908
|
+
input,
|
|
11909
|
+
folder,
|
|
11910
|
+
script,
|
|
11911
|
+
phase: "PRE_BUILD",
|
|
11912
|
+
source: script.preBuildScript,
|
|
11913
|
+
contextPath,
|
|
11914
|
+
hookContext: baseHookContext,
|
|
11915
|
+
logger,
|
|
11916
|
+
signal
|
|
11917
|
+
});
|
|
11918
|
+
scriptExecutions.push(execution);
|
|
11919
|
+
if (execution.causedBuildFailure) {
|
|
11920
|
+
failBuild = true;
|
|
11921
|
+
errorCode = "SCRIPT_FAILED";
|
|
11922
|
+
error = execution.error;
|
|
11923
|
+
break;
|
|
11924
|
+
}
|
|
11925
|
+
if (signal.aborted) break;
|
|
11585
11926
|
}
|
|
11586
|
-
if (signal.aborted) break;
|
|
11587
11927
|
}
|
|
11588
11928
|
if (!failBuild && !signal.aborted) {
|
|
11589
11929
|
try {
|
|
@@ -11641,7 +11981,7 @@ var runIosBuild = async (payload, timeoutMs, signal, onLog, context) => {
|
|
|
11641
11981
|
...baseHookContext,
|
|
11642
11982
|
buildFolder: input.artifactDirectory,
|
|
11643
11983
|
failed: failBuild || !signal.aborted && buildResult !== null && buildResult.exitCode !== 0,
|
|
11644
|
-
cancelled: signal.aborted || buildResult?.cancelled === true,
|
|
11984
|
+
cancelled: signal.aborted || preflightResult?.cancelled === true || buildResult?.cancelled === true,
|
|
11645
11985
|
errorCode,
|
|
11646
11986
|
error
|
|
11647
11987
|
},
|
|
@@ -11677,7 +12017,7 @@ var runIosBuild = async (payload, timeoutMs, signal, onLog, context) => {
|
|
|
11677
12017
|
}
|
|
11678
12018
|
const reports = [];
|
|
11679
12019
|
const testAction = ["TEST", "TEST_WITHOUT_BUILDING"].includes(input.action);
|
|
11680
|
-
const canGenerateReports = () => !signal.aborted && buildResult
|
|
12020
|
+
const canGenerateReports = () => !signal.aborted && buildResult !== null && !buildResult.cancelled && !buildResult.timedOut;
|
|
11681
12021
|
if (canGenerateReports() && testAction && input.advancedSettings.parseTestResults) {
|
|
11682
12022
|
const report = await writeJsonReport(
|
|
11683
12023
|
input,
|
|
@@ -11721,8 +12061,8 @@ var runIosBuild = async (payload, timeoutMs, signal, onLog, context) => {
|
|
|
11721
12061
|
await artifact(input.artifactDirectory, "RAW_LOG", rawLog)
|
|
11722
12062
|
);
|
|
11723
12063
|
}
|
|
11724
|
-
const cancelled = signal.aborted || buildResult?.cancelled === true;
|
|
11725
|
-
const timedOut = buildResult?.timedOut === true;
|
|
12064
|
+
const cancelled = signal.aborted || preflightResult?.cancelled === true || buildResult?.cancelled === true;
|
|
12065
|
+
const timedOut = preflightResult?.timedOut === true || buildResult?.timedOut === true;
|
|
11726
12066
|
const exitCode = cancelled ? null : failBuild || !buildResult ? 1 : buildResult.exitCode;
|
|
11727
12067
|
const args = xcodeBuildArguments(input);
|
|
11728
12068
|
const finalStateHash = await worktreeCodeStateHash(
|
|
@@ -11733,7 +12073,7 @@ var runIosBuild = async (payload, timeoutMs, signal, onLog, context) => {
|
|
|
11733
12073
|
const codeStateObservedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11734
12074
|
return {
|
|
11735
12075
|
exitCode,
|
|
11736
|
-
signal: buildResult?.signal ?? null,
|
|
12076
|
+
signal: buildResult?.signal ?? preflightResult?.signal ?? null,
|
|
11737
12077
|
timedOut,
|
|
11738
12078
|
cancelled,
|
|
11739
12079
|
errorCode,
|
|
@@ -11844,6 +12184,67 @@ var downloadIosBuildArtifact = async (payload, timeoutMs, signal, onLog, context
|
|
|
11844
12184
|
}
|
|
11845
12185
|
}
|
|
11846
12186
|
};
|
|
12187
|
+
var uploadTransferredIosBuildArtifact = async (payload, timeoutMs, signal, onLog, context) => {
|
|
12188
|
+
const input = parseBuildArtifactTransferUploadPayload(payload);
|
|
12189
|
+
if (!context?.uploadBuildArtifactTransfer) {
|
|
12190
|
+
throw new Error("This agent cannot upload transferred build artifacts");
|
|
12191
|
+
}
|
|
12192
|
+
const root = await realpath2(input.artifactDirectory);
|
|
12193
|
+
const target2 = await realpath2(
|
|
12194
|
+
containedPath2(root, input.artifactRelativePath)
|
|
12195
|
+
);
|
|
12196
|
+
const difference = relative3(root, target2);
|
|
12197
|
+
if (!difference || difference === ".." || difference.startsWith(`..${sep3}`) || isAbsolute3(difference)) {
|
|
12198
|
+
throw new Error("Build artifact resolves outside the build folder");
|
|
12199
|
+
}
|
|
12200
|
+
const information = await stat2(target2);
|
|
12201
|
+
let uploadPath = target2;
|
|
12202
|
+
let filename = basename2(target2);
|
|
12203
|
+
let contentType = "application/octet-stream";
|
|
12204
|
+
let temporaryArchive = null;
|
|
12205
|
+
try {
|
|
12206
|
+
if (information.isDirectory()) {
|
|
12207
|
+
temporaryArchive = join3(
|
|
12208
|
+
tmpdir2(),
|
|
12209
|
+
`ade-build-transfer-${input.transferId}-${randomUUID()}.tar.gz`
|
|
12210
|
+
);
|
|
12211
|
+
requireSuccess(
|
|
12212
|
+
await command(
|
|
12213
|
+
"tar",
|
|
12214
|
+
["-czf", temporaryArchive, "-C", dirname2(target2), basename2(target2)],
|
|
12215
|
+
timeoutMs,
|
|
12216
|
+
signal
|
|
12217
|
+
),
|
|
12218
|
+
"Could not package the runnable app"
|
|
12219
|
+
);
|
|
12220
|
+
uploadPath = temporaryArchive;
|
|
12221
|
+
filename = `${filename}.tar.gz`;
|
|
12222
|
+
contentType = "application/gzip";
|
|
12223
|
+
} else if (!information.isFile()) {
|
|
12224
|
+
throw new Error("Build artifact is not transferable");
|
|
12225
|
+
}
|
|
12226
|
+
const checksum = await fileChecksum(uploadPath);
|
|
12227
|
+
if (!checksum)
|
|
12228
|
+
throw new Error("Could not checksum the runnable app archive");
|
|
12229
|
+
await onLog({
|
|
12230
|
+
sequence: 0,
|
|
12231
|
+
stream: "SYSTEM",
|
|
12232
|
+
message: `Uploading ${filename} in resumable 16 MiB chunks`,
|
|
12233
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12234
|
+
});
|
|
12235
|
+
await context.uploadBuildArtifactTransfer({
|
|
12236
|
+
transferId: input.transferId,
|
|
12237
|
+
path: uploadPath,
|
|
12238
|
+
filename,
|
|
12239
|
+
contentType,
|
|
12240
|
+
checksum,
|
|
12241
|
+
signal
|
|
12242
|
+
});
|
|
12243
|
+
return successfulProcess;
|
|
12244
|
+
} finally {
|
|
12245
|
+
if (temporaryArchive) await rm3(temporaryArchive, { force: true });
|
|
12246
|
+
}
|
|
12247
|
+
};
|
|
11847
12248
|
async function runSimpleLogged(logger, options) {
|
|
11848
12249
|
return runLoggedCommand({
|
|
11849
12250
|
command: "xcrun",
|
|
@@ -11862,17 +12263,7 @@ async function runSimpleLogged(logger, options) {
|
|
|
11862
12263
|
function simulatorAppArguments(destinationId) {
|
|
11863
12264
|
return ["-a", "Simulator", "--args", "-CurrentDeviceUDID", destinationId];
|
|
11864
12265
|
}
|
|
11865
|
-
|
|
11866
|
-
const input = parseBuildDeploymentPayload(payload);
|
|
11867
|
-
const folder = await validateWorktree(
|
|
11868
|
-
input,
|
|
11869
|
-
Math.min(timeoutMs, 6e4),
|
|
11870
|
-
signal
|
|
11871
|
-
);
|
|
11872
|
-
const appPath = containedPath2(
|
|
11873
|
-
input.artifactDirectory,
|
|
11874
|
-
input.artifactRelativePath
|
|
11875
|
-
);
|
|
12266
|
+
async function deployPreparedIosBuild(input, folder, appPath, timeoutMs, signal, context) {
|
|
11876
12267
|
if (!await pathExists(appPath) || !appPath.endsWith(".app")) {
|
|
11877
12268
|
throw new Error("Runnable app artifact is missing");
|
|
11878
12269
|
}
|
|
@@ -12062,6 +12453,99 @@ var deployIosBuild = async (payload, timeoutMs, signal, _onLog, context) => {
|
|
|
12062
12453
|
} finally {
|
|
12063
12454
|
await logger.close();
|
|
12064
12455
|
}
|
|
12456
|
+
}
|
|
12457
|
+
var deployIosBuild = async (payload, timeoutMs, signal, _onLog, context) => {
|
|
12458
|
+
const input = parseBuildDeploymentPayload(payload);
|
|
12459
|
+
const folder = await validateWorktree(
|
|
12460
|
+
input,
|
|
12461
|
+
Math.min(timeoutMs, 6e4),
|
|
12462
|
+
signal
|
|
12463
|
+
);
|
|
12464
|
+
const appPath = containedPath2(
|
|
12465
|
+
input.artifactDirectory,
|
|
12466
|
+
input.artifactRelativePath
|
|
12467
|
+
);
|
|
12468
|
+
return deployPreparedIosBuild(
|
|
12469
|
+
input,
|
|
12470
|
+
folder,
|
|
12471
|
+
appPath,
|
|
12472
|
+
timeoutMs,
|
|
12473
|
+
signal,
|
|
12474
|
+
context
|
|
12475
|
+
);
|
|
12476
|
+
};
|
|
12477
|
+
async function createRemoteBuildStagingDirectory(buildId) {
|
|
12478
|
+
return realpath2(
|
|
12479
|
+
await mkdtemp2(join3(tmpdir2(), `ade-remote-build-${buildId}-`))
|
|
12480
|
+
);
|
|
12481
|
+
}
|
|
12482
|
+
var deployTransferredIosBuild = async (payload, timeoutMs, signal, onLog, context) => {
|
|
12483
|
+
const input = parseBuildRemoteDeploymentPayload(payload);
|
|
12484
|
+
if (!context?.downloadBuildArtifactTransfer) {
|
|
12485
|
+
throw new Error("This agent cannot download transferred build artifacts");
|
|
12486
|
+
}
|
|
12487
|
+
const staging = await createRemoteBuildStagingDirectory(input.buildId);
|
|
12488
|
+
const archive = join3(staging, "artifact.tar.gz");
|
|
12489
|
+
try {
|
|
12490
|
+
await onLog({
|
|
12491
|
+
sequence: 0,
|
|
12492
|
+
stream: "SYSTEM",
|
|
12493
|
+
message: "Waiting for the relayed build artifact",
|
|
12494
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12495
|
+
});
|
|
12496
|
+
await context.downloadBuildArtifactTransfer({
|
|
12497
|
+
transferId: input.transferId,
|
|
12498
|
+
path: archive,
|
|
12499
|
+
signal
|
|
12500
|
+
});
|
|
12501
|
+
const listing = await command(
|
|
12502
|
+
"tar",
|
|
12503
|
+
["-tzf", archive],
|
|
12504
|
+
Math.min(timeoutMs, 12e4),
|
|
12505
|
+
signal,
|
|
12506
|
+
staging
|
|
12507
|
+
);
|
|
12508
|
+
requireSuccess(listing, "Transferred build archive is malformed");
|
|
12509
|
+
const entries = listing.stdout.split("\n").map((entry) => entry.trim().replace(/^\.\//, "")).filter(Boolean);
|
|
12510
|
+
if (!entries.length || entries.some(
|
|
12511
|
+
(entry) => entry.startsWith("/") || entry.split("/").some((component) => component === "..")
|
|
12512
|
+
)) {
|
|
12513
|
+
throw new Error("Transferred build archive contains an unsafe path");
|
|
12514
|
+
}
|
|
12515
|
+
const roots = new Set(entries.map((entry) => entry.split("/")[0]));
|
|
12516
|
+
const root = [...roots][0];
|
|
12517
|
+
if (roots.size !== 1 || !root?.endsWith(".app")) {
|
|
12518
|
+
throw new Error("Transferred build archive must contain one app bundle");
|
|
12519
|
+
}
|
|
12520
|
+
requireSuccess(
|
|
12521
|
+
await command(
|
|
12522
|
+
"tar",
|
|
12523
|
+
["-xzf", archive, "-C", staging],
|
|
12524
|
+
Math.min(timeoutMs, 12e4),
|
|
12525
|
+
signal,
|
|
12526
|
+
staging
|
|
12527
|
+
),
|
|
12528
|
+
"Could not extract the transferred app"
|
|
12529
|
+
);
|
|
12530
|
+
const appPath = await realpath2(join3(staging, root));
|
|
12531
|
+
const pathDifference = relative3(staging, appPath);
|
|
12532
|
+
if (pathDifference === ".." || pathDifference.startsWith(`..${sep3}`) || isAbsolute3(pathDifference)) {
|
|
12533
|
+
throw new Error("Transferred app resolves outside its staging directory");
|
|
12534
|
+
}
|
|
12535
|
+
return await deployPreparedIosBuild(
|
|
12536
|
+
{
|
|
12537
|
+
...input,
|
|
12538
|
+
artifactDirectory: staging
|
|
12539
|
+
},
|
|
12540
|
+
staging,
|
|
12541
|
+
appPath,
|
|
12542
|
+
timeoutMs,
|
|
12543
|
+
signal,
|
|
12544
|
+
context
|
|
12545
|
+
);
|
|
12546
|
+
} finally {
|
|
12547
|
+
await rm3(staging, { recursive: true, force: true });
|
|
12548
|
+
}
|
|
12065
12549
|
};
|
|
12066
12550
|
function exportPlist(settings) {
|
|
12067
12551
|
const method = {
|
|
@@ -15385,7 +15869,7 @@ var runWorkflowGitCheckpoint = async (payloadValue, _timeoutMs, signal) => {
|
|
|
15385
15869
|
|
|
15386
15870
|
// src/handlers/worktrees.ts
|
|
15387
15871
|
import { createWriteStream as createWriteStream2, watch } from "node:fs";
|
|
15388
|
-
import { mkdtemp as mkdtemp6, open as
|
|
15872
|
+
import { mkdtemp as mkdtemp6, open as open3, readFile as readFile9, realpath as realpath9, rm as rm10, stat as stat10 } from "node:fs/promises";
|
|
15389
15873
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
15390
15874
|
import { basename as basename5, dirname as dirname8, isAbsolute as isAbsolute7, join as join14, relative as relative8 } from "node:path";
|
|
15391
15875
|
import { spawn as spawn7 } from "node:child_process";
|
|
@@ -16649,7 +17133,7 @@ function requestedDiffPaths(input) {
|
|
|
16649
17133
|
return [input.path];
|
|
16650
17134
|
}
|
|
16651
17135
|
async function readBoundedFile(path, limit) {
|
|
16652
|
-
const file = await
|
|
17136
|
+
const file = await open3(path, "r");
|
|
16653
17137
|
try {
|
|
16654
17138
|
const metadata = await file.stat();
|
|
16655
17139
|
if (!metadata.isFile()) throw new Error("Changed path is not a file");
|
|
@@ -18908,10 +19392,13 @@ var handlers = {
|
|
|
18908
19392
|
[IOS_SOURCE_PARSE_JOB_KIND]: parseBuildSourceMetadata,
|
|
18909
19393
|
[IOS_DESTINATIONS_JOB_KIND]: inspectBuildDestinations,
|
|
18910
19394
|
[IOS_RUN_DESTINATIONS_JOB_KIND]: inspectBuildRunDestinations,
|
|
19395
|
+
[IOS_AGENT_RUN_DESTINATIONS_JOB_KIND]: inspectAgentBuildRunDestinations,
|
|
18911
19396
|
[IOS_BUILD_JOB_KIND]: runIosBuild,
|
|
18912
19397
|
[IOS_BUILD_DELETE_JOB_KIND]: deleteIosBuild,
|
|
18913
19398
|
[IOS_ARTIFACT_DOWNLOAD_JOB_KIND]: downloadIosBuildArtifact,
|
|
19399
|
+
[IOS_ARTIFACT_TRANSFER_UPLOAD_JOB_KIND]: uploadTransferredIosBuildArtifact,
|
|
18914
19400
|
[IOS_DEPLOY_JOB_KIND]: deployIosBuild,
|
|
19401
|
+
[IOS_REMOTE_DEPLOY_JOB_KIND]: deployTransferredIosBuild,
|
|
18915
19402
|
[IOS_EXPORT_JOB_KIND]: exportIosArchive,
|
|
18916
19403
|
[IOS_TEST_RESULTS_JOB_KIND]: generateIosBuildReport,
|
|
18917
19404
|
[IOS_COVERAGE_REPORT_JOB_KIND]: generateIosBuildReport,
|
|
@@ -19008,6 +19495,8 @@ var JobExecutor = class {
|
|
|
19008
19495
|
reportBuildProgress: (input) => this.client.reportBuildProgress(input),
|
|
19009
19496
|
appendBuildLogChunks: (buildId, chunks) => this.client.appendBuildLogChunks(buildId, chunks),
|
|
19010
19497
|
uploadBuildArtifact: (input) => this.client.uploadBuildArtifact(input),
|
|
19498
|
+
uploadBuildArtifactTransfer: (input) => this.client.uploadBuildArtifactTransfer(input),
|
|
19499
|
+
downloadBuildArtifactTransfer: (input) => this.client.downloadBuildArtifactTransfer(input),
|
|
19011
19500
|
claimSigningSecretTransfer: (transferId) => this.client.claimSigningSecretTransfer(transferId),
|
|
19012
19501
|
claimWorkflowJobSecrets: () => this.client.claimWorkflowJobSecrets(claimed.id),
|
|
19013
19502
|
appendCommandOutput: (attemptId, chunks) => this.client.appendCommandOutput(claimed.id, attemptId, chunks)
|
|
@@ -60864,15 +61353,6 @@ var defaultDependencies = {
|
|
|
60864
61353
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
60865
61354
|
})
|
|
60866
61355
|
};
|
|
60867
|
-
function assertLoopbackServer(server) {
|
|
60868
|
-
const hostname2 = new URL(server).hostname.toLowerCase();
|
|
60869
|
-
const isIpv4Loopback = /^127(?:\.\d{1,3}){3}$/.test(hostname2);
|
|
60870
|
-
if (hostname2 !== "localhost" && hostname2 !== "[::1]" && !isIpv4Loopback) {
|
|
60871
|
-
throw new Error(
|
|
60872
|
-
`Development auto-enrollment is restricted to loopback servers; received ${hostname2}`
|
|
60873
|
-
);
|
|
60874
|
-
}
|
|
60875
|
-
}
|
|
60876
61356
|
async function waitForServer(client2, timeoutMs, signal, dependencies) {
|
|
60877
61357
|
const deadline = Date.now() + timeoutMs;
|
|
60878
61358
|
let lastError;
|
|
@@ -60886,17 +61366,18 @@ async function waitForServer(client2, timeoutMs, signal, dependencies) {
|
|
|
60886
61366
|
}
|
|
60887
61367
|
}
|
|
60888
61368
|
const detail = lastError instanceof Error ? `: ${lastError.message}` : "";
|
|
60889
|
-
throw new Error(
|
|
61369
|
+
throw new Error(
|
|
61370
|
+
`Timed out waiting for the development control plane${detail}`
|
|
61371
|
+
);
|
|
60890
61372
|
}
|
|
60891
61373
|
async function prepareDevelopmentAgent(options, signal, dependencies = defaultDependencies) {
|
|
60892
61374
|
const server = normalizeServer(options.server);
|
|
60893
|
-
assertLoopbackServer(server);
|
|
60894
61375
|
const websocketServer = options.websocketServer ?? defaultWebSocketServer(server);
|
|
60895
61376
|
const configFile = options.configFile ?? developmentConfigPath();
|
|
60896
61377
|
const inventory = dependencies.inventory();
|
|
60897
61378
|
const name = options.name ?? `${inventory.hostname}-dev`;
|
|
60898
61379
|
const anonymousClient = dependencies.createClient(server);
|
|
60899
|
-
console.log(`Waiting for
|
|
61380
|
+
console.log(`Waiting for development control plane at ${server} ...`);
|
|
60900
61381
|
await waitForServer(
|
|
60901
61382
|
anonymousClient,
|
|
60902
61383
|
options.waitTimeoutMs ?? DEFAULT_SERVER_WAIT_MS,
|