xcodebuild-axi 0.1.9 → 0.1.11

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 CHANGED
@@ -4,6 +4,155 @@ Notable changes to `xcodebuild-axi`. Versions follow
4
4
  [semver](https://semver.org/); the format follows
5
5
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
+ ## [0.1.11] - 2026-09-21
8
+
9
+ ### Added
10
+
11
+ - **`result --export <what>` gets files out of a bundle**: `attachments`,
12
+ `diagnostics`, `metrics`, or `evaluations`. A UI test's screenshots, the
13
+ diagnostics report `test --diagnostics` collects, and the CSVs behind a
14
+ performance measurement were all in the bundle and reachable only by shelling
15
+ out to `xcresulttool`.
16
+
17
+ It writes under `~/Library/Caches/xcodebuild-axi/exports/` by default, or
18
+ wherever `--to` says — never into the working tree, so asking what is in a CI
19
+ artifact cannot dirty the checkout it sits in. `--test`, `--filter '*.png'`
20
+ and `--failures` narrow what comes out, and each is **refused** where it does
21
+ not apply rather than ignored: a diagnostics report covers the whole run, so
22
+ `--test` beside it would answer a different question than the one asked.
23
+
24
+ The report is what landed and where, with each exported file tied back to the
25
+ test that produced it — a screenshot's filename is a UUID, and the manifest
26
+ is the only thing that makes it findable. `xcresulttool` narrates an
27
+ attachment export with one "Skipped export for <test>: no matching
28
+ attachments" per test, which is dropped.
29
+
30
+ - **`result --against <baseline.xcresult>`** answers the question CI actually
31
+ asks — not "did this run fail" but "did it fail in a way the last one did
32
+ not". It reports the counts on both sides and their direction
33
+ (`failures: 0 → 1 (+1 -0)`), then the tests that newly fail, the ones that
34
+ now pass, the ones added and removed, and any new warning. A failure the
35
+ baseline did not have is listed first, because it is the only part of a
36
+ comparison that stops a merge.
37
+
38
+ Two bundles with nothing in common — a build against a test run — get an
39
+ explicit answer saying so. `xcresulttool` prints a bare `null` there, which
40
+ is otherwise indistinguishable from a clean comparison.
41
+
42
+ - **`result <a> <b> [...] --merge`** combines the bundles of a sharded test run
43
+ into one, which is the only way that run gets a single verdict. The merged
44
+ bundle is read back and reported from, rather than described from its
45
+ inputs. It lands under `~/Library/Caches` unless `--to` says otherwise, and a
46
+ `--to` that already exists is refused rather than written over — the bundle
47
+ under our own cache directory is ours to clear, one the caller named is not.
48
+
49
+ With these two, **`xcresulttool` coverage is 81% of its leaves** and every
50
+ remaining one is deprecated by Xcode or superseded here.
51
+
52
+ - **`coverage` sees everything `xccov` sees.** Five leaves, and the command's
53
+ path argument now also takes an `.xccovreport` or an `.xccovarchive`:
54
+
55
+ - `--functions <file>` is per-function coverage, which is where an uncovered
56
+ branch finally gets a name. A file at 60% says a test is missing; it does
57
+ not say which one.
58
+ - `--lines <file>` is how many times each line ran, read out of the archive
59
+ rather than the report. Consecutive lines that never ran collapse into one
60
+ range — a 400-line file was 400 rows to find the eight that matter.
61
+ - `--against <path>` answers "did coverage drop" from two bundles this tool
62
+ wrote, reporting which way it moved overall and then per file, worst
63
+ first.
64
+ - `--merge` combines the coverage of a sharded run. `xccov merge` takes
65
+ report/archive **pairs** rather than result bundles, so each bundle is
66
+ unpacked with `xcresulttool export coverage` first — that two-step is the
67
+ reason merging coverage across shards is something people give up on.
68
+
69
+ With them, **`xccov` coverage is 100%** and companion coverage overall is
70
+ 43.7%.
71
+
72
+ ### Fixed
73
+
74
+ - `xccov`'s refusals are no longer wrapped in NSError. A bad path came back as
75
+ `Error: Error Domain=XCCovErrorDomain Code=0 "Failed to load result bundle"
76
+ UserInfo={NSLocalizedDescription=Failed to load result bundle,
77
+ NSUnderlyingError=0x… {…}}` — 400 characters around a five-word answer that
78
+ was already in there.
79
+
80
+ ## [0.1.10] - 2026-09-21
81
+
82
+ ### Added
83
+
84
+ - **`result` reads the rest of what is in an `.xcresult`.** Seven modes, each
85
+ its own question, none of which had an answer short of shelling out to
86
+ `xcresulttool` and parsing JSON:
87
+
88
+ - `--tests` lists every test the run recorded, failures first, with the
89
+ identifier `--only` and `--activities --test` take.
90
+ - `--activities --test <id>` prints the step-by-step trail of one test,
91
+ indented by depth — what a UI test actually did before it failed.
92
+ - `--insights` reports Xcode's own diagnosis of the run.
93
+ - `--metrics` reports the performance measurements an `XCTMetric` test took,
94
+ which `test --perf-diagnostics` collects and nothing could read back.
95
+ - `--log [build|action|console]` reports the stored log as the timed tree it
96
+ is: the slowest sections of a build, ranked, without rebuilding it.
97
+ - `--available` says what the bundle holds — test results, coverage,
98
+ diagnostics, which logs — so a read that would fail can be skipped rather
99
+ than attempted.
100
+ - `--metadata` reports the bundle's own format version, storage backend and
101
+ compression.
102
+
103
+ That takes `xcresulttool` coverage from 19% to 52.4% of its leaves, and the
104
+ companion tools as a whole from 18.3% to 28.2%.
105
+
106
+ - **The export options plist is fully covered.** `export` gained a flag for
107
+ each of the twelve keys that had none, so nothing about a distribution
108
+ requires authoring XML any more:
109
+
110
+ `--profile <id>=<name>`, `--certificate`, `--installer-certificate`,
111
+ `--distribution-bundle-id`, `--keep-swift-symbols`, `--internal-only`,
112
+ `--app-store-info`, `--icloud-env`, `--thinning`, `--manifest <key>=<url>`,
113
+ `--odr-base-url`, `--no-embed-odr`.
114
+
115
+ The ones that change whether a release works at all: `--profile` and
116
+ `--certificate` are manual signing, which until now could be _asked for_
117
+ with `--signing-style manual` and not completed — so the flag existed and
118
+ the export it produced could not be signed as intended. `--internal-only`
119
+ marks a TestFlight build as not for external distribution, which is what a
120
+ PR build wants. `--manifest` is over-the-web distribution, and it is refused
121
+ unless all three of its URLs are present, because a partial manifest exports
122
+ without an error and produces a link that cannot install.
123
+
124
+ Two spellings are handled rather than passed on: Xcode writes its named
125
+ thinning options with angle brackets (`<none>`), and the Xcode 26 method
126
+ names (`app-store`, `ad-hoc`, `development`) still work and are reported as
127
+ the current name they mean.
128
+
129
+ - `info --sdk <name>` reports one SDK in full — path, platform path, platform,
130
+ and both versions. That was `xcodebuild -version -sdk <name> <infoitem>`,
131
+ which answers one field per invocation and only if you already know the
132
+ field names. A build script that needs the SDK path, or an agent asked which
133
+ build of the SDK it compiled against, had to shell out for it.
134
+
135
+ With it, **every leaf of xcodebuild's own surface that this tool wraps is
136
+ covered**: 160 of 161, and the one left is `installsrc`, which is declined
137
+ on purpose and says so.
138
+
139
+ ### Changed
140
+
141
+ - The flags that shape the generated options plist are now **refused**
142
+ alongside `--options` rather than silently ignored. `--upload` already was;
143
+ `--team`, `--signing-style` and the ten new ones were not, so a release could
144
+ pass `--team` beside a hand-written plist and get a build signed by whatever
145
+ the plist said.
146
+
147
+ ### Fixed
148
+
149
+ - `xcresulttool`'s own explanation of a failed read is now the error message.
150
+ It writes `Error: …` on stderr and exits non-zero; the reason was thrown away
151
+ and replaced with a guess ("the bundle may be from an incompatible Xcode
152
+ version"), so `--log console` on a bundle with no console log was reported as
153
+ a version problem rather than as the absent log it is. The guess is only
154
+ offered now when `xcresulttool` gave no reason of its own.
155
+
7
156
  ## [0.1.9] - 2026-09-21
8
157
 
9
158
  ### Added
@@ -294,7 +443,9 @@ First release.
294
443
  - 100% of the 117 options `xcodebuild -help` lists are covered, declared in
295
444
  `src/surface.ts` and checked against the installed Xcode in CI.
296
445
 
297
- [unreleased]: https://github.com/alexrrouse/xcodebuild-axi/compare/v0.1.9...HEAD
446
+ [unreleased]: https://github.com/alexrrouse/xcodebuild-axi/compare/v0.1.11...HEAD
447
+ [0.1.11]: https://github.com/alexrrouse/xcodebuild-axi/compare/v0.1.10...v0.1.11
448
+ [0.1.10]: https://github.com/alexrrouse/xcodebuild-axi/compare/v0.1.9...v0.1.10
298
449
  [0.1.9]: https://github.com/alexrrouse/xcodebuild-axi/compare/v0.1.8...v0.1.9
299
450
  [0.1.8]: https://github.com/alexrrouse/xcodebuild-axi/compare/v0.1.7...v0.1.8
300
451
  [0.1.7]: https://github.com/alexrrouse/xcodebuild-axi/compare/v0.1.6...v0.1.7
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  <a href="https://www.npmjs.com/package/xcodebuild-axi"><img alt="npm" src="https://img.shields.io/npm/v/xcodebuild-axi?style=flat-square" /></a>
5
5
  <a href="https://axi.md/"><img alt="AXI" src="https://img.shields.io/badge/AXI-compliant-blue?style=flat-square" /></a>
6
6
  <img alt="Platform" src="https://img.shields.io/badge/platform-macOS-lightgrey?style=flat-square" />
7
- <!-- coverage-badge:start --><img alt="xcodebuild coverage" src="https://img.shields.io/badge/xcodebuild_coverage-91.3%25-brightgreen?style=flat-square" /><!-- coverage-badge:end -->
7
+ <!-- coverage-badge:start --><img alt="xcodebuild coverage" src="https://img.shields.io/badge/xcodebuild_coverage-99.4%25-brightgreen?style=flat-square" /><!-- coverage-badge:end -->
8
8
  <img alt="License" src="https://img.shields.io/badge/license-MIT-green?style=flat-square" />
9
9
  </p>
10
10
 
@@ -171,7 +171,7 @@ Every command takes `--help`.
171
171
 
172
172
  <!-- coverage:start -->
173
173
 
174
- **Coverage: 91.3% of the 161 leaves `xcodebuild` documents** — every option, build action, export options key, `-create-xcframework` argument, and the second forms that only a usage line mentions.
174
+ **Coverage: 99.4% of the 161 leaves `xcodebuild` documents** — every option, build action, export options key, `-create-xcframework` argument, and the second forms that only a usage line mentions.
175
175
 
176
176
  A leaf is one switch you could type. Counting options alone says 100% (117/117), which was true and hid every gap below: an option is one thing, and `-exportOptionsPlist` alone opens eighteen more.
177
177
 
@@ -179,10 +179,10 @@ A leaf is one switch you could type. Counting options alone says 100% (117/117),
179
179
  | ------------------------------------------------------ | ------- | --------------- |
180
180
  | `xcodebuild -help` options | 117 | 117 (100%) |
181
181
  | build actions | 10 | 9 (90%) |
182
- | second forms (`-version <infoitem>`, `-license check`) | 8 | 7 (87.5%) |
183
- | `-exportOptionsPlist` keys | 18 | 6 (33.3%) |
182
+ | second forms (`-version <infoitem>`, `-license check`) | 8 | 8 (100%) |
183
+ | `-exportOptionsPlist` keys | 18 | 18 (100%) |
184
184
  | `-create-xcframework` options | 8 | 8 (100%) |
185
- | **total** | **161** | **147 (91.3%)** |
185
+ | **total** | **161** | **160 (99.4%)** |
186
186
 
187
187
  **Reach: 100%** of the 328 command-and-option pairs. The same options, counted once per command xcodebuild accepts them on — because `-target` exposed on `build` and missing from `settings` is not covered for anyone asking `settings`. 0 pairs are open.
188
188
 
@@ -204,65 +204,33 @@ The one action left out is `installsrc` — it copies sources into `SRCROOT` as
204
204
 
205
205
  ### Companion tools
206
206
 
207
- `xcresulttool`, `xccov` and `simctl` are not xcodebuild, so they are not in the number above — but this tool wraps all three, and an agent that has to shell out to one directly has dropped back down. **18.3% of 71 leaves**, counted the same way:
207
+ `xcresulttool`, `xccov` and `simctl` are not xcodebuild, so they are not in the number above — but this tool wraps all three, and an agent that has to shell out to one directly has dropped back down. **43.7% of 71 leaves**, counted the same way:
208
208
 
209
209
  | Tool | Leaves | Covered |
210
210
  | -------------- | ------ | --------- |
211
- | `xcresulttool` | 21 | 4 (19%) |
212
- | `xccov` | 9 | 4 (44.4%) |
211
+ | `xcresulttool` | 21 | 17 (81%) |
212
+ | `xccov` | 9 | 9 (100%) |
213
213
  | `simctl` | 41 | 5 (12.2%) |
214
214
 
215
215
  ### Still open
216
216
 
217
- 45 leaves are known gaps rather than decisions — each one a reason someone would still reach for the raw tool:
218
-
219
- | Leaf | Unreachable from | What that costs |
220
- | ----------------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------- |
221
- | `-version -sdk <name> <infoitem>` | `info` | `info --sdks` lists canonical names; an SDK's Path or ProductBuildVersion cannot be asked for |
222
- | `<buildsetting>=<value>` | `build` | an override can be resolved but not built with, so `--setting` answers a question it cannot then act on |
223
- | `provisioningProfiles` (export options) | `export` | manual signing can be asked for but not completed — the profile per executable has no flag |
224
- | `signingCertificate` (export options) | `export` | manual signing cannot name the certificate to sign with |
225
- | `installerSigningCertificate` (export options) | `export` | a macOS installer package cannot name its signing certificate |
226
- | `thinning` (export options) | `export` | non-App Store exports cannot be thinned for a device variant |
227
- | `stripSwiftSymbols` (export options) | `export` | Swift symbols are always stripped, with no way to keep them |
228
- | `testFlightInternalTestingOnly` (export options) | `export` | a build cannot be marked internal-only, which is what a PR build wants |
229
- | `distributionBundleIdentifier` (export options) | `export` | an archive with several apps cannot pick which one to export |
230
- | `generateAppStoreInformation` (export options) | `export` | App Store information cannot be generated for an upload |
231
- | `iCloudContainerEnvironment` (export options) | `export` | a CloudKit app cannot choose the Development or Production container |
232
- | `manifest` (export options) | `export` | an over-the-web distribution manifest cannot be written |
233
- | `embedOnDemandResourcesAssetPacksInBundle` (export options) | `export` | on-demand resource asset packs cannot be embedded for testing |
234
- | `onDemandResourcesAssetPacksBaseURL` (export options) | `export` | on-demand resource asset packs cannot be pointed at a host |
235
- | `xcresulttool get test-results tests` | `result` | the full test tree of a finished run cannot be listed |
236
- | `xcresulttool get test-results activities` | `result` | the step-by-step activity trail of a failing test cannot be read |
237
- | `xcresulttool get test-results insights` | `result` | Xcode's own diagnosis of a run is left on the floor |
238
- | `xcresulttool get test-results metrics` | `result` | `test --perf-diagnostics` collects performance metrics nothing can read back |
239
- | `xcresulttool get log` | `result` | the build log inside the bundle is reachable only as the raw transcript file |
240
- | `xcresulttool get content-availability` | `result` | whether a bundle even has coverage or test results is found out by failing to read it |
241
- | `xcresulttool export diagnostics` | `result` | `test --diagnostics` collects a diagnostics report that cannot be extracted |
242
- | `xcresulttool export attachments` | `result` | UI test screenshots and attachments cannot be got out of the bundle |
243
- | `xcresulttool export metrics` | `result` | performance measurements cannot be exported as CSV |
244
- | `xcresulttool export evaluations` | `result` | evaluation attachments cannot be exported |
245
- | `xcresulttool compare` | `result` | two runs cannot be diffed, which is the question CI asks most |
246
- | `xcresulttool merge` | `result` | the bundles of a sharded test run cannot be combined |
247
- | `xcresulttool metadata` | `result` | a bundle's own metadata cannot be read |
248
- | `xccov view --report --functions-for-file` | `coverage` | coverage stops at the file, so the uncovered function has no name |
249
- | `xccov view --archive` | `coverage` | a standalone .xccovarchive cannot be read, only a result bundle |
250
- | `xccov view --file` | `coverage` | the per-line coverage of one file cannot be printed |
251
- | `xccov diff` | `coverage` | 'did coverage drop' cannot be answered from two bundles this tool wrote |
252
- | `xccov merge` | `coverage` | the coverage of a sharded run cannot be combined |
253
- | `simctl install` | `sim` | a built .app cannot be put on the simulator it was built for |
254
- | `simctl launch` | `sim` | the app a build just produced cannot be run |
255
- | `simctl terminate` | `?` | a running app cannot be stopped |
256
- | `simctl uninstall` | `?` | an installed app cannot be removed |
257
- | `simctl listapps` | `sim` | what is installed on a simulator cannot be listed |
258
- | `simctl create` | `?` | a missing device cannot be created |
259
- | `simctl delete` | `sim` | stale devices cannot be reclaimed, and they cost gigabytes |
260
- | `simctl io` | `sim` | a screenshot of a failing UI cannot be taken |
261
- | `simctl openurl` | `sim` | a deep link cannot be opened, which is how deep links are tested |
262
- | `simctl privacy` | `sim` | a permission prompt cannot be granted ahead of a UI test |
263
- | `simctl push` | `sim` | a push notification cannot be simulated |
264
- | `simctl status_bar` | `sim` | the status bar cannot be pinned, which screenshot tests need |
265
- | `simctl ui` | `sim` | dark mode and content size cannot be set for a test run |
217
+ 13 leaves are known gaps rather than decisions — each one a reason someone would still reach for the raw tool:
218
+
219
+ | Leaf | Unreachable from | What that costs |
220
+ | ------------------- | ---------------- | ---------------------------------------------------------------- |
221
+ | `simctl install` | `sim` | a built .app cannot be put on the simulator it was built for |
222
+ | `simctl launch` | `sim` | the app a build just produced cannot be run |
223
+ | `simctl terminate` | `?` | a running app cannot be stopped |
224
+ | `simctl uninstall` | `?` | an installed app cannot be removed |
225
+ | `simctl listapps` | `sim` | what is installed on a simulator cannot be listed |
226
+ | `simctl create` | `?` | a missing device cannot be created |
227
+ | `simctl delete` | `sim` | stale devices cannot be reclaimed, and they cost gigabytes |
228
+ | `simctl io` | `sim` | a screenshot of a failing UI cannot be taken |
229
+ | `simctl openurl` | `sim` | a deep link cannot be opened, which is how deep links are tested |
230
+ | `simctl privacy` | `sim` | a permission prompt cannot be granted ahead of a UI test |
231
+ | `simctl push` | `sim` | a push notification cannot be simulated |
232
+ | `simctl status_bar` | `sim` | the status bar cannot be pinned, which screenshot tests need |
233
+ | `simctl ui` | `sim` | dark mode and content size cannot be set for a test run |
266
234
 
267
235
  The denominator is read from `xcodebuild -help` rather than hand-maintained, and this table is written against **Xcode 27.0** — the option list moves between releases. `npm run coverage:check` fails on that Xcode if an option here is unclassified or has been dropped, and reports the difference without failing on any other.
268
236
 
@@ -19,17 +19,46 @@ export declare function readArchiveInfo(archivePath: string): Promise<ArchiveInf
19
19
  /** The `method` values Xcode 27 accepts in an export options plist. */
20
20
  export declare const EXPORT_METHODS: readonly ["app-store-connect", "release-testing", "enterprise", "debugging", "developer-id", "mac-application", "validation"];
21
21
  /**
22
- * Build a minimal export options plist.
22
+ * The names Xcode 26 and earlier used, which every existing pipeline and every
23
+ * answer written before Xcode 27 still says. xcodebuild itself still accepts
24
+ * them, so refusing them here would be this tool being stricter than the thing
25
+ * it wraps.
26
+ */
27
+ export declare const EXPORT_METHOD_ALIASES: Record<string, string>;
28
+ /**
29
+ * Every key `-exportOptionsPlist` accepts, and what it is for.
23
30
  *
24
- * `-exportArchive` requires one, and authoring XML by hand is exactly the kind
25
- * of unguessable side quest an agent fails at. Given a method (and optionally
26
- * a team), the plist is derivable, so derive it.
31
+ * The plist is the whole configuration surface of `-exportArchive`. A key with
32
+ * no flag in front of it is a key that sends the agent back to authoring XML,
33
+ * which is the side quest this file exists to remove.
27
34
  */
28
- export declare function exportOptionsPlist(options: {
35
+ export interface ExportOptions {
29
36
  method: string;
30
37
  destination?: string;
31
38
  teamID?: string;
32
39
  signingStyle?: string;
40
+ signingCertificate?: string;
41
+ installerSigningCertificate?: string;
42
+ /** Bundle identifier -> profile name or UUID, for manual signing. */
43
+ provisioningProfiles?: Record<string, string>;
44
+ distributionBundleIdentifier?: string;
33
45
  uploadSymbols?: boolean;
46
+ stripSwiftSymbols?: boolean;
34
47
  manageAppVersionAndBuildNumber?: boolean;
35
- }): string;
48
+ testFlightInternalTestingOnly?: boolean;
49
+ generateAppStoreInformation?: boolean;
50
+ iCloudContainerEnvironment?: string;
51
+ thinning?: string;
52
+ /** appURL, displayImageURL and fullSizeImageURL, for web distribution. */
53
+ manifest?: Record<string, string>;
54
+ embedOnDemandResourcesAssetPacksInBundle?: boolean;
55
+ onDemandResourcesAssetPacksBaseURL?: string;
56
+ }
57
+ /**
58
+ * Build an export options plist.
59
+ *
60
+ * `-exportArchive` requires one, and authoring XML by hand is exactly the kind
61
+ * of unguessable side quest an agent fails at. Given a method (and whatever
62
+ * else was asked for), the plist is derivable, so derive it.
63
+ */
64
+ export declare function exportOptionsPlist(options: ExportOptions): string;
@@ -46,31 +46,52 @@ export const EXPORT_METHODS = [
46
46
  "validation",
47
47
  ];
48
48
  /**
49
- * Build a minimal export options plist.
49
+ * The names Xcode 26 and earlier used, which every existing pipeline and every
50
+ * answer written before Xcode 27 still says. xcodebuild itself still accepts
51
+ * them, so refusing them here would be this tool being stricter than the thing
52
+ * it wraps.
53
+ */
54
+ export const EXPORT_METHOD_ALIASES = {
55
+ "app-store": "app-store-connect",
56
+ "ad-hoc": "release-testing",
57
+ development: "debugging",
58
+ };
59
+ /**
60
+ * The order keys are written in. Xcode does not care, but a generated file a
61
+ * human may end up reading should group signing with signing.
62
+ */
63
+ const EXPORT_OPTION_ORDER = [
64
+ "method",
65
+ "destination",
66
+ "teamID",
67
+ "signingStyle",
68
+ "signingCertificate",
69
+ "installerSigningCertificate",
70
+ "provisioningProfiles",
71
+ "distributionBundleIdentifier",
72
+ "uploadSymbols",
73
+ "stripSwiftSymbols",
74
+ "manageAppVersionAndBuildNumber",
75
+ "testFlightInternalTestingOnly",
76
+ "generateAppStoreInformation",
77
+ "iCloudContainerEnvironment",
78
+ "thinning",
79
+ "manifest",
80
+ "embedOnDemandResourcesAssetPacksInBundle",
81
+ "onDemandResourcesAssetPacksBaseURL",
82
+ ];
83
+ /**
84
+ * Build an export options plist.
50
85
  *
51
86
  * `-exportArchive` requires one, and authoring XML by hand is exactly the kind
52
- * of unguessable side quest an agent fails at. Given a method (and optionally
53
- * a team), the plist is derivable, so derive it.
87
+ * of unguessable side quest an agent fails at. Given a method (and whatever
88
+ * else was asked for), the plist is derivable, so derive it.
54
89
  */
55
90
  export function exportOptionsPlist(options) {
56
- const entries = [
57
- ` <key>method</key>\n <string>${options.method}</string>`,
58
- ];
59
- if (options.destination) {
60
- entries.push(` <key>destination</key>\n <string>${options.destination}</string>`);
61
- }
62
- if (options.teamID) {
63
- entries.push(` <key>teamID</key>\n <string>${options.teamID}</string>`);
64
- }
65
- if (options.signingStyle) {
66
- entries.push(` <key>signingStyle</key>\n <string>${options.signingStyle}</string>`);
67
- }
68
- if (options.uploadSymbols !== undefined) {
69
- entries.push(` <key>uploadSymbols</key>\n <${options.uploadSymbols}/>`);
70
- }
71
- if (options.manageAppVersionAndBuildNumber !== undefined) {
72
- entries.push(` <key>manageAppVersionAndBuildNumber</key>\n <${options.manageAppVersionAndBuildNumber}/>`);
73
- }
91
+ const entries = EXPORT_OPTION_ORDER.flatMap((key) => {
92
+ const value = options[key];
93
+ return value === undefined ? [] : [` <key>${key}</key>\n${render(value)}`];
94
+ });
74
95
  return `<?xml version="1.0" encoding="UTF-8"?>
75
96
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
76
97
  <plist version="1.0">
@@ -80,4 +101,25 @@ ${entries.join("\n")}
80
101
  </plist>
81
102
  `;
82
103
  }
104
+ function render(value) {
105
+ if (typeof value === "boolean")
106
+ return ` <${value}/>`;
107
+ if (typeof value === "string")
108
+ return ` <string>${escapeXml(value)}</string>`;
109
+ const inner = Object.entries(value)
110
+ .map(([key, entry]) => ` <key>${escapeXml(key)}</key>\n <string>${escapeXml(entry)}</string>`)
111
+ .join("\n");
112
+ return ` <dict>\n${inner}\n </dict>`;
113
+ }
114
+ /**
115
+ * A manifest URL with a query string carries `&`, and an unescaped one makes
116
+ * the plist unparseable -- which xcodebuild reports as a failed export rather
117
+ * than as a bad file.
118
+ */
119
+ function escapeXml(value) {
120
+ return value
121
+ .replace(/&/g, "&amp;")
122
+ .replace(/</g, "&lt;")
123
+ .replace(/>/g, "&gt;");
124
+ }
83
125
  //# sourceMappingURL=archive.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/archive.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/archive.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,14 @@
1
- export declare const COVERAGE_HELP = "usage: xcodebuild-axi coverage <path.xcresult> [flags]\nReads code coverage out of a result bundle \u2014 one percentage per target, not the\nthousands of per-file lines xccov prints by default.\nflags[4]:\n --files list per-file coverage as well as per-target\n --target <name> only this target\n --below <percent> only files under this coverage, e.g. --below 50\n --max <n> rows to list before summarizing the rest (default: 25)\nnote:\n Coverage has to have been collected. Run the tests with\n `xcodebuild-axi test --coverage` if the bundle has none.\nexamples:\n xcodebuild-axi coverage ~/Library/Caches/xcodebuild-axi/MyApps-1a2b3c4d/MyApp-test.xcresult\n xcodebuild-axi coverage build/MyApp.xcresult --files --below 50\n";
2
- export declare const COVERAGE_FLAGS: readonly ["--files", "--target", "--below", "--max"];
1
+ import { type CoverageLine } from "../xccov.js";
2
+ export declare const COVERAGE_HELP = "usage: xcodebuild-axi coverage <path> [flags]\nReads code coverage out of a result bundle \u2014 one percentage per target, not the\nthousands of per-file lines xccov prints by default. The path can also be an\n.xccovreport or .xccovarchive.\nflags[9]:\n --files list per-file coverage as well as per-target\n --target <name> only this target\n --below <percent> only files under this coverage, e.g. --below 50\n --functions <file> per-function coverage for one file, so an uncovered\n branch has a name\n --lines <file> how many times each line of one file ran\n --against <path> compare coverage with an earlier run: what moved, and\n which files went with it\n --merge combine the coverage of two or more runs into one report\n --to <path> where --merge writes (default: under ~/Library/Caches)\n --max <n> rows to list before summarizing the rest (default: 25)\nnote:\n Coverage has to have been collected. Run the tests with\n `xcodebuild-axi test --coverage` if the bundle has none.\n\n --lines reads the archive rather than the report: the report knows what\n percentage of a file ran, the archive knows how many times each line did.\nexamples:\n xcodebuild-axi coverage ~/Library/Caches/xcodebuild-axi/MyApps-1a2b3c4d/MyApp-test.xcresult\n xcodebuild-axi coverage build/MyApp.xcresult --files --below 50\n xcodebuild-axi coverage build/MyApp.xcresult --functions Checkout.swift\n xcodebuild-axi coverage build/MyApp.xcresult --against build/baseline.xcresult\n";
3
+ export declare const COVERAGE_FLAGS: readonly ["--files", "--target", "--below", "--functions", "--lines", "--against", "--merge", "--to", "--max"];
3
4
  export declare function coverageCommand(args: string[]): Promise<string>;
5
+ /** Consecutive lines that never ran, as ranges rather than one row each. */
6
+ export declare function uncoveredRanges(lines: CoverageLine[]): Array<Record<string, unknown>>;
7
+ /**
8
+ * Which way coverage moved, in words.
9
+ *
10
+ * Words rather than a signed percentage because TOON quotes any scalar that
11
+ * starts with `-`, and `"-2.1%"` costs more in quotes than `down 2.1%` costs
12
+ * in letters.
13
+ */
14
+ export declare function movement(delta: number): string;