@better-update/cli 0.3.0 → 0.3.2

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 (152) hide show
  1. package/dist/index.js +5319 -0
  2. package/dist/index.js.map +1 -0
  3. package/package.json +12 -9
  4. package/CHANGELOG.md +0 -58
  5. package/oxlint.config.ts +0 -6
  6. package/src/app-layer.ts +0 -29
  7. package/src/application/build-workflow.ts +0 -222
  8. package/src/application/command-exit.ts +0 -13
  9. package/src/application/login.ts +0 -87
  10. package/src/application/update-promote.ts +0 -88
  11. package/src/application/update-publish.ts +0 -402
  12. package/src/application/update-rollback.ts +0 -275
  13. package/src/commands/analytics/adoption.ts +0 -40
  14. package/src/commands/analytics/channels.ts +0 -35
  15. package/src/commands/analytics/helpers.ts +0 -3
  16. package/src/commands/analytics/index.ts +0 -13
  17. package/src/commands/analytics/platforms.ts +0 -39
  18. package/src/commands/analytics/updates.ts +0 -35
  19. package/src/commands/audit-logs/helpers.ts +0 -3
  20. package/src/commands/audit-logs/index.ts +0 -8
  21. package/src/commands/audit-logs/list.ts +0 -66
  22. package/src/commands/branches.ts +0 -70
  23. package/src/commands/build/android.ts +0 -129
  24. package/src/commands/build/index.ts +0 -63
  25. package/src/commands/build/ios.ts +0 -199
  26. package/src/commands/build/reserve-and-upload.test.ts +0 -263
  27. package/src/commands/build/reserve-and-upload.ts +0 -160
  28. package/src/commands/build/run-step.ts +0 -131
  29. package/src/commands/builds/compatibility-matrix.ts +0 -48
  30. package/src/commands/builds/delete.ts +0 -15
  31. package/src/commands/builds/get.ts +0 -34
  32. package/src/commands/builds/helpers.ts +0 -3
  33. package/src/commands/builds/index.ts +0 -20
  34. package/src/commands/builds/install-link.ts +0 -20
  35. package/src/commands/builds/list.ts +0 -38
  36. package/src/commands/channels/create.ts +0 -37
  37. package/src/commands/channels/delete.ts +0 -15
  38. package/src/commands/channels/helpers.ts +0 -18
  39. package/src/commands/channels/index.ts +0 -24
  40. package/src/commands/channels/list.ts +0 -38
  41. package/src/commands/channels/pause.ts +0 -15
  42. package/src/commands/channels/resume.ts +0 -15
  43. package/src/commands/channels/rollout/complete.ts +0 -17
  44. package/src/commands/channels/rollout/create.ts +0 -36
  45. package/src/commands/channels/rollout/index.ts +0 -11
  46. package/src/commands/channels/rollout/revert.ts +0 -17
  47. package/src/commands/channels/rollout/update.ts +0 -23
  48. package/src/commands/channels/update.ts +0 -32
  49. package/src/commands/credentials/delete.ts +0 -24
  50. package/src/commands/credentials/index.ts +0 -10
  51. package/src/commands/credentials/list.ts +0 -33
  52. package/src/commands/credentials/upload.ts +0 -91
  53. package/src/commands/env/delete.ts +0 -35
  54. package/src/commands/env/export.ts +0 -27
  55. package/src/commands/env/get.ts +0 -25
  56. package/src/commands/env/helpers.ts +0 -13
  57. package/src/commands/env/import.ts +0 -31
  58. package/src/commands/env/index.ts +0 -24
  59. package/src/commands/env/list.ts +0 -44
  60. package/src/commands/env/pull.ts +0 -27
  61. package/src/commands/env/set.ts +0 -42
  62. package/src/commands/fingerprint/compare.ts +0 -25
  63. package/src/commands/fingerprint/generate.ts +0 -18
  64. package/src/commands/fingerprint/index.ts +0 -9
  65. package/src/commands/init.ts +0 -35
  66. package/src/commands/login.ts +0 -13
  67. package/src/commands/logout.ts +0 -12
  68. package/src/commands/projects.ts +0 -84
  69. package/src/commands/status.ts +0 -48
  70. package/src/commands/update/delete.ts +0 -15
  71. package/src/commands/update/helpers.ts +0 -22
  72. package/src/commands/update/index.ts +0 -22
  73. package/src/commands/update/list.ts +0 -60
  74. package/src/commands/update/promote.ts +0 -30
  75. package/src/commands/update/publish.ts +0 -94
  76. package/src/commands/update/rollback.ts +0 -42
  77. package/src/commands/update/rollout/complete.ts +0 -17
  78. package/src/commands/update/rollout/index.ts +0 -10
  79. package/src/commands/update/rollout/revert.ts +0 -17
  80. package/src/commands/update/rollout/set.ts +0 -23
  81. package/src/index.ts +0 -53
  82. package/src/lib/android-keystore.test.ts +0 -114
  83. package/src/lib/android-keystore.ts +0 -76
  84. package/src/lib/android-signing-gradle.test.ts +0 -95
  85. package/src/lib/android-signing-gradle.ts +0 -52
  86. package/src/lib/app-json.ts +0 -81
  87. package/src/lib/apple-auth.test.ts +0 -402
  88. package/src/lib/apple-auth.ts +0 -132
  89. package/src/lib/artifact-finder.test.ts +0 -195
  90. package/src/lib/artifact-finder.ts +0 -122
  91. package/src/lib/browser-login.test.ts +0 -88
  92. package/src/lib/browser-login.ts +0 -193
  93. package/src/lib/build-profile.test.ts +0 -290
  94. package/src/lib/build-profile.ts +0 -234
  95. package/src/lib/cli-schemas.ts +0 -39
  96. package/src/lib/command-errors.ts +0 -60
  97. package/src/lib/credentials-downloader.ts +0 -181
  98. package/src/lib/credentials-manager.ts +0 -354
  99. package/src/lib/env-exporter.test.ts +0 -96
  100. package/src/lib/env-exporter.ts +0 -28
  101. package/src/lib/exit-codes.ts +0 -82
  102. package/src/lib/expo-config.ts +0 -130
  103. package/src/lib/expo-export.test.ts +0 -94
  104. package/src/lib/expo-export.ts +0 -281
  105. package/src/lib/fingerprint.ts +0 -67
  106. package/src/lib/format-error.ts +0 -22
  107. package/src/lib/git-context.ts +0 -56
  108. package/src/lib/gradle-config.ts +0 -126
  109. package/src/lib/ios-export-options.test.ts +0 -98
  110. package/src/lib/ios-export-options.ts +0 -62
  111. package/src/lib/ios-keychain.ts +0 -181
  112. package/src/lib/ios-provisioning.test.ts +0 -115
  113. package/src/lib/ios-provisioning.ts +0 -179
  114. package/src/lib/output.ts +0 -32
  115. package/src/lib/pkcs12.ts +0 -73
  116. package/src/lib/plist.ts +0 -39
  117. package/src/lib/post-build-validation.ts +0 -146
  118. package/src/lib/presigned-upload.test.ts +0 -140
  119. package/src/lib/presigned-upload.ts +0 -35
  120. package/src/lib/record.ts +0 -5
  121. package/src/lib/resolve-named-resource.ts +0 -24
  122. package/src/lib/runtime-version.test.ts +0 -119
  123. package/src/lib/runtime-version.ts +0 -62
  124. package/src/lib/sha256.test.ts +0 -108
  125. package/src/lib/sha256.ts +0 -80
  126. package/src/lib/signed-payloads.test.ts +0 -181
  127. package/src/lib/signed-payloads.ts +0 -164
  128. package/src/lib/string-utils.ts +0 -4
  129. package/src/lib/temp-dir.ts +0 -14
  130. package/src/lib/test-utils.ts +0 -13
  131. package/src/lib/update-platforms.test.ts +0 -45
  132. package/src/lib/update-platforms.ts +0 -19
  133. package/src/lib/xcpretty-formatter.ts +0 -21
  134. package/src/services/api-client.ts +0 -42
  135. package/src/services/apple-session-store.ts +0 -100
  136. package/src/services/auth-store.ts +0 -85
  137. package/src/services/cli-runtime.ts +0 -46
  138. package/src/services/config-store.ts +0 -108
  139. package/src/services/presigned-upload.ts +0 -84
  140. package/src/services/update-asset-uploader.ts +0 -72
  141. package/src/types/keychain.d.ts +0 -22
  142. package/tests/e2e/build.test.ts +0 -270
  143. package/tests/e2e/commands.test.ts +0 -694
  144. package/tests/e2e/ota-lifecycle.test.ts +0 -275
  145. package/tests/e2e/publish.test.ts +0 -150
  146. package/tests/helpers/cli-e2e.ts +0 -426
  147. package/tests/helpers/pty-driver.ts +0 -142
  148. package/tests/interactive/harness/provider-prompt.ts +0 -54
  149. package/tests/interactive/login.test.ts +0 -47
  150. package/tests/interactive/provider-select.test.ts +0 -59
  151. package/tsconfig.json +0 -7
  152. package/vitest.config.ts +0 -38
