@better-update/cli 0.17.0 → 0.18.1

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 CHANGED
@@ -14,7 +14,7 @@ import { createServer } from "node:http";
14
14
  import { maxBy, uniqBy } from "es-toolkit";
15
15
  import { createHash, randomBytes, randomUUID } from "node:crypto";
16
16
  import forge from "node-forge";
17
- import { createReadStream, existsSync, promises, readFileSync, writeFileSync } from "node:fs";
17
+ import { accessSync, chmodSync, constants, createReadStream, existsSync, promises, readFileSync, writeFileSync } from "node:fs";
18
18
  import { spawn as spawn$1 } from "node-pty";
19
19
  import chalk from "chalk";
20
20
  import os from "node:os";
@@ -31,7 +31,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
31
31
 
32
32
  //#endregion
33
33
  //#region package.json
34
- var version = "0.17.0";
34
+ var version = "0.18.1";
35
35
 
36
36
  //#endregion
37
37
  //#region src/lib/interactive-mode.ts
@@ -200,11 +200,11 @@ var NotAcceptable = class extends Schema.TaggedError()("NotAcceptable", { messag
200
200
  //#endregion
201
201
  //#region ../../packages/api/src/groups/android-application-identifiers.ts
202
202
  const idParam$16 = HttpApiSchema.param("id", Schema.String);
203
- const projectIdParam$3 = HttpApiSchema.param("projectId", Schema.String);
204
- var AndroidApplicationIdentifiersGroup = class extends HttpApiGroup.make("androidApplicationIdentifiers").add(HttpApiEndpoint.get("list")`/api/projects/${projectIdParam$3}/android-application-identifiers`.addSuccess(Schema.Struct({ items: Schema.Array(AndroidApplicationIdentifier) })).annotateContext(OpenApi.annotations({
203
+ const projectIdParam$4 = HttpApiSchema.param("projectId", Schema.String);
204
+ var AndroidApplicationIdentifiersGroup = class extends HttpApiGroup.make("androidApplicationIdentifiers").add(HttpApiEndpoint.get("list")`/api/projects/${projectIdParam$4}/android-application-identifiers`.addSuccess(Schema.Struct({ items: Schema.Array(AndroidApplicationIdentifier) })).annotateContext(OpenApi.annotations({
205
205
  title: "List Android application identifiers",
206
206
  description: "List all Android package identifiers for a project"
207
- }))).add(HttpApiEndpoint.post("create")`/api/projects/${projectIdParam$3}/android-application-identifiers`.setPayload(CreateAndroidApplicationIdentifierBody).addSuccess(AndroidApplicationIdentifier, { status: 201 }).annotateContext(OpenApi.annotations({
207
+ }))).add(HttpApiEndpoint.post("create")`/api/projects/${projectIdParam$4}/android-application-identifiers`.setPayload(CreateAndroidApplicationIdentifierBody).addSuccess(AndroidApplicationIdentifier, { status: 201 }).annotateContext(OpenApi.annotations({
208
208
  title: "Create Android application identifier",
209
209
  description: "Register an Android package name for a project"
210
210
  }))).add(HttpApiEndpoint.del("delete")`/api/android-application-identifiers/${idParam$16}`.addSuccess(DeleteAndroidApplicationIdentifierResult).annotateContext(OpenApi.annotations({
@@ -612,11 +612,11 @@ const AssetUploadResult = Schema.Struct({
612
612
 
613
613
  //#endregion
614
614
  //#region ../../packages/api/src/groups/assets.ts
615
- const hashParam = HttpApiSchema.param("hash", Schema.String);
615
+ const hashParam$1 = HttpApiSchema.param("hash", Schema.String);
616
616
  var AssetsGroup = class extends HttpApiGroup.make("assets").add(HttpApiEndpoint.post("upload", "/api/assets/upload").setPayload(AssetUploadBody).addSuccess(AssetUploadResult, { status: 201 }).annotateContext(OpenApi.annotations({
617
617
  title: "Upload assets",
618
618
  description: "Upload asset files to R2 storage (deduplicated by content hash)"
619
- }))).add(HttpApiEndpoint.post("finalize")`/api/assets/${hashParam}/finalize`.addSuccess(Asset).annotateContext(OpenApi.annotations({
619
+ }))).add(HttpApiEndpoint.post("finalize")`/api/assets/${hashParam$1}/finalize`.addSuccess(Asset).annotateContext(OpenApi.annotations({
620
620
  title: "Finalize asset upload",
621
621
  description: "Verify a directly uploaded asset in R2 and mark it available for updates"
622
622
  }))).addError(BadRequest).addError(NotFound).addError(Forbidden).annotateContext(OpenApi.annotations({
@@ -721,7 +721,8 @@ const ResolveBuildCredentialsIosBody = Schema.Struct({
721
721
  });
722
722
  const ResolveBuildCredentialsAndroidBody = Schema.Struct({
723
723
  platform: Schema.Literal("android"),
724
- applicationIdentifier: AndroidPackageName
724
+ applicationIdentifier: AndroidPackageName,
725
+ buildProfile: Schema.optional(Schema.String.pipe(Schema.minLength(1), Schema.maxLength(120)))
725
726
  });
726
727
  const ResolveBuildCredentialsBody = Schema.Union(ResolveBuildCredentialsIosBody, ResolveBuildCredentialsAndroidBody);
727
728
  const IosBuildDistributionCertificate = Schema.Struct({
@@ -777,8 +778,8 @@ const ResolveBuildCredentialsResult = Schema.Union(ResolveBuildCredentialsIosRes
777
778
 
778
779
  //#endregion
779
780
  //#region ../../packages/api/src/groups/build-credentials.ts
780
- const projectIdParam$2 = HttpApiSchema.param("projectId", Schema.String);
781
- var BuildCredentialsGroup = class extends HttpApiGroup.make("buildCredentials").add(HttpApiEndpoint.post("resolve")`/api/projects/${projectIdParam$2}/build-credentials/resolve`.setPayload(ResolveBuildCredentialsBody).addSuccess(ResolveBuildCredentialsResult).annotateContext(OpenApi.annotations({
781
+ const projectIdParam$3 = HttpApiSchema.param("projectId", Schema.String);
782
+ var BuildCredentialsGroup = class extends HttpApiGroup.make("buildCredentials").add(HttpApiEndpoint.post("resolve")`/api/projects/${projectIdParam$3}/build-credentials/resolve`.setPayload(ResolveBuildCredentialsBody).addSuccess(ResolveBuildCredentialsResult).annotateContext(OpenApi.annotations({
782
783
  title: "Resolve build credentials",
783
784
  description: "Return decrypted signing assets for a project build. Regenerates the iOS provisioning profile via Apple ASC when the registered device roster has changed since the profile was last generated."
784
785
  }))).addError(NotFound).addError(BadRequest).addError(Forbidden).annotateContext(OpenApi.annotations({
@@ -789,6 +790,7 @@ var BuildCredentialsGroup = class extends HttpApiGroup.make("buildCredentials").
789
790
  //#endregion
790
791
  //#region ../../packages/api/src/domain/build.ts
791
792
  const Distribution = Schema.Literal("app-store", "ad-hoc", "development", "enterprise", "simulator", "play-store", "direct");
793
+ const BuildAudience = Schema.Literal("internal", "store");
792
794
  const ArtifactFormat = Schema.Literal("ipa", "apk", "aab", "tar.gz");
793
795
  const Sha256Hex = Schema.String.pipe(Schema.pattern(/^[a-fA-F0-9]{64}$/u), Schema.maxLength(64));
794
796
  const CreateBuildCommonFields = {
@@ -805,6 +807,7 @@ const CreateBuildCommonFields = {
805
807
  key: Schema.String,
806
808
  value: Schema.Unknown
807
809
  })),
810
+ fingerprintHash: Schema.optional(Schema.String.pipe(Schema.minLength(1))),
808
811
  sha256: Sha256Hex,
809
812
  byteSize: Schema.Number.pipe(Schema.nonNegative())
810
813
  };
@@ -822,6 +825,7 @@ var Build = class extends Schema.Class("Build")({
822
825
  gitCommit: Schema.NullOr(Schema.String),
823
826
  message: Schema.NullOr(Schema.String),
824
827
  metadataJson: Schema.String,
828
+ fingerprintHash: Schema.NullOr(Schema.String),
825
829
  createdAt: DateTimeString
826
830
  }) {};
827
831
  var BuildArtifact = class extends Schema.Class("BuildArtifact")({
@@ -873,6 +877,7 @@ const ListBuildsParams = Schema.Struct({
873
877
  profile: Schema.optional(Schema.String),
874
878
  runtimeVersion: Schema.optional(Schema.String),
875
879
  distribution: Schema.optional(Distribution),
880
+ audience: Schema.optional(BuildAudience),
876
881
  ...PaginationParams.fields,
877
882
  sort: Schema.optional(BuildSort)
878
883
  });
@@ -1245,6 +1250,108 @@ var EnvVarsGroup = class extends HttpApiGroup.make("env-vars").add(HttpApiEndpoi
1245
1250
  description: "Manage environment variables for project builds and deployments"
1246
1251
  })) {};
1247
1252
 
1253
+ //#endregion
1254
+ //#region ../../packages/api/src/domain/update.ts
1255
+ var Update = class extends Schema.Class("Update")({
1256
+ id: Id,
1257
+ branchId: Id,
1258
+ runtimeVersion: Schema.String,
1259
+ platform: Platform,
1260
+ message: Schema.String,
1261
+ metadataJson: Schema.String,
1262
+ extraJson: Schema.NullOr(Schema.String),
1263
+ groupId: Schema.String,
1264
+ rolloutPercentage: Schema.Number,
1265
+ isRollback: Schema.Boolean,
1266
+ signature: Schema.NullOr(Schema.String),
1267
+ certificateChain: Schema.NullOr(Schema.String),
1268
+ manifestBody: Schema.NullOr(Schema.String),
1269
+ directiveBody: Schema.NullOr(Schema.String),
1270
+ fingerprintHash: Schema.NullOr(Schema.String),
1271
+ totalAssetSize: Schema.Number,
1272
+ createdAt: DateTimeString
1273
+ }) {};
1274
+ const UpdateSortColumn = Schema.Literal("createdAt", "runtimeVersion", "platform", "rolloutPercentage");
1275
+ /**
1276
+ * Sort param: column name optionally prefixed with `-` for descending.
1277
+ * Example: `runtimeVersion` (asc), `-createdAt` (desc).
1278
+ */
1279
+ const UpdateSort = Schema.Union(UpdateSortColumn, Schema.TemplateLiteral("-", UpdateSortColumn));
1280
+ const ListUpdatesParams = Schema.Struct({
1281
+ projectId: Id,
1282
+ branchId: Schema.optional(Id),
1283
+ platform: Schema.optional(Platform),
1284
+ runtimeVersion: Schema.optional(Schema.String),
1285
+ ...PaginationParams.fields,
1286
+ sort: Schema.optional(UpdateSort)
1287
+ });
1288
+ const AssetRef = Schema.Struct({
1289
+ hash: Schema.String,
1290
+ key: Schema.String,
1291
+ isLaunch: Schema.Boolean,
1292
+ contentChecksum: Schema.optional(Schema.String)
1293
+ });
1294
+ const UpdateAssetEntry = Schema.Struct({
1295
+ hash: Schema.String,
1296
+ key: Schema.String,
1297
+ isLaunch: Schema.Boolean,
1298
+ contentChecksum: Schema.NullOr(Schema.String)
1299
+ });
1300
+ const CreateUpdateBody = Schema.Struct({
1301
+ branch: Schema.String.pipe(Schema.minLength(1)),
1302
+ slug: Schema.String.pipe(Schema.minLength(1)),
1303
+ runtimeVersion: Schema.String.pipe(Schema.minLength(1)),
1304
+ platform: Platform,
1305
+ message: Schema.String,
1306
+ groupId: Schema.String.pipe(Schema.minLength(1)),
1307
+ metadata: Schema.Record({
1308
+ key: Schema.String,
1309
+ value: Schema.Unknown
1310
+ }),
1311
+ extra: Schema.optional(Schema.Record({
1312
+ key: Schema.String,
1313
+ value: Schema.Unknown
1314
+ })),
1315
+ assets: Schema.Array(AssetRef),
1316
+ manifestBody: Schema.optional(Schema.String),
1317
+ directiveBody: Schema.optional(Schema.String),
1318
+ isRollback: Schema.optional(Schema.Boolean),
1319
+ signature: Schema.optional(Schema.String),
1320
+ certificateChain: Schema.optional(Schema.String),
1321
+ rolloutPercentage: Schema.optional(Schema.Number.pipe(Schema.int(), Schema.between(1, 100))),
1322
+ fingerprintHash: Schema.optional(Schema.String.pipe(Schema.minLength(1)))
1323
+ });
1324
+ const RepublishBody = Schema.Struct({
1325
+ sourceUpdateId: Schema.optional(Id),
1326
+ sourceGroupId: Schema.optional(Schema.String.pipe(Schema.minLength(1))),
1327
+ destinationBranchId: Schema.optional(Id),
1328
+ destinationChannel: Schema.optional(Schema.String.pipe(Schema.minLength(1))),
1329
+ message: Schema.optional(Schema.String),
1330
+ signedUpdates: Schema.optional(Schema.Array(Schema.Struct({
1331
+ sourceUpdateId: Id,
1332
+ manifestBody: Schema.String.pipe(Schema.minLength(1)),
1333
+ signature: Schema.String.pipe(Schema.minLength(1)),
1334
+ certificateChain: Schema.String.pipe(Schema.minLength(1))
1335
+ })))
1336
+ });
1337
+ const RepublishResult = Schema.Struct({ updates: Schema.Array(Update) });
1338
+ const DeleteUpdateResult = Schema.Struct({ deleted: Schema.Number });
1339
+
1340
+ //#endregion
1341
+ //#region ../../packages/api/src/groups/fingerprints.ts
1342
+ const projectIdParam$2 = HttpApiSchema.param("projectId", Id);
1343
+ const hashParam = HttpApiSchema.param("hash", Schema.String.pipe(Schema.minLength(1)));
1344
+ const FingerprintDetail = Schema.Struct({
1345
+ hash: Schema.String,
1346
+ projectId: Id,
1347
+ builds: Schema.Array(BuildWithArtifact),
1348
+ updates: Schema.Array(Update)
1349
+ });
1350
+ var FingerprintsGroup = class extends HttpApiGroup.make("fingerprints").add(HttpApiEndpoint.get("get")`/api/projects/${projectIdParam$2}/fingerprints/${hashParam}`.addSuccess(FingerprintDetail).annotateContext(OpenApi.annotations({
1351
+ title: "Get fingerprint",
1352
+ description: "Fetch builds and updates compatible with a given fingerprint hash within a project."
1353
+ }))).addError(Forbidden, { status: 403 }).addError(NotFound, { status: 404 }).addError(BadRequest, { status: 400 }) {};
1354
+
1248
1355
  //#endregion
1249
1356
  //#region ../../packages/api/src/domain/google-service-account-key.ts
1250
1357
  var GoogleServiceAccountKey = class extends Schema.Class("GoogleServiceAccountKey")({
@@ -1433,83 +1540,6 @@ var ProjectsGroup = class extends HttpApiGroup.make("projects").add(HttpApiEndpo
1433
1540
  description: "Project management endpoints"
1434
1541
  })) {};
1435
1542
 
1436
- //#endregion
1437
- //#region ../../packages/api/src/domain/update.ts
1438
- var Update = class extends Schema.Class("Update")({
1439
- id: Id,
1440
- branchId: Id,
1441
- runtimeVersion: Schema.String,
1442
- platform: Platform,
1443
- message: Schema.String,
1444
- metadataJson: Schema.String,
1445
- extraJson: Schema.NullOr(Schema.String),
1446
- groupId: Schema.String,
1447
- rolloutPercentage: Schema.Number,
1448
- isRollback: Schema.Boolean,
1449
- signature: Schema.NullOr(Schema.String),
1450
- certificateChain: Schema.NullOr(Schema.String),
1451
- manifestBody: Schema.NullOr(Schema.String),
1452
- directiveBody: Schema.NullOr(Schema.String),
1453
- createdAt: DateTimeString
1454
- }) {};
1455
- const UpdateSortColumn = Schema.Literal("createdAt", "runtimeVersion", "platform", "rolloutPercentage");
1456
- /**
1457
- * Sort param: column name optionally prefixed with `-` for descending.
1458
- * Example: `runtimeVersion` (asc), `-createdAt` (desc).
1459
- */
1460
- const UpdateSort = Schema.Union(UpdateSortColumn, Schema.TemplateLiteral("-", UpdateSortColumn));
1461
- const ListUpdatesParams = Schema.Struct({
1462
- projectId: Id,
1463
- branchId: Schema.optional(Id),
1464
- platform: Schema.optional(Platform),
1465
- ...PaginationParams.fields,
1466
- sort: Schema.optional(UpdateSort)
1467
- });
1468
- const AssetRef = Schema.Struct({
1469
- hash: Schema.String,
1470
- key: Schema.String,
1471
- isLaunch: Schema.Boolean,
1472
- contentChecksum: Schema.optional(Schema.String)
1473
- });
1474
- const CreateUpdateBody = Schema.Struct({
1475
- branch: Schema.String.pipe(Schema.minLength(1)),
1476
- slug: Schema.String.pipe(Schema.minLength(1)),
1477
- runtimeVersion: Schema.String.pipe(Schema.minLength(1)),
1478
- platform: Platform,
1479
- message: Schema.String,
1480
- groupId: Schema.String.pipe(Schema.minLength(1)),
1481
- metadata: Schema.Record({
1482
- key: Schema.String,
1483
- value: Schema.Unknown
1484
- }),
1485
- extra: Schema.optional(Schema.Record({
1486
- key: Schema.String,
1487
- value: Schema.Unknown
1488
- })),
1489
- assets: Schema.Array(AssetRef),
1490
- manifestBody: Schema.optional(Schema.String),
1491
- directiveBody: Schema.optional(Schema.String),
1492
- isRollback: Schema.optional(Schema.Boolean),
1493
- signature: Schema.optional(Schema.String),
1494
- certificateChain: Schema.optional(Schema.String),
1495
- rolloutPercentage: Schema.optional(Schema.Number.pipe(Schema.int(), Schema.between(1, 100)))
1496
- });
1497
- const RepublishBody = Schema.Struct({
1498
- sourceUpdateId: Schema.optional(Id),
1499
- sourceGroupId: Schema.optional(Schema.String.pipe(Schema.minLength(1))),
1500
- destinationBranchId: Schema.optional(Id),
1501
- destinationChannel: Schema.optional(Schema.String.pipe(Schema.minLength(1))),
1502
- message: Schema.optional(Schema.String),
1503
- signedUpdates: Schema.optional(Schema.Array(Schema.Struct({
1504
- sourceUpdateId: Id,
1505
- manifestBody: Schema.String.pipe(Schema.minLength(1)),
1506
- signature: Schema.String.pipe(Schema.minLength(1)),
1507
- certificateChain: Schema.String.pipe(Schema.minLength(1))
1508
- })))
1509
- });
1510
- const RepublishResult = Schema.Struct({ updates: Schema.Array(Update) });
1511
- const DeleteUpdateResult = Schema.Struct({ deleted: Schema.Number });
1512
-
1513
1543
  //#endregion
1514
1544
  //#region ../../packages/api/src/groups/updates.ts
1515
1545
  const idParam$1 = HttpApiSchema.param("id", Schema.String);
@@ -1528,6 +1558,12 @@ var UpdatesGroup = class extends HttpApiGroup.make("updates").add(HttpApiEndpoin
1528
1558
  }))).add(HttpApiEndpoint.get("get")`/api/updates/${idParam$1}`.addSuccess(Update).annotateContext(OpenApi.annotations({
1529
1559
  title: "Get update",
1530
1560
  description: "Fetch a single update by ID"
1561
+ }))).add(HttpApiEndpoint.get("getGroup")`/api/update-groups/${groupIdParam}`.addSuccess(Schema.Struct({ items: Schema.Array(Update) })).annotateContext(OpenApi.annotations({
1562
+ title: "Get update group",
1563
+ description: "Fetch all updates in a group (paired iOS + Android variants)"
1564
+ }))).add(HttpApiEndpoint.get("listAssets")`/api/updates/${idParam$1}/assets`.addSuccess(Schema.Array(UpdateAssetEntry)).annotateContext(OpenApi.annotations({
1565
+ title: "List update assets",
1566
+ description: "Fetch the asset references (key + hash + launch flag) for an update"
1531
1567
  }))).add(HttpApiEndpoint.del("deleteGroup")`/api/updates/${groupIdParam}`.addSuccess(DeleteUpdateResult).annotateContext(OpenApi.annotations({
1532
1568
  title: "Delete update group",
1533
1569
  description: "Delete all updates in a group (paired iOS + Android updates)"
@@ -1605,7 +1641,7 @@ var WebhooksGroup = class extends HttpApiGroup.make("webhooks").add(HttpApiEndpo
1605
1641
 
1606
1642
  //#endregion
1607
1643
  //#region ../../packages/api/src/api.ts
1608
- var ManagementApi = class extends HttpApi.make("management-api").add(ProjectsGroup).add(BranchesGroup).add(ChannelsGroup).add(UpdatesGroup).add(AssetsGroup).add(AnalyticsGroup).add(BuildsGroup).add(EnvVarsGroup).add(AuditLogsGroup).add(DevicesGroup).add(AppleTeamsGroup).add(AppleDistributionCertificatesGroup).add(ApplePushKeysGroup).add(AscApiKeysGroup).add(AppleProvisioningProfilesGroup).add(GoogleServiceAccountKeysGroup).add(IosBundleConfigurationsGroup).add(AndroidApplicationIdentifiersGroup).add(AndroidUploadKeystoresGroup).add(AndroidBuildCredentialsGroup).add(BuildCredentialsGroup).add(MeGroup).add(WebhooksGroup).middleware(Authentication).annotateContext(OpenApi.annotations({
1644
+ var ManagementApi = class extends HttpApi.make("management-api").add(ProjectsGroup).add(BranchesGroup).add(ChannelsGroup).add(UpdatesGroup).add(AssetsGroup).add(AnalyticsGroup).add(BuildsGroup).add(EnvVarsGroup).add(FingerprintsGroup).add(AuditLogsGroup).add(DevicesGroup).add(AppleTeamsGroup).add(AppleDistributionCertificatesGroup).add(ApplePushKeysGroup).add(AscApiKeysGroup).add(AppleProvisioningProfilesGroup).add(GoogleServiceAccountKeysGroup).add(IosBundleConfigurationsGroup).add(AndroidApplicationIdentifiersGroup).add(AndroidUploadKeystoresGroup).add(AndroidBuildCredentialsGroup).add(BuildCredentialsGroup).add(MeGroup).add(WebhooksGroup).middleware(Authentication).annotateContext(OpenApi.annotations({
1609
1645
  title: "Better Update Management API",
1610
1646
  version: "1.0.0",
1611
1647
  description: "Management API for OTA update publishing, deployment, and analytics"
@@ -4150,7 +4186,8 @@ const downloadAndroidCredentials = (api, options) => Effect.gen(function* () {
4150
4186
  path: { projectId: options.projectId },
4151
4187
  payload: {
4152
4188
  platform: "android",
4153
- applicationIdentifier: options.applicationIdentifier
4189
+ applicationIdentifier: options.applicationIdentifier,
4190
+ ...options.buildProfile === void 0 ? {} : { buildProfile: options.buildProfile }
4154
4191
  }
4155
4192
  }).pipe(Effect.mapError((cause) => resolveErrorToMissingCredentials(cause, "android")));
4156
4193
  if (resolved.platform !== "android") return yield* Effect.fail(new MissingCredentialsError({
@@ -4407,7 +4444,23 @@ const mergeEnv$1 = (overrides) => {
4407
4444
  for (const [key, value] of Object.entries(overrides)) merged[key] = value;
4408
4445
  return merged;
4409
4446
  };
4447
+ let spawnHelperChecked = false;
4448
+ const ensureSpawnHelperExecutable = () => {
4449
+ if (spawnHelperChecked) return;
4450
+ spawnHelperChecked = true;
4451
+ if (process$1.platform === "win32") return;
4452
+ try {
4453
+ const nodeRequire = createRequire(import.meta.url);
4454
+ const helperPath = path.join(path.dirname(nodeRequire.resolve("node-pty/package.json")), "prebuilds", `${process$1.platform}-${process$1.arch}`, "spawn-helper");
4455
+ try {
4456
+ accessSync(helperPath, constants.X_OK);
4457
+ } catch {
4458
+ chmodSync(helperPath, 493);
4459
+ }
4460
+ } catch {}
4461
+ };
4410
4462
  const trySpawn = (input) => {
4463
+ ensureSpawnHelperExecutable();
4411
4464
  const { cols, rows } = ptyDimensions();
4412
4465
  try {
4413
4466
  return spawn$1(input.command, [...input.args], {
@@ -4594,7 +4647,8 @@ const runAndroidBuild = (input) => Effect.gen(function* () {
4594
4647
  const credentials = input.credentialsSource === "local" ? yield* loadLocalAndroidCredentials({ projectRoot }) : yield* downloadAndroidCredentials(api, {
4595
4648
  projectId,
4596
4649
  applicationIdentifier,
4597
- tempDir
4650
+ tempDir,
4651
+ buildProfile: input.profileName
4598
4652
  });
4599
4653
  yield* runStep({
4600
4654
  command: "bunx",
@@ -5913,7 +5967,8 @@ const buildReserveCommon = (input) => ({
5913
5967
  ...input.buildNumber === void 0 ? {} : { buildNumber: input.buildNumber },
5914
5968
  ...input.gitContext.ref === void 0 ? {} : { gitRef: input.gitContext.ref },
5915
5969
  ...input.gitContext.commit === void 0 ? {} : { gitCommit: input.gitContext.commit },
5916
- ...input.message === void 0 ? {} : { message: input.message }
5970
+ ...input.message === void 0 ? {} : { message: input.message },
5971
+ ...input.fingerprintHash === void 0 ? {} : { fingerprintHash: input.fingerprintHash }
5917
5972
  });
5918
5973
  const callReserve = (api, input) => {
5919
5974
  const common = buildReserveCommon(input);
@@ -6407,6 +6462,26 @@ const pullEnvVars = (api, { projectId, environment }) => {
6407
6462
  } }).pipe(Effect.map((result) => Object.fromEntries(result.items.map((item) => [item.key, item.value]))), Effect.mapError((cause) => new EnvExportError({ message: `Failed to export environment variables for "${environment}": ${String(cause)}` })));
6408
6463
  };
6409
6464
 
6465
+ //#endregion
6466
+ //#region src/lib/fingerprint.ts
6467
+ var FingerprintError = class extends Data.TaggedError("FingerprintError") {};
6468
+ const runFingerprintFull = (projectRoot) => Effect.gen(function* () {
6469
+ const cmd = Command.make("bunx", "@expo/fingerprint", projectRoot).pipe(Command.workingDirectory(projectRoot));
6470
+ const stdout = yield* Command.string(cmd).pipe(Effect.mapError((cause) => new FingerprintError({ message: `Failed to run "@expo/fingerprint": ${cause.message}` })));
6471
+ const parsed = yield* Effect.try({
6472
+ try: () => JSON.parse(stdout),
6473
+ catch: () => new FingerprintError({ message: "Failed to parse @expo/fingerprint output as JSON." })
6474
+ });
6475
+ if (!isRecord(parsed)) return yield* new FingerprintError({ message: "@expo/fingerprint output was not a JSON object." });
6476
+ const { hash } = parsed;
6477
+ if (typeof hash !== "string" || hash.length === 0) return yield* new FingerprintError({ message: "@expo/fingerprint output did not contain a \"hash\" string field." });
6478
+ const sourcesRaw = parsed["sources"];
6479
+ return {
6480
+ hash,
6481
+ sources: Array.isArray(sourcesRaw) ? sourcesRaw : []
6482
+ };
6483
+ });
6484
+
6410
6485
  //#endregion
6411
6486
  //#region src/lib/git-context.ts
6412
6487
  const runString = (cmd, cwd) => Command.string(Command.workingDirectory(cmd, cwd));
@@ -6670,26 +6745,6 @@ const ensureRepoClean = ({ projectRoot, allowDirty, label }) => Effect.gen(funct
6670
6745
  if (!(yield* promptConfirm(`Continue ${label} with uncommitted changes?`, { initialValue: false }))) yield* new DirtyRepoError({ message: `${label} cancelled by user.` });
6671
6746
  });
6672
6747
 
6673
- //#endregion
6674
- //#region src/lib/fingerprint.ts
6675
- var FingerprintError = class extends Data.TaggedError("FingerprintError") {};
6676
- const runFingerprintFull = (projectRoot) => Effect.gen(function* () {
6677
- const cmd = Command.make("bunx", "@expo/fingerprint", projectRoot).pipe(Command.workingDirectory(projectRoot));
6678
- const stdout = yield* Command.string(cmd).pipe(Effect.mapError((cause) => new FingerprintError({ message: `Failed to run "@expo/fingerprint": ${cause.message}` })));
6679
- const parsed = yield* Effect.try({
6680
- try: () => JSON.parse(stdout),
6681
- catch: () => new FingerprintError({ message: "Failed to parse @expo/fingerprint output as JSON." })
6682
- });
6683
- if (!isRecord(parsed)) return yield* new FingerprintError({ message: "@expo/fingerprint output was not a JSON object." });
6684
- const { hash } = parsed;
6685
- if (typeof hash !== "string" || hash.length === 0) return yield* new FingerprintError({ message: "@expo/fingerprint output did not contain a \"hash\" string field." });
6686
- const sourcesRaw = parsed["sources"];
6687
- return {
6688
- hash,
6689
- sources: Array.isArray(sourcesRaw) ? sourcesRaw : []
6690
- };
6691
- });
6692
-
6693
6748
  //#endregion
6694
6749
  //#region src/lib/runtime-version.ts
6695
6750
  const resolveRuntimeVersion = ({ raw, appVersion, projectRoot }) => Effect.gen(function* () {
@@ -6768,7 +6823,8 @@ const runAndroidPlatformBuild = (input) => Effect.gen(function* () {
6768
6823
  applicationIdentifier,
6769
6824
  envVars,
6770
6825
  projectId,
6771
- credentialsSource
6826
+ credentialsSource,
6827
+ profileName: profile.name
6772
6828
  }),
6773
6829
  target: androidProfile.format === "aab" ? {
6774
6830
  platform: "android",
@@ -6869,6 +6925,7 @@ const runBuildWorkflow = (options) => Effect.scoped(Effect.gen(function* () {
6869
6925
  ...rawGitContext.commit === void 0 ? {} : { commit: rawGitContext.commit },
6870
6926
  dirty: rawGitContext.dirty
6871
6927
  };
6928
+ const fingerprintHash = yield* runFingerprintFull(userCwd).pipe(Effect.map((entry) => entry.hash), Effect.catchAll(() => Effect.succeed(void 0)));
6872
6929
  const result = yield* reserveAndUpload(api, {
6873
6930
  target,
6874
6931
  projectId,
@@ -6879,6 +6936,7 @@ const runBuildWorkflow = (options) => Effect.scoped(Effect.gen(function* () {
6879
6936
  bundleId,
6880
6937
  gitContext,
6881
6938
  ...options.message === void 0 ? {} : { message: options.message },
6939
+ ...fingerprintHash === void 0 ? {} : { fingerprintHash },
6882
6940
  artifactPath: build.artifactPath,
6883
6941
  sha256: build.sha256,
6884
6942
  byteSize: build.byteSize
@@ -7766,6 +7824,7 @@ const runUploadWorkflow = (options) => Effect.gen(function* () {
7766
7824
  ...rawGitContext.commit === void 0 ? {} : { commit: rawGitContext.commit },
7767
7825
  dirty: rawGitContext.dirty
7768
7826
  };
7827
+ const fingerprintHash = yield* runFingerprintFull(projectRoot).pipe(Effect.map((entry) => entry.hash), Effect.catchAll(() => Effect.succeed(void 0)));
7769
7828
  const result = yield* reserveAndUpload(api, {
7770
7829
  target,
7771
7830
  projectId,
@@ -7776,6 +7835,7 @@ const runUploadWorkflow = (options) => Effect.gen(function* () {
7776
7835
  bundleId,
7777
7836
  gitContext,
7778
7837
  ...options.message === void 0 ? {} : { message: options.message },
7838
+ ...fingerprintHash === void 0 ? {} : { fingerprintHash },
7779
7839
  artifactPath: options.artifactPath,
7780
7840
  sha256,
7781
7841
  byteSize
@@ -13615,7 +13675,8 @@ const publishPlatform = (params) => Effect.gen(function* () {
13615
13675
  signature: params.signedPayload.signature,
13616
13676
  certificateChain: params.signedPayload.certificateChain
13617
13677
  } : {},
13618
- ...params.rolloutPercentage === void 0 ? {} : { rolloutPercentage: params.rolloutPercentage }
13678
+ ...params.rolloutPercentage === void 0 ? {} : { rolloutPercentage: params.rolloutPercentage },
13679
+ ...params.fingerprintHash === void 0 ? {} : { fingerprintHash: params.fingerprintHash }
13619
13680
  } }).pipe(Effect.mapError((cause) => new UpdatePublishError({ message: `Failed to publish ${params.platform} update: ${formatCause(cause)}` })));
13620
13681
  return {
13621
13682
  platform: params.platform,
@@ -13664,6 +13725,7 @@ const runUpdatePublish = (options) => Effect.scoped(Effect.gen(function* () {
13664
13725
  const sharedExportDir = options.inputDir === void 0 ? void 0 : path.resolve(projectRoot, options.inputDir);
13665
13726
  const groupId = randomUUID();
13666
13727
  const message = resolvedMessage ?? "Publish via better-update CLI";
13728
+ const fingerprintHash = yield* runFingerprintFull(projectRoot).pipe(Effect.map((result) => result.hash), Effect.catchAll(() => Effect.succeed(void 0)));
13667
13729
  if ((yield* InteractiveMode).allow && !options.auto) {
13668
13730
  if (!(yield* confirmPublishPreview({
13669
13731
  branch,
@@ -13709,6 +13771,7 @@ const runUpdatePublish = (options) => Effect.scoped(Effect.gen(function* () {
13709
13771
  platform,
13710
13772
  signedPayload: signedPayloads[platform] ?? null,
13711
13773
  rolloutPercentage: options.rolloutPercentage,
13774
+ fingerprintHash,
13712
13775
  skipBundler: options.skipBundler,
13713
13776
  noBytecode: options.noBytecode,
13714
13777
  sourceMaps: options.sourceMaps