@better-update/cli 0.45.0 → 0.46.0
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 +123 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -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.
|
|
38
|
+
var version = "0.46.0";
|
|
39
39
|
|
|
40
40
|
//#endregion
|
|
41
41
|
//#region src/lib/interactive-mode.ts
|
|
@@ -5055,6 +5055,27 @@ const writeEasJsonPatch = (projectRoot, patch) => Effect.gen(function* () {
|
|
|
5055
5055
|
return filePath;
|
|
5056
5056
|
});
|
|
5057
5057
|
/**
|
|
5058
|
+
* Set `submit.<profileName>.ios.ascApiKeyId` in `eas.json`, preserving every
|
|
5059
|
+
* other submit profile and key. Used after auto-resolving/creating an ASC API
|
|
5060
|
+
* key during `submit` so the next run reuses it instead of creating another.
|
|
5061
|
+
*/
|
|
5062
|
+
const setSubmitProfileAscApiKeyId = (projectRoot, profileName, ascApiKeyId) => Effect.gen(function* () {
|
|
5063
|
+
const existing = (yield* readEasJsonRaw(projectRoot)) ?? {};
|
|
5064
|
+
const submit = isRecord$1(existing["submit"]) ? existing["submit"] : {};
|
|
5065
|
+
const profile = isRecord$1(submit[profileName]) ? submit[profileName] : {};
|
|
5066
|
+
const ios = isRecord$1(profile["ios"]) ? profile["ios"] : {};
|
|
5067
|
+
return yield* writeEasJsonPatch(projectRoot, { submit: {
|
|
5068
|
+
...submit,
|
|
5069
|
+
[profileName]: {
|
|
5070
|
+
...profile,
|
|
5071
|
+
ios: {
|
|
5072
|
+
...ios,
|
|
5073
|
+
ascApiKeyId
|
|
5074
|
+
}
|
|
5075
|
+
}
|
|
5076
|
+
} });
|
|
5077
|
+
});
|
|
5078
|
+
/**
|
|
5058
5079
|
* Default `build` profiles scaffolded by `init` / `build configure`. Mirrors the
|
|
5059
5080
|
* EAS three-tier convention: `development` (dev-client, internal), `preview`
|
|
5060
5081
|
* (internal QA) and `production` (store). Keep in sync with the build-profile
|
|
@@ -28459,6 +28480,20 @@ const generateAndUploadAscApiKeyViaAppleId = (api, input) => Effect.gen(function
|
|
|
28459
28480
|
role: input.role
|
|
28460
28481
|
};
|
|
28461
28482
|
});
|
|
28483
|
+
/**
|
|
28484
|
+
* List the team's active App Store Connect API keys as seen on Apple (via the
|
|
28485
|
+
* cookie session). Used before auto-creating a key to avoid making a redundant
|
|
28486
|
+
* one — note a key's `.p8` is downloadable only once, so a key listed here is
|
|
28487
|
+
* usable for publishing only if its `.p8` was captured at creation (i.e. it is
|
|
28488
|
+
* already in the vault). Surfacing them lets the caller warn + respect Apple's
|
|
28489
|
+
* per-team key cap rather than blindly creating another.
|
|
28490
|
+
*/
|
|
28491
|
+
const listAscApiKeysViaAppleId = (ctx) => Effect.gen(function* () {
|
|
28492
|
+
return (yield* wrap("apple-list-asc-keys", async () => AppleUtils.ApiKey.getAsync(ctx))).filter((key) => key.attributes.isActive).map((key) => ({
|
|
28493
|
+
keyId: key.id,
|
|
28494
|
+
nickname: key.attributes.nickname
|
|
28495
|
+
}));
|
|
28496
|
+
});
|
|
28462
28497
|
|
|
28463
28498
|
//#endregion
|
|
28464
28499
|
//#region src/application/credentials-manager-ios-asc.ts
|
|
@@ -35518,6 +35553,78 @@ const statusCommand = defineCommand({
|
|
|
35518
35553
|
}), { json: "value" })
|
|
35519
35554
|
});
|
|
35520
35555
|
|
|
35556
|
+
//#endregion
|
|
35557
|
+
//#region src/application/submit-asc-key.ts
|
|
35558
|
+
const ROLE_CHOICES = [{
|
|
35559
|
+
value: "ADMIN",
|
|
35560
|
+
label: "ADMIN (default)"
|
|
35561
|
+
}, {
|
|
35562
|
+
value: "APP_MANAGER",
|
|
35563
|
+
label: "APP_MANAGER (least privilege for app management)"
|
|
35564
|
+
}];
|
|
35565
|
+
const CREATE_CHOICE = "__create__";
|
|
35566
|
+
/** Best-effort: write the resolved id back to eas.json so the next run reuses it. */
|
|
35567
|
+
const persist = (input, keyId) => setSubmitProfileAscApiKeyId(input.projectRoot, input.profileName, keyId).pipe(Effect.flatMap((path) => printHuman(`Saved ascApiKeyId to ${path} (submit profile "${input.profileName}") for reuse.`)), Effect.catchAll((error) => printHuman(`Note: could not write ascApiKeyId to eas.json (${error.message}). Add it manually to reuse this key.`)));
|
|
35568
|
+
/** Log in, warn about any existing team keys, then (with consent) create + persist. */
|
|
35569
|
+
const createAndPersist = (input) => Effect.gen(function* () {
|
|
35570
|
+
const auth = yield* AppleAuth;
|
|
35571
|
+
const session = yield* auth.ensureLoggedIn();
|
|
35572
|
+
const ctx = auth.buildRequestContext(session);
|
|
35573
|
+
const teamKeys = yield* listAscApiKeysViaAppleId(ctx).pipe(Effect.orElseSucceed(() => []));
|
|
35574
|
+
const hasTeamKeys = teamKeys.length > 0;
|
|
35575
|
+
if (hasTeamKeys) {
|
|
35576
|
+
yield* printHuman(`Your Apple team already has ${String(teamKeys.length)} App Store Connect API key(s): ${teamKeys.map((key) => key.nickname).join(", ")}.`);
|
|
35577
|
+
yield* printHuman("A key's .p8 is downloadable only once at creation, so an existing key is reusable only if you still have its .p8 (import it with `credentials upload-asc-key`). Apple also caps the number of keys per team.");
|
|
35578
|
+
}
|
|
35579
|
+
if (!(yield* promptConfirm(hasTeamKeys ? "Create a new ASC API key anyway?" : "No App Store Connect API key found. Create one now from your Apple ID?", { initialValue: !hasTeamKeys }))) return null;
|
|
35580
|
+
const role = yield* promptSelect("Select a role for the generated API key", ROLE_CHOICES);
|
|
35581
|
+
yield* printHuman("Creating an App Store Connect API key via your Apple ID...");
|
|
35582
|
+
const created = yield* generateAndUploadAscApiKeyViaAppleId(input.api, {
|
|
35583
|
+
context: ctx,
|
|
35584
|
+
appleTeamIdentifier: session.teamId,
|
|
35585
|
+
nickname: defaultAscApiKeyNickname(),
|
|
35586
|
+
role
|
|
35587
|
+
});
|
|
35588
|
+
yield* printHuman(`Created and stored ASC API key ${created.keyId}.`);
|
|
35589
|
+
yield* persist(input, created.id);
|
|
35590
|
+
return created.id;
|
|
35591
|
+
});
|
|
35592
|
+
/**
|
|
35593
|
+
* Resolve an ASC API key id to upload a `submit` build with when none is set in
|
|
35594
|
+
* the submit profile. Reuses a stored vault key when possible (the only keys we
|
|
35595
|
+
* hold a usable `.p8` for), else offers to create one from the Apple ID session.
|
|
35596
|
+
* Returns the resolved id, or `null` when none could be resolved — non-interactive
|
|
35597
|
+
* runs, a declined prompt, or any failure (login/create/network) degrade to `null`
|
|
35598
|
+
* so the caller falls back to queuing the submission with guidance rather than
|
|
35599
|
+
* crashing. Persists the resolved id to `eas.json` for reuse.
|
|
35600
|
+
*/
|
|
35601
|
+
const ensureAscApiKeyForSubmit = (input) => Effect.gen(function* () {
|
|
35602
|
+
if (!(yield* InteractiveMode).allow) return null;
|
|
35603
|
+
const stored = yield* input.api.ascApiKeys.list();
|
|
35604
|
+
if (stored.items.length === 1) {
|
|
35605
|
+
const [only] = stored.items;
|
|
35606
|
+
if (only !== void 0) {
|
|
35607
|
+
yield* printHuman(`Using your stored ASC API key "${only.name}" (${only.keyId}).`);
|
|
35608
|
+
yield* persist(input, only.id);
|
|
35609
|
+
return only.id;
|
|
35610
|
+
}
|
|
35611
|
+
}
|
|
35612
|
+
if (stored.items.length > 1) {
|
|
35613
|
+
const picked = yield* promptSelect("No ASC API key in this submit profile. Pick one to use, or create a new one:", [...stored.items.map((key) => ({
|
|
35614
|
+
value: key.id,
|
|
35615
|
+
label: `${key.name} (${key.keyId})`
|
|
35616
|
+
})), {
|
|
35617
|
+
value: CREATE_CHOICE,
|
|
35618
|
+
label: "Create a new ASC API key from my Apple ID"
|
|
35619
|
+
}]);
|
|
35620
|
+
if (picked !== CREATE_CHOICE) {
|
|
35621
|
+
yield* persist(input, picked);
|
|
35622
|
+
return picked;
|
|
35623
|
+
}
|
|
35624
|
+
}
|
|
35625
|
+
return yield* createAndPersist(input);
|
|
35626
|
+
}).pipe(Effect.catchAll((error) => printHuman(`Could not set up an App Store Connect API key (${messageOf(error)}). The submission was queued — create one with \`credentials generate asc-key\` and re-run.`).pipe(Effect.as(null))));
|
|
35627
|
+
|
|
35521
35628
|
//#endregion
|
|
35522
35629
|
//#region src/commands/submit/index.ts
|
|
35523
35630
|
const PLATFORMS = ["ios", "android"];
|
|
@@ -35604,7 +35711,17 @@ const runFlow = (api, projectId, args) => Effect.gen(function* () {
|
|
|
35604
35711
|
appleId: iosProfile?.appleId,
|
|
35605
35712
|
ascApiKeyId: iosProfile?.ascApiKeyId,
|
|
35606
35713
|
hasAppSpecificPassword: hasAppleAppSpecificPassword()
|
|
35607
|
-
})
|
|
35714
|
+
}) ?? (yield* Effect.gen(function* () {
|
|
35715
|
+
const resolvedKeyId = yield* ensureAscApiKeyForSubmit({
|
|
35716
|
+
api,
|
|
35717
|
+
projectRoot: args.projectRoot,
|
|
35718
|
+
profileName: args.profile
|
|
35719
|
+
});
|
|
35720
|
+
return resolvedKeyId === null ? null : {
|
|
35721
|
+
kind: "asc-api-key",
|
|
35722
|
+
ascApiKeyId: resolvedKeyId
|
|
35723
|
+
};
|
|
35724
|
+
}));
|
|
35608
35725
|
if (auth === null) {
|
|
35609
35726
|
yield* printHuman("iOS submission queued. Add ascApiKeyId to the eas.json submit profile, or set appleId + the EXPO_APPLE_APP_SPECIFIC_PASSWORD env var, to enable client-side altool upload.");
|
|
35610
35727
|
return submission;
|
|
@@ -35618,7 +35735,7 @@ const runFlow = (api, projectId, args) => Effect.gen(function* () {
|
|
|
35618
35735
|
value: args.archive.archiveUrl
|
|
35619
35736
|
},
|
|
35620
35737
|
auth,
|
|
35621
|
-
ascApiKeyId: iosProfile?.ascApiKeyId,
|
|
35738
|
+
ascApiKeyId: auth.kind === "asc-api-key" ? auth.ascApiKeyId : iosProfile?.ascApiKeyId,
|
|
35622
35739
|
config: {
|
|
35623
35740
|
bundleIdentifier: iosConfig.bundleIdentifier,
|
|
35624
35741
|
ascAppId: iosProfile?.ascAppId,
|
|
@@ -35702,7 +35819,8 @@ const submitCommand = defineCommand({
|
|
|
35702
35819
|
}
|
|
35703
35820
|
const projectId = yield* readProjectId;
|
|
35704
35821
|
const api = yield* apiClient;
|
|
35705
|
-
const
|
|
35822
|
+
const projectRoot = yield* (yield* CliRuntime).cwd;
|
|
35823
|
+
const easProfile = yield* readSubmitProfile(projectRoot, args.profile);
|
|
35706
35824
|
const archive = yield* resolveArchive(api, projectId, platform, {
|
|
35707
35825
|
id: args.id,
|
|
35708
35826
|
path: args.path,
|
|
@@ -35716,6 +35834,7 @@ const submitCommand = defineCommand({
|
|
|
35716
35834
|
yield* runFlow(api, projectId, {
|
|
35717
35835
|
platform,
|
|
35718
35836
|
profile: args.profile,
|
|
35837
|
+
projectRoot,
|
|
35719
35838
|
easProfile,
|
|
35720
35839
|
archive,
|
|
35721
35840
|
wait: args.wait,
|