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.
- package/README.md +305 -101
- package/build/build/createContext.d.ts +2 -1
- package/build/build/createContext.js +3 -1
- package/build/build/ios/credentials.js +5 -1
- package/build/build/ios/prepareJob.js +3 -0
- package/build/build/runBuildAndSubmit.d.ts +1 -0
- package/build/build/runBuildAndSubmit.js +1 -0
- package/build/build/types.d.ts +1 -0
- package/build/commands/build/index.d.ts +1 -0
- package/build/commands/build/index.js +13 -0
- package/build/commands/build/internal.d.ts +1 -0
- package/build/commands/build/internal.js +5 -0
- package/build/commands/go.d.ts +1 -0
- package/build/commands/go.js +29 -1
- package/build/commands/observe/events.d.ts +3 -3
- package/build/commands/observe/events.js +69 -51
- package/build/commands/observe/{logs.d.ts → metrics-summary.d.ts} +3 -11
- package/build/commands/observe/metrics-summary.js +95 -0
- package/build/commands/observe/metrics.d.ts +8 -3
- package/build/commands/observe/metrics.js +63 -47
- package/build/commands/observe/routes.d.ts +0 -1
- package/build/commands/observe/routes.js +0 -4
- package/build/commands/observe/versions.d.ts +0 -1
- package/build/commands/observe/versions.js +0 -4
- package/build/commands/update/embedded/upload.d.ts +20 -0
- package/build/commands/update/embedded/upload.js +129 -0
- package/build/credentials/context.d.ts +2 -0
- package/build/credentials/context.js +2 -0
- package/build/credentials/ios/actions/DeviceUtils.d.ts +2 -0
- package/build/credentials/ios/actions/DeviceUtils.js +21 -0
- package/build/credentials/ios/actions/SetUpAdhocProvisioningProfile.d.ts +1 -0
- package/build/credentials/ios/actions/SetUpAdhocProvisioningProfile.js +76 -14
- package/build/credentials/ios/actions/SetUpTargetBuildCredentials.js +8 -0
- package/build/graphql/client.js +1 -0
- package/build/graphql/generated.d.ts +70 -1
- package/build/graphql/mutations/EmbeddedUpdateAssetMutation.d.ts +13 -0
- package/build/graphql/mutations/EmbeddedUpdateAssetMutation.js +32 -0
- package/build/graphql/mutations/EmbeddedUpdateMutation.d.ts +14 -0
- package/build/graphql/mutations/EmbeddedUpdateMutation.js +37 -0
- package/build/graphql/queries/WorkflowRunQuery.d.ts +2 -1
- package/build/graphql/queries/WorkflowRunQuery.js +19 -6
- package/build/observe/formatCustomEvents.js +5 -5
- package/build/observe/metricNames.js +2 -0
- package/build/update/embeddedManifest.d.ts +3 -0
- package/build/update/embeddedManifest.js +28 -0
- package/oclif.manifest.json +1852 -1714
- package/package.json +6 -6
- 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
|
|
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
|
|
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
|
|
36
|
-
static
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
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
|
|
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 {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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,
|
|
105
|
+
log_1.default.log((0, formatEvents_1.buildObserveEventsTable)(events, pageInfo, {
|
|
106
|
+
metricName,
|
|
92
107
|
daysBack,
|
|
93
|
-
|
|
94
|
-
|
|
108
|
+
startTime,
|
|
109
|
+
endTime,
|
|
110
|
+
totalEventCount,
|
|
95
111
|
}));
|
|
96
112
|
}
|
|
97
113
|
}
|
|
@@ -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;
|
|
@@ -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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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);
|
package/build/graphql/client.js
CHANGED
|
@@ -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
|
}
|