eas-cli 19.0.8 → 20.0.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 (48) hide show
  1. package/README.md +305 -101
  2. package/build/build/createContext.d.ts +2 -1
  3. package/build/build/createContext.js +3 -1
  4. package/build/build/ios/credentials.js +5 -1
  5. package/build/build/ios/prepareJob.js +3 -0
  6. package/build/build/runBuildAndSubmit.d.ts +1 -0
  7. package/build/build/runBuildAndSubmit.js +1 -0
  8. package/build/build/types.d.ts +1 -0
  9. package/build/commands/build/index.d.ts +1 -0
  10. package/build/commands/build/index.js +13 -0
  11. package/build/commands/build/internal.d.ts +1 -0
  12. package/build/commands/build/internal.js +5 -0
  13. package/build/commands/go.d.ts +1 -0
  14. package/build/commands/go.js +29 -1
  15. package/build/commands/observe/events.d.ts +3 -3
  16. package/build/commands/observe/events.js +69 -51
  17. package/build/commands/observe/{logs.d.ts → metrics-summary.d.ts} +3 -11
  18. package/build/commands/observe/metrics-summary.js +95 -0
  19. package/build/commands/observe/metrics.d.ts +8 -3
  20. package/build/commands/observe/metrics.js +63 -47
  21. package/build/commands/observe/routes.d.ts +0 -1
  22. package/build/commands/observe/routes.js +0 -4
  23. package/build/commands/observe/versions.d.ts +0 -1
  24. package/build/commands/observe/versions.js +0 -4
  25. package/build/commands/update/embedded/upload.d.ts +20 -0
  26. package/build/commands/update/embedded/upload.js +129 -0
  27. package/build/credentials/context.d.ts +2 -0
  28. package/build/credentials/context.js +2 -0
  29. package/build/credentials/ios/actions/DeviceUtils.d.ts +2 -0
  30. package/build/credentials/ios/actions/DeviceUtils.js +21 -0
  31. package/build/credentials/ios/actions/SetUpAdhocProvisioningProfile.d.ts +1 -0
  32. package/build/credentials/ios/actions/SetUpAdhocProvisioningProfile.js +76 -14
  33. package/build/credentials/ios/actions/SetUpTargetBuildCredentials.js +8 -0
  34. package/build/graphql/client.js +1 -0
  35. package/build/graphql/generated.d.ts +70 -1
  36. package/build/graphql/mutations/EmbeddedUpdateAssetMutation.d.ts +13 -0
  37. package/build/graphql/mutations/EmbeddedUpdateAssetMutation.js +32 -0
  38. package/build/graphql/mutations/EmbeddedUpdateMutation.d.ts +14 -0
  39. package/build/graphql/mutations/EmbeddedUpdateMutation.js +37 -0
  40. package/build/graphql/queries/WorkflowRunQuery.d.ts +2 -1
  41. package/build/graphql/queries/WorkflowRunQuery.js +19 -6
  42. package/build/observe/formatCustomEvents.js +5 -5
  43. package/build/observe/metricNames.js +2 -0
  44. package/build/update/embeddedManifest.d.ts +3 -0
  45. package/build/update/embeddedManifest.js +28 -0
  46. package/oclif.manifest.json +1852 -1714
  47. package/package.json +6 -6
  48. package/build/commands/observe/logs.js +0 -141
@@ -3,50 +3,45 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const core_1 = require("@oclif/core");
5
5
  const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
6
+ const errors_1 = require("../../commandUtils/errors");
6
7
  const flags_1 = require("../../commandUtils/flags");
8
+ const pagination_1 = require("../../commandUtils/pagination");
7
9
  const log_1 = tslib_1.__importDefault(require("../../log"));
8
- const fetchMetrics_1 = require("../../observe/fetchMetrics");
10
+ const fetchEvents_1 = require("../../observe/fetchEvents");
9
11
  const flags_2 = require("../../observe/flags");
10
- const formatMetrics_1 = require("../../observe/formatMetrics");
11
12
  const metricNames_1 = require("../../observe/metricNames");
13
+ const formatEvents_1 = require("../../observe/formatEvents");
12
14
  const platforms_1 = require("../../observe/platforms");
13
15
  const resolveProjectContext_1 = require("../../observe/resolveProjectContext");
14
16
  const startAndEndTime_1 = require("../../observe/startAndEndTime");
17
+ const prompts_1 = require("../../prompts");
15
18
  const json_1 = require("../../utils/json");