@@ -1,270 +0,0 @@
1
- import { execFileSync } from "node:child_process";
2
- import { createHash } from "node:crypto";
3
- import { mkdtempSync, readFileSync, rmSync } from "node:fs";
4
- import os from "node:os";
5
- import path from "node:path";
6
-
7
- import { setupCliE2E } from "../helpers/cli-e2e";
8
-
9
- // Gated on ANDROID_HOME because gradlew requires a functioning Android SDK
10
- // Toolchain; the build-credentials resolve endpoint is covered in the server
11
- // E2E suite independently.
12
- const hasAndroidSdk = Boolean(process.env["ANDROID_HOME"]);
13
-
14
- const FIXTURE_DIR = path.resolve(import.meta.dirname, "../../../../fixtures/build-e2e-app");
15
-
16
- const buildAppJsonTemplate = {
17
- expo: {
18
- name: "E2E Build App",
19
- slug: "e2e-build-app",
20
- owner: "e2e-build",
21
- version: "1.0.0",
22
- runtimeVersion: "1.0.0",
23
- ios: {
24
- bundleIdentifier: "com.example.e2ebuild",
25
- buildNumber: "1",
26
- },
27
- android: {
28
- package: "com.example.e2ebuild",
29
- versionCode: 1,
30
- },
31
- extra: {
32
- betterUpdate: {
33
- profiles: {
34
- production: {
35
- environment: "production",
36
- ios: { distribution: "ad-hoc" },
37
- android: { distribution: "direct", format: "apk" },
38
- },
39
- },
40
- },
41
- },
42
- },
43
- };
44
-
45
- const KEYSTORE_PASSWORD = "e2epass123";
46
- const KEY_ALIAS = "e2e-key";
47
- const KEY_PASSWORD = "e2epass123";
48
-
49
- const cli = setupCliE2E(".wrangler/state/e2e-cli-build", {
50
- projectDir: FIXTURE_DIR,
51
- appJsonTemplate: buildAppJsonTemplate,
52
- });
53
-
54
- const buildState = {
55
- buildId: "",
56
- expectedByteSize: 0,
57
- expectedSha256: "",
58
- };
59
-
60
- describe.skipIf(!hasAndroidSdk)("CLI build journey — Android", () => {
61
- beforeAll(async () => {
62
- if (!hasAndroidSdk) {
63
- return;
64
- }
65
- // Generate a self-signed Android keystore for signing the build.
66
- const tmpDir = mkdtempSync(path.join(os.tmpdir(), "build-e2e-keystore-"));
67
- const keystorePath = path.join(tmpDir, "e2e.keystore");
68
-
69
- execFileSync(
70
- "keytool",
71
- [
72
- "-genkeypair",
73
- "-v",
74
- "-keystore",
75
- keystorePath,
76
- "-alias",
77
- KEY_ALIAS,
78
- "-keyalg",
79
- "RSA",
80
- "-keysize",
81
- "2048",
82
- "-validity",
83
- "365",
84
- "-storepass",
85
- KEYSTORE_PASSWORD,
86
- "-keypass",
87
- KEY_PASSWORD,
88
- "-dname",
89
- "CN=E2E Test, O=Better Update",
90
- ],
91
- { stdio: "pipe" },
92
- );
93
-
94
- const keystoreBlob = readFileSync(keystorePath).toString("base64");
95
- rmSync(tmpDir, { recursive: true, force: true });
96
-
97
- // Seed keystore credential on the test server via new typed endpoint.
98
- const createResponse = await cli.postAuthorized("/api/android/upload-keystores", {
99
- keystoreBase64: keystoreBlob,
100
- keyAlias: KEY_ALIAS,
101
- keystorePassword: KEYSTORE_PASSWORD,
102
- keyPassword: KEY_PASSWORD,
103
- });
104
- expect(createResponse.status).toBe(201);
105
- const createBody = await createResponse.json();
106
- const keystoreId = createBody.id as string;
107
-
108
- // Register Android application identifier + default build credentials group so the
109
- // Resolve endpoint can find a keystore binding for package name.
110
- const appResponse = await cli.postAuthorized(
111
- `/api/projects/${cli.getProjectId()}/android-application-identifiers`,
112
- { packageName: "com.example.e2ebuild" },
113
- );
114
- expect(appResponse.status).toBe(201);
115
- const appBody = await appResponse.json();
116
- const appId = appBody.id as string;
117
-
118
- const groupResponse = await cli.postAuthorized(
119
- `/api/android-application-identifiers/${appId}/build-credentials`,
120
- {
121
- name: "Default",
122
- isDefault: true,
123
- androidUploadKeystoreId: keystoreId,
124
- },
125
- );
126
- expect(groupResponse.status).toBe(201);
127
- });
128
-
129
- test("links the fixture app to the seeded project", () => {
130
- const result = cli.runCli("init");
131
- expect(result.exitCode).toBe(0);
132
- expect(result.stderr).toBe("");
133
- expect(result.stdout).toContain("Linking project: E2E Build App (e2e-build-app)");
134
- expect(result.stdout).toContain("Found existing project: E2E Build App Project");
135
- expect(result.stdout).toContain("Project linked successfully");
136
-
137
- const appJson = cli.readAppJson();
138
- const expo = appJson["expo"] as Record<string, unknown>;
139
- const extra = expo["extra"] as Record<string, unknown>;
140
- const betterUpdate = extra["betterUpdate"] as Record<string, unknown>;
141
- expect(betterUpdate["projectId"]).toBe(cli.getProjectId());
142
- });
143
-
144
- test("builds an Android APK and uploads it", () => {
145
- const result = cli.runCli("build", "--platform", "android", "--message", "E2E Android build");
146
- expect(result.exitCode).toBe(0);
147
-
148
- // Build workflow prints artifact path before upload.
149
- expect(result.stdout).toContain("Artifact produced:");
150
-
151
- // After upload, key-value summary is printed.
152
- const buildIdMatch = /^Build ID\s+(.+)$/m.exec(result.stdout);
153
- expect(buildIdMatch).toBeDefined();
154
- buildState.buildId = buildIdMatch![1]!.trim();
155
-
156
- expect(result.stdout).toMatch(/^Status\s+uploaded$/m);
157
- expect(result.stdout).toMatch(/^Platform\s+android$/m);
158
- expect(result.stdout).toMatch(/^Profile\s+production$/m);
159
- expect(result.stdout).toMatch(/^Runtime version\s+1\.0\.0$/m);
160
- expect(result.stdout).toMatch(/^SHA-256\s+[a-f0-9]{64}$/m);
161
- expect(result.stdout).toMatch(/^Bytes\s+\d+$/m);
162
- }, 600_000);
163
-
164
- test("lists the uploaded build via CLI", () => {
165
- expect(buildState.buildId).not.toBe("");
166
-
167
- const result = cli.runCli("builds", "list");
168
- expect(result.exitCode).toBe(0);
169
- expect(result.stderr).toBe("");
170
- expect(result.stdout).toContain(buildState.buildId);
171
- expect(result.stdout).toContain("android");
172
- expect(result.stdout).toContain("production");
173
- expect(result.stdout).toContain("direct");
174
- });
175
-
176
- test("gets build details via CLI", () => {
177
- const result = cli.runCli("builds", "get", buildState.buildId);
178
- expect(result.exitCode).toBe(0);
179
- expect(result.stderr).toBe("");
180
- expect(result.stdout).toMatch(/^Platform\s+android$/m);
181
- expect(result.stdout).toMatch(/^Profile\s+production$/m);
182
- expect(result.stdout).toMatch(/^Distribution\s+direct$/m);
183
- expect(result.stdout).toMatch(/^Runtime Version\s+1\.0\.0$/m);
184
- expect(result.stdout).toMatch(/^Bundle ID\s+com\.example\.e2ebuild$/m);
185
- expect(result.stdout).toMatch(/^Message\s+E2E Android build$/m);
186
- expect(result.stdout).toContain("apk");
187
- });
188
-
189
- test("verifies build metadata in API", async () => {
190
- const response = await cli.getAuthorized(`/api/builds/${buildState.buildId}`);
191
- expect(response.status).toBe(200);
192
-
193
- const build = (await response.json()) as {
194
- id: string;
195
- platform: string;
196
- profile: string;
197
- distribution: string;
198
- runtimeVersion: string;
199
- bundleId: string;
200
- message: string;
201
- artifact: { format: string; byteSize: number; sha256: string } | null;
202
- };
203
-
204
- expect(build.id).toBe(buildState.buildId);
205
- expect(build.platform).toBe("android");
206
- expect(build.profile).toBe("production");
207
- expect(build.distribution).toBe("direct");
208
- expect(build.runtimeVersion).toBe("1.0.0");
209
- expect(build.bundleId).toBe("com.example.e2ebuild");
210
- expect(build.message).toBe("E2E Android build");
211
- expect(build.artifact).not.toBeNull();
212
- expect(build.artifact!.format).toBe("apk");
213
- expect(build.artifact!.byteSize).toBeGreaterThan(0);
214
- expect(build.artifact!.sha256).toMatch(/^[a-f0-9]{64}$/);
215
-
216
- buildState.expectedByteSize = build.artifact!.byteSize;
217
- buildState.expectedSha256 = build.artifact!.sha256;
218
- });
219
-
220
- test("gets install link for the build", () => {
221
- const result = cli.runCli("builds", "install-link", buildState.buildId);
222
- expect(result.exitCode).toBe(0);
223
- expect(result.stderr).toBe("");
224
- expect(result.stdout).toMatch(/^Artifact URL\s+http.+$/m);
225
- // Android direct distribution has no itms-services install URL.
226
- expect(result.stdout).toMatch(/^Install URL\s+-$/m);
227
- });
228
-
229
- test("downloads the uploaded artifact and verifies its integrity", async () => {
230
- expect(buildState.expectedByteSize).toBeGreaterThan(0);
231
- expect(buildState.expectedSha256).not.toBe("");
232
-
233
- // Get a fresh artifact URL via the API.
234
- const linkResponse = await cli.getAuthorized(`/api/builds/${buildState.buildId}/install-link`);
235
- expect(linkResponse.status).toBe(200);
236
- const { artifactUrl } = (await linkResponse.json()) as { artifactUrl: string };
237
-
238
- // Download the full artifact binary.
239
- const downloadResponse = await fetch(artifactUrl);
240
- expect(downloadResponse.status).toBe(200);
241
-
242
- const body = Buffer.from(await downloadResponse.arrayBuffer());
243
- expect(body.byteLength).toBe(buildState.expectedByteSize);
244
-
245
- const actualSha256 = createHash("sha256").update(body).digest("hex");
246
- expect(actualSha256).toBe(buildState.expectedSha256);
247
- });
248
-
249
- test("deletes the build and confirms removal", () => {
250
- const deleteResult = cli.runCli("builds", "delete", buildState.buildId);
251
- expect(deleteResult.exitCode).toBe(0);
252
- expect(deleteResult.stderr).toBe("");
253
- expect(deleteResult.stdout).toContain(`Build ${buildState.buildId} deleted.`);
254
-
255
- const listResult = cli.runCli("builds", "list");
256
- expect(listResult.exitCode).toBe(0);
257
- expect(listResult.stdout).not.toContain(buildState.buildId);
258
- });
259
-
260
- test("builds with --no-upload and skips the upload step", () => {
261
- const result = cli.runCli("build", "--platform", "android", "--no-upload");
262
- expect(result.exitCode).toBe(0);
263
-
264
- expect(result.stdout).toContain("Artifact produced:");
265
- expect(result.stdout).toMatch(/^Artifact\s+.+\.apk$/m);
266
- expect(result.stdout).toMatch(/^SHA-256\s+[a-f0-9]{64}$/m);
267
- expect(result.stdout).toMatch(/^Bytes\s+\d+$/m);
268
- expect(result.stdout).toMatch(/^Upload\s+skipped \(--no-upload\)$/m);
269
- }, 600_000);
270
- });