@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,131 +0,0 @@
1
- import process from "node:process";
2
-
3
- import { Command } from "@effect/platform";
4
- import { Effect, Fiber, Stream } from "effect";
5
-
6
- import type { CommandExecutor } from "@effect/platform";
7
- import type { Scope } from "effect";
8
-
9
- import { BuildFailedError } from "../../lib/exit-codes";
10
-
11
- import type { XcodebuildFormatter } from "../../lib/xcpretty-formatter";
12
-
13
- export const runStep = (
14
- cmd: Command.Command,
15
- step: string,
16
- ): Effect.Effect<void, BuildFailedError, CommandExecutor.CommandExecutor> =>
17
- Command.exitCode(cmd.pipe(Command.stdout("inherit"), Command.stderr("inherit"))).pipe(
18
- Effect.mapError(
19
- (cause) =>
20
- new BuildFailedError({
21
- step,
22
- exitCode: 1,
23
- message: `${step} failed to spawn: ${String(cause)}`,
24
- }),
25
- ),
26
- Effect.flatMap((code) =>
27
- code === 0
28
- ? Effect.void
29
- : Effect.fail(
30
- new BuildFailedError({
31
- step,
32
- exitCode: code,
33
- message: `${step} exited with code ${code}`,
34
- }),
35
- ),
36
- ),
37
- );
38
-
39
- /**
40
- * Run a build step with stdout piped through a formatter (e.g., xcpretty).
41
- * stderr passes through to the terminal directly.
42
- */
43
- export const runStepFormatted = (
44
- cmd: Command.Command,
45
- step: string,
46
- formatter: XcodebuildFormatter,
47
- ): Effect.Effect<void, BuildFailedError, CommandExecutor.CommandExecutor | Scope.Scope> =>
48
- Effect.gen(function* () {
49
- const proc = yield* Command.start(
50
- cmd.pipe(Command.stdout("pipe"), Command.stderr("pipe")),
51
- ).pipe(
52
- Effect.mapError(
53
- (cause) =>
54
- new BuildFailedError({
55
- step,
56
- exitCode: 1,
57
- message: `${step} failed to spawn: ${String(cause)}`,
58
- }),
59
- ),
60
- );
61
-
62
- const stdoutFiber = yield* proc.stdout.pipe(
63
- Stream.decodeText(),
64
- Stream.splitLines,
65
- Stream.runForEach((line) => {
66
- const formatted = formatter.pipe(line);
67
- return formatted.length > 0
68
- ? Effect.sync(() => {
69
- for (const output of formatted) {
70
- process.stdout.write(`${output}\n`);
71
- }
72
- })
73
- : Effect.void;
74
- }),
75
- Effect.mapError(
76
- (cause) =>
77
- new BuildFailedError({
78
- step,
79
- exitCode: 1,
80
- message: `${step} stdout stream error: ${String(cause)}`,
81
- }),
82
- ),
83
- Effect.fork,
84
- );
85
-
86
- const stderrFiber = yield* proc.stderr.pipe(
87
- Stream.decodeText(),
88
- Stream.splitLines,
89
- Stream.runForEach((line) => Effect.sync(() => process.stderr.write(`${line}\n`))),
90
- Effect.mapError(
91
- (cause) =>
92
- new BuildFailedError({
93
- step,
94
- exitCode: 1,
95
- message: `${step} stderr stream error: ${String(cause)}`,
96
- }),
97
- ),
98
- Effect.fork,
99
- );
100
-
101
- // Join fibers concurrently — stream errors are non-fatal, exit code takes precedence.
102
- yield* Effect.all([Fiber.join(stdoutFiber), Fiber.join(stderrFiber)], {
103
- concurrency: 2,
104
- }).pipe(Effect.catchAll(() => Effect.void));
105
-
106
- const code = yield* proc.exitCode.pipe(
107
- Effect.mapError(
108
- (cause) =>
109
- new BuildFailedError({
110
- step,
111
- exitCode: 1,
112
- message: `${step} exit code error: ${String(cause)}`,
113
- }),
114
- ),
115
- );
116
-
117
- if (code !== 0) {
118
- // Print build summary on failure for xcpretty diagnostics
119
- const summary = formatter.getBuildSummary();
120
- if (summary) {
121
- process.stderr.write(`${summary}\n`);
122
- }
123
-
124
- return yield* new BuildFailedError({
125
- step,
126
- exitCode: code,
127
- message: `${step} exited with code ${code}`,
128
- });
129
- }
130
- return undefined;
131
- });
@@ -1,48 +0,0 @@
1
- import { Command } from "@effect/cli";
2
- import { Console, Effect } 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 { handleBuildsCommandErrors } from "./helpers";
8
-
9
- export const compatibilityMatrixCommand = Command.make("compatibility-matrix", {}, () =>
10
- Effect.gen(function* () {
11
- const projectId = yield* readProjectId;
12
- const api = yield* apiClient;
13
- const result = yield* api.builds.compatibilityMatrix({
14
- urlParams: { projectId },
15
- });
16
-
17
- if (result.rows.length === 0 && result.missingRuntimeVersions.length === 0) {
18
- yield* Console.log("No compatibility data found.");
19
- return;
20
- }
21
-
22
- if (result.rows.length > 0) {
23
- yield* Console.log("Build-to-Channel Compatibility:");
24
- yield* printTable(
25
- ["Build ID", "Platform", "Runtime Version", "Channels"],
26
- result.rows.map((row) => [
27
- row.id,
28
- row.platform,
29
- row.runtimeVersion ?? "-",
30
- row.channels.map((channel) => channel.channelName).join(", ") || "-",
31
- ]),
32
- );
33
- }
34
-
35
- if (result.missingRuntimeVersions.length > 0) {
36
- yield* Console.log("\nMissing Runtime Versions:");
37
- yield* printTable(
38
- ["Channel", "Platform", "Runtime Version", "Updates"],
39
- result.missingRuntimeVersions.map((missing) => [
40
- missing.channelName,
41
- missing.platform,
42
- missing.runtimeVersion,
43
- String(missing.updateCount),
44
- ]),
45
- );
46
- }
47
- }).pipe(handleBuildsCommandErrors),
48
- );
@@ -1,15 +0,0 @@
1
- import { Args, Command } from "@effect/cli";
2
- import { Console, Effect } from "effect";
3
-
4
- import { apiClient } from "../../services/api-client";
5
- import { handleBuildsCommandErrors } from "./helpers";
6
-
7
- const id = Args.text({ name: "id" });
8
-
9
- export const deleteCommand = Command.make("delete", { id }, (opts) =>
10
- Effect.gen(function* () {
11
- const api = yield* apiClient;
12
- yield* api.builds.delete({ path: { id: opts.id } });
13
- yield* Console.log(`Build ${opts.id} deleted.`);
14
- }).pipe(handleBuildsCommandErrors),
15
- );
@@ -1,34 +0,0 @@
1
- import { Args, Command } from "@effect/cli";
2
- import { Effect } from "effect";
3
-
4
- import { printKeyValue } from "../../lib/output";
5
- import { apiClient } from "../../services/api-client";
6
- import { handleBuildsCommandErrors } from "./helpers";
7
-
8
- const id = Args.text({ name: "id" });
9
-
10
- export const getCommand = Command.make("get", { id }, (opts) =>
11
- Effect.gen(function* () {
12
- const api = yield* apiClient;
13
- const build = yield* api.builds.get({ path: { id: opts.id } });
14
- yield* printKeyValue([
15
- ["ID", build.id],
16
- ["Platform", build.platform],
17
- ["Profile", build.profile],
18
- ["Distribution", build.distribution],
19
- ["Version", build.appVersion ?? "-"],
20
- ["Build Number", build.buildNumber ?? "-"],
21
- ["Runtime Version", build.runtimeVersion ?? "-"],
22
- ["Bundle ID", build.bundleId ?? "-"],
23
- ["Git Ref", build.gitRef ?? "-"],
24
- ["Message", build.message ?? "-"],
25
- [
26
- "Artifact",
27
- build.artifact
28
- ? `${build.artifact.format} (${String(build.artifact.byteSize)} bytes)`
29
- : "none",
30
- ],
31
- ["Created", build.createdAt],
32
- ]);
33
- }).pipe(handleBuildsCommandErrors),
34
- );
@@ -1,3 +0,0 @@
1
- import { makeCommandErrorHandler } from "../../lib/command-errors";
2
-
3
- export const handleBuildsCommandErrors = makeCommandErrorHandler();
@@ -1,20 +0,0 @@
1
- import { Command } from "@effect/cli";
2
- import { Console } from "effect";
3
-
4
- import { compatibilityMatrixCommand } from "./compatibility-matrix";
5
- import { deleteCommand } from "./delete";
6
- import { getCommand } from "./get";
7
- import { installLinkCommand } from "./install-link";
8
- import { listCommand } from "./list";
9
-
10
- export const buildsCommand = Command.make("builds", {}, () =>
11
- Console.log("Manage builds. Run with --help for subcommands."),
12
- ).pipe(
13
- Command.withSubcommands([
14
- listCommand,
15
- getCommand,
16
- deleteCommand,
17
- installLinkCommand,
18
- compatibilityMatrixCommand,
19
- ]),
20
- );
@@ -1,20 +0,0 @@
1
- import { Args, Command } from "@effect/cli";
2
- import { Effect } from "effect";
3
-
4
- import { printKeyValue } from "../../lib/output";
5
- import { apiClient } from "../../services/api-client";
6
- import { handleBuildsCommandErrors } from "./helpers";
7
-
8
- const id = Args.text({ name: "id" });
9
-
10
- export const installLinkCommand = Command.make("install-link", { id }, (opts) =>
11
- Effect.gen(function* () {
12
- const api = yield* apiClient;
13
- const result = yield* api.builds.getInstallLink({ path: { id: opts.id } });
14
- yield* printKeyValue([
15
- ["Artifact URL", result.artifactUrl],
16
- ["Install URL", result.installUrl ?? "-"],
17
- ["Expires", String(result.expires)],
18
- ]);
19
- }).pipe(handleBuildsCommandErrors),
20
- );
@@ -1,38 +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 { printTable } from "../../lib/output";
6
- import { apiClient } from "../../services/api-client";
7
- import { handleBuildsCommandErrors } from "./helpers";
8
-
9
- const platform = Options.choice("platform", ["ios", "android"]).pipe(Options.optional);
10
- const limit = Options.integer("limit").pipe(Options.withDefault(10));
11
-
12
- export const listCommand = Command.make("list", { platform, limit }, (opts) =>
13
- Effect.gen(function* () {
14
- const projectId = yield* readProjectId;
15
- const api = yield* apiClient;
16
-
17
- const platformFilter = Option.match(opts.platform, {
18
- onNone: () => ({}) as Record<string, string>,
19
- onSome: (platformValue) => ({ platform: platformValue }) as Record<string, string>,
20
- });
21
-
22
- const { items } = yield* api.builds.list({
23
- urlParams: { projectId, ...platformFilter, page: 1, limit: opts.limit },
24
- });
25
-
26
- yield* printTable(
27
- ["ID", "Platform", "Profile", "Distribution", "Version", "Created"],
28
- items.map((build) => [
29
- build.id,
30
- build.platform,
31
- build.profile,
32
- build.distribution,
33
- build.appVersion ?? "-",
34
- build.createdAt,
35
- ]),
36
- );
37
- }).pipe(handleBuildsCommandErrors),
38
- );
@@ -1,37 +0,0 @@
1
- import { Command, Options } from "@effect/cli";
2
- import { Effect } 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 { handleChannelCommandErrors, resolveNamedResourceId } from "./helpers";
8
-
9
- const name = Options.text("name");
10
- const branch = Options.text("branch");
11
-
12
- export const createCommand = Command.make("create", { name, branch }, (opts) =>
13
- Effect.gen(function* () {
14
- const projectId = yield* readProjectId;
15
- const api = yield* apiClient;
16
-
17
- const { items: branches } = yield* api.branches.list({
18
- urlParams: { projectId, page: 1, limit: 1000 },
19
- });
20
- const branchId = yield* resolveNamedResourceId({
21
- items: branches,
22
- kind: "Branch",
23
- name: opts.branch,
24
- });
25
-
26
- const channel = yield* api.channels.create({
27
- payload: { projectId, name: opts.name, branchId },
28
- });
29
-
30
- yield* printKeyValue([
31
- ["ID", channel.id],
32
- ["Name", channel.name],
33
- ["Branch", opts.branch],
34
- ["Created", channel.createdAt],
35
- ]);
36
- }).pipe(handleChannelCommandErrors),
37
- );
@@ -1,15 +0,0 @@
1
- import { Args, Command } from "@effect/cli";
2
- import { Console, Effect } from "effect";
3
-
4
- import { apiClient } from "../../services/api-client";
5
- import { handleChannelCommandErrors } from "./helpers";
6
-
7
- const id = Args.text({ name: "id" });
8
-
9
- export const deleteCommand = Command.make("delete", { id }, (opts) =>
10
- Effect.gen(function* () {
11
- const api = yield* apiClient;
12
- yield* api.channels.delete({ path: { id: opts.id } });
13
- yield* Console.log(`Channel ${opts.id} deleted.`);
14
- }).pipe(handleChannelCommandErrors),
15
- );
@@ -1,18 +0,0 @@
1
- import { Data } from "effect";
2
-
3
- import { makeCommandErrorHandler } from "../../lib/command-errors";
4
- import { resolveNamedResourceId as resolveNamedResourceIdBase } from "../../lib/resolve-named-resource";
5
-
6
- export class ChannelCommandError extends Data.TaggedError("ChannelCommandError")<{
7
- readonly message: string;
8
- }> {}
9
-
10
- export const handleChannelCommandErrors = makeCommandErrorHandler({
11
- ChannelCommandError: 2,
12
- });
13
-
14
- export const resolveNamedResourceId = (params: {
15
- readonly items: readonly { readonly id: string; readonly name: string }[];
16
- readonly kind: string;
17
- readonly name: string;
18
- }) => resolveNamedResourceIdBase(params, (message) => new ChannelCommandError({ message }));
@@ -1,24 +0,0 @@
1
- import { Command } from "@effect/cli";
2
- import { Console } from "effect";
3
-
4
- import { createCommand } from "./create";
5
- import { deleteCommand } from "./delete";
6
- import { listCommand } from "./list";
7
- import { pauseCommand } from "./pause";
8
- import { resumeCommand } from "./resume";
9
- import { rolloutCommand } from "./rollout";
10
- import { updateCommand } from "./update";
11
-
12
- export const channelsCommand = Command.make("channels", {}, () =>
13
- Console.log("Manage channels. Run with --help for subcommands."),
14
- ).pipe(
15
- Command.withSubcommands([
16
- listCommand,
17
- createCommand,
18
- updateCommand,
19
- pauseCommand,
20
- resumeCommand,
21
- deleteCommand,
22
- rolloutCommand,
23
- ]),
24
- );
@@ -1,38 +0,0 @@
1
- import { Command } from "@effect/cli";
2
- import { Console, Effect } 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 { handleChannelCommandErrors } from "./helpers";
8
-
9
- export const listCommand = Command.make("list", {}, () =>
10
- Effect.gen(function* () {
11
- const projectId = yield* readProjectId;
12
- const api = yield* apiClient;
13
-
14
- const [{ items }, { items: branches }] = yield* Effect.all([
15
- api.channels.list({ urlParams: { projectId, page: 1, limit: 1000 } }),
16
- api.branches.list({ urlParams: { projectId, page: 1, limit: 1000 } }),
17
- ]);
18
-
19
- if (items.length === 0) {
20
- yield* Console.log("No channels found.");
21
- return;
22
- }
23
-
24
- const branchNames = new Map(branches.map((branch) => [branch.id, branch.name]));
25
-
26
- yield* printTable(
27
- ["ID", "Name", "Branch", "Paused", "Rollout", "Created"],
28
- items.map((channel) => [
29
- channel.id,
30
- channel.name,
31
- branchNames.get(channel.branchId) ?? channel.branchId,
32
- channel.isPaused ? "yes" : "no",
33
- channel.branchMappingJson === null ? "-" : "active",
34
- channel.createdAt,
35
- ]),
36
- );
37
- }).pipe(handleChannelCommandErrors),
38
- );
@@ -1,15 +0,0 @@
1
- import { Args, Command } from "@effect/cli";
2
- import { Console, Effect } from "effect";
3
-
4
- import { apiClient } from "../../services/api-client";
5
- import { handleChannelCommandErrors } from "./helpers";
6
-
7
- const id = Args.text({ name: "id" });
8
-
9
- export const pauseCommand = Command.make("pause", { id }, (opts) =>
10
- Effect.gen(function* () {
11
- const api = yield* apiClient;
12
- const channel = yield* api.channels.pause({ path: { id: opts.id } });
13
- yield* Console.log(`Channel "${channel.name}" paused.`);
14
- }).pipe(handleChannelCommandErrors),
15
- );
@@ -1,15 +0,0 @@
1
- import { Args, Command } from "@effect/cli";
2
- import { Console, Effect } from "effect";
3
-
4
- import { apiClient } from "../../services/api-client";
5
- import { handleChannelCommandErrors } from "./helpers";
6
-
7
- const id = Args.text({ name: "id" });
8
-
9
- export const resumeCommand = Command.make("resume", { id }, (opts) =>
10
- Effect.gen(function* () {
11
- const api = yield* apiClient;
12
- const channel = yield* api.channels.resume({ path: { id: opts.id } });
13
- yield* Console.log(`Channel "${channel.name}" resumed.`);
14
- }).pipe(handleChannelCommandErrors),
15
- );
@@ -1,17 +0,0 @@
1
- import { Args, Command } from "@effect/cli";
2
- import { Console, Effect } from "effect";
3
-
4
- import { apiClient } from "../../../services/api-client";
5
- import { handleChannelCommandErrors } from "../helpers";
6
-
7
- const channelId = Args.text({ name: "channelId" });
8
-
9
- export const completeCommand = Command.make("complete", { channelId }, (opts) =>
10
- Effect.gen(function* () {
11
- const api = yield* apiClient;
12
- const channel = yield* api.channels.completeBranchRollout({
13
- path: { id: opts.channelId },
14
- });
15
- yield* Console.log(`Completed rollout on channel "${channel.name}".`);
16
- }).pipe(handleChannelCommandErrors),
17
- );
@@ -1,36 +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 { rolloutPercentageOption } from "../../../lib/cli-schemas";
6
- import { apiClient } from "../../../services/api-client";
7
- import { handleChannelCommandErrors, resolveNamedResourceId } from "../helpers";
8
-
9
- const channelId = Args.text({ name: "channelId" });
10
- const branch = Options.text("branch");
11
- const percentage = rolloutPercentageOption("percentage");
12
-
13
- export const createCommand = Command.make("create", { channelId, branch, percentage }, (opts) =>
14
- Effect.gen(function* () {
15
- const projectId = yield* readProjectId;
16
- const api = yield* apiClient;
17
-
18
- const { items: branches } = yield* api.branches.list({
19
- urlParams: { projectId, page: 1, limit: 1000 },
20
- });
21
- const newBranchId = yield* resolveNamedResourceId({
22
- items: branches,
23
- kind: "Branch",
24
- name: opts.branch,
25
- });
26
-
27
- const channel = yield* api.channels.createBranchRollout({
28
- path: { id: opts.channelId },
29
- payload: { newBranchId, percentage: opts.percentage },
30
- });
31
-
32
- yield* Console.log(
33
- `Started rollout on channel "${channel.name}" to branch "${opts.branch}" at ${String(opts.percentage)}%.`,
34
- );
35
- }).pipe(handleChannelCommandErrors),
36
- );
@@ -1,11 +0,0 @@
1
- import { Command } from "@effect/cli";
2
- import { Console } from "effect";
3
-
4
- import { completeCommand } from "./complete";
5
- import { createCommand } from "./create";
6
- import { revertCommand } from "./revert";
7
- import { updateCommand } from "./update";
8
-
9
- export const rolloutCommand = Command.make("rollout", {}, () =>
10
- Console.log("Manage channel branch rollouts. Run with --help for subcommands."),
11
- ).pipe(Command.withSubcommands([createCommand, updateCommand, completeCommand, revertCommand]));
@@ -1,17 +0,0 @@
1
- import { Args, Command } from "@effect/cli";
2
- import { Console, Effect } from "effect";
3
-
4
- import { apiClient } from "../../../services/api-client";
5
- import { handleChannelCommandErrors } from "../helpers";
6
-
7
- const channelId = Args.text({ name: "channelId" });
8
-
9
- export const revertCommand = Command.make("revert", { channelId }, (opts) =>
10
- Effect.gen(function* () {
11
- const api = yield* apiClient;
12
- const channel = yield* api.channels.revertBranchRollout({
13
- path: { id: opts.channelId },
14
- });
15
- yield* Console.log(`Reverted rollout on channel "${channel.name}".`);
16
- }).pipe(handleChannelCommandErrors),
17
- );
@@ -1,23 +0,0 @@
1
- import { Args, Command } from "@effect/cli";
2
- import { Console, Effect } from "effect";
3
-
4
- import { rolloutPercentageOption } from "../../../lib/cli-schemas";
5
- import { apiClient } from "../../../services/api-client";
6
- import { handleChannelCommandErrors } from "../helpers";
7
-
8
- const channelId = Args.text({ name: "channelId" });
9
- const percentage = rolloutPercentageOption("percentage");
10
-
11
- export const updateCommand = Command.make("update", { channelId, percentage }, (opts) =>
12
- Effect.gen(function* () {
13
- const api = yield* apiClient;
14
- const channel = yield* api.channels.updateBranchRollout({
15
- path: { id: opts.channelId },
16
- payload: { percentage: opts.percentage },
17
- });
18
-
19
- yield* Console.log(
20
- `Updated rollout on channel "${channel.name}" to ${String(opts.percentage)}%.`,
21
- );
22
- }).pipe(handleChannelCommandErrors),
23
- );
@@ -1,32 +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 { apiClient } from "../../services/api-client";
6
- import { handleChannelCommandErrors, resolveNamedResourceId } from "./helpers";
7
-
8
- const id = Args.text({ name: "id" });
9
- const branch = Options.text("branch");
10
-
11
- export const updateCommand = Command.make("update", { id, branch }, (opts) =>
12
- Effect.gen(function* () {
13
- const projectId = yield* readProjectId;
14
- const api = yield* apiClient;
15
-
16
- const { items: branches } = yield* api.branches.list({
17
- urlParams: { projectId, page: 1, limit: 1000 },
18
- });
19
- const branchId = yield* resolveNamedResourceId({
20
- items: branches,
21
- kind: "Branch",
22
- name: opts.branch,
23
- });
24
-
25
- const channel = yield* api.channels.update({
26
- path: { id: opts.id },
27
- payload: { branchId },
28
- });
29
-
30
- yield* Console.log(`Channel "${channel.name}" relinked to branch "${opts.branch}".`);
31
- }).pipe(handleChannelCommandErrors),
32
- );
@@ -1,24 +0,0 @@
1
- import { Args, Command, Options } from "@effect/cli";
2
- import { Console, Effect } from "effect";
3
-
4
- import { deleteCredential } from "../../lib/credentials-manager";
5
- import { apiClient } from "../../services/api-client";
6
-
7
- const id = Args.text({ name: "id" });
8
- const platform = Options.choice("platform", ["ios", "android"] as const);
9
- const type = Options.choice("type", [
10
- "distribution-certificate",
11
- "provisioning-profile",
12
- "push-key",
13
- "asc-api-key",
14
- "keystore",
15
- "google-service-account-key",
16
- ] as const);
17
-
18
- export const deleteCommand = Command.make("delete", { id, platform, type }, (opts) =>
19
- Effect.gen(function* () {
20
- const api = yield* apiClient;
21
- yield* deleteCredential(api, { id: opts.id, platform: opts.platform, type: opts.type });
22
- yield* Console.log(`Credential ${opts.id} deleted.`);
23
- }),
24
- );
@@ -1,10 +0,0 @@
1
- import { Command } from "@effect/cli";
2
- import { Console } from "effect";
3
-
4
- import { deleteCommand } from "./delete";
5
- import { listCommand } from "./list";
6
- import { uploadCommand } from "./upload";
7
-
8
- export const credentialsCommand = Command.make("credentials", {}, () =>
9
- Console.log("Manage credentials. Run with --help for subcommands."),
10
- ).pipe(Command.withSubcommands([listCommand, uploadCommand, deleteCommand]));