xcodebuild-axi 0.1.0
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/CHANGELOG.md +35 -0
- package/LICENSE +21 -0
- package/README.md +304 -0
- package/dist/bin/xcodebuild-axi.d.ts +2 -0
- package/dist/bin/xcodebuild-axi.js +8 -0
- package/dist/bin/xcodebuild-axi.js.map +1 -0
- package/dist/src/action.d.ts +68 -0
- package/dist/src/action.js +373 -0
- package/dist/src/action.js.map +1 -0
- package/dist/src/archive.d.ts +33 -0
- package/dist/src/archive.js +77 -0
- package/dist/src/archive.js.map +1 -0
- package/dist/src/args.d.ts +23 -0
- package/dist/src/args.js +123 -0
- package/dist/src/args.js.map +1 -0
- package/dist/src/auth.d.ts +11 -0
- package/dist/src/auth.js +51 -0
- package/dist/src/auth.js.map +1 -0
- package/dist/src/cli.d.ts +20 -0
- package/dist/src/cli.js +132 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/commands/analyze.d.ts +2 -0
- package/dist/src/commands/analyze.js +32 -0
- package/dist/src/commands/analyze.js.map +1 -0
- package/dist/src/commands/archive.d.ts +2 -0
- package/dist/src/commands/archive.js +63 -0
- package/dist/src/commands/archive.js.map +1 -0
- package/dist/src/commands/build.d.ts +2 -0
- package/dist/src/commands/build.js +60 -0
- package/dist/src/commands/build.js.map +1 -0
- package/dist/src/commands/clean.d.ts +2 -0
- package/dist/src/commands/clean.js +39 -0
- package/dist/src/commands/clean.js.map +1 -0
- package/dist/src/commands/coverage.d.ts +2 -0
- package/dist/src/commands/coverage.js +111 -0
- package/dist/src/commands/coverage.js.map +1 -0
- package/dist/src/commands/destinations.d.ts +2 -0
- package/dist/src/commands/destinations.js +56 -0
- package/dist/src/commands/destinations.js.map +1 -0
- package/dist/src/commands/export.d.ts +2 -0
- package/dist/src/commands/export.js +170 -0
- package/dist/src/commands/export.js.map +1 -0
- package/dist/src/commands/find.d.ts +2 -0
- package/dist/src/commands/find.js +70 -0
- package/dist/src/commands/find.js.map +1 -0
- package/dist/src/commands/home.d.ts +9 -0
- package/dist/src/commands/home.js +97 -0
- package/dist/src/commands/home.js.map +1 -0
- package/dist/src/commands/info.d.ts +2 -0
- package/dist/src/commands/info.js +95 -0
- package/dist/src/commands/info.js.map +1 -0
- package/dist/src/commands/localize.d.ts +2 -0
- package/dist/src/commands/localize.js +121 -0
- package/dist/src/commands/localize.js.map +1 -0
- package/dist/src/commands/migrate.d.ts +8 -0
- package/dist/src/commands/migrate.js +177 -0
- package/dist/src/commands/migrate.js.map +1 -0
- package/dist/src/commands/packages.d.ts +2 -0
- package/dist/src/commands/packages.js +108 -0
- package/dist/src/commands/packages.js.map +1 -0
- package/dist/src/commands/platforms.d.ts +2 -0
- package/dist/src/commands/platforms.js +379 -0
- package/dist/src/commands/platforms.js.map +1 -0
- package/dist/src/commands/result.d.ts +2 -0
- package/dist/src/commands/result.js +100 -0
- package/dist/src/commands/result.js.map +1 -0
- package/dist/src/commands/schemes.d.ts +2 -0
- package/dist/src/commands/schemes.js +39 -0
- package/dist/src/commands/schemes.js.map +1 -0
- package/dist/src/commands/settings.d.ts +2 -0
- package/dist/src/commands/settings.js +191 -0
- package/dist/src/commands/settings.js.map +1 -0
- package/dist/src/commands/setup.d.ts +2 -0
- package/dist/src/commands/setup.js +84 -0
- package/dist/src/commands/setup.js.map +1 -0
- package/dist/src/commands/sim.d.ts +2 -0
- package/dist/src/commands/sim.js +187 -0
- package/dist/src/commands/sim.js.map +1 -0
- package/dist/src/commands/test.d.ts +2 -0
- package/dist/src/commands/test.js +317 -0
- package/dist/src/commands/test.js.map +1 -0
- package/dist/src/commands/testplans.d.ts +8 -0
- package/dist/src/commands/testplans.js +75 -0
- package/dist/src/commands/testplans.js.map +1 -0
- package/dist/src/commands/tests.d.ts +13 -0
- package/dist/src/commands/tests.js +154 -0
- package/dist/src/commands/tests.js.map +1 -0
- package/dist/src/commands/xcframework.d.ts +2 -0
- package/dist/src/commands/xcframework.js +77 -0
- package/dist/src/commands/xcframework.js.map +1 -0
- package/dist/src/context.d.ts +23 -0
- package/dist/src/context.js +72 -0
- package/dist/src/context.js.map +1 -0
- package/dist/src/destination.d.ts +50 -0
- package/dist/src/destination.js +174 -0
- package/dist/src/destination.js.map +1 -0
- package/dist/src/errors.d.ts +6 -0
- package/dist/src/errors.js +55 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/packages.d.ts +31 -0
- package/dist/src/packages.js +117 -0
- package/dist/src/packages.js.map +1 -0
- package/dist/src/report.d.ts +20 -0
- package/dist/src/report.js +39 -0
- package/dist/src/report.js.map +1 -0
- package/dist/src/scheme.d.ts +22 -0
- package/dist/src/scheme.js +66 -0
- package/dist/src/scheme.js.map +1 -0
- package/dist/src/simctl.d.ts +33 -0
- package/dist/src/simctl.js +85 -0
- package/dist/src/simctl.js.map +1 -0
- package/dist/src/surface.d.ts +55 -0
- package/dist/src/surface.js +350 -0
- package/dist/src/surface.js.map +1 -0
- package/dist/src/toon.d.ts +34 -0
- package/dist/src/toon.js +79 -0
- package/dist/src/toon.js.map +1 -0
- package/dist/src/version.d.ts +1 -0
- package/dist/src/version.js +24 -0
- package/dist/src/version.js.map +1 -0
- package/dist/src/xccov.d.ts +25 -0
- package/dist/src/xccov.js +78 -0
- package/dist/src/xccov.js.map +1 -0
- package/dist/src/xcodebuild.d.ts +65 -0
- package/dist/src/xcodebuild.js +163 -0
- package/dist/src/xcodebuild.js.map +1 -0
- package/dist/src/xcresult.d.ts +96 -0
- package/dist/src/xcresult.js +156 -0
- package/dist/src/xcresult.js.map +1 -0
- package/package.json +75 -0
- package/skills/xcodebuild-axi/SKILL.md +64 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { ProjectContext } from "./context.js";
|
|
2
|
+
/** Override the wrapped binary. Unset keeps the PATH lookup. */
|
|
3
|
+
export declare function xcodebuildBin(): string;
|
|
4
|
+
export interface MetadataResult {
|
|
5
|
+
stdout: string;
|
|
6
|
+
stderr: string;
|
|
7
|
+
exitCode: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Run a short, read-only xcodebuild invocation and buffer its output.
|
|
11
|
+
*
|
|
12
|
+
* Only for the `-list`/`-showdestinations`/`-showBuildSettings` family, whose
|
|
13
|
+
* output is measured in kilobytes. Builds and tests go through `runBuild`,
|
|
14
|
+
* which never holds a transcript in memory.
|
|
15
|
+
*/
|
|
16
|
+
export declare function runMetadata(args: string[]): Promise<MetadataResult>;
|
|
17
|
+
export interface BuildRun {
|
|
18
|
+
exitCode: number;
|
|
19
|
+
logPath: string;
|
|
20
|
+
resultPath: string;
|
|
21
|
+
/** Wall-clock seconds the invocation took. */
|
|
22
|
+
seconds: number;
|
|
23
|
+
/** The tail of the transcript, for failures no result bundle explains. */
|
|
24
|
+
tail: string;
|
|
25
|
+
}
|
|
26
|
+
export interface BuildRunOptions {
|
|
27
|
+
args: string[];
|
|
28
|
+
/** Filename stem for the log and result bundle, e.g. "MyApp-iPhone-17-Pro". */
|
|
29
|
+
label: string;
|
|
30
|
+
/** Omit for invocations that act on the toolchain rather than a project. */
|
|
31
|
+
project?: ProjectContext;
|
|
32
|
+
/** Overrides the cache location for both artifacts. */
|
|
33
|
+
outDir?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Run a build or test, streaming the transcript straight to disk.
|
|
37
|
+
*
|
|
38
|
+
* The transcript is the thing this tool exists to keep out of the agent's
|
|
39
|
+
* context, so it is never buffered: a single passing test run of one app in a
|
|
40
|
+
* real repo measured 549 KB, and a full verify run 2.5 MB. Only the last few
|
|
41
|
+
* KB are kept in memory, for the failures that produce no result bundle.
|
|
42
|
+
*/
|
|
43
|
+
export declare function runBuild(options: BuildRunOptions): Promise<BuildRun>;
|
|
44
|
+
/**
|
|
45
|
+
* Where toolchain-level runs land — platform downloads and the like, which
|
|
46
|
+
* belong to the machine rather than to any one project.
|
|
47
|
+
*/
|
|
48
|
+
export declare function globalArtifactDir(): string;
|
|
49
|
+
/**
|
|
50
|
+
* Where logs and result bundles land.
|
|
51
|
+
*
|
|
52
|
+
* Under the user's cache directory rather than the repo, so running this tool
|
|
53
|
+
* never dirties a working tree or a `.gitignore`. The project path is hashed
|
|
54
|
+
* into the name so two checkouts of the same repo do not collide.
|
|
55
|
+
*/
|
|
56
|
+
export declare function artifactDir(project: ProjectContext): string;
|
|
57
|
+
/**
|
|
58
|
+
* Strip xcodebuild's fixed preamble.
|
|
59
|
+
*
|
|
60
|
+
* Every invocation — including the read-only ones — reprints the command line,
|
|
61
|
+
* "Resolve Package Graph", and the full resolved package list. In a workspace
|
|
62
|
+
* with 16 local packages that is ~1.2 KB of identical text in front of a 349-byte
|
|
63
|
+
* answer, paid on every call.
|
|
64
|
+
*/
|
|
65
|
+
export declare function stripPreamble(output: string): string;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { execFile, spawn } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { createWriteStream, mkdirSync, rmSync } from "node:fs";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { AxiError, xcodeNotInstalledError } from "./errors.js";
|
|
7
|
+
const MAX_METADATA_BYTES = 8 * 1024 * 1024;
|
|
8
|
+
/** Override the wrapped binary. Unset keeps the PATH lookup. */
|
|
9
|
+
export function xcodebuildBin() {
|
|
10
|
+
const fromEnv = process.env["XCODEBUILD_BIN"]?.trim();
|
|
11
|
+
return fromEnv && fromEnv.length > 0 ? fromEnv : "xcodebuild";
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Run a short, read-only xcodebuild invocation and buffer its output.
|
|
15
|
+
*
|
|
16
|
+
* Only for the `-list`/`-showdestinations`/`-showBuildSettings` family, whose
|
|
17
|
+
* output is measured in kilobytes. Builds and tests go through `runBuild`,
|
|
18
|
+
* which never holds a transcript in memory.
|
|
19
|
+
*/
|
|
20
|
+
export function runMetadata(args) {
|
|
21
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
22
|
+
execFile(xcodebuildBin(), args, { maxBuffer: MAX_METADATA_BYTES, encoding: "utf-8" }, (error, stdout, stderr) => {
|
|
23
|
+
if (error && error.code === "ENOENT") {
|
|
24
|
+
rejectPromise(xcodeNotInstalledError());
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (error &&
|
|
28
|
+
error.code ===
|
|
29
|
+
"ERR_CHILD_PROCESS_STDIO_MAXBUFFER") {
|
|
30
|
+
rejectPromise(new AxiError("xcodebuild produced more output than this command buffers", "UNKNOWN", [
|
|
31
|
+
"Use `xcodebuild-axi build` or `test`, which stream to a log instead",
|
|
32
|
+
]));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
resolvePromise({
|
|
36
|
+
stdout,
|
|
37
|
+
stderr,
|
|
38
|
+
exitCode: typeof error?.code === "number" ? error.code : error ? 1 : 0,
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Run a build or test, streaming the transcript straight to disk.
|
|
45
|
+
*
|
|
46
|
+
* The transcript is the thing this tool exists to keep out of the agent's
|
|
47
|
+
* context, so it is never buffered: a single passing test run of one app in a
|
|
48
|
+
* real repo measured 549 KB, and a full verify run 2.5 MB. Only the last few
|
|
49
|
+
* KB are kept in memory, for the failures that produce no result bundle.
|
|
50
|
+
*/
|
|
51
|
+
export function runBuild(options) {
|
|
52
|
+
const dir = options.outDir ??
|
|
53
|
+
(options.project ? artifactDir(options.project) : globalArtifactDir());
|
|
54
|
+
mkdirSync(dir, { recursive: true });
|
|
55
|
+
const logPath = join(dir, `${options.label}.log`);
|
|
56
|
+
const resultPath = join(dir, `${options.label}.xcresult`);
|
|
57
|
+
// xcodebuild refuses to write over an existing result bundle and dies before
|
|
58
|
+
// running anything — "error: Existing file at -resultBundlePath". The bundle
|
|
59
|
+
// is our artifact, not the user's, so clear it rather than make every caller
|
|
60
|
+
// remember to.
|
|
61
|
+
rmSync(resultPath, { recursive: true, force: true });
|
|
62
|
+
const args = [...options.args, "-resultBundlePath", resultPath];
|
|
63
|
+
const started = Date.now();
|
|
64
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
65
|
+
const log = createWriteStream(logPath);
|
|
66
|
+
const child = spawn(xcodebuildBin(), args, {
|
|
67
|
+
cwd: process.cwd(),
|
|
68
|
+
// NSUnbufferedIO makes xcodebuild flush per line, so a wedged run leaves
|
|
69
|
+
// a usable log instead of an empty one.
|
|
70
|
+
env: { ...process.env, NSUnbufferedIO: "YES" },
|
|
71
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
72
|
+
});
|
|
73
|
+
const tail = [];
|
|
74
|
+
const keepTail = (chunk) => {
|
|
75
|
+
tail.push(chunk.toString("utf-8"));
|
|
76
|
+
while (tail.length > 40)
|
|
77
|
+
tail.shift();
|
|
78
|
+
};
|
|
79
|
+
child.stdout.pipe(log, { end: false });
|
|
80
|
+
child.stderr.pipe(log, { end: false });
|
|
81
|
+
child.stdout.on("data", keepTail);
|
|
82
|
+
child.stderr.on("data", keepTail);
|
|
83
|
+
child.on("error", (error) => {
|
|
84
|
+
log.end();
|
|
85
|
+
rejectPromise(error.code === "ENOENT"
|
|
86
|
+
? xcodeNotInstalledError()
|
|
87
|
+
: error);
|
|
88
|
+
});
|
|
89
|
+
child.on("close", (code) => {
|
|
90
|
+
log.end(() => resolvePromise({
|
|
91
|
+
exitCode: code ?? 1,
|
|
92
|
+
logPath,
|
|
93
|
+
resultPath,
|
|
94
|
+
seconds: (Date.now() - started) / 1000,
|
|
95
|
+
tail: tail.join("").slice(-8000),
|
|
96
|
+
}));
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Where toolchain-level runs land — platform downloads and the like, which
|
|
102
|
+
* belong to the machine rather than to any one project.
|
|
103
|
+
*/
|
|
104
|
+
export function globalArtifactDir() {
|
|
105
|
+
return join(homedir(), "Library", "Caches", "xcodebuild-axi", "toolchain");
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Where logs and result bundles land.
|
|
109
|
+
*
|
|
110
|
+
* Under the user's cache directory rather than the repo, so running this tool
|
|
111
|
+
* never dirties a working tree or a `.gitignore`. The project path is hashed
|
|
112
|
+
* into the name so two checkouts of the same repo do not collide.
|
|
113
|
+
*/
|
|
114
|
+
export function artifactDir(project) {
|
|
115
|
+
const hash = createHash("sha256")
|
|
116
|
+
.update(project.path)
|
|
117
|
+
.digest("hex")
|
|
118
|
+
.slice(0, 8);
|
|
119
|
+
return join(homedir(), "Library", "Caches", "xcodebuild-axi", `${project.name}-${hash}`);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Strip xcodebuild's fixed preamble.
|
|
123
|
+
*
|
|
124
|
+
* Every invocation — including the read-only ones — reprints the command line,
|
|
125
|
+
* "Resolve Package Graph", and the full resolved package list. In a workspace
|
|
126
|
+
* with 16 local packages that is ~1.2 KB of identical text in front of a 349-byte
|
|
127
|
+
* answer, paid on every call.
|
|
128
|
+
*/
|
|
129
|
+
export function stripPreamble(output) {
|
|
130
|
+
const lines = output.split("\n");
|
|
131
|
+
const kept = [];
|
|
132
|
+
let skippingPackageList = false;
|
|
133
|
+
for (const line of lines) {
|
|
134
|
+
if (skippingPackageList) {
|
|
135
|
+
// The list is indented; the first unindented line ends it.
|
|
136
|
+
if (line.trim().length === 0 || /^\s/.test(line))
|
|
137
|
+
continue;
|
|
138
|
+
skippingPackageList = false;
|
|
139
|
+
}
|
|
140
|
+
if (/^Resolved source packages:/.test(line)) {
|
|
141
|
+
skippingPackageList = true;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (NOISE.some((pattern) => pattern.test(line)))
|
|
145
|
+
continue;
|
|
146
|
+
kept.push(line);
|
|
147
|
+
}
|
|
148
|
+
return kept
|
|
149
|
+
.join("\n")
|
|
150
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
151
|
+
.trim();
|
|
152
|
+
}
|
|
153
|
+
const NOISE = [
|
|
154
|
+
/^Command line invocation:/,
|
|
155
|
+
/^\s+\/.*\/xcodebuild /,
|
|
156
|
+
/^Resolve Package Graph$/,
|
|
157
|
+
/^Prepare packages$/,
|
|
158
|
+
/^User defaults from command line:/,
|
|
159
|
+
/^Build settings from command line:/,
|
|
160
|
+
/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ xcodebuild\[\d+:\d+\]/,
|
|
161
|
+
/^note: /,
|
|
162
|
+
];
|
|
163
|
+
//# sourceMappingURL=xcodebuild.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xcodebuild.js","sourceRoot":"","sources":["../../src/xcodebuild.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export interface TestDevice {
|
|
2
|
+
deviceName?: string;
|
|
3
|
+
modelName?: string;
|
|
4
|
+
platform?: string;
|
|
5
|
+
osVersion?: string;
|
|
6
|
+
architecture?: string;
|
|
7
|
+
deviceId?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface TestFailure {
|
|
10
|
+
testName?: string;
|
|
11
|
+
targetName?: string;
|
|
12
|
+
failureText?: string;
|
|
13
|
+
testIdentifierString?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface TestSummary {
|
|
16
|
+
title?: string;
|
|
17
|
+
result?: string;
|
|
18
|
+
totalTestCount?: number;
|
|
19
|
+
passedTests?: number;
|
|
20
|
+
failedTests?: number;
|
|
21
|
+
skippedTests?: number;
|
|
22
|
+
expectedFailures?: number;
|
|
23
|
+
startTime?: number;
|
|
24
|
+
finishTime?: number;
|
|
25
|
+
environmentDescription?: string;
|
|
26
|
+
testFailures?: TestFailure[];
|
|
27
|
+
runtimeWarnings?: RawIssue[];
|
|
28
|
+
devicesAndConfigurations?: {
|
|
29
|
+
device?: TestDevice;
|
|
30
|
+
passedTests?: number;
|
|
31
|
+
failedTests?: number;
|
|
32
|
+
skippedTests?: number;
|
|
33
|
+
}[];
|
|
34
|
+
}
|
|
35
|
+
export interface RawIssue {
|
|
36
|
+
issueType?: string;
|
|
37
|
+
message?: string;
|
|
38
|
+
targetName?: string;
|
|
39
|
+
sourceURL?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface BuildResults {
|
|
42
|
+
actionTitle?: string;
|
|
43
|
+
status?: string;
|
|
44
|
+
errorCount?: number;
|
|
45
|
+
warningCount?: number;
|
|
46
|
+
analyzerWarningCount?: number;
|
|
47
|
+
errors?: RawIssue[];
|
|
48
|
+
warnings?: RawIssue[];
|
|
49
|
+
analyzerWarnings?: RawIssue[];
|
|
50
|
+
destination?: TestDevice;
|
|
51
|
+
startTime?: number;
|
|
52
|
+
endTime?: number;
|
|
53
|
+
}
|
|
54
|
+
/** A diagnostic, flattened to the shape a TOON row wants. */
|
|
55
|
+
export interface Diagnostic {
|
|
56
|
+
file: string;
|
|
57
|
+
line: number | "";
|
|
58
|
+
col: number | "";
|
|
59
|
+
type: string;
|
|
60
|
+
message: string;
|
|
61
|
+
}
|
|
62
|
+
export declare function readTestSummary(path: string): Promise<TestSummary>;
|
|
63
|
+
export declare function readBuildResults(path: string): Promise<BuildResults>;
|
|
64
|
+
/**
|
|
65
|
+
* Pull `file`, `line` and `col` out of a diagnostic's sourceURL.
|
|
66
|
+
*
|
|
67
|
+
* The URL looks like
|
|
68
|
+
* file:///path/To.swift#EndingColumnNumber=83&StartingLineNumber=165&...
|
|
69
|
+
* and the line and column in that fragment are **zero-based** — a diagnostic
|
|
70
|
+
* reported at `StartingLineNumber=165` is on line 166 as any editor counts it.
|
|
71
|
+
* Getting this wrong sends the agent to the wrong line, which is worse than
|
|
72
|
+
* sending it nowhere, so the offset is applied here and nowhere else.
|
|
73
|
+
*/
|
|
74
|
+
export declare function parseSourceURL(sourceURL: string | undefined): {
|
|
75
|
+
file: string;
|
|
76
|
+
line: number | "";
|
|
77
|
+
col: number | "";
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Repo-relative when the file is under cwd, absolute otherwise.
|
|
81
|
+
*
|
|
82
|
+
* Both sides need the `/private` prefix stripped, not just the diagnostic's:
|
|
83
|
+
* on macOS `process.cwd()` in `/tmp` reports `/private/tmp`, so comparing the
|
|
84
|
+
* two raw would make every path look outside the tree and stay absolute.
|
|
85
|
+
*/
|
|
86
|
+
export declare function relativize(file: string, cwd?: string): string;
|
|
87
|
+
/**
|
|
88
|
+
* Flatten issues to TOON rows, dropping exact duplicates.
|
|
89
|
+
*
|
|
90
|
+
* The same Swift diagnostic is reported once per target that compiles the
|
|
91
|
+
* file, so a warning in a shared package can appear a dozen times in one
|
|
92
|
+
* build. Only the first is information; the rest are paid-for repetition.
|
|
93
|
+
*/
|
|
94
|
+
export declare function toDiagnostics(issues: RawIssue[] | undefined): Diagnostic[];
|
|
95
|
+
/** The device a run actually landed on, as one readable line. */
|
|
96
|
+
export declare function describeDevice(device: TestDevice | undefined): string;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { relative } from "node:path";
|
|
4
|
+
import { AxiError } from "./errors.js";
|
|
5
|
+
/**
|
|
6
|
+
* Structured reads of an .xcresult bundle.
|
|
7
|
+
*
|
|
8
|
+
* This is the whole reason the tool can be small: xcodebuild's transcript is
|
|
9
|
+
* megabytes of noise, but the bundle it writes alongside carries the same
|
|
10
|
+
* answers as a few KB of JSON — pass/fail counts, per-device breakdown, and
|
|
11
|
+
* every diagnostic with a precise source location.
|
|
12
|
+
*/
|
|
13
|
+
const MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
14
|
+
function xcresulttool(args, path) {
|
|
15
|
+
if (!existsSync(path)) {
|
|
16
|
+
throw new AxiError(`No result bundle at ${path}`, "RESULT_NOT_FOUND", [
|
|
17
|
+
"Run `xcodebuild-axi build` or `test` first — each prints the bundle path it wrote",
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
20
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
21
|
+
execFile("xcrun", ["xcresulttool", ...args, "--path", path, "--compact"], { maxBuffer: MAX_BUFFER_BYTES, encoding: "utf-8" }, (error, stdout) => {
|
|
22
|
+
if (error && stdout.trim().length === 0) {
|
|
23
|
+
rejectPromise(new AxiError(`Could not read result bundle at ${path}`, "RESULT_NOT_FOUND", [
|
|
24
|
+
"The bundle may be from an incompatible Xcode version, or still being written",
|
|
25
|
+
]));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
resolvePromise(JSON.parse(stdout));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
rejectPromise(new AxiError(`Result bundle at ${path} produced unreadable output`, "RESULT_NOT_FOUND"));
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export function readTestSummary(path) {
|
|
38
|
+
return xcresulttool(["get", "test-results", "summary"], path);
|
|
39
|
+
}
|
|
40
|
+
export function readBuildResults(path) {
|
|
41
|
+
return xcresulttool(["get", "build-results"], path);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Pull `file`, `line` and `col` out of a diagnostic's sourceURL.
|
|
45
|
+
*
|
|
46
|
+
* The URL looks like
|
|
47
|
+
* file:///path/To.swift#EndingColumnNumber=83&StartingLineNumber=165&...
|
|
48
|
+
* and the line and column in that fragment are **zero-based** — a diagnostic
|
|
49
|
+
* reported at `StartingLineNumber=165` is on line 166 as any editor counts it.
|
|
50
|
+
* Getting this wrong sends the agent to the wrong line, which is worse than
|
|
51
|
+
* sending it nowhere, so the offset is applied here and nowhere else.
|
|
52
|
+
*/
|
|
53
|
+
export function parseSourceURL(sourceURL) {
|
|
54
|
+
if (!sourceURL)
|
|
55
|
+
return { file: "", line: "", col: "" };
|
|
56
|
+
const hashIndex = sourceURL.indexOf("#");
|
|
57
|
+
const rawPath = hashIndex === -1 ? sourceURL : sourceURL.slice(0, hashIndex);
|
|
58
|
+
const fragment = hashIndex === -1 ? "" : sourceURL.slice(hashIndex + 1);
|
|
59
|
+
let file = rawPath.startsWith("file://")
|
|
60
|
+
? rawPath.slice("file://".length)
|
|
61
|
+
: rawPath;
|
|
62
|
+
try {
|
|
63
|
+
file = decodeURIComponent(file);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// A malformed escape is not worth failing a whole build report over.
|
|
67
|
+
}
|
|
68
|
+
const params = new URLSearchParams(fragment);
|
|
69
|
+
const line = toZeroBasedNumber(params.get("StartingLineNumber"));
|
|
70
|
+
const col = toZeroBasedNumber(params.get("StartingColumnNumber"));
|
|
71
|
+
return { file: relativize(file), line, col };
|
|
72
|
+
}
|
|
73
|
+
function toZeroBasedNumber(raw) {
|
|
74
|
+
if (raw === null)
|
|
75
|
+
return "";
|
|
76
|
+
const parsed = Number(raw);
|
|
77
|
+
return Number.isFinite(parsed) ? parsed + 1 : "";
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Repo-relative when the file is under cwd, absolute otherwise.
|
|
81
|
+
*
|
|
82
|
+
* Both sides need the `/private` prefix stripped, not just the diagnostic's:
|
|
83
|
+
* on macOS `process.cwd()` in `/tmp` reports `/private/tmp`, so comparing the
|
|
84
|
+
* two raw would make every path look outside the tree and stay absolute.
|
|
85
|
+
*/
|
|
86
|
+
export function relativize(file, cwd = process.cwd()) {
|
|
87
|
+
if (!file.startsWith("/"))
|
|
88
|
+
return file;
|
|
89
|
+
const normalized = stripPrivate(file);
|
|
90
|
+
const rel = relative(stripPrivate(cwd), normalized);
|
|
91
|
+
return rel.startsWith("..") ? normalized : rel;
|
|
92
|
+
}
|
|
93
|
+
function stripPrivate(path) {
|
|
94
|
+
return path.startsWith("/private/") ? path.slice("/private".length) : path;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Flatten issues to TOON rows, dropping exact duplicates.
|
|
98
|
+
*
|
|
99
|
+
* The same Swift diagnostic is reported once per target that compiles the
|
|
100
|
+
* file, so a warning in a shared package can appear a dozen times in one
|
|
101
|
+
* build. Only the first is information; the rest are paid-for repetition.
|
|
102
|
+
*/
|
|
103
|
+
export function toDiagnostics(issues) {
|
|
104
|
+
const seen = new Set();
|
|
105
|
+
const out = [];
|
|
106
|
+
for (const issue of issues ?? []) {
|
|
107
|
+
const { file, line, col } = parseSourceURL(issue.sourceURL);
|
|
108
|
+
const message = (issue.message ?? "").replace(/\s+/g, " ").trim();
|
|
109
|
+
const key = `${file}:${line}:${col}:${message}`;
|
|
110
|
+
if (seen.has(key))
|
|
111
|
+
continue;
|
|
112
|
+
seen.add(key);
|
|
113
|
+
out.push({
|
|
114
|
+
file,
|
|
115
|
+
line,
|
|
116
|
+
col,
|
|
117
|
+
type: shortIssueType(issue.issueType),
|
|
118
|
+
message,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
/** "Swift Compiler Error" is four tokens to say "swift". */
|
|
124
|
+
function shortIssueType(issueType) {
|
|
125
|
+
if (!issueType)
|
|
126
|
+
return "error";
|
|
127
|
+
const lower = issueType.toLowerCase();
|
|
128
|
+
if (lower.includes("swift"))
|
|
129
|
+
return "swift";
|
|
130
|
+
if (lower.includes("clang") || lower.includes("c compiler"))
|
|
131
|
+
return "clang";
|
|
132
|
+
if (lower.includes("linker"))
|
|
133
|
+
return "linker";
|
|
134
|
+
if (lower.includes("script"))
|
|
135
|
+
return "script";
|
|
136
|
+
if (lower.includes("package"))
|
|
137
|
+
return "package";
|
|
138
|
+
if (lower.includes("uncategorized"))
|
|
139
|
+
return "xcodebuild";
|
|
140
|
+
return lower.replace(/ (error|warning)$/, "").replace(/\s+/g, "-");
|
|
141
|
+
}
|
|
142
|
+
/** The device a run actually landed on, as one readable line. */
|
|
143
|
+
export function describeDevice(device) {
|
|
144
|
+
if (!device)
|
|
145
|
+
return "unknown";
|
|
146
|
+
const name = device.deviceName || device.modelName;
|
|
147
|
+
const parts = [
|
|
148
|
+
name,
|
|
149
|
+
device.osVersion
|
|
150
|
+
? `${device.platform ?? "iOS"} ${device.osVersion}`
|
|
151
|
+
: device.platform,
|
|
152
|
+
];
|
|
153
|
+
const described = parts.filter((part) => part && part.length > 0).join(" · ");
|
|
154
|
+
return described.length > 0 ? described : "unknown";
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=xcresult.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xcresult.js","sourceRoot":"","sources":["../../src/xcresult.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "xcodebuild-axi",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AXI-compliant xcodebuild wrapper — turns multi-megabyte Xcode build and test transcripts into a few lines of TOON",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/alexrrouse/xcodebuild-axi.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/alexrrouse/xcodebuild-axi#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/alexrrouse/xcodebuild-axi/issues"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"xcodebuild",
|
|
16
|
+
"xcode",
|
|
17
|
+
"ios",
|
|
18
|
+
"swift",
|
|
19
|
+
"cli",
|
|
20
|
+
"agent",
|
|
21
|
+
"axi",
|
|
22
|
+
"toon"
|
|
23
|
+
],
|
|
24
|
+
"bin": {
|
|
25
|
+
"xcodebuild-axi": "dist/bin/xcodebuild-axi.js"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"skills/xcodebuild-axi",
|
|
30
|
+
"LICENSE",
|
|
31
|
+
"README.md",
|
|
32
|
+
"CHANGELOG.md"
|
|
33
|
+
],
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"dev": "tsx bin/xcodebuild-axi.ts",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:watch": "vitest",
|
|
42
|
+
"lint": "eslint .",
|
|
43
|
+
"format": "prettier --write .",
|
|
44
|
+
"format:check": "prettier --check .",
|
|
45
|
+
"build:skill": "tsx scripts/build-skill.ts",
|
|
46
|
+
"prepublishOnly": "npm run build",
|
|
47
|
+
"coverage": "tsx scripts/coverage.ts",
|
|
48
|
+
"coverage:check": "tsx scripts/coverage.ts --check",
|
|
49
|
+
"benchmark": "tsx scripts/benchmark.ts"
|
|
50
|
+
},
|
|
51
|
+
"author": "Alex Rouse",
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"os": [
|
|
54
|
+
"darwin"
|
|
55
|
+
],
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=20"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"@toon-format/toon": "^4.1.1",
|
|
61
|
+
"axi-sdk-js": "^0.1.10"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@eslint/js": "^10.0.1",
|
|
65
|
+
"@types/node": "^22.0.0",
|
|
66
|
+
"eslint": "^10.1.0",
|
|
67
|
+
"globals": "^17.4.0",
|
|
68
|
+
"gpt-tokenizer": "^4.0.0",
|
|
69
|
+
"prettier": "^3.8.1",
|
|
70
|
+
"tsx": "^4.0.0",
|
|
71
|
+
"typescript": "^5.7.0",
|
|
72
|
+
"typescript-eslint": "^8.58.0",
|
|
73
|
+
"vitest": "^5.0.1"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: xcodebuild-axi
|
|
3
|
+
description: >
|
|
4
|
+
Build, test, and inspect Xcode projects without reading xcodebuild's output.
|
|
5
|
+
Use for any iOS/macOS build, test run, scheme or destination lookup, build
|
|
6
|
+
setting read, or .xcresult inspection — raw xcodebuild prints hundreds of
|
|
7
|
+
kilobytes to say what this prints in a few lines.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# xcodebuild-axi
|
|
11
|
+
|
|
12
|
+
Agent-ergonomic wrapper around xcodebuild. Prefer it over raw `xcodebuild` for any build / test / inspect of an Xcode project.
|
|
13
|
+
|
|
14
|
+
Run it with no arguments first — it reports the project in front of you, its
|
|
15
|
+
schemes, and how the last run went.
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npx -y xcodebuild-axi
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Commands
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
usage: xcodebuild-axi [command] [flags]
|
|
25
|
+
commands[23]:
|
|
26
|
+
(none)=dashboard
|
|
27
|
+
build, test, tests, clean, analyze, archive, export
|
|
28
|
+
schemes, destinations, testplans, settings, packages, info
|
|
29
|
+
result, coverage, sim, platforms, setup
|
|
30
|
+
localize, xcframework, find, migrate
|
|
31
|
+
flags[2]:
|
|
32
|
+
--help, -v/-V/--version
|
|
33
|
+
env[2]:
|
|
34
|
+
XCODEBUILD_BIN override the wrapped xcodebuild binary
|
|
35
|
+
DEVELOPER_DIR select an Xcode, as xcodebuild itself reads it
|
|
36
|
+
exit:
|
|
37
|
+
0 success, 1 the build or tests failed, 2 usage error
|
|
38
|
+
examples:
|
|
39
|
+
xcodebuild-axi
|
|
40
|
+
xcodebuild-axi build --scheme MyApp
|
|
41
|
+
xcodebuild-axi test --scheme MyApp --device "iPhone 17 Pro" --coverage
|
|
42
|
+
xcodebuild-axi settings --key PRODUCT_BUNDLE_IDENTIFIER
|
|
43
|
+
xcodebuild-axi setup hooks
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Every command takes `--help` with its own flags and examples. The CLI's own
|
|
47
|
+
output is the authority on everything below the top level; prefer asking it
|
|
48
|
+
over guessing.
|
|
49
|
+
|
|
50
|
+
## What to reach for
|
|
51
|
+
|
|
52
|
+
- Building or testing: `build` and `test` report only what failed, with
|
|
53
|
+
`file,line,col`. The full transcript is written to a log whose path they
|
|
54
|
+
print, so detail is one read away and never the default.
|
|
55
|
+
- Picking a device: pass `--device "iPhone 17 Pro"`, or nothing at all to get
|
|
56
|
+
the newest simulator. Do not hand-write a `-destination` specifier.
|
|
57
|
+
- Reading a previous run: `result <path.xcresult>`, which re-reads without
|
|
58
|
+
rebuilding.
|
|
59
|
+
- Build settings: `settings --key NAME`, not a full dump.
|
|
60
|
+
|
|
61
|
+
## Exit codes
|
|
62
|
+
|
|
63
|
+
`0` success, `1` the build or tests failed, `2` usage error. A failed build
|
|
64
|
+
still prints its full report to stdout.
|