xcodebuild-axi 0.1.7 → 0.1.9

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.
Files changed (89) hide show
  1. package/CHANGELOG.md +137 -2
  2. package/README.md +77 -2
  3. package/dist/src/action.d.ts +8 -0
  4. package/dist/src/action.js +37 -30
  5. package/dist/src/action.js.map +1 -1
  6. package/dist/src/args.d.ts +11 -0
  7. package/dist/src/args.js +30 -0
  8. package/dist/src/args.js.map +1 -1
  9. package/dist/src/cli.d.ts +8 -0
  10. package/dist/src/cli.js +53 -22
  11. package/dist/src/cli.js.map +1 -1
  12. package/dist/src/commands/analyze.d.ts +2 -0
  13. package/dist/src/commands/analyze.js +3 -1
  14. package/dist/src/commands/analyze.js.map +1 -1
  15. package/dist/src/commands/archive.d.ts +1 -0
  16. package/dist/src/commands/archive.js +7 -3
  17. package/dist/src/commands/archive.js.map +1 -1
  18. package/dist/src/commands/build.d.ts +1 -0
  19. package/dist/src/commands/build.js +2 -2
  20. package/dist/src/commands/build.js.map +1 -1
  21. package/dist/src/commands/clean.d.ts +2 -1
  22. package/dist/src/commands/clean.js +38 -4
  23. package/dist/src/commands/clean.js.map +1 -1
  24. package/dist/src/commands/coverage.d.ts +1 -0
  25. package/dist/src/commands/coverage.js +7 -2
  26. package/dist/src/commands/coverage.js.map +1 -1
  27. package/dist/src/commands/destinations.d.ts +1 -0
  28. package/dist/src/commands/destinations.js +6 -2
  29. package/dist/src/commands/destinations.js.map +1 -1
  30. package/dist/src/commands/find.d.ts +1 -0
  31. package/dist/src/commands/find.js +2 -2
  32. package/dist/src/commands/find.js.map +1 -1
  33. package/dist/src/commands/info.d.ts +1 -0
  34. package/dist/src/commands/info.js +2 -2
  35. package/dist/src/commands/info.js.map +1 -1
  36. package/dist/src/commands/localize.d.ts +1 -0
  37. package/dist/src/commands/localize.js +2 -2
  38. package/dist/src/commands/localize.js.map +1 -1
  39. package/dist/src/commands/migrate.d.ts +1 -0
  40. package/dist/src/commands/migrate.js +2 -2
  41. package/dist/src/commands/migrate.js.map +1 -1
  42. package/dist/src/commands/packages.d.ts +2 -1
  43. package/dist/src/commands/packages.js +24 -8
  44. package/dist/src/commands/packages.js.map +1 -1
  45. package/dist/src/commands/platforms.d.ts +1 -0
  46. package/dist/src/commands/platforms.js +2 -2
  47. package/dist/src/commands/platforms.js.map +1 -1
  48. package/dist/src/commands/result.d.ts +1 -0
  49. package/dist/src/commands/result.js +10 -11
  50. package/dist/src/commands/result.js.map +1 -1
  51. package/dist/src/commands/schemes.d.ts +1 -0
  52. package/dist/src/commands/schemes.js +2 -1
  53. package/dist/src/commands/schemes.js.map +1 -1
  54. package/dist/src/commands/settings.d.ts +7 -1
  55. package/dist/src/commands/settings.js +136 -22
  56. package/dist/src/commands/settings.js.map +1 -1
  57. package/dist/src/commands/setup.d.ts +1 -0
  58. package/dist/src/commands/setup.js +2 -2
  59. package/dist/src/commands/setup.js.map +1 -1
  60. package/dist/src/commands/sim.d.ts +1 -0
  61. package/dist/src/commands/sim.js +2 -2
  62. package/dist/src/commands/sim.js.map +1 -1
  63. package/dist/src/commands/test.d.ts +1 -0
  64. package/dist/src/commands/test.js +11 -15
  65. package/dist/src/commands/test.js.map +1 -1
  66. package/dist/src/commands/testplans.d.ts +1 -0
  67. package/dist/src/commands/testplans.js +2 -1
  68. package/dist/src/commands/testplans.js.map +1 -1
  69. package/dist/src/commands/tests.d.ts +2 -1
  70. package/dist/src/commands/tests.js +21 -9
  71. package/dist/src/commands/tests.js.map +1 -1
  72. package/dist/src/commands/xcframework.d.ts +19 -1
  73. package/dist/src/commands/xcframework.js +116 -27
  74. package/dist/src/commands/xcframework.js.map +1 -1
  75. package/dist/src/errors.js +10 -0
  76. package/dist/src/errors.js.map +1 -1
  77. package/dist/src/report.d.ts +22 -1
  78. package/dist/src/report.js +40 -0
  79. package/dist/src/report.js.map +1 -1
  80. package/dist/src/scheme.d.ts +34 -0
  81. package/dist/src/scheme.js +46 -0
  82. package/dist/src/scheme.js.map +1 -1
  83. package/dist/src/surface.d.ts +134 -1
  84. package/dist/src/surface.js +668 -265
  85. package/dist/src/surface.js.map +1 -1
  86. package/dist/src/xcresult.d.ts +42 -0
  87. package/dist/src/xcresult.js +41 -0
  88. package/dist/src/xcresult.js.map +1 -1
  89. package/package.json +1 -1
@@ -6,6 +6,20 @@
6
6
  * rather than inferred: every option `xcodebuild -help` prints is classified,
7
7
  * and `npm run coverage -- --check` fails if a new Xcode adds one this file
8
8
  * has never heard of.
9
+ *
10
+ * "Covered" is asked along four axes, because for a long time it was only
11
+ * asked along the first and the misses all landed on the other three:
12
+ *
13
+ * 1. **Options** — does any command reach this option at all.
14
+ * 2. **Reach** — does every command xcodebuild accepts it on reach it. An
15
+ * option exposed on `build` and missing from `settings` is not covered for
16
+ * anyone asking `settings`, and nothing used to say so.
17
+ * 3. **Forms and sub-surfaces** — the options behind an option: the keys of
18
+ * `-exportOptionsPlist`, the arguments of `-create-xcframework`, the second
19
+ * modes that `-help` documents only inside a usage line.
20
+ * 4. **Companion tools** — `xcresulttool`, `xccov`, `simctl`. Not xcodebuild,
21
+ * so not in its headline number, but this tool wraps them and an agent that
22
+ * has to shell out to one directly has dropped back down.
9
23
  */
