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