16
- const DEFAULT_METRICS = [
17
- 'expo.app_startup.cold_launch_time',
18
- 'expo.app_startup.warm_launch_time',
19
- 'expo.app_startup.tti',
20
- 'expo.app_startup.ttr',
21
- 'expo.app_startup.bundle_load_time',
22
- ];
23
- const DEFAULT_STATS_TABLE = ['median', 'eventCount'];
24
- const DEFAULT_STATS_JSON = [
25
- 'min',
26
- 'median',
27
- 'max',
28
- 'average',
29
- 'p80',
30
- 'p90',
31
- 'p99',
32
- 'eventCount',
33
- ];
19
+ const DEFAULT_EVENTS_LIMIT = 10;
34
20
  class ObserveMetrics extends EasCommand_1.default {
35
- static hidden = true;
36
- static description = 'display app performance metrics grouped by app version';
37
- static flags = {
38
- ...flags_2.ObservePlatformFlag,
39
- metric: core_1.Flags.option({
40
- description: 'Metric name to display (can be specified multiple times).',
41
- multiple: true,
21
+ static description = 'display individual performance metric samples ordered by value';
22
+ static args = {
23
+ metric: core_1.Args.string({
24
+ description: 'Metric to query (e.g. tti, cold_launch)',
25
+ required: false,
42
26
  options: Object.keys(metricNames_1.METRIC_ALIASES),
27
+ }),
28
+ };
29
+ static flags = {
30
+ sort: core_1.Flags.option({
31
+ description: 'Sort order for events',
32
+ options: Object.values(fetchEvents_1.EventsOrderPreset).map(s => s.toLowerCase()),
33
+ required: false,
34
+ default: fetchEvents_1.EventsOrderPreset.Oldest.valueOf().toLowerCase(),
43
35
  })(),
44
- stat: core_1.Flags.option({
45
- description: 'Statistic to display per metric (can be specified multiple times)',
46
- multiple: true,
47
- options: DEFAULT_STATS_JSON,
48
- })(),
36
+ ...flags_2.ObservePlatformFlag,
37
+ ...flags_2.ObserveAfterFlag,
38
+ limit: (0, pagination_1.getLimitFlagWithCustomValues)({
39
+ defaultTo: DEFAULT_EVENTS_LIMIT,
40
+ limit: 100,
41
+ }),
49
42
  ...flags_2.ObserveTimeRangeFlags,
43
+ ...flags_2.ObserveAppVersionFlag,
44
+ ...flags_2.ObserveUpdateIdFlag,
50
45
  ...flags_2.ObserveProjectIdFlag,
51
46
  ...flags_1.EasNonInteractiveAndJsonFlags,
52
47
  };
@@ -58,7 +53,7 @@ class ObserveMetrics extends EasCommand_1.default {
58
53
  ...this.ContextOptions.LoggedIn,
59
54
  };
60
55
  async runAsync() {
61
- const { flags } = await this.parse(ObserveMetrics);
56
+ const { flags, args } = await this.parse(ObserveMetrics);
62
57
  const { projectId, graphqlClient } = await (0, resolveProjectContext_1.resolveObserveCommandContextAsync)({
63
58
  command: this,
64
59
  commandClass: ObserveMetrics,
@@ -69,29 +64,50 @@ class ObserveMetrics extends EasCommand_1.default {
69
64
  if (flags.json) {
70
65
  (0, json_1.enableJsonOutput)();
71
66
  }
67
+ let metricName;
68
+ if (args.metric) {
69
+ metricName = (0, metricNames_1.resolveMetricName)(args.metric);
70
+ }
71
+ else if (flags['non-interactive']) {
72
+ throw new errors_1.EasCommandError('A metric argument is required in non-interactive mode. Available metrics: ' +
73
+ Object.keys(metricNames_1.METRIC_ALIASES).join(', '));
74
+ }
72
75
  else {
73
- log_1.default.warn('EAS Observe is in preview and subject to breaking changes.');
76
+ const choices = Object.entries(metricNames_1.METRIC_SHORT_NAMES).map(([fullName, displayName]) => ({
77
+ title: `${displayName} (${fullName})`,
78
+ value: fullName,
79
+ }));
80
+ metricName = await (0, prompts_1.selectAsync)('Select a metric', choices);
74
81
  }
75
- const metricNames = flags.metric?.length
76
- ? flags.metric.map(metricNames_1.resolveMetricName)
77
- : DEFAULT_METRICS;
82
+ const orderBy = (0, fetchEvents_1.resolveOrderBy)(flags.sort);
78
83
  const { daysBack, startTime, endTime } = (0, startAndEndTime_1.resolveTimeRange)(flags);
84
+ const platform = (0, platforms_1.appObservePlatformFromFlag)(flags.platform);
79
85
  const platforms = (0, platforms_1.appPlatformsFromFlag)(flags.platform);
80
- const { metricsMap, buildNumbersMap, updateIdsMap, totalEventCounts } = await (0, fetchMetrics_1.fetchObserveMetricsAsync)(graphqlClient, projectId, metricNames, platforms, startTime, endTime);
81
- const argumentsStat = flags.stat?.length
82
- ? Array.from(new Set(flags.stat.map(formatMetrics_1.resolveStatKey)))
83
- : undefined;
86
+ const [{ events, pageInfo }, totalEventCount] = await Promise.all([
87
+ (0, fetchEvents_1.fetchObserveEventsAsync)(graphqlClient, projectId, {
88
+ metricName,
89
+ orderBy,
90
+ limit: flags.limit ?? DEFAULT_EVENTS_LIMIT,
91
+ ...(flags.after && { after: flags.after }),
92
+ startTime,
93
+ endTime,
94
+ platform,
95
+ appVersion: flags['app-version'],
96
+ updateId: flags['update-id'],
97
+ }),
98
+ (0, fetchEvents_1.fetchTotalEventCountAsync)(graphqlClient, projectId, metricName, platforms, startTime, endTime),
99
+ ]);
84
100
  if (flags.json) {
85
- const stats = argumentsStat ?? DEFAULT_STATS_JSON;
86
- (0, json_1.printJsonOnlyOutput)((0, formatMetrics_1.buildObserveMetricsJson)(metricsMap, metricNames, stats, totalEventCounts, buildNumbersMap, updateIdsMap));
101
+ (0, json_1.printJsonOnlyOutput)((0, formatEvents_1.buildObserveEventsJson)(events, pageInfo));
87
102
  }
88
103
  else {
89
- const stats = argumentsStat ?? DEFAULT_STATS_TABLE;
90
104
  log_1.default.addNewLineIfNone();
91
- log_1.default.log((0, formatMetrics_1.buildObserveMetricsTable)(metricsMap, metricNames, stats, {
105
+ log_1.default.log((0, formatEvents_1.buildObserveEventsTable)(events, pageInfo, {
106
+ metricName,
92
107
  daysBack,
93
- buildNumbersMap,
94
- totalEventCounts,
108
+ startTime,
109
+ endTime,
110
+ totalEventCount,
95
111
  }));
96
112
  }
97
113
  }
@@ -1,6 +1,5 @@
1
1
  import EasCommand from '../../commandUtils/EasCommand';
2
2
  export default class ObserveRoutes extends EasCommand {
3
- static hidden: boolean;
4
3
  static description: string;
5
4
  static flags: {
6
5
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
@@ -19,7 +19,6 @@ const STAT_OPTIONS = ['median', 'med', 'p90', 'count', 'event_count', 'eventCoun
19
19
  const DEFAULT_STATS_TABLE = ['median', 'count'];
20
20
  const DEFAULT_STATS_JSON = ['median', 'p90', 'count'];
21
21
  class ObserveRoutes extends EasCommand_1.default {
22
- static hidden = true;
23
22
  static description = 'display app navigation route metrics (Cold TTR, Warm TTR, TTI) grouped by route name';
24
23
  static flags = {
25
24
  ...flags_2.ObservePlatformFlag,
@@ -70,9 +69,6 @@ class ObserveRoutes extends EasCommand_1.default {
70
69
  if (flags.json) {
71
70
  (0, json_1.enableJsonOutput)();
72
71
  }
73
- else {
74
- log_1.default.warn('EAS Observe is in preview and subject to breaking changes.');
75
- }
76
72
  const metricNames = flags.metric?.length
77
73
  ? Array.from(new Set(flags.metric.map(metricNames_1.resolveNavigationMetricName)))
78
74
  : formatNavigationRoutes_1.NAVIGATION_METRIC_NAMES;
@@ -1,6 +1,5 @@
1
1
  import EasCommand from '../../commandUtils/EasCommand';
2
2
  export default class ObserveVersions extends EasCommand {
3
- static hidden: boolean;
4
3
  static description: string;
5
4
  static flags: {
6
5
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
@@ -12,7 +12,6 @@ const resolveProjectContext_1 = require("../../observe/resolveProjectContext");
12
12
  const startAndEndTime_1 = require("../../observe/startAndEndTime");
13
13
  const json_1 = require("../../utils/json");
14
14
  class ObserveVersions extends EasCommand_1.default {
15
- static hidden = true;
16
15
  static description = 'display app versions with build and update details';
17
16
  static flags = {
18
17
  ...flags_2.ObservePlatformFlag,
@@ -39,9 +38,6 @@ class ObserveVersions extends EasCommand_1.default {
39
38
  if (flags.json) {
40
39
  (0, json_1.enableJsonOutput)();
41
40
  }
42
- else {
43
- log_1.default.warn('EAS Observe is in preview and subject to breaking changes.');
44
- }
45
41
  const { startTime, endTime } = (0, startAndEndTime_1.resolveTimeRange)(flags);
46
42
  const platforms = (0, platforms_1.appPlatformsFromFlag)(flags.platform);
47
43
  const results = await (0, fetchVersions_1.fetchObserveVersionsAsync)(graphqlClient, projectId, platforms, startTime, endTime);
@@ -0,0 +1,20 @@
1
+ import { Platform } from '@expo/eas-build-job';
2
+ import EasCommand from '../../../commandUtils/EasCommand';
3
+ export default class UpdateEmbeddedUpload extends EasCommand {
4
+ static description: string;
5
+ static examples: string[];
6
+ static flags: {
7
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
+ 'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ platform: import("@oclif/core/lib/interfaces").OptionFlag<Platform, import("@oclif/core/lib/interfaces").CustomOptions>;
10
+ bundle: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ manifest: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
12
+ channel: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
13
+ 'build-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
14
+ };
15
+ static contextDefinition: {
16
+ loggedIn: import("../../../commandUtils/context/LoggedInContextField").default;
17
+ privateProjectConfig: import("../../../commandUtils/context/PrivateProjectConfigContextField").PrivateProjectConfigContextField;
18
+ };
19
+ runAsync(): Promise<void>;
20
+ }
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const eas_build_job_1 = require("@expo/eas-build-job");
5
+ const config_plugins_1 = require("@expo/config-plugins");
6
+ const core_1 = require("@oclif/core");
7
+ const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
8
+ const EasCommand_1 = tslib_1.__importDefault(require("../../../commandUtils/EasCommand"));
9
+ const flags_1 = require("../../../commandUtils/flags");
10
+ const EmbeddedUpdateAssetMutation_1 = require("../../../graphql/mutations/EmbeddedUpdateAssetMutation");
11
+ const EmbeddedUpdateMutation_1 = require("../../../graphql/mutations/EmbeddedUpdateMutation");
12
+ const AppPlatform_1 = require("../../../graphql/types/AppPlatform");
13
+ const log_1 = tslib_1.__importDefault(require("../../../log"));
14
+ const ora_1 = require("../../../ora");
15
+ const embeddedManifest_1 = require("../../../update/embeddedManifest");
16
+ const uploads_1 = require("../../../uploads");
17
+ const json_1 = require("../../../utils/json");
18
+ const promise_1 = require("../../../utils/promise");
19
+ const MAX_ATTEMPTS = 10;
20
+ const RETRY_BASE_DELAY_MS = 3_000;
21
+ const RETRY_MAX_DELAY_MS = 10_000;
22
+ class UpdateEmbeddedUpload extends EasCommand_1.default {
23
+ static description = 'upload the JS bundle embedded in a native build so EAS Update can generate bsdiff patches against it';
24
+ static examples = [
25
+ '$ eas update:embedded:upload --platform ios --bundle ios/build/App.app/main.jsbundle --manifest ios/build/App.app/app.manifest --channel production',
26
+ '$ eas update:embedded:upload --platform android --bundle android/app/src/main/assets/index.android.bundle --manifest android/app/src/main/assets/app.manifest --channel production --build-id <BUILD-ID>',
27
+ ];
28
+ static flags = {
29
+ platform: core_1.Flags.option({
30
+ char: 'p',
31
+ description: 'Platform of the embedded bundle',
32
+ options: [eas_build_job_1.Platform.IOS, eas_build_job_1.Platform.ANDROID],
33
+ required: true,
34
+ })(),
35
+ bundle: core_1.Flags.string({
36
+ description: 'Path to the embedded JS bundle file',
37
+ required: true,
38
+ }),
39
+ manifest: core_1.Flags.string({
40
+ description: 'Path to the app.manifest file embedded in the build',
41
+ required: true,
42
+ }),
43
+ channel: core_1.Flags.string({
44
+ description: 'Channel name the embedded update should be associated with',
45
+ required: true,
46
+ }),
47
+ 'build-id': core_1.Flags.string({
48
+ description: 'EAS Build ID that produced this binary (required when invoked from EAS Build)',
49
+ required: false,
50
+ }),
51
+ ...flags_1.EasNonInteractiveAndJsonFlags,
52
+ };
53
+ static contextDefinition = {
54
+ ...this.ContextOptions.ProjectConfig,
55
+ };
56
+ async runAsync() {
57
+ const { flags } = await this.parse(UpdateEmbeddedUpload);
58
+ const { json: jsonFlag, nonInteractive } = (0, flags_1.resolveNonInteractiveAndJsonFlags)(flags);
59
+ const platform = flags.platform;
60
+ const bundlePath = flags.bundle;
61
+ const manifestPath = flags.manifest;
62
+ const channelName = flags.channel;
63
+ const buildId = flags['build-id'];
64
+ const { loggedIn: { graphqlClient }, privateProjectConfig: { projectId, exp, projectDir }, } = await this.getContextAsync(UpdateEmbeddedUpload, {
65
+ nonInteractive,
66
+ withServerSideEnvironment: null,
67
+ });
68
+ if (jsonFlag) {
69
+ (0, json_1.enableJsonOutput)();
70
+ }
71
+ if (!(await fs_extra_1.default.pathExists(bundlePath))) {
72
+ core_1.Errors.error(`Bundle file not found at "${bundlePath}". Check that the path is correct and points to the JS bundle in your native build output.`, { exit: 1 });
73
+ }
74
+ const { id: embeddedUpdateId } = await (0, embeddedManifest_1.readEmbeddedManifestAsync)(manifestPath);
75
+ const runtimeVersion = await config_plugins_1.Updates.getRuntimeVersionNullableAsync(projectDir, exp, platform);
76
+ if (runtimeVersion === null) {
77
+ core_1.Errors.error(`Could not resolve runtimeVersion for platform "${platform}". ` +
78
+ `Ensure runtimeVersion is set in your app.json under the expo key.`, { exit: 1 });
79
+ }
80
+ const appPlatform = (0, AppPlatform_1.toAppPlatform)(platform);
81
+ const uploadSpinner = (0, ora_1.ora)('Uploading bundle...').start();
82
+ const contentType = 'application/javascript';
83
+ const uploadSpec = await EmbeddedUpdateAssetMutation_1.EmbeddedUpdateAssetMutation.getSignedUploadSpecAsync(graphqlClient, {
84
+ appId: projectId,
85
+ embeddedUpdateId,
86
+ contentType,
87
+ });
88
+ await (0, uploads_1.uploadWithPresignedPostWithRetryAsync)(bundlePath, { url: uploadSpec.presignedUrl, fields: uploadSpec.fields }, () => { });
89
+ uploadSpinner.succeed('Uploaded bundle');
90
+ const registerSpinner = (0, ora_1.ora)('Registering embedded update...').start();
91
+ let embeddedUpdate;
92
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
93
+ try {
94
+ embeddedUpdate = await EmbeddedUpdateMutation_1.EmbeddedUpdateMutation.uploadEmbeddedUpdateAsync(graphqlClient, {
95
+ appId: projectId,
96
+ platform: appPlatform,
97
+ runtimeVersion,
98
+ channel: channelName,
99
+ embeddedUpdateId,
100
+ turtleBuildId: buildId,
101
+ });
102
+ break;
103
+ }
104
+ catch (e) {
105
+ if ((0, EmbeddedUpdateMutation_1.isEmbeddedUpdateAssetNotAvailableError)(e)) {
106
+ if (attempt < MAX_ATTEMPTS) {
107
+ await (0, promise_1.sleepAsync)(Math.min(RETRY_BASE_DELAY_MS * 2 ** (attempt - 1), RETRY_MAX_DELAY_MS));
108
+ }
109
+ continue;
110
+ }
111
+ registerSpinner.fail('Failed to register embedded update');
112
+ if ((0, EmbeddedUpdateMutation_1.isEmbeddedUpdateAlreadyExistsError)(e)) {
113
+ core_1.Errors.error(`An embedded update with id "${embeddedUpdateId}" is already registered for this app. Delete it before re-uploading.`, { exit: 1 });
114
+ }
115
+ throw e;
116
+ }
117
+ }
118
+ if (embeddedUpdate === undefined) {
119
+ registerSpinner.fail('Failed to register embedded update');
120
+ throw new Error('Embedded bundle could not be processed in time. Try re-running the command in a moment.');
121
+ }
122
+ registerSpinner.succeed(`Registered ${platform} embedded update (runtimeVersion: ${runtimeVersion}, channel: "${channelName}")`);
123
+ log_1.default.log(`Embedded update ID: ${embeddedUpdate.id}`);
124
+ if (jsonFlag) {
125
+ (0, json_1.printJsonOnlyOutput)(embeddedUpdate);
126
+ }
127
+ }
128
+ }
129
+ exports.default = UpdateEmbeddedUpload;
@@ -20,6 +20,7 @@ export declare class CredentialsContext {
20
20
  readonly nonInteractive: boolean;
21
21
  readonly autoAcceptCredentialReuse: boolean;
22
22
  readonly freezeCredentials: boolean;
23
+ readonly refreshAdHocProvisioningProfile: boolean;
23
24
  readonly projectDir: string;
24
25
  readonly user: Actor;
25
26
  readonly graphqlClient: ExpoGraphqlClient;
@@ -40,6 +41,7 @@ export declare class CredentialsContext {
40
41
  vcsClient: Client;
41
42
  freezeCredentials?: boolean;
42
43
  autoAcceptCredentialReuse?: boolean;
44
+ refreshAdHocProvisioningProfile?: boolean;
43
45
  env?: Env;
44
46
  });
45
47
  get hasProjectContext(): boolean;
@@ -18,6 +18,7 @@ class CredentialsContext {
18
18
  nonInteractive;
19
19
  autoAcceptCredentialReuse;
20
20
  freezeCredentials = false;
21
+ refreshAdHocProvisioningProfile = false;
21
22
  projectDir;
22
23
  user;
23
24
  graphqlClient;
@@ -39,6 +40,7 @@ class CredentialsContext {
39
40
  this.autoAcceptCredentialReuse = options.autoAcceptCredentialReuse ?? false;
40
41
  this.projectInfo = options.projectInfo;
41
42
  this.freezeCredentials = options.freezeCredentials ?? false;
43
+ this.refreshAdHocProvisioningProfile = options.refreshAdHocProvisioningProfile ?? false;
42
44
  this.usesBroadcastPushNotifications =
43
45
  options.projectInfo?.exp.ios?.usesBroadcastPushNotifications ?? false;
44
46
  }
@@ -1,3 +1,5 @@
1
1
  import { AppleDevice, AppleDeviceFragment } from '../../../graphql/generated';
2
+ import { ApplePlatform } from '../appstore/constants';
3
+ export declare function filterDevicesForApplePlatform(devices: AppleDeviceFragment[], applePlatform: ApplePlatform): AppleDeviceFragment[];
2
4
  export declare function chooseDevicesAsync(allDevices: AppleDeviceFragment[], preselectedDeviceIdentifiers?: string[]): Promise<AppleDevice[]>;
3
5
  export declare function formatDeviceLabel(device: AppleDeviceFragment): string;
@@ -1,9 +1,30 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.filterDevicesForApplePlatform = filterDevicesForApplePlatform;
3
4
  exports.chooseDevicesAsync = chooseDevicesAsync;
4
5
  exports.formatDeviceLabel = formatDeviceLabel;
5
6
  const prompts_1 = require("../.././../prompts");
7
+ const generated_1 = require("../../../graphql/generated");
6
8
  const AppleDevice_1 = require("../../../graphql/types/credentials/AppleDevice");
9
+ const constants_1 = require("../appstore/constants");
10
+ function filterDevicesForApplePlatform(devices, applePlatform) {
11
+ if (applePlatform === constants_1.ApplePlatform.TV_OS) {
12
+ throw new Error('Filtering for tvOS is not supported yet');
13
+ }
14
+ if (applePlatform === constants_1.ApplePlatform.VISION_OS) {
15
+ throw new Error('Filtering for visionOS is not supported yet');
16
+ }
17
+ return devices.filter(device => isDeviceCompatibleWithApplePlatform(device, applePlatform));
18
+ }
19
+ function isDeviceCompatibleWithApplePlatform(device, applePlatform) {
20
+ switch (applePlatform) {
21
+ case constants_1.ApplePlatform.IOS:
22
+ return (device.deviceClass === generated_1.AppleDeviceClass.Iphone ||
23
+ device.deviceClass === generated_1.AppleDeviceClass.Ipad);
24
+ case constants_1.ApplePlatform.MAC_OS:
25
+ return device.deviceClass === generated_1.AppleDeviceClass.Mac;
26
+ }
27
+ }
7
28
  async function chooseDevicesAsync(allDevices, preselectedDeviceIdentifiers = []) {
8
29
  const preselectedDeviceIdentifierSet = new Set(preselectedDeviceIdentifiers);
9
30
  const isSelected = (device) => preselectedDeviceIdentifierSet.size === 0 ||
@@ -16,6 +16,7 @@ export declare class SetUpAdhocProvisioningProfile {
16
16
  private shouldUseExistingProfileAsync;
17
17
  private promptForReuseActionAsync;
18
18
  private registerDevicesAsync;
19
+ private ensureAppStoreAuthenticatedForAdhocRefreshAsync;
19
20
  }
20
21
  export declare function doUDIDsMatch(udidsA: string[], udidsB: string[]): boolean;
21
22
  export {};
@@ -14,12 +14,16 @@ const DeviceUtils_1 = require("./DeviceUtils");
14
14
  const SetUpDistributionCertificate_1 = require("./SetUpDistributionCertificate");
15
15
  const action_1 = tslib_1.__importStar(require("../../../devices/actions/create/action"));
16
16
  const generated_1 = require("../../../graphql/generated");
17
+ const AppStoreConnectApiKeyQuery_1 = require("../../../graphql/queries/AppStoreConnectApiKeyQuery");
17
18
  const log_1 = tslib_1.__importDefault(require("../../../log"));
18
19
  const target_1 = require("../../../project/ios/target");
19
20
  const prompts_1 = require("../../../prompts");
20
21
  const differenceBy_1 = tslib_1.__importDefault(require("../../../utils/expodash/differenceBy"));
21
22
  const errors_1 = require("../../errors");
23
+ const GraphqlClient_1 = require("../api/GraphqlClient");
24
+ const authenticateTypes_1 = require("../appstore/authenticateTypes");
22
25
  const constants_1 = require("../appstore/constants");
26
+ const resolveCredentials_1 = require("../appstore/resolveCredentials");
23
27
  const validateProvisioningProfile_1 = require("../validators/validateProvisioningProfile");
24
28
  var ReuseAction;
25
29
  (function (ReuseAction) {
@@ -34,7 +38,14 @@ class SetUpAdhocProvisioningProfile {
34
38
  }
35
39
  async runAsync(ctx) {
36
40
  const { app } = this.options;
41
+ if (ctx.refreshAdHocProvisioningProfile && ctx.freezeCredentials) {
42
+ throw new Error('Cannot refresh ad-hoc provisioning profile when credentials are frozen. Remove --freeze-credentials or --refresh-ad-hoc-provisioning-profile.');
43
+ }
37
44
  const distCert = await new SetUpDistributionCertificate_1.SetUpDistributionCertificate(app, generated_1.IosDistributionType.AdHoc).runAsync(ctx);
45
+ if (ctx.nonInteractive && ctx.refreshAdHocProvisioningProfile) {
46
+ await this.ensureAppStoreAuthenticatedForAdhocRefreshAsync(ctx, app);
47
+ return await this.runWithDistributionCertificateAsync(ctx, distCert);
48
+ }
38
49
  const areBuildCredentialsSetup = await this.areBuildCredentialsSetupAsync(ctx);
39
50
  if (ctx.nonInteractive) {
40
51
  if (areBuildCredentialsSetup) {
@@ -66,6 +77,9 @@ class SetUpAdhocProvisioningProfile {
66
77
  // 2. Fetch devices registered on EAS servers
67
78
  let registeredAppleDevices = await ctx.ios.getDevicesForAppleTeamAsync(ctx.graphqlClient, app, appleTeam);
68
79
  if (registeredAppleDevices.length === 0) {
80
+ if (ctx.nonInteractive) {
81
+ throw new Error('No devices are registered for this Apple team. Register devices with eas device:create first.');
82
+ }
69
83
  const shouldRegisterDevices = await (0, prompts_1.confirmAsync)({
70
84
  message: `You don't have any registered devices yet. Would you like to register them now?`,
71
85
  initial: true,
@@ -79,9 +93,11 @@ class SetUpAdhocProvisioningProfile {
79
93
  }
80
94
  // 3. Choose devices for internal distribution
81
95
  const provisionedDeviceIdentifiers = (currentBuildCredentials?.provisioningProfile?.appleDevices ?? []).map(i => i.identifier);
82
- const chosenDevices = await (0, DeviceUtils_1.chooseDevicesAsync)(registeredAppleDevices, provisionedDeviceIdentifiers);
83
- // 4. Reuse or create the profile on Apple Developer Portal
84
96
  const applePlatform = (0, target_1.getApplePlatformFromTarget)(target);
97
+ const chosenDevices = ctx.nonInteractive && ctx.refreshAdHocProvisioningProfile
98
+ ? (0, DeviceUtils_1.filterDevicesForApplePlatform)(registeredAppleDevices, applePlatform)
99
+ : await (0, DeviceUtils_1.chooseDevicesAsync)(registeredAppleDevices, provisionedDeviceIdentifiers);
100
+ // 4. Reuse or create the profile on Apple Developer Portal
85
101
  const profileType = applePlatform === constants_1.ApplePlatform.TV_OS
86
102
  ? apple_utils_1.ProfileType.TVOS_APP_ADHOC
87
103
  : apple_utils_1.ProfileType.IOS_APP_ADHOC;
@@ -106,18 +122,23 @@ class SetUpAdhocProvisioningProfile {
106
122
  log_1.default.warn(`- ${(0, DeviceUtils_1.formatDeviceLabel)(missingDevice)}`);
107
123
  }
108
124
  log_1.default.log('Most commonly devices fail to to be provisioned while they are still being processed by Apple, which can take up to 24-72 hours. Check your Apple Developer Portal page at https://developer.apple.com/account/resources/devices/list, the devices in "Processing" status cannot be provisioned yet');
109
- const shouldContinue = await (0, prompts_1.selectAsync)('Do you want to continue without provisioning these devices?', [
110
- {
111
- title: 'Yes',
112
- value: true,
113
- },
114
- {
115
- title: 'No (EAS CLI will exit)',
116
- value: false,
117
- },
118
- ]);
119
- if (!shouldContinue) {
120
- core_1.Errors.exit(1);
125
+ if (ctx.nonInteractive && ctx.refreshAdHocProvisioningProfile) {
126
+ // Continue without prompting in non-interactive refresh mode.
127
+ }
128
+ else {
129
+ const shouldContinue = await (0, prompts_1.selectAsync)('Do you want to continue without provisioning these devices?', [
130
+ {
131
+ title: 'Yes',
132
+ value: true,
133
+ },
134
+ {
135
+ title: 'No (EAS CLI will exit)',
136
+ value: false,
137
+ },
138
+ ]);
139
+ if (!shouldContinue) {
140
+ core_1.Errors.exit(1);
141
+ }
121
142
  }
122
143
  }
123
144
  // 7. Create (or update) app build credentials
@@ -237,8 +258,49 @@ class SetUpAdhocProvisioningProfile {
237
258
  }
238
259
  }
239
260
  }
261
+ async ensureAppStoreAuthenticatedForAdhocRefreshAsync(ctx, app) {
262
+ if ((0, resolveCredentials_1.hasAscEnvVars)()) {
263
+ await ctx.appStore.ensureAuthenticatedAsync({ mode: authenticateTypes_1.AuthenticationMode.API_KEY });
264
+ return;
265
+ }
266
+ const resolvedKey = await resolveAscApiKeyForAppCredentialsAsync({
267
+ graphqlClient: ctx.graphqlClient,
268
+ app,
269
+ });
270
+ if (!resolvedKey) {
271
+ throw new Error('No App Store Connect API Key found for ad-hoc provisioning profile refresh. In non-interactive mode, provide one via:\n' +
272
+ ' - Environment variables: EXPO_ASC_API_KEY_PATH, EXPO_ASC_KEY_ID, EXPO_ASC_ISSUER_ID\n' +
273
+ ' - EAS credentials service: configure an App Store Connect API Key for submissions on this app');
274
+ }
275
+ await ctx.appStore.ensureAuthenticatedAsync({
276
+ mode: authenticateTypes_1.AuthenticationMode.API_KEY,
277
+ ascApiKey: resolvedKey.ascApiKey,
278
+ teamId: resolvedKey.teamId,
279
+ teamName: resolvedKey.teamName,
280
+ // Provide a non-enterprise team type to avoid interactive team-type resolution.
281
+ // Ad-hoc profile handling below uses explicit ProfileType and does not branch on team.inHouse.
282
+ teamType: authenticateTypes_1.AppleTeamType.COMPANY_OR_ORGANIZATION,
283
+ });
284
+ }
240
285
  }
241
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
+ }
242
304
  function doUDIDsMatch(udidsA, udidsB) {
243
305
  const setA = new Set(udidsA);
244
306
  const setB = new Set(udidsB);
@@ -36,6 +36,14 @@ class SetUpTargetBuildCredentials {
36
36
  }
37
37
  async setupBuildCredentialsAsync(ctx) {
38
38
  const { app, distribution, enterpriseProvisioning, target } = this.options;
39
+ if (ctx.refreshAdHocProvisioningProfile) {
40
+ if (distribution !== 'internal') {
41
+ throw new Error('--refresh-ad-hoc-provisioning-profile is only supported for internal distribution builds.');
42
+ }
43
+ if (enterpriseProvisioning === 'universal') {
44
+ throw new Error('--refresh-ad-hoc-provisioning-profile is only supported for ad-hoc internal builds.');
45
+ }
46
+ }
39
47
  if (distribution === 'internal') {
40
48
  if (enterpriseProvisioning === 'adhoc') {
41
49
  return await new SetUpAdhocProvisioningProfile_1.SetUpAdhocProvisioningProfile({ app, target }).runAsync(ctx);
@@ -16,6 +16,7 @@ async function withErrorHandlingAsync(promise) {
16
16
  ![
17
17
  'EAS_BUILD_FREE_TIER_LIMIT_EXCEEDED',
18
18
  'EAS_BUILD_FREE_TIER_IOS_LIMIT_EXCEEDED',
19
+ 'EMBEDDED_UPDATE_ASSET_NOT_AVAILABLE',
19
20
  ].includes(e?.extensions?.errorCode))) {
20
21
  log_1.default.error(`We've encountered a transient error. Try again shortly.`);
21
22
  }