@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,33 +0,0 @@
1
- import { Command, Options } from "@effect/cli";
2
- import { Console, Effect, Option } from "effect";
3
-
4
- import { filterCredentials, listAllCredentials } from "../../lib/credentials-manager";
5
- import { printTable } from "../../lib/output";
6
- import { apiClient } from "../../services/api-client";
7
-
8
- const platform = Options.choice("platform", ["ios", "android"] as const).pipe(Options.optional);
9
-
10
- export const listCommand = Command.make("list", { platform }, (opts) =>
11
- Effect.gen(function* () {
12
- const api = yield* apiClient;
13
- const rows = yield* listAllCredentials(api);
14
-
15
- const filtered = filterCredentials(
16
- rows,
17
- Option.match(opts.platform, {
18
- onNone: () => ({}),
19
- onSome: (platformValue) => ({ platform: platformValue }),
20
- }),
21
- );
22
-
23
- if (filtered.length === 0) {
24
- yield* Console.log("No credentials found.");
25
- return;
26
- }
27
-
28
- yield* printTable(
29
- ["ID", "Name", "Platform", "Type", "Distribution"],
30
- filtered.map((row) => [row.id, row.name, row.platform, row.type, row.distribution ?? "-"]),
31
- );
32
- }),
33
- );
@@ -1,91 +0,0 @@
1
- import { Command, Options } from "@effect/cli";
2
- import { Console, Effect, Option } from "effect";
3
-
4
- import { uploadCredential } from "../../lib/credentials-manager";
5
- import { printKeyValue } from "../../lib/output";
6
- import { apiClient } from "../../services/api-client";
7
-
8
- import type { CliCredentialType } from "../../lib/credentials-manager";
9
-
10
- const platform = Options.choice("platform", ["ios", "android"] as const);
11
- const type = Options.choice("type", [
12
- "distribution-certificate",
13
- "provisioning-profile",
14
- "push-key",
15
- "asc-api-key",
16
- "keystore",
17
- "google-service-account-key",
18
- ] as const);
19
- const name = Options.text("name");
20
- const file = Options.text("file");
21
-
22
- const password = Options.text("password").pipe(Options.optional);
23
- const keyAlias = Options.text("key-alias").pipe(Options.optional);
24
- const keyPassword = Options.text("key-password").pipe(Options.optional);
25
- const keyId = Options.text("key-id").pipe(Options.optional);
26
- const issuerId = Options.text("issuer-id").pipe(Options.optional);
27
- const appleTeamIdentifier = Options.text("apple-team-identifier").pipe(Options.optional);
28
-
29
- export const uploadCommand = Command.make(
30
- "upload",
31
- {
32
- platform,
33
- type,
34
- name,
35
- file,
36
- password,
37
- keyAlias,
38
- keyPassword,
39
- keyId,
40
- issuerId,
41
- appleTeamIdentifier,
42
- },
43
- (opts) =>
44
- Effect.gen(function* () {
45
- const api = yield* apiClient;
46
-
47
- const passwordOpt = Option.getOrUndefined(opts.password);
48
- const keyAliasOpt = Option.getOrUndefined(opts.keyAlias);
49
- const keyPasswordOpt = Option.getOrUndefined(opts.keyPassword);
50
- const keyIdOpt = Option.getOrUndefined(opts.keyId);
51
- const issuerIdOpt = Option.getOrUndefined(opts.issuerId);
52
- const appleTeamIdentifierOpt = Option.getOrUndefined(opts.appleTeamIdentifier);
53
-
54
- const input: {
55
- readonly platform: typeof opts.platform;
56
- readonly type: CliCredentialType;
57
- readonly name: string;
58
- readonly filePath: string;
59
- readonly password?: string;
60
- readonly keyAlias?: string;
61
- readonly keyPassword?: string;
62
- readonly keyId?: string;
63
- readonly issuerId?: string;
64
- readonly appleTeamIdentifier?: string;
65
- } = {
66
- platform: opts.platform,
67
- type: opts.type,
68
- name: opts.name,
69
- filePath: opts.file,
70
- ...(passwordOpt === undefined ? {} : { password: passwordOpt }),
71
- ...(keyAliasOpt === undefined ? {} : { keyAlias: keyAliasOpt }),
72
- ...(keyPasswordOpt === undefined ? {} : { keyPassword: keyPasswordOpt }),
73
- ...(keyIdOpt === undefined ? {} : { keyId: keyIdOpt }),
74
- ...(issuerIdOpt === undefined ? {} : { issuerId: issuerIdOpt }),
75
- ...(appleTeamIdentifierOpt === undefined
76
- ? {}
77
- : { appleTeamIdentifier: appleTeamIdentifierOpt }),
78
- };
79
-
80
- const credential = yield* uploadCredential(api, input);
81
-
82
- yield* Console.log("Credential uploaded successfully.");
83
- yield* Console.log("");
84
- yield* printKeyValue([
85
- ["ID", credential.id],
86
- ["Name", credential.name],
87
- ["Platform", credential.platform],
88
- ["Type", credential.type],
89
- ]);
90
- }),
91
- );
@@ -1,35 +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 { EnvResourceNotFoundError, handleEnvCommandErrors } from "./helpers";
7
-
8
- const keyArg = Args.text({ name: "KEY" });
9
- const environmentOption = Options.text("environment").pipe(Options.withDefault("production"));
10
-
11
- export const deleteCommand = Command.make(
12
- "delete",
13
- { key: keyArg, environment: environmentOption },
14
- ({ key, environment }) =>
15
- Effect.gen(function* () {
16
- const projectId = yield* readProjectId;
17
- const api = yield* apiClient;
18
-
19
- const existing = yield* api["env-vars"].list({
20
- urlParams: { projectId, environment },
21
- });
22
-
23
- const match = existing.items.find((item) => item.key === key);
24
-
25
- if (!match) {
26
- return yield* new EnvResourceNotFoundError({
27
- message: `Environment variable ${key} not found in ${environment}`,
28
- });
29
- }
30
-
31
- yield* api["env-vars"].delete({ path: { id: match.id } });
32
- yield* Console.log(`Deleted ${key} from ${environment}`);
33
- return undefined;
34
- }).pipe(handleEnvCommandErrors),
35
- );
@@ -1,27 +0,0 @@
1
- import { 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 { handleEnvCommandErrors } from "./helpers";
7
-
8
- const environmentOption = Options.text("environment").pipe(Options.withDefault("production"));
9
-
10
- export const exportCommand = Command.make(
11
- "export",
12
- { environment: environmentOption },
13
- ({ environment }) =>
14
- Effect.gen(function* () {
15
- const projectId = yield* readProjectId;
16
- const api = yield* apiClient;
17
-
18
- const result = yield* api["env-vars"].export({
19
- urlParams: { projectId, environment },
20
- });
21
-
22
- for (const item of result.items) {
23
- const escaped = item.value.replaceAll("'", String.raw`'\''`);
24
- yield* Console.log(`${item.key}='${escaped}'`);
25
- }
26
- }).pipe(handleEnvCommandErrors),
27
- );
@@ -1,25 +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 { handleEnvCommandErrors } 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 envVar = yield* api["env-vars"].get({ path: { id: opts.id } });
14
- yield* printKeyValue([
15
- ["ID", envVar.id],
16
- ["Key", envVar.key],
17
- ["Environment", envVar.environment],
18
- ["Visibility", envVar.visibility],
19
- // eslint-disable-next-line eslint-js/no-restricted-syntax -- EnvVar.value nullable at storage; display empty when absent
20
- ["Value", envVar.visibility === "plaintext" ? (envVar.value ?? "") : "******"],
21
- ["Created", envVar.createdAt],
22
- ["Updated", envVar.updatedAt],
23
- ]);
24
- }).pipe(handleEnvCommandErrors),
25
- );
@@ -1,13 +0,0 @@
1
- import { Data } from "effect";
2
-
3
- import { makeCommandErrorHandler } from "../../lib/command-errors";
4
-
5
- export class EnvResourceNotFoundError extends Data.TaggedError("EnvResourceNotFoundError")<{
6
- readonly message: string;
7
- }> {}
8
-
9
- export const handleEnvCommandErrors = makeCommandErrorHandler({
10
- EnvResourceNotFoundError: 1,
11
- SystemError: 6,
12
- BadArgument: 6,
13
- });
@@ -1,31 +0,0 @@
1
- import { Args, Command, Options } from "@effect/cli";
2
- import { FileSystem } from "@effect/platform";
3
- import { Console, Effect } from "effect";
4
-
5
- import { readProjectId } from "../../lib/app-json";
6
- import { apiClient } from "../../services/api-client";
7
- import { handleEnvCommandErrors } from "./helpers";
8
-
9
- const fileArg = Args.text({ name: "file" });
10
- const environmentOption = Options.text("environment").pipe(Options.withDefault("production"));
11
-
12
- export const importCommand = Command.make(
13
- "import",
14
- { file: fileArg, environment: environmentOption },
15
- ({ file, environment }) =>
16
- Effect.gen(function* () {
17
- const fs = yield* FileSystem.FileSystem;
18
- const content = yield* fs.readFileString(file);
19
-
20
- const projectId = yield* readProjectId;
21
- const api = yield* apiClient;
22
-
23
- const result = yield* api["env-vars"].bulkImport({
24
- payload: { projectId, environment, content, visibility: "plaintext" },
25
- });
26
-
27
- yield* Console.log(
28
- `Imported: ${String(result.created)} created, ${String(result.updated)} updated, ${String(result.skipped)} skipped`,
29
- );
30
- }).pipe(handleEnvCommandErrors),
31
- );
@@ -1,24 +0,0 @@
1
- import { Command } from "@effect/cli";
2
- import { Console } from "effect";
3
-
4
- import { deleteCommand } from "./delete";
5
- import { exportCommand } from "./export";
6
- import { getCommand } from "./get";
7
- import { importCommand } from "./import";
8
- import { listCommand } from "./list";
9
- import { pullCommand } from "./pull";
10
- import { setCommand } from "./set";
11
-
12
- export const envCommand = Command.make("env", {}, () =>
13
- Console.log("Manage environment variables. Run with --help for subcommands."),
14
- ).pipe(
15
- Command.withSubcommands([
16
- listCommand,
17
- getCommand,
18
- setCommand,
19
- deleteCommand,
20
- importCommand,
21
- exportCommand,
22
- pullCommand,
23
- ]),
24
- );
@@ -1,44 +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 { handleEnvCommandErrors } from "./helpers";
8
-
9
- const environmentOption = Options.text("environment").pipe(Options.optional);
10
-
11
- export const listCommand = Command.make(
12
- "list",
13
- { environment: environmentOption },
14
- ({ environment }) =>
15
- Effect.gen(function* () {
16
- const projectId = yield* readProjectId;
17
- const api = yield* apiClient;
18
-
19
- const envFilter = Option.match(environment, {
20
- onNone: () => ({}),
21
- onSome: (value) => ({ environment: value }),
22
- });
23
-
24
- const result = yield* api["env-vars"].list({
25
- urlParams: { projectId, ...envFilter },
26
- });
27
-
28
- if (result.items.length === 0) {
29
- yield* Console.log("No environment variables found.");
30
- return;
31
- }
32
-
33
- yield* printTable(
34
- ["Key", "Environment", "Visibility", "Value"],
35
- result.items.map((item) => [
36
- item.key,
37
- item.environment,
38
- item.visibility,
39
- // eslint-disable-next-line eslint-js/no-restricted-syntax -- EnvVar.value nullable at storage; display empty when absent
40
- item.visibility === "plaintext" ? (item.value ?? "") : "••••••",
41
- ]),
42
- );
43
- }).pipe(handleEnvCommandErrors),
44
- );
@@ -1,27 +0,0 @@
1
- import { 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 { handleEnvCommandErrors } from "./helpers";
7
-
8
- const environmentOption = Options.text("environment").pipe(Options.withDefault("production"));
9
-
10
- export const pullCommand = Command.make(
11
- "pull",
12
- { environment: environmentOption },
13
- ({ environment }) =>
14
- Effect.gen(function* () {
15
- const projectId = yield* readProjectId;
16
- const api = yield* apiClient;
17
-
18
- const result = yield* api["env-vars"].export({
19
- urlParams: { projectId, environment },
20
- });
21
-
22
- for (const item of result.items) {
23
- const escaped = item.value.replaceAll("'", String.raw`'\''`);
24
- yield* Console.log(`export ${item.key}='${escaped}'`);
25
- }
26
- }).pipe(handleEnvCommandErrors),
27
- );
@@ -1,42 +0,0 @@
1
- import { Command, Options } from "@effect/cli";
2
- import { Console, Effect } from "effect";
3
-
4
- import { readProjectId } from "../../lib/app-json";
5
- import { keyValueArg } from "../../lib/cli-schemas";
6
- import { apiClient } from "../../services/api-client";
7
- import { handleEnvCommandErrors } from "./helpers";
8
-
9
- const keyValue = keyValueArg("KEY=VALUE");
10
- const environmentOption = Options.text("environment").pipe(Options.withDefault("production"));
11
- const visibilityOption = Options.choice("visibility", ["plaintext", "sensitive", "secret"]).pipe(
12
- Options.withDefault("plaintext" as const),
13
- );
14
-
15
- export const setCommand = Command.make(
16
- "set",
17
- { keyValue, environment: environmentOption, visibility: visibilityOption },
18
- ({ keyValue: { key, value }, environment, visibility }) =>
19
- Effect.gen(function* () {
20
- const projectId = yield* readProjectId;
21
- const api = yield* apiClient;
22
-
23
- const existing = yield* api["env-vars"].list({
24
- urlParams: { projectId, environment },
25
- });
26
-
27
- const match = existing.items.find((item) => item.key === key);
28
-
29
- if (match) {
30
- yield* api["env-vars"].update({
31
- path: { id: match.id },
32
- payload: { value, visibility },
33
- });
34
- yield* Console.log(`Updated ${key} in ${environment}`);
35
- } else {
36
- yield* api["env-vars"].create({
37
- payload: { projectId, environment, key, value, visibility },
38
- });
39
- yield* Console.log(`Created ${key} in ${environment}`);
40
- }
41
- }).pipe(handleEnvCommandErrors),
42
- );
@@ -1,25 +0,0 @@
1
- import { Args, Command } from "@effect/cli";
2
- import { Console, Effect } from "effect";
3
-
4
- import { exitWith } from "../../application/command-exit";
5
- import { runFingerprintFull } from "../../lib/fingerprint";
6
- import { CliRuntime } from "../../services/cli-runtime";
7
-
8
- const hash = Args.text({ name: "hash" });
9
-
10
- export const compareCommand = Command.make("compare", { hash }, (opts) =>
11
- Effect.gen(function* () {
12
- const runtime = yield* CliRuntime;
13
- const projectRoot = yield* runtime.cwd;
14
- const result = yield* runFingerprintFull(projectRoot);
15
-
16
- if (result.hash === opts.hash) {
17
- yield* Console.log("Fingerprints match.");
18
- return undefined;
19
- }
20
- yield* Console.log("Fingerprints differ.");
21
- yield* Console.log(` Local: ${result.hash}`);
22
- yield* Console.log(` Provided: ${opts.hash}`);
23
- return yield* exitWith(1, "Fingerprint mismatch");
24
- }).pipe(Effect.catchTag("FingerprintError", (error) => exitWith(2, error.message))),
25
- );
@@ -1,18 +0,0 @@
1
- import { Command } from "@effect/cli";
2
- import { Console, Effect } from "effect";
3
-
4
- import { exitWith } from "../../application/command-exit";
5
- import { runFingerprintFull } from "../../lib/fingerprint";
6
- import { CliRuntime } from "../../services/cli-runtime";
7
-
8
- export const generateCommand = Command.make("generate", {}, () =>
9
- Effect.gen(function* () {
10
- const runtime = yield* CliRuntime;
11
- const projectRoot = yield* runtime.cwd;
12
- const result = yield* runFingerprintFull(projectRoot);
13
- yield* Console.log(result.hash);
14
- if (result.sources.length > 0) {
15
- yield* Console.log(`${result.sources.length} sources`);
16
- }
17
- }).pipe(Effect.catchTag("FingerprintError", (error) => exitWith(2, error.message))),
18
- );
@@ -1,9 +0,0 @@
1
- import { Command } from "@effect/cli";
2
- import { Console } from "effect";
3
-
4
- import { compareCommand } from "./compare";
5
- import { generateCommand } from "./generate";
6
-
7
- export const fingerprintCommand = Command.make("fingerprint", {}, () =>
8
- Console.log("Fingerprint utilities. Use --help for subcommands."),
9
- ).pipe(Command.withSubcommands([generateCommand, compareCommand]));
@@ -1,35 +0,0 @@
1
- import { Command } from "@effect/cli";
2
- import { Console, Effect } from "effect";
3
-
4
- import { readAppJson, readSlug, writeProjectId } from "../lib/app-json";
5
- import { asString } from "../lib/build-profile";
6
- import { asRecord } from "../lib/record";
7
- import { apiClient } from "../services/api-client";
8
-
9
- export const initCommand = Command.make("init", {}, () =>
10
- Effect.gen(function* () {
11
- const appJson = yield* readAppJson;
12
- const expo = asRecord(appJson["expo"]);
13
- const name = asString(expo?.["name"]) ?? asString(expo?.["slug"]) ?? "untitled";
14
- const slug = yield* readSlug;
15
-
16
- yield* Console.log(`Linking project: ${name} (${slug})`);
17
-
18
- const api = yield* apiClient;
19
- const { items } = yield* api.projects.list({ urlParams: { page: 1, limit: 100 } });
20
-
21
- const existing = items.find((project) => project.slug === slug);
22
-
23
- if (existing) {
24
- yield* Console.log(`Found existing project: ${existing.name} (${existing.id})`);
25
- yield* writeProjectId(existing.id);
26
- } else {
27
- yield* Console.log("No existing project found. Creating new project...");
28
- const project = yield* api.projects.create({ payload: { name, slug } });
29
- yield* Console.log(`Created project: ${project.name} (${project.id})`);
30
- yield* writeProjectId(project.id);
31
- }
32
-
33
- yield* Console.log("Project linked successfully. ID saved to app.json.");
34
- }),
35
- );
@@ -1,13 +0,0 @@
1
- import { Command as CliCommand, Options } from "@effect/cli";
2
- import { Cause, Effect } from "effect";
3
-
4
- import { exitWith } from "../application/command-exit";
5
- import { runLogin } from "../application/login";
6
-
7
- const manualApiKey = Options.boolean("api-key");
8
-
9
- const loginFailed = (cause: Cause.Cause<unknown>) => exitWith(1, Cause.pretty(cause));
10
-
11
- export const loginCommand = CliCommand.make("login", { manualApiKey }, (opts) =>
12
- runLogin({ manualApiKey: opts.manualApiKey }).pipe(Effect.catchAllCause(loginFailed)),
13
- );
@@ -1,12 +0,0 @@
1
- import { Command } from "@effect/cli";
2
- import { Console, Effect } from "effect";
3
-
4
- import { AuthStore } from "../services/auth-store";
5
-
6
- export const logoutCommand = Command.make("logout", {}, () =>
7
- Effect.gen(function* () {
8
- const authStore = yield* AuthStore;
9
- yield* authStore.clearToken;
10
- yield* Console.log("Logged out. Auth token removed.");
11
- }),
12
- );
@@ -1,84 +0,0 @@
1
- import { Args, Command, Options } from "@effect/cli";
2
- import { Console, Effect } from "effect";
3
-
4
- import { makeCommandErrorHandler } from "../lib/command-errors";
5
- import { printKeyValue, printTable } from "../lib/output";
6
- import { apiClient } from "../services/api-client";
7
-
8
- const handleErrors = makeCommandErrorHandler();
9
-
10
- const idArg = Args.text({ name: "id" });
11
- const nameOption = Options.text("name");
12
- const slugOption = Options.text("slug");
13
-
14
- const listCommand = Command.make("list", {}, () =>
15
- Effect.gen(function* () {
16
- const api = yield* apiClient;
17
- const { items } = yield* api.projects.list({
18
- urlParams: { page: 1, limit: 1000 },
19
- });
20
-
21
- if (items.length === 0) {
22
- yield* Console.log("No projects found.");
23
- return;
24
- }
25
-
26
- yield* printTable(
27
- ["ID", "Name", "Slug", "Created"],
28
- items.map((project) => [project.id, project.name, project.slug, project.createdAt]),
29
- );
30
- }).pipe(handleErrors),
31
- );
32
-
33
- const createCommand = Command.make("create", { name: nameOption, slug: slugOption }, (opts) =>
34
- Effect.gen(function* () {
35
- const api = yield* apiClient;
36
- const project = yield* api.projects.create({
37
- payload: { name: opts.name, slug: opts.slug },
38
- });
39
- yield* printKeyValue([
40
- ["ID", project.id],
41
- ["Name", project.name],
42
- ["Slug", project.slug],
43
- ["Created", project.createdAt],
44
- ]);
45
- }).pipe(handleErrors),
46
- );
47
-
48
- const getCommand = Command.make("get", { id: idArg }, (opts) =>
49
- Effect.gen(function* () {
50
- const api = yield* apiClient;
51
- const project = yield* api.projects.get({ path: { id: opts.id } });
52
- yield* printKeyValue([
53
- ["ID", project.id],
54
- ["Name", project.name],
55
- ["Slug", project.slug],
56
- ["Created", project.createdAt],
57
- ]);
58
- }).pipe(handleErrors),
59
- );
60
-
61
- const renameCommand = Command.make("rename", { id: idArg, name: nameOption }, (opts) =>
62
- Effect.gen(function* () {
63
- const api = yield* apiClient;
64
- const project = yield* api.projects.rename({
65
- path: { id: opts.id },
66
- payload: { name: opts.name },
67
- });
68
- yield* Console.log(`Project renamed to "${project.name}".`);
69
- }).pipe(handleErrors),
70
- );
71
-
72
- const deleteCommand = Command.make("delete", { id: idArg }, (opts) =>
73
- Effect.gen(function* () {
74
- const api = yield* apiClient;
75
- yield* api.projects.delete({ path: { id: opts.id } });
76
- yield* Console.log(`Project ${opts.id} deleted.`);
77
- }).pipe(handleErrors),
78
- );
79
-
80
- export const projectsCommand = Command.make("projects", {}, () =>
81
- Console.log("Manage projects. Run with --help for subcommands."),
82
- ).pipe(
83
- Command.withSubcommands([listCommand, createCommand, getCommand, renameCommand, deleteCommand]),
84
- );
@@ -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 { listAllCredentials } from "../lib/credentials-manager";
6
- import { printKeyValue } from "../lib/output";
7
- import { apiClient } from "../services/api-client";
8
-
9
- export const statusCommand = Command.make("status", {}, () =>
10
- Effect.gen(function* () {
11
- const projectId = yield* readProjectId;
12
- const api = yield* apiClient;
13
-
14
- const { project, credentials, builds } = yield* Effect.all(
15
- {
16
- project: api.projects.get({ path: { id: projectId } }),
17
- credentials: listAllCredentials(api),
18
- builds: api.builds.list({ urlParams: { projectId } }),
19
- },
20
- { concurrency: "unbounded" },
21
- );
22
-
23
- yield* Console.log("Project");
24
- yield* Console.log("-------");
25
- yield* printKeyValue([
26
- ["Name", project.name],
27
- ["ID", project.id],
28
- ["Slug", project.slug],
29
- ["Created", project.createdAt],
30
- ]);
31
-
32
- yield* Console.log("");
33
- yield* Console.log("Credentials");
34
- yield* Console.log("-----------");
35
- const iosCreds = credentials.filter((cred) => cred.platform === "ios").length;
36
- const androidCreds = credentials.filter((cred) => cred.platform === "android").length;
37
- yield* printKeyValue([
38
- ["iOS", String(iosCreds)],
39
- ["Android", String(androidCreds)],
40
- ["Total", String(credentials.length)],
41
- ]);
42
-
43
- yield* Console.log("");
44
- yield* Console.log("Builds");
45
- yield* Console.log("------");
46
- yield* printKeyValue([["Total", String(builds.total)]]);
47
- }),
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 { handleUpdateCommandErrors } from "./helpers";
6
-
7
- const groupId = Args.text({ name: "groupId" });
8
-
9
- export const deleteCommand = Command.make("delete", { groupId }, (opts) =>
10
- Effect.gen(function* () {
11
- const api = yield* apiClient;
12
- const result = yield* api.updates.deleteGroup({ path: { groupId: opts.groupId } });
13
- yield* Console.log(`Deleted ${String(result.deleted)} update(s) from group ${opts.groupId}.`);
14
- }).pipe(handleUpdateCommandErrors),
15
- );