eas-cli 20.1.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.
- package/README.md +130 -119
- package/build/commands/simulator/start.d.ts +1 -0
- package/build/commands/simulator/start.js +5 -0
- package/build/commands/update/rollback.d.ts +11 -0
- package/build/commands/update/rollback.js +117 -14
- package/build/credentials/ios/actions/AscApiKeyUtils.d.ts +20 -0
- package/build/credentials/ios/actions/AscApiKeyUtils.js +64 -0
- package/build/credentials/ios/actions/ConfigureProvisioningProfile.js +2 -4
- package/build/credentials/ios/actions/CreateProvisioningProfile.js +2 -4
- package/build/credentials/ios/actions/SetUpAdhocProvisioningProfile.js +3 -20
- package/build/credentials/ios/actions/SetUpProvisioningProfile.d.ts +10 -0
- package/build/credentials/ios/actions/SetUpProvisioningProfile.js +39 -5
- package/build/credentials/ios/appstore/resolveCredentials.d.ts +1 -0
- package/build/credentials/ios/appstore/resolveCredentials.js +1 -0
- package/build/graphql/generated.d.ts +92 -0
- package/build/graphql/queries/UpdateQuery.d.ts +2 -1
- package/build/graphql/queries/UpdateQuery.js +52 -0
- package/oclif.manifest.json +355 -303
- package/package.json +2 -2
|
@@ -8,6 +8,7 @@ export default class SimulatorStart extends EasCommand {
|
|
|
8
8
|
platform: import("@oclif/core/lib/interfaces").OptionFlag<"android" | "ios", import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
9
|
type: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
10
|
'package-version': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
'max-duration-minutes': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
12
|
force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
12
13
|
'out-config-type': import("@oclif/core/lib/interfaces").OptionFlag<"env" | "dotenv", import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
14
|
};
|
|
@@ -38,6 +38,10 @@ class SimulatorStart extends EasCommand_1.default {
|
|
|
38
38
|
'package-version': core_1.Flags.string({
|
|
39
39
|
description: 'Version of the package backing the device run session (e.g. "0.1.3-alpha.3"). Defaults to "latest" when omitted.',
|
|
40
40
|
}),
|
|
41
|
+
'max-duration-minutes': core_1.Flags.integer({
|
|
42
|
+
description: 'Maximum duration of the device run session in minutes before it is automatically stopped. Only customizable on paid plans. Defaults to a value derived from the job run priority when omitted.',
|
|
43
|
+
min: 0,
|
|
44
|
+
}),
|
|
41
45
|
force: core_1.Flags.boolean({
|
|
42
46
|
description: '[default: true] Create a new device session even when an existing simulator session is present in the environment.',
|
|
43
47
|
default: true,
|
|
@@ -85,6 +89,7 @@ class SimulatorStart extends EasCommand_1.default {
|
|
|
85
89
|
platform,
|
|
86
90
|
type: utils_1.DEVICE_RUN_SESSION_TYPE_BY_FLAG_VALUE[flags.type],
|
|
87
91
|
packageVersion: flags['package-version'],
|
|
92
|
+
maxRunTimeMinutes: flags['max-duration-minutes'],
|
|
88
93
|
});
|
|
89
94
|
deviceRunSessionId = session.id;
|
|
90
95
|
const jobRunId = (0, nullthrows_1.default)(session.turtleJobRun?.id, 'Expected device run session to start');
|
|
@@ -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 = '
|
|
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 {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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 (
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
61
|
-
|
|
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;
|
|
@@ -18446,6 +18446,98 @@ export type ViewUpdateGroupsOnBranchQuery = {
|
|
|
18446
18446
|
};
|
|
18447
18447
|
};
|
|
18448
18448
|
};
|
|
18449
|
+
export type ViewUpdateGroupsPaginatedOnBranchQueryVariables = Exact<{
|
|
18450
|
+
appId: Scalars['String']['input'];
|
|
18451
|
+
branchName: Scalars['String']['input'];
|
|
18452
|
+
first?: InputMaybe<Scalars['Int']['input']>;
|
|
18453
|
+
last?: InputMaybe<Scalars['Int']['input']>;
|
|
18454
|
+
after?: InputMaybe<Scalars['String']['input']>;
|
|
18455
|
+
before?: InputMaybe<Scalars['String']['input']>;
|
|
18456
|
+
filter?: InputMaybe<UpdatesFilterV2>;
|
|
18457
|
+
}>;
|
|
18458
|
+
export type ViewUpdateGroupsPaginatedOnBranchQuery = {
|
|
18459
|
+
__typename?: 'RootQuery';
|
|
18460
|
+
app: {
|
|
18461
|
+
__typename?: 'AppQuery';
|
|
18462
|
+
byId: {
|
|
18463
|
+
__typename?: 'App';
|
|
18464
|
+
id: string;
|
|
18465
|
+
updateBranchByName?: {
|
|
18466
|
+
__typename?: 'UpdateBranch';
|
|
18467
|
+
id: string;
|
|
18468
|
+
updateGroupsPaginated: {
|
|
18469
|
+
__typename?: 'UpdateGroupsConnection';
|
|
18470
|
+
edges: Array<{
|
|
18471
|
+
__typename?: 'UpdateGroupEdge';
|
|
18472
|
+
node: Array<{
|
|
18473
|
+
__typename?: 'Update';
|
|
18474
|
+
id: string;
|
|
18475
|
+
group: string;
|
|
18476
|
+
message?: string | null;
|
|
18477
|
+
createdAt: any;
|
|
18478
|
+
runtimeVersion: string;
|
|
18479
|
+
platform: string;
|
|
18480
|
+
manifestFragment: string;
|
|
18481
|
+
isRollBackToEmbedded: boolean;
|
|
18482
|
+
manifestPermalink: string;
|
|
18483
|
+
gitCommitHash?: string | null;
|
|
18484
|
+
isGitWorkingTreeDirty: boolean;
|
|
18485
|
+
environment?: any | null;
|
|
18486
|
+
rolloutPercentage?: number | null;
|
|
18487
|
+
manifestHostOverride?: string | null;
|
|
18488
|
+
assetHostOverride?: string | null;
|
|
18489
|
+
actor?: {
|
|
18490
|
+
__typename: 'PartnerActor';
|
|
18491
|
+
username: string;
|
|
18492
|
+
id: string;
|
|
18493
|
+
} | {
|
|
18494
|
+
__typename: 'Robot';
|
|
18495
|
+
firstName?: string | null;
|
|
18496
|
+
id: string;
|
|
18497
|
+
} | {
|
|
18498
|
+
__typename: 'SSOUser';
|
|
18499
|
+
username: string;
|
|
18500
|
+
id: string;
|
|
18501
|
+
} | {
|
|
18502
|
+
__typename: 'User';
|
|
18503
|
+
username: string;
|
|
18504
|
+
id: string;
|
|
18505
|
+
} | null;
|
|
18506
|
+
branch: {
|
|
18507
|
+
__typename?: 'UpdateBranch';
|
|
18508
|
+
id: string;
|
|
18509
|
+
name: string;
|
|
18510
|
+
};
|
|
18511
|
+
codeSigningInfo?: {
|
|
18512
|
+
__typename?: 'CodeSigningInfo';
|
|
18513
|
+
keyid: string;
|
|
18514
|
+
sig: string;
|
|
18515
|
+
alg: string;
|
|
18516
|
+
} | null;
|
|
18517
|
+
rolloutControlUpdate?: {
|
|
18518
|
+
__typename?: 'Update';
|
|
18519
|
+
id: string;
|
|
18520
|
+
group: string;
|
|
18521
|
+
} | null;
|
|
18522
|
+
fingerprint?: {
|
|
18523
|
+
__typename?: 'Fingerprint';
|
|
18524
|
+
id: string;
|
|
18525
|
+
hash: string;
|
|
18526
|
+
debugInfoUrl?: string | null;
|
|
18527
|
+
source?: {
|
|
18528
|
+
__typename?: 'FingerprintSource';
|
|
18529
|
+
type: FingerprintSourceType;
|
|
18530
|
+
bucketKey: string;
|
|
18531
|
+
isDebugFingerprint?: boolean | null;
|
|
18532
|
+
} | null;
|
|
18533
|
+
} | null;
|
|
18534
|
+
}>;
|
|
18535
|
+
}>;
|
|
18536
|
+
};
|
|
18537
|
+
} | null;
|
|
18538
|
+
};
|
|
18539
|
+
};
|
|
18540
|
+
};
|
|
18449
18541
|
export type ViewUpdateGroupsOnAppQueryVariables = Exact<{
|
|
18450
18542
|
appId: Scalars['String']['input'];
|
|
18451
18543
|
limit: Scalars['Int']['input'];
|