eas-cli 0.35.0 → 0.36.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 +26 -26
- package/build/analytics/common.d.ts +8 -0
- package/build/analytics/common.js +19 -0
- package/build/analytics/events.d.ts +43 -0
- package/build/analytics/events.js +51 -0
- package/build/{analytics.d.ts → analytics/rudderstackClient.d.ts} +0 -0
- package/build/{analytics.js → analytics/rudderstackClient.js} +2 -2
- package/build/build/build.js +18 -27
- package/build/build/context.d.ts +1 -1
- package/build/build/context.js +2 -2
- package/build/build/ios/credentials.js +3 -3
- package/build/build/local.js +18 -27
- package/build/build/types.d.ts +0 -1
- package/build/commandUtils/EasCommand.js +4 -4
- package/build/commands/build/index.js +1 -1
- package/build/credentials/android/actions/RemoveFcm.js +4 -4
- package/build/credentials/android/utils/keystore.js +67 -32
- package/build/credentials/ios/actions/AscApiKeyUtils.js +8 -7
- package/build/credentials/ios/actions/AssignAscApiKey.js +1 -1
- package/build/credentials/ios/actions/CreateAscApiKey.js +2 -2
- package/build/credentials/ios/actions/RemoveAscApiKey.js +6 -6
- package/build/credentials/ios/actions/SetUpAscApiKey.js +8 -8
- package/build/credentials/ios/actions/SetUpSubmissionCredentials.js +3 -3
- package/build/credentials/ios/appstore/ascApiKey.js +12 -12
- package/build/credentials/ios/appstore/entitlements.d.ts +2 -2
- package/build/credentials/ios/appstore/entitlements.js +20 -10
- package/build/credentials/ios/credentials.js +2 -2
- package/build/credentials/ios/utils/printCredentials.js +1 -1
- package/build/credentials/manager/AndroidActions.js +3 -3
- package/build/credentials/manager/IosActions.js +5 -5
- package/build/credentials/manager/ManageIos.js +4 -4
- package/build/graphql/generated.d.ts +144 -53
- package/build/graphql/mutations/KeystoreGenerationUrlMutation.d.ts +3 -0
- package/build/graphql/mutations/KeystoreGenerationUrlMutation.js +23 -0
- package/build/submit/BaseSubmitter.d.ts +20 -4
- package/build/submit/BaseSubmitter.js +34 -1
- package/build/submit/android/AndroidSubmitter.d.ts +12 -6
- package/build/submit/android/AndroidSubmitter.js +31 -20
- package/build/submit/context.d.ts +2 -0
- package/build/submit/context.js +14 -0
- package/build/submit/ios/AppSpecificPasswordSource.d.ts +8 -1
- package/build/submit/ios/AppSpecificPasswordSource.js +44 -4
- package/build/submit/ios/CredentialsServiceSource.d.ts +3 -2
- package/build/submit/ios/CredentialsServiceSource.js +9 -3
- package/build/submit/ios/IosSubmitCommand.d.ts +1 -2
- package/build/submit/ios/IosSubmitCommand.js +2 -39
- package/build/submit/ios/IosSubmitter.d.ts +17 -7
- package/build/submit/ios/IosSubmitter.js +55 -29
- package/build/submit/submit.js +13 -4
- package/build/user/User.js +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +3 -3
- package/build/build/utils/analytics.d.ts +0 -22
- package/build/build/utils/analytics.js +0 -28
|
@@ -11,23 +11,23 @@ class RemoveFcm {
|
|
|
11
11
|
}
|
|
12
12
|
async runAsync(ctx) {
|
|
13
13
|
if (ctx.nonInteractive) {
|
|
14
|
-
throw new Error("Deleting an FCM
|
|
14
|
+
throw new Error("Deleting an FCM API Key is a destructive operation. Start the CLI without the '--non-interactive' flag to delete the credentials.");
|
|
15
15
|
}
|
|
16
16
|
const appCredentials = await ctx.android.getAndroidAppCredentialsWithCommonFieldsAsync(this.app);
|
|
17
17
|
const fcm = appCredentials === null || appCredentials === void 0 ? void 0 : appCredentials.androidFcm;
|
|
18
18
|
if (!fcm) {
|
|
19
|
-
log_1.default.warn(`There is no valid FCM
|
|
19
|
+
log_1.default.warn(`There is no valid FCM API Key defined for ${(0, GraphqlClient_1.formatProjectFullName)(this.app)}, ${this.app.androidApplicationIdentifier}`);
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
22
|
const confirm = await (0, prompts_1.confirmAsync)({
|
|
23
|
-
message: 'Permanently delete the FCM
|
|
23
|
+
message: 'Permanently delete the FCM API Key from Expo servers?',
|
|
24
24
|
initial: false,
|
|
25
25
|
});
|
|
26
26
|
if (!confirm) {
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
29
|
await ctx.android.deleteFcmAsync(fcm);
|
|
30
|
-
log_1.default.succeed('FCM
|
|
30
|
+
log_1.default.succeed('FCM API Key removed');
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
exports.RemoveFcm = RemoveFcm;
|
|
@@ -3,32 +3,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.generateRandomKeystoreAsync = exports.keytoolCommandExistsAsync = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const spawn_async_1 = (0, tslib_1.__importDefault)(require("@expo/spawn-async"));
|
|
6
|
+
const chalk_1 = (0, tslib_1.__importDefault)(require("chalk"));
|
|
6
7
|
const crypto_1 = (0, tslib_1.__importDefault)(require("crypto"));
|
|
7
8
|
const fs_extra_1 = (0, tslib_1.__importDefault)(require("fs-extra"));
|
|
9
|
+
// (dsokal) We actually want to use node-fetch but the change is in progress.
|
|
10
|
+
// See https://github.com/expo/eas-cli/issues/32 for context.
|
|
11
|
+
// eslint-disable-next-line no-restricted-imports
|
|
12
|
+
const node_fetch_1 = (0, tslib_1.__importDefault)(require("node-fetch"));
|
|
8
13
|
const path_1 = (0, tslib_1.__importDefault)(require("path"));
|
|
9
14
|
const uuid_1 = require("uuid");
|
|
10
|
-
const
|
|
15
|
+
const events_1 = require("../../../analytics/events");
|
|
11
16
|
const generated_1 = require("../../../graphql/generated");
|
|
17
|
+
const KeystoreGenerationUrlMutation_1 = require("../../../graphql/mutations/KeystoreGenerationUrlMutation");
|
|
12
18
|
const log_1 = (0, tslib_1.__importDefault)(require("../../../log"));
|
|
19
|
+
const ora_1 = require("../../../ora");
|
|
13
20
|
const paths_1 = require("../../../utils/paths");
|
|
14
21
|
async function keytoolCommandExistsAsync() {
|
|
15
22
|
try {
|
|
16
23
|
await (0, spawn_async_1.default)('keytool');
|
|
24
|
+
return true;
|
|
17
25
|
}
|
|
18
26
|
catch (error) {
|
|
19
27
|
return false;
|
|
20
28
|
}
|
|
21
|
-
return true;
|
|
22
29
|
}
|
|
23
30
|
exports.keytoolCommandExistsAsync = keytoolCommandExistsAsync;
|
|
24
|
-
async function ensureKeytoolCommandExistsAsync() {
|
|
25
|
-
if (!(await keytoolCommandExistsAsync())) {
|
|
26
|
-
log_1.default.error('keytool is required to run this command, make sure you have it installed?');
|
|
27
|
-
log_1.default.warn('keytool is a part of OpenJDK: https://openjdk.java.net/');
|
|
28
|
-
log_1.default.warn('Also make sure that keytool is in your PATH after installation.');
|
|
29
|
-
throw new Error('keytool not found');
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
31
|
var KeystoreCreateStep;
|
|
33
32
|
(function (KeystoreCreateStep) {
|
|
34
33
|
KeystoreCreateStep["Attempt"] = "attempt";
|
|
@@ -44,17 +43,38 @@ async function generateRandomKeystoreAsync(projectId) {
|
|
|
44
43
|
return await createKeystoreAsync(keystoreData, projectId);
|
|
45
44
|
}
|
|
46
45
|
exports.generateRandomKeystoreAsync = generateRandomKeystoreAsync;
|
|
47
|
-
async function createKeystoreAsync(
|
|
48
|
-
|
|
46
|
+
async function createKeystoreAsync(keystoreParams, projectId) {
|
|
47
|
+
events_1.Analytics.logEvent(events_1.BuildEvent.ANDROID_KEYSTORE_CREATE, {
|
|
49
48
|
project_id: projectId,
|
|
50
49
|
step: KeystoreCreateStep.Attempt,
|
|
51
50
|
type: generated_1.AndroidKeystoreType.Jks,
|
|
52
51
|
});
|
|
53
52
|
try {
|
|
54
|
-
|
|
53
|
+
let keystore;
|
|
54
|
+
let localAttempt = false;
|
|
55
|
+
if (await keytoolCommandExistsAsync()) {
|
|
56
|
+
try {
|
|
57
|
+
localAttempt = true;
|
|
58
|
+
keystore = await createKeystoreLocallyAsync(keystoreParams);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
log_1.default.error('Failed to generate keystore locally. Falling back to cloud generation.');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (!keystore) {
|
|
65
|
+
keystore = await createKeystoreInCloudAsync(keystoreParams, {
|
|
66
|
+
showKeytoolDetectionMsg: !localAttempt,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
events_1.Analytics.logEvent(events_1.BuildEvent.ANDROID_KEYSTORE_CREATE, {
|
|
70
|
+
project_id: projectId,
|
|
71
|
+
step: KeystoreCreateStep.Success,
|
|
72
|
+
type: generated_1.AndroidKeystoreType.Jks,
|
|
73
|
+
});
|
|
74
|
+
return keystore;
|
|
55
75
|
}
|
|
56
76
|
catch (error) {
|
|
57
|
-
|
|
77
|
+
events_1.Analytics.logEvent(events_1.BuildEvent.ANDROID_KEYSTORE_CREATE, {
|
|
58
78
|
project_id: projectId,
|
|
59
79
|
step: KeystoreCreateStep.Fail,
|
|
60
80
|
reason: error.message,
|
|
@@ -62,6 +82,8 @@ async function createKeystoreAsync(credentials, projectId) {
|
|
|
62
82
|
});
|
|
63
83
|
throw error;
|
|
64
84
|
}
|
|
85
|
+
}
|
|
86
|
+
async function createKeystoreLocallyAsync(keystoreParams) {
|
|
65
87
|
await fs_extra_1.default.mkdirp((0, paths_1.getTmpDirectory)());
|
|
66
88
|
const keystorePath = path_1.default.join((0, paths_1.getTmpDirectory)(), `${(0, uuid_1.v4)()}-keystore.jks`);
|
|
67
89
|
try {
|
|
@@ -71,13 +93,13 @@ async function createKeystoreAsync(credentials, projectId) {
|
|
|
71
93
|
'-storetype',
|
|
72
94
|
'JKS',
|
|
73
95
|
'-storepass',
|
|
74
|
-
|
|
96
|
+
keystoreParams.keystorePassword,
|
|
75
97
|
'-keypass',
|
|
76
|
-
|
|
98
|
+
keystoreParams.keyPassword,
|
|
77
99
|
'-keystore',
|
|
78
100
|
keystorePath,
|
|
79
101
|
'-alias',
|
|
80
|
-
|
|
102
|
+
keystoreParams.keyAlias,
|
|
81
103
|
'-keyalg',
|
|
82
104
|
'RSA',
|
|
83
105
|
'-keysize',
|
|
@@ -87,27 +109,40 @@ async function createKeystoreAsync(credentials, projectId) {
|
|
|
87
109
|
'-dname',
|
|
88
110
|
`CN=,OU=,O=,L=,S=,C=US`,
|
|
89
111
|
]);
|
|
90
|
-
analytics_1.default.logEvent(analytics_1.Event.ANDROID_KEYSTORE_CREATE, {
|
|
91
|
-
project_id: projectId,
|
|
92
|
-
step: KeystoreCreateStep.Success,
|
|
93
|
-
type: generated_1.AndroidKeystoreType.Jks,
|
|
94
|
-
});
|
|
95
112
|
return {
|
|
96
|
-
...
|
|
113
|
+
...keystoreParams,
|
|
97
114
|
keystore: await fs_extra_1.default.readFile(keystorePath, 'base64'),
|
|
98
115
|
type: generated_1.AndroidKeystoreType.Jks,
|
|
99
116
|
};
|
|
100
117
|
}
|
|
101
|
-
catch (error) {
|
|
102
|
-
analytics_1.default.logEvent(analytics_1.Event.ANDROID_KEYSTORE_CREATE, {
|
|
103
|
-
project_id: projectId,
|
|
104
|
-
step: KeystoreCreateStep.Fail,
|
|
105
|
-
reason: error.message,
|
|
106
|
-
type: generated_1.AndroidKeystoreType.Jks,
|
|
107
|
-
});
|
|
108
|
-
throw error;
|
|
109
|
-
}
|
|
110
118
|
finally {
|
|
111
119
|
await fs_extra_1.default.remove(keystorePath);
|
|
112
120
|
}
|
|
113
121
|
}
|
|
122
|
+
async function createKeystoreInCloudAsync(keystoreParams, { showKeytoolDetectionMsg = true } = {}) {
|
|
123
|
+
if (showKeytoolDetectionMsg) {
|
|
124
|
+
log_1.default.log(`Detected that you do not have ${chalk_1.default.bold('keytool')} installed locally.`);
|
|
125
|
+
}
|
|
126
|
+
const spinner = (0, ora_1.ora)('Generating keystore in the cloud...').start();
|
|
127
|
+
try {
|
|
128
|
+
const url = await KeystoreGenerationUrlMutation_1.KeystoreGenerationUrlMutation.createKeystoreGenerationUrlAsync();
|
|
129
|
+
const response = await (0, node_fetch_1.default)(url, {
|
|
130
|
+
method: 'POST',
|
|
131
|
+
body: JSON.stringify(keystoreParams),
|
|
132
|
+
headers: { 'Content-Type': 'application/json' },
|
|
133
|
+
});
|
|
134
|
+
const result = await response.json();
|
|
135
|
+
spinner.succeed();
|
|
136
|
+
return {
|
|
137
|
+
type: generated_1.AndroidKeystoreType.Jks,
|
|
138
|
+
keystore: result.keystoreBase64,
|
|
139
|
+
keystorePassword: result.keystorePassword,
|
|
140
|
+
keyAlias: result.keyAlias,
|
|
141
|
+
keyPassword: result.keyPassword,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
spinner.fail();
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -46,14 +46,14 @@ async function getMinimalAscApiKeyAsync(ascApiKey) {
|
|
|
46
46
|
exports.getMinimalAscApiKeyAsync = getMinimalAscApiKeyAsync;
|
|
47
47
|
async function provideOrGenerateAscApiKeyAsync(ctx, purpose) {
|
|
48
48
|
if (ctx.nonInteractive) {
|
|
49
|
-
|
|
49
|
+
throw new Error(`A new App Store Connect API Key cannot be created in non-interactive mode.`);
|
|
50
50
|
}
|
|
51
51
|
const userProvided = await promptForAscApiKeyAsync(ctx);
|
|
52
52
|
if (!userProvided) {
|
|
53
53
|
return await generateAscApiKeyAsync(ctx, purpose);
|
|
54
54
|
}
|
|
55
55
|
if (!ctx.appStore.authCtx) {
|
|
56
|
-
log_1.default.warn('Unable to validate App Store Connect
|
|
56
|
+
log_1.default.warn('Unable to validate App Store Connect API Key, you are not authenticated with Apple.');
|
|
57
57
|
return userProvided;
|
|
58
58
|
}
|
|
59
59
|
const isValidAndTracked = await (0, validateAscApiKey_1.isAscApiKeyValidAndTrackedAsync)(ctx, userProvided);
|
|
@@ -61,7 +61,7 @@ async function provideOrGenerateAscApiKeyAsync(ctx, purpose) {
|
|
|
61
61
|
return userProvided;
|
|
62
62
|
}
|
|
63
63
|
const useUserProvided = await (0, prompts_1.confirmAsync)({
|
|
64
|
-
message: `App Store Connect
|
|
64
|
+
message: `App Store Connect API Key with ID ${userProvided.keyId} is not valid on Apple's servers. Proceed anyway?`,
|
|
65
65
|
});
|
|
66
66
|
if (useUserProvided) {
|
|
67
67
|
return userProvided;
|
|
@@ -70,6 +70,7 @@ async function provideOrGenerateAscApiKeyAsync(ctx, purpose) {
|
|
|
70
70
|
}
|
|
71
71
|
exports.provideOrGenerateAscApiKeyAsync = provideOrGenerateAscApiKeyAsync;
|
|
72
72
|
async function generateAscApiKeyAsync(ctx, purpose) {
|
|
73
|
+
//console.log('debug');
|
|
73
74
|
await ctx.appStore.ensureAuthenticatedAsync();
|
|
74
75
|
const ascApiKey = await ctx.appStore.createAscApiKeyAsync({
|
|
75
76
|
nickname: getAscApiKeyName(purpose),
|
|
@@ -97,7 +98,7 @@ async function promptForKeyP8AndIdAsync() {
|
|
|
97
98
|
const { keyP8Path } = await (0, prompts_1.promptAsync)({
|
|
98
99
|
type: 'text',
|
|
99
100
|
name: 'keyP8Path',
|
|
100
|
-
message: 'Path to App Store Connect
|
|
101
|
+
message: 'Path to App Store Connect API Key:',
|
|
101
102
|
initial: 'AuthKey_ABCD.p8',
|
|
102
103
|
// eslint-disable-next-line async-protect/async-suffix
|
|
103
104
|
validate: async (filePath) => {
|
|
@@ -146,10 +147,10 @@ async function selectAscApiKeysFromAccountAsync(ctx, account, { filterDifferentA
|
|
|
146
147
|
if (ascApiKeysForAccount.length === 0) {
|
|
147
148
|
if (filterDifferentAppleTeam) {
|
|
148
149
|
const maybeAppleTeamId = (_a = ctx.appStore.authCtx) === null || _a === void 0 ? void 0 : _a.team.id;
|
|
149
|
-
log_1.default.warn(`There are no App Store Connect
|
|
150
|
+
log_1.default.warn(`There are no App Store Connect API Keys in your EAS account${maybeAppleTeamId ? ` matching Apple Team ID: ${maybeAppleTeamId}.` : '.'}`);
|
|
150
151
|
}
|
|
151
152
|
else {
|
|
152
|
-
log_1.default.warn(`There are no App Store Connect
|
|
153
|
+
log_1.default.warn(`There are no App Store Connect API Keys available in your EAS account.`);
|
|
153
154
|
}
|
|
154
155
|
return null;
|
|
155
156
|
}
|
|
@@ -161,7 +162,7 @@ async function selectAscApiKeysAsync(ascApiKeys) {
|
|
|
161
162
|
const { chosenAscApiKey } = await (0, prompts_1.promptAsync)({
|
|
162
163
|
type: 'select',
|
|
163
164
|
name: 'chosenAscApiKey',
|
|
164
|
-
message: 'Select an
|
|
165
|
+
message: 'Select an API Key from the list:',
|
|
165
166
|
choices: sortedAscApiKeys.map(ascApiKey => ({
|
|
166
167
|
title: formatAscApiKey(ascApiKey),
|
|
167
168
|
value: ascApiKey,
|
|
@@ -22,7 +22,7 @@ class AssignAscApiKey {
|
|
|
22
22
|
else {
|
|
23
23
|
throw new Error(`${purpose} is not yet supported.`);
|
|
24
24
|
}
|
|
25
|
-
log_1.default.succeed(`App Store Connect
|
|
25
|
+
log_1.default.succeed(`App Store Connect API Key assigned to ${this.app.projectName}: ${this.app.bundleIdentifier} for ${purpose}.`);
|
|
26
26
|
return updatedAppCredentials;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -10,11 +10,11 @@ class CreateAscApiKey {
|
|
|
10
10
|
}
|
|
11
11
|
async runAsync(ctx, purpose) {
|
|
12
12
|
if (ctx.nonInteractive) {
|
|
13
|
-
throw new Error(`A new App Store Connect
|
|
13
|
+
throw new Error(`A new App Store Connect API Key cannot be created in non-interactive mode.`);
|
|
14
14
|
}
|
|
15
15
|
const ascApiKey = await (0, AscApiKeyUtils_1.provideOrGenerateAscApiKeyAsync)(ctx, purpose);
|
|
16
16
|
const result = await ctx.ios.createAscApiKeyAsync(this.account, ascApiKey);
|
|
17
|
-
log_1.default.succeed('Created App Store Connect
|
|
17
|
+
log_1.default.succeed('Created App Store Connect API Key');
|
|
18
18
|
return result;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
@@ -13,7 +13,7 @@ class SelectAndRemoveAscApiKey {
|
|
|
13
13
|
const selected = await (0, AscApiKeyUtils_1.selectAscApiKeysFromAccountAsync)(ctx, this.account);
|
|
14
14
|
if (selected) {
|
|
15
15
|
await new RemoveAscApiKey(this.account, selected).runAsync(ctx);
|
|
16
|
-
log_1.default.succeed('Removed App Store Connect
|
|
16
|
+
log_1.default.succeed('Removed App Store Connect API Key');
|
|
17
17
|
log_1.default.newLine();
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -26,26 +26,26 @@ class RemoveAscApiKey {
|
|
|
26
26
|
}
|
|
27
27
|
async runAsync(ctx) {
|
|
28
28
|
if (ctx.nonInteractive) {
|
|
29
|
-
throw new Error(`Cannot remove App Store Connect
|
|
29
|
+
throw new Error(`Cannot remove App Store Connect API Keys in non-interactive mode.`);
|
|
30
30
|
}
|
|
31
31
|
// TODO(quin): add an extra edge on AppStoreConnectApiKey to find the apps using it
|
|
32
32
|
const confirm = await (0, prompts_1.confirmAsync)({
|
|
33
|
-
message: `Deleting this
|
|
33
|
+
message: `Deleting this API Key may affect your projects that rely on it. Do you want to continue?`,
|
|
34
34
|
});
|
|
35
35
|
if (!confirm) {
|
|
36
36
|
log_1.default.log('Aborting');
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
|
-
log_1.default.log('Removing
|
|
39
|
+
log_1.default.log('Removing API Key');
|
|
40
40
|
await ctx.ios.deleteAscApiKeyAsync(this.ascApiKey.id);
|
|
41
41
|
let shouldRevoke = false;
|
|
42
42
|
if (!ctx.nonInteractive) {
|
|
43
43
|
shouldRevoke = await (0, prompts_1.confirmAsync)({
|
|
44
|
-
message: `Do you also want to revoke this
|
|
44
|
+
message: `Do you also want to revoke this API Key on the Apple Developer Portal?`,
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
else if (ctx.nonInteractive) {
|
|
48
|
-
log_1.default.log('Skipping
|
|
48
|
+
log_1.default.log('Skipping API Key revocation on the Apple Developer Portal.');
|
|
49
49
|
}
|
|
50
50
|
if (shouldRevoke) {
|
|
51
51
|
await ctx.appStore.revokeAscApiKeyAsync(this.ascApiKey.keyIdentifier);
|
|
@@ -22,20 +22,20 @@ class SetUpAscApiKey {
|
|
|
22
22
|
this.purpose = purpose;
|
|
23
23
|
this.choices = [
|
|
24
24
|
{
|
|
25
|
-
title: '[Choose an existing ASC
|
|
25
|
+
title: '[Choose an existing ASC API Key]',
|
|
26
26
|
value: SetupAscApiKeyChoice.USE_EXISTING,
|
|
27
27
|
},
|
|
28
|
-
{ title: '[Add a new ASC
|
|
28
|
+
{ title: '[Add a new ASC API Key]', value: SetupAscApiKeyChoice.GENERATE },
|
|
29
29
|
];
|
|
30
30
|
}
|
|
31
31
|
async runAsync(ctx) {
|
|
32
32
|
const isKeySetup = await this.isAscApiKeySetupAsync(ctx, this.purpose);
|
|
33
33
|
if (isKeySetup) {
|
|
34
|
-
log_1.default.succeed('App Store Connect
|
|
35
|
-
return (0, nullthrows_1.default)(await ctx.ios.getIosAppCredentialsWithCommonFieldsAsync(this.app), 'iosAppCredentials cannot be null if App Store Connect
|
|
34
|
+
log_1.default.succeed('App Store Connect API Key already set up.');
|
|
35
|
+
return (0, nullthrows_1.default)(await ctx.ios.getIosAppCredentialsWithCommonFieldsAsync(this.app), 'iosAppCredentials cannot be null if App Store Connect API Key is already set up');
|
|
36
36
|
}
|
|
37
37
|
if (ctx.nonInteractive) {
|
|
38
|
-
throw new errors_1.MissingCredentialsNonInteractiveError('App Store Connect
|
|
38
|
+
throw new errors_1.MissingCredentialsNonInteractiveError('App Store Connect API Keys cannot be set up in --non-interactive mode.');
|
|
39
39
|
}
|
|
40
40
|
const keysForAccount = await (0, AscApiKeyUtils_1.getAscApiKeysFromAccountAsync)(ctx, this.app.account, {
|
|
41
41
|
filterDifferentAppleTeam: true,
|
|
@@ -64,10 +64,10 @@ class SetUpAscApiKey {
|
|
|
64
64
|
}
|
|
65
65
|
const [autoselectedKey] = (0, AscApiKeyUtils_1.sortAscApiKeysByUpdatedAtDesc)(validKeys);
|
|
66
66
|
const useAutoselected = await (0, prompts_1.confirmAsync)({
|
|
67
|
-
message: `Reuse this App Store Connect
|
|
67
|
+
message: `Reuse this App Store Connect API Key?\n${(0, AscApiKeyUtils_1.formatAscApiKey)(autoselectedKey)}`,
|
|
68
68
|
});
|
|
69
69
|
if (useAutoselected) {
|
|
70
|
-
log_1.default.log(`Using App Store Connect
|
|
70
|
+
log_1.default.log(`Using App Store Connect API Key with ID ${autoselectedKey.keyIdentifier}`);
|
|
71
71
|
return autoselectedKey;
|
|
72
72
|
}
|
|
73
73
|
else {
|
|
@@ -77,7 +77,7 @@ class SetUpAscApiKey {
|
|
|
77
77
|
async isAscApiKeySetupAsync(ctx, purpose) {
|
|
78
78
|
const appCredentials = await ctx.ios.getIosAppCredentialsWithCommonFieldsAsync(this.app);
|
|
79
79
|
if (purpose !== AscApiKeyUtils_1.AppStoreApiKeyPurpose.SUBMISSION_SERVICE) {
|
|
80
|
-
throw new Error(`App Store Connect
|
|
80
|
+
throw new Error(`App Store Connect API Key setup is not yet supported for ${purpose}.`);
|
|
81
81
|
}
|
|
82
82
|
return !!(appCredentials === null || appCredentials === void 0 ? void 0 : appCredentials.appStoreConnectApiKeyForSubmissions);
|
|
83
83
|
}
|
|
@@ -14,7 +14,7 @@ exports.PROMPT_FOR_APP_SPECIFIC_PASSWORD = 'PROMPT_FOR_APP_SPECIFIC_PASSWORD';
|
|
|
14
14
|
class SetUpSubmissionCredentials {
|
|
15
15
|
constructor(app) {
|
|
16
16
|
this.setupAscApiKeyAction = new SetUpAscApiKey_1.SetUpAscApiKey(app, AscApiKeyUtils_1.AppStoreApiKeyPurpose.SUBMISSION_SERVICE);
|
|
17
|
-
// Add this unrelated choice to ASC
|
|
17
|
+
// Add this unrelated choice to ASC API Key setup for legacy purposes -- we will deprecate it soon
|
|
18
18
|
this.setupAscApiKeyAction.choices = this.setupAscApiKeyAction.choices.concat({
|
|
19
19
|
title: '[Enter an App Specific Password]',
|
|
20
20
|
value: exports.PROMPT_FOR_APP_SPECIFIC_PASSWORD,
|
|
@@ -23,8 +23,8 @@ class SetUpSubmissionCredentials {
|
|
|
23
23
|
async runAsync(ctx) {
|
|
24
24
|
try {
|
|
25
25
|
const iosAppCredentials = await this.setupAscApiKeyAction.runAsync(ctx);
|
|
26
|
-
const { keyIdentifier, name } = (0, nullthrows_1.default)(iosAppCredentials.appStoreConnectApiKeyForSubmissions, 'ASC
|
|
27
|
-
log_1.default.log(`Using
|
|
26
|
+
const { keyIdentifier, name } = (0, nullthrows_1.default)(iosAppCredentials.appStoreConnectApiKeyForSubmissions, 'ASC API Key must be defined for EAS Submit');
|
|
27
|
+
log_1.default.log(`Using API Key ID: ${keyIdentifier}${name ? ` (${name})` : ''}`);
|
|
28
28
|
return iosAppCredentials;
|
|
29
29
|
}
|
|
30
30
|
catch (e) {
|
|
@@ -7,26 +7,26 @@ const log_1 = (0, tslib_1.__importDefault)(require("../../../log"));
|
|
|
7
7
|
const ora_1 = require("../../../ora");
|
|
8
8
|
const authenticate_1 = require("./authenticate");
|
|
9
9
|
async function listAscApiKeysAsync(authCtx) {
|
|
10
|
-
const spinner = (0, ora_1.ora)(`Fetching App Store Connect
|
|
10
|
+
const spinner = (0, ora_1.ora)(`Fetching App Store Connect API Keys.`).start();
|
|
11
11
|
try {
|
|
12
12
|
const context = (0, authenticate_1.getRequestContext)(authCtx);
|
|
13
13
|
const keys = await apple_utils_1.ApiKey.getAsync(context);
|
|
14
|
-
spinner.succeed(`Fetched App Store Connect
|
|
14
|
+
spinner.succeed(`Fetched App Store Connect API Keys.`);
|
|
15
15
|
return keys.map(key => getAscApiKeyInfo(key, authCtx));
|
|
16
16
|
}
|
|
17
17
|
catch (error) {
|
|
18
|
-
spinner.fail(`Failed to fetch App Store Connect
|
|
18
|
+
spinner.fail(`Failed to fetch App Store Connect API Keys.`);
|
|
19
19
|
throw error;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
exports.listAscApiKeysAsync = listAscApiKeysAsync;
|
|
23
23
|
async function getAscApiKeyAsync(authCtx, keyId) {
|
|
24
24
|
var _a;
|
|
25
|
-
const spinner = (0, ora_1.ora)(`Fetching App Store Connect
|
|
25
|
+
const spinner = (0, ora_1.ora)(`Fetching App Store Connect API Key.`).start();
|
|
26
26
|
try {
|
|
27
27
|
const context = (0, authenticate_1.getRequestContext)(authCtx);
|
|
28
28
|
const apiKey = await apple_utils_1.ApiKey.infoAsync(context, { id: keyId });
|
|
29
|
-
spinner.succeed(`Fetched App Store Connect
|
|
29
|
+
spinner.succeed(`Fetched App Store Connect API Key (ID: ${keyId}).`);
|
|
30
30
|
return getAscApiKeyInfo(apiKey, authCtx);
|
|
31
31
|
}
|
|
32
32
|
catch (error) {
|
|
@@ -36,13 +36,13 @@ async function getAscApiKeyAsync(authCtx, keyId) {
|
|
|
36
36
|
return null;
|
|
37
37
|
}
|
|
38
38
|
log_1.default.error(error);
|
|
39
|
-
spinner.fail(`Failed to fetch App Store Connect
|
|
39
|
+
spinner.fail(`Failed to fetch App Store Connect API Key.`);
|
|
40
40
|
throw error;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
exports.getAscApiKeyAsync = getAscApiKeyAsync;
|
|
44
44
|
async function createAscApiKeyAsync(authCtx, { nickname, allAppsVisible, roles, keyType, }) {
|
|
45
|
-
const spinner = (0, ora_1.ora)(`Creating App Store Connect
|
|
45
|
+
const spinner = (0, ora_1.ora)(`Creating App Store Connect API Key.`).start();
|
|
46
46
|
try {
|
|
47
47
|
const context = (0, authenticate_1.getRequestContext)(authCtx);
|
|
48
48
|
const key = await apple_utils_1.ApiKey.createAsync(context, {
|
|
@@ -66,30 +66,30 @@ async function createAscApiKeyAsync(authCtx, { nickname, allAppsVisible, roles,
|
|
|
66
66
|
}
|
|
67
67
|
// this object has more optional parameters populated
|
|
68
68
|
const fullKey = await apple_utils_1.ApiKey.infoAsync(context, { id: key.id });
|
|
69
|
-
spinner.succeed(`Created App Store Connect
|
|
69
|
+
spinner.succeed(`Created App Store Connect API Key.`);
|
|
70
70
|
return {
|
|
71
71
|
...getAscApiKeyInfo(fullKey, authCtx),
|
|
72
72
|
keyP8,
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
75
|
catch (err) {
|
|
76
|
-
spinner.fail('Failed to create App Store Connect
|
|
76
|
+
spinner.fail('Failed to create App Store Connect API Key.');
|
|
77
77
|
throw err;
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
exports.createAscApiKeyAsync = createAscApiKeyAsync;
|
|
81
81
|
async function revokeAscApiKeyAsync(authCtx, keyId) {
|
|
82
|
-
const spinner = (0, ora_1.ora)(`Revoking App Store Connect
|
|
82
|
+
const spinner = (0, ora_1.ora)(`Revoking App Store Connect API Key.`).start();
|
|
83
83
|
try {
|
|
84
84
|
const context = (0, authenticate_1.getRequestContext)(authCtx);
|
|
85
85
|
const apiKey = await apple_utils_1.ApiKey.infoAsync(context, { id: keyId });
|
|
86
86
|
const revokedKey = await apiKey.revokeAsync();
|
|
87
|
-
spinner.succeed(`Revoked App Store Connect
|
|
87
|
+
spinner.succeed(`Revoked App Store Connect API Key.`);
|
|
88
88
|
return getAscApiKeyInfo(revokedKey, authCtx);
|
|
89
89
|
}
|
|
90
90
|
catch (error) {
|
|
91
91
|
log_1.default.error(error);
|
|
92
|
-
spinner.fail(`Failed to revoke App Store Connect
|
|
92
|
+
spinner.fail(`Failed to revoke App Store Connect API Key.`);
|
|
93
93
|
throw error;
|
|
94
94
|
}
|
|
95
95
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Workflow } from '@expo/eas-build-job';
|
|
2
2
|
import { JSONObject } from '@expo/json-file';
|
|
3
|
-
export declare function getManagedEntitlementsJsonAsync(projectDir: string): Promise<JSONObject>;
|
|
4
|
-
export declare function resolveEntitlementsJsonAsync(projectDir: string, workflow: Workflow): Promise<JSONObject>;
|
|
3
|
+
export declare function getManagedEntitlementsJsonAsync(projectDir: string, env: Record<string, string>): Promise<JSONObject>;
|
|
4
|
+
export declare function resolveEntitlementsJsonAsync(projectDir: string, workflow: Workflow, env: Record<string, string>): Promise<JSONObject>;
|
|
@@ -7,23 +7,33 @@ const eas_build_job_1 = require("@expo/eas-build-job");
|
|
|
7
7
|
const plist_1 = (0, tslib_1.__importDefault)(require("@expo/plist"));
|
|
8
8
|
const prebuild_config_1 = require("@expo/prebuild-config");
|
|
9
9
|
const fs_extra_1 = (0, tslib_1.__importDefault)(require("fs-extra"));
|
|
10
|
-
async function getManagedEntitlementsJsonAsync(projectDir) {
|
|
10
|
+
async function getManagedEntitlementsJsonAsync(projectDir, env) {
|
|
11
11
|
var _a;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
const originalProcessEnv = process.env;
|
|
13
|
+
try {
|
|
14
|
+
process.env = {
|
|
15
|
+
...process.env,
|
|
16
|
+
...env,
|
|
17
|
+
};
|
|
18
|
+
const { exp } = (0, prebuild_config_1.getPrebuildConfig)(projectDir, { platforms: ['ios'] });
|
|
19
|
+
const expWithMods = await (0, config_plugins_1.compileModsAsync)(exp, {
|
|
20
|
+
projectRoot: projectDir,
|
|
21
|
+
platforms: ['ios'],
|
|
22
|
+
introspect: true,
|
|
23
|
+
});
|
|
24
|
+
return ((_a = expWithMods.ios) === null || _a === void 0 ? void 0 : _a.entitlements) || {};
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
process.env = originalProcessEnv;
|
|
28
|
+
}
|
|
19
29
|
}
|
|
20
30
|
exports.getManagedEntitlementsJsonAsync = getManagedEntitlementsJsonAsync;
|
|
21
|
-
async function resolveEntitlementsJsonAsync(projectDir, workflow) {
|
|
31
|
+
async function resolveEntitlementsJsonAsync(projectDir, workflow, env) {
|
|
22
32
|
if (workflow === eas_build_job_1.Workflow.GENERIC) {
|
|
23
33
|
return (await getEntitlementsJsonAsync(projectDir)) || {};
|
|
24
34
|
}
|
|
25
35
|
else if (workflow === eas_build_job_1.Workflow.MANAGED) {
|
|
26
|
-
return await getManagedEntitlementsJsonAsync(projectDir);
|
|
36
|
+
return await getManagedEntitlementsJsonAsync(projectDir, env);
|
|
27
37
|
}
|
|
28
38
|
else {
|
|
29
39
|
throw new Error(`Unknown workflow: ${workflow}`);
|
|
@@ -51,7 +51,7 @@ exports.distributionCertificateSchema = {
|
|
|
51
51
|
},
|
|
52
52
|
};
|
|
53
53
|
exports.ascApiKeyIdSchema = {
|
|
54
|
-
name: 'App Store Connect
|
|
54
|
+
name: 'App Store Connect API Key',
|
|
55
55
|
questions: [
|
|
56
56
|
{
|
|
57
57
|
field: 'keyId',
|
|
@@ -61,7 +61,7 @@ exports.ascApiKeyIdSchema = {
|
|
|
61
61
|
],
|
|
62
62
|
};
|
|
63
63
|
exports.ascApiKeyIssuerIdSchema = {
|
|
64
|
-
name: 'App Store Connect
|
|
64
|
+
name: 'App Store Connect API Key',
|
|
65
65
|
questions: [
|
|
66
66
|
{
|
|
67
67
|
field: 'issuerId',
|
|
@@ -198,7 +198,7 @@ function displayApplePushKey(maybePushKey, fields) {
|
|
|
198
198
|
fields.push({ label: '', value: '' });
|
|
199
199
|
}
|
|
200
200
|
function displayAscApiKey(maybeAscApiKey, fields) {
|
|
201
|
-
fields.push({ label: 'App Store Connect
|
|
201
|
+
fields.push({ label: 'App Store Connect API Key', value: '' });
|
|
202
202
|
if (maybeAscApiKey) {
|
|
203
203
|
const { keyIdentifier, issuerIdentifier, appleTeam, name, roles, updatedAt } = maybeAscApiKey;
|
|
204
204
|
fields.push({ label: 'Developer Portal ID', value: keyIdentifier });
|
|
@@ -10,7 +10,7 @@ exports.highLevelActions = [
|
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
value: Actions_1.AndroidActionType.ManageFcm,
|
|
13
|
-
title: 'Push Notifications: Manage your FCM
|
|
13
|
+
title: 'Push Notifications: Manage your FCM API Key',
|
|
14
14
|
scope: Actions_1.Scope.Manager,
|
|
15
15
|
},
|
|
16
16
|
{
|
|
@@ -71,12 +71,12 @@ exports.buildCredentialsActions = [
|
|
|
71
71
|
exports.fcmActions = [
|
|
72
72
|
{
|
|
73
73
|
value: Actions_1.AndroidActionType.CreateFcm,
|
|
74
|
-
title: 'Upload an FCM
|
|
74
|
+
title: 'Upload an FCM API Key',
|
|
75
75
|
scope: Actions_1.Scope.Project,
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
78
|
value: Actions_1.AndroidActionType.RemoveFcm,
|
|
79
|
-
title: 'Delete your FCM
|
|
79
|
+
title: 'Delete your FCM API Key',
|
|
80
80
|
scope: Actions_1.Scope.Project,
|
|
81
81
|
},
|
|
82
82
|
{
|
|
@@ -15,7 +15,7 @@ exports.highLevelActions = [
|
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
value: Actions_1.IosActionType.ManageAscApiKey,
|
|
18
|
-
title: 'App Store Connect: Manage your
|
|
18
|
+
title: 'App Store Connect: Manage your API Key',
|
|
19
19
|
scope: Actions_1.Scope.Manager,
|
|
20
20
|
},
|
|
21
21
|
{
|
|
@@ -80,22 +80,22 @@ function getAscApiKeyActions(ctx) {
|
|
|
80
80
|
return [
|
|
81
81
|
{
|
|
82
82
|
value: Actions_1.IosActionType.SetUpAscApiKeyForSubmissions,
|
|
83
|
-
title: 'Set up your project to use an
|
|
83
|
+
title: 'Set up your project to use an API Key for EAS Submit',
|
|
84
84
|
scope: Actions_1.Scope.Project,
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
value: Actions_1.IosActionType.UseExistingAscApiKeyForSubmissions,
|
|
88
|
-
title: 'Use an existing
|
|
88
|
+
title: 'Use an existing API Key for EAS Submit',
|
|
89
89
|
scope: Actions_1.Scope.Project,
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
92
|
value: Actions_1.IosActionType.CreateAscApiKeyForSubmissions,
|
|
93
|
-
title: 'Add a new
|
|
93
|
+
title: 'Add a new API Key For EAS Submit',
|
|
94
94
|
scope: ctx.hasProjectContext ? Actions_1.Scope.Project : Actions_1.Scope.Account,
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
97
|
value: Actions_1.IosActionType.RemoveAscApiKey,
|
|
98
|
-
title: 'Delete an
|
|
98
|
+
title: 'Delete an API Key',
|
|
99
99
|
scope: Actions_1.Scope.Account,
|
|
100
100
|
},
|
|
101
101
|
{
|