10
24
  /**
11
25
  * The Xcode whose `xcodebuild -help` this map was written against.
@@ -18,249 +32,187 @@
18
32
  * mismatch here and merely reports one elsewhere.
19
33
  */
20
34
  export const AUTHORED_AGAINST = "27.0";
21
- export const OPTION_COVERAGE = {
22
- "-allowProvisioningDeviceRegistration": {
23
- status: "exposed",
24
- via: "archive --allow-device-registration",
25
- },
26
- "-allowProvisioningUpdates": {
27
- status: "exposed",
28
- via: "build --allow-provisioning",
29
- },
30
- "-alltargets": { status: "exposed", via: "build --all-targets" },
31
- "-arch": { status: "exposed", via: "build --arch" },
32
- "-architecture": {
33
- status: "exposed",
34
- via: "platforms device-support --architecture",
35
- },
36
- "-archivePath": { status: "exposed", via: "archive --archive-path" },
37
- "-authenticationKeyID": { status: "exposed", via: "archive --auth-key-id" },
38
- "-authenticationKeyIssuerID": {
39
- status: "exposed",
40
- via: "archive --auth-key-issuer",
41
- },
42
- "-authenticationKeyPath": {
43
- status: "exposed",
44
- via: "archive --auth-key-path",
45
- },
46
- "-checkFirstLaunchStatus": {
47
- status: "exposed",
48
- via: "platforms first-launch --status",
49
- },
50
- "-clonedSourcePackagesDirPath": {
51
- status: "exposed",
52
- via: "packages --cache",
53
- },
54
- "-codesizeProfileOutputDir": { status: "exposed", via: "build --codesize" },
55
- "-collect-test-diagnostics": { status: "exposed", via: "test --diagnostics" },
56
- "-configuration": { status: "exposed", via: "build --configuration" },
57
- "-create-xcframework": { status: "exposed", via: "xcframework" },
58
- "-default-test-execution-time-allowance": {
59
- status: "exposed",
60
- via: "test --default-test-timeout",
61
- },
62
- "-defaultLanguage": {
63
- status: "exposed",
64
- via: "localize export --default-language",
65
- },
66
- "-defaultPackageRegistryURL": {
67
- status: "exposed",
68
- via: "packages --registry-url",
69
- },
70
- "-deleteComponent": { status: "exposed", via: "platforms component delete" },
71
- "-derivedDataPath": { status: "exposed", via: "build --derived-data" },
72
- "-destination": { status: "exposed", via: "build --destination" },
73
- "-destination-timeout": {
74
- status: "exposed",
75
- via: "build --destination-timeout",
76
- },
77
- "-disableAutomaticPackageResolution": {
78
- status: "exposed",
79
- via: "build --no-auto-resolve",
80
- },
81
- "-disablePackageRepositoryCache": {
82
- status: "exposed",
83
- via: "build --no-package-cache",
84
- },
85
- "-downloadAllPlatforms": {
86
- status: "exposed",
87
- via: "platforms download --all",
88
- },
89
- "-downloadComponent": {
90
- status: "exposed",
91
- via: "platforms component download",
92
- },
93
- "-downloadPlatform": { status: "exposed", via: "platforms download" },
94
- "-enableAddressSanitizer": {
95
- status: "exposed",
96
- via: "build --sanitizer address",
97
- },
98
- "-enableCodeCoverage": { status: "exposed", via: "test --coverage" },
99
- "-enableCodesizeProfile": { status: "exposed", via: "build --codesize" },
100
- "-enablePerformanceTestsDiagnostics": {
101
- status: "exposed",
102
- via: "test --perf-diagnostics",
103
- },
104
- "-enableThreadSanitizer": {
105
- status: "exposed",
106
- via: "build --sanitizer thread",
107
- },
108
- "-enableUndefinedBehaviorSanitizer": {
109
- status: "exposed",
110
- via: "build --sanitizer undefined",
111
- },
112
- "-enumerate-tests": { status: "exposed", via: "tests" },
113
- "-exportArchive": { status: "exposed", via: "export" },
114
- "-exportLanguage": { status: "exposed", via: "localize export --language" },
115
- "-exportLocalizations": { status: "exposed", via: "localize export" },
116
- "-exportNotarizedApp": { status: "exposed", via: "export --notarized" },
117
- "-exportOptionsPlist": { status: "exposed", via: "export --options" },
118
- "-exportPath": { status: "exposed", via: "export --output" },
119
- "-find-executable": { status: "exposed", via: "find" },
120
- "-find-library": { status: "exposed", via: "find --library" },
121
- "-hideShellScriptEnvironment": {
122
- status: "exposed",
123
- via: "build --hide-script-env",
124
- },
125
- "-importComponent": { status: "exposed", via: "platforms component import" },
126
- "-importLocalizations": { status: "exposed", via: "localize import" },
127
- "-importPath": {
128
- status: "exposed",
129
- via: "platforms component import --import-path",
130
- },
131
- "-importPlatform": { status: "exposed", via: "platforms import" },
132
- "-jobs": { status: "exposed", via: "build --jobs" },
133
- "-list": { status: "exposed", via: "schemes" },
134
- "-localizationPath": { status: "exposed", via: "localize --path" },
135
- "-maximum-concurrent-test-device-destinations": {
136
- status: "exposed",
137
- via: "test --max-device-destinations",
138
- },
139
- "-maximum-concurrent-test-simulator-destinations": {
140
- status: "exposed",
141
- via: "test --max-sim-destinations",
142
- },
143
- "-maximum-parallel-testing-workers": {
144
- status: "exposed",
145
- via: "test --max-workers",
146
- },
147
- "-maximum-test-execution-time-allowance": {
148
- status: "exposed",
149
- via: "test --test-timeout",
150
- },
151
- "-modelCode": {
152
- status: "exposed",
153
- via: "platforms device-support --model-code",
154
- },
155
- "-only-test-configuration": {
156
- status: "exposed",
157
- via: "test --only-configuration",
158
- },
159
- "-only-testing": { status: "exposed", via: "test --only" },
160
- "-onlyUsePackageVersionsFromResolvedFile": {
161
- status: "exposed",
162
- via: "packages --offline",
163
- },
164
- "-osVersion": {
165
- status: "exposed",
166
- via: "platforms device-support --os-version",
167
- },
168
- "-packageAuthorizationProvider": {
169
- status: "exposed",
170
- via: "packages --package-auth",
171
- },
172
- "-packageCachePath": { status: "exposed", via: "packages --package-cache" },
173
- "-packageDependencySCMToRegistryTransformation": {
174
- status: "exposed",
175
- via: "packages --scm-to-registry",
176
- },
177
- "-packageFingerprintPolicy": {
178
- status: "exposed",
179
- via: "packages --fingerprint-policy",
180
- },
181
- "-packageSigningEntityPolicy": {
182
- status: "exposed",
183
- via: "packages --signing-entity-policy",
184
- },
185
- "-parallel-testing-enabled": {
186
- status: "exposed",
187
- via: "test --parallel / --no-parallel",
188
- },
189
- "-parallel-testing-worker-count": {
190
- status: "exposed",
191
- via: "test --max-workers",
192
- },
193
- "-parallelizeTargets": {
194
- status: "exposed",
195
- via: "build --parallelize-targets",
196
- },
197
- "-platform": {
198
- status: "exposed",
199
- via: "platforms device-support --platform",
200
- },
201
- "-prepareDeviceSupport": {
202
- status: "exposed",
203
- via: "platforms device-support",
204
- },
205
- "-resolvePackageDependencies": {
206
- status: "exposed",
207
- via: "packages --resolve",
208
- },
209
- "-retry-tests-on-failure": { status: "exposed", via: "test --retry" },
210
- "-run-tests-until-failure": {
211
- status: "exposed",
212
- via: "test --until-failure",
213
- },
214
- "-runFirstLaunch": { status: "exposed", via: "platforms first-launch" },
215
- "-scheme": { status: "exposed", via: "build --scheme" },
216
- "-scmProvider": { status: "exposed", via: "packages --scm-provider" },
217
- "-sdk": { status: "exposed", via: "build --sdk" },
218
- "-showBuildSettings": { status: "exposed", via: "settings" },
219
- "-showBuildSettingsForIndex": {
220
- status: "exposed",
221
- via: "settings --for-index",
222
- },
223
- "-showBuildTimingSummary": { status: "exposed", via: "build --timing" },
224
- "-showComponent": { status: "exposed", via: "platforms component show" },
225
- "-showdestinations": { status: "exposed", via: "destinations" },
226
- "-showsdks": { status: "exposed", via: "info --sdks" },
227
- "-showTestPlans": { status: "exposed", via: "testplans" },
228
- "-skip-test-configuration": {
229
- status: "exposed",
230
- via: "test --skip-configuration",
231
- },
232
- "-skip-testing": { status: "exposed", via: "test --skip" },
233
- "-skipPackagePluginValidation": {
234
- status: "exposed",
235
- via: "build --skip-plugin-validation",
236
- },
237
- "-skipPackageSignatureValidation": {
238
- status: "exposed",
239
- via: "build --skip-signature-validation",
240
- },
241
- "-skipPackageUpdates": {
242
- status: "exposed",
243
- via: "build --skip-package-updates",
244
- },
245
- "-skipUnavailableActions": {
246
- status: "exposed",
247
- via: "build --skip-unavailable-actions",
248
- },
249
- "-target": { status: "exposed", via: "build --target" },
250
- "-test-iterations": { status: "exposed", via: "test --iterations" },
251
- "-test-repetition-relaunch-enabled": {
35
+ /**
36
+ * The commands an option can apply to, by kind.
37
+ *
38
+ * xcodebuild's own usage lines are the authority: `-target` and `-alltargets`
39
+ * sit on the same line as `-showBuildSettings`, so `settings` is expected to
40
+ * take them; `-showdestinations` appears only on the `-scheme` lines, so
41
+ * `destinations` is not expected to take a target.
42
+ */
43
+ export const BUILD_FAMILY = [
44
+ "build",
45
+ "test",
46
+ "tests",
47
+ "analyze",
48
+ "archive",
49
+ ];
50
+ export const SURFACES = {
51
+ /** Shapes compilation, so only the commands that compile something. */
52
+ build: BUILD_FAMILY,
53
+ /** Applies to any action word, `clean` included. */
54
+ action: [...BUILD_FAMILY, "clean"],
55
+ /** Changes what the project resolves to, which `settings` reports on. */
56
+ resolution: [...BUILD_FAMILY, "clean", "settings"],
57
+ /** Package resolution, which `packages` drives on its own. */
58
+ package: [...BUILD_FAMILY, "packages"],
59
+ /** Selects a scheme to act on. */
60
+ scheme: [
61
+ ...BUILD_FAMILY,
62
+ "clean",
63
+ "settings",
64
+ "destinations",
65
+ "testplans",
66
+ "packages",
67
+ "localize",
68
+ ],
69
+ /** Constrains which tests are run, and so which tests are enumerated. */
70
+ testing: ["test", "tests"],
71
+ };
72
+ /** `"settings --for-index"` -> `{ command: "settings", via: "settings --for-index" }`. */
73
+ export function at(via) {
74
+ return { command: via.split(/\s+/)[0], via };
75
+ }
76
+ /** The same flag on several commands, which is the common case. */
77
+ function sameFlag(commands, flag) {
78
+ return commands.map((command) => at(`${command} ${flag}`));
79
+ }
80
+ /**
81
+ * An option every command in a surface reaches by the same flag name, minus
82
+ * whichever of them is listed as a gap.
83
+ */
84
+ function everywhere(surface, flag, options = {}) {
85
+ const absent = new Set([
86
+ ...(options.missing ?? []).map((gap) => gap.command),
87
+ ...(options.declined ?? []).map((gap) => gap.command),
88
+ ]);
89
+ return {
252
90
  status: "exposed",
253
- via: "test --relaunch",
254
- },
255
- "-test-timeouts-enabled": { status: "exposed", via: "test --test-timeout" },
256
- "-testLanguage": { status: "exposed", via: "test --language" },
257
- "-testPlan": { status: "exposed", via: "test --test-plan" },
258
- "-testProductsPath": { status: "exposed", via: "test --test-products" },
259
- "-testRegion": { status: "exposed", via: "test --region" },
260
- "-toolchain": { status: "exposed", via: "build --toolchain" },
261
- "-version": { status: "exposed", via: "info" },
262
- "-xcconfig": { status: "exposed", via: "build --xcconfig" },
263
- "-xctestrun": { status: "exposed", via: "test --xctestrun" },
91
+ surface,
92
+ on: [
93
+ ...sameFlag(SURFACES[surface].filter((command) => !absent.has(command)), flag),
94
+ ...(options.also ?? []).map(at),
95
+ ],
96
+ ...(options.missing ? { missing: options.missing } : {}),
97
+ ...(options.declined ? { declined: options.declined } : {}),
98
+ };
99
+ }
100
+ /** An option reached from one place, which needs no surface. */
101
+ function only(...via) {
102
+ return { status: "exposed", on: via.map(at) };
103
+ }
104
+ const gap = (command, why) => ({ command, why });
105
+ /** The gaps this file exists to make visible, phrased once and shared. */
106
+ export const OPTION_COVERAGE = {
107
+ "-allowProvisioningDeviceRegistration": only("archive --allow-device-registration", "export --allow-device-registration"),
108
+ "-allowProvisioningUpdates": everywhere("build", "--allow-provisioning", {
109
+ also: ["export --allow-provisioning"],
110
+ }),
111
+ "-alltargets": everywhere("resolution", "--all-targets"),
112
+ "-arch": everywhere("resolution", "--arch"),
113
+ "-architecture": only("platforms device-support --architecture"),
114
+ "-archivePath": only("archive --archive-path", "export <path.xcarchive>"),
115
+ "-authenticationKeyID": only("archive --auth-key-id", "export --auth-key-id"),
116
+ "-authenticationKeyIssuerID": only("archive --auth-key-issuer", "export --auth-key-issuer"),
117
+ "-authenticationKeyPath": only("archive --auth-key-path", "export --auth-key-path"),
118
+ "-checkFirstLaunchStatus": only("platforms first-launch --status"),
119
+ "-clonedSourcePackagesDirPath": everywhere("package", "--cache"),
120
+ "-codesizeProfileOutputDir": everywhere("build", "--codesize"),
121
+ "-collect-test-diagnostics": only("test --diagnostics"),
122
+ "-configuration": everywhere("resolution", "--configuration"),
123
+ "-create-xcframework": only("xcframework"),
124
+ "-default-test-execution-time-allowance": only("test --default-test-timeout"),
125
+ "-defaultLanguage": only("localize export --default-language"),
126
+ "-defaultPackageRegistryURL": everywhere("package", "--registry-url"),
127
+ "-deleteComponent": only("platforms component delete"),
128
+ "-derivedDataPath": everywhere("resolution", "--derived-data", {
129
+ also: ["packages --derived-data"],
130
+ }),
131
+ "-destination": everywhere("resolution", "--destination"),
132
+ "-destination-timeout": everywhere("resolution", "--destination-timeout"),
133
+ "-disableAutomaticPackageResolution": everywhere("package", "--no-auto-resolve"),
134
+ "-disablePackageRepositoryCache": everywhere("package", "--no-package-cache"),
135
+ "-downloadAllPlatforms": only("platforms download --all"),
136
+ "-downloadComponent": only("platforms component download"),
137
+ "-downloadPlatform": only("platforms download"),
138
+ "-enableAddressSanitizer": everywhere("build", "--sanitizer address"),
139
+ "-enableCodeCoverage": only("test --coverage"),
140
+ "-enableCodesizeProfile": everywhere("build", "--codesize"),
141
+ "-enablePerformanceTestsDiagnostics": only("test --perf-diagnostics"),
142
+ "-enableThreadSanitizer": everywhere("build", "--sanitizer thread"),
143
+ "-enableUndefinedBehaviorSanitizer": everywhere("build", "--sanitizer undefined"),
144
+ "-enumerate-tests": only("tests"),
145
+ "-exportArchive": only("export"),
146
+ "-exportLanguage": only("localize export --language"),
147
+ "-exportLocalizations": only("localize export"),
148
+ "-exportNotarizedApp": only("export --notarized"),
149
+ "-exportOptionsPlist": only("export --options"),
150
+ "-exportPath": only("export --output", "platforms download --export-path", "platforms component download --export-path"),
151
+ "-find-executable": only("find"),
152
+ "-find-library": only("find --library"),
153
+ "-hideShellScriptEnvironment": everywhere("build", "--hide-script-env"),
154
+ "-importComponent": only("platforms component import"),
155
+ "-importLocalizations": only("localize import"),
156
+ "-importPath": only("platforms component import --import-path"),
157
+ "-importPlatform": only("platforms import"),
158
+ "-jobs": everywhere("build", "--jobs"),
159
+ "-list": only("schemes"),
160
+ "-localizationPath": only("localize --path"),
161
+ "-maximum-concurrent-test-device-destinations": only("test --max-device-destinations"),
162
+ "-maximum-concurrent-test-simulator-destinations": only("test --max-sim-destinations"),
163
+ "-maximum-parallel-testing-workers": only("test --max-workers"),
164
+ "-maximum-test-execution-time-allowance": only("test --test-timeout"),
165
+ "-modelCode": only("platforms device-support --model-code"),
166
+ "-only-test-configuration": only("test --only-configuration"),
167
+ "-only-testing": everywhere("testing", "--only"),
168
+ "-onlyUsePackageVersionsFromResolvedFile": everywhere("package", "--offline"),
169
+ "-osVersion": only("platforms device-support --os-version"),
170
+ "-packageAuthorizationProvider": everywhere("package", "--package-auth"),
171
+ "-packageCachePath": everywhere("package", "--package-cache"),
172
+ "-packageDependencySCMToRegistryTransformation": everywhere("package", "--scm-to-registry"),
173
+ "-packageFingerprintPolicy": everywhere("package", "--fingerprint-policy"),
174
+ "-packageSigningEntityPolicy": everywhere("package", "--signing-entity-policy"),
175
+ "-parallel-testing-enabled": only("test --parallel / --no-parallel"),
176
+ "-parallel-testing-worker-count": only("test --max-workers"),
177
+ "-parallelizeTargets": everywhere("build", "--parallelize-targets"),
178
+ "-platform": only("platforms device-support --platform"),
179
+ "-prepareDeviceSupport": only("platforms device-support"),
180
+ "-resolvePackageDependencies": only("packages --resolve"),
181
+ "-retry-tests-on-failure": only("test --retry"),
182
+ "-run-tests-until-failure": only("test --until-failure"),
183
+ "-runFirstLaunch": only("platforms first-launch"),
184
+ "-scheme": everywhere("scheme", "--scheme"),
185
+ "-scmProvider": everywhere("package", "--scm-provider"),
186
+ "-sdk": everywhere("resolution", "--sdk", {
187
+ also: ["find --sdk"],
188
+ }),
189
+ "-showBuildSettings": only("settings"),
190
+ "-showBuildSettingsForIndex": only("settings --for-index"),
191
+ "-showBuildTimingSummary": everywhere("build", "--timing"),
192
+ "-showComponent": only("platforms component show"),
193
+ "-showdestinations": only("destinations"),
194
+ "-showsdks": only("info --sdks"),
195
+ "-showTestPlans": only("testplans"),
196
+ "-skip-test-configuration": only("test --skip-configuration"),
197
+ "-skip-testing": everywhere("testing", "--skip"),
198
+ "-skipPackagePluginValidation": everywhere("package", "--skip-plugin-validation"),
199
+ "-skipPackageSignatureValidation": everywhere("package", "--skip-signature-validation"),
200
+ "-skipPackageUpdates": everywhere("package", "--skip-package-updates"),
201
+ "-skipUnavailableActions": everywhere("build", "--skip-unavailable-actions"),
202
+ "-target": everywhere("resolution", "--target"),
203
+ "-test-iterations": only("test --iterations"),
204
+ "-test-repetition-relaunch-enabled": only("test --relaunch"),
205
+ "-test-timeouts-enabled": only("test --test-timeout"),
206
+ "-testLanguage": only("test --language"),
207
+ "-testPlan": everywhere("testing", "--test-plan"),
208
+ "-testProductsPath": only("test --test-products"),
209
+ "-testRegion": only("test --region"),
210
+ "-toolchain": everywhere("resolution", "--toolchain", {
211
+ also: ["find --toolchain"],
212
+ }),
213
+ "-version": only("info"),
214
+ "-xcconfig": everywhere("resolution", "--xcconfig"),
215
+ "-xctestrun": only("test --xctestrun"),
264
216
  "-json": {
265
217
  status: "always",
266
218
  why: "every read-only query asks for JSON, then reports TOON",
@@ -269,10 +221,7 @@ export const OPTION_COVERAGE = {
269
221
  status: "always",
270
222
  why: "set from the .xcodeproj found in the working directory",
271
223
  },
272
- "-resultBundlePath": {
273
- status: "exposed",
274
- via: "build --artifacts-dir",
275
- },
224
+ "-resultBundlePath": everywhere("action", "--artifacts-dir"),
276
225
  "-skipMacroValidation": {
277
226
  status: "always",
278
227
  why: "macro trust is an interactive prompt in disguise, and an agent cannot answer it",
@@ -293,46 +242,438 @@ export const OPTION_COVERAGE = {
293
242
  status: "always",
294
243
  why: "set from the .xcworkspace found in the working directory",
295
244
  },
296
- "-convert-project": { status: "exposed", via: "migrate --format" },
245
+ "-convert-project": only("migrate --format"),
297
246
  "-help": {
298
247
  status: "superseded",
299
248
  why: "`xcodebuild-axi --help`, which answers it in a fraction of the tokens",
300
249
  },
301
- "-license": { status: "exposed", via: "platforms license" },
302
- "-quiet": { status: "exposed", via: "build --log-level quiet" },
303
- "-resultBundleVersion": { status: "exposed", via: "build --bundle-version" },
304
- "-resultStreamPath": { status: "exposed", via: "build --stream" },
250
+ "-license": only("platforms license"),
251
+ "-quiet": everywhere("action", "--log-level quiet"),
252
+ "-resultBundleVersion": everywhere("build", "--bundle-version"),
253
+ "-resultStreamPath": everywhere("build", "--stream"),
305
254
  "-usage": {
306
255
  status: "superseded",
307
256
  why: "`xcodebuild-axi <command> --help`, per command rather than all 117 at once",
308
257
  },
309
- "-verbose": { status: "exposed", via: "build --log-level verbose" },
258
+ "-verbose": everywhere("action", "--log-level verbose"),
310
259
  };
311
260
  /** The build actions, classified the same way. */
312
261
  export const ACTION_COVERAGE = {
313
- build: { status: "exposed", via: "build" },
314
- "build-for-testing": { status: "exposed", via: "build --for-testing" },
315
- analyze: { status: "exposed", via: "analyze" },
316
- archive: { status: "exposed", via: "archive" },
317
- test: { status: "exposed", via: "test" },
318
- "test-without-building": {
319
- status: "exposed",
320
- via: "test --without-building",
321
- },
322
- docbuild: { status: "exposed", via: "build --docs" },
323
- clean: { status: "exposed", via: "clean" },
324
- install: { status: "exposed", via: "build --install" },
262
+ build: only("build"),
263
+ "build-for-testing": only("build --for-testing"),
264
+ analyze: only("analyze"),
265
+ archive: only("archive"),
266
+ test: only("test"),
267
+ "test-without-building": only("test --without-building"),
268
+ docbuild: only("build --docs"),
269
+ clean: only("clean"),
270
+ install: only("build --install"),
325
271
  installsrc: {
326
272
  status: "n/a",
327
273
  why: "copies sources into SRCROOT as root; a packaging step, not an agent loop",
328
274
  },
329
275
  };
276
+ /**
277
+ * The second forms of an option, which `-help` documents only inside a usage
278
+ * line or a sentence. Nothing enumerates these, so they are the easiest kind
279
+ * of gap to ship: `-runFirstLaunch -checkForNewerComponents` is a flag on a
280
+ * flag, mentioned once, in prose.
281
+ */
282
+ export const FORM_COVERAGE = {
283
+ "-version -sdk <name> <infoitem>": {
284
+ status: "missing",
285
+ from: "info",
286
+ why: "`info --sdks` lists canonical names; an SDK's Path or ProductBuildVersion cannot be asked for",
287
+ },
288
+ "-runFirstLaunch -checkForNewerComponents": only("platforms first-launch --check-updates"),
289
+ "-license check": only("platforms license"),
290
+ "<buildsetting>=<value>": {
291
+ status: "exposed",
292
+ on: [{ command: "settings", via: "settings --setting" }],
293
+ missing: [
294
+ gap("build", "an override can be resolved but not built with, so `--setting` answers a question it cannot then act on"),
295
+ ],
296
+ },
297
+ "-only-testing @<response-file>": only("test --only"),
298
+ "-skip-testing @<response-file>": only("test --skip"),
299
+ "-showBuildSettings -json": {
300
+ status: "always",
301
+ why: "always asked for as JSON, then reported as TOON",
302
+ },
303
+ "-create-xcframework -help": {
304
+ status: "superseded",
305
+ why: "`xcodebuild-axi xcframework --help`",
306
+ },
307
+ };
308
+ /**
309
+ * `-exportOptionsPlist` keys.
310
+ *
311
+ * The plist is the whole configuration surface of `-exportArchive`, and
312
+ * authoring one by hand is exactly the side quest `--method` exists to remove
313
+ * — so a key with no flag is a key that sends the agent back to writing XML.
314
+ */
315
+ export const EXPORT_OPTION_COVERAGE = {
316
+ method: only("export --method"),
317
+ destination: only("export --upload"),
318
+ teamID: only("export --team"),
319
+ signingStyle: only("export --signing-style"),
320
+ uploadSymbols: only("export --no-upload-symbols"),
321
+ manageAppVersionAndBuildNumber: only("export --no-manage-version"),
322
+ provisioningProfiles: {
323
+ status: "missing",
324
+ from: "export",
325
+ why: "manual signing can be asked for but not completed — the profile per executable has no flag",
326
+ },
327
+ signingCertificate: {
328
+ status: "missing",
329
+ from: "export",
330
+ why: "manual signing cannot name the certificate to sign with",
331
+ },
332
+ installerSigningCertificate: {
333
+ status: "missing",
334
+ from: "export",
335
+ why: "a macOS installer package cannot name its signing certificate",
336
+ },
337
+ thinning: {
338
+ status: "missing",
339
+ from: "export",
340
+ why: "non-App Store exports cannot be thinned for a device variant",
341
+ },
342
+ stripSwiftSymbols: {
343
+ status: "missing",
344
+ from: "export",
345
+ why: "Swift symbols are always stripped, with no way to keep them",
346
+ },
347
+ testFlightInternalTestingOnly: {
348
+ status: "missing",
349
+ from: "export",
350
+ why: "a build cannot be marked internal-only, which is what a PR build wants",
351
+ },
352
+ distributionBundleIdentifier: {
353
+ status: "missing",
354
+ from: "export",
355
+ why: "an archive with several apps cannot pick which one to export",
356
+ },
357
+ generateAppStoreInformation: {
358
+ status: "missing",
359
+ from: "export",
360
+ why: "App Store information cannot be generated for an upload",
361
+ },
362
+ iCloudContainerEnvironment: {
363
+ status: "missing",
364
+ from: "export",
365
+ why: "a CloudKit app cannot choose the Development or Production container",
366
+ },
367
+ manifest: {
368
+ status: "missing",
369
+ from: "export",
370
+ why: "an over-the-web distribution manifest cannot be written",
371
+ },
372
+ embedOnDemandResourcesAssetPacksInBundle: {
373
+ status: "missing",
374
+ from: "export",
375
+ why: "on-demand resource asset packs cannot be embedded for testing",
376
+ },
377
+ onDemandResourcesAssetPacksBaseURL: {
378
+ status: "missing",
379
+ from: "export",
380
+ why: "on-demand resource asset packs cannot be pointed at a host",
381
+ },
382
+ };
383
+ /** `-create-xcframework`'s own options, which only its `-help` prints. */
384
+ export const XCFRAMEWORK_COVERAGE = {
385
+ "-framework": only("xcframework --framework"),
386
+ "-library": only("xcframework --library"),
387
+ "-headers": only("xcframework --headers"),
388
+ "-output": only("xcframework --output"),
389
+ "-archive": only("xcframework --archive"),
390
+ "-debug-symbols": only("xcframework --debug-symbols"),
391
+ "-allow-internal-distribution": only("xcframework --allow-internal-distribution"),
392
+ "-help": {
393
+ status: "superseded",
394
+ why: "`xcodebuild-axi xcframework --help`",
395
+ },
396
+ };
397
+ /**
398
+ * `xcresulttool`, which owns every answer about a run that already happened.
399
+ *
400
+ * The result bundle is this tool's source of truth (see AGENTS.md), so a leaf
401
+ * missing here is an answer the bundle holds and the tool cannot read out —
402
+ * `test --diagnostics` collecting a report nothing can open, for instance.
403
+ */
404
+ export const XCRESULT_COVERAGE = {
405
+ "get build-results": only("result", "build", "test"),
406
+ "get test-results summary": only("result", "test"),
407
+ "get test-results tests": {
408
+ status: "missing",
409
+ from: "result",
410
+ why: "the full test tree of a finished run cannot be listed",
411
+ },
412
+ "get test-results test-details": only("result", "test"),
413
+ "get test-results activities": {
414
+ status: "missing",
415
+ from: "result",
416
+ why: "the step-by-step activity trail of a failing test cannot be read",
417
+ },
418
+ "get test-results insights": {
419
+ status: "missing",
420
+ from: "result",
421
+ why: "Xcode's own diagnosis of a run is left on the floor",
422
+ },
423
+ "get test-results metrics": {
424
+ status: "missing",
425
+ from: "result",
426
+ why: "`test --perf-diagnostics` collects performance metrics nothing can read back",
427
+ },
428
+ "get log": {
429
+ status: "missing",
430
+ from: "result",
431
+ why: "the build log inside the bundle is reachable only as the raw transcript file",
432
+ },
433
+ "get content-availability": {
434
+ status: "missing",
435
+ from: "result",
436
+ why: "whether a bundle even has coverage or test results is found out by failing to read it",
437
+ },
438
+ "export diagnostics": {
439
+ status: "missing",
440
+ from: "result",
441
+ why: "`test --diagnostics` collects a diagnostics report that cannot be extracted",
442
+ },
443
+ "export attachments": {
444
+ status: "missing",
445
+ from: "result",
446
+ why: "UI test screenshots and attachments cannot be got out of the bundle",
447
+ },
448
+ "export metrics": {
449
+ status: "missing",
450
+ from: "result",
451
+ why: "performance measurements cannot be exported as CSV",
452
+ },
453
+ "export evaluations": {
454
+ status: "missing",
455
+ from: "result",
456
+ why: "evaluation attachments cannot be exported",
457
+ },
458
+ compare: {
459
+ status: "missing",
460
+ from: "result",
461
+ why: "two runs cannot be diffed, which is the question CI asks most",
462
+ },
463
+ merge: {
464
+ status: "missing",
465
+ from: "result",
466
+ why: "the bundles of a sharded test run cannot be combined",
467
+ },
468
+ metadata: {
469
+ status: "missing",
470
+ from: "result",
471
+ why: "a bundle's own metadata cannot be read",
472
+ },
473
+ "export coverage": {
474
+ status: "superseded",
475
+ why: "`coverage` reads the report with xccov rather than exporting an archive",
476
+ },
477
+ "get object": {
478
+ status: "n/a",
479
+ why: "deprecated by Xcode in favour of the typed subcommands",
480
+ },
481
+ "export object": {
482
+ status: "n/a",
483
+ why: "deprecated by Xcode in favour of get attachments|diagnostics|coverage",
484
+ },
485
+ graph: { status: "n/a", why: "deprecated by Xcode" },
486
+ formatDescription: { status: "n/a", why: "deprecated by Xcode" },
487
+ };
488
+ /** `xccov`, which owns coverage. */
489
+ export const XCCOV_COVERAGE = {
490
+ "view --report": only("coverage"),
491
+ "view --report --only-targets": only("coverage"),
492
+ "view --report --files-for-target": only("coverage --target"),
493
+ "view --report --file-list": only("coverage --files"),
494
+ "view --report --functions-for-file": {
495
+ status: "missing",
496
+ from: "coverage",
497
+ why: "coverage stops at the file, so the uncovered function has no name",
498
+ },
499
+ "view --archive": {
500
+ status: "missing",
501
+ from: "coverage",
502
+ why: "a standalone .xccovarchive cannot be read, only a result bundle",
503
+ },
504
+ "view --file": {
505
+ status: "missing",
506
+ from: "coverage",
507
+ why: "the per-line coverage of one file cannot be printed",
508
+ },
509
+ diff: {
510
+ status: "missing",
511
+ from: "coverage",
512
+ why: "'did coverage drop' cannot be answered from two bundles this tool wrote",
513
+ },
514
+ merge: {
515
+ status: "missing",
516
+ from: "coverage",
517
+ why: "the coverage of a sharded run cannot be combined",
518
+ },
519
+ };
520
+ /**
521
+ * `simctl`, which owns simulators.
522
+ *
523
+ * `sim` exists to put a destination into the state a build or test run needs,
524
+ * and stops there. The rest is classified so the boundary is a decision on
525
+ * the record rather than an omission.
526
+ */
527
+ export const SIMCTL_COVERAGE = {
528
+ list: only("sim list"),
529
+ boot: only("sim boot"),
530
+ shutdown: only("sim shutdown"),
531
+ erase: only("sim erase"),
532
+ install: {
533
+ status: "missing",
534
+ from: "sim",
535
+ why: "a built .app cannot be put on the simulator it was built for",
536
+ },
537
+ launch: {
538
+ status: "missing",
539
+ from: "sim",
540
+ why: "the app a build just produced cannot be run",
541
+ },
542
+ terminate: { status: "missing", why: "a running app cannot be stopped" },
543
+ uninstall: { status: "missing", why: "an installed app cannot be removed" },
544
+ listapps: {
545
+ status: "missing",
546
+ from: "sim",
547
+ why: "what is installed on a simulator cannot be listed",
548
+ },
549
+ create: { status: "missing", why: "a missing device cannot be created" },
550
+ delete: {
551
+ status: "missing",
552
+ from: "sim",
553
+ why: "stale devices cannot be reclaimed, and they cost gigabytes",
554
+ },
555
+ io: {
556
+ status: "missing",
557
+ from: "sim",
558
+ why: "a screenshot of a failing UI cannot be taken",
559
+ },
560
+ openurl: {
561
+ status: "missing",
562
+ from: "sim",
563
+ why: "a deep link cannot be opened, which is how deep links are tested",
564
+ },
565
+ privacy: {
566
+ status: "missing",
567
+ from: "sim",
568
+ why: "a permission prompt cannot be granted ahead of a UI test",
569
+ },
570
+ push: {
571
+ status: "missing",
572
+ from: "sim",
573
+ why: "a push notification cannot be simulated",
574
+ },
575
+ status_bar: {
576
+ status: "missing",
577
+ from: "sim",
578
+ why: "the status bar cannot be pinned, which screenshot tests need",
579
+ },
580
+ ui: {
581
+ status: "missing",
582
+ from: "sim",
583
+ why: "dark mode and content size cannot be set for a test run",
584
+ },
585
+ spawn: {
586
+ status: "n/a",
587
+ why: "running arbitrary processes on a device is not this tool's job",
588
+ },
589
+ diagnose: {
590
+ status: "n/a",
591
+ why: "a sysdiagnose is an Apple support artifact, not an agent loop",
592
+ },
593
+ addmedia: {
594
+ status: "n/a",
595
+ why: "photo library fixtures belong to a test target, not a build wrapper",
596
+ },
597
+ appinfo: {
598
+ status: "n/a",
599
+ why: "`listapps` answers the same question for an agent",
600
+ },
601
+ get_app_container: {
602
+ status: "n/a",
603
+ why: "a container path is a debugging detail, not a build output",
604
+ },
605
+ getenv: {
606
+ status: "n/a",
607
+ why: "device environment variables are a simctl debugging tool",
608
+ },
609
+ clone: {
610
+ status: "n/a",
611
+ why: "device farming is outside a build and test loop",
612
+ },
613
+ rename: { status: "n/a", why: "device naming is a Simulator.app concern" },
614
+ upgrade: {
615
+ status: "n/a",
616
+ why: "runtime migration is a Simulator.app concern",
617
+ },
618
+ reboot: { status: "n/a", why: "shutdown and boot cover what a run needs" },
619
+ runtime: {
620
+ status: "n/a",
621
+ why: "`platforms` downloads runtimes through xcodebuild",
622
+ },
623
+ location: {
624
+ status: "n/a",
625
+ why: "simulated location belongs to a test target",
626
+ },
627
+ keychain: {
628
+ status: "n/a",
629
+ why: "seeding a keychain belongs to a test target",
630
+ },
631
+ logverbose: { status: "n/a", why: "a simctl debugging switch" },
632
+ icloud_sync: {
633
+ status: "n/a",
634
+ why: "an iCloud testing concern, not a build one",
635
+ },
636
+ install_app_data: {
637
+ status: "n/a",
638
+ why: "app data fixtures belong to a test target",
639
+ },
640
+ personalization: {
641
+ status: "n/a",
642
+ why: "personalization manifests are a device provisioning concern",
643
+ },
644
+ pair: { status: "n/a", why: "watch pairing is a Simulator.app concern" },
645
+ pair_activate: {
646
+ status: "n/a",
647
+ why: "watch pairing is a Simulator.app concern",
648
+ },
649
+ unpair: { status: "n/a", why: "watch pairing is a Simulator.app concern" },
650
+ pbcopy: {
651
+ status: "n/a",
652
+ why: "pasteboard plumbing belongs to a test target",
653
+ },
654
+ pbpaste: {
655
+ status: "n/a",
656
+ why: "pasteboard plumbing belongs to a test target",
657
+ },
658
+ pbsync: {
659
+ status: "n/a",
660
+ why: "pasteboard plumbing belongs to a test target",
661
+ },
662
+ help: { status: "superseded", why: "`xcodebuild-axi sim --help`" },
663
+ };
664
+ /** Every non-xcodebuild surface this tool wraps, for one combined number. */
665
+ export const COMPANION_SURFACES = {
666
+ xcresulttool: XCRESULT_COVERAGE,
667
+ xccov: XCCOV_COVERAGE,
668
+ simctl: SIMCTL_COVERAGE,
669
+ };
330
670
  export function tally(map) {
331
671
  const values = Object.values(map);
332
672
  const count = (status) => values.filter((entry) => entry.status === status).length;
333
673
  const exposed = count("exposed");
334
674
  const always = count("always");
335
675
  const superseded = count("superseded");
676
+ const missing = count("missing");
336
677
  const na = count("n/a");
337
678
  const covered = exposed + always + superseded;
338
679
  return {
@@ -340,11 +681,73 @@ export function tally(map) {
340
681
  exposed,
341
682
  always,
342
683
  superseded,
684
+ missing,
343
685
  na,
344
686
  covered,
345
687
  // `n/a` options stay in the denominator on purpose. Declining to wrap
346
688
  // something is a choice this number should have to pay for.
347
- percent: Math.round((covered / values.length) * 1000) / 10,
689
+ percent: percent(covered, values.length),
348
690
  };
349
691
  }
692
+ export function percent(part, whole) {
693
+ return whole === 0 ? 100 : Math.round((part / whole) * 1000) / 10;
694
+ }
695
+ /** Every command an option applies to, whether or not it reaches it. */
696
+ export function applicable(entry) {
697
+ if (entry.status !== "exposed")
698
+ return [];
699
+ const commands = new Set(entry.surface ? SURFACES[entry.surface] : []);
700
+ for (const exposure of entry.on)
701
+ commands.add(exposure.command);
702
+ for (const item of entry.declined ?? [])
703
+ commands.add(item.command);
704
+ for (const item of entry.missing ?? [])
705
+ commands.add(item.command);
706
+ return [...commands];
707
+ }
708
+ /**
709
+ * Coverage counted per command rather than per option.
710
+ *
711
+ * The headline number asks whether an option is reachable at all; this one
712
+ * asks whether it is reachable from where you are standing, which is the
713
+ * question an agent that just got `unknown flag --target` was asking.
714
+ */
715
+ export function reach(map) {
716
+ let reached = 0;
717
+ let declined = 0;
718
+ let missing = 0;
719
+ for (const entry of Object.values(map)) {
720
+ if (entry.status !== "exposed")
721
+ continue;
722
+ reached += new Set(entry.on.map((exposure) => exposure.command)).size;
723
+ declined += entry.declined?.length ?? 0;
724
+ missing += entry.missing?.length ?? 0;
725
+ }
726
+ const pairs = reached + declined + missing;
727
+ return {
728
+ pairs,
729
+ reached,
730
+ declined,
731
+ missing,
732
+ percent: percent(reached, pairs),
733
+ };
734
+ }
735
+ /** Every gap in a map, as `option` -> the commands that should reach it. */
736
+ export function gaps(map) {
737
+ const out = [];
738
+ for (const [option, entry] of Object.entries(map)) {
739
+ if (entry.status !== "exposed")
740
+ continue;
741
+ for (const item of entry.missing ?? []) {
742
+ out.push({ option, command: item.command, why: item.why });
743
+ }
744
+ }
745
+ return out;
746
+ }
747
+ /** The README's one-line answer to "how do I reach this?". */
748
+ export function describe(entry) {
749
+ return entry.status === "exposed"
750
+ ? entry.on.map((exposure) => exposure.via).join(", ")
751
+ : entry.why;
752
+ }
350
753
  //# sourceMappingURL=surface.js.map