@ai-development-environment/control-agent 0.0.30 → 0.0.32
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 +703 -222
- package/package.json +1 -1
package/dist/control-agent.js
CHANGED
|
@@ -2288,6 +2288,7 @@ var IOS_DEPLOY_JOB_KIND = "ios.build.deploy";
|
|
|
2288
2288
|
var IOS_EXPORT_JOB_KIND = "ios.archive.export";
|
|
2289
2289
|
var IOS_TEST_RESULTS_JOB_KIND = "ios.test-results.parse";
|
|
2290
2290
|
var IOS_COVERAGE_REPORT_JOB_KIND = "ios.coverage.generate";
|
|
2291
|
+
var IOS_SIGNING_INSPECT_JOB_KIND = "ios.signing.inspect";
|
|
2291
2292
|
var IOS_BUILD_JOB_KINDS = [
|
|
2292
2293
|
IOS_SOURCE_DISCOVER_JOB_KIND,
|
|
2293
2294
|
IOS_SOURCE_PARSE_JOB_KIND,
|
|
@@ -2299,7 +2300,8 @@ var IOS_BUILD_JOB_KINDS = [
|
|
|
2299
2300
|
IOS_DEPLOY_JOB_KIND,
|
|
2300
2301
|
IOS_EXPORT_JOB_KIND,
|
|
2301
2302
|
IOS_TEST_RESULTS_JOB_KIND,
|
|
2302
|
-
IOS_COVERAGE_REPORT_JOB_KIND
|
|
2303
|
+
IOS_COVERAGE_REPORT_JOB_KIND,
|
|
2304
|
+
IOS_SIGNING_INSPECT_JOB_KIND
|
|
2303
2305
|
];
|
|
2304
2306
|
var BUILD_ACTIONS = [
|
|
2305
2307
|
"BUILD",
|
|
@@ -2840,6 +2842,29 @@ function parseBuildExportPayload(value) {
|
|
|
2840
2842
|
settings: parseBuildExportSettings(input.settings)
|
|
2841
2843
|
};
|
|
2842
2844
|
}
|
|
2845
|
+
function provisioningProfileType(profile) {
|
|
2846
|
+
if (profile.getTaskAllow) return "DEVELOPMENT";
|
|
2847
|
+
if (profile.provisionsAllDevices) return "ENTERPRISE";
|
|
2848
|
+
return profile.hasProvisionedDevices ? "AD_HOC" : "APP_STORE";
|
|
2849
|
+
}
|
|
2850
|
+
function parseBuildSigningInspectPayload(value) {
|
|
2851
|
+
const input = objectValue6(value, "build signing payload");
|
|
2852
|
+
return {
|
|
2853
|
+
buildId: stringValue6(input.buildId, "build signing payload.buildId"),
|
|
2854
|
+
codebaseId: stringValue6(
|
|
2855
|
+
input.codebaseId,
|
|
2856
|
+
"build signing payload.codebaseId"
|
|
2857
|
+
),
|
|
2858
|
+
artifactDirectory: stringValue6(
|
|
2859
|
+
input.artifactDirectory,
|
|
2860
|
+
"build signing payload.artifactDirectory"
|
|
2861
|
+
),
|
|
2862
|
+
archiveRelativePath: safeRelativePath2(
|
|
2863
|
+
input.archiveRelativePath,
|
|
2864
|
+
"build signing payload.archiveRelativePath"
|
|
2865
|
+
)
|
|
2866
|
+
};
|
|
2867
|
+
}
|
|
2843
2868
|
|
|
2844
2869
|
// src/inventory.ts
|
|
2845
2870
|
var AGENT_VERSION = "0.1.0";
|
|
@@ -2980,64 +3005,159 @@ function runCloudflared(payload, timeoutMs, signal, onLog) {
|
|
|
2980
3005
|
});
|
|
2981
3006
|
}
|
|
2982
3007
|
|
|
2983
|
-
// src/handlers/
|
|
2984
|
-
import {
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
3008
|
+
// src/handlers/signing.ts
|
|
3009
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
3010
|
+
import { readdir, stat as stat2 } from "node:fs/promises";
|
|
3011
|
+
import { homedir as homedir2 } from "node:os";
|
|
3012
|
+
import { basename, join as join2, relative, sep } from "node:path";
|
|
3013
|
+
|
|
3014
|
+
// ../agent-contract/src/plist.ts
|
|
3015
|
+
function xmlEscape(value) {
|
|
3016
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
3017
|
+
}
|
|
3018
|
+
function plistValue(value) {
|
|
3019
|
+
if (typeof value === "boolean") return value ? "<true/>" : "<false/>";
|
|
3020
|
+
if (typeof value === "string") return `<string>${xmlEscape(value)}</string>`;
|
|
3021
|
+
if (typeof value === "number" && Number.isInteger(value)) {
|
|
3022
|
+
return `<integer>${value}</integer>`;
|
|
2989
3023
|
}
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
throw new Error(`Unexpected ccusage.report payload field: ${keys[0]}`);
|
|
3024
|
+
if (Array.isArray(value)) {
|
|
3025
|
+
return `<array>${value.map((entry) => plistValue(entry)).join("")}</array>`;
|
|
2993
3026
|
}
|
|
2994
|
-
|
|
3027
|
+
if (value && typeof value === "object") {
|
|
3028
|
+
return `<dict>${Object.entries(value).map(([key, entry]) => `<key>${xmlEscape(key)}</key>${plistValue(entry)}`).join("")}</dict>`;
|
|
3029
|
+
}
|
|
3030
|
+
throw new Error("Unsupported plist value");
|
|
2995
3031
|
}
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3032
|
+
function plistDocument(value) {
|
|
3033
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
3034
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3035
|
+
<plist version="1.0">${plistValue(value)}</plist>
|
|
3036
|
+
`;
|
|
3037
|
+
}
|
|
3038
|
+
var ENTITIES = {
|
|
3039
|
+
amp: "&",
|
|
3040
|
+
lt: "<",
|
|
3041
|
+
gt: ">",
|
|
3042
|
+
quot: '"',
|
|
3043
|
+
apos: "'"
|
|
3044
|
+
};
|
|
3045
|
+
function decodeText(value) {
|
|
3046
|
+
return value.replace(
|
|
3047
|
+
/&(#x?[0-9a-fA-F]+|[a-z]+);/g,
|
|
3048
|
+
(match, entity) => {
|
|
3049
|
+
if (entity.startsWith("#x") || entity.startsWith("#X")) {
|
|
3050
|
+
return String.fromCodePoint(Number.parseInt(entity.slice(2), 16));
|
|
3010
3051
|
}
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
exceededLimit = true;
|
|
3014
|
-
return;
|
|
3052
|
+
if (entity.startsWith("#")) {
|
|
3053
|
+
return String.fromCodePoint(Number.parseInt(entity.slice(1), 10));
|
|
3015
3054
|
}
|
|
3016
|
-
|
|
3017
|
-
|
|
3055
|
+
return ENTITIES[entity] ?? match;
|
|
3056
|
+
}
|
|
3057
|
+
);
|
|
3058
|
+
}
|
|
3059
|
+
function nextTag(xml, from) {
|
|
3060
|
+
let index = from;
|
|
3061
|
+
for (; ; ) {
|
|
3062
|
+
const start = xml.indexOf("<", index);
|
|
3063
|
+
if (start < 0) return null;
|
|
3064
|
+
if (xml.startsWith("<!--", start)) {
|
|
3065
|
+
const close2 = xml.indexOf("-->", start);
|
|
3066
|
+
if (close2 < 0) return null;
|
|
3067
|
+
index = close2 + 3;
|
|
3068
|
+
continue;
|
|
3069
|
+
}
|
|
3070
|
+
if (xml.startsWith("<?", start) || xml.startsWith("<!", start)) {
|
|
3071
|
+
const close2 = xml.indexOf(">", start);
|
|
3072
|
+
if (close2 < 0) return null;
|
|
3073
|
+
index = close2 + 1;
|
|
3074
|
+
continue;
|
|
3075
|
+
}
|
|
3076
|
+
const close = xml.indexOf(">", start);
|
|
3077
|
+
if (close < 0) return null;
|
|
3078
|
+
const raw = xml.slice(start + 1, close);
|
|
3079
|
+
const closing = raw.startsWith("/");
|
|
3080
|
+
const selfClosing = raw.endsWith("/");
|
|
3081
|
+
const name = raw.slice(closing ? 1 : 0, selfClosing ? raw.length - 1 : raw.length).trim().split(/\s/)[0];
|
|
3082
|
+
return { name, closing, selfClosing, end: close + 1 };
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
function textUntilClose(xml, from, name) {
|
|
3086
|
+
const close = xml.indexOf(`</${name}`, from);
|
|
3087
|
+
if (close < 0) throw new Error(`Unterminated <${name}> in plist`);
|
|
3088
|
+
const end = xml.indexOf(">", close);
|
|
3089
|
+
return [decodeText(xml.slice(from, close)), end + 1];
|
|
3090
|
+
}
|
|
3091
|
+
var MAX_DEPTH = 64;
|
|
3092
|
+
function parseValue(xml, from, depth) {
|
|
3093
|
+
if (depth > MAX_DEPTH) throw new Error("plist nesting is too deep");
|
|
3094
|
+
const tag = nextTag(xml, from);
|
|
3095
|
+
if (!tag || tag.closing) throw new Error("Expected a plist value");
|
|
3096
|
+
if (tag.selfClosing) {
|
|
3097
|
+
switch (tag.name) {
|
|
3098
|
+
case "true":
|
|
3099
|
+
return [true, tag.end];
|
|
3100
|
+
case "false":
|
|
3101
|
+
return [false, tag.end];
|
|
3102
|
+
case "dict":
|
|
3103
|
+
return [{}, tag.end];
|
|
3104
|
+
case "array":
|
|
3105
|
+
return [[], tag.end];
|
|
3106
|
+
default:
|
|
3107
|
+
return ["", tag.end];
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
switch (tag.name) {
|
|
3111
|
+
case "dict": {
|
|
3112
|
+
const result = {};
|
|
3113
|
+
let cursor = tag.end;
|
|
3114
|
+
for (; ; ) {
|
|
3115
|
+
const next = nextTag(xml, cursor);
|
|
3116
|
+
if (!next) throw new Error("Unterminated <dict> in plist");
|
|
3117
|
+
if (next.closing && next.name === "dict") return [result, next.end];
|
|
3118
|
+
if (next.name !== "key") throw new Error("Expected <key> in <dict>");
|
|
3119
|
+
const [key, afterKey] = textUntilClose(xml, next.end, "key");
|
|
3120
|
+
const [value, afterValue] = parseValue(xml, afterKey, depth + 1);
|
|
3121
|
+
result[key] = value;
|
|
3122
|
+
cursor = afterValue;
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3125
|
+
case "array": {
|
|
3126
|
+
const result = [];
|
|
3127
|
+
let cursor = tag.end;
|
|
3128
|
+
for (; ; ) {
|
|
3129
|
+
const next = nextTag(xml, cursor);
|
|
3130
|
+
if (!next) throw new Error("Unterminated <array> in plist");
|
|
3131
|
+
if (next.closing && next.name === "array") return [result, next.end];
|
|
3132
|
+
const [value, afterValue] = parseValue(xml, cursor, depth + 1);
|
|
3133
|
+
result.push(value);
|
|
3134
|
+
cursor = afterValue;
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
case "integer":
|
|
3138
|
+
case "real": {
|
|
3139
|
+
const [text2, end] = textUntilClose(xml, tag.end, tag.name);
|
|
3140
|
+
return [Number(text2.trim()), end];
|
|
3141
|
+
}
|
|
3142
|
+
// Dates and data are returned verbatim: callers need the timestamp text and
|
|
3143
|
+
// never the certificate bytes, so decoding them would only cost memory.
|
|
3144
|
+
case "string":
|
|
3145
|
+
case "date":
|
|
3146
|
+
case "data": {
|
|
3147
|
+
const [text2, end] = textUntilClose(xml, tag.end, tag.name);
|
|
3148
|
+
return [tag.name === "string" ? text2 : text2.trim(), end];
|
|
3149
|
+
}
|
|
3150
|
+
default: {
|
|
3151
|
+
const [, end] = textUntilClose(xml, tag.end, tag.name);
|
|
3152
|
+
return [null, end];
|
|
3018
3153
|
}
|
|
3019
|
-
});
|
|
3020
|
-
if (result.exitCode !== 0 || result.cancelled || result.timedOut || result.signal !== null) {
|
|
3021
|
-
return result;
|
|
3022
|
-
}
|
|
3023
|
-
if (exceededLimit) {
|
|
3024
|
-
throw new Error(
|
|
3025
|
-
`ccusage JSON exceeded the ${MAX_CCUSAGE_STDOUT_BYTES} byte limit`
|
|
3026
|
-
);
|
|
3027
|
-
}
|
|
3028
|
-
let parsed;
|
|
3029
|
-
try {
|
|
3030
|
-
parsed = JSON.parse(stdout.join("\n"));
|
|
3031
|
-
} catch {
|
|
3032
|
-
throw new Error("ccusage returned malformed JSON");
|
|
3033
3154
|
}
|
|
3034
|
-
return { ...result, report: parseCcusageReport(parsed) };
|
|
3035
3155
|
}
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3156
|
+
function parsePlist(xml) {
|
|
3157
|
+
const start = xml.indexOf("<plist");
|
|
3158
|
+
const from = start < 0 ? 0 : xml.indexOf(">", start) + 1;
|
|
3159
|
+
return parseValue(xml, from, 0)[0];
|
|
3160
|
+
}
|
|
3041
3161
|
|
|
3042
3162
|
// src/capture-command.ts
|
|
3043
3163
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -3107,7 +3227,333 @@ function captureCommand(options) {
|
|
|
3107
3227
|
});
|
|
3108
3228
|
}
|
|
3109
3229
|
|
|
3230
|
+
// src/handlers/signing.ts
|
|
3231
|
+
var PROFILE_DIRECTORIES = [
|
|
3232
|
+
join2(
|
|
3233
|
+
homedir2(),
|
|
3234
|
+
"Library",
|
|
3235
|
+
"Developer",
|
|
3236
|
+
"Xcode",
|
|
3237
|
+
"UserData",
|
|
3238
|
+
"Provisioning Profiles"
|
|
3239
|
+
),
|
|
3240
|
+
join2(homedir2(), "Library", "MobileDevice", "Provisioning Profiles")
|
|
3241
|
+
];
|
|
3242
|
+
var PROFILE_EXTENSIONS = [".mobileprovision", ".provisionprofile"];
|
|
3243
|
+
function text(value) {
|
|
3244
|
+
return typeof value === "string" && value.trim() ? value : null;
|
|
3245
|
+
}
|
|
3246
|
+
function stringList(value) {
|
|
3247
|
+
return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
3248
|
+
}
|
|
3249
|
+
async function decodeProfile(path, timeoutMs, signal) {
|
|
3250
|
+
try {
|
|
3251
|
+
const result = await captureCommand({
|
|
3252
|
+
command: "/usr/bin/security",
|
|
3253
|
+
args: ["cms", "-D", "-i", path],
|
|
3254
|
+
timeoutMs: Math.min(timeoutMs, 1e4),
|
|
3255
|
+
signal
|
|
3256
|
+
});
|
|
3257
|
+
if (result.exitCode !== 0 || !result.stdout.trim()) return null;
|
|
3258
|
+
const parsed = parsePlist(result.stdout);
|
|
3259
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
3260
|
+
return null;
|
|
3261
|
+
}
|
|
3262
|
+
return parsed;
|
|
3263
|
+
} catch {
|
|
3264
|
+
return null;
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
function toProfile(uuid, raw, now) {
|
|
3268
|
+
const name = text(raw.Name);
|
|
3269
|
+
if (!name) return null;
|
|
3270
|
+
const entitlements = raw.Entitlements && typeof raw.Entitlements === "object" ? raw.Entitlements : {};
|
|
3271
|
+
const applicationIdentifier = text(entitlements["application-identifier"]);
|
|
3272
|
+
const teamId = stringList(raw.TeamIdentifier)[0] ?? text(entitlements["com.apple.developer.team-identifier"]);
|
|
3273
|
+
const bundleId = applicationIdentifier ? applicationIdentifier.replace(/^[A-Z0-9]+\./, "") : "*";
|
|
3274
|
+
const expiresAt = text(raw.ExpirationDate);
|
|
3275
|
+
const expiry = expiresAt ? Date.parse(expiresAt) : Number.NaN;
|
|
3276
|
+
return {
|
|
3277
|
+
uuid,
|
|
3278
|
+
name,
|
|
3279
|
+
teamId: teamId ?? null,
|
|
3280
|
+
teamName: text(raw.TeamName),
|
|
3281
|
+
bundleId,
|
|
3282
|
+
type: provisioningProfileType({
|
|
3283
|
+
getTaskAllow: entitlements["get-task-allow"] === true,
|
|
3284
|
+
hasProvisionedDevices: stringList(raw.ProvisionedDevices).length > 0,
|
|
3285
|
+
provisionsAllDevices: raw.ProvisionsAllDevices === true
|
|
3286
|
+
}),
|
|
3287
|
+
platforms: stringList(raw.Platform),
|
|
3288
|
+
expiresAt,
|
|
3289
|
+
expired: Number.isFinite(expiry) ? expiry < now : false,
|
|
3290
|
+
xcodeManaged: raw.IsXcodeManaged === true,
|
|
3291
|
+
certificateSha1s: certificateFingerprints(raw.DeveloperCertificates)
|
|
3292
|
+
};
|
|
3293
|
+
}
|
|
3294
|
+
function certificateFingerprints(value) {
|
|
3295
|
+
return stringList(value).flatMap((encoded) => {
|
|
3296
|
+
try {
|
|
3297
|
+
const der = Buffer.from(encoded.replace(/\s+/g, ""), "base64");
|
|
3298
|
+
if (!der.length) return [];
|
|
3299
|
+
return [createHash2("sha1").update(der).digest("hex").toUpperCase()];
|
|
3300
|
+
} catch {
|
|
3301
|
+
return [];
|
|
3302
|
+
}
|
|
3303
|
+
});
|
|
3304
|
+
}
|
|
3305
|
+
async function readProfiles(timeoutMs, signal) {
|
|
3306
|
+
const profiles = /* @__PURE__ */ new Map();
|
|
3307
|
+
const now = Date.now();
|
|
3308
|
+
for (const directory of PROFILE_DIRECTORIES) {
|
|
3309
|
+
let entries;
|
|
3310
|
+
try {
|
|
3311
|
+
entries = await readdir(directory);
|
|
3312
|
+
} catch {
|
|
3313
|
+
continue;
|
|
3314
|
+
}
|
|
3315
|
+
for (const entry of entries) {
|
|
3316
|
+
if (!PROFILE_EXTENSIONS.some((suffix) => entry.endsWith(suffix)))
|
|
3317
|
+
continue;
|
|
3318
|
+
signal.throwIfAborted();
|
|
3319
|
+
const raw = await decodeProfile(
|
|
3320
|
+
join2(directory, entry),
|
|
3321
|
+
timeoutMs,
|
|
3322
|
+
signal
|
|
3323
|
+
);
|
|
3324
|
+
if (!raw) continue;
|
|
3325
|
+
const uuid = text(raw.UUID) ?? basename(entry).replace(/\.[^.]+$/, "");
|
|
3326
|
+
const profile = toProfile(uuid, raw, now);
|
|
3327
|
+
if (profile && !profiles.has(profile.uuid)) {
|
|
3328
|
+
profiles.set(profile.uuid, profile);
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
return dedupeProfiles([...profiles.values()]).sort(
|
|
3333
|
+
(left, right) => left.name.localeCompare(right.name)
|
|
3334
|
+
);
|
|
3335
|
+
}
|
|
3336
|
+
function dedupeProfiles(profiles) {
|
|
3337
|
+
const best = /* @__PURE__ */ new Map();
|
|
3338
|
+
for (const profile of profiles) {
|
|
3339
|
+
const key = [
|
|
3340
|
+
profile.name,
|
|
3341
|
+
profile.type,
|
|
3342
|
+
profile.bundleId,
|
|
3343
|
+
profile.teamId ?? "",
|
|
3344
|
+
[...profile.platforms].sort().join(","),
|
|
3345
|
+
[...profile.certificateSha1s].sort().join(",")
|
|
3346
|
+
].join("\0");
|
|
3347
|
+
const existing = best.get(key);
|
|
3348
|
+
if (!existing || (profile.expiresAt ?? "") > (existing.expiresAt ?? "") || // Stable tie-break so repeated inspections return the same profile.
|
|
3349
|
+
profile.expiresAt === existing.expiresAt && profile.uuid < existing.uuid) {
|
|
3350
|
+
best.set(key, profile);
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
return [...best.values()];
|
|
3354
|
+
}
|
|
3355
|
+
var IDENTITY_PATTERN = /^\s*\d+\)\s+([0-9A-F]{40})\s+"(.+)"\s*$/i;
|
|
3356
|
+
async function readIdentities(timeoutMs, signal) {
|
|
3357
|
+
try {
|
|
3358
|
+
const result = await captureCommand({
|
|
3359
|
+
command: "/usr/bin/security",
|
|
3360
|
+
args: ["find-identity", "-v", "-p", "codesigning"],
|
|
3361
|
+
timeoutMs: Math.min(timeoutMs, 1e4),
|
|
3362
|
+
signal
|
|
3363
|
+
});
|
|
3364
|
+
if (result.exitCode !== 0) return [];
|
|
3365
|
+
const identities = /* @__PURE__ */ new Map();
|
|
3366
|
+
for (const line of result.stdout.split("\n")) {
|
|
3367
|
+
const match = IDENTITY_PATTERN.exec(line);
|
|
3368
|
+
if (!match) continue;
|
|
3369
|
+
const [, sha1, name] = match;
|
|
3370
|
+
const team = /\(([A-Z0-9]{10})\)\s*$/.exec(name);
|
|
3371
|
+
identities.set(sha1, { sha1, name, teamId: team?.[1] ?? null });
|
|
3372
|
+
}
|
|
3373
|
+
return [...identities.values()];
|
|
3374
|
+
} catch {
|
|
3375
|
+
return [];
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
async function readArchiveBundles(archivePath, timeoutMs, signal) {
|
|
3379
|
+
const products = join2(archivePath, "Products");
|
|
3380
|
+
const bundles = [];
|
|
3381
|
+
const queue = [products];
|
|
3382
|
+
while (queue.length) {
|
|
3383
|
+
signal.throwIfAborted();
|
|
3384
|
+
const current = queue.shift();
|
|
3385
|
+
let entries;
|
|
3386
|
+
try {
|
|
3387
|
+
entries = await readdir(current, { withFileTypes: true });
|
|
3388
|
+
} catch {
|
|
3389
|
+
continue;
|
|
3390
|
+
}
|
|
3391
|
+
for (const entry of entries) {
|
|
3392
|
+
if (entry.isSymbolicLink() || !entry.isDirectory()) continue;
|
|
3393
|
+
const path = join2(current, entry.name);
|
|
3394
|
+
const signable = /\.(app|appex)$/.test(entry.name);
|
|
3395
|
+
if (!signable) {
|
|
3396
|
+
if (!/\.[A-Za-z0-9]+$/.test(entry.name)) queue.push(path);
|
|
3397
|
+
continue;
|
|
3398
|
+
}
|
|
3399
|
+
const bundleId = await bundleIdentifier(path, timeoutMs, signal);
|
|
3400
|
+
if (bundleId) {
|
|
3401
|
+
bundles.push({
|
|
3402
|
+
bundleId,
|
|
3403
|
+
name: basename(entry.name).replace(/\.(app|appex)$/, ""),
|
|
3404
|
+
relativePath: relative(archivePath, path).split(sep).join("/"),
|
|
3405
|
+
...await embeddedProfile(path, timeoutMs, signal)
|
|
3406
|
+
});
|
|
3407
|
+
}
|
|
3408
|
+
queue.push(path);
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
return bundles.sort(
|
|
3412
|
+
(left, right) => left.relativePath.localeCompare(right.relativePath)
|
|
3413
|
+
);
|
|
3414
|
+
}
|
|
3415
|
+
async function bundleIdentifier(bundlePath, timeoutMs, signal) {
|
|
3416
|
+
try {
|
|
3417
|
+
const result = await captureCommand({
|
|
3418
|
+
command: "/usr/bin/plutil",
|
|
3419
|
+
args: [
|
|
3420
|
+
"-extract",
|
|
3421
|
+
"CFBundleIdentifier",
|
|
3422
|
+
"raw",
|
|
3423
|
+
"-o",
|
|
3424
|
+
"-",
|
|
3425
|
+
join2(bundlePath, "Info.plist")
|
|
3426
|
+
],
|
|
3427
|
+
timeoutMs: Math.min(timeoutMs, 5e3),
|
|
3428
|
+
signal
|
|
3429
|
+
});
|
|
3430
|
+
const value = result.stdout.trim();
|
|
3431
|
+
return result.exitCode === 0 && value ? value : null;
|
|
3432
|
+
} catch {
|
|
3433
|
+
return null;
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
async function embeddedProfile(bundlePath, timeoutMs, signal) {
|
|
3437
|
+
for (const name of [
|
|
3438
|
+
"embedded.mobileprovision",
|
|
3439
|
+
"embedded.provisionprofile"
|
|
3440
|
+
]) {
|
|
3441
|
+
const path = join2(bundlePath, name);
|
|
3442
|
+
try {
|
|
3443
|
+
await stat2(path);
|
|
3444
|
+
} catch {
|
|
3445
|
+
continue;
|
|
3446
|
+
}
|
|
3447
|
+
const raw = await decodeProfile(path, timeoutMs, signal);
|
|
3448
|
+
if (raw) {
|
|
3449
|
+
return {
|
|
3450
|
+
embeddedProfileUuid: text(raw.UUID),
|
|
3451
|
+
embeddedProfileName: text(raw.Name)
|
|
3452
|
+
};
|
|
3453
|
+
}
|
|
3454
|
+
}
|
|
3455
|
+
return { embeddedProfileUuid: null, embeddedProfileName: null };
|
|
3456
|
+
}
|
|
3457
|
+
function teamsFrom(profiles, identities) {
|
|
3458
|
+
const teams = /* @__PURE__ */ new Map();
|
|
3459
|
+
for (const profile of profiles) {
|
|
3460
|
+
if (!profile.teamId) continue;
|
|
3461
|
+
const existing = teams.get(profile.teamId);
|
|
3462
|
+
if (!existing && profile.teamName)
|
|
3463
|
+
teams.set(profile.teamId, profile.teamName);
|
|
3464
|
+
else if (!existing) teams.set(profile.teamId, profile.teamId);
|
|
3465
|
+
}
|
|
3466
|
+
for (const identity of identities) {
|
|
3467
|
+
if (!identity.teamId || teams.has(identity.teamId)) continue;
|
|
3468
|
+
const name = /^[^:]+:\s*(.+?)\s*\([A-Z0-9]{10}\)\s*$/.exec(identity.name);
|
|
3469
|
+
teams.set(identity.teamId, name?.[1] ?? identity.teamId);
|
|
3470
|
+
}
|
|
3471
|
+
return [...teams.entries()].map(([id, name]) => ({ id, name })).sort((left, right) => left.name.localeCompare(right.name));
|
|
3472
|
+
}
|
|
3473
|
+
async function inspectSigningAssets(archivePath, timeoutMs, signal) {
|
|
3474
|
+
const [profiles, identities, bundles] = await Promise.all([
|
|
3475
|
+
readProfiles(timeoutMs, signal),
|
|
3476
|
+
readIdentities(timeoutMs, signal),
|
|
3477
|
+
readArchiveBundles(archivePath, timeoutMs, signal)
|
|
3478
|
+
]);
|
|
3479
|
+
return {
|
|
3480
|
+
teams: teamsFrom(profiles, identities),
|
|
3481
|
+
identities,
|
|
3482
|
+
profiles,
|
|
3483
|
+
bundles
|
|
3484
|
+
};
|
|
3485
|
+
}
|
|
3486
|
+
var inspectIosSigning = async (payload, timeoutMs, signal) => {
|
|
3487
|
+
const input = parseBuildSigningInspectPayload(payload);
|
|
3488
|
+
const archivePath = join2(input.artifactDirectory, input.archiveRelativePath);
|
|
3489
|
+
const inspection = await inspectSigningAssets(archivePath, timeoutMs, signal);
|
|
3490
|
+
return {
|
|
3491
|
+
exitCode: 0,
|
|
3492
|
+
signal: null,
|
|
3493
|
+
timedOut: false,
|
|
3494
|
+
cancelled: false,
|
|
3495
|
+
...inspection
|
|
3496
|
+
};
|
|
3497
|
+
};
|
|
3498
|
+
|
|
3499
|
+
// src/handlers/ccusage.ts
|
|
3500
|
+
import { Buffer as Buffer2 } from "node:buffer";
|
|
3501
|
+
var MAX_CCUSAGE_STDOUT_BYTES = 16 * 1024 * 1024;
|
|
3502
|
+
function validateCcusagePayload(payload) {
|
|
3503
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
3504
|
+
throw new Error("ccusage.report payload must be an object");
|
|
3505
|
+
}
|
|
3506
|
+
const keys = Object.keys(payload);
|
|
3507
|
+
if (keys.length > 0) {
|
|
3508
|
+
throw new Error(`Unexpected ccusage.report payload field: ${keys[0]}`);
|
|
3509
|
+
}
|
|
3510
|
+
return {};
|
|
3511
|
+
}
|
|
3512
|
+
async function runCcusage(payload, timeoutMs, signal, onLog) {
|
|
3513
|
+
validateCcusagePayload(payload);
|
|
3514
|
+
const stdout = [];
|
|
3515
|
+
let stdoutBytes = 0;
|
|
3516
|
+
let exceededLimit = false;
|
|
3517
|
+
const result = await runProcess({
|
|
3518
|
+
command: "ccusage",
|
|
3519
|
+
args: ["--json"],
|
|
3520
|
+
timeoutMs,
|
|
3521
|
+
signal,
|
|
3522
|
+
onLog: async (log) => {
|
|
3523
|
+
if (log.stream !== "STDOUT") {
|
|
3524
|
+
await onLog(log);
|
|
3525
|
+
return;
|
|
3526
|
+
}
|
|
3527
|
+
const lineBytes = Buffer2.byteLength(log.message, "utf8") + 1;
|
|
3528
|
+
if (stdoutBytes + lineBytes > MAX_CCUSAGE_STDOUT_BYTES) {
|
|
3529
|
+
exceededLimit = true;
|
|
3530
|
+
return;
|
|
3531
|
+
}
|
|
3532
|
+
stdoutBytes += lineBytes;
|
|
3533
|
+
stdout.push(log.message);
|
|
3534
|
+
}
|
|
3535
|
+
});
|
|
3536
|
+
if (result.exitCode !== 0 || result.cancelled || result.timedOut || result.signal !== null) {
|
|
3537
|
+
return result;
|
|
3538
|
+
}
|
|
3539
|
+
if (exceededLimit) {
|
|
3540
|
+
throw new Error(
|
|
3541
|
+
`ccusage JSON exceeded the ${MAX_CCUSAGE_STDOUT_BYTES} byte limit`
|
|
3542
|
+
);
|
|
3543
|
+
}
|
|
3544
|
+
let parsed;
|
|
3545
|
+
try {
|
|
3546
|
+
parsed = JSON.parse(stdout.join("\n"));
|
|
3547
|
+
} catch {
|
|
3548
|
+
throw new Error("ccusage returned malformed JSON");
|
|
3549
|
+
}
|
|
3550
|
+
return { ...result, report: parseCcusageReport(parsed) };
|
|
3551
|
+
}
|
|
3552
|
+
|
|
3110
3553
|
// src/handlers/build-data.ts
|
|
3554
|
+
import { lstat, opendir, readdir as readdir2, realpath, rm } from "node:fs/promises";
|
|
3555
|
+
import { homedir as homedir3 } from "node:os";
|
|
3556
|
+
import { basename as basename2, dirname as dirname2, isAbsolute, join as join3, resolve } from "node:path";
|
|
3111
3557
|
var successfulProcess = {
|
|
3112
3558
|
exitCode: 0,
|
|
3113
3559
|
signal: null,
|
|
@@ -3124,7 +3570,7 @@ function safeRelativePath3(value) {
|
|
|
3124
3570
|
}
|
|
3125
3571
|
async function configuredRoots(payload) {
|
|
3126
3572
|
if (payload.mode === "DEFAULT") {
|
|
3127
|
-
return [
|
|
3573
|
+
return [join3(homedir3(), "Library", "Developer", "Xcode", "DerivedData")];
|
|
3128
3574
|
}
|
|
3129
3575
|
if (payload.mode === "ABSOLUTE") {
|
|
3130
3576
|
if (!payload.path || !isAbsolute(payload.path)) {
|
|
@@ -3173,7 +3619,7 @@ async function scanRoot(configuredRoot, timeoutMs, signal) {
|
|
|
3173
3619
|
}
|
|
3174
3620
|
let children;
|
|
3175
3621
|
try {
|
|
3176
|
-
children = await
|
|
3622
|
+
children = await readdir2(rootPath2, { withFileTypes: true });
|
|
3177
3623
|
} catch (error) {
|
|
3178
3624
|
return {
|
|
3179
3625
|
entries: [],
|
|
@@ -3184,9 +3630,9 @@ async function scanRoot(configuredRoot, timeoutMs, signal) {
|
|
|
3184
3630
|
for (const child of children) {
|
|
3185
3631
|
signal.throwIfAborted();
|
|
3186
3632
|
if (!child.isDirectory()) continue;
|
|
3187
|
-
const path =
|
|
3633
|
+
const path = join3(rootPath2, child.name);
|
|
3188
3634
|
const projectPath = await workspacePath(
|
|
3189
|
-
|
|
3635
|
+
join3(path, "info.plist"),
|
|
3190
3636
|
timeoutMs,
|
|
3191
3637
|
signal
|
|
3192
3638
|
);
|
|
@@ -3228,7 +3674,7 @@ function assertDirectChild(rootPath2, path) {
|
|
|
3228
3674
|
}
|
|
3229
3675
|
const root = resolve(rootPath2);
|
|
3230
3676
|
const target2 = resolve(path);
|
|
3231
|
-
if (target2 === root || dirname2(target2) !== root ||
|
|
3677
|
+
if (target2 === root || dirname2(target2) !== root || basename2(target2) !== basename2(path)) {
|
|
3232
3678
|
throw new Error("Build Data target must be a direct child of its root");
|
|
3233
3679
|
}
|
|
3234
3680
|
}
|
|
@@ -3242,7 +3688,7 @@ async function allocatedBytes(path, signal) {
|
|
|
3242
3688
|
try {
|
|
3243
3689
|
for await (const entry of directory) {
|
|
3244
3690
|
signal.throwIfAborted();
|
|
3245
|
-
total += await allocatedBytes(
|
|
3691
|
+
total += await allocatedBytes(join3(path, entry.name), signal);
|
|
3246
3692
|
}
|
|
3247
3693
|
} finally {
|
|
3248
3694
|
await directory.close().catch(() => void 0);
|
|
@@ -3310,9 +3756,9 @@ var deleteBuildData = async (rawPayload, _timeoutMs, signal) => {
|
|
|
3310
3756
|
};
|
|
3311
3757
|
|
|
3312
3758
|
// src/handlers/codebases.ts
|
|
3313
|
-
import { readdir as
|
|
3314
|
-
import { homedir as
|
|
3315
|
-
import { dirname as dirname3, join as
|
|
3759
|
+
import { readdir as readdir3, realpath as realpath2, stat as stat3 } from "node:fs/promises";
|
|
3760
|
+
import { homedir as homedir4 } from "node:os";
|
|
3761
|
+
import { dirname as dirname3, join as join4, resolve as resolve2 } from "node:path";
|
|
3316
3762
|
var successfulProcess2 = {
|
|
3317
3763
|
exitCode: 0,
|
|
3318
3764
|
signal: null,
|
|
@@ -3389,7 +3835,7 @@ function baseSnapshot(folder) {
|
|
|
3389
3835
|
}
|
|
3390
3836
|
async function fetchedAt(commonDirectory) {
|
|
3391
3837
|
try {
|
|
3392
|
-
return (await
|
|
3838
|
+
return (await stat3(join4(commonDirectory, "FETCH_HEAD"))).mtime.toISOString();
|
|
3393
3839
|
} catch {
|
|
3394
3840
|
return null;
|
|
3395
3841
|
}
|
|
@@ -3400,7 +3846,7 @@ async function inspectCodebaseProcess(selectedFolder, timeoutMs, signal, expecte
|
|
|
3400
3846
|
let selected;
|
|
3401
3847
|
try {
|
|
3402
3848
|
selected = await realpath2(fallbackFolder);
|
|
3403
|
-
if (!(await
|
|
3849
|
+
if (!(await stat3(selected)).isDirectory())
|
|
3404
3850
|
throw new Error("Folder is not a directory");
|
|
3405
3851
|
} catch (error) {
|
|
3406
3852
|
return {
|
|
@@ -3568,23 +4014,23 @@ async function inspectCodebase(selectedFolder, timeoutMs, signal, expectedOrigin
|
|
|
3568
4014
|
}
|
|
3569
4015
|
var browseCodebaseDirectories = async (payload) => {
|
|
3570
4016
|
const input = codebaseBrowsePayload(payload);
|
|
3571
|
-
const homePath = await realpath2(
|
|
4017
|
+
const homePath = await realpath2(homedir4());
|
|
3572
4018
|
const path = await realpath2(input.path ? resolve2(input.path) : homePath);
|
|
3573
|
-
if (!(await
|
|
4019
|
+
if (!(await stat3(path)).isDirectory())
|
|
3574
4020
|
throw new Error("Path is not a directory");
|
|
3575
|
-
const candidates = await
|
|
4021
|
+
const candidates = await readdir3(path, { withFileTypes: true });
|
|
3576
4022
|
const entries = [];
|
|
3577
4023
|
for (const candidate of candidates) {
|
|
3578
4024
|
if (candidate.isDirectory()) {
|
|
3579
4025
|
entries.push({
|
|
3580
4026
|
name: candidate.name,
|
|
3581
|
-
path:
|
|
4027
|
+
path: join4(path, candidate.name),
|
|
3582
4028
|
hidden: candidate.name.startsWith(".")
|
|
3583
4029
|
});
|
|
3584
4030
|
} else if (candidate.isSymbolicLink()) {
|
|
3585
4031
|
try {
|
|
3586
|
-
const target2 =
|
|
3587
|
-
if ((await
|
|
4032
|
+
const target2 = join4(path, candidate.name);
|
|
4033
|
+
if ((await stat3(target2)).isDirectory()) {
|
|
3588
4034
|
entries.push({
|
|
3589
4035
|
name: candidate.name,
|
|
3590
4036
|
path: target2,
|
|
@@ -4154,14 +4600,14 @@ import {
|
|
|
4154
4600
|
mkdir as mkdir2,
|
|
4155
4601
|
readFile as readFile2,
|
|
4156
4602
|
realpath as realpath3,
|
|
4157
|
-
readdir as
|
|
4603
|
+
readdir as readdir4,
|
|
4158
4604
|
rename as rename2,
|
|
4159
4605
|
rm as rm2,
|
|
4160
|
-
stat as
|
|
4606
|
+
stat as stat4,
|
|
4161
4607
|
writeFile as writeFile2
|
|
4162
4608
|
} from "node:fs/promises";
|
|
4163
|
-
import { homedir as
|
|
4164
|
-
import { dirname as dirname4, isAbsolute as isAbsolute2, join as
|
|
4609
|
+
import { homedir as homedir5 } from "node:os";
|
|
4610
|
+
import { dirname as dirname4, isAbsolute as isAbsolute2, join as join5, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
|
|
4165
4611
|
import { promisify } from "node:util";
|
|
4166
4612
|
var executeFile = promisify(execFile);
|
|
4167
4613
|
var successfulProcess3 = {
|
|
@@ -4216,27 +4662,27 @@ var PROJECT_ROOTS = {
|
|
|
4216
4662
|
};
|
|
4217
4663
|
async function directoryExists(path) {
|
|
4218
4664
|
try {
|
|
4219
|
-
return (await
|
|
4665
|
+
return (await stat4(path)).isDirectory();
|
|
4220
4666
|
} catch {
|
|
4221
4667
|
return false;
|
|
4222
4668
|
}
|
|
4223
4669
|
}
|
|
4224
4670
|
async function configuredTools(tools) {
|
|
4225
|
-
const homePath =
|
|
4671
|
+
const homePath = homedir5();
|
|
4226
4672
|
return Promise.all(
|
|
4227
4673
|
AI_TOOLS.filter((tool) => tools.includes(tool)).map(async (tool) => ({
|
|
4228
4674
|
tool,
|
|
4229
|
-
configured: await directoryExists(
|
|
4675
|
+
configured: await directoryExists(join5(homePath, ...CONFIG_PATHS[tool])),
|
|
4230
4676
|
homePath
|
|
4231
4677
|
}))
|
|
4232
4678
|
);
|
|
4233
4679
|
}
|
|
4234
4680
|
function rootPath(location) {
|
|
4235
|
-
const base = location.scope === "GLOBAL" ?
|
|
4681
|
+
const base = location.scope === "GLOBAL" ? homedir5() : location.folder;
|
|
4236
4682
|
const parts = location.scope === "GLOBAL" ? GLOBAL_ROOTS[location.rootKind] : PROJECT_ROOTS[location.rootKind];
|
|
4237
4683
|
const path = resolve3(base, ...parts);
|
|
4238
4684
|
const normalizedBase = resolve3(base);
|
|
4239
|
-
if (path !== normalizedBase && !path.startsWith(`${normalizedBase}${
|
|
4685
|
+
if (path !== normalizedBase && !path.startsWith(`${normalizedBase}${sep2}`)) {
|
|
4240
4686
|
throw new Error("Skill root escaped its configured base directory");
|
|
4241
4687
|
}
|
|
4242
4688
|
return path;
|
|
@@ -4248,12 +4694,12 @@ async function listPackageFiles(skillDirectory) {
|
|
|
4248
4694
|
const files = [];
|
|
4249
4695
|
let totalBytes = 0;
|
|
4250
4696
|
const visit = async (directory) => {
|
|
4251
|
-
for (const entry of await
|
|
4252
|
-
const path =
|
|
4697
|
+
for (const entry of await readdir4(directory, { withFileTypes: true })) {
|
|
4698
|
+
const path = join5(directory, entry.name);
|
|
4253
4699
|
const metadata = await lstat2(path);
|
|
4254
4700
|
if (metadata.isSymbolicLink()) {
|
|
4255
4701
|
throw new Error(
|
|
4256
|
-
`Nested symbolic link is not supported: ${
|
|
4702
|
+
`Nested symbolic link is not supported: ${relative2(skillDirectory, path)}`
|
|
4257
4703
|
);
|
|
4258
4704
|
}
|
|
4259
4705
|
if (metadata.isDirectory()) {
|
|
@@ -4265,14 +4711,14 @@ async function listPackageFiles(skillDirectory) {
|
|
|
4265
4711
|
throw new Error("Skill contains too many files");
|
|
4266
4712
|
if (metadata.size > MAX_SKILL_FILE_BYTES) {
|
|
4267
4713
|
throw new Error(
|
|
4268
|
-
`${
|
|
4714
|
+
`${relative2(skillDirectory, path)} exceeds the per-file limit`
|
|
4269
4715
|
);
|
|
4270
4716
|
}
|
|
4271
4717
|
totalBytes += metadata.size;
|
|
4272
4718
|
if (totalBytes > MAX_SKILL_PACKAGE_BYTES)
|
|
4273
4719
|
throw new Error("Skill exceeds the package size limit");
|
|
4274
4720
|
files.push({
|
|
4275
|
-
path:
|
|
4721
|
+
path: relative2(skillDirectory, path).split(sep2).join("/"),
|
|
4276
4722
|
contentsBase64: (await readFile2(path)).toString("base64"),
|
|
4277
4723
|
executable: (metadata.mode & 73) !== 0
|
|
4278
4724
|
});
|
|
@@ -4295,7 +4741,7 @@ async function readPackage(directory) {
|
|
|
4295
4741
|
const frontmatter = parseSkillMetadata(
|
|
4296
4742
|
Buffer.from(definition.contentsBase64, "base64").toString("utf8")
|
|
4297
4743
|
);
|
|
4298
|
-
if (frontmatter.name !== directory.split(
|
|
4744
|
+
if (frontmatter.name !== directory.split(sep2).at(-1)) {
|
|
4299
4745
|
throw new Error("Skill name must match its directory name");
|
|
4300
4746
|
}
|
|
4301
4747
|
return {
|
|
@@ -4336,12 +4782,12 @@ async function scanRoot2(location, configured, targets, warnings) {
|
|
|
4336
4782
|
if (!consumers.length) return [];
|
|
4337
4783
|
const target2 = targetForFolder(targets, location.folder);
|
|
4338
4784
|
const installations = [];
|
|
4339
|
-
for (const entry of await
|
|
4785
|
+
for (const entry of await readdir4(root, { withFileTypes: true })) {
|
|
4340
4786
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
4341
|
-
const directory =
|
|
4787
|
+
const directory = join5(root, entry.name);
|
|
4342
4788
|
try {
|
|
4343
4789
|
const skillPackage = await readPackage(directory);
|
|
4344
|
-
const relativeDirectory = location.folder ?
|
|
4790
|
+
const relativeDirectory = location.folder ? relative2(location.folder, directory).split(sep2).join("/") : "";
|
|
4345
4791
|
installations.push({
|
|
4346
4792
|
...location,
|
|
4347
4793
|
codebaseId: target2?.codebaseId ?? null,
|
|
@@ -4417,7 +4863,7 @@ var readSkills = async (payload) => {
|
|
|
4417
4863
|
}
|
|
4418
4864
|
packages.push({
|
|
4419
4865
|
...request,
|
|
4420
|
-
package: await readPackage(
|
|
4866
|
+
package: await readPackage(join5(root, request.skillName))
|
|
4421
4867
|
});
|
|
4422
4868
|
}
|
|
4423
4869
|
return { ...successfulProcess3, packages };
|
|
@@ -4452,7 +4898,7 @@ async function updateManagedExclude(folder, relativeSkillPath, present) {
|
|
|
4452
4898
|
if (line.trim()) managed.add(line.trim());
|
|
4453
4899
|
}
|
|
4454
4900
|
}
|
|
4455
|
-
const pattern = `/${relativeSkillPath.split(
|
|
4901
|
+
const pattern = `/${relativeSkillPath.split(sep2).join("/").replace(/^\/+/, "")}/`;
|
|
4456
4902
|
if (present) managed.add(pattern);
|
|
4457
4903
|
else managed.delete(pattern);
|
|
4458
4904
|
const block = managed.size ? `${MANAGED_EXCLUDE_START}
|
|
@@ -4467,7 +4913,7 @@ ${MANAGED_EXCLUDE_END}` : "";
|
|
|
4467
4913
|
}
|
|
4468
4914
|
async function assertUntracked(location, skillDirectory) {
|
|
4469
4915
|
if (location.scope !== "PROJECT") return;
|
|
4470
|
-
const relativeDirectory =
|
|
4916
|
+
const relativeDirectory = relative2(location.folder, skillDirectory).split(sep2).join("/");
|
|
4471
4917
|
if (await trackedProjectPath(location.folder, relativeDirectory)) {
|
|
4472
4918
|
throw new Error(
|
|
4473
4919
|
`${relativeDirectory} is tracked by Git and cannot be managed automatically`
|
|
@@ -4479,16 +4925,16 @@ async function writePackage(location, skillPackage) {
|
|
|
4479
4925
|
throw new Error("New skill copies may only be written to shared roots");
|
|
4480
4926
|
}
|
|
4481
4927
|
const root = rootPath(location);
|
|
4482
|
-
const destination =
|
|
4928
|
+
const destination = join5(root, skillPackage.name);
|
|
4483
4929
|
await assertUntracked(location, destination);
|
|
4484
4930
|
await mkdir2(root, { recursive: true });
|
|
4485
|
-
const temporary =
|
|
4486
|
-
const backup =
|
|
4931
|
+
const temporary = join5(root, `.${skillPackage.name}.${randomUUID()}.tmp`);
|
|
4932
|
+
const backup = join5(root, `.${skillPackage.name}.${randomUUID()}.bak`);
|
|
4487
4933
|
await mkdir2(temporary, { recursive: true });
|
|
4488
4934
|
try {
|
|
4489
4935
|
for (const file of skillPackage.files) {
|
|
4490
4936
|
const path = resolve3(temporary, ...file.path.split("/"));
|
|
4491
|
-
if (!path.startsWith(`${temporary}${
|
|
4937
|
+
if (!path.startsWith(`${temporary}${sep2}`))
|
|
4492
4938
|
throw new Error("Skill file escaped temporary directory");
|
|
4493
4939
|
await mkdir2(dirname4(path), { recursive: true });
|
|
4494
4940
|
await writeFile2(path, Buffer.from(file.contentsBase64, "base64"));
|
|
@@ -4521,7 +4967,7 @@ var applySkills = async (payload) => {
|
|
|
4521
4967
|
for (const operation of operations) {
|
|
4522
4968
|
const root = rootPath(operation);
|
|
4523
4969
|
const skillName = operation.kind === "WRITE" ? operation.package.name : operation.skillName;
|
|
4524
|
-
const destination =
|
|
4970
|
+
const destination = join5(root, skillName);
|
|
4525
4971
|
await assertUntracked(operation, destination);
|
|
4526
4972
|
if (operation.kind === "WRITE") {
|
|
4527
4973
|
await writePackage(operation, operation.package);
|
|
@@ -4541,7 +4987,7 @@ var applySkills = async (payload) => {
|
|
|
4541
4987
|
if (operation.scope === "PROJECT" && operation.manageGitExclude) {
|
|
4542
4988
|
await updateManagedExclude(
|
|
4543
4989
|
operation.folder,
|
|
4544
|
-
|
|
4990
|
+
relative2(operation.folder, destination),
|
|
4545
4991
|
operation.kind === "WRITE"
|
|
4546
4992
|
);
|
|
4547
4993
|
}
|
|
@@ -4551,16 +4997,16 @@ var applySkills = async (payload) => {
|
|
|
4551
4997
|
|
|
4552
4998
|
// src/handlers/worktrees.ts
|
|
4553
4999
|
import { createWriteStream, watch } from "node:fs";
|
|
4554
|
-
import { mkdtemp, open, readFile as readFile3, realpath as realpath4, rm as rm3, stat as
|
|
5000
|
+
import { mkdtemp, open, readFile as readFile3, realpath as realpath4, rm as rm3, stat as stat5 } from "node:fs/promises";
|
|
4555
5001
|
import { tmpdir } from "node:os";
|
|
4556
|
-
import { basename as
|
|
5002
|
+
import { basename as basename3, dirname as dirname5, join as join6, relative as relative4 } from "node:path";
|
|
4557
5003
|
import { spawn as spawn4 } from "node:child_process";
|
|
4558
5004
|
|
|
4559
5005
|
// src/git-code-state.ts
|
|
4560
|
-
import { createHash as
|
|
5006
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
4561
5007
|
import { createReadStream as createReadStream2 } from "node:fs";
|
|
4562
5008
|
import { lstat as lstat3, readlink } from "node:fs/promises";
|
|
4563
|
-
import { isAbsolute as isAbsolute3, relative as
|
|
5009
|
+
import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve4, sep as sep3 } from "node:path";
|
|
4564
5010
|
import { spawn as spawn3 } from "node:child_process";
|
|
4565
5011
|
function gitEnvironment() {
|
|
4566
5012
|
return {
|
|
@@ -4574,8 +5020,8 @@ function remainingTimeoutMs(deadline) {
|
|
|
4574
5020
|
}
|
|
4575
5021
|
function containedPath(folder, path) {
|
|
4576
5022
|
const absolutePath = resolve4(folder, path);
|
|
4577
|
-
const difference =
|
|
4578
|
-
if (!difference || difference === ".." || difference.startsWith(`..${
|
|
5023
|
+
const difference = relative3(folder, absolutePath);
|
|
5024
|
+
if (!difference || difference === ".." || difference.startsWith(`..${sep3}`) || isAbsolute3(difference)) {
|
|
4579
5025
|
return null;
|
|
4580
5026
|
}
|
|
4581
5027
|
return absolutePath;
|
|
@@ -4684,7 +5130,7 @@ async function worktreeCodeStateHash(folder, timeoutMs, signal) {
|
|
|
4684
5130
|
if (head.exitCode !== 0 || untracked.exitCode !== 0 || submodules.exitCode !== 0 || operation.signal.aborted) {
|
|
4685
5131
|
return null;
|
|
4686
5132
|
}
|
|
4687
|
-
const hash =
|
|
5133
|
+
const hash = createHash3("sha256");
|
|
4688
5134
|
hash.update("head\0");
|
|
4689
5135
|
hash.update(head.stdout.trim());
|
|
4690
5136
|
hash.update("\0diff\0");
|
|
@@ -4963,7 +5409,7 @@ async function origin(folder, timeoutMs, signal) {
|
|
|
4963
5409
|
}
|
|
4964
5410
|
async function validateWorktree(input, timeoutMs, signal) {
|
|
4965
5411
|
const folder = await realpath4(input.folder);
|
|
4966
|
-
if (!(await
|
|
5412
|
+
if (!(await stat5(folder)).isDirectory())
|
|
4967
5413
|
throw new Error("Worktree is missing");
|
|
4968
5414
|
const observedGitDirectory = await gitDirectory(folder, timeoutMs, signal);
|
|
4969
5415
|
if (observedGitDirectory !== input.gitDirectory) {
|
|
@@ -5047,7 +5493,7 @@ async function inspectWorktreeItem(folderValue2, rootFolder, baseBranch, primary
|
|
|
5047
5493
|
return {
|
|
5048
5494
|
gitDirectory: gitDir,
|
|
5049
5495
|
folder,
|
|
5050
|
-
relativePath:
|
|
5496
|
+
relativePath: relative4(rootFolder, folder) || ".",
|
|
5051
5497
|
primary,
|
|
5052
5498
|
branch,
|
|
5053
5499
|
headSha: headResult.exitCode === 0 ? headResult.stdout.trim() : null,
|
|
@@ -5074,7 +5520,7 @@ async function inspectWorktreeItem(folderValue2, rootFolder, baseBranch, primary
|
|
|
5074
5520
|
return {
|
|
5075
5521
|
gitDirectory: folderValue2,
|
|
5076
5522
|
folder: folderValue2,
|
|
5077
|
-
relativePath:
|
|
5523
|
+
relativePath: relative4(rootFolder, folderValue2) || ".",
|
|
5078
5524
|
primary,
|
|
5079
5525
|
branch: null,
|
|
5080
5526
|
headSha: null,
|
|
@@ -5190,12 +5636,12 @@ function parseNumstat(value) {
|
|
|
5190
5636
|
}
|
|
5191
5637
|
async function untrackedLines(folder, path) {
|
|
5192
5638
|
try {
|
|
5193
|
-
const file = await
|
|
5639
|
+
const file = await stat5(`${folder}/${path}`);
|
|
5194
5640
|
if (!file.isFile() || file.size > 1024 * 1024) return null;
|
|
5195
5641
|
const contents = await readFile3(`${folder}/${path}`);
|
|
5196
5642
|
if (contents.includes(0)) return null;
|
|
5197
|
-
const
|
|
5198
|
-
return
|
|
5643
|
+
const text2 = contents.toString("utf8");
|
|
5644
|
+
return text2 ? text2.split("\n").length - (text2.endsWith("\n") ? 1 : 0) : 0;
|
|
5199
5645
|
} catch {
|
|
5200
5646
|
return null;
|
|
5201
5647
|
}
|
|
@@ -5401,7 +5847,7 @@ async function comparisonSides(input, folder, timeoutMs, signal) {
|
|
|
5401
5847
|
if (!input.path) return { before: null, after: null };
|
|
5402
5848
|
const path = input.path;
|
|
5403
5849
|
const previousPath = input.previousPath ?? path;
|
|
5404
|
-
const currentPath =
|
|
5850
|
+
const currentPath = join6(folder, path);
|
|
5405
5851
|
if (input.scope === "UNTRACKED") {
|
|
5406
5852
|
return { before: null, after: { kind: "FILE", path: currentPath } };
|
|
5407
5853
|
}
|
|
@@ -5485,8 +5931,8 @@ async function availableSide(side, folder, timeoutMs, signal) {
|
|
|
5485
5931
|
}
|
|
5486
5932
|
try {
|
|
5487
5933
|
const resolved = await realpath4(side.path);
|
|
5488
|
-
const difference =
|
|
5489
|
-
return difference !== ".." && !difference.startsWith("../") && (await
|
|
5934
|
+
const difference = relative4(folder, resolved);
|
|
5935
|
+
return difference !== ".." && !difference.startsWith("../") && (await stat5(resolved)).isFile();
|
|
5490
5936
|
} catch {
|
|
5491
5937
|
return false;
|
|
5492
5938
|
}
|
|
@@ -5557,7 +6003,7 @@ async function inspectRequestedDiff(input, folder, timeoutMs, signal) {
|
|
|
5557
6003
|
let patch;
|
|
5558
6004
|
if (input.scope === "UNTRACKED") {
|
|
5559
6005
|
const bounded = await readBoundedFile(
|
|
5560
|
-
|
|
6006
|
+
join6(folder, input.path),
|
|
5561
6007
|
MAX_DIFF_BYTES
|
|
5562
6008
|
);
|
|
5563
6009
|
const contents = bounded.contents;
|
|
@@ -5728,7 +6174,7 @@ var downloadWorktreeDiffAsset = async (payload, timeoutMs, signal, _onLog, conte
|
|
|
5728
6174
|
try {
|
|
5729
6175
|
if (selected.kind === "FILE") {
|
|
5730
6176
|
uploadPath = await realpath4(selected.path);
|
|
5731
|
-
const difference =
|
|
6177
|
+
const difference = relative4(folder, uploadPath);
|
|
5732
6178
|
if (difference === ".." || difference.startsWith("../")) {
|
|
5733
6179
|
throw new Error("Diff image resolves outside the worktree");
|
|
5734
6180
|
}
|
|
@@ -5745,8 +6191,8 @@ var downloadWorktreeDiffAsset = async (payload, timeoutMs, signal, _onLog, conte
|
|
|
5745
6191
|
if (!/^\d+$/.test(size) || Number(size) > 20 * 1024 * 1024) {
|
|
5746
6192
|
throw new Error("Diff image exceeds the 20 MiB limit");
|
|
5747
6193
|
}
|
|
5748
|
-
temporaryDirectory = await mkdtemp(
|
|
5749
|
-
uploadPath =
|
|
6194
|
+
temporaryDirectory = await mkdtemp(join6(tmpdir(), "ade-diff-image-"));
|
|
6195
|
+
uploadPath = join6(temporaryDirectory, basename3(input.path));
|
|
5750
6196
|
await writeGitObject(
|
|
5751
6197
|
folder,
|
|
5752
6198
|
selected.specification,
|
|
@@ -5755,14 +6201,14 @@ var downloadWorktreeDiffAsset = async (payload, timeoutMs, signal, _onLog, conte
|
|
|
5755
6201
|
signal
|
|
5756
6202
|
);
|
|
5757
6203
|
}
|
|
5758
|
-
const information = await
|
|
6204
|
+
const information = await stat5(uploadPath);
|
|
5759
6205
|
if (!information.isFile() || information.size > 20 * 1024 * 1024) {
|
|
5760
6206
|
throw new Error("Diff image exceeds the 20 MiB limit");
|
|
5761
6207
|
}
|
|
5762
6208
|
await context.uploadBuildArtifact({
|
|
5763
6209
|
uploadId: input.uploadId,
|
|
5764
6210
|
path: uploadPath,
|
|
5765
|
-
filename:
|
|
6211
|
+
filename: basename3(input.path),
|
|
5766
6212
|
contentType: imageContentType(input.path)
|
|
5767
6213
|
});
|
|
5768
6214
|
return successfulProcess4;
|
|
@@ -5906,7 +6352,7 @@ async function chooseNewBranch(folder, candidates, allowedCurrentBranch, timeout
|
|
|
5906
6352
|
}
|
|
5907
6353
|
async function validateBranchRoot(input, timeoutMs, signal) {
|
|
5908
6354
|
const rootFolder = await realpath4(input.rootFolder);
|
|
5909
|
-
if (!(await
|
|
6355
|
+
if (!(await stat5(rootFolder)).isDirectory()) {
|
|
5910
6356
|
throw new Error("Base repository is missing");
|
|
5911
6357
|
}
|
|
5912
6358
|
if (await origin(rootFolder, timeoutMs, signal) !== input.expectedOrigin) {
|
|
@@ -5969,13 +6415,13 @@ var branchWorktree = async (payload, timeoutMs, signal) => {
|
|
|
5969
6415
|
timeoutMs,
|
|
5970
6416
|
signal
|
|
5971
6417
|
) : input.candidates[0];
|
|
5972
|
-
const targetFolder = input.action === "CREATE" ?
|
|
6418
|
+
const targetFolder = input.action === "CREATE" ? join6(
|
|
5973
6419
|
dirname5(rootFolder),
|
|
5974
|
-
`${
|
|
6420
|
+
`${basename3(rootFolder)}-${branch.replaceAll("/", "-")}`
|
|
5975
6421
|
) : changeFolder;
|
|
5976
6422
|
if (input.action === "CREATE") {
|
|
5977
6423
|
try {
|
|
5978
|
-
await
|
|
6424
|
+
await stat5(targetFolder);
|
|
5979
6425
|
throw new Error(`Worktree folder already exists: ${targetFolder}`);
|
|
5980
6426
|
} catch (error) {
|
|
5981
6427
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
@@ -6209,12 +6655,12 @@ var checkoutMovedWorktree = async (payload, timeoutMs, signal) => {
|
|
|
6209
6655
|
`Branch ${input.branch} is already checked out in ${occupiedFolder}`
|
|
6210
6656
|
);
|
|
6211
6657
|
}
|
|
6212
|
-
targetFolder =
|
|
6658
|
+
targetFolder = join6(
|
|
6213
6659
|
dirname5(rootFolder),
|
|
6214
|
-
`${
|
|
6660
|
+
`${basename3(rootFolder)}-${input.branch.replaceAll("/", "-")}`
|
|
6215
6661
|
);
|
|
6216
6662
|
try {
|
|
6217
|
-
await
|
|
6663
|
+
await stat5(targetFolder);
|
|
6218
6664
|
throw new Error(`Worktree folder already exists: ${targetFolder}`);
|
|
6219
6665
|
} catch (error) {
|
|
6220
6666
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
@@ -6375,7 +6821,7 @@ var checkoutMovedWorktree = async (payload, timeoutMs, signal) => {
|
|
|
6375
6821
|
var deleteWorktree = async (payload, timeoutMs, signal) => {
|
|
6376
6822
|
const input = worktreeDeleteJobPayload(payload);
|
|
6377
6823
|
const rootFolder = await realpath4(input.rootFolder);
|
|
6378
|
-
if (!(await
|
|
6824
|
+
if (!(await stat5(rootFolder)).isDirectory()) {
|
|
6379
6825
|
throw new Error("Base repository is missing");
|
|
6380
6826
|
}
|
|
6381
6827
|
if (await origin(rootFolder, timeoutMs, signal) !== input.expectedOrigin) {
|
|
@@ -6569,30 +7015,31 @@ var operateWorktree = async (payload, timeoutMs, signal) => {
|
|
|
6569
7015
|
};
|
|
6570
7016
|
|
|
6571
7017
|
// src/handlers/builds.ts
|
|
6572
|
-
import { createHash as
|
|
7018
|
+
import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
|
|
6573
7019
|
import {
|
|
6574
7020
|
cp,
|
|
6575
7021
|
mkdir as mkdir3,
|
|
6576
7022
|
mkdtemp as mkdtemp2,
|
|
6577
7023
|
open as open2,
|
|
6578
|
-
readdir as
|
|
7024
|
+
readdir as readdir5,
|
|
6579
7025
|
readFile as readFile4,
|
|
6580
7026
|
realpath as realpath5,
|
|
6581
7027
|
rename as rename3,
|
|
6582
7028
|
rm as rm4,
|
|
6583
|
-
stat as
|
|
7029
|
+
stat as stat6,
|
|
6584
7030
|
writeFile as writeFile3
|
|
6585
7031
|
} from "node:fs/promises";
|
|
6586
|
-
import { createWriteStream as createWriteStream2 } from "node:fs";
|
|
7032
|
+
import { createReadStream as createReadStream3, createWriteStream as createWriteStream2 } from "node:fs";
|
|
6587
7033
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
7034
|
+
import { pipeline } from "node:stream/promises";
|
|
6588
7035
|
import {
|
|
6589
|
-
basename as
|
|
7036
|
+
basename as basename4,
|
|
6590
7037
|
dirname as dirname6,
|
|
6591
7038
|
isAbsolute as isAbsolute4,
|
|
6592
|
-
join as
|
|
6593
|
-
relative as
|
|
7039
|
+
join as join7,
|
|
7040
|
+
relative as relative5,
|
|
6594
7041
|
resolve as resolve5,
|
|
6595
|
-
sep as
|
|
7042
|
+
sep as sep4
|
|
6596
7043
|
} from "node:path";
|
|
6597
7044
|
import { spawn as spawn5 } from "node:child_process";
|
|
6598
7045
|
var successfulProcess5 = {
|
|
@@ -6651,7 +7098,7 @@ function requireSuccess3(result, fallback) {
|
|
|
6651
7098
|
}
|
|
6652
7099
|
async function validateWorktree2(input, timeoutMs, signal) {
|
|
6653
7100
|
const folder = await realpath5(input.folder);
|
|
6654
|
-
if (!(await
|
|
7101
|
+
if (!(await stat6(folder)).isDirectory())
|
|
6655
7102
|
throw new Error("Worktree is missing");
|
|
6656
7103
|
const gitDirectory2 = requireSuccess3(
|
|
6657
7104
|
await command2(
|
|
@@ -6685,8 +7132,8 @@ async function validateWorktree2(input, timeoutMs, signal) {
|
|
|
6685
7132
|
}
|
|
6686
7133
|
function containedPath2(root, relativePath) {
|
|
6687
7134
|
const target2 = resolve5(root, relativePath);
|
|
6688
|
-
const difference =
|
|
6689
|
-
if (difference === ".." || difference.startsWith(`..${
|
|
7135
|
+
const difference = relative5(root, target2);
|
|
7136
|
+
if (difference === ".." || difference.startsWith(`..${sep4}`) || isAbsolute4(difference)) {
|
|
6690
7137
|
throw new Error("Path must stay within the worktree");
|
|
6691
7138
|
}
|
|
6692
7139
|
return target2;
|
|
@@ -6694,11 +7141,11 @@ function containedPath2(root, relativePath) {
|
|
|
6694
7141
|
async function validateSource(root, source) {
|
|
6695
7142
|
const target2 = containedPath2(root, source.relativePath);
|
|
6696
7143
|
const resolved = await realpath5(target2);
|
|
6697
|
-
const difference =
|
|
6698
|
-
if (difference === ".." || difference.startsWith(`..${
|
|
7144
|
+
const difference = relative5(root, resolved);
|
|
7145
|
+
if (difference === ".." || difference.startsWith(`..${sep4}`) || isAbsolute4(difference)) {
|
|
6699
7146
|
throw new Error("Build source resolves outside the worktree");
|
|
6700
7147
|
}
|
|
6701
|
-
const information = await
|
|
7148
|
+
const information = await stat6(resolved);
|
|
6702
7149
|
if (source.kind === "PACKAGE" && !information.isFile()) {
|
|
6703
7150
|
throw new Error("Package.swift is missing");
|
|
6704
7151
|
}
|
|
@@ -6712,10 +7159,10 @@ async function discoverSourcesInFolder(folder) {
|
|
|
6712
7159
|
const queue = [folder];
|
|
6713
7160
|
while (queue.length && sources.length < DISCOVERY_LIMIT) {
|
|
6714
7161
|
const current = queue.shift();
|
|
6715
|
-
for (const entry of await
|
|
7162
|
+
for (const entry of await readdir5(current, { withFileTypes: true })) {
|
|
6716
7163
|
if (entry.isSymbolicLink()) continue;
|
|
6717
|
-
const absolute =
|
|
6718
|
-
const path =
|
|
7164
|
+
const absolute = join7(current, entry.name);
|
|
7165
|
+
const path = relative5(folder, absolute).split(sep4).join("/");
|
|
6719
7166
|
if (entry.isDirectory()) {
|
|
6720
7167
|
if (entry.name.endsWith(".xcodeproj")) {
|
|
6721
7168
|
sources.push({ kind: "PROJECT", relativePath: path });
|
|
@@ -6785,7 +7232,7 @@ function metadataFromList(value) {
|
|
|
6785
7232
|
};
|
|
6786
7233
|
}
|
|
6787
7234
|
async function workspaceProjectPaths(workspacePath2, folder) {
|
|
6788
|
-
const contentsPath =
|
|
7235
|
+
const contentsPath = join7(workspacePath2, "contents.xcworkspacedata");
|
|
6789
7236
|
let contents;
|
|
6790
7237
|
try {
|
|
6791
7238
|
contents = await readFile4(contentsPath, "utf8");
|
|
@@ -6807,14 +7254,14 @@ async function workspaceProjectPaths(workspacePath2, folder) {
|
|
|
6807
7254
|
const projects = [];
|
|
6808
7255
|
for (const location of locations) {
|
|
6809
7256
|
const candidate = resolve5(dirname6(workspacePath2), location);
|
|
6810
|
-
const difference =
|
|
6811
|
-
if (difference === ".." || difference.startsWith(`..${
|
|
7257
|
+
const difference = relative5(folder, candidate);
|
|
7258
|
+
if (difference === ".." || difference.startsWith(`..${sep4}`) || isAbsolute4(difference)) {
|
|
6812
7259
|
continue;
|
|
6813
7260
|
}
|
|
6814
7261
|
try {
|
|
6815
7262
|
const resolved = await realpath5(candidate);
|
|
6816
|
-
const resolvedDifference =
|
|
6817
|
-
if (resolvedDifference === ".." || resolvedDifference.startsWith(`..${
|
|
7263
|
+
const resolvedDifference = relative5(folder, resolved);
|
|
7264
|
+
if (resolvedDifference === ".." || resolvedDifference.startsWith(`..${sep4}`) || isAbsolute4(resolvedDifference) || !(await stat6(resolved)).isDirectory()) {
|
|
6818
7265
|
continue;
|
|
6819
7266
|
}
|
|
6820
7267
|
projects.push(resolved);
|
|
@@ -7029,8 +7476,8 @@ function genericBuildDestinations(action) {
|
|
|
7029
7476
|
];
|
|
7030
7477
|
}
|
|
7031
7478
|
async function listPhysicalDevices(timeoutMs, signal) {
|
|
7032
|
-
const directory = await mkdtemp2(
|
|
7033
|
-
const output =
|
|
7479
|
+
const directory = await mkdtemp2(join7(tmpdir2(), "ade-devices-"));
|
|
7480
|
+
const output = join7(directory, "devices.json");
|
|
7034
7481
|
try {
|
|
7035
7482
|
const result = await command2(
|
|
7036
7483
|
"xcrun",
|
|
@@ -7428,7 +7875,7 @@ function actionArgument(action) {
|
|
|
7428
7875
|
}[action];
|
|
7429
7876
|
}
|
|
7430
7877
|
function xcodeBuildArguments(input) {
|
|
7431
|
-
const resultBundle =
|
|
7878
|
+
const resultBundle = join7(input.artifactDirectory, "result.xcresult");
|
|
7432
7879
|
const sourcePath = containedPath2(input.folder, input.source.relativePath);
|
|
7433
7880
|
const usesCapturedTestProducts = input.action === "TEST_WITHOUT_BUILDING";
|
|
7434
7881
|
const args = [
|
|
@@ -7450,13 +7897,13 @@ function xcodeBuildArguments(input) {
|
|
|
7450
7897
|
if (input.action === "ARCHIVE") {
|
|
7451
7898
|
args.push(
|
|
7452
7899
|
"-archivePath",
|
|
7453
|
-
|
|
7900
|
+
join7(input.artifactDirectory, "archive.xcarchive")
|
|
7454
7901
|
);
|
|
7455
7902
|
}
|
|
7456
7903
|
if (input.action === "BUILD_FOR_TESTING") {
|
|
7457
7904
|
args.push(
|
|
7458
7905
|
"-testProductsPath",
|
|
7459
|
-
|
|
7906
|
+
join7(input.artifactDirectory, "test-products.xctestproducts")
|
|
7460
7907
|
);
|
|
7461
7908
|
}
|
|
7462
7909
|
if (input.action === "TEST_WITHOUT_BUILDING" && input.advancedSettings.priorTestProductsPath) {
|
|
@@ -7519,15 +7966,15 @@ function classifyFailure(output) {
|
|
|
7519
7966
|
}
|
|
7520
7967
|
async function runHook(options) {
|
|
7521
7968
|
const phaseDirectory = options.phase === "PRE_BUILD" ? "pre" : "post";
|
|
7522
|
-
const directory =
|
|
7969
|
+
const directory = join7(
|
|
7523
7970
|
options.input.artifactDirectory,
|
|
7524
7971
|
"hooks",
|
|
7525
7972
|
phaseDirectory
|
|
7526
7973
|
);
|
|
7527
7974
|
const prefix = `${String(options.script.position).padStart(3, "0")}-${options.script.id}`;
|
|
7528
|
-
const file =
|
|
7529
|
-
const runner =
|
|
7530
|
-
const log =
|
|
7975
|
+
const file = join7(directory, `${prefix}.mjs`);
|
|
7976
|
+
const runner = join7(directory, `${prefix}.runner.mjs`);
|
|
7977
|
+
const log = join7(directory, `${prefix}.log`);
|
|
7531
7978
|
const started = Date.now();
|
|
7532
7979
|
try {
|
|
7533
7980
|
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
@@ -7542,7 +7989,7 @@ async function runHook(options) {
|
|
|
7542
7989
|
await writeFile3(
|
|
7543
7990
|
runner,
|
|
7544
7991
|
`import { readFile } from "node:fs/promises";
|
|
7545
|
-
const hookModule = await import(${JSON.stringify(`./${
|
|
7992
|
+
const hookModule = await import(${JSON.stringify(`./${basename4(file)}`)});
|
|
7546
7993
|
if (hookModule.default !== undefined && typeof hookModule.default !== "function") {
|
|
7547
7994
|
throw new TypeError("The default build hook export must be a function");
|
|
7548
7995
|
}
|
|
@@ -7579,7 +8026,7 @@ if (typeof hookModule.default === "function") {
|
|
|
7579
8026
|
exitCode: result.exitCode,
|
|
7580
8027
|
durationMs: Date.now() - started,
|
|
7581
8028
|
causedBuildFailure: failed && options.script.failureBehavior === "FAIL_BUILD",
|
|
7582
|
-
outputRelativePath:
|
|
8029
|
+
outputRelativePath: relative5(options.input.artifactDirectory, log),
|
|
7583
8030
|
error: failed ? cleanError3(result.output || "Build hook failed") : null
|
|
7584
8031
|
};
|
|
7585
8032
|
} catch (hookError) {
|
|
@@ -7592,44 +8039,75 @@ if (typeof hookModule.default === "function") {
|
|
|
7592
8039
|
exitCode: null,
|
|
7593
8040
|
durationMs: Date.now() - started,
|
|
7594
8041
|
causedBuildFailure: !options.signal.aborted && options.script.failureBehavior === "FAIL_BUILD",
|
|
7595
|
-
outputRelativePath:
|
|
8042
|
+
outputRelativePath: relative5(options.input.artifactDirectory, log),
|
|
7596
8043
|
error
|
|
7597
8044
|
};
|
|
7598
8045
|
}
|
|
7599
8046
|
}
|
|
7600
8047
|
async function pathExists(path) {
|
|
7601
8048
|
try {
|
|
7602
|
-
await
|
|
8049
|
+
await stat6(path);
|
|
7603
8050
|
return true;
|
|
7604
8051
|
} catch {
|
|
7605
8052
|
return false;
|
|
7606
8053
|
}
|
|
7607
8054
|
}
|
|
7608
8055
|
async function pathSize(path) {
|
|
7609
|
-
const information = await
|
|
8056
|
+
const information = await stat6(path);
|
|
7610
8057
|
if (information.isFile()) return information.size;
|
|
7611
8058
|
if (!information.isDirectory()) return 0;
|
|
7612
8059
|
let size = 0;
|
|
7613
|
-
for (const entry of await
|
|
8060
|
+
for (const entry of await readdir5(path, { withFileTypes: true })) {
|
|
7614
8061
|
if (entry.isSymbolicLink()) continue;
|
|
7615
|
-
size += await pathSize(
|
|
8062
|
+
size += await pathSize(join7(path, entry.name));
|
|
7616
8063
|
}
|
|
7617
8064
|
return size;
|
|
7618
8065
|
}
|
|
7619
8066
|
async function fileChecksum(path) {
|
|
7620
8067
|
try {
|
|
7621
|
-
const information = await
|
|
7622
|
-
if (!information.isFile()
|
|
7623
|
-
|
|
7624
|
-
|
|
8068
|
+
const information = await stat6(path);
|
|
8069
|
+
if (!information.isFile()) return null;
|
|
8070
|
+
const hash = createHash4("sha256");
|
|
8071
|
+
await pipeline(createReadStream3(path), hash);
|
|
8072
|
+
return hash.digest("hex");
|
|
8073
|
+
} catch {
|
|
8074
|
+
return null;
|
|
8075
|
+
}
|
|
8076
|
+
}
|
|
8077
|
+
async function plistString(plistPath, keyPath, timeoutMs, signal) {
|
|
8078
|
+
try {
|
|
8079
|
+
const result = await command2(
|
|
8080
|
+
"/usr/bin/plutil",
|
|
8081
|
+
["-extract", keyPath, "raw", "-o", "-", plistPath],
|
|
8082
|
+
Math.min(timeoutMs, 5e3),
|
|
8083
|
+
signal
|
|
8084
|
+
);
|
|
8085
|
+
const value = result.stdout.trim();
|
|
8086
|
+
return result.exitCode === 0 && value ? value : null;
|
|
7625
8087
|
} catch {
|
|
7626
8088
|
return null;
|
|
7627
8089
|
}
|
|
7628
8090
|
}
|
|
8091
|
+
async function archiveApplicationProperties(archivePath, timeoutMs, signal) {
|
|
8092
|
+
const plistPath = join7(archivePath, "Info.plist");
|
|
8093
|
+
const read = (key) => plistString(plistPath, `ApplicationProperties.${key}`, timeoutMs, signal);
|
|
8094
|
+
const [bundleIdentifier2, bundleShortVersion, bundleVersion, applicationPath] = await Promise.all([
|
|
8095
|
+
read("CFBundleIdentifier"),
|
|
8096
|
+
read("CFBundleShortVersionString"),
|
|
8097
|
+
read("CFBundleVersion"),
|
|
8098
|
+
read("ApplicationPath")
|
|
8099
|
+
]);
|
|
8100
|
+
return {
|
|
8101
|
+
bundleIdentifier: bundleIdentifier2,
|
|
8102
|
+
bundleShortVersion,
|
|
8103
|
+
bundleVersion,
|
|
8104
|
+
applicationName: applicationPath ? basename4(applicationPath).replace(/\.app$/, "") : null
|
|
8105
|
+
};
|
|
8106
|
+
}
|
|
7629
8107
|
async function artifact(root, kind, path, metadata = {}) {
|
|
7630
8108
|
return {
|
|
7631
8109
|
kind,
|
|
7632
|
-
relativePath:
|
|
8110
|
+
relativePath: relative5(root, path).split(sep4).join("/"),
|
|
7633
8111
|
sizeBytes: await pathSize(path),
|
|
7634
8112
|
checksum: await fileChecksum(path),
|
|
7635
8113
|
metadata
|
|
@@ -7657,7 +8135,7 @@ function testSourceFile(node, suite) {
|
|
|
7657
8135
|
node.sourceFile
|
|
7658
8136
|
].find((value) => typeof value === "string" && !!value);
|
|
7659
8137
|
if (explicit) {
|
|
7660
|
-
return { file:
|
|
8138
|
+
return { file: basename4(explicit), filePath: explicit };
|
|
7661
8139
|
}
|
|
7662
8140
|
const identifier = typeof node.nodeIdentifier === "string" ? node.nodeIdentifier : "";
|
|
7663
8141
|
const identifierOwner = identifier.split("/")[0]?.trim();
|
|
@@ -7770,9 +8248,9 @@ function normalizeCoverage(value) {
|
|
|
7770
8248
|
};
|
|
7771
8249
|
}
|
|
7772
8250
|
async function writeJsonReport(input, kind, timeoutMs, signal) {
|
|
7773
|
-
const resultBundle =
|
|
8251
|
+
const resultBundle = join7(input.artifactDirectory, "result.xcresult");
|
|
7774
8252
|
const filename = kind === "TEST_RESULTS" ? "test-results.json" : "code-coverage.json";
|
|
7775
|
-
const destination =
|
|
8253
|
+
const destination = join7(input.artifactDirectory, filename);
|
|
7776
8254
|
const temporary = `${destination}.tmp-${randomUUID2()}`;
|
|
7777
8255
|
try {
|
|
7778
8256
|
if (!await pathExists(resultBundle)) {
|
|
@@ -7907,7 +8385,7 @@ async function snapshotCoverageChanges(input, folder, timeoutMs, signal) {
|
|
|
7907
8385
|
const types = changeTypesFromStatus(status2.stdout);
|
|
7908
8386
|
for (const path of untracked.stdout.split("\0").filter(Boolean)) {
|
|
7909
8387
|
try {
|
|
7910
|
-
const contents = await readFile4(
|
|
8388
|
+
const contents = await readFile4(join7(folder, path));
|
|
7911
8389
|
if (contents.includes(0)) continue;
|
|
7912
8390
|
const lineCount = contents.length ? contents.toString("utf8").split("\n").length - (contents.toString("utf8").endsWith("\n") ? 1 : 0) : 0;
|
|
7913
8391
|
lines.set(
|
|
@@ -7936,12 +8414,12 @@ async function addChangedCoverage(report, changes, input, folder, timeoutMs, sig
|
|
|
7936
8414
|
let changedExecutableLines = 0;
|
|
7937
8415
|
for (const change of changes) {
|
|
7938
8416
|
const coveragePath = coveragePaths.find(
|
|
7939
|
-
(candidate) =>
|
|
8417
|
+
(candidate) => relative5(folder, candidate).split(sep4).join("/") === change.path
|
|
7940
8418
|
);
|
|
7941
8419
|
let covered = 0;
|
|
7942
8420
|
let executable = 0;
|
|
7943
8421
|
if (coveragePath && change.lines.length) {
|
|
7944
|
-
const temporary2 =
|
|
8422
|
+
const temporary2 = join7(
|
|
7945
8423
|
input.artifactDirectory,
|
|
7946
8424
|
`.changed-coverage-${randomUUID2()}.json`
|
|
7947
8425
|
);
|
|
@@ -8002,7 +8480,7 @@ async function addChangedCoverage(report, changes, input, folder, timeoutMs, sig
|
|
|
8002
8480
|
changedLineCoverage: changedExecutableLines ? changedCoveredLines / changedExecutableLines : null
|
|
8003
8481
|
};
|
|
8004
8482
|
const data = { ...report.data, changedFiles };
|
|
8005
|
-
const destination =
|
|
8483
|
+
const destination = join7(input.artifactDirectory, "worktree-coverage.json");
|
|
8006
8484
|
const temporary = `${destination}.tmp-${randomUUID2()}`;
|
|
8007
8485
|
await writeFile3(temporary, JSON.stringify({ summary, data }, null, 2), {
|
|
8008
8486
|
mode: 384
|
|
@@ -8050,13 +8528,13 @@ async function findFiles(root, predicate, limit = 20) {
|
|
|
8050
8528
|
const current = queue.shift();
|
|
8051
8529
|
let entries;
|
|
8052
8530
|
try {
|
|
8053
|
-
entries = await
|
|
8531
|
+
entries = await readdir5(current, { withFileTypes: true });
|
|
8054
8532
|
} catch {
|
|
8055
8533
|
continue;
|
|
8056
8534
|
}
|
|
8057
8535
|
for (const entry of entries) {
|
|
8058
8536
|
if (entry.isSymbolicLink()) continue;
|
|
8059
|
-
const path =
|
|
8537
|
+
const path = join7(current, entry.name);
|
|
8060
8538
|
if (entry.isDirectory()) queue.push(path);
|
|
8061
8539
|
else if (entry.isFile() && predicate(entry.name)) results.push(path);
|
|
8062
8540
|
if (results.length >= limit) break;
|
|
@@ -8066,7 +8544,7 @@ async function findFiles(root, predicate, limit = 20) {
|
|
|
8066
8544
|
}
|
|
8067
8545
|
async function captureArtifacts(input, folder, signal, includeProducts = true) {
|
|
8068
8546
|
const artifacts = [];
|
|
8069
|
-
const resultBundle =
|
|
8547
|
+
const resultBundle = join7(input.artifactDirectory, "result.xcresult");
|
|
8070
8548
|
if (await pathExists(resultBundle)) {
|
|
8071
8549
|
const coverageProbe = await command2(
|
|
8072
8550
|
"xcrun",
|
|
@@ -8093,7 +8571,7 @@ async function captureArtifacts(input, folder, signal, includeProducts = true) {
|
|
|
8093
8571
|
);
|
|
8094
8572
|
}
|
|
8095
8573
|
if (!includeProducts) return artifacts;
|
|
8096
|
-
const archivePath =
|
|
8574
|
+
const archivePath = join7(input.artifactDirectory, "archive.xcarchive");
|
|
8097
8575
|
if (await pathExists(archivePath)) {
|
|
8098
8576
|
artifacts.push(
|
|
8099
8577
|
await artifact(input.artifactDirectory, "ARCHIVE", archivePath)
|
|
@@ -8116,10 +8594,10 @@ async function captureArtifacts(input, folder, signal, includeProducts = true) {
|
|
|
8116
8594
|
if (apps.length === 1) {
|
|
8117
8595
|
const settings = apps[0].buildSettings;
|
|
8118
8596
|
const productName = settings.FULL_PRODUCT_NAME ?? settings.WRAPPER_NAME;
|
|
8119
|
-
const source = settings.TARGET_BUILD_DIR && productName ?
|
|
8597
|
+
const source = settings.TARGET_BUILD_DIR && productName ? join7(settings.TARGET_BUILD_DIR, productName) : null;
|
|
8120
8598
|
if (source && await pathExists(source)) {
|
|
8121
|
-
const productDirectory =
|
|
8122
|
-
const destination =
|
|
8599
|
+
const productDirectory = join7(input.artifactDirectory, "products");
|
|
8600
|
+
const destination = join7(productDirectory, basename4(source));
|
|
8123
8601
|
await mkdir3(productDirectory, { recursive: true, mode: 448 });
|
|
8124
8602
|
await rm4(destination, { recursive: true, force: true });
|
|
8125
8603
|
await cp(source, destination, {
|
|
@@ -8136,7 +8614,7 @@ async function captureArtifacts(input, folder, signal, includeProducts = true) {
|
|
|
8136
8614
|
}
|
|
8137
8615
|
}
|
|
8138
8616
|
if (input.action === "BUILD_FOR_TESTING") {
|
|
8139
|
-
const testDirectory =
|
|
8617
|
+
const testDirectory = join7(
|
|
8140
8618
|
input.artifactDirectory,
|
|
8141
8619
|
"test-products.xctestproducts"
|
|
8142
8620
|
);
|
|
@@ -8151,7 +8629,7 @@ async function captureArtifacts(input, folder, signal, includeProducts = true) {
|
|
|
8151
8629
|
)) {
|
|
8152
8630
|
artifacts.push(
|
|
8153
8631
|
await artifact(input.artifactDirectory, "XCTESTRUN", file, {
|
|
8154
|
-
testPlan:
|
|
8632
|
+
testPlan: basename4(file, ".xctestrun")
|
|
8155
8633
|
})
|
|
8156
8634
|
);
|
|
8157
8635
|
}
|
|
@@ -8171,14 +8649,14 @@ var runIosBuild = async (payload, timeoutMs, signal, onLog, context) => {
|
|
|
8171
8649
|
const testProductsPath = input.advancedSettings.priorTestProductsPath;
|
|
8172
8650
|
const xctestrunPath = input.advancedSettings.priorXctestrunPath;
|
|
8173
8651
|
if (testProductsPath) {
|
|
8174
|
-
if (!testProductsPath.endsWith(".xctestproducts") || !await pathExists(testProductsPath) || !(await
|
|
8652
|
+
if (!testProductsPath.endsWith(".xctestproducts") || !await pathExists(testProductsPath) || !(await stat6(testProductsPath)).isDirectory()) {
|
|
8175
8653
|
throw new Error("The captured test-products artifact is unavailable");
|
|
8176
8654
|
}
|
|
8177
|
-
} else if (!xctestrunPath?.endsWith(".xctestrun") || !await pathExists(xctestrunPath) || !(await
|
|
8655
|
+
} else if (!xctestrunPath?.endsWith(".xctestrun") || !await pathExists(xctestrunPath) || !(await stat6(xctestrunPath)).isFile()) {
|
|
8178
8656
|
throw new Error("The captured .xctestrun artifact is unavailable");
|
|
8179
8657
|
}
|
|
8180
8658
|
}
|
|
8181
|
-
if (!isAbsolute4(input.artifactDirectory) ||
|
|
8659
|
+
if (!isAbsolute4(input.artifactDirectory) || basename4(input.artifactDirectory) !== input.buildId) {
|
|
8182
8660
|
throw new Error("Build artifact directory is invalid");
|
|
8183
8661
|
}
|
|
8184
8662
|
const sourceStateHash = await worktreeCodeStateHash(
|
|
@@ -8187,11 +8665,11 @@ var runIosBuild = async (payload, timeoutMs, signal, onLog, context) => {
|
|
|
8187
8665
|
signal
|
|
8188
8666
|
);
|
|
8189
8667
|
await mkdir3(input.artifactDirectory, { recursive: true, mode: 448 });
|
|
8190
|
-
const rawLog =
|
|
8668
|
+
const rawLog = join7(input.artifactDirectory, "build.log");
|
|
8191
8669
|
const logger = new BuildLogger(input.buildId, context, rawLog, process.env);
|
|
8192
8670
|
try {
|
|
8193
8671
|
const scriptExecutions = [];
|
|
8194
|
-
const contextPath =
|
|
8672
|
+
const contextPath = join7(input.artifactDirectory, "context.json");
|
|
8195
8673
|
const baseHookContext = {
|
|
8196
8674
|
buildId: input.buildId,
|
|
8197
8675
|
branch: input.branch ?? null,
|
|
@@ -8424,7 +8902,7 @@ var runIosBuild = async (payload, timeoutMs, signal, onLog, context) => {
|
|
|
8424
8902
|
};
|
|
8425
8903
|
var generateIosBuildReport = async (payload, timeoutMs, signal, onLog) => {
|
|
8426
8904
|
const input = parseBuildReportPayload(payload);
|
|
8427
|
-
if (!isAbsolute4(input.artifactDirectory) ||
|
|
8905
|
+
if (!isAbsolute4(input.artifactDirectory) || basename4(input.artifactDirectory) !== input.buildId) {
|
|
8428
8906
|
throw new Error("Build artifact directory is invalid");
|
|
8429
8907
|
}
|
|
8430
8908
|
if (signal.aborted) return { ...successfulProcess5, cancelled: true };
|
|
@@ -8467,25 +8945,25 @@ var downloadIosBuildArtifact = async (payload, timeoutMs, signal, onLog, context
|
|
|
8467
8945
|
const target2 = await realpath5(
|
|
8468
8946
|
containedPath2(root, input.artifactRelativePath)
|
|
8469
8947
|
);
|
|
8470
|
-
const difference =
|
|
8471
|
-
if (!difference || difference === ".." || difference.startsWith(`..${
|
|
8948
|
+
const difference = relative5(root, target2);
|
|
8949
|
+
if (!difference || difference === ".." || difference.startsWith(`..${sep4}`) || isAbsolute4(difference)) {
|
|
8472
8950
|
throw new Error("Build artifact resolves outside the build folder");
|
|
8473
8951
|
}
|
|
8474
|
-
const information = await
|
|
8952
|
+
const information = await stat6(target2);
|
|
8475
8953
|
let uploadPath = target2;
|
|
8476
|
-
let filename =
|
|
8954
|
+
let filename = basename4(target2);
|
|
8477
8955
|
let contentType = "application/octet-stream";
|
|
8478
8956
|
let temporaryArchive = null;
|
|
8479
8957
|
try {
|
|
8480
8958
|
if (information.isDirectory()) {
|
|
8481
|
-
temporaryArchive =
|
|
8959
|
+
temporaryArchive = join7(
|
|
8482
8960
|
tmpdir2(),
|
|
8483
8961
|
`ade-build-artifact-${randomUUID2()}.tar.gz`
|
|
8484
8962
|
);
|
|
8485
8963
|
requireSuccess3(
|
|
8486
8964
|
await command2(
|
|
8487
8965
|
"tar",
|
|
8488
|
-
["-czf", temporaryArchive, "-C", dirname6(target2),
|
|
8966
|
+
["-czf", temporaryArchive, "-C", dirname6(target2), basename4(target2)],
|
|
8489
8967
|
timeoutMs,
|
|
8490
8968
|
signal
|
|
8491
8969
|
),
|
|
@@ -8548,24 +9026,24 @@ var deployIosBuild = async (payload, timeoutMs, signal, _onLog, context) => {
|
|
|
8548
9026
|
if (!await pathExists(appPath) || !appPath.endsWith(".app")) {
|
|
8549
9027
|
throw new Error("Runnable app artifact is missing");
|
|
8550
9028
|
}
|
|
8551
|
-
const deploymentsDirectory =
|
|
9029
|
+
const deploymentsDirectory = join7(input.artifactDirectory, "deployments");
|
|
8552
9030
|
await mkdir3(deploymentsDirectory, { recursive: true, mode: 448 });
|
|
8553
9031
|
const logger = new BuildLogger(
|
|
8554
9032
|
input.buildId,
|
|
8555
9033
|
context,
|
|
8556
|
-
|
|
9034
|
+
join7(deploymentsDirectory, "deployments.log"),
|
|
8557
9035
|
process.env
|
|
8558
9036
|
);
|
|
8559
9037
|
try {
|
|
8560
9038
|
const outcomes = [];
|
|
8561
9039
|
for (const deployment of input.deployments) {
|
|
8562
|
-
const directory =
|
|
9040
|
+
const directory = join7(
|
|
8563
9041
|
input.artifactDirectory,
|
|
8564
9042
|
"deployments",
|
|
8565
9043
|
deployment.id
|
|
8566
9044
|
);
|
|
8567
9045
|
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
8568
|
-
const logPath =
|
|
9046
|
+
const logPath = join7(directory, "deployment.log");
|
|
8569
9047
|
const started = Date.now();
|
|
8570
9048
|
let failure = null;
|
|
8571
9049
|
try {
|
|
@@ -8719,7 +9197,7 @@ var deployIosBuild = async (payload, timeoutMs, signal, _onLog, context) => {
|
|
|
8719
9197
|
status: signal.aborted ? "CANCELLED" : failure ? "FAILED" : "SUCCEEDED",
|
|
8720
9198
|
error: failure,
|
|
8721
9199
|
durationMs: Date.now() - started,
|
|
8722
|
-
outputRelativePath:
|
|
9200
|
+
outputRelativePath: relative5(input.artifactDirectory, logPath)
|
|
8723
9201
|
});
|
|
8724
9202
|
if (signal.aborted) break;
|
|
8725
9203
|
}
|
|
@@ -8735,17 +9213,6 @@ var deployIosBuild = async (payload, timeoutMs, signal, _onLog, context) => {
|
|
|
8735
9213
|
await logger.close();
|
|
8736
9214
|
}
|
|
8737
9215
|
};
|
|
8738
|
-
function xml(value) {
|
|
8739
|
-
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
8740
|
-
}
|
|
8741
|
-
function plistValue(value) {
|
|
8742
|
-
if (typeof value === "boolean") return value ? "<true/>" : "<false/>";
|
|
8743
|
-
if (typeof value === "string") return `<string>${xml(value)}</string>`;
|
|
8744
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
8745
|
-
return `<dict>${Object.entries(value).map(([key, entry]) => `<key>${xml(key)}</key>${plistValue(entry)}`).join("")}</dict>`;
|
|
8746
|
-
}
|
|
8747
|
-
throw new Error("Unsupported export plist value");
|
|
8748
|
-
}
|
|
8749
9216
|
function exportPlist(settings) {
|
|
8750
9217
|
const method = {
|
|
8751
9218
|
DEBUGGING: "debugging",
|
|
@@ -8767,10 +9234,7 @@ function exportPlist(settings) {
|
|
|
8767
9234
|
if (Object.keys(settings.provisioningProfiles).length) {
|
|
8768
9235
|
values.provisioningProfiles = settings.provisioningProfiles;
|
|
8769
9236
|
}
|
|
8770
|
-
return
|
|
8771
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
8772
|
-
<plist version="1.0">${plistValue(values)}</plist>
|
|
8773
|
-
`;
|
|
9237
|
+
return plistDocument(values);
|
|
8774
9238
|
}
|
|
8775
9239
|
var exportIosArchive = async (payload, timeoutMs, signal, _onLog, context) => {
|
|
8776
9240
|
const input = parseBuildExportPayload(payload);
|
|
@@ -8786,14 +9250,14 @@ var exportIosArchive = async (payload, timeoutMs, signal, _onLog, context) => {
|
|
|
8786
9250
|
if (!await pathExists(archivePath) || !archivePath.endsWith(".xcarchive")) {
|
|
8787
9251
|
throw new Error("Archive artifact is missing");
|
|
8788
9252
|
}
|
|
8789
|
-
const exportDirectory =
|
|
9253
|
+
const exportDirectory = join7(
|
|
8790
9254
|
input.artifactDirectory,
|
|
8791
9255
|
"exports",
|
|
8792
9256
|
input.exportId
|
|
8793
9257
|
);
|
|
8794
9258
|
await mkdir3(exportDirectory, { recursive: true, mode: 448 });
|
|
8795
|
-
const plistPath =
|
|
8796
|
-
const logPath =
|
|
9259
|
+
const plistPath = join7(exportDirectory, "ExportOptions.plist");
|
|
9260
|
+
const logPath = join7(exportDirectory, "export.log");
|
|
8797
9261
|
await writeFile3(plistPath, exportPlist(input.settings), { mode: 384 });
|
|
8798
9262
|
const logger = new BuildLogger(input.buildId, context, logPath, process.env);
|
|
8799
9263
|
try {
|
|
@@ -8825,14 +9289,30 @@ var exportIosArchive = async (payload, timeoutMs, signal, _onLog, context) => {
|
|
|
8825
9289
|
signal: result.signal,
|
|
8826
9290
|
timedOut: result.timedOut,
|
|
8827
9291
|
cancelled: result.cancelled,
|
|
8828
|
-
outputRelativePath:
|
|
9292
|
+
outputRelativePath: relative5(input.artifactDirectory, exportDirectory),
|
|
8829
9293
|
sizeBytes: result.exitCode === 0 ? await pathSize(exportDirectory) : null,
|
|
8830
|
-
error: result.exitCode === 0 ? null : cleanError3(result.output)
|
|
9294
|
+
error: result.exitCode === 0 ? null : cleanError3(result.output),
|
|
9295
|
+
artifacts: result.exitCode === 0 ? await exportedArtifacts(input, exportDirectory, archivePath, signal) : []
|
|
8831
9296
|
};
|
|
8832
9297
|
} finally {
|
|
8833
9298
|
await logger.close();
|
|
8834
9299
|
}
|
|
8835
9300
|
};
|
|
9301
|
+
async function exportedArtifacts(input, exportDirectory, archivePath, signal) {
|
|
9302
|
+
const [ipaPath] = await findFiles(
|
|
9303
|
+
exportDirectory,
|
|
9304
|
+
(name) => name.endsWith(".ipa"),
|
|
9305
|
+
5
|
|
9306
|
+
);
|
|
9307
|
+
if (!ipaPath) return [];
|
|
9308
|
+
return [
|
|
9309
|
+
await artifact(input.artifactDirectory, "IPA", ipaPath, {
|
|
9310
|
+
exportId: input.exportId,
|
|
9311
|
+
exportMethod: input.settings.method,
|
|
9312
|
+
...await archiveApplicationProperties(archivePath, 1e4, signal)
|
|
9313
|
+
})
|
|
9314
|
+
];
|
|
9315
|
+
}
|
|
8836
9316
|
|
|
8837
9317
|
// src/handlers/index.ts
|
|
8838
9318
|
var handlers = {
|
|
@@ -8869,7 +9349,8 @@ var handlers = {
|
|
|
8869
9349
|
[IOS_DEPLOY_JOB_KIND]: deployIosBuild,
|
|
8870
9350
|
[IOS_EXPORT_JOB_KIND]: exportIosArchive,
|
|
8871
9351
|
[IOS_TEST_RESULTS_JOB_KIND]: generateIosBuildReport,
|
|
8872
|
-
[IOS_COVERAGE_REPORT_JOB_KIND]: generateIosBuildReport
|
|
9352
|
+
[IOS_COVERAGE_REPORT_JOB_KIND]: generateIosBuildReport,
|
|
9353
|
+
[IOS_SIGNING_INSPECT_JOB_KIND]: inspectIosSigning
|
|
8873
9354
|
};
|
|
8874
9355
|
|
|
8875
9356
|
// src/repository-coordinator.ts
|