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