@better-update/cli 0.6.2 → 0.6.4
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.
- package/dist/index.mjs +2105 -1418
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { Command as Command$1, FetchHttpClient, FileSystem, HttpApi, HttpApiClient, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, HttpApiSchema, HttpApiSecurity, HttpClient, HttpClientRequest, OpenApi } from "@effect/platform";
|
|
3
|
+
import { defineCommand, runMain } from "citty";
|
|
4
|
+
import { Console, Context, Data, Deferred, Duration, Effect, Fiber, Layer, Match, Option, ParseResult, Schema, Stream } from "effect";
|
|
5
|
+
import { Command, FetchHttpClient, FileSystem, HttpApi, HttpApiClient, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, HttpApiSchema, HttpApiSecurity, HttpClient, HttpClientRequest, OpenApi } from "@effect/platform";
|
|
6
|
+
import { NodeContext } from "@effect/platform-node";
|
|
8
7
|
import path from "node:path";
|
|
8
|
+
import process$1 from "node:process";
|
|
9
9
|
import { maxBy, uniqBy } from "es-toolkit";
|
|
10
10
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
11
11
|
import { createReadStream } from "node:fs";
|
|
@@ -14,10 +14,92 @@ import plist from "@expo/plist";
|
|
|
14
14
|
import { ExpoRunFormatter } from "@expo/xcpretty";
|
|
15
15
|
import { getFormattedSerialNumber, getX509Certificate, parsePKCS12 } from "@expo/pkcs12";
|
|
16
16
|
import { createServer } from "node:http";
|
|
17
|
+
import { cancel, isCancel, password } from "@clack/prompts";
|
|
17
18
|
|
|
18
19
|
//#region \0rolldown/runtime.js
|
|
19
20
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
20
21
|
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region package.json
|
|
24
|
+
var version = "0.6.4";
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/lib/exit-codes.ts
|
|
28
|
+
var AuthRequiredError = class extends Data.TaggedError("AuthRequiredError") {};
|
|
29
|
+
var ProjectNotLinkedError = class extends Data.TaggedError("ProjectNotLinkedError") {};
|
|
30
|
+
var UploadFailedError = class extends Data.TaggedError("UploadFailedError") {};
|
|
31
|
+
var BuildProfileError = class extends Data.TaggedError("BuildProfileError") {};
|
|
32
|
+
var RuntimeVersionError = class extends Data.TaggedError("RuntimeVersionError") {};
|
|
33
|
+
var MissingCredentialsError = class extends Data.TaggedError("MissingCredentialsError") {};
|
|
34
|
+
var BuildFailedError = class extends Data.TaggedError("BuildFailedError") {};
|
|
35
|
+
var ReserveError = class extends Data.TaggedError("ReserveError") {};
|
|
36
|
+
var CompleteError = class extends Data.TaggedError("CompleteError") {};
|
|
37
|
+
var PresignedUrlExpiredError = class extends Data.TaggedError("PresignedUrlExpiredError") {};
|
|
38
|
+
var ArtifactNotFoundError = class extends Data.TaggedError("ArtifactNotFoundError") {};
|
|
39
|
+
var KeychainError = class extends Data.TaggedError("KeychainError") {};
|
|
40
|
+
var ProvisioningError = class extends Data.TaggedError("ProvisioningError") {};
|
|
41
|
+
var EnvExportError = class extends Data.TaggedError("EnvExportError") {};
|
|
42
|
+
var UpdatePublishError = class extends Data.TaggedError("UpdatePublishError") {};
|
|
43
|
+
var UpdateRollbackError = class extends Data.TaggedError("UpdateRollbackError") {};
|
|
44
|
+
var UpdatePromoteError = class extends Data.TaggedError("UpdatePromoteError") {};
|
|
45
|
+
var CredentialValidationError = class extends Data.TaggedError("CredentialValidationError") {};
|
|
46
|
+
var AppleAuthError = class extends Data.TaggedError("AppleAuthError") {};
|
|
47
|
+
var InvalidArgumentError = class extends Data.TaggedError("InvalidArgumentError") {};
|
|
48
|
+
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/lib/format-error.ts
|
|
51
|
+
const formatCause = (cause) => {
|
|
52
|
+
if (cause instanceof Error) return cause.message;
|
|
53
|
+
if (typeof cause === "object" && cause !== null) {
|
|
54
|
+
const tagged = cause;
|
|
55
|
+
const tag = typeof tagged._tag === "string" ? tagged._tag : void 0;
|
|
56
|
+
const message = typeof tagged.message === "string" ? tagged.message : void 0;
|
|
57
|
+
if (tag && message) return `${tag}: ${message}`;
|
|
58
|
+
if (message) return message;
|
|
59
|
+
if (tag) return tag;
|
|
60
|
+
}
|
|
61
|
+
return String(cause);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/lib/record.ts
|
|
66
|
+
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
67
|
+
const asRecord = (value) => isRecord(value) ? value : void 0;
|
|
68
|
+
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region src/lib/app-json.ts
|
|
71
|
+
const readAppJson = Effect.gen(function* () {
|
|
72
|
+
const content = yield* (yield* FileSystem.FileSystem).readFileString("./app.json").pipe(Effect.mapError(() => new ProjectNotLinkedError({ message: "app.json not found in current directory" })));
|
|
73
|
+
const parsed = yield* Effect.try({
|
|
74
|
+
try: () => JSON.parse(content),
|
|
75
|
+
catch: () => new ProjectNotLinkedError({ message: "app.json contains malformed JSON" })
|
|
76
|
+
});
|
|
77
|
+
if (!isRecord(parsed)) return yield* new ProjectNotLinkedError({ message: "app.json must be a JSON object" });
|
|
78
|
+
return parsed;
|
|
79
|
+
});
|
|
80
|
+
const readProjectId = Effect.gen(function* () {
|
|
81
|
+
const projectId = asRecord(asRecord(asRecord((yield* readAppJson)["expo"])?.["extra"])?.["betterUpdate"])?.["projectId"];
|
|
82
|
+
if (typeof projectId !== "string") return yield* new ProjectNotLinkedError({ message: "Project not linked. Run `better-update link` to connect this project, or set expo.extra.betterUpdate.projectId in app.json." });
|
|
83
|
+
return projectId;
|
|
84
|
+
});
|
|
85
|
+
const readSlug = Effect.gen(function* () {
|
|
86
|
+
const slug = asRecord((yield* readAppJson)["expo"])?.["slug"];
|
|
87
|
+
if (typeof slug !== "string") return yield* new ProjectNotLinkedError({ message: "Missing expo.slug in app.json. Required to identify the project." });
|
|
88
|
+
return slug;
|
|
89
|
+
});
|
|
90
|
+
const writeProjectId = (id) => Effect.gen(function* () {
|
|
91
|
+
const fs = yield* FileSystem.FileSystem;
|
|
92
|
+
const appJson = yield* readAppJson;
|
|
93
|
+
const expo = asRecord(appJson["expo"]) ?? {};
|
|
94
|
+
const extra = asRecord(expo["extra"]) ?? {};
|
|
95
|
+
const betterUpdate = asRecord(extra["betterUpdate"]) ?? {};
|
|
96
|
+
betterUpdate["projectId"] = id;
|
|
97
|
+
extra["betterUpdate"] = betterUpdate;
|
|
98
|
+
expo["extra"] = extra;
|
|
99
|
+
appJson["expo"] = expo;
|
|
100
|
+
yield* fs.writeFileString("./app.json", `${JSON.stringify(appJson, null, 2)}\n`);
|
|
101
|
+
}).pipe(Effect.mapError((cause) => cause instanceof ProjectNotLinkedError ? cause : new ProjectNotLinkedError({ message: `Failed to write project ID to app.json: ${formatCause(cause)}` })));
|
|
102
|
+
|
|
21
103
|
//#endregion
|
|
22
104
|
//#region ../../packages/api/src/auth/context.ts
|
|
23
105
|
var AuthContext = class extends Context.Tag("api/AuthContext")() {};
|
|
@@ -1363,65 +1445,23 @@ var ProtocolApi = class extends HttpApi.make("protocol-api").add(ManifestGroup).
|
|
|
1363
1445
|
description: "Expo Updates protocol endpoints (unauthenticated)"
|
|
1364
1446
|
})) {};
|
|
1365
1447
|
|
|
1366
|
-
//#endregion
|
|
1367
|
-
//#region src/lib/exit-codes.ts
|
|
1368
|
-
var AuthRequiredError = class extends Data.TaggedError("AuthRequiredError") {};
|
|
1369
|
-
var ProjectNotLinkedError = class extends Data.TaggedError("ProjectNotLinkedError") {};
|
|
1370
|
-
var UploadFailedError = class extends Data.TaggedError("UploadFailedError") {};
|
|
1371
|
-
var BuildProfileError = class extends Data.TaggedError("BuildProfileError") {};
|
|
1372
|
-
var RuntimeVersionError = class extends Data.TaggedError("RuntimeVersionError") {};
|
|
1373
|
-
var MissingCredentialsError = class extends Data.TaggedError("MissingCredentialsError") {};
|
|
1374
|
-
var BuildFailedError = class extends Data.TaggedError("BuildFailedError") {};
|
|
1375
|
-
var ReserveError = class extends Data.TaggedError("ReserveError") {};
|
|
1376
|
-
var CompleteError = class extends Data.TaggedError("CompleteError") {};
|
|
1377
|
-
var PresignedUrlExpiredError = class extends Data.TaggedError("PresignedUrlExpiredError") {};
|
|
1378
|
-
var ArtifactNotFoundError = class extends Data.TaggedError("ArtifactNotFoundError") {};
|
|
1379
|
-
var KeychainError = class extends Data.TaggedError("KeychainError") {};
|
|
1380
|
-
var ProvisioningError = class extends Data.TaggedError("ProvisioningError") {};
|
|
1381
|
-
var EnvExportError = class extends Data.TaggedError("EnvExportError") {};
|
|
1382
|
-
var UpdatePublishError = class extends Data.TaggedError("UpdatePublishError") {};
|
|
1383
|
-
var UpdateRollbackError = class extends Data.TaggedError("UpdateRollbackError") {};
|
|
1384
|
-
var UpdatePromoteError = class extends Data.TaggedError("UpdatePromoteError") {};
|
|
1385
|
-
var CredentialValidationError = class extends Data.TaggedError("CredentialValidationError") {};
|
|
1386
|
-
var AppleAuthError = class extends Data.TaggedError("AppleAuthError") {};
|
|
1387
|
-
|
|
1388
|
-
//#endregion
|
|
1389
|
-
//#region src/lib/format-error.ts
|
|
1390
|
-
const formatCause = (cause) => {
|
|
1391
|
-
if (cause instanceof Error) return cause.message;
|
|
1392
|
-
if (typeof cause === "object" && cause !== null) {
|
|
1393
|
-
const tagged = cause;
|
|
1394
|
-
const tag = typeof tagged._tag === "string" ? tagged._tag : void 0;
|
|
1395
|
-
const message = typeof tagged.message === "string" ? tagged.message : void 0;
|
|
1396
|
-
if (tag && message) return `${tag}: ${message}`;
|
|
1397
|
-
if (message) return message;
|
|
1398
|
-
if (tag) return tag;
|
|
1399
|
-
}
|
|
1400
|
-
return String(cause);
|
|
1401
|
-
};
|
|
1402
|
-
|
|
1403
|
-
//#endregion
|
|
1404
|
-
//#region src/lib/record.ts
|
|
1405
|
-
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1406
|
-
const asRecord = (value) => isRecord(value) ? value : void 0;
|
|
1407
|
-
|
|
1408
1448
|
//#endregion
|
|
1409
1449
|
//#region src/services/cli-runtime.ts
|
|
1410
|
-
const definedEnvironment = () => Object.fromEntries(Object.entries(process.env).flatMap(([key, value]) => typeof value === "string" ? [[key, value]] : []));
|
|
1450
|
+
const definedEnvironment = () => Object.fromEntries(Object.entries(process$1.env).flatMap(([key, value]) => typeof value === "string" ? [[key, value]] : []));
|
|
1411
1451
|
var CliRuntime = class extends Context.Tag("cli/CliRuntime")() {};
|
|
1412
1452
|
const CliRuntimeLive = Layer.succeed(CliRuntime, {
|
|
1413
|
-
argv: [...process.argv],
|
|
1414
|
-
platform: process.platform,
|
|
1415
|
-
cwd: Effect.sync(() => process.cwd()),
|
|
1416
|
-
getEnv: (name) => Effect.sync(() => process.env[name]),
|
|
1417
|
-
homeDirectory: Effect.sync(() => process.env["HOME"] ?? process.env["USERPROFILE"] ?? process.cwd()),
|
|
1418
|
-
userName: Effect.sync(() => process.env["USER"] ?? process.env["USERNAME"] ?? "better-update"),
|
|
1453
|
+
argv: [...process$1.argv],
|
|
1454
|
+
platform: process$1.platform,
|
|
1455
|
+
cwd: Effect.sync(() => process$1.cwd()),
|
|
1456
|
+
getEnv: (name) => Effect.sync(() => process$1.env[name]),
|
|
1457
|
+
homeDirectory: Effect.sync(() => process$1.env["HOME"] ?? process$1.env["USERPROFILE"] ?? process$1.cwd()),
|
|
1458
|
+
userName: Effect.sync(() => process$1.env["USER"] ?? process$1.env["USERNAME"] ?? "better-update"),
|
|
1419
1459
|
commandEnvironment: (overrides = {}) => Effect.sync(() => ({
|
|
1420
1460
|
...definedEnvironment(),
|
|
1421
1461
|
...overrides
|
|
1422
1462
|
})),
|
|
1423
1463
|
setExitCode: (code) => Effect.sync(() => {
|
|
1424
|
-
process.exitCode = code;
|
|
1464
|
+
process$1.exitCode = code;
|
|
1425
1465
|
})
|
|
1426
1466
|
});
|
|
1427
1467
|
|
|
@@ -1608,55 +1648,6 @@ const PresignedUploadLayer = PresignedUploadClientLive.pipe(Layer.provide(CliPla
|
|
|
1608
1648
|
const UpdateAssetUploaderLayer = UpdateAssetUploaderLive.pipe(Layer.provide(Layer.mergeAll(ApiClientLayer, PresignedUploadLayer)));
|
|
1609
1649
|
const CliLive = Layer.mergeAll(CliAdapterDependencies, ApiClientLayer, PresignedUploadLayer, UpdateAssetUploaderLayer);
|
|
1610
1650
|
|
|
1611
|
-
//#endregion
|
|
1612
|
-
//#region src/lib/app-json.ts
|
|
1613
|
-
const readAppJson = Effect.gen(function* () {
|
|
1614
|
-
const content = yield* (yield* FileSystem.FileSystem).readFileString("./app.json").pipe(Effect.mapError(() => new ProjectNotLinkedError({ message: "app.json not found in current directory" })));
|
|
1615
|
-
const parsed = yield* Effect.try({
|
|
1616
|
-
try: () => JSON.parse(content),
|
|
1617
|
-
catch: () => new ProjectNotLinkedError({ message: "app.json contains malformed JSON" })
|
|
1618
|
-
});
|
|
1619
|
-
if (!isRecord(parsed)) return yield* new ProjectNotLinkedError({ message: "app.json must be a JSON object" });
|
|
1620
|
-
return parsed;
|
|
1621
|
-
});
|
|
1622
|
-
const readProjectId = Effect.gen(function* () {
|
|
1623
|
-
const projectId = asRecord(asRecord(asRecord((yield* readAppJson)["expo"])?.["extra"])?.["betterUpdate"])?.["projectId"];
|
|
1624
|
-
if (typeof projectId !== "string") return yield* new ProjectNotLinkedError({ message: "Project not linked. Run `better-update link` to connect this project, or set expo.extra.betterUpdate.projectId in app.json." });
|
|
1625
|
-
return projectId;
|
|
1626
|
-
});
|
|
1627
|
-
const readSlug = Effect.gen(function* () {
|
|
1628
|
-
const slug = asRecord((yield* readAppJson)["expo"])?.["slug"];
|
|
1629
|
-
if (typeof slug !== "string") return yield* new ProjectNotLinkedError({ message: "Missing expo.slug in app.json. Required to identify the project." });
|
|
1630
|
-
return slug;
|
|
1631
|
-
});
|
|
1632
|
-
const writeProjectId = (id) => Effect.gen(function* () {
|
|
1633
|
-
const fs = yield* FileSystem.FileSystem;
|
|
1634
|
-
const appJson = yield* readAppJson;
|
|
1635
|
-
const expo = asRecord(appJson["expo"]) ?? {};
|
|
1636
|
-
const extra = asRecord(expo["extra"]) ?? {};
|
|
1637
|
-
const betterUpdate = asRecord(extra["betterUpdate"]) ?? {};
|
|
1638
|
-
betterUpdate["projectId"] = id;
|
|
1639
|
-
extra["betterUpdate"] = betterUpdate;
|
|
1640
|
-
expo["extra"] = extra;
|
|
1641
|
-
appJson["expo"] = expo;
|
|
1642
|
-
yield* fs.writeFileString("./app.json", `${JSON.stringify(appJson, null, 2)}\n`);
|
|
1643
|
-
}).pipe(Effect.mapError((cause) => cause instanceof ProjectNotLinkedError ? cause : new ProjectNotLinkedError({ message: `Failed to write project ID to app.json: ${formatCause(cause)}` })));
|
|
1644
|
-
|
|
1645
|
-
//#endregion
|
|
1646
|
-
//#region src/lib/output.ts
|
|
1647
|
-
const printTable = (headers, rows) => Effect.gen(function* () {
|
|
1648
|
-
const allRows = [headers, ...rows];
|
|
1649
|
-
const colWidths = headers.map((_, colIndex) => Math.max(...allRows.map((row) => (row[colIndex] ?? "").length)));
|
|
1650
|
-
const formatRow = (row) => row.map((cell, idx) => cell.padEnd(colWidths[idx] ?? 0)).join(" ");
|
|
1651
|
-
yield* Console.log(formatRow(headers));
|
|
1652
|
-
yield* Console.log(colWidths.map((width) => "-".repeat(width)).join(" "));
|
|
1653
|
-
for (const row of rows) yield* Console.log(formatRow(row));
|
|
1654
|
-
});
|
|
1655
|
-
const printKeyValue = (pairs) => Effect.gen(function* () {
|
|
1656
|
-
const maxKeyLen = Math.max(...pairs.map(([key]) => key.length));
|
|
1657
|
-
for (const [key, value] of pairs) yield* Console.log(`${key.padEnd(maxKeyLen)} ${value}`);
|
|
1658
|
-
});
|
|
1659
|
-
|
|
1660
1651
|
//#endregion
|
|
1661
1652
|
//#region src/application/command-exit.ts
|
|
1662
1653
|
const exitWith = (code, message) => Console.error(message).pipe(Effect.zipRight(Effect.gen(function* () {
|
|
@@ -1671,7 +1662,8 @@ const BASE_TAG_MAP = {
|
|
|
1671
1662
|
NotFound: 1,
|
|
1672
1663
|
Conflict: 1,
|
|
1673
1664
|
Forbidden: 1,
|
|
1674
|
-
BadRequest: 2
|
|
1665
|
+
BadRequest: 2,
|
|
1666
|
+
InvalidArgumentError: 2
|
|
1675
1667
|
};
|
|
1676
1668
|
const SYSTEM_TAG_MESSAGE = {
|
|
1677
1669
|
SystemError: (error) => `Filesystem error: ${error.message}`,
|
|
@@ -1698,287 +1690,394 @@ const makeCommandErrorHandler = (extras = {}) => {
|
|
|
1698
1690
|
};
|
|
1699
1691
|
|
|
1700
1692
|
//#endregion
|
|
1701
|
-
//#region src/
|
|
1702
|
-
const
|
|
1693
|
+
//#region src/lib/citty-effect.ts
|
|
1694
|
+
const runEffect = async (effect, extras = {}) => {
|
|
1695
|
+
const provided = makeCommandErrorHandler(extras)(effect).pipe(Effect.provide(CliLive));
|
|
1696
|
+
return Effect.runPromise(provided.pipe(Effect.asVoid));
|
|
1697
|
+
};
|
|
1698
|
+
|
|
1699
|
+
//#endregion
|
|
1700
|
+
//#region src/lib/output.ts
|
|
1701
|
+
const printTable = (headers, rows) => Effect.gen(function* () {
|
|
1702
|
+
const allRows = [headers, ...rows];
|
|
1703
|
+
const colWidths = headers.map((_, colIndex) => Math.max(...allRows.map((row) => (row[colIndex] ?? "").length)));
|
|
1704
|
+
const formatRow = (row) => row.map((cell, idx) => cell.padEnd(colWidths[idx] ?? 0)).join(" ");
|
|
1705
|
+
yield* Console.log(formatRow(headers));
|
|
1706
|
+
yield* Console.log(colWidths.map((width) => "-".repeat(width)).join(" "));
|
|
1707
|
+
for (const row of rows) yield* Console.log(formatRow(row));
|
|
1708
|
+
});
|
|
1709
|
+
const printKeyValue = (pairs) => Effect.gen(function* () {
|
|
1710
|
+
const maxKeyLen = Math.max(...pairs.map(([key]) => key.length));
|
|
1711
|
+
for (const [key, value] of pairs) yield* Console.log(`${key.padEnd(maxKeyLen)} ${value}`);
|
|
1712
|
+
});
|
|
1703
1713
|
|
|
1704
1714
|
//#endregion
|
|
1705
1715
|
//#region src/commands/analytics/adoption.ts
|
|
1706
|
-
const
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1716
|
+
const adoptionCommand = defineCommand({
|
|
1717
|
+
meta: {
|
|
1718
|
+
name: "adoption",
|
|
1719
|
+
description: "Show update adoption across devices"
|
|
1720
|
+
},
|
|
1721
|
+
args: { period: {
|
|
1722
|
+
type: "enum",
|
|
1723
|
+
options: [
|
|
1724
|
+
"1d",
|
|
1725
|
+
"7d",
|
|
1726
|
+
"30d",
|
|
1727
|
+
"90d"
|
|
1728
|
+
],
|
|
1729
|
+
description: "Time window"
|
|
1730
|
+
} },
|
|
1731
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
1732
|
+
const projectId = yield* readProjectId;
|
|
1733
|
+
const api = yield* apiClient;
|
|
1734
|
+
const periodFilter = args.period ? { period: args.period } : {};
|
|
1735
|
+
const result = yield* api.analytics.adoption({ urlParams: {
|
|
1736
|
+
projectId,
|
|
1737
|
+
...periodFilter
|
|
1738
|
+
} });
|
|
1739
|
+
if (result.updates.length === 0) {
|
|
1740
|
+
yield* Console.log("No adoption data found.");
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
yield* printTable([
|
|
1744
|
+
"Update ID",
|
|
1745
|
+
"Devices",
|
|
1746
|
+
"First Seen",
|
|
1747
|
+
"Last Seen"
|
|
1748
|
+
], result.updates.map((update) => [
|
|
1749
|
+
update.updateId,
|
|
1750
|
+
String(update.devices),
|
|
1751
|
+
update.firstSeen,
|
|
1752
|
+
update.lastSeen
|
|
1753
|
+
]));
|
|
1754
|
+
}))
|
|
1755
|
+
});
|
|
1739
1756
|
|
|
1740
1757
|
//#endregion
|
|
1741
1758
|
//#region src/commands/analytics/channels.ts
|
|
1742
|
-
const
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
},
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
})
|
|
1759
|
+
const channelsCommand$1 = defineCommand({
|
|
1760
|
+
meta: {
|
|
1761
|
+
name: "channels",
|
|
1762
|
+
description: "Stats for a specific channel"
|
|
1763
|
+
},
|
|
1764
|
+
args: {
|
|
1765
|
+
channel: {
|
|
1766
|
+
type: "string",
|
|
1767
|
+
required: true,
|
|
1768
|
+
description: "Channel name"
|
|
1769
|
+
},
|
|
1770
|
+
period: {
|
|
1771
|
+
type: "enum",
|
|
1772
|
+
options: [
|
|
1773
|
+
"1d",
|
|
1774
|
+
"7d",
|
|
1775
|
+
"30d",
|
|
1776
|
+
"90d"
|
|
1777
|
+
],
|
|
1778
|
+
description: "Time window"
|
|
1779
|
+
}
|
|
1780
|
+
},
|
|
1781
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
1782
|
+
const projectId = yield* readProjectId;
|
|
1783
|
+
const api = yield* apiClient;
|
|
1784
|
+
const periodFilter = args.period ? { period: args.period } : {};
|
|
1785
|
+
const result = yield* api.analytics.channels({ urlParams: {
|
|
1786
|
+
projectId,
|
|
1787
|
+
channel: args.channel,
|
|
1788
|
+
...periodFilter
|
|
1789
|
+
} });
|
|
1790
|
+
yield* printKeyValue([
|
|
1791
|
+
["Channel", result.channel],
|
|
1792
|
+
["Total Requests", String(result.totalRequests)],
|
|
1793
|
+
["Unique Devices", String(result.uniqueDevices)],
|
|
1794
|
+
["Manifest", String(result.responseTypeDistribution.manifest)],
|
|
1795
|
+
["Directive", String(result.responseTypeDistribution.directive)],
|
|
1796
|
+
["No Update", String(result.responseTypeDistribution.no_update)]
|
|
1797
|
+
]);
|
|
1798
|
+
}))
|
|
1799
|
+
});
|
|
1773
1800
|
|
|
1774
1801
|
//#endregion
|
|
1775
1802
|
//#region src/commands/analytics/platforms.ts
|
|
1776
|
-
const
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1803
|
+
const platformsCommand = defineCommand({
|
|
1804
|
+
meta: {
|
|
1805
|
+
name: "platforms",
|
|
1806
|
+
description: "Stats by platform"
|
|
1807
|
+
},
|
|
1808
|
+
args: { period: {
|
|
1809
|
+
type: "enum",
|
|
1810
|
+
options: [
|
|
1811
|
+
"1d",
|
|
1812
|
+
"7d",
|
|
1813
|
+
"30d",
|
|
1814
|
+
"90d"
|
|
1815
|
+
],
|
|
1816
|
+
description: "Time window"
|
|
1817
|
+
} },
|
|
1818
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
1819
|
+
const projectId = yield* readProjectId;
|
|
1820
|
+
const api = yield* apiClient;
|
|
1821
|
+
const periodFilter = args.period ? { period: args.period } : {};
|
|
1822
|
+
const result = yield* api.analytics.platforms({ urlParams: {
|
|
1823
|
+
projectId,
|
|
1824
|
+
...periodFilter
|
|
1825
|
+
} });
|
|
1826
|
+
if (result.platforms.length === 0) {
|
|
1827
|
+
yield* Console.log("No platform data found.");
|
|
1828
|
+
return;
|
|
1829
|
+
}
|
|
1830
|
+
yield* printTable([
|
|
1831
|
+
"Platform",
|
|
1832
|
+
"Requests",
|
|
1833
|
+
"Devices"
|
|
1834
|
+
], result.platforms.map((platform) => [
|
|
1835
|
+
platform.platform,
|
|
1836
|
+
String(platform.requests),
|
|
1837
|
+
String(platform.devices)
|
|
1838
|
+
]));
|
|
1839
|
+
}))
|
|
1840
|
+
});
|
|
1807
1841
|
|
|
1808
1842
|
//#endregion
|
|
1809
1843
|
//#region src/commands/analytics/updates.ts
|
|
1810
|
-
const
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
},
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
}
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
})
|
|
1844
|
+
const updatesCommand = defineCommand({
|
|
1845
|
+
meta: {
|
|
1846
|
+
name: "updates",
|
|
1847
|
+
description: "Stats for a specific update"
|
|
1848
|
+
},
|
|
1849
|
+
args: {
|
|
1850
|
+
"update-id": {
|
|
1851
|
+
type: "string",
|
|
1852
|
+
required: true,
|
|
1853
|
+
description: "Update ID"
|
|
1854
|
+
},
|
|
1855
|
+
period: {
|
|
1856
|
+
type: "enum",
|
|
1857
|
+
options: [
|
|
1858
|
+
"1d",
|
|
1859
|
+
"7d",
|
|
1860
|
+
"30d",
|
|
1861
|
+
"90d"
|
|
1862
|
+
],
|
|
1863
|
+
description: "Time window"
|
|
1864
|
+
}
|
|
1865
|
+
},
|
|
1866
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
1867
|
+
const projectId = yield* readProjectId;
|
|
1868
|
+
const api = yield* apiClient;
|
|
1869
|
+
const periodFilter = args.period ? { period: args.period } : {};
|
|
1870
|
+
const result = yield* api.analytics.updates({ urlParams: {
|
|
1871
|
+
projectId,
|
|
1872
|
+
updateId: args["update-id"],
|
|
1873
|
+
...periodFilter
|
|
1874
|
+
} });
|
|
1875
|
+
yield* printKeyValue([
|
|
1876
|
+
["Update ID", result.updateId],
|
|
1877
|
+
["Total Requests", String(result.totalRequests)],
|
|
1878
|
+
["Unique Devices", String(result.uniqueDevices)],
|
|
1879
|
+
["Manifest", String(result.byResponseType.manifest)],
|
|
1880
|
+
["Directive", String(result.byResponseType.directive)],
|
|
1881
|
+
["No Update", String(result.byResponseType.no_update)]
|
|
1882
|
+
]);
|
|
1883
|
+
}))
|
|
1884
|
+
});
|
|
1841
1885
|
|
|
1842
1886
|
//#endregion
|
|
1843
1887
|
//#region src/commands/analytics/index.ts
|
|
1844
|
-
const analyticsCommand =
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1888
|
+
const analyticsCommand = defineCommand({
|
|
1889
|
+
meta: {
|
|
1890
|
+
name: "analytics",
|
|
1891
|
+
description: "View deployment analytics"
|
|
1892
|
+
},
|
|
1893
|
+
subCommands: {
|
|
1894
|
+
adoption: adoptionCommand,
|
|
1895
|
+
updates: updatesCommand,
|
|
1896
|
+
channels: channelsCommand$1,
|
|
1897
|
+
platforms: platformsCommand
|
|
1898
|
+
}
|
|
1899
|
+
});
|
|
1854
1900
|
|
|
1855
1901
|
//#endregion
|
|
1856
1902
|
//#region src/commands/audit-logs/list.ts
|
|
1857
|
-
const
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
}
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
}
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
}
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
})
|
|
1903
|
+
const listCommand$7 = defineCommand({
|
|
1904
|
+
meta: {
|
|
1905
|
+
name: "list",
|
|
1906
|
+
description: "List audit log entries"
|
|
1907
|
+
},
|
|
1908
|
+
args: {
|
|
1909
|
+
action: {
|
|
1910
|
+
type: "string",
|
|
1911
|
+
description: "Filter by action"
|
|
1912
|
+
},
|
|
1913
|
+
"resource-type": {
|
|
1914
|
+
type: "string",
|
|
1915
|
+
description: "Filter by resource type"
|
|
1916
|
+
},
|
|
1917
|
+
"actor-id": {
|
|
1918
|
+
type: "string",
|
|
1919
|
+
description: "Filter by actor ID"
|
|
1920
|
+
},
|
|
1921
|
+
from: {
|
|
1922
|
+
type: "string",
|
|
1923
|
+
description: "ISO timestamp lower bound"
|
|
1924
|
+
},
|
|
1925
|
+
to: {
|
|
1926
|
+
type: "string",
|
|
1927
|
+
description: "ISO timestamp upper bound"
|
|
1928
|
+
}
|
|
1929
|
+
},
|
|
1930
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
1931
|
+
const api = yield* apiClient;
|
|
1932
|
+
const filters = {};
|
|
1933
|
+
if (args.action) filters["action"] = args.action;
|
|
1934
|
+
if (args["resource-type"]) filters["resourceType"] = args["resource-type"];
|
|
1935
|
+
if (args["actor-id"]) filters["actorId"] = args["actor-id"];
|
|
1936
|
+
if (args.from) filters["from"] = args.from;
|
|
1937
|
+
if (args.to) filters["to"] = args.to;
|
|
1938
|
+
const { items } = yield* api["audit-logs"].list({ urlParams: {
|
|
1939
|
+
...filters,
|
|
1940
|
+
page: 1,
|
|
1941
|
+
limit: 100
|
|
1942
|
+
} });
|
|
1943
|
+
if (items.length === 0) {
|
|
1944
|
+
yield* Console.log("No audit log entries found.");
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
yield* printTable([
|
|
1948
|
+
"ID",
|
|
1949
|
+
"Action",
|
|
1950
|
+
"Resource Type",
|
|
1951
|
+
"Resource ID",
|
|
1952
|
+
"Actor",
|
|
1953
|
+
"Source",
|
|
1954
|
+
"Created"
|
|
1955
|
+
], items.map((log) => [
|
|
1956
|
+
log.id,
|
|
1957
|
+
log.action,
|
|
1958
|
+
log.resourceType,
|
|
1959
|
+
log.resourceId ?? "-",
|
|
1960
|
+
log.actorEmail,
|
|
1961
|
+
log.source,
|
|
1962
|
+
log.createdAt
|
|
1963
|
+
]));
|
|
1964
|
+
}))
|
|
1965
|
+
});
|
|
1919
1966
|
|
|
1920
1967
|
//#endregion
|
|
1921
1968
|
//#region src/commands/audit-logs/index.ts
|
|
1922
|
-
const auditLogsCommand =
|
|
1969
|
+
const auditLogsCommand = defineCommand({
|
|
1970
|
+
meta: {
|
|
1971
|
+
name: "audit-logs",
|
|
1972
|
+
description: "View audit logs"
|
|
1973
|
+
},
|
|
1974
|
+
subCommands: { list: listCommand$7 }
|
|
1975
|
+
});
|
|
1923
1976
|
|
|
1924
1977
|
//#endregion
|
|
1925
1978
|
//#region src/commands/branches.ts
|
|
1926
|
-
const
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
projectId
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1979
|
+
const listCommand$6 = defineCommand({
|
|
1980
|
+
meta: {
|
|
1981
|
+
name: "list",
|
|
1982
|
+
description: "List branches for the linked project"
|
|
1983
|
+
},
|
|
1984
|
+
run: async () => runEffect(Effect.gen(function* () {
|
|
1985
|
+
const projectId = yield* readProjectId;
|
|
1986
|
+
const { items } = yield* (yield* apiClient).branches.list({ urlParams: {
|
|
1987
|
+
projectId,
|
|
1988
|
+
page: 1,
|
|
1989
|
+
limit: 1e3
|
|
1990
|
+
} });
|
|
1991
|
+
if (items.length === 0) {
|
|
1992
|
+
yield* Console.log("No branches found.");
|
|
1993
|
+
return;
|
|
1994
|
+
}
|
|
1995
|
+
yield* printTable([
|
|
1996
|
+
"ID",
|
|
1997
|
+
"Name",
|
|
1998
|
+
"Created"
|
|
1999
|
+
], items.map((branch) => [
|
|
2000
|
+
branch.id,
|
|
2001
|
+
branch.name,
|
|
2002
|
+
branch.createdAt
|
|
2003
|
+
]));
|
|
2004
|
+
}))
|
|
2005
|
+
});
|
|
2006
|
+
const createCommand$3 = defineCommand({
|
|
2007
|
+
meta: {
|
|
2008
|
+
name: "create",
|
|
2009
|
+
description: "Create a branch"
|
|
2010
|
+
},
|
|
2011
|
+
args: { name: {
|
|
2012
|
+
type: "string",
|
|
2013
|
+
required: true,
|
|
2014
|
+
description: "Branch name"
|
|
2015
|
+
} },
|
|
2016
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
2017
|
+
const projectId = yield* readProjectId;
|
|
2018
|
+
const branch = yield* (yield* apiClient).branches.create({ payload: {
|
|
2019
|
+
projectId,
|
|
2020
|
+
name: args.name
|
|
2021
|
+
} });
|
|
2022
|
+
yield* printKeyValue([
|
|
2023
|
+
["ID", branch.id],
|
|
2024
|
+
["Name", branch.name],
|
|
2025
|
+
["Created", branch.createdAt]
|
|
2026
|
+
]);
|
|
2027
|
+
}))
|
|
2028
|
+
});
|
|
2029
|
+
const renameCommand$1 = defineCommand({
|
|
2030
|
+
meta: {
|
|
2031
|
+
name: "rename",
|
|
2032
|
+
description: "Rename a branch"
|
|
2033
|
+
},
|
|
2034
|
+
args: {
|
|
2035
|
+
id: {
|
|
2036
|
+
type: "positional",
|
|
2037
|
+
required: true,
|
|
2038
|
+
description: "Branch ID"
|
|
2039
|
+
},
|
|
2040
|
+
name: {
|
|
2041
|
+
type: "string",
|
|
2042
|
+
required: true,
|
|
2043
|
+
description: "New branch name"
|
|
2044
|
+
}
|
|
2045
|
+
},
|
|
2046
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
2047
|
+
const branch = yield* (yield* apiClient).branches.rename({
|
|
2048
|
+
path: { id: args.id },
|
|
2049
|
+
payload: { name: args.name }
|
|
2050
|
+
});
|
|
2051
|
+
yield* Console.log(`Branch renamed to "${branch.name}".`);
|
|
2052
|
+
}))
|
|
2053
|
+
});
|
|
2054
|
+
const deleteCommand$6 = defineCommand({
|
|
2055
|
+
meta: {
|
|
2056
|
+
name: "delete",
|
|
2057
|
+
description: "Delete a branch"
|
|
2058
|
+
},
|
|
2059
|
+
args: { id: {
|
|
2060
|
+
type: "positional",
|
|
2061
|
+
required: true,
|
|
2062
|
+
description: "Branch ID"
|
|
2063
|
+
} },
|
|
2064
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
2065
|
+
yield* (yield* apiClient).branches.delete({ path: { id: args.id } });
|
|
2066
|
+
yield* Console.log(`Branch ${args.id} deleted.`);
|
|
2067
|
+
}))
|
|
2068
|
+
});
|
|
2069
|
+
const branchesCommand = defineCommand({
|
|
2070
|
+
meta: {
|
|
2071
|
+
name: "branches",
|
|
2072
|
+
description: "Manage branches"
|
|
2073
|
+
},
|
|
2074
|
+
subCommands: {
|
|
2075
|
+
list: listCommand$6,
|
|
2076
|
+
create: createCommand$3,
|
|
2077
|
+
rename: renameCommand$1,
|
|
2078
|
+
delete: deleteCommand$6
|
|
1939
2079
|
}
|
|
1940
|
-
|
|
1941
|
-
"ID",
|
|
1942
|
-
"Name",
|
|
1943
|
-
"Created"
|
|
1944
|
-
], items.map((branch) => [
|
|
1945
|
-
branch.id,
|
|
1946
|
-
branch.name,
|
|
1947
|
-
branch.createdAt
|
|
1948
|
-
]));
|
|
1949
|
-
}).pipe(handleErrors$1));
|
|
1950
|
-
const createCommand$3 = Command.make("create", { name: nameOption$1 }, (opts) => Effect.gen(function* () {
|
|
1951
|
-
const projectId = yield* readProjectId;
|
|
1952
|
-
const branch = yield* (yield* apiClient).branches.create({ payload: {
|
|
1953
|
-
projectId,
|
|
1954
|
-
name: opts.name
|
|
1955
|
-
} });
|
|
1956
|
-
yield* printKeyValue([
|
|
1957
|
-
["ID", branch.id],
|
|
1958
|
-
["Name", branch.name],
|
|
1959
|
-
["Created", branch.createdAt]
|
|
1960
|
-
]);
|
|
1961
|
-
}).pipe(handleErrors$1));
|
|
1962
|
-
const renameCommand$1 = Command.make("rename", {
|
|
1963
|
-
id: idArg$1,
|
|
1964
|
-
name: nameOption$1
|
|
1965
|
-
}, (opts) => Effect.gen(function* () {
|
|
1966
|
-
const branch = yield* (yield* apiClient).branches.rename({
|
|
1967
|
-
path: { id: opts.id },
|
|
1968
|
-
payload: { name: opts.name }
|
|
1969
|
-
});
|
|
1970
|
-
yield* Console.log(`Branch renamed to "${branch.name}".`);
|
|
1971
|
-
}).pipe(handleErrors$1));
|
|
1972
|
-
const deleteCommand$6 = Command.make("delete", { id: idArg$1 }, (opts) => Effect.gen(function* () {
|
|
1973
|
-
yield* (yield* apiClient).branches.delete({ path: { id: opts.id } });
|
|
1974
|
-
yield* Console.log(`Branch ${opts.id} deleted.`);
|
|
1975
|
-
}).pipe(handleErrors$1));
|
|
1976
|
-
const branchesCommand = Command.make("branches", {}, () => Console.log("Manage branches. Run with --help for subcommands.")).pipe(Command.withSubcommands([
|
|
1977
|
-
listCommand$6,
|
|
1978
|
-
createCommand$3,
|
|
1979
|
-
renameCommand$1,
|
|
1980
|
-
deleteCommand$6
|
|
1981
|
-
]));
|
|
2080
|
+
});
|
|
1982
2081
|
|
|
1983
2082
|
//#endregion
|
|
1984
2083
|
//#region src/lib/android-signing-gradle.ts
|
|
@@ -2213,7 +2312,7 @@ const sha256Namespaced = (contentType, contentSha256Hex) => {
|
|
|
2213
2312
|
|
|
2214
2313
|
//#endregion
|
|
2215
2314
|
//#region src/commands/build/run-step.ts
|
|
2216
|
-
const runStep = (cmd, step) => Command
|
|
2315
|
+
const runStep = (cmd, step) => Command.exitCode(cmd.pipe(Command.stdout("inherit"), Command.stderr("inherit"))).pipe(Effect.mapError((cause) => new BuildFailedError({
|
|
2217
2316
|
step,
|
|
2218
2317
|
exitCode: 1,
|
|
2219
2318
|
message: `${step} failed to spawn: ${String(cause)}`
|
|
@@ -2227,7 +2326,7 @@ const runStep = (cmd, step) => Command$1.exitCode(cmd.pipe(Command$1.stdout("inh
|
|
|
2227
2326
|
* stderr passes through to the terminal directly.
|
|
2228
2327
|
*/
|
|
2229
2328
|
const runStepFormatted = (cmd, step, formatter) => Effect.gen(function* () {
|
|
2230
|
-
const proc = yield* Command
|
|
2329
|
+
const proc = yield* Command.start(cmd.pipe(Command.stdout("pipe"), Command.stderr("pipe"))).pipe(Effect.mapError((cause) => new BuildFailedError({
|
|
2231
2330
|
step,
|
|
2232
2331
|
exitCode: 1,
|
|
2233
2332
|
message: `${step} failed to spawn: ${String(cause)}`
|
|
@@ -2235,14 +2334,14 @@ const runStepFormatted = (cmd, step, formatter) => Effect.gen(function* () {
|
|
|
2235
2334
|
const stdoutFiber = yield* proc.stdout.pipe(Stream.decodeText(), Stream.splitLines, Stream.runForEach((line) => {
|
|
2236
2335
|
const formatted = formatter.pipe(line);
|
|
2237
2336
|
return formatted.length > 0 ? Effect.sync(() => {
|
|
2238
|
-
for (const output of formatted) process.stdout.write(`${output}\n`);
|
|
2337
|
+
for (const output of formatted) process$1.stdout.write(`${output}\n`);
|
|
2239
2338
|
}) : Effect.void;
|
|
2240
2339
|
}), Effect.mapError((cause) => new BuildFailedError({
|
|
2241
2340
|
step,
|
|
2242
2341
|
exitCode: 1,
|
|
2243
2342
|
message: `${step} stdout stream error: ${String(cause)}`
|
|
2244
2343
|
})), Effect.fork);
|
|
2245
|
-
const stderrFiber = yield* proc.stderr.pipe(Stream.decodeText(), Stream.splitLines, Stream.runForEach((line) => Effect.sync(() => process.stderr.write(`${line}\n`))), Effect.mapError((cause) => new BuildFailedError({
|
|
2344
|
+
const stderrFiber = yield* proc.stderr.pipe(Stream.decodeText(), Stream.splitLines, Stream.runForEach((line) => Effect.sync(() => process$1.stderr.write(`${line}\n`))), Effect.mapError((cause) => new BuildFailedError({
|
|
2246
2345
|
step,
|
|
2247
2346
|
exitCode: 1,
|
|
2248
2347
|
message: `${step} stderr stream error: ${String(cause)}`
|
|
@@ -2255,7 +2354,7 @@ const runStepFormatted = (cmd, step, formatter) => Effect.gen(function* () {
|
|
|
2255
2354
|
})));
|
|
2256
2355
|
if (code !== 0) {
|
|
2257
2356
|
const summary = formatter.getBuildSummary();
|
|
2258
|
-
if (summary) process.stderr.write(`${summary}\n`);
|
|
2357
|
+
if (summary) process$1.stderr.write(`${summary}\n`);
|
|
2259
2358
|
return yield* new BuildFailedError({
|
|
2260
2359
|
step,
|
|
2261
2360
|
exitCode: code,
|
|
@@ -2293,7 +2392,7 @@ const runAndroidBuild = (input) => Effect.gen(function* () {
|
|
|
2293
2392
|
applicationIdentifier,
|
|
2294
2393
|
tempDir
|
|
2295
2394
|
});
|
|
2296
|
-
yield* runStep(Command
|
|
2395
|
+
yield* runStep(Command.make("bunx", "expo", "prebuild", "--platform", "android", "--clean").pipe(Command.workingDirectory(projectRoot), Command.env(commandEnv)), "expo prebuild android");
|
|
2297
2396
|
const fs = yield* FileSystem.FileSystem;
|
|
2298
2397
|
const signingGradlePath = path.join(tempDir, "signing.gradle");
|
|
2299
2398
|
yield* fs.writeFileString(signingGradlePath, renderSigningGradle({
|
|
@@ -2303,7 +2402,7 @@ const runAndroidBuild = (input) => Effect.gen(function* () {
|
|
|
2303
2402
|
keyPassword: credentials.keyPassword
|
|
2304
2403
|
}));
|
|
2305
2404
|
const taskName = gradleTaskName(format, flavor, buildType);
|
|
2306
|
-
yield* runStep(Command
|
|
2405
|
+
yield* runStep(Command.make("./gradlew", "--init-script", signingGradlePath, `:app:${taskName}`).pipe(Command.workingDirectory(androidDir), Command.env(commandEnv)), "gradlew");
|
|
2307
2406
|
const artifactPath = yield* findAndroidArtifact({
|
|
2308
2407
|
projectRoot,
|
|
2309
2408
|
format,
|
|
@@ -2358,9 +2457,9 @@ const renderExportOptionsPlist = ({ method, teamId, bundleId, provisioningProfil
|
|
|
2358
2457
|
|
|
2359
2458
|
//#endregion
|
|
2360
2459
|
//#region src/lib/ios-keychain.ts
|
|
2361
|
-
const runOrFail = (cmd, step) => Command
|
|
2460
|
+
const runOrFail = (cmd, step) => Command.string(cmd).pipe(Effect.mapError((cause) => new KeychainError({ message: `keychain ${step} failed: ${String(cause)}` })));
|
|
2362
2461
|
const listCurrentKeychains = Effect.gen(function* () {
|
|
2363
|
-
return (yield* runOrFail(Command
|
|
2462
|
+
return (yield* runOrFail(Command.make("security", "list-keychains", "-d", "user"), "list-keychains")).split("\n").map((line) => line.trim().replace(/^"/, "").replace(/"$/, "")).filter((line) => line.length > 0);
|
|
2364
2463
|
});
|
|
2365
2464
|
const parseSigningIdentity = (output) => {
|
|
2366
2465
|
const lines = output.split("\n");
|
|
@@ -2381,13 +2480,13 @@ const acquireKeychain = ({ tempDir, p12Path, p12Password }) => {
|
|
|
2381
2480
|
const keychainPassword = randomBytes(32).toString("hex");
|
|
2382
2481
|
return Effect.acquireRelease(Effect.gen(function* () {
|
|
2383
2482
|
const priorKeychains = yield* listCurrentKeychains;
|
|
2384
|
-
yield* runOrFail(Command
|
|
2385
|
-
yield* runOrFail(Command
|
|
2386
|
-
yield* runOrFail(Command
|
|
2387
|
-
yield* runOrFail(Command
|
|
2388
|
-
yield* runOrFail(Command
|
|
2389
|
-
yield* runOrFail(Command
|
|
2390
|
-
const signingIdentity = parseSigningIdentity(yield* runOrFail(Command
|
|
2483
|
+
yield* runOrFail(Command.make("security", "create-keychain", "-p", keychainPassword, keychainPath), "create-keychain");
|
|
2484
|
+
yield* runOrFail(Command.make("security", "unlock-keychain", "-p", keychainPassword, keychainPath), "unlock-keychain");
|
|
2485
|
+
yield* runOrFail(Command.make("security", "set-keychain-settings", "-t", "3600", "-l", keychainPath), "set-keychain-settings");
|
|
2486
|
+
yield* runOrFail(Command.make("security", "import", p12Path, "-k", keychainPath, "-P", p12Password, "-T", "/usr/bin/codesign"), "import");
|
|
2487
|
+
yield* runOrFail(Command.make("security", "set-key-partition-list", "-S", "apple-tool:,apple:,codesign:", "-s", "-k", keychainPassword, keychainPath), "set-key-partition-list");
|
|
2488
|
+
yield* runOrFail(Command.make("security", "list-keychains", "-d", "user", "-s", keychainPath, ...priorKeychains), "list-keychains -s (add)");
|
|
2489
|
+
const signingIdentity = parseSigningIdentity(yield* runOrFail(Command.make("security", "find-identity", "-v", "-p", "codesigning", keychainPath), "find-identity"));
|
|
2391
2490
|
if (!signingIdentity) return yield* new KeychainError({ message: "No code signing identity found after importing .p12 into ephemeral keychain." });
|
|
2392
2491
|
return {
|
|
2393
2492
|
handle: {
|
|
@@ -2398,8 +2497,8 @@ const acquireKeychain = ({ tempDir, p12Path, p12Password }) => {
|
|
|
2398
2497
|
priorKeychains
|
|
2399
2498
|
};
|
|
2400
2499
|
}), ({ priorKeychains }) => Effect.gen(function* () {
|
|
2401
|
-
yield* Command
|
|
2402
|
-
yield* Command
|
|
2500
|
+
yield* Command.string(Command.make("security", "list-keychains", "-d", "user", "-s", ...priorKeychains)).pipe(Effect.catchAll(() => Effect.void));
|
|
2501
|
+
yield* Command.string(Command.make("security", "delete-keychain", keychainPath)).pipe(Effect.catchAll(() => Effect.void));
|
|
2403
2502
|
})).pipe(Effect.map(({ handle }) => handle));
|
|
2404
2503
|
};
|
|
2405
2504
|
|
|
@@ -2464,7 +2563,7 @@ const userProvisioningProfilesDir = () => path.join(os.homedir(), "Library", "Mo
|
|
|
2464
2563
|
*/
|
|
2465
2564
|
const installProvisioningProfile = ({ profilePath }) => Effect.acquireRelease(Effect.gen(function* () {
|
|
2466
2565
|
const fs = yield* FileSystem.FileSystem;
|
|
2467
|
-
const info = yield* extractProvisioningInfo(yield* Command
|
|
2566
|
+
const info = yield* extractProvisioningInfo(yield* Command.string(Command.make("security", "cms", "-D", "-i", profilePath)).pipe(Effect.mapError((cause) => new ProvisioningError({ message: `security cms -D failed for ${profilePath}: ${String(cause)}` }))));
|
|
2468
2567
|
const targetDir = userProvisioningProfilesDir();
|
|
2469
2568
|
const installedPath = path.join(targetDir, `${info.uuid}.mobileprovision`);
|
|
2470
2569
|
yield* fs.makeDirectory(targetDir, { recursive: true }).pipe(Effect.catchAll((cause) => new ProvisioningError({ message: `Failed to create provisioning profiles dir: ${String(cause)}` })));
|
|
@@ -2534,7 +2633,7 @@ const checkBundleId = (appDir, expectedBundleId) => Effect.gen(function* () {
|
|
|
2534
2633
|
const checkEmbeddedProfile = (appDir, expectedUuid, expectedTeamId) => Effect.gen(function* () {
|
|
2535
2634
|
const warnings = [];
|
|
2536
2635
|
const profilePath = path.join(appDir, "embedded.mobileprovision");
|
|
2537
|
-
const parsed = parsePlistXml(yield* Command
|
|
2636
|
+
const parsed = parsePlistXml(yield* Command.string(Command.make("security", "cms", "-D", "-i", profilePath)));
|
|
2538
2637
|
const actualUuid = parsed["UUID"];
|
|
2539
2638
|
if (typeof actualUuid === "string" && actualUuid !== expectedUuid) warnings.push(`Profile UUID mismatch: expected "${expectedUuid}", got "${actualUuid}"`);
|
|
2540
2639
|
const teamIdentifiers = parsed["TeamIdentifier"];
|
|
@@ -2585,8 +2684,8 @@ const runIosBuild = (input) => Effect.gen(function* () {
|
|
|
2585
2684
|
distribution,
|
|
2586
2685
|
tempDir
|
|
2587
2686
|
});
|
|
2588
|
-
yield* runStep(Command
|
|
2589
|
-
yield* runStep(Command
|
|
2687
|
+
yield* runStep(Command.make("bunx", "expo", "prebuild", "--platform", "ios", "--clean").pipe(Command.workingDirectory(projectRoot), Command.env(commandEnv)), "expo prebuild ios");
|
|
2688
|
+
yield* runStep(Command.make("pod", "install").pipe(Command.workingDirectory(iosDir), Command.env(commandEnv)), "pod install");
|
|
2590
2689
|
const keychain = yield* acquireKeychain({
|
|
2591
2690
|
tempDir,
|
|
2592
2691
|
p12Path: credentials.p12Path,
|
|
@@ -2597,7 +2696,7 @@ const runIosBuild = (input) => Effect.gen(function* () {
|
|
|
2597
2696
|
const scheme = iosProfile.scheme ?? workspaceFilename.replace(/\.xcworkspace$/, "");
|
|
2598
2697
|
const configuration = iosProfile.buildConfiguration ?? "Release";
|
|
2599
2698
|
const archivePath = path.join(tempDir, "build.xcarchive");
|
|
2600
|
-
const archiveCmd = Command
|
|
2699
|
+
const archiveCmd = Command.make("xcodebuild", "-workspace", workspaceFilename, "-scheme", scheme, "-configuration", configuration, "-archivePath", archivePath, "-allowProvisioningUpdates", "archive", "CODE_SIGN_STYLE=Manual", `DEVELOPMENT_TEAM=${provisioning.teamId}`, `CODE_SIGN_IDENTITY=${keychain.signingIdentity}`, `PROVISIONING_PROFILE_SPECIFIER=${provisioning.name}`).pipe(Command.workingDirectory(iosDir), Command.env(commandEnv));
|
|
2601
2700
|
const formatter = input.rawOutput ? void 0 : createXcodebuildFormatter(projectRoot);
|
|
2602
2701
|
yield* formatter ? runStepFormatted(archiveCmd, "xcodebuild archive", formatter) : runStep(archiveCmd, "xcodebuild archive");
|
|
2603
2702
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -2609,7 +2708,7 @@ const runIosBuild = (input) => Effect.gen(function* () {
|
|
|
2609
2708
|
provisioningProfileName: provisioning.name
|
|
2610
2709
|
}));
|
|
2611
2710
|
const exportPath = path.join(tempDir, "export");
|
|
2612
|
-
const exportCmd = Command
|
|
2711
|
+
const exportCmd = Command.make("xcodebuild", "-exportArchive", "-archivePath", archivePath, "-exportPath", exportPath, "-exportOptionsPlist", exportOptionsPath, "-allowProvisioningUpdates").pipe(Command.workingDirectory(iosDir), Command.env(commandEnv));
|
|
2613
2712
|
yield* formatter ? runStepFormatted(exportCmd, "xcodebuild exportArchive", formatter) : runStep(exportCmd, "xcodebuild exportArchive");
|
|
2614
2713
|
yield* validateIosBuild({
|
|
2615
2714
|
archivePath,
|
|
@@ -2819,8 +2918,8 @@ const pullEnvVars = (api, { projectId, environment }) => api["env-vars"].export(
|
|
|
2819
2918
|
const readExpoConfig = (projectRoot, envVars = {}) => Effect.acquireUseRelease(Effect.sync(() => {
|
|
2820
2919
|
const previous = {};
|
|
2821
2920
|
for (const [key, value] of Object.entries(envVars)) {
|
|
2822
|
-
previous[key] = process.env[key];
|
|
2823
|
-
process.env[key] = value;
|
|
2921
|
+
previous[key] = process$1.env[key];
|
|
2922
|
+
process$1.env[key] = value;
|
|
2824
2923
|
}
|
|
2825
2924
|
return previous;
|
|
2826
2925
|
}), () => Effect.try({
|
|
@@ -2831,8 +2930,8 @@ const readExpoConfig = (projectRoot, envVars = {}) => Effect.acquireUseRelease(E
|
|
|
2831
2930
|
},
|
|
2832
2931
|
catch: () => void 0
|
|
2833
2932
|
}).pipe(Effect.catchAll(() => Effect.succeed(void 0))), (previous) => Effect.sync(() => {
|
|
2834
|
-
for (const [key, value] of Object.entries(previous)) if (value === void 0) delete process.env[key];
|
|
2835
|
-
else process.env[key] = value;
|
|
2933
|
+
for (const [key, value] of Object.entries(previous)) if (value === void 0) delete process$1.env[key];
|
|
2934
|
+
else process$1.env[key] = value;
|
|
2836
2935
|
}));
|
|
2837
2936
|
const extractBuildNumber = (config, platform) => {
|
|
2838
2937
|
if (platform === "ios") return config.ios?.buildNumber;
|
|
@@ -2862,7 +2961,7 @@ const readAppMetaFromConfig = (config, platform) => Effect.gen(function* () {
|
|
|
2862
2961
|
|
|
2863
2962
|
//#endregion
|
|
2864
2963
|
//#region src/lib/git-context.ts
|
|
2865
|
-
const runString = (cmd, cwd) => Command
|
|
2964
|
+
const runString = (cmd, cwd) => Command.string(Command.workingDirectory(cmd, cwd));
|
|
2866
2965
|
/**
|
|
2867
2966
|
* Best-effort git context extraction. If git is missing, the directory isn't
|
|
2868
2967
|
* a repo, or any command fails, we silently return undefined fields so the
|
|
@@ -2871,10 +2970,10 @@ const runString = (cmd, cwd) => Command$1.string(Command$1.workingDirectory(cmd,
|
|
|
2871
2970
|
*/
|
|
2872
2971
|
const readGitContext = (projectRoot) => Effect.gen(function* () {
|
|
2873
2972
|
const [commit, ref, commitMessage, status] = yield* Effect.all([
|
|
2874
|
-
runString(Command
|
|
2875
|
-
runString(Command
|
|
2876
|
-
runString(Command
|
|
2877
|
-
runString(Command
|
|
2973
|
+
runString(Command.make("git", "rev-parse", "HEAD"), projectRoot).pipe(Effect.map((output) => output.trim()), Effect.catchAll(() => Effect.succeed(""))),
|
|
2974
|
+
runString(Command.make("git", "symbolic-ref", "--short", "HEAD"), projectRoot).pipe(Effect.map((output) => output.trim()), Effect.catchAll(() => Effect.succeed(""))),
|
|
2975
|
+
runString(Command.make("git", "log", "-1", "--format=%s"), projectRoot).pipe(Effect.map((output) => output.trim()), Effect.catchAll(() => Effect.succeed(""))),
|
|
2976
|
+
runString(Command.make("git", "status", "--porcelain"), projectRoot).pipe(Effect.catchAll(() => Effect.succeed("")))
|
|
2878
2977
|
], { concurrency: "unbounded" });
|
|
2879
2978
|
return {
|
|
2880
2979
|
ref: ref.length > 0 ? ref : void 0,
|
|
@@ -2946,8 +3045,8 @@ const unquote = (input) => input.startsWith("\"") && input.endsWith("\"") ? inpu
|
|
|
2946
3045
|
//#region src/lib/fingerprint.ts
|
|
2947
3046
|
var FingerprintError = class extends Data.TaggedError("FingerprintError") {};
|
|
2948
3047
|
const runFingerprintFull = (projectRoot) => Effect.gen(function* () {
|
|
2949
|
-
const cmd = Command
|
|
2950
|
-
const stdout = yield* Command
|
|
3048
|
+
const cmd = Command.make("bunx", "@expo/fingerprint", projectRoot).pipe(Command.workingDirectory(projectRoot));
|
|
3049
|
+
const stdout = yield* Command.string(cmd).pipe(Effect.mapError((cause) => new FingerprintError({ message: `Failed to run "@expo/fingerprint": ${cause.message}` })));
|
|
2951
3050
|
const parsed = yield* Effect.try({
|
|
2952
3051
|
try: () => JSON.parse(stdout),
|
|
2953
3052
|
catch: () => new FingerprintError({ message: "Failed to parse @expo/fingerprint output as JSON." })
|
|
@@ -3124,162 +3223,260 @@ const runBuildWorkflow = (options) => Effect.scoped(Effect.gen(function* () {
|
|
|
3124
3223
|
|
|
3125
3224
|
//#endregion
|
|
3126
3225
|
//#region src/commands/build/index.ts
|
|
3127
|
-
const
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3226
|
+
const BUILD_EXIT_EXTRAS = {
|
|
3227
|
+
BuildProfileError: 2,
|
|
3228
|
+
RuntimeVersionError: 2,
|
|
3229
|
+
MissingCredentialsError: 5,
|
|
3230
|
+
BuildFailedError: 6,
|
|
3231
|
+
KeychainError: 6,
|
|
3232
|
+
ProvisioningError: 6,
|
|
3233
|
+
ArtifactNotFoundError: 6,
|
|
3234
|
+
ReserveError: 7,
|
|
3235
|
+
UploadFailedError: 7,
|
|
3236
|
+
PresignedUrlExpiredError: 7,
|
|
3237
|
+
CompleteError: 7,
|
|
3238
|
+
EnvExportError: 7
|
|
3239
|
+
};
|
|
3240
|
+
const buildCommand = defineCommand({
|
|
3241
|
+
meta: {
|
|
3242
|
+
name: "build",
|
|
3243
|
+
description: "Build the app locally and optionally upload"
|
|
3244
|
+
},
|
|
3245
|
+
args: {
|
|
3246
|
+
platform: {
|
|
3247
|
+
type: "enum",
|
|
3248
|
+
options: ["ios", "android"],
|
|
3249
|
+
required: true
|
|
3250
|
+
},
|
|
3251
|
+
profile: {
|
|
3252
|
+
type: "string",
|
|
3253
|
+
default: "production",
|
|
3254
|
+
description: "Build profile name"
|
|
3255
|
+
},
|
|
3256
|
+
message: {
|
|
3257
|
+
type: "string",
|
|
3258
|
+
description: "Optional build message"
|
|
3259
|
+
},
|
|
3260
|
+
upload: {
|
|
3261
|
+
type: "boolean",
|
|
3262
|
+
default: true,
|
|
3263
|
+
description: "Upload the built artifact to better-update",
|
|
3264
|
+
negativeDescription: "Skip upload (use --no-upload)"
|
|
3265
|
+
},
|
|
3266
|
+
"raw-output": {
|
|
3267
|
+
type: "boolean",
|
|
3268
|
+
description: "Stream raw Gradle/Xcode output"
|
|
3269
|
+
}
|
|
3270
|
+
},
|
|
3271
|
+
run: async ({ args }) => runEffect(runBuildWorkflow({
|
|
3272
|
+
platform: args.platform,
|
|
3273
|
+
profileName: args.profile,
|
|
3274
|
+
message: args.message,
|
|
3275
|
+
noUpload: !args.upload,
|
|
3276
|
+
rawOutput: args["raw-output"] ?? false
|
|
3277
|
+
}), BUILD_EXIT_EXTRAS)
|
|
3278
|
+
});
|
|
3166
3279
|
|
|
3167
3280
|
//#endregion
|
|
3168
3281
|
//#region src/commands/builds/compatibility-matrix.ts
|
|
3169
|
-
const compatibilityMatrixCommand =
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
"
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
"
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3282
|
+
const compatibilityMatrixCommand = defineCommand({
|
|
3283
|
+
meta: {
|
|
3284
|
+
name: "compatibility-matrix",
|
|
3285
|
+
description: "Show build-to-channel compatibility and missing runtime versions"
|
|
3286
|
+
},
|
|
3287
|
+
run: async () => runEffect(Effect.gen(function* () {
|
|
3288
|
+
const projectId = yield* readProjectId;
|
|
3289
|
+
const result = yield* (yield* apiClient).builds.compatibilityMatrix({ urlParams: { projectId } });
|
|
3290
|
+
if (result.rows.length === 0 && result.missingRuntimeVersions.length === 0) {
|
|
3291
|
+
yield* Console.log("No compatibility data found.");
|
|
3292
|
+
return;
|
|
3293
|
+
}
|
|
3294
|
+
if (result.rows.length > 0) {
|
|
3295
|
+
yield* Console.log("Build-to-Channel Compatibility:");
|
|
3296
|
+
yield* printTable([
|
|
3297
|
+
"Build ID",
|
|
3298
|
+
"Platform",
|
|
3299
|
+
"Runtime Version",
|
|
3300
|
+
"Channels"
|
|
3301
|
+
], result.rows.map((row) => [
|
|
3302
|
+
row.id,
|
|
3303
|
+
row.platform,
|
|
3304
|
+
row.runtimeVersion ?? "-",
|
|
3305
|
+
row.channels.map((channel) => channel.channelName).join(", ") || "-"
|
|
3306
|
+
]));
|
|
3307
|
+
}
|
|
3308
|
+
if (result.missingRuntimeVersions.length > 0) {
|
|
3309
|
+
yield* Console.log("\nMissing Runtime Versions:");
|
|
3310
|
+
yield* printTable([
|
|
3311
|
+
"Channel",
|
|
3312
|
+
"Platform",
|
|
3313
|
+
"Runtime Version",
|
|
3314
|
+
"Updates"
|
|
3315
|
+
], result.missingRuntimeVersions.map((missing) => [
|
|
3316
|
+
missing.channelName,
|
|
3317
|
+
missing.platform,
|
|
3318
|
+
missing.runtimeVersion,
|
|
3319
|
+
String(missing.updateCount)
|
|
3320
|
+
]));
|
|
3321
|
+
}
|
|
3322
|
+
}))
|
|
3323
|
+
});
|
|
3205
3324
|
|
|
3206
3325
|
//#endregion
|
|
3207
3326
|
//#region src/commands/builds/delete.ts
|
|
3208
|
-
const
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
}
|
|
3327
|
+
const deleteCommand$5 = defineCommand({
|
|
3328
|
+
meta: {
|
|
3329
|
+
name: "delete",
|
|
3330
|
+
description: "Delete a build"
|
|
3331
|
+
},
|
|
3332
|
+
args: { id: {
|
|
3333
|
+
type: "positional",
|
|
3334
|
+
required: true,
|
|
3335
|
+
description: "Build ID"
|
|
3336
|
+
} },
|
|
3337
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
3338
|
+
yield* (yield* apiClient).builds.delete({ path: { id: args.id } });
|
|
3339
|
+
yield* Console.log(`Build ${args.id} deleted.`);
|
|
3340
|
+
}))
|
|
3341
|
+
});
|
|
3213
3342
|
|
|
3214
3343
|
//#endregion
|
|
3215
3344
|
//#region src/commands/builds/get.ts
|
|
3216
|
-
const
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
[
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3345
|
+
const getCommand$2 = defineCommand({
|
|
3346
|
+
meta: {
|
|
3347
|
+
name: "get",
|
|
3348
|
+
description: "Show a build"
|
|
3349
|
+
},
|
|
3350
|
+
args: { id: {
|
|
3351
|
+
type: "positional",
|
|
3352
|
+
required: true,
|
|
3353
|
+
description: "Build ID"
|
|
3354
|
+
} },
|
|
3355
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
3356
|
+
const build = yield* (yield* apiClient).builds.get({ path: { id: args.id } });
|
|
3357
|
+
yield* printKeyValue([
|
|
3358
|
+
["ID", build.id],
|
|
3359
|
+
["Platform", build.platform],
|
|
3360
|
+
["Profile", build.profile],
|
|
3361
|
+
["Distribution", build.distribution],
|
|
3362
|
+
["Version", build.appVersion ?? "-"],
|
|
3363
|
+
["Build Number", build.buildNumber ?? "-"],
|
|
3364
|
+
["Runtime Version", build.runtimeVersion ?? "-"],
|
|
3365
|
+
["Bundle ID", build.bundleId ?? "-"],
|
|
3366
|
+
["Git Ref", build.gitRef ?? "-"],
|
|
3367
|
+
["Message", build.message ?? "-"],
|
|
3368
|
+
["Artifact", build.artifact ? `${build.artifact.format} (${String(build.artifact.byteSize)} bytes)` : "none"],
|
|
3369
|
+
["Created", build.createdAt]
|
|
3370
|
+
]);
|
|
3371
|
+
}))
|
|
3372
|
+
});
|
|
3234
3373
|
|
|
3235
3374
|
//#endregion
|
|
3236
3375
|
//#region src/commands/builds/install-link.ts
|
|
3237
|
-
const
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3376
|
+
const installLinkCommand = defineCommand({
|
|
3377
|
+
meta: {
|
|
3378
|
+
name: "install-link",
|
|
3379
|
+
description: "Get install/artifact URLs for a build"
|
|
3380
|
+
},
|
|
3381
|
+
args: { id: {
|
|
3382
|
+
type: "positional",
|
|
3383
|
+
required: true,
|
|
3384
|
+
description: "Build ID"
|
|
3385
|
+
} },
|
|
3386
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
3387
|
+
const result = yield* (yield* apiClient).builds.getInstallLink({ path: { id: args.id } });
|
|
3388
|
+
yield* printKeyValue([
|
|
3389
|
+
["Artifact URL", result.artifactUrl],
|
|
3390
|
+
["Install URL", result.installUrl ?? "-"],
|
|
3391
|
+
["Expires", String(result.expires)]
|
|
3392
|
+
]);
|
|
3393
|
+
}))
|
|
3394
|
+
});
|
|
3395
|
+
|
|
3396
|
+
//#endregion
|
|
3397
|
+
//#region src/lib/cli-schemas.ts
|
|
3398
|
+
const RolloutPercentage = Schema.Number.pipe(Schema.int(), Schema.between(1, 100)).annotations({
|
|
3399
|
+
message: () => "Rollout percentage must be between 1 and 100.",
|
|
3400
|
+
identifier: "RolloutPercentage"
|
|
3401
|
+
});
|
|
3402
|
+
const KeyValuePair = Schema.Struct({
|
|
3403
|
+
key: Schema.String,
|
|
3404
|
+
value: Schema.String
|
|
3405
|
+
});
|
|
3406
|
+
const KeyValueFromString = Schema.transformOrFail(Schema.String, KeyValuePair, {
|
|
3407
|
+
strict: true,
|
|
3408
|
+
decode: (input, _options, ast) => {
|
|
3409
|
+
const eqIndex = input.indexOf("=");
|
|
3410
|
+
if (eqIndex <= 0) return ParseResult.fail(new ParseResult.Type(ast, input, "Invalid format. Use KEY=VALUE (e.g. API_KEY=abc123)"));
|
|
3411
|
+
return ParseResult.succeed({
|
|
3412
|
+
key: input.slice(0, eqIndex),
|
|
3413
|
+
value: input.slice(eqIndex + 1)
|
|
3414
|
+
});
|
|
3415
|
+
},
|
|
3416
|
+
encode: ({ key, value }) => ParseResult.succeed(`${key}=${value}`)
|
|
3417
|
+
});
|
|
3418
|
+
const parseRolloutPercentage = (raw, flag) => Effect.try({
|
|
3419
|
+
try: () => Schema.decodeUnknownSync(RolloutPercentage)(Number(raw)),
|
|
3420
|
+
catch: () => new InvalidArgumentError({ message: `--${flag} must be an integer between 1 and 100, got "${raw}".` })
|
|
3421
|
+
});
|
|
3422
|
+
const parseKeyValue = (raw) => Effect.try({
|
|
3423
|
+
try: () => Schema.decodeUnknownSync(KeyValueFromString)(raw),
|
|
3424
|
+
catch: () => new InvalidArgumentError({ message: "Invalid format. Use KEY=VALUE (e.g. API_KEY=abc123)" })
|
|
3425
|
+
});
|
|
3426
|
+
const parseLimit = (raw, defaultValue) => {
|
|
3427
|
+
if (raw === void 0) return Effect.succeed(defaultValue);
|
|
3428
|
+
const parsed = Number(raw);
|
|
3429
|
+
if (!Number.isInteger(parsed) || parsed < 1) return Effect.fail(new InvalidArgumentError({ message: `--limit must be a positive integer, got "${raw}".` }));
|
|
3430
|
+
return Effect.succeed(parsed);
|
|
3431
|
+
};
|
|
3246
3432
|
|
|
3247
3433
|
//#endregion
|
|
3248
3434
|
//#region src/commands/builds/list.ts
|
|
3249
|
-
const
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3435
|
+
const listCommand$5 = defineCommand({
|
|
3436
|
+
meta: {
|
|
3437
|
+
name: "list",
|
|
3438
|
+
description: "List builds for the linked project"
|
|
3439
|
+
},
|
|
3440
|
+
args: {
|
|
3441
|
+
platform: {
|
|
3442
|
+
type: "enum",
|
|
3443
|
+
options: ["ios", "android"],
|
|
3444
|
+
description: "Filter by platform"
|
|
3445
|
+
},
|
|
3446
|
+
limit: {
|
|
3447
|
+
type: "string",
|
|
3448
|
+
default: "10",
|
|
3449
|
+
description: "Max rows (default 10)"
|
|
3450
|
+
}
|
|
3451
|
+
},
|
|
3452
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
3453
|
+
const limit = yield* parseLimit(args.limit, 10);
|
|
3454
|
+
const projectId = yield* readProjectId;
|
|
3455
|
+
const api = yield* apiClient;
|
|
3456
|
+
const platformFilter = args.platform ? { platform: args.platform } : {};
|
|
3457
|
+
const { items } = yield* api.builds.list({ urlParams: {
|
|
3458
|
+
projectId,
|
|
3459
|
+
...platformFilter,
|
|
3460
|
+
page: 1,
|
|
3461
|
+
limit
|
|
3462
|
+
} });
|
|
3463
|
+
yield* printTable([
|
|
3464
|
+
"ID",
|
|
3465
|
+
"Platform",
|
|
3466
|
+
"Profile",
|
|
3467
|
+
"Distribution",
|
|
3468
|
+
"Version",
|
|
3469
|
+
"Created"
|
|
3470
|
+
], items.map((build) => [
|
|
3471
|
+
build.id,
|
|
3472
|
+
build.platform,
|
|
3473
|
+
build.profile,
|
|
3474
|
+
build.distribution,
|
|
3475
|
+
build.appVersion ?? "-",
|
|
3476
|
+
build.createdAt
|
|
3477
|
+
]));
|
|
3478
|
+
}))
|
|
3479
|
+
});
|
|
3283
3480
|
|
|
3284
3481
|
//#endregion
|
|
3285
3482
|
//#region src/application/upload-workflow.ts
|
|
@@ -3369,44 +3566,67 @@ const runUploadWorkflow = (options) => Effect.gen(function* () {
|
|
|
3369
3566
|
|
|
3370
3567
|
//#endregion
|
|
3371
3568
|
//#region src/commands/builds/upload.ts
|
|
3372
|
-
const
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
}
|
|
3569
|
+
const UPLOAD_EXIT_EXTRAS = {
|
|
3570
|
+
BuildProfileError: 2,
|
|
3571
|
+
RuntimeVersionError: 2,
|
|
3572
|
+
ArtifactNotFoundError: 6,
|
|
3573
|
+
BuildFailedError: 6,
|
|
3574
|
+
ReserveError: 7,
|
|
3575
|
+
UploadFailedError: 7,
|
|
3576
|
+
PresignedUrlExpiredError: 7,
|
|
3577
|
+
CompleteError: 7,
|
|
3578
|
+
EnvExportError: 7
|
|
3579
|
+
};
|
|
3580
|
+
const uploadCommand$1 = defineCommand({
|
|
3581
|
+
meta: {
|
|
3582
|
+
name: "upload",
|
|
3583
|
+
description: "Upload an existing artifact to better-update"
|
|
3584
|
+
},
|
|
3585
|
+
args: {
|
|
3586
|
+
"artifact-path": {
|
|
3587
|
+
type: "positional",
|
|
3588
|
+
required: true,
|
|
3589
|
+
description: "Path to artifact"
|
|
3590
|
+
},
|
|
3591
|
+
platform: {
|
|
3592
|
+
type: "enum",
|
|
3593
|
+
options: ["ios", "android"],
|
|
3594
|
+
required: true
|
|
3595
|
+
},
|
|
3596
|
+
profile: {
|
|
3597
|
+
type: "string",
|
|
3598
|
+
default: "production",
|
|
3599
|
+
description: "Build profile name"
|
|
3600
|
+
},
|
|
3601
|
+
message: {
|
|
3602
|
+
type: "string",
|
|
3603
|
+
description: "Optional build message"
|
|
3604
|
+
}
|
|
3605
|
+
},
|
|
3606
|
+
run: async ({ args }) => runEffect(runUploadWorkflow({
|
|
3607
|
+
artifactPath: args["artifact-path"],
|
|
3608
|
+
platform: args.platform,
|
|
3609
|
+
profileName: args.profile,
|
|
3610
|
+
message: args.message
|
|
3611
|
+
}), UPLOAD_EXIT_EXTRAS)
|
|
3612
|
+
});
|
|
3399
3613
|
|
|
3400
3614
|
//#endregion
|
|
3401
3615
|
//#region src/commands/builds/index.ts
|
|
3402
|
-
const buildsCommand =
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3616
|
+
const buildsCommand = defineCommand({
|
|
3617
|
+
meta: {
|
|
3618
|
+
name: "builds",
|
|
3619
|
+
description: "Manage builds"
|
|
3620
|
+
},
|
|
3621
|
+
subCommands: {
|
|
3622
|
+
list: listCommand$5,
|
|
3623
|
+
get: getCommand$2,
|
|
3624
|
+
delete: deleteCommand$5,
|
|
3625
|
+
"install-link": installLinkCommand,
|
|
3626
|
+
"compatibility-matrix": compatibilityMatrixCommand,
|
|
3627
|
+
upload: uploadCommand$1
|
|
3628
|
+
}
|
|
3629
|
+
});
|
|
3410
3630
|
|
|
3411
3631
|
//#endregion
|
|
3412
3632
|
//#region src/lib/resolve-named-resource.ts
|
|
@@ -3419,237 +3639,337 @@ const resolveNamedResourceId$2 = (params, makeError) => Effect.gen(function* ()
|
|
|
3419
3639
|
//#endregion
|
|
3420
3640
|
//#region src/commands/channels/helpers.ts
|
|
3421
3641
|
var ChannelCommandError = class extends Data.TaggedError("ChannelCommandError") {};
|
|
3422
|
-
const
|
|
3642
|
+
const channelErrorExtras = { ChannelCommandError: 2 };
|
|
3423
3643
|
const resolveNamedResourceId$1 = (params) => resolveNamedResourceId$2(params, (message) => new ChannelCommandError({ message }));
|
|
3424
3644
|
|
|
3425
3645
|
//#endregion
|
|
3426
3646
|
//#region src/commands/channels/create.ts
|
|
3427
|
-
const
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
})
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
})
|
|
3647
|
+
const createCommand$2 = defineCommand({
|
|
3648
|
+
meta: {
|
|
3649
|
+
name: "create",
|
|
3650
|
+
description: "Create a channel"
|
|
3651
|
+
},
|
|
3652
|
+
args: {
|
|
3653
|
+
name: {
|
|
3654
|
+
type: "string",
|
|
3655
|
+
required: true,
|
|
3656
|
+
description: "Channel name"
|
|
3657
|
+
},
|
|
3658
|
+
branch: {
|
|
3659
|
+
type: "string",
|
|
3660
|
+
required: true,
|
|
3661
|
+
description: "Initial branch name"
|
|
3662
|
+
}
|
|
3663
|
+
},
|
|
3664
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
3665
|
+
const projectId = yield* readProjectId;
|
|
3666
|
+
const api = yield* apiClient;
|
|
3667
|
+
const { items: branches } = yield* api.branches.list({ urlParams: {
|
|
3668
|
+
projectId,
|
|
3669
|
+
page: 1,
|
|
3670
|
+
limit: 1e3
|
|
3671
|
+
} });
|
|
3672
|
+
const branchId = yield* resolveNamedResourceId$1({
|
|
3673
|
+
items: branches,
|
|
3674
|
+
kind: "Branch",
|
|
3675
|
+
name: args.branch
|
|
3676
|
+
});
|
|
3677
|
+
const channel = yield* api.channels.create({ payload: {
|
|
3678
|
+
projectId,
|
|
3679
|
+
name: args.name,
|
|
3680
|
+
branchId
|
|
3681
|
+
} });
|
|
3682
|
+
yield* printKeyValue([
|
|
3683
|
+
["ID", channel.id],
|
|
3684
|
+
["Name", channel.name],
|
|
3685
|
+
["Branch", args.branch],
|
|
3686
|
+
["Created", channel.createdAt]
|
|
3687
|
+
]);
|
|
3688
|
+
}), channelErrorExtras)
|
|
3689
|
+
});
|
|
3457
3690
|
|
|
3458
3691
|
//#endregion
|
|
3459
3692
|
//#region src/commands/channels/delete.ts
|
|
3460
|
-
const
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
}
|
|
3693
|
+
const deleteCommand$4 = defineCommand({
|
|
3694
|
+
meta: {
|
|
3695
|
+
name: "delete",
|
|
3696
|
+
description: "Delete a channel"
|
|
3697
|
+
},
|
|
3698
|
+
args: { id: {
|
|
3699
|
+
type: "positional",
|
|
3700
|
+
required: true,
|
|
3701
|
+
description: "Channel ID"
|
|
3702
|
+
} },
|
|
3703
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
3704
|
+
yield* (yield* apiClient).channels.delete({ path: { id: args.id } });
|
|
3705
|
+
yield* Console.log(`Channel ${args.id} deleted.`);
|
|
3706
|
+
}), channelErrorExtras)
|
|
3707
|
+
});
|
|
3465
3708
|
|
|
3466
3709
|
//#endregion
|
|
3467
3710
|
//#region src/commands/channels/list.ts
|
|
3468
|
-
const listCommand$4 =
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
channel
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3711
|
+
const listCommand$4 = defineCommand({
|
|
3712
|
+
meta: {
|
|
3713
|
+
name: "list",
|
|
3714
|
+
description: "List channels for the linked project"
|
|
3715
|
+
},
|
|
3716
|
+
run: async () => runEffect(Effect.gen(function* () {
|
|
3717
|
+
const projectId = yield* readProjectId;
|
|
3718
|
+
const api = yield* apiClient;
|
|
3719
|
+
const [{ items }, { items: branches }] = yield* Effect.all([api.channels.list({ urlParams: {
|
|
3720
|
+
projectId,
|
|
3721
|
+
page: 1,
|
|
3722
|
+
limit: 1e3
|
|
3723
|
+
} }), api.branches.list({ urlParams: {
|
|
3724
|
+
projectId,
|
|
3725
|
+
page: 1,
|
|
3726
|
+
limit: 1e3
|
|
3727
|
+
} })]);
|
|
3728
|
+
if (items.length === 0) {
|
|
3729
|
+
yield* Console.log("No channels found.");
|
|
3730
|
+
return;
|
|
3731
|
+
}
|
|
3732
|
+
const branchNames = new Map(branches.map((branch) => [branch.id, branch.name]));
|
|
3733
|
+
yield* printTable([
|
|
3734
|
+
"ID",
|
|
3735
|
+
"Name",
|
|
3736
|
+
"Branch",
|
|
3737
|
+
"Paused",
|
|
3738
|
+
"Rollout",
|
|
3739
|
+
"Created"
|
|
3740
|
+
], items.map((channel) => [
|
|
3741
|
+
channel.id,
|
|
3742
|
+
channel.name,
|
|
3743
|
+
branchNames.get(channel.branchId) ?? channel.branchId,
|
|
3744
|
+
channel.isPaused ? "yes" : "no",
|
|
3745
|
+
channel.branchMappingJson === null ? "-" : "active",
|
|
3746
|
+
channel.createdAt
|
|
3747
|
+
]));
|
|
3748
|
+
}), channelErrorExtras)
|
|
3749
|
+
});
|
|
3501
3750
|
|
|
3502
3751
|
//#endregion
|
|
3503
3752
|
//#region src/commands/channels/pause.ts
|
|
3504
|
-
const
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
}
|
|
3753
|
+
const pauseCommand = defineCommand({
|
|
3754
|
+
meta: {
|
|
3755
|
+
name: "pause",
|
|
3756
|
+
description: "Pause a channel"
|
|
3757
|
+
},
|
|
3758
|
+
args: { id: {
|
|
3759
|
+
type: "positional",
|
|
3760
|
+
required: true,
|
|
3761
|
+
description: "Channel ID"
|
|
3762
|
+
} },
|
|
3763
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
3764
|
+
const channel = yield* (yield* apiClient).channels.pause({ path: { id: args.id } });
|
|
3765
|
+
yield* Console.log(`Channel "${channel.name}" paused.`);
|
|
3766
|
+
}), channelErrorExtras)
|
|
3767
|
+
});
|
|
3509
3768
|
|
|
3510
3769
|
//#endregion
|
|
3511
3770
|
//#region src/commands/channels/resume.ts
|
|
3512
|
-
const
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
}
|
|
3771
|
+
const resumeCommand = defineCommand({
|
|
3772
|
+
meta: {
|
|
3773
|
+
name: "resume",
|
|
3774
|
+
description: "Resume a paused channel"
|
|
3775
|
+
},
|
|
3776
|
+
args: { id: {
|
|
3777
|
+
type: "positional",
|
|
3778
|
+
required: true,
|
|
3779
|
+
description: "Channel ID"
|
|
3780
|
+
} },
|
|
3781
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
3782
|
+
const channel = yield* (yield* apiClient).channels.resume({ path: { id: args.id } });
|
|
3783
|
+
yield* Console.log(`Channel "${channel.name}" resumed.`);
|
|
3784
|
+
}), channelErrorExtras)
|
|
3785
|
+
});
|
|
3517
3786
|
|
|
3518
3787
|
//#endregion
|
|
3519
3788
|
//#region src/commands/channels/rollout/complete.ts
|
|
3520
|
-
const
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
}).pipe(handleChannelCommandErrors));
|
|
3525
|
-
|
|
3526
|
-
//#endregion
|
|
3527
|
-
//#region src/lib/cli-schemas.ts
|
|
3528
|
-
const RolloutPercentage = Schema.Number.pipe(Schema.int(), Schema.between(1, 100)).annotations({
|
|
3529
|
-
message: () => "Rollout percentage must be between 1 and 100.",
|
|
3530
|
-
identifier: "RolloutPercentage"
|
|
3531
|
-
});
|
|
3532
|
-
const rolloutPercentageOption = (name) => Options.integer(name).pipe(Options.withSchema(RolloutPercentage));
|
|
3533
|
-
const KeyValuePair = Schema.Struct({
|
|
3534
|
-
key: Schema.String,
|
|
3535
|
-
value: Schema.String
|
|
3536
|
-
});
|
|
3537
|
-
const KeyValueFromString = Schema.transformOrFail(Schema.String, KeyValuePair, {
|
|
3538
|
-
strict: true,
|
|
3539
|
-
decode: (input, _options, ast) => {
|
|
3540
|
-
const eqIndex = input.indexOf("=");
|
|
3541
|
-
if (eqIndex <= 0) return ParseResult.fail(new ParseResult.Type(ast, input, "Invalid format. Use KEY=VALUE (e.g. API_KEY=abc123)"));
|
|
3542
|
-
return ParseResult.succeed({
|
|
3543
|
-
key: input.slice(0, eqIndex),
|
|
3544
|
-
value: input.slice(eqIndex + 1)
|
|
3545
|
-
});
|
|
3789
|
+
const completeCommand$1 = defineCommand({
|
|
3790
|
+
meta: {
|
|
3791
|
+
name: "complete",
|
|
3792
|
+
description: "Complete the active branch rollout"
|
|
3546
3793
|
},
|
|
3547
|
-
|
|
3794
|
+
args: { channelId: {
|
|
3795
|
+
type: "positional",
|
|
3796
|
+
required: true,
|
|
3797
|
+
description: "Channel ID"
|
|
3798
|
+
} },
|
|
3799
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
3800
|
+
const channel = yield* (yield* apiClient).channels.completeBranchRollout({ path: { id: args.channelId } });
|
|
3801
|
+
yield* Console.log(`Completed rollout on channel "${channel.name}".`);
|
|
3802
|
+
}), channelErrorExtras)
|
|
3548
3803
|
});
|
|
3549
|
-
const keyValueArg = (name) => Args.text({ name }).pipe(Args.withSchema(KeyValueFromString));
|
|
3550
3804
|
|
|
3551
3805
|
//#endregion
|
|
3552
3806
|
//#region src/commands/channels/rollout/create.ts
|
|
3553
|
-
const
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
const channel = yield* api.channels.createBranchRollout({
|
|
3574
|
-
path: { id: opts.channelId },
|
|
3575
|
-
payload: {
|
|
3576
|
-
newBranchId,
|
|
3577
|
-
percentage: opts.percentage
|
|
3807
|
+
const createCommand$1 = defineCommand({
|
|
3808
|
+
meta: {
|
|
3809
|
+
name: "create",
|
|
3810
|
+
description: "Start a branch rollout on a channel"
|
|
3811
|
+
},
|
|
3812
|
+
args: {
|
|
3813
|
+
channelId: {
|
|
3814
|
+
type: "positional",
|
|
3815
|
+
required: true,
|
|
3816
|
+
description: "Channel ID"
|
|
3817
|
+
},
|
|
3818
|
+
branch: {
|
|
3819
|
+
type: "string",
|
|
3820
|
+
required: true,
|
|
3821
|
+
description: "Target branch name"
|
|
3822
|
+
},
|
|
3823
|
+
percentage: {
|
|
3824
|
+
type: "string",
|
|
3825
|
+
required: true,
|
|
3826
|
+
description: "Initial rollout percentage (1-100)"
|
|
3578
3827
|
}
|
|
3579
|
-
}
|
|
3580
|
-
|
|
3581
|
-
|
|
3828
|
+
},
|
|
3829
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
3830
|
+
const percentage = yield* parseRolloutPercentage(args.percentage, "percentage");
|
|
3831
|
+
const projectId = yield* readProjectId;
|
|
3832
|
+
const api = yield* apiClient;
|
|
3833
|
+
const { items: branches } = yield* api.branches.list({ urlParams: {
|
|
3834
|
+
projectId,
|
|
3835
|
+
page: 1,
|
|
3836
|
+
limit: 1e3
|
|
3837
|
+
} });
|
|
3838
|
+
const newBranchId = yield* resolveNamedResourceId$1({
|
|
3839
|
+
items: branches,
|
|
3840
|
+
kind: "Branch",
|
|
3841
|
+
name: args.branch
|
|
3842
|
+
});
|
|
3843
|
+
const channel = yield* api.channels.createBranchRollout({
|
|
3844
|
+
path: { id: args.channelId },
|
|
3845
|
+
payload: {
|
|
3846
|
+
newBranchId,
|
|
3847
|
+
percentage
|
|
3848
|
+
}
|
|
3849
|
+
});
|
|
3850
|
+
yield* Console.log(`Started rollout on channel "${channel.name}" to branch "${args.branch}" at ${String(percentage)}%.`);
|
|
3851
|
+
}), channelErrorExtras)
|
|
3852
|
+
});
|
|
3582
3853
|
|
|
3583
3854
|
//#endregion
|
|
3584
3855
|
//#region src/commands/channels/rollout/revert.ts
|
|
3585
|
-
const
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
}
|
|
3856
|
+
const revertCommand$1 = defineCommand({
|
|
3857
|
+
meta: {
|
|
3858
|
+
name: "revert",
|
|
3859
|
+
description: "Revert the active branch rollout"
|
|
3860
|
+
},
|
|
3861
|
+
args: { channelId: {
|
|
3862
|
+
type: "positional",
|
|
3863
|
+
required: true,
|
|
3864
|
+
description: "Channel ID"
|
|
3865
|
+
} },
|
|
3866
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
3867
|
+
const channel = yield* (yield* apiClient).channels.revertBranchRollout({ path: { id: args.channelId } });
|
|
3868
|
+
yield* Console.log(`Reverted rollout on channel "${channel.name}".`);
|
|
3869
|
+
}), channelErrorExtras)
|
|
3870
|
+
});
|
|
3590
3871
|
|
|
3591
3872
|
//#endregion
|
|
3592
3873
|
//#region src/commands/channels/rollout/update.ts
|
|
3593
|
-
const
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3874
|
+
const updateCommand$2 = defineCommand({
|
|
3875
|
+
meta: {
|
|
3876
|
+
name: "update",
|
|
3877
|
+
description: "Update the rollout percentage on a channel"
|
|
3878
|
+
},
|
|
3879
|
+
args: {
|
|
3880
|
+
channelId: {
|
|
3881
|
+
type: "positional",
|
|
3882
|
+
required: true,
|
|
3883
|
+
description: "Channel ID"
|
|
3884
|
+
},
|
|
3885
|
+
percentage: {
|
|
3886
|
+
type: "string",
|
|
3887
|
+
required: true,
|
|
3888
|
+
description: "New rollout percentage (1-100)"
|
|
3889
|
+
}
|
|
3890
|
+
},
|
|
3891
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
3892
|
+
const percentage = yield* parseRolloutPercentage(args.percentage, "percentage");
|
|
3893
|
+
const channel = yield* (yield* apiClient).channels.updateBranchRollout({
|
|
3894
|
+
path: { id: args.channelId },
|
|
3895
|
+
payload: { percentage }
|
|
3896
|
+
});
|
|
3897
|
+
yield* Console.log(`Updated rollout on channel "${channel.name}" to ${String(percentage)}%.`);
|
|
3898
|
+
}), channelErrorExtras)
|
|
3899
|
+
});
|
|
3900
|
+
|
|
3606
3901
|
//#endregion
|
|
3607
3902
|
//#region src/commands/channels/rollout/index.ts
|
|
3608
|
-
const rolloutCommand$1 =
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3903
|
+
const rolloutCommand$1 = defineCommand({
|
|
3904
|
+
meta: {
|
|
3905
|
+
name: "rollout",
|
|
3906
|
+
description: "Manage channel branch rollouts"
|
|
3907
|
+
},
|
|
3908
|
+
subCommands: {
|
|
3909
|
+
create: createCommand$1,
|
|
3910
|
+
update: updateCommand$2,
|
|
3911
|
+
complete: completeCommand$1,
|
|
3912
|
+
revert: revertCommand$1
|
|
3913
|
+
}
|
|
3914
|
+
});
|
|
3614
3915
|
|
|
3615
3916
|
//#endregion
|
|
3616
3917
|
//#region src/commands/channels/update.ts
|
|
3617
|
-
const
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
})
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3918
|
+
const updateCommand$1 = defineCommand({
|
|
3919
|
+
meta: {
|
|
3920
|
+
name: "update",
|
|
3921
|
+
description: "Relink a channel to a different branch"
|
|
3922
|
+
},
|
|
3923
|
+
args: {
|
|
3924
|
+
id: {
|
|
3925
|
+
type: "positional",
|
|
3926
|
+
required: true,
|
|
3927
|
+
description: "Channel ID"
|
|
3928
|
+
},
|
|
3929
|
+
branch: {
|
|
3930
|
+
type: "string",
|
|
3931
|
+
required: true,
|
|
3932
|
+
description: "Target branch name"
|
|
3933
|
+
}
|
|
3934
|
+
},
|
|
3935
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
3936
|
+
const projectId = yield* readProjectId;
|
|
3937
|
+
const api = yield* apiClient;
|
|
3938
|
+
const { items: branches } = yield* api.branches.list({ urlParams: {
|
|
3939
|
+
projectId,
|
|
3940
|
+
page: 1,
|
|
3941
|
+
limit: 1e3
|
|
3942
|
+
} });
|
|
3943
|
+
const branchId = yield* resolveNamedResourceId$1({
|
|
3944
|
+
items: branches,
|
|
3945
|
+
kind: "Branch",
|
|
3946
|
+
name: args.branch
|
|
3947
|
+
});
|
|
3948
|
+
const channel = yield* api.channels.update({
|
|
3949
|
+
path: { id: args.id },
|
|
3950
|
+
payload: { branchId }
|
|
3951
|
+
});
|
|
3952
|
+
yield* Console.log(`Channel "${channel.name}" relinked to branch "${args.branch}".`);
|
|
3953
|
+
}), channelErrorExtras)
|
|
3954
|
+
});
|
|
3641
3955
|
|
|
3642
3956
|
//#endregion
|
|
3643
3957
|
//#region src/commands/channels/index.ts
|
|
3644
|
-
const channelsCommand =
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3958
|
+
const channelsCommand = defineCommand({
|
|
3959
|
+
meta: {
|
|
3960
|
+
name: "channels",
|
|
3961
|
+
description: "Manage channels"
|
|
3962
|
+
},
|
|
3963
|
+
subCommands: {
|
|
3964
|
+
list: listCommand$4,
|
|
3965
|
+
create: createCommand$2,
|
|
3966
|
+
update: updateCommand$1,
|
|
3967
|
+
pause: pauseCommand,
|
|
3968
|
+
resume: resumeCommand,
|
|
3969
|
+
delete: deleteCommand$4,
|
|
3970
|
+
rollout: rolloutCommand$1
|
|
3971
|
+
}
|
|
3972
|
+
});
|
|
3653
3973
|
|
|
3654
3974
|
//#endregion
|
|
3655
3975
|
//#region src/lib/pkcs12.ts
|
|
@@ -3882,355 +4202,529 @@ const deleteCredential = (api, input) => {
|
|
|
3882
4202
|
|
|
3883
4203
|
//#endregion
|
|
3884
4204
|
//#region src/commands/credentials/delete.ts
|
|
3885
|
-
const
|
|
3886
|
-
const platform$4 = Options.choice("platform", ["ios", "android"]);
|
|
3887
|
-
const type$1 = Options.choice("type", [
|
|
4205
|
+
const CREDENTIAL_TYPES$1 = [
|
|
3888
4206
|
"distribution-certificate",
|
|
3889
4207
|
"provisioning-profile",
|
|
3890
4208
|
"push-key",
|
|
3891
4209
|
"asc-api-key",
|
|
3892
4210
|
"keystore",
|
|
3893
4211
|
"google-service-account-key"
|
|
3894
|
-
]
|
|
3895
|
-
const deleteCommand$3 =
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
},
|
|
3900
|
-
|
|
3901
|
-
id:
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
4212
|
+
];
|
|
4213
|
+
const deleteCommand$3 = defineCommand({
|
|
4214
|
+
meta: {
|
|
4215
|
+
name: "delete",
|
|
4216
|
+
description: "Delete a credential"
|
|
4217
|
+
},
|
|
4218
|
+
args: {
|
|
4219
|
+
id: {
|
|
4220
|
+
type: "positional",
|
|
4221
|
+
required: true,
|
|
4222
|
+
description: "Credential ID"
|
|
4223
|
+
},
|
|
4224
|
+
platform: {
|
|
4225
|
+
type: "enum",
|
|
4226
|
+
options: ["ios", "android"],
|
|
4227
|
+
required: true
|
|
4228
|
+
},
|
|
4229
|
+
type: {
|
|
4230
|
+
type: "enum",
|
|
4231
|
+
options: [...CREDENTIAL_TYPES$1],
|
|
4232
|
+
required: true
|
|
4233
|
+
}
|
|
4234
|
+
},
|
|
4235
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
4236
|
+
yield* deleteCredential(yield* apiClient, {
|
|
4237
|
+
id: args.id,
|
|
4238
|
+
platform: args.platform,
|
|
4239
|
+
type: args.type
|
|
4240
|
+
});
|
|
4241
|
+
yield* Console.log(`Credential ${args.id} deleted.`);
|
|
4242
|
+
}))
|
|
4243
|
+
});
|
|
3907
4244
|
|
|
3908
4245
|
//#endregion
|
|
3909
4246
|
//#region src/commands/credentials/list.ts
|
|
3910
|
-
const
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
}
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
4247
|
+
const listCommand$3 = defineCommand({
|
|
4248
|
+
meta: {
|
|
4249
|
+
name: "list",
|
|
4250
|
+
description: "List credentials across platforms"
|
|
4251
|
+
},
|
|
4252
|
+
args: { platform: {
|
|
4253
|
+
type: "enum",
|
|
4254
|
+
options: ["ios", "android"],
|
|
4255
|
+
description: "Filter by platform"
|
|
4256
|
+
} },
|
|
4257
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
4258
|
+
const filtered = filterCredentials(yield* listAllCredentials(yield* apiClient), args.platform ? { platform: args.platform } : {});
|
|
4259
|
+
if (filtered.length === 0) {
|
|
4260
|
+
yield* Console.log("No credentials found.");
|
|
4261
|
+
return;
|
|
4262
|
+
}
|
|
4263
|
+
yield* printTable([
|
|
4264
|
+
"ID",
|
|
4265
|
+
"Name",
|
|
4266
|
+
"Platform",
|
|
4267
|
+
"Type",
|
|
4268
|
+
"Distribution"
|
|
4269
|
+
], filtered.map((row) => [
|
|
4270
|
+
row.id,
|
|
4271
|
+
row.name,
|
|
4272
|
+
row.platform,
|
|
4273
|
+
row.type,
|
|
4274
|
+
row.distribution ?? "-"
|
|
4275
|
+
]));
|
|
4276
|
+
}))
|
|
4277
|
+
});
|
|
3934
4278
|
|
|
3935
4279
|
//#endregion
|
|
3936
4280
|
//#region src/commands/credentials/upload.ts
|
|
3937
|
-
const
|
|
3938
|
-
const type = Options.choice("type", [
|
|
4281
|
+
const CREDENTIAL_TYPES = [
|
|
3939
4282
|
"distribution-certificate",
|
|
3940
4283
|
"provisioning-profile",
|
|
3941
4284
|
"push-key",
|
|
3942
4285
|
"asc-api-key",
|
|
3943
4286
|
"keystore",
|
|
3944
4287
|
"google-service-account-key"
|
|
3945
|
-
]
|
|
3946
|
-
const
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
4288
|
+
];
|
|
4289
|
+
const uploadCommand = defineCommand({
|
|
4290
|
+
meta: {
|
|
4291
|
+
name: "upload",
|
|
4292
|
+
description: "Upload a credential"
|
|
4293
|
+
},
|
|
4294
|
+
args: {
|
|
4295
|
+
platform: {
|
|
4296
|
+
type: "enum",
|
|
4297
|
+
options: ["ios", "android"],
|
|
4298
|
+
required: true
|
|
4299
|
+
},
|
|
4300
|
+
type: {
|
|
4301
|
+
type: "enum",
|
|
4302
|
+
options: [...CREDENTIAL_TYPES],
|
|
4303
|
+
required: true
|
|
4304
|
+
},
|
|
4305
|
+
name: {
|
|
4306
|
+
type: "string",
|
|
4307
|
+
required: true,
|
|
4308
|
+
description: "Display name"
|
|
4309
|
+
},
|
|
4310
|
+
file: {
|
|
4311
|
+
type: "string",
|
|
4312
|
+
required: true,
|
|
4313
|
+
description: "Path to credential file"
|
|
4314
|
+
},
|
|
4315
|
+
password: {
|
|
4316
|
+
type: "string",
|
|
4317
|
+
description: "File password (keystore/p12)"
|
|
4318
|
+
},
|
|
4319
|
+
"key-alias": {
|
|
4320
|
+
type: "string",
|
|
4321
|
+
description: "Keystore alias"
|
|
4322
|
+
},
|
|
4323
|
+
"key-password": {
|
|
4324
|
+
type: "string",
|
|
4325
|
+
description: "Keystore key password"
|
|
4326
|
+
},
|
|
4327
|
+
"key-id": {
|
|
4328
|
+
type: "string",
|
|
4329
|
+
description: "ASC API key ID"
|
|
4330
|
+
},
|
|
4331
|
+
"issuer-id": {
|
|
4332
|
+
type: "string",
|
|
4333
|
+
description: "ASC API issuer ID"
|
|
4334
|
+
},
|
|
4335
|
+
"apple-team-identifier": {
|
|
4336
|
+
type: "string",
|
|
4337
|
+
description: "Apple Team ID"
|
|
4338
|
+
}
|
|
4339
|
+
},
|
|
4340
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
4341
|
+
const credential = yield* uploadCredential(yield* apiClient, {
|
|
4342
|
+
platform: args.platform,
|
|
4343
|
+
type: args.type,
|
|
4344
|
+
name: args.name,
|
|
4345
|
+
filePath: args.file,
|
|
4346
|
+
...args.password === void 0 ? {} : { password: args.password },
|
|
4347
|
+
...args["key-alias"] === void 0 ? {} : { keyAlias: args["key-alias"] },
|
|
4348
|
+
...args["key-password"] === void 0 ? {} : { keyPassword: args["key-password"] },
|
|
4349
|
+
...args["key-id"] === void 0 ? {} : { keyId: args["key-id"] },
|
|
4350
|
+
...args["issuer-id"] === void 0 ? {} : { issuerId: args["issuer-id"] },
|
|
4351
|
+
...args["apple-team-identifier"] === void 0 ? {} : { appleTeamIdentifier: args["apple-team-identifier"] }
|
|
4352
|
+
});
|
|
4353
|
+
yield* Console.log("Credential uploaded successfully.");
|
|
4354
|
+
yield* Console.log("");
|
|
4355
|
+
yield* printKeyValue([
|
|
4356
|
+
["ID", credential.id],
|
|
4357
|
+
["Name", credential.name],
|
|
4358
|
+
["Platform", credential.platform],
|
|
4359
|
+
["Type", credential.type]
|
|
4360
|
+
]);
|
|
4361
|
+
}))
|
|
4362
|
+
});
|
|
3994
4363
|
|
|
3995
4364
|
//#endregion
|
|
3996
4365
|
//#region src/commands/credentials/index.ts
|
|
3997
|
-
const credentialsCommand =
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4366
|
+
const credentialsCommand = defineCommand({
|
|
4367
|
+
meta: {
|
|
4368
|
+
name: "credentials",
|
|
4369
|
+
description: "Manage credentials"
|
|
4370
|
+
},
|
|
4371
|
+
subCommands: {
|
|
4372
|
+
list: listCommand$3,
|
|
4373
|
+
upload: uploadCommand,
|
|
4374
|
+
delete: deleteCommand$3
|
|
4375
|
+
}
|
|
4376
|
+
});
|
|
4002
4377
|
|
|
4003
4378
|
//#endregion
|
|
4004
4379
|
//#region src/commands/env/helpers.ts
|
|
4005
4380
|
var EnvResourceNotFoundError = class extends Data.TaggedError("EnvResourceNotFoundError") {};
|
|
4006
|
-
const
|
|
4381
|
+
const envErrorExtras = {
|
|
4007
4382
|
EnvResourceNotFoundError: 1,
|
|
4008
4383
|
SystemError: 6,
|
|
4009
4384
|
BadArgument: 6
|
|
4010
|
-
}
|
|
4385
|
+
};
|
|
4011
4386
|
|
|
4012
4387
|
//#endregion
|
|
4013
4388
|
//#region src/commands/env/delete.ts
|
|
4014
|
-
const
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
}
|
|
4389
|
+
const deleteCommand$2 = defineCommand({
|
|
4390
|
+
meta: {
|
|
4391
|
+
name: "delete",
|
|
4392
|
+
description: "Delete an environment variable by key"
|
|
4393
|
+
},
|
|
4394
|
+
args: {
|
|
4395
|
+
key: {
|
|
4396
|
+
type: "positional",
|
|
4397
|
+
required: true,
|
|
4398
|
+
description: "Env var key"
|
|
4399
|
+
},
|
|
4400
|
+
environment: {
|
|
4401
|
+
type: "string",
|
|
4402
|
+
default: "production",
|
|
4403
|
+
description: "Target environment"
|
|
4404
|
+
}
|
|
4405
|
+
},
|
|
4406
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
4407
|
+
const projectId = yield* readProjectId;
|
|
4408
|
+
const api = yield* apiClient;
|
|
4409
|
+
const match = (yield* api["env-vars"].list({ urlParams: {
|
|
4410
|
+
projectId,
|
|
4411
|
+
environment: args.environment
|
|
4412
|
+
} })).items.find((item) => item.key === args.key);
|
|
4413
|
+
if (!match) return yield* new EnvResourceNotFoundError({ message: `Environment variable ${args.key} not found in ${args.environment}` });
|
|
4414
|
+
yield* api["env-vars"].delete({ path: { id: match.id } });
|
|
4415
|
+
yield* Console.log(`Deleted ${args.key} from ${args.environment}`);
|
|
4416
|
+
}), envErrorExtras)
|
|
4417
|
+
});
|
|
4030
4418
|
|
|
4031
4419
|
//#endregion
|
|
4032
4420
|
//#region src/commands/env/export.ts
|
|
4033
|
-
const
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
}
|
|
4044
|
-
|
|
4421
|
+
const exportCommand = defineCommand({
|
|
4422
|
+
meta: {
|
|
4423
|
+
name: "export",
|
|
4424
|
+
description: "Print env vars in KEY='value' format"
|
|
4425
|
+
},
|
|
4426
|
+
args: { environment: {
|
|
4427
|
+
type: "string",
|
|
4428
|
+
default: "production",
|
|
4429
|
+
description: "Target environment"
|
|
4430
|
+
} },
|
|
4431
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
4432
|
+
const projectId = yield* readProjectId;
|
|
4433
|
+
const result = yield* (yield* apiClient)["env-vars"].export({ urlParams: {
|
|
4434
|
+
projectId,
|
|
4435
|
+
environment: args.environment
|
|
4436
|
+
} });
|
|
4437
|
+
for (const item of result.items) {
|
|
4438
|
+
const escaped = item.value.replaceAll("'", String.raw`'\''`);
|
|
4439
|
+
yield* Console.log(`${item.key}='${escaped}'`);
|
|
4440
|
+
}
|
|
4441
|
+
}), envErrorExtras)
|
|
4442
|
+
});
|
|
4045
4443
|
|
|
4046
4444
|
//#endregion
|
|
4047
4445
|
//#region src/commands/env/get.ts
|
|
4048
|
-
const
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4446
|
+
const getCommand$1 = defineCommand({
|
|
4447
|
+
meta: {
|
|
4448
|
+
name: "get",
|
|
4449
|
+
description: "Show an environment variable"
|
|
4450
|
+
},
|
|
4451
|
+
args: { id: {
|
|
4452
|
+
type: "positional",
|
|
4453
|
+
required: true,
|
|
4454
|
+
description: "Env var ID"
|
|
4455
|
+
} },
|
|
4456
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
4457
|
+
const envVar = yield* (yield* apiClient)["env-vars"].get({ path: { id: args.id } });
|
|
4458
|
+
yield* printKeyValue([
|
|
4459
|
+
["ID", envVar.id],
|
|
4460
|
+
["Key", envVar.key],
|
|
4461
|
+
["Environment", envVar.environment],
|
|
4462
|
+
["Visibility", envVar.visibility],
|
|
4463
|
+
["Value", envVar.visibility === "plaintext" ? envVar.value ?? "" : "******"],
|
|
4464
|
+
["Created", envVar.createdAt],
|
|
4465
|
+
["Updated", envVar.updatedAt]
|
|
4466
|
+
]);
|
|
4467
|
+
}), envErrorExtras)
|
|
4468
|
+
});
|
|
4061
4469
|
|
|
4062
4470
|
//#endregion
|
|
4063
4471
|
//#region src/commands/env/import.ts
|
|
4064
|
-
const
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
}
|
|
4472
|
+
const importCommand = defineCommand({
|
|
4473
|
+
meta: {
|
|
4474
|
+
name: "import",
|
|
4475
|
+
description: "Bulk-import env vars from a dotenv file"
|
|
4476
|
+
},
|
|
4477
|
+
args: {
|
|
4478
|
+
file: {
|
|
4479
|
+
type: "positional",
|
|
4480
|
+
required: true,
|
|
4481
|
+
description: "Path to .env file"
|
|
4482
|
+
},
|
|
4483
|
+
environment: {
|
|
4484
|
+
type: "string",
|
|
4485
|
+
default: "production",
|
|
4486
|
+
description: "Target environment"
|
|
4487
|
+
}
|
|
4488
|
+
},
|
|
4489
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
4490
|
+
const content = yield* (yield* FileSystem.FileSystem).readFileString(args.file);
|
|
4491
|
+
const projectId = yield* readProjectId;
|
|
4492
|
+
const result = yield* (yield* apiClient)["env-vars"].bulkImport({ payload: {
|
|
4493
|
+
projectId,
|
|
4494
|
+
environment: args.environment,
|
|
4495
|
+
content,
|
|
4496
|
+
visibility: "plaintext"
|
|
4497
|
+
} });
|
|
4498
|
+
yield* Console.log(`Imported: ${String(result.created)} created, ${String(result.updated)} updated, ${String(result.skipped)} skipped`);
|
|
4499
|
+
}), envErrorExtras)
|
|
4500
|
+
});
|
|
4080
4501
|
|
|
4081
4502
|
//#endregion
|
|
4082
4503
|
//#region src/commands/env/list.ts
|
|
4083
|
-
const
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
yield*
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4504
|
+
const listCommand$2 = defineCommand({
|
|
4505
|
+
meta: {
|
|
4506
|
+
name: "list",
|
|
4507
|
+
description: "List environment variables"
|
|
4508
|
+
},
|
|
4509
|
+
args: { environment: {
|
|
4510
|
+
type: "string",
|
|
4511
|
+
description: "Filter by environment"
|
|
4512
|
+
} },
|
|
4513
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
4514
|
+
const projectId = yield* readProjectId;
|
|
4515
|
+
const api = yield* apiClient;
|
|
4516
|
+
const envFilter = args.environment ? { environment: args.environment } : {};
|
|
4517
|
+
const result = yield* api["env-vars"].list({ urlParams: {
|
|
4518
|
+
projectId,
|
|
4519
|
+
...envFilter
|
|
4520
|
+
} });
|
|
4521
|
+
if (result.items.length === 0) {
|
|
4522
|
+
yield* Console.log("No environment variables found.");
|
|
4523
|
+
return;
|
|
4524
|
+
}
|
|
4525
|
+
yield* printTable([
|
|
4526
|
+
"Key",
|
|
4527
|
+
"Environment",
|
|
4528
|
+
"Visibility",
|
|
4529
|
+
"Value"
|
|
4530
|
+
], result.items.map((item) => [
|
|
4531
|
+
item.key,
|
|
4532
|
+
item.environment,
|
|
4533
|
+
item.visibility,
|
|
4534
|
+
item.visibility === "plaintext" ? item.value ?? "" : "••••••"
|
|
4535
|
+
]));
|
|
4536
|
+
}), envErrorExtras)
|
|
4537
|
+
});
|
|
4111
4538
|
|
|
4112
4539
|
//#endregion
|
|
4113
4540
|
//#region src/commands/env/pull.ts
|
|
4114
|
-
const
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
}
|
|
4125
|
-
|
|
4541
|
+
const pullCommand = defineCommand({
|
|
4542
|
+
meta: {
|
|
4543
|
+
name: "pull",
|
|
4544
|
+
description: "Print env vars in `export KEY='value'` format"
|
|
4545
|
+
},
|
|
4546
|
+
args: { environment: {
|
|
4547
|
+
type: "string",
|
|
4548
|
+
default: "production",
|
|
4549
|
+
description: "Target environment"
|
|
4550
|
+
} },
|
|
4551
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
4552
|
+
const projectId = yield* readProjectId;
|
|
4553
|
+
const result = yield* (yield* apiClient)["env-vars"].export({ urlParams: {
|
|
4554
|
+
projectId,
|
|
4555
|
+
environment: args.environment
|
|
4556
|
+
} });
|
|
4557
|
+
for (const item of result.items) {
|
|
4558
|
+
const escaped = item.value.replaceAll("'", String.raw`'\''`);
|
|
4559
|
+
yield* Console.log(`export ${item.key}='${escaped}'`);
|
|
4560
|
+
}
|
|
4561
|
+
}), envErrorExtras)
|
|
4562
|
+
});
|
|
4126
4563
|
|
|
4127
4564
|
//#endregion
|
|
4128
4565
|
//#region src/commands/env/set.ts
|
|
4129
|
-
const
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4566
|
+
const setCommand$1 = defineCommand({
|
|
4567
|
+
meta: {
|
|
4568
|
+
name: "set",
|
|
4569
|
+
description: "Create or update an environment variable"
|
|
4570
|
+
},
|
|
4571
|
+
args: {
|
|
4572
|
+
keyValue: {
|
|
4573
|
+
type: "positional",
|
|
4574
|
+
required: true,
|
|
4575
|
+
description: "KEY=VALUE pair (e.g. API_KEY=abc123)"
|
|
4576
|
+
},
|
|
4577
|
+
environment: {
|
|
4578
|
+
type: "string",
|
|
4579
|
+
default: "production",
|
|
4580
|
+
description: "Target environment"
|
|
4581
|
+
},
|
|
4582
|
+
visibility: {
|
|
4583
|
+
type: "enum",
|
|
4584
|
+
options: [
|
|
4585
|
+
"plaintext",
|
|
4586
|
+
"sensitive",
|
|
4587
|
+
"secret"
|
|
4588
|
+
],
|
|
4589
|
+
default: "plaintext",
|
|
4590
|
+
description: "Value visibility"
|
|
4591
|
+
}
|
|
4592
|
+
},
|
|
4593
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
4594
|
+
const { key, value } = yield* parseKeyValue(args.keyValue);
|
|
4595
|
+
const { environment } = args;
|
|
4596
|
+
const { visibility } = args;
|
|
4597
|
+
const projectId = yield* readProjectId;
|
|
4598
|
+
const api = yield* apiClient;
|
|
4599
|
+
const match = (yield* api["env-vars"].list({ urlParams: {
|
|
4600
|
+
projectId,
|
|
4601
|
+
environment
|
|
4602
|
+
} })).items.find((item) => item.key === key);
|
|
4603
|
+
if (match) {
|
|
4604
|
+
yield* api["env-vars"].update({
|
|
4605
|
+
path: { id: match.id },
|
|
4606
|
+
payload: {
|
|
4607
|
+
value,
|
|
4608
|
+
visibility
|
|
4609
|
+
}
|
|
4610
|
+
});
|
|
4611
|
+
yield* Console.log(`Updated ${key} in ${environment}`);
|
|
4612
|
+
} else {
|
|
4613
|
+
yield* api["env-vars"].create({ payload: {
|
|
4614
|
+
projectId,
|
|
4615
|
+
environment,
|
|
4616
|
+
key,
|
|
4151
4617
|
value,
|
|
4152
4618
|
visibility
|
|
4153
|
-
}
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
}
|
|
4157
|
-
|
|
4158
|
-
projectId,
|
|
4159
|
-
environment,
|
|
4160
|
-
key,
|
|
4161
|
-
value,
|
|
4162
|
-
visibility
|
|
4163
|
-
} });
|
|
4164
|
-
yield* Console.log(`Created ${key} in ${environment}`);
|
|
4165
|
-
}
|
|
4166
|
-
}).pipe(handleEnvCommandErrors));
|
|
4619
|
+
} });
|
|
4620
|
+
yield* Console.log(`Created ${key} in ${environment}`);
|
|
4621
|
+
}
|
|
4622
|
+
}), envErrorExtras)
|
|
4623
|
+
});
|
|
4167
4624
|
|
|
4168
4625
|
//#endregion
|
|
4169
4626
|
//#region src/commands/env/index.ts
|
|
4170
|
-
const envCommand =
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4627
|
+
const envCommand = defineCommand({
|
|
4628
|
+
meta: {
|
|
4629
|
+
name: "env",
|
|
4630
|
+
description: "Manage environment variables"
|
|
4631
|
+
},
|
|
4632
|
+
subCommands: {
|
|
4633
|
+
list: listCommand$2,
|
|
4634
|
+
get: getCommand$1,
|
|
4635
|
+
set: setCommand$1,
|
|
4636
|
+
delete: deleteCommand$2,
|
|
4637
|
+
import: importCommand,
|
|
4638
|
+
export: exportCommand,
|
|
4639
|
+
pull: pullCommand
|
|
4640
|
+
}
|
|
4641
|
+
});
|
|
4179
4642
|
|
|
4180
4643
|
//#endregion
|
|
4181
4644
|
//#region src/commands/fingerprint/compare.ts
|
|
4182
|
-
const
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4645
|
+
const compareCommand = defineCommand({
|
|
4646
|
+
meta: {
|
|
4647
|
+
name: "compare",
|
|
4648
|
+
description: "Compare a fingerprint hash against the current project"
|
|
4649
|
+
},
|
|
4650
|
+
args: { hash: {
|
|
4651
|
+
type: "positional",
|
|
4652
|
+
required: true,
|
|
4653
|
+
description: "Fingerprint hash to compare"
|
|
4654
|
+
} },
|
|
4655
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
4656
|
+
const result = yield* runFingerprintFull(yield* (yield* CliRuntime).cwd);
|
|
4657
|
+
if (result.hash === args.hash) {
|
|
4658
|
+
yield* Console.log("Fingerprints match.");
|
|
4659
|
+
return;
|
|
4660
|
+
}
|
|
4661
|
+
yield* Console.log("Fingerprints differ.");
|
|
4662
|
+
yield* Console.log(` Local: ${result.hash}`);
|
|
4663
|
+
yield* Console.log(` Provided: ${args.hash}`);
|
|
4664
|
+
return yield* exitWith(1, "Fingerprint mismatch");
|
|
4665
|
+
}), { FingerprintError: 2 })
|
|
4666
|
+
});
|
|
4194
4667
|
|
|
4195
4668
|
//#endregion
|
|
4196
4669
|
//#region src/commands/fingerprint/generate.ts
|
|
4197
|
-
const generateCommand =
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
}
|
|
4670
|
+
const generateCommand = defineCommand({
|
|
4671
|
+
meta: {
|
|
4672
|
+
name: "generate",
|
|
4673
|
+
description: "Compute a fingerprint for the current project"
|
|
4674
|
+
},
|
|
4675
|
+
run: async () => runEffect(Effect.gen(function* () {
|
|
4676
|
+
const result = yield* runFingerprintFull(yield* (yield* CliRuntime).cwd);
|
|
4677
|
+
yield* Console.log(result.hash);
|
|
4678
|
+
if (result.sources.length > 0) yield* Console.log(`${result.sources.length} sources`);
|
|
4679
|
+
}), { FingerprintError: 2 })
|
|
4680
|
+
});
|
|
4202
4681
|
|
|
4203
4682
|
//#endregion
|
|
4204
4683
|
//#region src/commands/fingerprint/index.ts
|
|
4205
|
-
const fingerprintCommand =
|
|
4684
|
+
const fingerprintCommand = defineCommand({
|
|
4685
|
+
meta: {
|
|
4686
|
+
name: "fingerprint",
|
|
4687
|
+
description: "Fingerprint utilities"
|
|
4688
|
+
},
|
|
4689
|
+
subCommands: {
|
|
4690
|
+
generate: generateCommand,
|
|
4691
|
+
compare: compareCommand
|
|
4692
|
+
}
|
|
4693
|
+
});
|
|
4206
4694
|
|
|
4207
4695
|
//#endregion
|
|
4208
4696
|
//#region src/commands/init.ts
|
|
4209
|
-
const initCommand =
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
} else {
|
|
4224
|
-
yield* Console.log("No existing project found. Creating new project...");
|
|
4225
|
-
const project = yield* api.projects.create({ payload: {
|
|
4226
|
-
name,
|
|
4227
|
-
slug
|
|
4697
|
+
const initCommand = defineCommand({
|
|
4698
|
+
meta: {
|
|
4699
|
+
name: "init",
|
|
4700
|
+
description: "Link the local Expo project to a better-update project"
|
|
4701
|
+
},
|
|
4702
|
+
run: async () => runEffect(Effect.gen(function* () {
|
|
4703
|
+
const expo = asRecord((yield* readAppJson)["expo"]);
|
|
4704
|
+
const name = asString$1(expo?.["name"]) ?? asString$1(expo?.["slug"]) ?? "untitled";
|
|
4705
|
+
const slug = yield* readSlug;
|
|
4706
|
+
yield* Console.log(`Linking project: ${name} (${slug})`);
|
|
4707
|
+
const api = yield* apiClient;
|
|
4708
|
+
const { items } = yield* api.projects.list({ urlParams: {
|
|
4709
|
+
page: 1,
|
|
4710
|
+
limit: 100
|
|
4228
4711
|
} });
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
}
|
|
4712
|
+
const existing = items.find((project) => project.slug === slug);
|
|
4713
|
+
if (existing) {
|
|
4714
|
+
yield* Console.log(`Found existing project: ${existing.name} (${existing.id})`);
|
|
4715
|
+
yield* writeProjectId(existing.id);
|
|
4716
|
+
} else {
|
|
4717
|
+
yield* Console.log("No existing project found. Creating new project...");
|
|
4718
|
+
const project = yield* api.projects.create({ payload: {
|
|
4719
|
+
name,
|
|
4720
|
+
slug
|
|
4721
|
+
} });
|
|
4722
|
+
yield* Console.log(`Created project: ${project.name} (${project.id})`);
|
|
4723
|
+
yield* writeProjectId(project.id);
|
|
4724
|
+
}
|
|
4725
|
+
yield* Console.log("Project linked successfully. ID saved to app.json.");
|
|
4726
|
+
}))
|
|
4727
|
+
});
|
|
4234
4728
|
|
|
4235
4729
|
//#endregion
|
|
4236
4730
|
//#region src/lib/browser-login.ts
|
|
@@ -4375,17 +4869,27 @@ const createBrowserLoginServer = (options = {}) => {
|
|
|
4375
4869
|
};
|
|
4376
4870
|
};
|
|
4377
4871
|
|
|
4872
|
+
//#endregion
|
|
4873
|
+
//#region src/lib/prompts.ts
|
|
4874
|
+
const promptPassword = async (message) => {
|
|
4875
|
+
const value = await password({ message });
|
|
4876
|
+
if (isCancel(value)) {
|
|
4877
|
+
cancel("Operation cancelled.");
|
|
4878
|
+
process.exit(130);
|
|
4879
|
+
}
|
|
4880
|
+
return value;
|
|
4881
|
+
};
|
|
4882
|
+
|
|
4378
4883
|
//#endregion
|
|
4379
4884
|
//#region src/application/login.ts
|
|
4380
|
-
const tokenPrompt = Prompt.password({ message: "Paste your API key (from dashboard > API Keys):" });
|
|
4381
4885
|
const buildOpenBrowserCommand = (platform, url) => {
|
|
4382
|
-
if (platform === "darwin") return Command
|
|
4383
|
-
if (platform === "win32") return Command
|
|
4384
|
-
return Command
|
|
4886
|
+
if (platform === "darwin") return Command.make("open", url);
|
|
4887
|
+
if (platform === "win32") return Command.make("cmd", "/c", "start", "", url);
|
|
4888
|
+
return Command.make("xdg-open", url);
|
|
4385
4889
|
};
|
|
4386
4890
|
const openBrowser = (url) => Effect.gen(function* () {
|
|
4387
4891
|
const command = buildOpenBrowserCommand((yield* CliRuntime).platform, url);
|
|
4388
|
-
if (!(yield* Command
|
|
4892
|
+
if (!(yield* Command.exitCode(command).pipe(Effect.map((code) => code === 0), Effect.catchAll(() => Effect.succeed(false))))) yield* Console.log(`Open this URL manually:\n${url}`);
|
|
4389
4893
|
});
|
|
4390
4894
|
const browserLogin = Effect.scoped(Effect.gen(function* () {
|
|
4391
4895
|
const configStore = yield* ConfigStore;
|
|
@@ -4405,7 +4909,7 @@ const manualLogin = Effect.gen(function* () {
|
|
|
4405
4909
|
yield* Console.log("Log in to better-update with an existing API key");
|
|
4406
4910
|
yield* Console.log("Get your API key from the dashboard > API Keys page");
|
|
4407
4911
|
yield* Console.log("");
|
|
4408
|
-
const token =
|
|
4912
|
+
const token = yield* Effect.promise(async () => promptPassword("Paste your API key (from dashboard > API Keys):"));
|
|
4409
4913
|
yield* (yield* AuthStore).saveToken(token);
|
|
4410
4914
|
yield* Console.log("");
|
|
4411
4915
|
yield* Console.log("Logged in successfully. Token saved to ~/.better-update/auth.json");
|
|
@@ -4420,198 +4924,298 @@ const runLogin = (options) => Effect.gen(function* () {
|
|
|
4420
4924
|
|
|
4421
4925
|
//#endregion
|
|
4422
4926
|
//#region src/commands/login.ts
|
|
4423
|
-
const
|
|
4424
|
-
|
|
4425
|
-
|
|
4927
|
+
const loginCommand = defineCommand({
|
|
4928
|
+
meta: {
|
|
4929
|
+
name: "login",
|
|
4930
|
+
description: "Log in to better-update"
|
|
4931
|
+
},
|
|
4932
|
+
args: { "api-key": {
|
|
4933
|
+
type: "boolean",
|
|
4934
|
+
description: "Paste an API key manually instead of opening the browser"
|
|
4935
|
+
} },
|
|
4936
|
+
run: async ({ args }) => runEffect(runLogin({ manualApiKey: args["api-key"] ?? false }))
|
|
4937
|
+
});
|
|
4426
4938
|
|
|
4427
4939
|
//#endregion
|
|
4428
4940
|
//#region src/commands/logout.ts
|
|
4429
|
-
const logoutCommand =
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4941
|
+
const logoutCommand = defineCommand({
|
|
4942
|
+
meta: {
|
|
4943
|
+
name: "logout",
|
|
4944
|
+
description: "Remove the stored auth token"
|
|
4945
|
+
},
|
|
4946
|
+
run: async () => runEffect(Effect.gen(function* () {
|
|
4947
|
+
yield* (yield* AuthStore).clearToken;
|
|
4948
|
+
yield* Console.log("Logged out. Auth token removed.");
|
|
4949
|
+
}))
|
|
4950
|
+
});
|
|
4433
4951
|
|
|
4434
4952
|
//#endregion
|
|
4435
4953
|
//#region src/commands/projects.ts
|
|
4436
|
-
const
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4954
|
+
const listCommand$1 = defineCommand({
|
|
4955
|
+
meta: {
|
|
4956
|
+
name: "list",
|
|
4957
|
+
description: "List all projects"
|
|
4958
|
+
},
|
|
4959
|
+
run: async () => runEffect(Effect.gen(function* () {
|
|
4960
|
+
const { items } = yield* (yield* apiClient).projects.list({ urlParams: {
|
|
4961
|
+
page: 1,
|
|
4962
|
+
limit: 1e3
|
|
4963
|
+
} });
|
|
4964
|
+
if (items.length === 0) {
|
|
4965
|
+
yield* Console.log("No projects found.");
|
|
4966
|
+
return;
|
|
4967
|
+
}
|
|
4968
|
+
yield* printTable([
|
|
4969
|
+
"ID",
|
|
4970
|
+
"Name",
|
|
4971
|
+
"Slug",
|
|
4972
|
+
"Created"
|
|
4973
|
+
], items.map((project) => [
|
|
4974
|
+
project.id,
|
|
4975
|
+
project.name,
|
|
4976
|
+
project.slug,
|
|
4977
|
+
project.createdAt
|
|
4978
|
+
]));
|
|
4979
|
+
}))
|
|
4980
|
+
});
|
|
4981
|
+
const createCommand = defineCommand({
|
|
4982
|
+
meta: {
|
|
4983
|
+
name: "create",
|
|
4984
|
+
description: "Create a new project"
|
|
4985
|
+
},
|
|
4986
|
+
args: {
|
|
4987
|
+
name: {
|
|
4988
|
+
type: "string",
|
|
4989
|
+
required: true,
|
|
4990
|
+
description: "Display name"
|
|
4991
|
+
},
|
|
4992
|
+
slug: {
|
|
4993
|
+
type: "string",
|
|
4994
|
+
required: true,
|
|
4995
|
+
description: "URL-safe slug"
|
|
4996
|
+
}
|
|
4997
|
+
},
|
|
4998
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
4999
|
+
const project = yield* (yield* apiClient).projects.create({ payload: {
|
|
5000
|
+
name: args.name,
|
|
5001
|
+
slug: args.slug
|
|
5002
|
+
} });
|
|
5003
|
+
yield* printKeyValue([
|
|
5004
|
+
["ID", project.id],
|
|
5005
|
+
["Name", project.name],
|
|
5006
|
+
["Slug", project.slug],
|
|
5007
|
+
["Created", project.createdAt]
|
|
5008
|
+
]);
|
|
5009
|
+
}))
|
|
5010
|
+
});
|
|
5011
|
+
const getCommand = defineCommand({
|
|
5012
|
+
meta: {
|
|
5013
|
+
name: "get",
|
|
5014
|
+
description: "Show a project"
|
|
5015
|
+
},
|
|
5016
|
+
args: { id: {
|
|
5017
|
+
type: "positional",
|
|
5018
|
+
required: true,
|
|
5019
|
+
description: "Project ID"
|
|
5020
|
+
} },
|
|
5021
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
5022
|
+
const project = yield* (yield* apiClient).projects.get({ path: { id: args.id } });
|
|
5023
|
+
yield* printKeyValue([
|
|
5024
|
+
["ID", project.id],
|
|
5025
|
+
["Name", project.name],
|
|
5026
|
+
["Slug", project.slug],
|
|
5027
|
+
["Created", project.createdAt]
|
|
5028
|
+
]);
|
|
5029
|
+
}))
|
|
5030
|
+
});
|
|
5031
|
+
const renameCommand = defineCommand({
|
|
5032
|
+
meta: {
|
|
5033
|
+
name: "rename",
|
|
5034
|
+
description: "Rename a project"
|
|
5035
|
+
},
|
|
5036
|
+
args: {
|
|
5037
|
+
id: {
|
|
5038
|
+
type: "positional",
|
|
5039
|
+
required: true,
|
|
5040
|
+
description: "Project ID"
|
|
5041
|
+
},
|
|
5042
|
+
name: {
|
|
5043
|
+
type: "string",
|
|
5044
|
+
required: true,
|
|
5045
|
+
description: "New display name"
|
|
5046
|
+
}
|
|
5047
|
+
},
|
|
5048
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
5049
|
+
const project = yield* (yield* apiClient).projects.rename({
|
|
5050
|
+
path: { id: args.id },
|
|
5051
|
+
payload: { name: args.name }
|
|
5052
|
+
});
|
|
5053
|
+
yield* Console.log(`Project renamed to "${project.name}".`);
|
|
5054
|
+
}))
|
|
5055
|
+
});
|
|
5056
|
+
const deleteCommand$1 = defineCommand({
|
|
5057
|
+
meta: {
|
|
5058
|
+
name: "delete",
|
|
5059
|
+
description: "Delete a project"
|
|
5060
|
+
},
|
|
5061
|
+
args: { id: {
|
|
5062
|
+
type: "positional",
|
|
5063
|
+
required: true,
|
|
5064
|
+
description: "Project ID"
|
|
5065
|
+
} },
|
|
5066
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
5067
|
+
yield* (yield* apiClient).projects.delete({ path: { id: args.id } });
|
|
5068
|
+
yield* Console.log(`Project ${args.id} deleted.`);
|
|
5069
|
+
}))
|
|
5070
|
+
});
|
|
5071
|
+
const projectsCommand = defineCommand({
|
|
5072
|
+
meta: {
|
|
5073
|
+
name: "projects",
|
|
5074
|
+
description: "Manage projects"
|
|
5075
|
+
},
|
|
5076
|
+
subCommands: {
|
|
5077
|
+
list: listCommand$1,
|
|
5078
|
+
create: createCommand,
|
|
5079
|
+
get: getCommand,
|
|
5080
|
+
rename: renameCommand,
|
|
5081
|
+
delete: deleteCommand$1
|
|
4448
5082
|
}
|
|
4449
|
-
|
|
4450
|
-
"ID",
|
|
4451
|
-
"Name",
|
|
4452
|
-
"Slug",
|
|
4453
|
-
"Created"
|
|
4454
|
-
], items.map((project) => [
|
|
4455
|
-
project.id,
|
|
4456
|
-
project.name,
|
|
4457
|
-
project.slug,
|
|
4458
|
-
project.createdAt
|
|
4459
|
-
]));
|
|
4460
|
-
}).pipe(handleErrors));
|
|
4461
|
-
const createCommand = Command.make("create", {
|
|
4462
|
-
name: nameOption,
|
|
4463
|
-
slug: slugOption
|
|
4464
|
-
}, (opts) => Effect.gen(function* () {
|
|
4465
|
-
const project = yield* (yield* apiClient).projects.create({ payload: {
|
|
4466
|
-
name: opts.name,
|
|
4467
|
-
slug: opts.slug
|
|
4468
|
-
} });
|
|
4469
|
-
yield* printKeyValue([
|
|
4470
|
-
["ID", project.id],
|
|
4471
|
-
["Name", project.name],
|
|
4472
|
-
["Slug", project.slug],
|
|
4473
|
-
["Created", project.createdAt]
|
|
4474
|
-
]);
|
|
4475
|
-
}).pipe(handleErrors));
|
|
4476
|
-
const getCommand = Command.make("get", { id: idArg }, (opts) => Effect.gen(function* () {
|
|
4477
|
-
const project = yield* (yield* apiClient).projects.get({ path: { id: opts.id } });
|
|
4478
|
-
yield* printKeyValue([
|
|
4479
|
-
["ID", project.id],
|
|
4480
|
-
["Name", project.name],
|
|
4481
|
-
["Slug", project.slug],
|
|
4482
|
-
["Created", project.createdAt]
|
|
4483
|
-
]);
|
|
4484
|
-
}).pipe(handleErrors));
|
|
4485
|
-
const renameCommand = Command.make("rename", {
|
|
4486
|
-
id: idArg,
|
|
4487
|
-
name: nameOption
|
|
4488
|
-
}, (opts) => Effect.gen(function* () {
|
|
4489
|
-
const project = yield* (yield* apiClient).projects.rename({
|
|
4490
|
-
path: { id: opts.id },
|
|
4491
|
-
payload: { name: opts.name }
|
|
4492
|
-
});
|
|
4493
|
-
yield* Console.log(`Project renamed to "${project.name}".`);
|
|
4494
|
-
}).pipe(handleErrors));
|
|
4495
|
-
const deleteCommand$1 = Command.make("delete", { id: idArg }, (opts) => Effect.gen(function* () {
|
|
4496
|
-
yield* (yield* apiClient).projects.delete({ path: { id: opts.id } });
|
|
4497
|
-
yield* Console.log(`Project ${opts.id} deleted.`);
|
|
4498
|
-
}).pipe(handleErrors));
|
|
4499
|
-
const projectsCommand = Command.make("projects", {}, () => Console.log("Manage projects. Run with --help for subcommands.")).pipe(Command.withSubcommands([
|
|
4500
|
-
listCommand$1,
|
|
4501
|
-
createCommand,
|
|
4502
|
-
getCommand,
|
|
4503
|
-
renameCommand,
|
|
4504
|
-
deleteCommand$1
|
|
4505
|
-
]));
|
|
5083
|
+
});
|
|
4506
5084
|
|
|
4507
5085
|
//#endregion
|
|
4508
5086
|
//#region src/commands/status.ts
|
|
4509
|
-
const statusCommand =
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
5087
|
+
const statusCommand = defineCommand({
|
|
5088
|
+
meta: {
|
|
5089
|
+
name: "status",
|
|
5090
|
+
description: "Show project status (credentials, builds)"
|
|
5091
|
+
},
|
|
5092
|
+
run: async () => runEffect(Effect.gen(function* () {
|
|
5093
|
+
const projectId = yield* readProjectId;
|
|
5094
|
+
const api = yield* apiClient;
|
|
5095
|
+
const { project, credentials, builds } = yield* Effect.all({
|
|
5096
|
+
project: api.projects.get({ path: { id: projectId } }),
|
|
5097
|
+
credentials: listAllCredentials(api),
|
|
5098
|
+
builds: api.builds.list({ urlParams: { projectId } })
|
|
5099
|
+
}, { concurrency: "unbounded" });
|
|
5100
|
+
yield* Console.log("Project");
|
|
5101
|
+
yield* Console.log("-------");
|
|
5102
|
+
yield* printKeyValue([
|
|
5103
|
+
["Name", project.name],
|
|
5104
|
+
["ID", project.id],
|
|
5105
|
+
["Slug", project.slug],
|
|
5106
|
+
["Created", project.createdAt]
|
|
5107
|
+
]);
|
|
5108
|
+
yield* Console.log("");
|
|
5109
|
+
yield* Console.log("Credentials");
|
|
5110
|
+
yield* Console.log("-----------");
|
|
5111
|
+
const iosCreds = credentials.filter((cred) => cred.platform === "ios").length;
|
|
5112
|
+
const androidCreds = credentials.filter((cred) => cred.platform === "android").length;
|
|
5113
|
+
yield* printKeyValue([
|
|
5114
|
+
["iOS", String(iosCreds)],
|
|
5115
|
+
["Android", String(androidCreds)],
|
|
5116
|
+
["Total", String(credentials.length)]
|
|
5117
|
+
]);
|
|
5118
|
+
yield* Console.log("");
|
|
5119
|
+
yield* Console.log("Builds");
|
|
5120
|
+
yield* Console.log("------");
|
|
5121
|
+
yield* printKeyValue([["Total", String(builds.total)]]);
|
|
5122
|
+
}))
|
|
5123
|
+
});
|
|
4540
5124
|
|
|
4541
5125
|
//#endregion
|
|
4542
5126
|
//#region src/commands/update/helpers.ts
|
|
4543
5127
|
var UpdateCommandError = class extends Data.TaggedError("UpdateCommandError") {};
|
|
4544
|
-
const
|
|
5128
|
+
const updateErrorExtras = {
|
|
4545
5129
|
UpdateCommandError: 2,
|
|
4546
5130
|
BuildProfileError: 2,
|
|
4547
5131
|
RuntimeVersionError: 2,
|
|
4548
5132
|
UpdateRollbackError: 2,
|
|
4549
5133
|
UpdatePromoteError: 2
|
|
4550
|
-
}
|
|
5134
|
+
};
|
|
4551
5135
|
const resolveNamedResourceId = (params) => resolveNamedResourceId$2(params, (message) => new UpdateCommandError({ message }));
|
|
4552
5136
|
|
|
4553
5137
|
//#endregion
|
|
4554
5138
|
//#region src/commands/update/delete.ts
|
|
4555
|
-
const
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
}
|
|
5139
|
+
const deleteCommand = defineCommand({
|
|
5140
|
+
meta: {
|
|
5141
|
+
name: "delete",
|
|
5142
|
+
description: "Delete an update group"
|
|
5143
|
+
},
|
|
5144
|
+
args: { groupId: {
|
|
5145
|
+
type: "positional",
|
|
5146
|
+
required: true,
|
|
5147
|
+
description: "Update group ID"
|
|
5148
|
+
} },
|
|
5149
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
5150
|
+
const result = yield* (yield* apiClient).updates.deleteGroup({ path: { groupId: args.groupId } });
|
|
5151
|
+
yield* Console.log(`Deleted ${String(result.deleted)} update(s) from group ${args.groupId}.`);
|
|
5152
|
+
}), updateErrorExtras)
|
|
5153
|
+
});
|
|
4560
5154
|
|
|
4561
5155
|
//#endregion
|
|
4562
5156
|
//#region src/commands/update/list.ts
|
|
4563
|
-
const
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
5157
|
+
const listCommand = defineCommand({
|
|
5158
|
+
meta: {
|
|
5159
|
+
name: "list",
|
|
5160
|
+
description: "List recent updates"
|
|
5161
|
+
},
|
|
5162
|
+
args: {
|
|
5163
|
+
branch: {
|
|
5164
|
+
type: "string",
|
|
5165
|
+
description: "Filter by branch name"
|
|
5166
|
+
},
|
|
5167
|
+
limit: {
|
|
5168
|
+
type: "string",
|
|
5169
|
+
default: "20",
|
|
5170
|
+
description: "Max rows (default 20)"
|
|
5171
|
+
}
|
|
5172
|
+
},
|
|
5173
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
5174
|
+
const limit = yield* parseLimit(args.limit, 20);
|
|
5175
|
+
const projectId = yield* readProjectId;
|
|
5176
|
+
const api = yield* apiClient;
|
|
5177
|
+
const { items: branches } = yield* api.branches.list({ urlParams: {
|
|
5178
|
+
projectId,
|
|
5179
|
+
page: 1,
|
|
5180
|
+
limit: 1e3
|
|
5181
|
+
} });
|
|
5182
|
+
const branchId = args.branch ? yield* resolveNamedResourceId({
|
|
4579
5183
|
items: branches,
|
|
4580
5184
|
kind: "Branch",
|
|
4581
|
-
name:
|
|
4582
|
-
})
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
})
|
|
5185
|
+
name: args.branch
|
|
5186
|
+
}) : void 0;
|
|
5187
|
+
const { items } = yield* api.updates.list({ urlParams: {
|
|
5188
|
+
projectId,
|
|
5189
|
+
...branchId === void 0 ? {} : { branchId },
|
|
5190
|
+
page: 1,
|
|
5191
|
+
limit
|
|
5192
|
+
} });
|
|
5193
|
+
if (items.length === 0) {
|
|
5194
|
+
yield* Console.log("No updates found.");
|
|
5195
|
+
return;
|
|
5196
|
+
}
|
|
5197
|
+
const branchNames = new Map(branches.map((item) => [item.id, item.name]));
|
|
5198
|
+
yield* printTable([
|
|
5199
|
+
"Update ID",
|
|
5200
|
+
"Group",
|
|
5201
|
+
"Branch",
|
|
5202
|
+
"Platform",
|
|
5203
|
+
"Runtime",
|
|
5204
|
+
"Rollout",
|
|
5205
|
+
"Rollback",
|
|
5206
|
+
"Created"
|
|
5207
|
+
], items.map((item) => [
|
|
5208
|
+
item.id,
|
|
5209
|
+
item.groupId,
|
|
5210
|
+
branchNames.get(item.branchId) ?? item.branchId,
|
|
5211
|
+
item.platform,
|
|
5212
|
+
item.runtimeVersion,
|
|
5213
|
+
`${String(item.rolloutPercentage)}%`,
|
|
5214
|
+
item.isRollback ? "yes" : "no",
|
|
5215
|
+
item.createdAt
|
|
5216
|
+
]));
|
|
5217
|
+
}), updateErrorExtras)
|
|
5218
|
+
});
|
|
4615
5219
|
|
|
4616
5220
|
//#endregion
|
|
4617
5221
|
//#region src/lib/signed-payloads.ts
|
|
@@ -4704,27 +5308,37 @@ const runUpdatePromote = (options) => Effect.gen(function* () {
|
|
|
4704
5308
|
|
|
4705
5309
|
//#endregion
|
|
4706
5310
|
//#region src/commands/update/promote.ts
|
|
4707
|
-
const
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
}).
|
|
5311
|
+
const promoteCommand = defineCommand({
|
|
5312
|
+
meta: {
|
|
5313
|
+
name: "promote",
|
|
5314
|
+
description: "Promote an existing update to a channel"
|
|
5315
|
+
},
|
|
5316
|
+
args: {
|
|
5317
|
+
updateId: {
|
|
5318
|
+
type: "positional",
|
|
5319
|
+
required: true,
|
|
5320
|
+
description: "Source update ID"
|
|
5321
|
+
},
|
|
5322
|
+
channel: {
|
|
5323
|
+
type: "string",
|
|
5324
|
+
required: true,
|
|
5325
|
+
description: "Target channel name"
|
|
5326
|
+
},
|
|
5327
|
+
"manifest-body-file": { type: "string" },
|
|
5328
|
+
"signature-file": { type: "string" },
|
|
5329
|
+
"certificate-chain-file": { type: "string" }
|
|
5330
|
+
},
|
|
5331
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
5332
|
+
const result = yield* runUpdatePromote({
|
|
5333
|
+
updateId: args.updateId,
|
|
5334
|
+
channel: args.channel,
|
|
5335
|
+
manifestBodyFile: args["manifest-body-file"],
|
|
5336
|
+
signatureFile: args["signature-file"],
|
|
5337
|
+
certificateChainFile: args["certificate-chain-file"]
|
|
5338
|
+
});
|
|
5339
|
+
yield* Console.log(`Promoted update ${result.sourceUpdateId} to channel "${result.channel}" as update ${result.updateId}.`);
|
|
5340
|
+
}), updateErrorExtras)
|
|
5341
|
+
});
|
|
4728
5342
|
|
|
4729
5343
|
//#endregion
|
|
4730
5344
|
//#region src/lib/expo-export.ts
|
|
@@ -4754,8 +5368,8 @@ const inferContentType = (fileExt, isLaunch) => {
|
|
|
4754
5368
|
default: return "application/octet-stream";
|
|
4755
5369
|
}
|
|
4756
5370
|
};
|
|
4757
|
-
const makeBunxCommand = (...args) => Command
|
|
4758
|
-
const runCommand = (cmd, step) => Command
|
|
5371
|
+
const makeBunxCommand = (...args) => Command.make("bunx", ...args);
|
|
5372
|
+
const runCommand = (cmd, step) => Command.exitCode(cmd.pipe(Command.stdout("inherit"), Command.stderr("inherit"))).pipe(Effect.mapError((cause) => new BuildFailedError({
|
|
4759
5373
|
step,
|
|
4760
5374
|
exitCode: 1,
|
|
4761
5375
|
message: `${step} failed to spawn: ${String(cause)}`
|
|
@@ -4766,7 +5380,7 @@ const runCommand = (cmd, step) => Command$1.exitCode(cmd.pipe(Command$1.stdout("
|
|
|
4766
5380
|
}))));
|
|
4767
5381
|
const readExpoPublicConfig = ({ projectRoot, envVars }) => Effect.gen(function* () {
|
|
4768
5382
|
const commandEnv = yield* (yield* CliRuntime).commandEnvironment(envVars);
|
|
4769
|
-
const stdout = yield* Command
|
|
5383
|
+
const stdout = yield* Command.string(makeBunxCommand("expo", "config", "--type", "public", "--json").pipe(Command.workingDirectory(projectRoot), Command.env(commandEnv))).pipe(Effect.mapError((cause) => new UpdatePublishError({ message: `Failed to read Expo public config: ${String(cause)}` })));
|
|
4770
5384
|
const config = asRecord(yield* Effect.try({
|
|
4771
5385
|
try: () => JSON.parse(stdout),
|
|
4772
5386
|
catch: () => new UpdatePublishError({ message: "Expo public config output was not valid JSON." })
|
|
@@ -4786,7 +5400,7 @@ const runExpoExport = ({ projectRoot, exportDir, platform, envVars, clear }) =>
|
|
|
4786
5400
|
"--dump-assetmap"
|
|
4787
5401
|
];
|
|
4788
5402
|
if (clear) args.push("--clear");
|
|
4789
|
-
return yield* runCommand(makeBunxCommand(...args).pipe(Command
|
|
5403
|
+
return yield* runCommand(makeBunxCommand(...args).pipe(Command.workingDirectory(projectRoot), Command.env(commandEnv)), `expo export ${platform}`);
|
|
4790
5404
|
});
|
|
4791
5405
|
const readExpoExportAssets = ({ exportDir, platform }) => Effect.gen(function* () {
|
|
4792
5406
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -5000,86 +5614,101 @@ const runUpdatePublish = (options) => Effect.scoped(Effect.gen(function* () {
|
|
|
5000
5614
|
|
|
5001
5615
|
//#endregion
|
|
5002
5616
|
//#region src/commands/update/publish.ts
|
|
5003
|
-
const
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
const
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
"
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5617
|
+
const PUBLISH_EXIT_EXTRAS = {
|
|
5618
|
+
BuildProfileError: 2,
|
|
5619
|
+
RuntimeVersionError: 2,
|
|
5620
|
+
EnvExportError: 7,
|
|
5621
|
+
BuildFailedError: 6,
|
|
5622
|
+
UpdatePublishError: 7
|
|
5623
|
+
};
|
|
5624
|
+
const publishCommand = defineCommand({
|
|
5625
|
+
meta: {
|
|
5626
|
+
name: "publish",
|
|
5627
|
+
description: "Publish a new OTA update group"
|
|
5628
|
+
},
|
|
5629
|
+
args: {
|
|
5630
|
+
branch: {
|
|
5631
|
+
type: "string",
|
|
5632
|
+
description: "Target branch name"
|
|
5633
|
+
},
|
|
5634
|
+
platform: {
|
|
5635
|
+
type: "enum",
|
|
5636
|
+
options: [
|
|
5637
|
+
"ios",
|
|
5638
|
+
"android",
|
|
5639
|
+
"all"
|
|
5640
|
+
],
|
|
5641
|
+
default: "all",
|
|
5642
|
+
description: "Platform(s) to publish"
|
|
5643
|
+
},
|
|
5644
|
+
message: {
|
|
5645
|
+
type: "string",
|
|
5646
|
+
description: "Optional update message"
|
|
5647
|
+
},
|
|
5648
|
+
environment: {
|
|
5649
|
+
type: "string",
|
|
5650
|
+
default: "production",
|
|
5651
|
+
description: "Env vars scope"
|
|
5652
|
+
},
|
|
5653
|
+
auto: {
|
|
5654
|
+
type: "boolean",
|
|
5655
|
+
description: "Skip prompts (for CI)"
|
|
5656
|
+
},
|
|
5657
|
+
clear: {
|
|
5658
|
+
type: "boolean",
|
|
5659
|
+
description: "Drop existing assets before upload"
|
|
5660
|
+
},
|
|
5661
|
+
"rollout-percentage": {
|
|
5662
|
+
type: "string",
|
|
5663
|
+
description: "Initial rollout percentage (1-100)"
|
|
5664
|
+
},
|
|
5665
|
+
"manifest-body-file": { type: "string" },
|
|
5666
|
+
"signature-file": { type: "string" },
|
|
5667
|
+
"certificate-chain-file": { type: "string" },
|
|
5668
|
+
"manifest-body-file-ios": { type: "string" },
|
|
5669
|
+
"signature-file-ios": { type: "string" },
|
|
5670
|
+
"certificate-chain-file-ios": { type: "string" },
|
|
5671
|
+
"manifest-body-file-android": { type: "string" },
|
|
5672
|
+
"signature-file-android": { type: "string" },
|
|
5673
|
+
"certificate-chain-file-android": { type: "string" }
|
|
5674
|
+
},
|
|
5675
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
5676
|
+
const rolloutPercentage = args["rollout-percentage"] ? yield* parseRolloutPercentage(args["rollout-percentage"], "rollout-percentage") : void 0;
|
|
5677
|
+
const result = yield* runUpdatePublish({
|
|
5678
|
+
branch: args.branch,
|
|
5679
|
+
platform: args.platform,
|
|
5680
|
+
message: args.message,
|
|
5681
|
+
auto: args.auto ?? false,
|
|
5682
|
+
environment: args.environment,
|
|
5683
|
+
clear: args.clear ?? false,
|
|
5684
|
+
rolloutPercentage,
|
|
5685
|
+
manifestBodyFile: args["manifest-body-file"],
|
|
5686
|
+
signatureFile: args["signature-file"],
|
|
5687
|
+
certificateChainFile: args["certificate-chain-file"],
|
|
5688
|
+
manifestBodyFileIos: args["manifest-body-file-ios"],
|
|
5689
|
+
signatureFileIos: args["signature-file-ios"],
|
|
5690
|
+
certificateChainFileIos: args["certificate-chain-file-ios"],
|
|
5691
|
+
manifestBodyFileAndroid: args["manifest-body-file-android"],
|
|
5692
|
+
signatureFileAndroid: args["signature-file-android"],
|
|
5693
|
+
certificateChainFileAndroid: args["certificate-chain-file-android"]
|
|
5694
|
+
});
|
|
5695
|
+
yield* Console.log(`Published update group ${result.groupId} to branch "${result.branch}".`);
|
|
5696
|
+
yield* Console.log("");
|
|
5697
|
+
yield* printTable([
|
|
5698
|
+
"Platform",
|
|
5699
|
+
"Update ID",
|
|
5700
|
+
"Runtime Version",
|
|
5701
|
+
"Uploaded",
|
|
5702
|
+
"Reused"
|
|
5703
|
+
], result.results.map((entry) => [
|
|
5704
|
+
entry.platform,
|
|
5705
|
+
entry.updateId,
|
|
5706
|
+
entry.runtimeVersion,
|
|
5707
|
+
String(entry.uploadedAssets),
|
|
5708
|
+
String(entry.deduplicatedAssets)
|
|
5709
|
+
]));
|
|
5710
|
+
}), PUBLISH_EXIT_EXTRAS)
|
|
5711
|
+
});
|
|
5083
5712
|
|
|
5084
5713
|
//#endregion
|
|
5085
5714
|
//#region ../../packages/expo-protocol/src/index.ts
|
|
@@ -5186,121 +5815,179 @@ const runUpdateRollback = (options) => Effect.gen(function* () {
|
|
|
5186
5815
|
|
|
5187
5816
|
//#endregion
|
|
5188
5817
|
//#region src/commands/update/rollback.ts
|
|
5189
|
-
const
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5818
|
+
const rollbackCommand = defineCommand({
|
|
5819
|
+
meta: {
|
|
5820
|
+
name: "rollback",
|
|
5821
|
+
description: "Roll back updates on a branch"
|
|
5822
|
+
},
|
|
5823
|
+
args: {
|
|
5824
|
+
branch: {
|
|
5825
|
+
type: "string",
|
|
5826
|
+
required: true,
|
|
5827
|
+
description: "Branch to roll back"
|
|
5828
|
+
},
|
|
5829
|
+
platform: {
|
|
5830
|
+
type: "enum",
|
|
5831
|
+
options: [
|
|
5832
|
+
"ios",
|
|
5833
|
+
"android",
|
|
5834
|
+
"all"
|
|
5835
|
+
],
|
|
5836
|
+
default: "all",
|
|
5837
|
+
description: "Platform(s) to roll back"
|
|
5838
|
+
},
|
|
5839
|
+
message: { type: "string" },
|
|
5840
|
+
"commit-time": { type: "string" },
|
|
5841
|
+
"directive-body-file": { type: "string" },
|
|
5842
|
+
"signature-file": { type: "string" },
|
|
5843
|
+
"certificate-chain-file": { type: "string" }
|
|
5844
|
+
},
|
|
5845
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
5846
|
+
const result = yield* runUpdateRollback({
|
|
5847
|
+
branch: args.branch,
|
|
5848
|
+
platform: args.platform,
|
|
5849
|
+
message: args.message,
|
|
5850
|
+
commitTime: args["commit-time"],
|
|
5851
|
+
directiveBodyFile: args["directive-body-file"],
|
|
5852
|
+
signatureFile: args["signature-file"],
|
|
5853
|
+
certificateChainFile: args["certificate-chain-file"]
|
|
5854
|
+
});
|
|
5855
|
+
yield* Console.log(`Created rollback group ${result.groupId} on branch "${result.branch}" at ${result.commitTime}.`);
|
|
5856
|
+
yield* Console.log("");
|
|
5857
|
+
yield* printTable([
|
|
5858
|
+
"Platform",
|
|
5859
|
+
"Update ID",
|
|
5860
|
+
"Runtime Version"
|
|
5861
|
+
], result.results.map((entry) => [
|
|
5862
|
+
entry.platform,
|
|
5863
|
+
entry.updateId,
|
|
5864
|
+
entry.runtimeVersion
|
|
5865
|
+
]));
|
|
5866
|
+
}), updateErrorExtras)
|
|
5867
|
+
});
|
|
5230
5868
|
|
|
5231
5869
|
//#endregion
|
|
5232
5870
|
//#region src/commands/update/rollout/complete.ts
|
|
5233
|
-
const
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
}
|
|
5871
|
+
const completeCommand = defineCommand({
|
|
5872
|
+
meta: {
|
|
5873
|
+
name: "complete",
|
|
5874
|
+
description: "Complete the rollout for an update"
|
|
5875
|
+
},
|
|
5876
|
+
args: { updateId: {
|
|
5877
|
+
type: "positional",
|
|
5878
|
+
required: true,
|
|
5879
|
+
description: "Update ID"
|
|
5880
|
+
} },
|
|
5881
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
5882
|
+
const result = yield* (yield* apiClient).updates.completeRollout({ path: { id: args.updateId } });
|
|
5883
|
+
yield* Console.log(`Completed rollout for ${args.updateId}. Current rollout is ${String(result.rolloutPercentage)}%.`);
|
|
5884
|
+
}), updateErrorExtras)
|
|
5885
|
+
});
|
|
5238
5886
|
|
|
5239
5887
|
//#endregion
|
|
5240
5888
|
//#region src/commands/update/rollout/revert.ts
|
|
5241
|
-
const
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
}
|
|
5889
|
+
const revertCommand = defineCommand({
|
|
5890
|
+
meta: {
|
|
5891
|
+
name: "revert",
|
|
5892
|
+
description: "Revert the rollout for an update"
|
|
5893
|
+
},
|
|
5894
|
+
args: { updateId: {
|
|
5895
|
+
type: "positional",
|
|
5896
|
+
required: true,
|
|
5897
|
+
description: "Update ID"
|
|
5898
|
+
} },
|
|
5899
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
5900
|
+
const result = yield* (yield* apiClient).updates.revertRollout({ path: { id: args.updateId } });
|
|
5901
|
+
yield* Console.log(`Reverted rollout for ${args.updateId}. Current rollout is ${String(result.rolloutPercentage)}%.`);
|
|
5902
|
+
}), updateErrorExtras)
|
|
5903
|
+
});
|
|
5246
5904
|
|
|
5247
5905
|
//#endregion
|
|
5248
5906
|
//#region src/commands/update/rollout/set.ts
|
|
5249
|
-
const
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5907
|
+
const setCommand = defineCommand({
|
|
5908
|
+
meta: {
|
|
5909
|
+
name: "set",
|
|
5910
|
+
description: "Set the rollout percentage for an update"
|
|
5911
|
+
},
|
|
5912
|
+
args: {
|
|
5913
|
+
updateId: {
|
|
5914
|
+
type: "positional",
|
|
5915
|
+
required: true,
|
|
5916
|
+
description: "Update ID"
|
|
5917
|
+
},
|
|
5918
|
+
percentage: {
|
|
5919
|
+
type: "string",
|
|
5920
|
+
required: true,
|
|
5921
|
+
description: "Rollout percentage (1-100)"
|
|
5922
|
+
}
|
|
5923
|
+
},
|
|
5924
|
+
run: async ({ args }) => runEffect(Effect.gen(function* () {
|
|
5925
|
+
const percentage = yield* parseRolloutPercentage(args.percentage, "percentage");
|
|
5926
|
+
const result = yield* (yield* apiClient).updates.editRollout({
|
|
5927
|
+
path: { id: args.updateId },
|
|
5928
|
+
payload: { percentage }
|
|
5929
|
+
});
|
|
5930
|
+
yield* Console.log(`Updated rollout for ${args.updateId} to ${String(result.rolloutPercentage)}%.`);
|
|
5931
|
+
}), updateErrorExtras)
|
|
5932
|
+
});
|
|
5261
5933
|
|
|
5262
5934
|
//#endregion
|
|
5263
5935
|
//#region src/commands/update/rollout/index.ts
|
|
5264
|
-
const rolloutCommand =
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5936
|
+
const rolloutCommand = defineCommand({
|
|
5937
|
+
meta: {
|
|
5938
|
+
name: "rollout",
|
|
5939
|
+
description: "Manage per-update rollouts"
|
|
5940
|
+
},
|
|
5941
|
+
subCommands: {
|
|
5942
|
+
set: setCommand,
|
|
5943
|
+
complete: completeCommand,
|
|
5944
|
+
revert: revertCommand
|
|
5945
|
+
}
|
|
5946
|
+
});
|
|
5269
5947
|
|
|
5270
5948
|
//#endregion
|
|
5271
5949
|
//#region src/commands/update/index.ts
|
|
5272
|
-
const updateCommand =
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5950
|
+
const updateCommand = defineCommand({
|
|
5951
|
+
meta: {
|
|
5952
|
+
name: "update",
|
|
5953
|
+
description: "Manage OTA updates"
|
|
5954
|
+
},
|
|
5955
|
+
subCommands: {
|
|
5956
|
+
publish: publishCommand,
|
|
5957
|
+
list: listCommand,
|
|
5958
|
+
delete: deleteCommand,
|
|
5959
|
+
promote: promoteCommand,
|
|
5960
|
+
rollback: rollbackCommand,
|
|
5961
|
+
rollout: rolloutCommand
|
|
5962
|
+
}
|
|
5963
|
+
});
|
|
5280
5964
|
|
|
5281
5965
|
//#endregion
|
|
5282
5966
|
//#region src/index.ts
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5967
|
+
await runMain(defineCommand({
|
|
5968
|
+
meta: {
|
|
5969
|
+
name: "better-update",
|
|
5970
|
+
version,
|
|
5971
|
+
description: "Publish OTA updates and builds for Expo apps"
|
|
5972
|
+
},
|
|
5973
|
+
subCommands: {
|
|
5974
|
+
login: loginCommand,
|
|
5975
|
+
logout: logoutCommand,
|
|
5976
|
+
init: initCommand,
|
|
5977
|
+
status: statusCommand,
|
|
5978
|
+
projects: projectsCommand,
|
|
5979
|
+
branches: branchesCommand,
|
|
5980
|
+
channels: channelsCommand,
|
|
5981
|
+
build: buildCommand,
|
|
5982
|
+
builds: buildsCommand,
|
|
5983
|
+
credentials: credentialsCommand,
|
|
5984
|
+
env: envCommand,
|
|
5985
|
+
fingerprint: fingerprintCommand,
|
|
5986
|
+
update: updateCommand,
|
|
5987
|
+
analytics: analyticsCommand,
|
|
5988
|
+
"audit-logs": auditLogsCommand
|
|
5989
|
+
}
|
|
5990
|
+
}));
|
|
5304
5991
|
|
|
5305
5992
|
//#endregion
|
|
5306
5993
|
export { };
|