@better-update/cli 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/dist/index.mjs +5190 -0
  2. package/dist/index.mjs.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,40 +0,0 @@
1
- import { Command, Options } from "@effect/cli";
2
- import { Console, Effect, Option } from "effect";
3
-
4
- import { readProjectId } from "../../lib/app-json";
5
- import { printTable } from "../../lib/output";
6
- import { apiClient } from "../../services/api-client";
7
- import { handleAnalyticsCommandErrors } from "./helpers";
8
-
9
- const period = Options.choice("period", ["1d", "7d", "30d", "90d"]).pipe(Options.optional);
10
-
11
- export const adoptionCommand = Command.make("adoption", { period }, (opts) =>
12
- Effect.gen(function* () {
13
- const projectId = yield* readProjectId;
14
- const api = yield* apiClient;
15
-
16
- const periodFilter = Option.match(opts.period, {
17
- onNone: () => ({}) as Record<string, string>,
18
- onSome: (periodValue) => ({ period: periodValue }) as Record<string, string>,
19
- });
20
-
21
- const result = yield* api.analytics.adoption({
22
- urlParams: { projectId, ...periodFilter },
23
- });
24
-
25
- if (result.updates.length === 0) {
26
- yield* Console.log("No adoption data found.");
27
- return;
28
- }
29
-
30
- yield* printTable(
31
- ["Update ID", "Devices", "First Seen", "Last Seen"],
32
- result.updates.map((update) => [
33
- update.updateId,
34
- String(update.devices),
35
- update.firstSeen,
36
- update.lastSeen,
37
- ]),
38
- );
39
- }).pipe(handleAnalyticsCommandErrors),
40
- );
@@ -1,35 +0,0 @@
1
- import { Command, Options } from "@effect/cli";
2
- import { Effect, Option } from "effect";
3
-
4
- import { readProjectId } from "../../lib/app-json";
5
- import { printKeyValue } from "../../lib/output";
6
- import { apiClient } from "../../services/api-client";
7
- import { handleAnalyticsCommandErrors } from "./helpers";
8
-
9
- const channel = Options.text("channel");
10
- const period = Options.choice("period", ["1d", "7d", "30d", "90d"]).pipe(Options.optional);
11
-
12
- export const channelsCommand = Command.make("channels", { channel, period }, (opts) =>
13
- Effect.gen(function* () {
14
- const projectId = yield* readProjectId;
15
- const api = yield* apiClient;
16
-
17
- const periodFilter = Option.match(opts.period, {
18
- onNone: () => ({}) as Record<string, string>,
19
- onSome: (periodValue) => ({ period: periodValue }) as Record<string, string>,
20
- });
21
-
22
- const result = yield* api.analytics.channels({
23
- urlParams: { projectId, channel: opts.channel, ...periodFilter },
24
- });
25
-
26
- yield* printKeyValue([
27
- ["Channel", result.channel],
28
- ["Total Requests", String(result.totalRequests)],
29
- ["Unique Devices", String(result.uniqueDevices)],
30
- ["Manifest", String(result.responseTypeDistribution.manifest)],
31
- ["Directive", String(result.responseTypeDistribution.directive)],
32
- ["No Update", String(result.responseTypeDistribution.no_update)],
33
- ]);
34
- }).pipe(handleAnalyticsCommandErrors),
35
- );
@@ -1,3 +0,0 @@
1
- import { makeCommandErrorHandler } from "../../lib/command-errors";
2
-
3
- export const handleAnalyticsCommandErrors = makeCommandErrorHandler();
@@ -1,13 +0,0 @@
1
- import { Command } from "@effect/cli";
2
- import { Console } from "effect";
3
-
4
- import { adoptionCommand } from "./adoption";
5
- import { channelsCommand } from "./channels";
6
- import { platformsCommand } from "./platforms";
7
- import { updatesCommand } from "./updates";
8
-
9
- export const analyticsCommand = Command.make("analytics", {}, () =>
10
- Console.log("View deployment analytics. Run with --help for subcommands."),
11
- ).pipe(
12
- Command.withSubcommands([adoptionCommand, updatesCommand, channelsCommand, platformsCommand]),
13
- );
@@ -1,39 +0,0 @@
1
- import { Command, Options } from "@effect/cli";
2
- import { Console, Effect, Option } from "effect";
3
-
4
- import { readProjectId } from "../../lib/app-json";
5
- import { printTable } from "../../lib/output";
6
- import { apiClient } from "../../services/api-client";
7
- import { handleAnalyticsCommandErrors } from "./helpers";
8
-
9
- const period = Options.choice("period", ["1d", "7d", "30d", "90d"]).pipe(Options.optional);
10
-
11
- export const platformsCommand = Command.make("platforms", { period }, (opts) =>
12
- Effect.gen(function* () {
13
- const projectId = yield* readProjectId;
14
- const api = yield* apiClient;
15
-
16
- const periodFilter = Option.match(opts.period, {
17
- onNone: () => ({}) as Record<string, string>,
18
- onSome: (periodValue) => ({ period: periodValue }) as Record<string, string>,
19
- });
20
-
21
- const result = yield* api.analytics.platforms({
22
- urlParams: { projectId, ...periodFilter },
23
- });
24
-
25
- if (result.platforms.length === 0) {
26
- yield* Console.log("No platform data found.");
27
- return;
28
- }
29
-
30
- yield* printTable(
31
- ["Platform", "Requests", "Devices"],
32
- result.platforms.map((platform) => [
33
- platform.platform,
34
- String(platform.requests),
35
- String(platform.devices),
36
- ]),
37
- );
38
- }).pipe(handleAnalyticsCommandErrors),
39
- );
@@ -1,35 +0,0 @@
1
- import { Command, Options } from "@effect/cli";
2
- import { Effect, Option } from "effect";
3
-
4
- import { readProjectId } from "../../lib/app-json";
5
- import { printKeyValue } from "../../lib/output";
6
- import { apiClient } from "../../services/api-client";
7
- import { handleAnalyticsCommandErrors } from "./helpers";
8
-
9
- const updateId = Options.text("update-id");
10
- const period = Options.choice("period", ["1d", "7d", "30d", "90d"]).pipe(Options.optional);
11
-
12
- export const updatesCommand = Command.make("updates", { updateId, period }, (opts) =>
13
- Effect.gen(function* () {
14
- const projectId = yield* readProjectId;
15
- const api = yield* apiClient;
16
-
17
- const periodFilter = Option.match(opts.period, {
18
- onNone: () => ({}) as Record<string, string>,
19
- onSome: (periodValue) => ({ period: periodValue }) as Record<string, string>,
20
- });
21
-
22
- const result = yield* api.analytics.updates({
23
- urlParams: { projectId, updateId: opts.updateId, ...periodFilter },
24
- });
25
-
26
- yield* printKeyValue([
27
- ["Update ID", result.updateId],
28
- ["Total Requests", String(result.totalRequests)],
29
- ["Unique Devices", String(result.uniqueDevices)],
30
- ["Manifest", String(result.byResponseType.manifest)],
31
- ["Directive", String(result.byResponseType.directive)],
32
- ["No Update", String(result.byResponseType.no_update)],
33
- ]);
34
- }).pipe(handleAnalyticsCommandErrors),
35
- );
@@ -1,3 +0,0 @@
1
- import { makeCommandErrorHandler } from "../../lib/command-errors";
2
-
3
- export const handleAuditLogCommandErrors = makeCommandErrorHandler();
@@ -1,8 +0,0 @@
1
- import { Command } from "@effect/cli";
2
- import { Console } from "effect";
3
-
4
- import { listCommand } from "./list";
5
-
6
- export const auditLogsCommand = Command.make("audit-logs", {}, () =>
7
- Console.log("View audit logs. Run with --help for subcommands."),
8
- ).pipe(Command.withSubcommands([listCommand]));
@@ -1,66 +0,0 @@
1
- import { Command, Options } from "@effect/cli";
2
- import { Console, Effect, Option } from "effect";
3
-
4
- import { printTable } from "../../lib/output";
5
- import { apiClient } from "../../services/api-client";
6
- import { handleAuditLogCommandErrors } from "./helpers";
7
-
8
- const action = Options.text("action").pipe(Options.optional);
9
- const resourceType = Options.text("resource-type").pipe(Options.optional);
10
- const actorId = Options.text("actor-id").pipe(Options.optional);
11
- const from = Options.text("from").pipe(Options.optional);
12
- const to = Options.text("to").pipe(Options.optional);
13
-
14
- export const listCommand = Command.make(
15
- "list",
16
- { action, resourceType, actorId, from, to },
17
- (opts) =>
18
- Effect.gen(function* () {
19
- const api = yield* apiClient;
20
-
21
- const filters = {
22
- ...Option.match(opts.action, {
23
- onNone: () => ({}),
24
- onSome: (value) => ({ action: value }),
25
- }),
26
- ...Option.match(opts.resourceType, {
27
- onNone: () => ({}),
28
- onSome: (value) => ({ resourceType: value }),
29
- }),
30
- ...Option.match(opts.actorId, {
31
- onNone: () => ({}),
32
- onSome: (value) => ({ actorId: value }),
33
- }),
34
- ...Option.match(opts.from, {
35
- onNone: () => ({}),
36
- onSome: (value) => ({ from: value }),
37
- }),
38
- ...Option.match(opts.to, {
39
- onNone: () => ({}),
40
- onSome: (value) => ({ to: value }),
41
- }),
42
- } as Record<string, string>;
43
-
44
- const { items } = yield* api["audit-logs"].list({
45
- urlParams: { ...filters, page: 1, limit: 100 },
46
- });
47
-
48
- if (items.length === 0) {
49
- yield* Console.log("No audit log entries found.");
50
- return;
51
- }
52
-
53
- yield* printTable(
54
- ["ID", "Action", "Resource Type", "Resource ID", "Actor", "Source", "Created"],
55
- items.map((log) => [
56
- log.id,
57
- log.action,
58
- log.resourceType,
59
- log.resourceId ?? "-",
60
- log.actorEmail,
61
- log.source,
62
- log.createdAt,
63
- ]),
64
- );
65
- }).pipe(handleAuditLogCommandErrors),
66
- );
@@ -1,70 +0,0 @@
1
- import { Args, Command, Options } from "@effect/cli";
2
- import { Console, Effect } from "effect";
3
-
4
- import { readProjectId } from "../lib/app-json";
5
- import { makeCommandErrorHandler } from "../lib/command-errors";
6
- import { printKeyValue, printTable } from "../lib/output";
7
- import { apiClient } from "../services/api-client";
8
-
9
- const handleErrors = makeCommandErrorHandler();
10
-
11
- const idArg = Args.text({ name: "id" });
12
- const nameOption = Options.text("name");
13
-
14
- const listCommand = Command.make("list", {}, () =>
15
- Effect.gen(function* () {
16
- const projectId = yield* readProjectId;
17
- const api = yield* apiClient;
18
- const { items } = yield* api.branches.list({
19
- urlParams: { projectId, page: 1, limit: 1000 },
20
- });
21
-
22
- if (items.length === 0) {
23
- yield* Console.log("No branches found.");
24
- return;
25
- }
26
-
27
- yield* printTable(
28
- ["ID", "Name", "Created"],
29
- items.map((branch) => [branch.id, branch.name, branch.createdAt]),
30
- );
31
- }).pipe(handleErrors),
32
- );
33
-
34
- const createCommand = Command.make("create", { name: nameOption }, (opts) =>
35
- Effect.gen(function* () {
36
- const projectId = yield* readProjectId;
37
- const api = yield* apiClient;
38
- const branch = yield* api.branches.create({
39
- payload: { projectId, name: opts.name },
40
- });
41
- yield* printKeyValue([
42
- ["ID", branch.id],
43
- ["Name", branch.name],
44
- ["Created", branch.createdAt],
45
- ]);
46
- }).pipe(handleErrors),
47
- );
48
-
49
- const renameCommand = Command.make("rename", { id: idArg, name: nameOption }, (opts) =>
50
- Effect.gen(function* () {
51
- const api = yield* apiClient;
52
- const branch = yield* api.branches.rename({
53
- path: { id: opts.id },
54
- payload: { name: opts.name },
55
- });
56
- yield* Console.log(`Branch renamed to "${branch.name}".`);
57
- }).pipe(handleErrors),
58
- );
59
-
60
- const deleteCommand = Command.make("delete", { id: idArg }, (opts) =>
61
- Effect.gen(function* () {
62
- const api = yield* apiClient;
63
- yield* api.branches.delete({ path: { id: opts.id } });
64
- yield* Console.log(`Branch ${opts.id} deleted.`);
65
- }).pipe(handleErrors),
66
- );
67
-
68
- export const branchesCommand = Command.make("branches", {}, () =>
69
- Console.log("Manage branches. Run with --help for subcommands."),
70
- ).pipe(Command.withSubcommands([listCommand, createCommand, renameCommand, deleteCommand]));
@@ -1,129 +0,0 @@
1
- import path from "node:path";
2
-
3
- import { Command, FileSystem } from "@effect/platform";
4
- import { Effect } from "effect";
5
-
6
- import type { CommandExecutor } from "@effect/platform";
7
- import type { PlatformError } from "@effect/platform/Error";
8
-
9
- import { renderSigningGradle } from "../../lib/android-signing-gradle";
10
- import { findAndroidArtifact } from "../../lib/artifact-finder";
11
- import { downloadAndroidCredentials } from "../../lib/credentials-downloader";
12
- import { sha256File } from "../../lib/sha256";
13
- import { capitalize } from "../../lib/string-utils";
14
- import { CliRuntime } from "../../services/cli-runtime";
15
- import { runStep } from "./run-step";
16
-
17
- import type { AndroidProfile } from "../../lib/build-profile";
18
- import type {
19
- ArtifactNotFoundError,
20
- BuildFailedError,
21
- MissingCredentialsError,
22
- } from "../../lib/exit-codes";
23
- import type { ApiClient } from "../../services/api-client";
24
-
25
- export interface RunAndroidBuildInput {
26
- readonly api: ApiClient;
27
- readonly tempDir: string;
28
- readonly projectRoot: string;
29
- readonly androidProfile: AndroidProfile;
30
- readonly applicationIdentifier: string;
31
- readonly envVars: Record<string, string>;
32
- readonly projectId: string;
33
- }
34
-
35
- export interface RunAndroidBuildResult {
36
- readonly artifactPath: string;
37
- readonly byteSize: number;
38
- readonly sha256: string;
39
- }
40
-
41
- /**
42
- * Compose the Gradle task name from flavor, format, and buildType.
43
- *
44
- * Gradle naming convention: `<verb><Flavor><Variant>`, e.g.
45
- * - no flavor + apk + release → `assembleRelease`
46
- * - no flavor + aab + release → `bundleRelease`
47
- * - flavor=prod + aab + release → `bundleProdRelease`
48
- * - flavor=prod + apk + debug → `assembleProdDebug`
49
- */
50
- const gradleTaskName = (
51
- format: "apk" | "aab",
52
- flavor: string | undefined,
53
- buildType: "debug" | "release",
54
- ): string => {
55
- const verb = format === "aab" ? "bundle" : "assemble";
56
- return flavor
57
- ? `${verb}${capitalize(flavor)}${capitalize(buildType)}`
58
- : `${verb}${capitalize(buildType)}`;
59
- };
60
-
61
- export const runAndroidBuild = (
62
- input: RunAndroidBuildInput,
63
- ): Effect.Effect<
64
- RunAndroidBuildResult,
65
- BuildFailedError | MissingCredentialsError | ArtifactNotFoundError | PlatformError,
66
- CliRuntime | CommandExecutor.CommandExecutor | FileSystem.FileSystem
67
- > =>
68
- Effect.gen(function* () {
69
- const { api, tempDir, projectRoot, androidProfile, applicationIdentifier, envVars, projectId } =
70
- input;
71
- const runtime = yield* CliRuntime;
72
-
73
- // Record build start so artifact-finder can reject stale outputs from
74
- // Earlier builds that may still live in `android/app/build/outputs/`.
75
- const buildStartMs = Date.now();
76
-
77
- const { format } = androidProfile;
78
- const { flavor } = androidProfile;
79
- const buildType = androidProfile.buildType ?? "release";
80
- const androidDir = path.join(projectRoot, "android");
81
- const commandEnv = yield* runtime.commandEnvironment(envVars);
82
-
83
- const credentials = yield* downloadAndroidCredentials(api, {
84
- projectId,
85
- applicationIdentifier,
86
- tempDir,
87
- });
88
-
89
- yield* runStep(
90
- Command.make("bunx", "expo", "prebuild", "--platform", "android", "--clean").pipe(
91
- Command.workingDirectory(projectRoot),
92
- Command.env(commandEnv),
93
- ),
94
- "expo prebuild android",
95
- );
96
-
97
- const fs = yield* FileSystem.FileSystem;
98
- const signingGradlePath = path.join(tempDir, "signing.gradle");
99
- yield* fs.writeFileString(
100
- signingGradlePath,
101
- renderSigningGradle({
102
- keystorePath: credentials.keystorePath,
103
- storePassword: credentials.storePassword,
104
- keyAlias: credentials.keyAlias,
105
- keyPassword: credentials.keyPassword,
106
- }),
107
- );
108
-
109
- const taskName = gradleTaskName(format, flavor, buildType);
110
- yield* runStep(
111
- Command.make("./gradlew", "--init-script", signingGradlePath, `:app:${taskName}`).pipe(
112
- Command.workingDirectory(androidDir),
113
- Command.env(commandEnv),
114
- ),
115
- "gradlew",
116
- );
117
-
118
- const artifactPath = yield* findAndroidArtifact({
119
- projectRoot,
120
- format,
121
- ...(flavor === undefined ? {} : { flavor }),
122
- buildType,
123
- minMtimeMs: buildStartMs,
124
- });
125
-
126
- const { sha256, byteSize } = yield* sha256File(artifactPath);
127
-
128
- return { artifactPath, byteSize, sha256 };
129
- });
@@ -1,63 +0,0 @@
1
- import { Command, Options } from "@effect/cli";
2
- import { Effect, Option } from "effect";
3
-
4
- import type { BadArgument, SystemError } from "@effect/platform/Error";
5
-
6
- import { runBuildWorkflow } from "../../application/build-workflow";
7
- import { exitWith } from "../../application/command-exit";
8
-
9
- import type {
10
- ArtifactNotFoundError,
11
- AuthRequiredError,
12
- BuildFailedError,
13
- BuildProfileError,
14
- CompleteError,
15
- EnvExportError,
16
- KeychainError,
17
- MissingCredentialsError,
18
- PresignedUrlExpiredError,
19
- ProjectNotLinkedError,
20
- ProvisioningError,
21
- ReserveError,
22
- RuntimeVersionError,
23
- UploadFailedError,
24
- } from "../../lib/exit-codes";
25
-
26
- const platform = Options.choice("platform", ["ios", "android"] as const);
27
- const profile = Options.text("profile").pipe(Options.withDefault("production"));
28
- const message = Options.text("message").pipe(Options.optional);
29
- const noUpload = Options.boolean("no-upload");
30
- const rawOutput = Options.boolean("raw-output");
31
-
32
- export const buildCommand = Command.make(
33
- "build",
34
- { platform, profile, message, noUpload, rawOutput },
35
- (opts) =>
36
- runBuildWorkflow({
37
- platform: opts.platform,
38
- profileName: opts.profile,
39
- message: Option.getOrUndefined(opts.message),
40
- noUpload: opts.noUpload,
41
- rawOutput: opts.rawOutput,
42
- }).pipe(
43
- Effect.catchTags({
44
- AuthRequiredError: (err: AuthRequiredError) => exitWith(3, err.message),
45
- ProjectNotLinkedError: (err: ProjectNotLinkedError) => exitWith(4, err.message),
46
- BuildProfileError: (err: BuildProfileError) => exitWith(2, err.message),
47
- RuntimeVersionError: (err: RuntimeVersionError) => exitWith(2, err.message),
48
- MissingCredentialsError: (err: MissingCredentialsError) =>
49
- exitWith(5, `${err.message}\n${err.hint}`),
50
- BuildFailedError: (err: BuildFailedError) => exitWith(6, err.message),
51
- KeychainError: (err: KeychainError) => exitWith(6, err.message),
52
- ProvisioningError: (err: ProvisioningError) => exitWith(6, err.message),
53
- ArtifactNotFoundError: (err: ArtifactNotFoundError) => exitWith(6, err.message),
54
- ReserveError: (err: ReserveError) => exitWith(7, err.message),
55
- UploadFailedError: (err: UploadFailedError) => exitWith(7, err.message),
56
- PresignedUrlExpiredError: (err: PresignedUrlExpiredError) => exitWith(7, err.message),
57
- CompleteError: (err: CompleteError) => exitWith(7, err.message),
58
- EnvExportError: (err: EnvExportError) => exitWith(7, err.message),
59
- SystemError: (err: SystemError) => exitWith(6, `Filesystem error: ${err.message}`),
60
- BadArgument: (err: BadArgument) => exitWith(6, `Invalid argument: ${err.message}`),
61
- }),
62
- ),
63
- );