eas-cli 20.0.0 → 20.2.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.
Files changed (40) hide show
  1. package/README.md +197 -116
  2. package/build/commands/simulator/exec.d.ts +12 -0
  3. package/build/commands/simulator/exec.js +42 -0
  4. package/build/commands/simulator/get.d.ts +2 -1
  5. package/build/commands/simulator/get.js +13 -7
  6. package/build/commands/simulator/start.d.ts +4 -0
  7. package/build/commands/simulator/start.js +70 -5
  8. package/build/commands/simulator/stop.d.ts +2 -1
  9. package/build/commands/simulator/stop.js +16 -9
  10. package/build/commands/update/embedded/delete.d.ts +15 -0
  11. package/build/commands/update/embedded/delete.js +55 -0
  12. package/build/commands/update/embedded/list.d.ts +19 -0
  13. package/build/commands/update/embedded/list.js +132 -0
  14. package/build/commands/update/embedded/view.d.ts +17 -0
  15. package/build/commands/update/embedded/view.js +75 -0
  16. package/build/commands/update/rollback.d.ts +11 -0
  17. package/build/commands/update/rollback.js +117 -14
  18. package/build/credentials/ios/actions/AscApiKeyUtils.d.ts +20 -0
  19. package/build/credentials/ios/actions/AscApiKeyUtils.js +64 -0
  20. package/build/credentials/ios/actions/ConfigureProvisioningProfile.js +2 -4
  21. package/build/credentials/ios/actions/CreateProvisioningProfile.js +2 -4
  22. package/build/credentials/ios/actions/SetUpAdhocProvisioningProfile.js +3 -20
  23. package/build/credentials/ios/actions/SetUpProvisioningProfile.d.ts +10 -0
  24. package/build/credentials/ios/actions/SetUpProvisioningProfile.js +39 -5
  25. package/build/credentials/ios/appstore/resolveCredentials.d.ts +1 -0
  26. package/build/credentials/ios/appstore/resolveCredentials.js +1 -0
  27. package/build/graphql/generated.d.ts +457 -4
  28. package/build/graphql/generated.js +35 -1
  29. package/build/graphql/mutations/EmbeddedUpdateMutation.d.ts +5 -0
  30. package/build/graphql/mutations/EmbeddedUpdateMutation.js +14 -0
  31. package/build/graphql/queries/EmbeddedUpdateQuery.d.ts +18 -0
  32. package/build/graphql/queries/EmbeddedUpdateQuery.js +81 -0
  33. package/build/graphql/queries/UpdateQuery.d.ts +2 -1
  34. package/build/graphql/queries/UpdateQuery.js +52 -0
  35. package/build/simulator/env.d.ts +7 -0
  36. package/build/simulator/env.js +44 -0
  37. package/build/simulator/utils.d.ts +3 -1
  38. package/build/simulator/utils.js +26 -7
  39. package/oclif.manifest.json +945 -525
  40. package/package.json +5 -5
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatEmbeddedUpdate = formatEmbeddedUpdate;
4
+ const tslib_1 = require("tslib");
5
+ const core_1 = require("@oclif/core");
6
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
+ const EasCommand_1 = tslib_1.__importDefault(require("../../../commandUtils/EasCommand"));
8
+ const flags_1 = require("../../../commandUtils/flags");
9
+ const EmbeddedUpdateQuery_1 = require("../../../graphql/queries/EmbeddedUpdateQuery");
10
+ const log_1 = tslib_1.__importDefault(require("../../../log"));
11
+ const date_1 = require("../../../utils/date");
12
+ const files_1 = require("../../../utils/files");
13
+ const formatFields_1 = tslib_1.__importDefault(require("../../../utils/formatFields"));
14
+ const json_1 = require("../../../utils/json");
15
+ class UpdateEmbeddedView extends EasCommand_1.default {
16
+ static description = 'view details of an embedded update registered with EAS Update';
17
+ static args = {
18
+ id: core_1.Args.string({
19
+ required: true,
20
+ description: 'The ID of the embedded update (manifest UUID from app.manifest).',
21
+ }),
22
+ };
23
+ static flags = {
24
+ ...flags_1.EasJsonOnlyFlag,
25
+ };
26
+ static contextDefinition = {
27
+ ...this.ContextOptions.ProjectId,
28
+ ...this.ContextOptions.LoggedIn,
29
+ };
30
+ async runAsync() {
31
+ const { args: { id: embeddedUpdateId }, flags: { json: jsonFlag }, } = await this.parse(UpdateEmbeddedView);
32
+ const { projectId, loggedIn: { graphqlClient }, } = await this.getContextAsync(UpdateEmbeddedView, { nonInteractive: true });
33
+ if (jsonFlag) {
34
+ (0, json_1.enableJsonOutput)();
35
+ }
36
+ let embeddedUpdate;
37
+ try {
38
+ embeddedUpdate = await EmbeddedUpdateQuery_1.EmbeddedUpdateQuery.viewByIdAsync(graphqlClient, {
39
+ embeddedUpdateId,
40
+ appId: projectId,
41
+ });
42
+ }
43
+ catch (e) {
44
+ if ((0, EmbeddedUpdateQuery_1.isEmbeddedUpdateNotFoundError)(e)) {
45
+ core_1.Errors.error(`No embedded update found with id "${embeddedUpdateId}" for this project. ` +
46
+ `Run "eas update:embedded:list" to see the embedded updates registered for this app.`, { exit: 1 });
47
+ }
48
+ throw e;
49
+ }
50
+ if (jsonFlag) {
51
+ (0, json_1.printJsonOnlyOutput)(embeddedUpdate);
52
+ return;
53
+ }
54
+ log_1.default.addNewLineIfNone();
55
+ log_1.default.log(chalk_1.default.bold('Embedded update:'));
56
+ log_1.default.log(formatEmbeddedUpdate(embeddedUpdate));
57
+ }
58
+ }
59
+ exports.default = UpdateEmbeddedView;
60
+ function formatEmbeddedUpdate(embeddedUpdate) {
61
+ const createdAt = new Date(embeddedUpdate.createdAt);
62
+ const bundleSize = embeddedUpdate.launchAsset.finalFileSize ?? embeddedUpdate.launchAsset.fileSize;
63
+ return (0, formatFields_1.default)([
64
+ { label: 'ID', value: embeddedUpdate.id },
65
+ { label: 'Platform', value: embeddedUpdate.platform.toLowerCase() },
66
+ { label: 'Runtime version', value: embeddedUpdate.runtimeVersion },
67
+ { label: 'Channel', value: embeddedUpdate.channel },
68
+ { label: 'Bundle size', value: (0, files_1.formatBytes)(bundleSize) },
69
+ { label: 'Bundle SHA-256', value: embeddedUpdate.launchAsset.fileSHA256 },
70
+ {
71
+ label: 'Created at',
72
+ value: `${createdAt.toLocaleString()} (${(0, date_1.fromNow)(createdAt)} ago)`,
73
+ },
74
+ ]);
75
+ }
@@ -1,8 +1,19 @@
1
1
  import EasCommand from '../../commandUtils/EasCommand';
