@better-update/cli 0.47.1 → 0.47.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 +72 -36
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import path from "node:path";
|
|
|
9
9
|
import process$1 from "node:process";
|
|
10
10
|
import AppleUtils from "@expo/apple-utils";
|
|
11
11
|
import { autocomplete, cancel, confirm, isCancel, multiselect, password, select, text } from "@clack/prompts";
|
|
12
|
-
import { open, readFile, writeFile } from "node:fs/promises";
|
|
12
|
+
import { mkdtemp, open, readFile, rm, writeFile } from "node:fs/promises";
|
|
13
13
|
import { X509Certificate, createHash, createSign, createVerify, randomBytes, randomUUID } from "node:crypto";
|
|
14
14
|
import { accessSync, chmodSync, constants, createReadStream, promises } from "node:fs";
|
|
15
15
|
import { Entry } from "@napi-rs/keyring";
|
|
@@ -35,7 +35,7 @@ var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
|
|
|
35
35
|
|
|
36
36
|
//#endregion
|
|
37
37
|
//#region package.json
|
|
38
|
-
var version = "0.47.
|
|
38
|
+
var version = "0.47.3";
|
|
39
39
|
|
|
40
40
|
//#endregion
|
|
41
41
|
//#region src/lib/interactive-mode.ts
|
|
@@ -3466,7 +3466,9 @@ const promptText = (message, options) => Effect.gen(function* () {
|
|
|
3466
3466
|
return handleCancel(yield* Effect.promise(async () => text(compact({
|
|
3467
3467
|
message,
|
|
3468
3468
|
placeholder: options?.placeholder,
|
|
3469
|
-
defaultValue: options?.defaultValue
|
|
3469
|
+
defaultValue: options?.defaultValue,
|
|
3470
|
+
initialValue: options?.initialValue,
|
|
3471
|
+
validate: options?.validate
|
|
3470
3472
|
}))));
|
|
3471
3473
|
});
|
|
3472
3474
|
const promptConfirm = (message, options) => Effect.gen(function* () {
|
|
@@ -21541,9 +21543,16 @@ const ExecErrorSchema = Schema.Struct({
|
|
|
21541
21543
|
stdout: Schema.optional(Schema.String),
|
|
21542
21544
|
stderr: Schema.optional(Schema.String)
|
|
21543
21545
|
});
|
|
21544
|
-
const runAltool = (args) => Effect.tryPromise({
|
|
21546
|
+
const runAltool = (args, extraEnv) => Effect.tryPromise({
|
|
21545
21547
|
try: async () => {
|
|
21546
|
-
const
|
|
21548
|
+
const options = extraEnv ? {
|
|
21549
|
+
encoding: "utf8",
|
|
21550
|
+
env: {
|
|
21551
|
+
...process.env,
|
|
21552
|
+
...extraEnv
|
|
21553
|
+
}
|
|
21554
|
+
} : { encoding: "utf8" };
|
|
21555
|
+
const { stdout, stderr } = await execFileAsync("xcrun", ["altool", ...args], options);
|
|
21547
21556
|
return {
|
|
21548
21557
|
exitCode: 0,
|
|
21549
21558
|
stdout,
|
|
@@ -21693,11 +21702,23 @@ const resolveAscUploadCredentials = (params) => Effect.gen(function* () {
|
|
|
21693
21702
|
p8Pem: creds.p8Pem
|
|
21694
21703
|
})), Effect.catchAll((error) => printHuman(`Could not prepare ASC API key ${credsKeyId} (${messageOf(error)}).`).pipe(Effect.as(null))));
|
|
21695
21704
|
});
|
|
21696
|
-
/**
|
|
21697
|
-
|
|
21698
|
-
|
|
21699
|
-
|
|
21700
|
-
|
|
21705
|
+
/**
|
|
21706
|
+
* `altool --apiKey <id>` searches for a file named *exactly* `AuthKey_<id>.p8` in
|
|
21707
|
+
* the standard `private_keys` dirs plus `$API_PRIVATE_KEYS_DIR`. Write the decrypted
|
|
21708
|
+
* `.p8` under that exact name into a fresh private temp dir and return the dir so the
|
|
21709
|
+
* caller can point `API_PRIVATE_KEYS_DIR` at it (and remove it afterward — it holds
|
|
21710
|
+
* the unencrypted signing key).
|
|
21711
|
+
*/
|
|
21712
|
+
const writeP8KeyDir = (credentials) => Effect.promise(async () => {
|
|
21713
|
+
const dir = await mkdtemp(path.join(tmpdir(), "better-update-asc-"));
|
|
21714
|
+
await writeFile(path.join(dir, `AuthKey_${credentials.keyId}.p8`), credentials.p8Pem, "utf8");
|
|
21715
|
+
return dir;
|
|
21716
|
+
});
|
|
21717
|
+
const removeKeyDir = (dir) => Effect.promise(async () => {
|
|
21718
|
+
await rm(dir, {
|
|
21719
|
+
recursive: true,
|
|
21720
|
+
force: true
|
|
21721
|
+
});
|
|
21701
21722
|
});
|
|
21702
21723
|
const baseAltoolArgs = (ipaPath) => [
|
|
21703
21724
|
"--upload-app",
|
|
@@ -21722,15 +21743,12 @@ const buildAltoolArgs = (params) => Effect.gen(function* () {
|
|
|
21722
21743
|
code: "SUBMISSION_ASC_KEY_FETCH_FAILED",
|
|
21723
21744
|
message: "ASC API key is required for an asc-api-key upload but was not resolved."
|
|
21724
21745
|
});
|
|
21725
|
-
const p8Path = yield* writeP8ForAltool(params.ascCredentials);
|
|
21726
21746
|
return [
|
|
21727
21747
|
...baseAltoolArgs(params.ipaPath),
|
|
21728
21748
|
"--apiKey",
|
|
21729
21749
|
params.ascCredentials.keyId,
|
|
21730
21750
|
"--apiIssuer",
|
|
21731
|
-
params.ascCredentials.issuerId
|
|
21732
|
-
"--apiKeyDir",
|
|
21733
|
-
path.dirname(p8Path)
|
|
21751
|
+
params.ascCredentials.issuerId
|
|
21734
21752
|
];
|
|
21735
21753
|
});
|
|
21736
21754
|
const runIosSubmit = (inputs) => Effect.gen(function* () {
|
|
@@ -21756,7 +21774,7 @@ const runIosSubmit = (inputs) => Effect.gen(function* () {
|
|
|
21756
21774
|
ipaPath
|
|
21757
21775
|
});
|
|
21758
21776
|
yield* patchSubmissionStatus(inputs.api, inputs.submissionId, { status: "IN_PROGRESS" });
|
|
21759
|
-
const result = yield* runAltool(altoolArgs);
|
|
21777
|
+
const result = inputs.auth.kind === "asc-api-key" && ascCredentials !== null ? yield* Effect.acquireUseRelease(writeP8KeyDir(ascCredentials), (keyDir) => runAltool(altoolArgs, { API_PRIVATE_KEYS_DIR: keyDir }), (keyDir) => removeKeyDir(keyDir)) : yield* runAltool(altoolArgs);
|
|
21760
21778
|
if (result.exitCode !== 0) {
|
|
21761
21779
|
yield* patchSubmissionStatus(inputs.api, inputs.submissionId, {
|
|
21762
21780
|
status: "ERRORED",
|
|
@@ -35046,21 +35064,34 @@ const createApp = (cookieCtx, name, companyName, input) => wrapConnect("apple-cr
|
|
|
35046
35064
|
});
|
|
35047
35065
|
}));
|
|
35048
35066
|
/**
|
|
35049
|
-
* Best-effort App Store name default
|
|
35050
|
-
* `name` (app.json `expo.name`)
|
|
35051
|
-
*
|
|
35052
|
-
*
|
|
35067
|
+
* Best-effort App Store name default to pre-fill the prompt with. Prefers the
|
|
35068
|
+
* Expo config's `name` (app.json `expo.name`), then the passed-in fallback (the
|
|
35069
|
+
* better-update project name) so non-Expo projects — which have no `@expo/config`
|
|
35070
|
+
* — still get a sensible default. Returns `undefined` when neither is available.
|
|
35053
35071
|
*/
|
|
35054
|
-
const resolveDefaultAppName = (
|
|
35072
|
+
const resolveDefaultAppName = (input) => readExpoConfig(input.projectRoot).pipe(Effect.map((config) => config.name?.trim() ? config.name.trim() : void 0), Effect.orElseSucceed(() => void 0), Effect.map((expoName) => {
|
|
35073
|
+
const fallback = input.defaultAppName?.trim();
|
|
35074
|
+
return expoName ?? (fallback || void 0);
|
|
35075
|
+
}));
|
|
35076
|
+
/** Reject a blank app name so the prompt re-asks instead of 500'ing `App.createAsync`. */
|
|
35077
|
+
const requireNonEmptyName = (value) => value?.trim() ? void 0 : "An app name is required.";
|
|
35055
35078
|
/**
|
|
35056
|
-
* The App Store name to create the app under. A configured `appName`
|
|
35057
|
-
* prompt
|
|
35058
|
-
*
|
|
35079
|
+
* The App Store name to create the app under. A non-empty configured `appName`
|
|
35080
|
+
* wins; else prompt — pre-filled with the resolved default (Expo `expo.name` or
|
|
35081
|
+
* the better-update project name) and *required*, so an empty Enter re-asks
|
|
35082
|
+
* rather than reaching `App.createAsync` with a blank name (which Apple 500s).
|
|
35059
35083
|
*/
|
|
35060
35084
|
const resolveAppName = (input) => Effect.gen(function* () {
|
|
35061
|
-
|
|
35062
|
-
|
|
35063
|
-
|
|
35085
|
+
const configured = input.appName?.trim();
|
|
35086
|
+
if (configured) return configured;
|
|
35087
|
+
const defaultName = yield* resolveDefaultAppName(input);
|
|
35088
|
+
return (yield* promptText("App name (as shown on the App Store)", defaultName === void 0 ? {
|
|
35089
|
+
placeholder: input.bundleIdentifier,
|
|
35090
|
+
validate: requireNonEmptyName
|
|
35091
|
+
} : {
|
|
35092
|
+
initialValue: defaultName,
|
|
35093
|
+
validate: requireNonEmptyName
|
|
35094
|
+
})).trim();
|
|
35064
35095
|
});
|
|
35065
35096
|
const ensureAscAppForSubmit = (input) => Effect.gen(function* () {
|
|
35066
35097
|
const ctx = buildTokenRequestContext(input.credentials);
|
|
@@ -35269,16 +35300,20 @@ const submitIosBranch = (params) => Effect.gen(function* () {
|
|
|
35269
35300
|
return false;
|
|
35270
35301
|
}
|
|
35271
35302
|
let resolvedAscAppId = iosProfile?.ascAppId;
|
|
35272
|
-
if (wantsConfig && resolvedAscAppId === void 0 && ascCredentials !== null)
|
|
35273
|
-
|
|
35274
|
-
|
|
35275
|
-
|
|
35276
|
-
|
|
35277
|
-
|
|
35278
|
-
|
|
35279
|
-
|
|
35280
|
-
|
|
35281
|
-
|
|
35303
|
+
if (wantsConfig && resolvedAscAppId === void 0 && ascCredentials !== null) {
|
|
35304
|
+
const defaultAppName = yield* api.projects.get({ path: { id: params.projectId } }).pipe(Effect.map((project) => project.name), Effect.orElseSucceed(() => void 0));
|
|
35305
|
+
resolvedAscAppId = toOptional(yield* ensureAscAppForSubmit({
|
|
35306
|
+
credentials: ascCredentials,
|
|
35307
|
+
projectRoot: params.projectRoot,
|
|
35308
|
+
profileName: params.profile,
|
|
35309
|
+
bundleIdentifier: iosConfig.bundleIdentifier,
|
|
35310
|
+
appName: iosProfile?.appName,
|
|
35311
|
+
defaultAppName,
|
|
35312
|
+
sku: iosProfile?.sku,
|
|
35313
|
+
companyName: iosProfile?.companyName,
|
|
35314
|
+
primaryLocale: iosProfile?.language
|
|
35315
|
+
}));
|
|
35316
|
+
}
|
|
35282
35317
|
yield* printHuman(auth.kind === "app-specific-password" ? "Running xcrun altool upload (Apple ID app-specific password)..." : "Running xcrun altool upload (ASC API key)...");
|
|
35283
35318
|
yield* runIosSubmit({
|
|
35284
35319
|
api,
|
|
@@ -35318,6 +35353,7 @@ const runFlow = (api, projectId, args) => Effect.gen(function* () {
|
|
|
35318
35353
|
if (args.platform === "ios" && iosConfig !== void 0) {
|
|
35319
35354
|
if (!(yield* submitIosBranch({
|
|
35320
35355
|
api,
|
|
35356
|
+
projectId,
|
|
35321
35357
|
submissionId: submission.id,
|
|
35322
35358
|
projectRoot: args.projectRoot,
|
|
35323
35359
|
profile: args.profile,
|