2
2
  export default class UpdateRollback extends EasCommand {
3
3
  static description: string;
4
+ static args: {
5
+ groupId: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
6
+ };
4
7
  static flags: {
8
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ 'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ message: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ platform: import("@oclif/core/lib/interfaces").OptionFlag<"android" | "ios" | "all", import("@oclif/core/lib/interfaces").CustomOptions>;
5
12
  'private-key-path': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
6
13
  };
14
+ static contextDefinition: {
15
+ loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
16
+ privateProjectConfig: import("../../commandUtils/context/PrivateProjectConfigContextField").PrivateProjectConfigContextField;
17
+ };
7
18
  runAsync(): Promise<void>;
8
19
  }
@@ -5,35 +5,138 @@ const core_1 = require("@oclif/core");
5
5
  const republish_1 = tslib_1.__importDefault(require("./republish"));
6
6
  const roll_back_to_embedded_1 = tslib_1.__importDefault(require("./roll-back-to-embedded"));
7
7
  const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
8
+ const flags_1 = require("../../commandUtils/flags");
9
+ const UpdateQuery_1 = require("../../graphql/queries/UpdateQuery");
8
10
  const prompts_1 = require("../../prompts");
11
+ const defaultRollbackPlatforms = ['android', 'ios'];
9
12
  class UpdateRollback extends EasCommand_1.default {
10
- static description = 'Roll back to an embedded update or an existing update. Users wishing to run this command non-interactively should instead execute "eas update:republish" or "eas update:roll-back-to-embedded".';
13
+ static description = 'roll back to an embedded update or an existing update';
14
+ static args = {
15
+ groupId: core_1.Args.string({
16
+ description: 'The ID of the update group to roll back. Must be the latest update for its branch and runtime version. The update group published before it is republished; if there is none, a roll back to the embedded update is published. Required in non-interactive mode.',
17
+ required: false,
18
+ }),
19
+ };
11
20
  static flags = {
21
+ message: core_1.Flags.string({
22
+ char: 'm',
23
+ description: 'Short message describing the rollback update',
24
+ required: false,
25
+ }),
26
+ platform: core_1.Flags.option({
27
+ char: 'p',
28
+ options: [...defaultRollbackPlatforms, 'all'],
29
+ default: 'all',
30
+ required: false,
31
+ })(),
12
32
  'private-key-path': core_1.Flags.string({
13
33
  description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory. Only relevant if you are using code signing: https://docs.expo.dev/eas-update/code-signing/`,
14
34
  required: false,
15
35
  }),
36
+ ...flags_1.EasNonInteractiveAndJsonFlags,
37
+ };
38
+ static contextDefinition = {
39
+ ...this.ContextOptions.ProjectConfig,
40
+ ...this.ContextOptions.LoggedIn,
16
41
  };
17
42
  async runAsync() {
18
- const { flags } = await this.parse(UpdateRollback);
19
- const { choice } = await (0, prompts_1.promptAsync)({
20
- type: 'select',
21
- message: 'Which type of update would you like to roll back to?',
22
- name: 'choice',
23
- choices: [
24
- { title: 'Published Update', value: 'published' },
25
- { title: 'Embedded Update', value: 'embedded' },
26
- ],
27
- });
43
+ const { args, flags } = await this.parse(UpdateRollback);
44
+ const { json, nonInteractive } = (0, flags_1.resolveNonInteractiveAndJsonFlags)(flags);
45
+ const groupId = args.groupId;
46
+ const platform = flags.platform;
47
+ const messageArg = flags.message;
28
48
  const privateKeyPathArg = flags['private-key-path']
29
49
  ? ['--private-key-path', flags['private-key-path']]
30
50
  : [];
31
- if (choice === 'published') {
32
- await republish_1.default.run(privateKeyPathArg);
51
+ if (!groupId) {
52
+ if (nonInteractive) {
53
+ throw new Error('The update group ID argument is required in non-interactive mode.');
54
+ }
55
+ const { choice } = await (0, prompts_1.promptAsync)({
56
+ type: 'select',
57
+ message: 'Which type of update would you like to roll back to?',
58
+ name: 'choice',
59
+ choices: [
60
+ { title: 'Published Update', value: 'published' },
61
+ { title: 'Embedded Update', value: 'embedded' },
62
+ ],
63
+ });
64
+ if (choice === 'published') {
65
+ await republish_1.default.run(privateKeyPathArg);
66
+ }
67
+ else {
68
+ await roll_back_to_embedded_1.default.run(privateKeyPathArg);
69
+ }
70
+ return;
71
+ }
72
+ const { loggedIn: { graphqlClient }, privateProjectConfig: { projectId }, } = await this.getContextAsync(UpdateRollback, {
73
+ nonInteractive,
74
+ withServerSideEnvironment: null,
75
+ });
76
+ const sourceGroup = await getSourceUpdateGroupAsync(graphqlClient, groupId);
77
+ const previousGroup = await getPreviousUpdateGroupAsync(graphqlClient, projectId, sourceGroup);
78
+ const commonArgs = [
79
+ '--non-interactive',
80
+ '--platform',
81
+ platform,
82
+ ...privateKeyPathArg,
83
+ ...(json ? ['--json'] : []),
84
+ ];
85
+ if (previousGroup) {
86
+ const message = messageArg ??
87
+ `Roll back to "${previousGroup.message ?? ''}" (group: ${previousGroup.groupId})`;
88
+ await republish_1.default.run([
89
+ '--group',
90
+ previousGroup.groupId,
91
+ '--message',
92
+ message,
93
+ ...commonArgs,
94
+ ]);
33
95
  }
34
96
  else {
35
- await roll_back_to_embedded_1.default.run(privateKeyPathArg);
97
+ const message = messageArg ?? 'Roll back to embedded';
98
+ await roll_back_to_embedded_1.default.run([
99
+ '--branch',
100
+ sourceGroup.branchName,
101
+ '--runtime-version',
102
+ sourceGroup.runtimeVersion,
103
+ '--message',
104
+ message,
105
+ ...commonArgs,
106
+ ]);
36
107
  }
37
108
  }
38
109
  }
39
110
  exports.default = UpdateRollback;
111
+ async function getSourceUpdateGroupAsync(graphqlClient, groupId) {
112
+ // viewUpdateGroupAsync throws if no updates are found for the group ID.
113
+ const updateGroup = await UpdateQuery_1.UpdateQuery.viewUpdateGroupAsync(graphqlClient, { groupId });
114
+ const arbitraryUpdate = updateGroup[0];
115
+ return {
116
+ groupId,
117
+ branchName: arbitraryUpdate.branch.name,
118
+ runtimeVersion: arbitraryUpdate.runtimeVersion,
119
+ };
120
+ }
121
+ async function getPreviousUpdateGroupAsync(graphqlClient, projectId, sourceGroup) {
122
+ // Clients on a given runtime version are served the latest update group on the branch,
123
+ // so a rollback is only meaningful when the source group is that latest group. Fetch the
124
+ // two most recent groups for the runtime version (returned most-recent-first): the first
125
+ // must be the source group, and the second (if any) is the update to roll back to.
126
+ const latestGroups = await UpdateQuery_1.UpdateQuery.viewUpdateGroupsPaginatedOnBranchAsync(graphqlClient, {
127
+ appId: projectId,
128
+ branchName: sourceGroup.branchName,
129
+ first: 2,
130
+ filter: { runtimeVersions: [sourceGroup.runtimeVersion] },
131
+ });
132
+ const latestGroup = latestGroups[0];
133
+ if (!latestGroup?.length || latestGroup[0].group !== sourceGroup.groupId) {
134
+ throw new Error(`Update group "${sourceGroup.groupId}" is not the latest update on branch "${sourceGroup.branchName}" for runtime version "${sourceGroup.runtimeVersion}"${latestGroup?.length ? ` (the latest is "${latestGroup[0].group}")` : ''}. Only the latest update can be rolled back.`);
135
+ }
136
+ // Source group is the only update for this runtime version: roll back to embedded.
137
+ const previousGroup = latestGroups[1];
138
+ if (!previousGroup?.length) {
139
+ return null;
140
+ }
141
+ return { groupId: previousGroup[0].group, message: previousGroup[0].message ?? null };
142
+ }
@@ -1,6 +1,9 @@
1
+ import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
1
2
  import { AccountFragment, AppStoreConnectApiKeyFragment } from '../../../graphql/generated';
2
3
  import { CredentialsContext } from '../../context';
4
+ import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
3
5
  import { AscApiKey } from '../appstore/Credentials.types';
6
+ import { AppleTeamType } from '../appstore/authenticateTypes';
4
7
  import { AscApiKeyPath, MinimalAscApiKey } from '../credentials';
5
8
  export declare enum AppStoreApiKeyPurpose {
6
9
  SUBMISSION_SERVICE = "EAS Submit",
@@ -19,3 +22,20 @@ export declare function selectAscApiKeysFromAccountAsync(ctx: CredentialsContext
19
22
  }): Promise<AppStoreConnectApiKeyFragment | null>;
20
23
  export declare function sortAscApiKeysByUpdatedAtDesc(keys: AppStoreConnectApiKeyFragment[]): AppStoreConnectApiKeyFragment[];
21
24
  export declare function formatAscApiKey(ascApiKey: AppStoreConnectApiKeyFragment): string;
25
+ export declare function resolveAscApiKeyForAppCredentialsAsync({ graphqlClient, app, }: {
26
+ graphqlClient: ExpoGraphqlClient;
27
+ app: AppLookupParams;
28
+ }): Promise<{
29
+ ascApiKey: MinimalAscApiKey;
30
+ teamId?: string;
31
+ teamName?: string;
32
+ } | null>;
33
+ /**
34
+ * Best-effort helper that populates `ctx.appStore.authCtx` in non-interactive mode
35
+ * by loading an App Store Connect API key (from env vars, or by fetching the app's
36
+ * stored key from the www GraphQL API).
37
+ *
38
+ * Returns true if `ctx.appStore.authCtx` is set after the call, false otherwise.
39
+ * Never throws.
40
+ */
41
+ export declare function tryAuthenticateAppStoreWithEasAscApiKeyAsync(ctx: CredentialsContext, app: AppLookupParams, teamType: AppleTeamType): Promise<boolean>;
@@ -10,6 +10,8 @@ exports.getAscApiKeysFromAccountAsync = getAscApiKeysFromAccountAsync;
10
10
  exports.selectAscApiKeysFromAccountAsync = selectAscApiKeysFromAccountAsync;
11
11
  exports.sortAscApiKeysByUpdatedAtDesc = sortAscApiKeysByUpdatedAtDesc;
12
12
  exports.formatAscApiKey = formatAscApiKey;
13
+ exports.resolveAscApiKeyForAppCredentialsAsync = resolveAscApiKeyForAppCredentialsAsync;
14
+ exports.tryAuthenticateAppStoreWithEasAscApiKeyAsync = tryAuthenticateAppStoreWithEasAscApiKeyAsync;
13
15
  const tslib_1 = require("tslib");
14
16
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
15
17
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
@@ -17,10 +19,14 @@ const nanoid_1 = require("nanoid");
17
19
  const path_1 = tslib_1.__importDefault(require("path"));
18
20
  const apple_utils_1 = require("@expo/apple-utils");
19
21
  const AppleTeamFormatting_1 = require("./AppleTeamFormatting");
22
+ const AppStoreConnectApiKeyQuery_1 = require("../../../graphql/queries/AppStoreConnectApiKeyQuery");
20
23
  const log_1 = tslib_1.__importStar(require("../../../log"));
21
24
  const prompts_1 = require("../../../prompts");
22
25
  const date_1 = require("../../../utils/date");
23
26
  const promptForCredentials_1 = require("../../utils/promptForCredentials");
27
+ const GraphqlClient_1 = require("../api/GraphqlClient");
28
+ const authenticateTypes_1 = require("../appstore/authenticateTypes");
29
+ const resolveCredentials_1 = require("../appstore/resolveCredentials");
24
30
  const credentials_1 = require("../credentials");
25
31
  const validateAscApiKey_1 = require("../validators/validateAscApiKey");
26
32
  var AppStoreApiKeyPurpose;
@@ -210,3 +216,61 @@ function formatAscApiKey(ascApiKey) {
210
216
  line += chalk_1.default.gray(`\n Updated: ${(0, date_1.fromNow)(new Date(updatedAt))} ago`);
211
217
  return line;
212
218
  }
219
+ async function resolveAscApiKeyForAppCredentialsAsync({ graphqlClient, app, }) {
220
+ const ascKeyFragment = await (0, GraphqlClient_1.getAscApiKeyForAppSubmissionsAsync)(graphqlClient, app);
221
+ if (!ascKeyFragment) {
222
+ return null;
223
+ }
224
+ const fullKey = await AppStoreConnectApiKeyQuery_1.AppStoreConnectApiKeyQuery.getByIdAsync(graphqlClient, ascKeyFragment.id);
225
+ return {
226
+ ascApiKey: {
227
+ keyP8: fullKey.keyP8,
228
+ keyId: fullKey.keyIdentifier,
229
+ issuerId: fullKey.issuerIdentifier,
230
+ },
231
+ teamId: ascKeyFragment.appleTeam?.appleTeamIdentifier,
232
+ teamName: ascKeyFragment.appleTeam?.appleTeamName ?? undefined,
233
+ };
234
+ }
235
+ /**
236
+ * Best-effort helper that populates `ctx.appStore.authCtx` in non-interactive mode
237
+ * by loading an App Store Connect API key (from env vars, or by fetching the app's
238
+ * stored key from the www GraphQL API).
239
+ *
240
+ * Returns true if `ctx.appStore.authCtx` is set after the call, false otherwise.
241
+ * Never throws.
242
+ */
243
+ async function tryAuthenticateAppStoreWithEasAscApiKeyAsync(ctx, app, teamType) {
244
+ if (ctx.appStore.authCtx) {
245
+ return true;
246
+ }
247
+ try {
248
+ if ((0, resolveCredentials_1.hasAscEnvVars)()) {
249
+ await ctx.appStore.ensureAuthenticatedAsync({
250
+ mode: authenticateTypes_1.AuthenticationMode.API_KEY,
251
+ teamType,
252
+ });
253
+ return !!ctx.appStore.authCtx;
254
+ }
255
+ const resolvedKey = await resolveAscApiKeyForAppCredentialsAsync({
256
+ graphqlClient: ctx.graphqlClient,
257
+ app,
258
+ });
259
+ if (!resolvedKey) {
260
+ return false;
261
+ }
262
+ log_1.default.log('Using App Store Connect API Key from EAS credentials service.');
263
+ await ctx.appStore.ensureAuthenticatedAsync({
264
+ mode: authenticateTypes_1.AuthenticationMode.API_KEY,
265
+ ascApiKey: resolvedKey.ascApiKey,
266
+ teamId: resolvedKey.teamId,
267
+ teamName: resolvedKey.teamName,
268
+ teamType,
269
+ });
270
+ return !!ctx.appStore.authCtx;
271
+ }
272
+ catch (err) {
273
+ log_1.default.warn(`Failed to authenticate with the App Store Connect API key from EAS credentials service: ${err.message ?? err}`);
274
+ return false;
275
+ }
276
+ }
@@ -8,7 +8,6 @@ const log_1 = tslib_1.__importStar(require("../../../log"));
8
8
  const ora_1 = require("../../../ora");
9
9
  const target_1 = require("../../../project/ios/target");
10
10
  const errors_1 = require("../../errors");
11
- const authenticateTypes_1 = require("../appstore/authenticateTypes");
12
11
  class ConfigureProvisioningProfile {
13
12
  app;
14
13
  target;
@@ -24,9 +23,8 @@ class ConfigureProvisioningProfile {
24
23
  if (ctx.freezeCredentials) {
25
24
  throw new errors_1.ForbidCredentialModificationError('Remove the --freeze-credentials flag to configure a Provisioning Profile.');
26
25
  }
27
- else if (ctx.nonInteractive &&
28
- ctx.appStore.defaultAuthenticationMode !== authenticateTypes_1.AuthenticationMode.API_KEY) {
29
- throw new errors_1.InsufficientAuthenticationNonInteractiveError(`In order to configure your Provisioning Profile, authentication with an ASC API key is required in non-interactive mode. ${(0, log_1.learnMore)('https://docs.expo.dev/build/building-on-ci/#optional-provide-an-asc-api-token-for-your-apple-team')}`);
26
+ else if (ctx.nonInteractive && !ctx.appStore.authCtx) {
27
+ throw new errors_1.InsufficientAuthenticationNonInteractiveError(`In order to configure your Provisioning Profile, authentication with an ASC API key is required in non-interactive mode. Either set the EXPO_ASC_API_KEY_PATH/EXPO_ASC_KEY_ID/EXPO_ASC_ISSUER_ID environment variables, or configure an App Store Connect API Key for submissions for bundle identifier ${this.app.bundleIdentifier} on EAS. ${(0, log_1.learnMore)('https://docs.expo.dev/build/building-on-ci/#optional-provide-an-asc-api-token-for-your-apple-team')}`);
30
28
  }
31
29
  const { developerPortalIdentifier, provisioningProfile } = this.originalProvisioningProfile;
32
30
  if (!developerPortalIdentifier && !provisioningProfile) {
@@ -9,7 +9,6 @@ const ProvisioningProfileUtils_1 = require("./ProvisioningProfileUtils");
9
9
  const log_1 = tslib_1.__importStar(require("../../../log"));
10
10
  const errors_1 = require("../../errors");
11
11
  const promptForCredentials_1 = require("../../utils/promptForCredentials");
12
- const authenticateTypes_1 = require("../appstore/authenticateTypes");
13
12
  const credentials_1 = require("../credentials");
14
13
  class CreateProvisioningProfile {
15
14
  app;
@@ -24,9 +23,8 @@ class CreateProvisioningProfile {
24
23
  if (ctx.freezeCredentials) {
25
24
  throw new errors_1.ForbidCredentialModificationError('Run this command again without the --freeze-credentials flag in order to generate a new Provisioning Profile.');
26
25
  }
27
- else if (ctx.nonInteractive &&
28
- ctx.appStore.defaultAuthenticationMode !== authenticateTypes_1.AuthenticationMode.API_KEY) {
29
- throw new errors_1.InsufficientAuthenticationNonInteractiveError(`In order to generate a new Provisioning Profile, authentication with an ASC API key is required in non-interactive mode. ${(0, log_1.learnMore)('https://docs.expo.dev/build/building-on-ci/#optional-provide-an-asc-api-token-for-your-apple-team')}`);
26
+ else if (ctx.nonInteractive && !ctx.appStore.authCtx) {
27
+ throw new errors_1.InsufficientAuthenticationNonInteractiveError(`In order to generate a new Provisioning Profile, authentication with an ASC API key is required in non-interactive mode. Either set the EXPO_ASC_API_KEY_PATH/EXPO_ASC_KEY_ID/EXPO_ASC_ISSUER_ID environment variables, or configure an App Store Connect API Key for submissions for bundle identifier ${this.app.bundleIdentifier} on EAS. ${(0, log_1.learnMore)('https://docs.expo.dev/build/building-on-ci/#optional-provide-an-asc-api-token-for-your-apple-team')}`);
30
28
  }
31
29
  const appleAuthCtx = await ctx.appStore.ensureAuthenticatedAsync();
32
30
  const provisioningProfile = await this.provideOrGenerateAsync(ctx, appleAuthCtx);
@@ -11,16 +11,15 @@ const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
11
11
  const AppleTeamUtils_1 = require("./AppleTeamUtils");
12
12
  const BuildCredentialsUtils_1 = require("./BuildCredentialsUtils");
13
13
  const DeviceUtils_1 = require("./DeviceUtils");
14
+ const AscApiKeyUtils_1 = require("./AscApiKeyUtils");
14
15
  const SetUpDistributionCertificate_1 = require("./SetUpDistributionCertificate");
15
16
  const action_1 = tslib_1.__importStar(require("../../../devices/actions/create/action"));
16
17
  const generated_1 = require("../../../graphql/generated");
17
- const AppStoreConnectApiKeyQuery_1 = require("../../../graphql/queries/AppStoreConnectApiKeyQuery");
18
18
  const log_1 = tslib_1.__importDefault(require("../../../log"));
19
19
  const target_1 = require("../../../project/ios/target");
20
20
  const prompts_1 = require("../../../prompts");
21
21
  const differenceBy_1 = tslib_1.__importDefault(require("../../../utils/expodash/differenceBy"));
22
22
  const errors_1 = require("../../errors");
23
- const GraphqlClient_1 = require("../api/GraphqlClient");
24
23
  const authenticateTypes_1 = require("../appstore/authenticateTypes");
25
24
  const constants_1 = require("../appstore/constants");
26
25
  const resolveCredentials_1 = require("../appstore/resolveCredentials");
@@ -263,7 +262,7 @@ class SetUpAdhocProvisioningProfile {
263
262
  await ctx.appStore.ensureAuthenticatedAsync({ mode: authenticateTypes_1.AuthenticationMode.API_KEY });
264
263
  return;
265
264
  }
266
- const resolvedKey = await resolveAscApiKeyForAppCredentialsAsync({
265
+ const resolvedKey = await (0, AscApiKeyUtils_1.resolveAscApiKeyForAppCredentialsAsync)({
267
266
  graphqlClient: ctx.graphqlClient,
268
267
  app,
269
268
  });
@@ -272,6 +271,7 @@ class SetUpAdhocProvisioningProfile {
272
271
  ' - Environment variables: EXPO_ASC_API_KEY_PATH, EXPO_ASC_KEY_ID, EXPO_ASC_ISSUER_ID\n' +
273
272
  ' - EAS credentials service: configure an App Store Connect API Key for submissions on this app');
274
273
  }
274
+ log_1.default.log('Using App Store Connect API Key from EAS credentials service.');
275
275
  await ctx.appStore.ensureAuthenticatedAsync({
276
276
  mode: authenticateTypes_1.AuthenticationMode.API_KEY,
277
277
  ascApiKey: resolvedKey.ascApiKey,
@@ -284,23 +284,6 @@ class SetUpAdhocProvisioningProfile {
284
284
  }
285
285
  }
286
286
  exports.SetUpAdhocProvisioningProfile = SetUpAdhocProvisioningProfile;
287
- async function resolveAscApiKeyForAppCredentialsAsync({ graphqlClient, app, }) {
288
- const ascKeyFragment = await (0, GraphqlClient_1.getAscApiKeyForAppSubmissionsAsync)(graphqlClient, app);
289
- if (!ascKeyFragment) {
290
- return null;
291
- }
292
- log_1.default.log('Using App Store Connect API Key from EAS credentials service.');
293
- const fullKey = await AppStoreConnectApiKeyQuery_1.AppStoreConnectApiKeyQuery.getByIdAsync(graphqlClient, ascKeyFragment.id);
294
- return {
295
- ascApiKey: {
296
- keyP8: fullKey.keyP8,
297
- keyId: fullKey.keyIdentifier,
298
- issuerId: fullKey.issuerIdentifier,
299
- },
300
- teamId: ascKeyFragment.appleTeam?.appleTeamIdentifier,
301
- teamName: ascKeyFragment.appleTeam?.appleTeamName ?? undefined,
302
- };
303
- }
304
287
  function doUDIDsMatch(udidsA, udidsB) {
305
288
  const setA = new Set(udidsA);
306
289
  const setB = new Set(udidsB);
@@ -15,5 +15,15 @@ export declare class SetUpProvisioningProfile {
15
15
  createAndAssignProfileAsync(ctx: CredentialsContext, distCert: AppleDistributionCertificateFragment): Promise<IosAppBuildCredentialsFragment>;
16
16
  configureAndAssignProfileAsync(ctx: CredentialsContext, distCert: AppleDistributionCertificateFragment, originalProvisioningProfile: AppleProvisioningProfileFragment): Promise<IosAppBuildCredentialsFragment | null>;
17
17
  runAsync(ctx: CredentialsContext): Promise<IosAppBuildCredentialsFragment>;
18
+ /**
19
+ * The team type determines `team.inHouse`, which in turn selects the Apple profile
20
+ * type used for every subsequent profile lookup and creation (IOS_APP_INHOUSE for
21
+ * enterprise vs IOS_APP_STORE otherwise). We derive it from the distribution
22
+ * type, which is exactly what the requested operation needs: enterprise
23
+ * builds require an in-house team, other distribution types don't.
24
+ * A genuine team/distribution mismatch is rejected by Apple regardless of this value.
25
+ */
26
+ private getDerivedTeamTypeForAuthentication;
27
+ private resolveTeamTypeForAuthentication;
18
28
  private getCurrentProfileStoreInfo;
19
29
  }
@@ -3,15 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SetUpProvisioningProfile = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
6
+ const AscApiKeyUtils_1 = require("./AscApiKeyUtils");
6
7
  const BuildCredentialsUtils_1 = require("./BuildCredentialsUtils");
7
8
  const ConfigureProvisioningProfile_1 = require("./ConfigureProvisioningProfile");
8
9
  const CreateProvisioningProfile_1 = require("./CreateProvisioningProfile");
9
10
  const ProvisioningProfileUtils_1 = require("./ProvisioningProfileUtils");
10
11
  const SetUpDistributionCertificate_1 = require("./SetUpDistributionCertificate");
11
- const log_1 = require("../../../log");
12
+ const generated_1 = require("../../../graphql/generated");
13
+ const log_1 = tslib_1.__importStar(require("../../../log"));
12
14
  const target_1 = require("../../../project/ios/target");
13
15
  const prompts_1 = require("../../../prompts");
14
16
  const errors_1 = require("../../errors");
17
+ const resolveCredentials_1 = require("../appstore/resolveCredentials");
15
18
  const authenticateTypes_1 = require("../appstore/authenticateTypes");
16
19
  const validateProvisioningProfile_1 = require("../validators/validateProvisioningProfile");
17
20
  /**
@@ -50,16 +53,31 @@ class SetUpProvisioningProfile {
50
53
  }
51
54
  async runAsync(ctx) {
52
55
  const distCert = await new SetUpDistributionCertificate_1.SetUpDistributionCertificate(this.app, this.distributionType).runAsync(ctx);
53
- const areBuildCredentialsSetup = await this.areBuildCredentialsSetupAsync(ctx);
56
+ if (ctx.nonInteractive && !ctx.appStore.authCtx) {
57
+ await (0, AscApiKeyUtils_1.tryAuthenticateAppStoreWithEasAscApiKeyAsync)(ctx, this.app, this.resolveTeamTypeForAuthentication());
58
+ }
59
+ let areBuildCredentialsSetup;
60
+ try {
61
+ areBuildCredentialsSetup = await this.areBuildCredentialsSetupAsync(ctx);
62
+ }
63
+ catch (error) {
64
+ if (ctx.nonInteractive) {
65
+ log_1.default.warn('Skipping Provisioning Profile validation on Apple servers due to an unexpected validation error. Continuing with local validation result.');
66
+ log_1.default.debug('Provisioning profile validation on Apple servers failed:', error);
67
+ areBuildCredentialsSetup = true;
68
+ }
69
+ else {
70
+ throw error;
71
+ }
72
+ }
54
73
  if (areBuildCredentialsSetup) {
55
74
  return (0, nullthrows_1.default)(await (0, BuildCredentialsUtils_1.getBuildCredentialsAsync)(ctx, this.app, this.distributionType));
56
75
  }
57
76
  if (ctx.freezeCredentials) {
58
77
  throw new errors_1.ForbidCredentialModificationError('Provisioning profile is not configured correctly. Remove the --freeze-credentials flag to configure it.');
59
78
  }
60
- else if (ctx.nonInteractive &&
61
- ctx.appStore.defaultAuthenticationMode !== authenticateTypes_1.AuthenticationMode.API_KEY) {
62
- throw new errors_1.InsufficientAuthenticationNonInteractiveError(`In order to configure your Provisioning Profile, authentication with an ASC API key is required in non-interactive mode. ${(0, log_1.learnMore)('https://docs.expo.dev/build/building-on-ci/#optional-provide-an-asc-api-token-for-your-apple-team')}`);
79
+ if (ctx.nonInteractive && !ctx.appStore.authCtx) {
80
+ throw new errors_1.InsufficientAuthenticationNonInteractiveError(`In order to configure your Provisioning Profile, authentication with an ASC API key is required in non-interactive mode. Either set the EXPO_ASC_API_KEY_PATH/EXPO_ASC_KEY_ID/EXPO_ASC_ISSUER_ID environment variables, or configure an App Store Connect API Key for submissions for bundle identifier ${this.app.bundleIdentifier} on EAS. ${(0, log_1.learnMore)('https://docs.expo.dev/build/building-on-ci/#optional-provide-an-asc-api-token-for-your-apple-team')}`);
63
81
  }
64
82
  const currentProfile = await (0, BuildCredentialsUtils_1.getProvisioningProfileAsync)(ctx, this.app, this.distributionType);
65
83
  if (!currentProfile) {
@@ -93,6 +111,22 @@ class SetUpProvisioningProfile {
93
111
  }
94
112
  return updatedProfile;
95
113
  }
114
+ /**
115
+ * The team type determines `team.inHouse`, which in turn selects the Apple profile
116
+ * type used for every subsequent profile lookup and creation (IOS_APP_INHOUSE for
117
+ * enterprise vs IOS_APP_STORE otherwise). We derive it from the distribution
118
+ * type, which is exactly what the requested operation needs: enterprise
119
+ * builds require an in-house team, other distribution types don't.
120
+ * A genuine team/distribution mismatch is rejected by Apple regardless of this value.
121
+ */
122
+ getDerivedTeamTypeForAuthentication() {
123
+ return this.distributionType === generated_1.IosDistributionType.Enterprise
124
+ ? authenticateTypes_1.AppleTeamType.IN_HOUSE
125
+ : authenticateTypes_1.AppleTeamType.COMPANY_OR_ORGANIZATION;
126
+ }
127
+ resolveTeamTypeForAuthentication() {
128
+ return (0, resolveCredentials_1.resolveAppleTeamTypeFromEnvironment)() ?? this.getDerivedTeamTypeForAuthentication();
129
+ }
96
130
  getCurrentProfileStoreInfo(profiles, currentProfile) {
97
131
  return (profiles.find(profile => currentProfile.developerPortalIdentifier
98
132
  ? currentProfile.developerPortalIdentifier === profile.provisioningProfileId
@@ -10,6 +10,7 @@ import { MinimalAscApiKey } from '../credentials';
10
10
  export declare function resolveUserCredentialsAsync(options: Partial<Auth.UserCredentials>): Promise<Partial<Auth.UserCredentials>>;
11
11
  export declare function hasAscEnvVars(): boolean;
12
12
  export declare function resolveAscApiKeyAsync(ascApiKey?: MinimalAscApiKey): Promise<MinimalAscApiKey>;
13
+ export declare function resolveAppleTeamTypeFromEnvironment(): AppleTeamType | undefined;
13
14
  export declare function resolveAppleTeamAsync(options?: {
14
15
  teamId?: string;
15
16
  teamName?: string;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveUserCredentialsAsync = resolveUserCredentialsAsync;
4
4
  exports.hasAscEnvVars = hasAscEnvVars;
5
5
  exports.resolveAscApiKeyAsync = resolveAscApiKeyAsync;
6
+ exports.resolveAppleTeamTypeFromEnvironment = resolveAppleTeamTypeFromEnvironment;
6
7
  exports.resolveAppleTeamAsync = resolveAppleTeamAsync;
7
8
  exports.promptPasswordAsync = promptPasswordAsync;
8
9
  exports.deletePasswordAsync = deletePasswordAsync;