eas-cli 0.31.0 → 0.33.1
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 +29 -28
- package/build/build/android/UpdatesModule.js +4 -15
- package/build/build/android/configure.js +1 -1
- package/build/build/configure.js +12 -6
- package/build/build/ios/UpdatesModule.js +4 -16
- package/build/build/ios/configure.js +1 -1
- package/build/build/utils/appJson.d.ts +1 -0
- package/build/build/utils/appJson.js +13 -4
- package/build/build/utils/devClient.d.ts +4 -4
- package/build/build/utils/devClient.js +8 -16
- package/build/commands/branch/publish.js +14 -43
- package/build/commands/build/index.d.ts +1 -1
- package/build/commands/build/index.js +29 -24
- package/build/commands/submit.js +16 -11
- package/build/credentials/android/actions/SetupGoogleServiceAccountKey.js +3 -3
- package/build/credentials/context.d.ts +1 -1
- package/build/credentials/context.js +5 -5
- package/build/credentials/errors.d.ts +3 -0
- package/build/credentials/errors.js +7 -1
- package/build/credentials/ios/IosCredentialsProvider.js +1 -1
- package/build/credentials/ios/actions/AscApiKeyUtils.d.ts +5 -0
- package/build/credentials/ios/actions/AscApiKeyUtils.js +63 -0
- package/build/credentials/ios/actions/DistributionCertificateUtils.js +5 -5
- package/build/credentials/ios/actions/SetupTargetBuildCredentials.js +1 -1
- package/build/credentials/ios/appstore/AppStoreApi.d.ts +7 -1
- package/build/credentials/ios/appstore/AppStoreApi.js +17 -0
- package/build/credentials/ios/appstore/Credentials.js +3 -3
- package/build/credentials/ios/appstore/Credentials.types.d.ts +13 -0
- package/build/credentials/ios/appstore/ascApiKey.d.ts +9 -0
- package/build/credentials/ios/appstore/ascApiKey.js +99 -0
- package/build/credentials/ios/credentials.d.ts +17 -0
- package/build/credentials/ios/credentials.js +16 -1
- package/build/credentials/manager/ManageAndroid.js +1 -1
- package/build/credentials/manager/ManageIos.js +1 -1
- package/build/graphql/generated.d.ts +49 -113
- package/build/graphql/generated.js +24 -28
- package/build/log.d.ts +11 -1
- package/build/log.js +21 -10
- package/build/project/android/applicationId.d.ts +1 -1
- package/build/project/android/applicationId.js +7 -6
- package/build/project/ios/bundleIdentifier.d.ts +1 -1
- package/build/project/ios/bundleIdentifier.js +7 -6
- package/build/submit/ArchiveSource.d.ts +7 -2
- package/build/submit/ArchiveSource.js +94 -11
- package/build/submit/ios/AscApiKeySource.d.ts +28 -0
- package/build/submit/ios/AscApiKeySource.js +51 -0
- package/build/submit/ios/IosSubmitCommand.d.ts +2 -0
- package/build/submit/ios/IosSubmitCommand.js +55 -3
- package/build/submit/ios/IosSubmitter.d.ts +3 -1
- package/build/submit/ios/IosSubmitter.js +48 -4
- package/build/submit/submit.js +1 -1
- package/build/submit/utils/builds.d.ts +3 -1
- package/build/submit/utils/builds.js +6 -6
- package/build/utils/profiles.d.ts +11 -0
- package/build/utils/profiles.js +39 -0
- package/oclif.manifest.json +1 -1
- package/package.json +6 -6
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isUuidV4 = exports.getArchiveAsync = exports.ArchiveSourceType = void 0;
|
|
3
|
+
exports.isUuidV4 = exports.getArchiveAsync = exports.ArchiveSourceType = exports.BUILD_LIST_ITEM_COUNT = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
6
6
|
const chalk_1 = (0, tslib_1.__importDefault)(require("chalk"));
|
|
7
7
|
const url_1 = require("url");
|
|
8
8
|
const uuid = (0, tslib_1.__importStar)(require("uuid"));
|
|
9
|
+
const generated_1 = require("../graphql/generated");
|
|
9
10
|
const BuildQuery_1 = require("../graphql/queries/BuildQuery");
|
|
10
11
|
const AppPlatform_1 = require("../graphql/types/AppPlatform");
|
|
11
12
|
const log_1 = (0, tslib_1.__importStar)(require("../log"));
|
|
@@ -13,13 +14,15 @@ const platform_1 = require("../platform");
|
|
|
13
14
|
const prompts_1 = require("../prompts");
|
|
14
15
|
const builds_1 = require("./utils/builds");
|
|
15
16
|
const files_1 = require("./utils/files");
|
|
17
|
+
exports.BUILD_LIST_ITEM_COUNT = 4;
|
|
16
18
|
var ArchiveSourceType;
|
|
17
19
|
(function (ArchiveSourceType) {
|
|
18
20
|
ArchiveSourceType[ArchiveSourceType["url"] = 0] = "url";
|
|
19
21
|
ArchiveSourceType[ArchiveSourceType["latest"] = 1] = "latest";
|
|
20
22
|
ArchiveSourceType[ArchiveSourceType["path"] = 2] = "path";
|
|
21
23
|
ArchiveSourceType[ArchiveSourceType["buildId"] = 3] = "buildId";
|
|
22
|
-
ArchiveSourceType[ArchiveSourceType["
|
|
24
|
+
ArchiveSourceType[ArchiveSourceType["buildList"] = 4] = "buildList";
|
|
25
|
+
ArchiveSourceType[ArchiveSourceType["prompt"] = 5] = "prompt";
|
|
23
26
|
})(ArchiveSourceType = exports.ArchiveSourceType || (exports.ArchiveSourceType = {}));
|
|
24
27
|
async function getArchiveAsync(source) {
|
|
25
28
|
switch (source.sourceType) {
|
|
@@ -38,6 +41,9 @@ async function getArchiveAsync(source) {
|
|
|
38
41
|
case ArchiveSourceType.buildId: {
|
|
39
42
|
return await handleBuildIdSourceAsync(source);
|
|
40
43
|
}
|
|
44
|
+
case ArchiveSourceType.buildList: {
|
|
45
|
+
return await handleBuildListSourceAsync(source);
|
|
46
|
+
}
|
|
41
47
|
}
|
|
42
48
|
}
|
|
43
49
|
exports.getArchiveAsync = getArchiveAsync;
|
|
@@ -67,7 +73,7 @@ async function handleUrlSourceAsync(source) {
|
|
|
67
73
|
}
|
|
68
74
|
async function handleLatestSourceAsync(source) {
|
|
69
75
|
try {
|
|
70
|
-
const latestBuild = await (0, builds_1.
|
|
76
|
+
const [latestBuild] = await (0, builds_1.getRecentBuildsForSubmissionAsync)((0, AppPlatform_1.toAppPlatform)(source.platform), source.projectId);
|
|
71
77
|
if (!latestBuild) {
|
|
72
78
|
log_1.default.error(chalk_1.default.bold("Couldn't find any builds for this project on EAS servers. It looks like you haven't run 'eas build' yet."));
|
|
73
79
|
return getArchiveAsync({
|
|
@@ -128,6 +134,83 @@ async function handleBuildIdSourceAsync(source) {
|
|
|
128
134
|
});
|
|
129
135
|
}
|
|
130
136
|
}
|
|
137
|
+
async function handleBuildListSourceAsync(source) {
|
|
138
|
+
try {
|
|
139
|
+
const appPlatform = (0, AppPlatform_1.toAppPlatform)(source.platform);
|
|
140
|
+
const expiryDate = new Date(); // artifacts expire after 30 days
|
|
141
|
+
expiryDate.setDate(expiryDate.getDate() - 30);
|
|
142
|
+
const recentBuilds = await (0, builds_1.getRecentBuildsForSubmissionAsync)(appPlatform, source.projectId, {
|
|
143
|
+
limit: exports.BUILD_LIST_ITEM_COUNT,
|
|
144
|
+
});
|
|
145
|
+
if (recentBuilds.length < 1) {
|
|
146
|
+
log_1.default.error(chalk_1.default.bold(`Couldn't find any ${platform_1.appPlatformDisplayNames[appPlatform]} builds for this project on EAS servers. ` +
|
|
147
|
+
"It looks like you haven't run 'eas build' yet."));
|
|
148
|
+
return getArchiveAsync({
|
|
149
|
+
...source,
|
|
150
|
+
sourceType: ArchiveSourceType.prompt,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
if (recentBuilds.every(it => new Date(it.updatedAt) < expiryDate)) {
|
|
154
|
+
log_1.default.error(chalk_1.default.bold('It looks like all of your build artifacts have expired. ' +
|
|
155
|
+
'EAS keeps your build artifacts only for 30 days.'));
|
|
156
|
+
return getArchiveAsync({
|
|
157
|
+
...source,
|
|
158
|
+
sourceType: ArchiveSourceType.prompt,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
const choices = recentBuilds.map(build => formatBuildChoice(build, expiryDate));
|
|
162
|
+
choices.push({
|
|
163
|
+
title: 'None of the above (select another option)',
|
|
164
|
+
value: null,
|
|
165
|
+
});
|
|
166
|
+
const { selectedBuild } = await (0, prompts_1.promptAsync)({
|
|
167
|
+
name: 'selectedBuild',
|
|
168
|
+
type: 'select',
|
|
169
|
+
message: 'Which build would you like to submit?',
|
|
170
|
+
choices: choices.map(choice => ({ ...choice, title: `- ${choice.title}` })),
|
|
171
|
+
// @ts-expect-error field documented in npm, but not defined in typescript
|
|
172
|
+
warn: 'This artifact has expired',
|
|
173
|
+
});
|
|
174
|
+
if (selectedBuild == null) {
|
|
175
|
+
return getArchiveAsync({
|
|
176
|
+
...source,
|
|
177
|
+
sourceType: ArchiveSourceType.prompt,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
build: selectedBuild,
|
|
182
|
+
source,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
log_1.default.error(err);
|
|
187
|
+
throw err;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function formatBuildChoice(build, expiryDate) {
|
|
191
|
+
const { id, platform, updatedAt, appVersion, sdkVersion, runtimeVersion, buildProfile, appBuildVersion, releaseChannel, } = build;
|
|
192
|
+
const formatValue = (field) => field ? chalk_1.default.bold(field) : chalk_1.default.dim('Unknown');
|
|
193
|
+
const buildDate = new Date(updatedAt);
|
|
194
|
+
const maybeRuntimeVersion = runtimeVersion ? `Runtime: ${formatValue(runtimeVersion)}` : null;
|
|
195
|
+
const maybeSdkVersion = sdkVersion ? `SDK: ${formatValue(sdkVersion)}` : null;
|
|
196
|
+
const appBuildVersionString = `${platform === generated_1.AppPlatform.Android ? 'Version code' : 'Build number'}: ${formatValue(appBuildVersion)}`;
|
|
197
|
+
const title = [
|
|
198
|
+
`ID: ${chalk_1.default.dim(id)}, Finished at: ${chalk_1.default.bold(buildDate.toLocaleString())}`,
|
|
199
|
+
[
|
|
200
|
+
`\tApp version: ${formatValue(appVersion)}, ${appBuildVersionString}`,
|
|
201
|
+
maybeRuntimeVersion,
|
|
202
|
+
maybeSdkVersion,
|
|
203
|
+
]
|
|
204
|
+
.filter(it => it != null)
|
|
205
|
+
.join(', '),
|
|
206
|
+
`\tProfile: ${formatValue(buildProfile)}, Release channel: ${formatValue(releaseChannel)}`,
|
|
207
|
+
].join('\n');
|
|
208
|
+
return {
|
|
209
|
+
title,
|
|
210
|
+
value: build,
|
|
211
|
+
disabled: buildDate < expiryDate,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
131
214
|
async function handlePromptSourceAsync(source) {
|
|
132
215
|
const { sourceType: sourceTypeRaw } = await (0, prompts_1.promptAsync)({
|
|
133
216
|
name: 'sourceType',
|
|
@@ -135,16 +218,16 @@ async function handlePromptSourceAsync(source) {
|
|
|
135
218
|
message: 'What would you like to submit?',
|
|
136
219
|
choices: [
|
|
137
220
|
{
|
|
138
|
-
title: '
|
|
139
|
-
value: ArchiveSourceType.
|
|
221
|
+
title: 'Select a build from EAS',
|
|
222
|
+
value: ArchiveSourceType.buildList,
|
|
140
223
|
},
|
|
141
|
-
{ title: '
|
|
224
|
+
{ title: 'Provide a URL to the app archive', value: ArchiveSourceType.url },
|
|
142
225
|
{
|
|
143
|
-
title: '
|
|
226
|
+
title: 'Provide a path to a local app binary file',
|
|
144
227
|
value: ArchiveSourceType.path,
|
|
145
228
|
},
|
|
146
229
|
{
|
|
147
|
-
title: '
|
|
230
|
+
title: 'Provide a build ID to identify a build on EAS',
|
|
148
231
|
value: ArchiveSourceType.buildId,
|
|
149
232
|
},
|
|
150
233
|
],
|
|
@@ -167,10 +250,10 @@ async function handlePromptSourceAsync(source) {
|
|
|
167
250
|
path,
|
|
168
251
|
});
|
|
169
252
|
}
|
|
170
|
-
case ArchiveSourceType.
|
|
253
|
+
case ArchiveSourceType.buildList: {
|
|
171
254
|
return getArchiveAsync({
|
|
172
255
|
...source,
|
|
173
|
-
sourceType: ArchiveSourceType.
|
|
256
|
+
sourceType: ArchiveSourceType.buildList,
|
|
174
257
|
});
|
|
175
258
|
}
|
|
176
259
|
case ArchiveSourceType.buildId: {
|
|
@@ -181,7 +264,7 @@ async function handlePromptSourceAsync(source) {
|
|
|
181
264
|
id,
|
|
182
265
|
});
|
|
183
266
|
}
|
|
184
|
-
|
|
267
|
+
default:
|
|
185
268
|
throw new Error('This should never happen');
|
|
186
269
|
}
|
|
187
270
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AscApiKeyPath, MinimalAscApiKey } from '../../credentials/ios/credentials';
|
|
2
|
+
export declare enum AscApiKeySourceType {
|
|
3
|
+
path = 0,
|
|
4
|
+
prompt = 1
|
|
5
|
+
}
|
|
6
|
+
interface AscApiKeySourceBase {
|
|
7
|
+
sourceType: AscApiKeySourceType;
|
|
8
|
+
}
|
|
9
|
+
interface AscApiKeyPromptSource extends AscApiKeySourceBase {
|
|
10
|
+
sourceType: AscApiKeySourceType.prompt;
|
|
11
|
+
}
|
|
12
|
+
interface AscApiKeyEnvVarSource extends AscApiKeySourceBase {
|
|
13
|
+
sourceType: AscApiKeySourceType.path;
|
|
14
|
+
path: AscApiKeyPath;
|
|
15
|
+
}
|
|
16
|
+
export declare type AscApiKeySource = AscApiKeyEnvVarSource | AscApiKeyPromptSource;
|
|
17
|
+
declare type AscApiKeySummary = {
|
|
18
|
+
source: 'local' | 'EAS servers';
|
|
19
|
+
path?: string;
|
|
20
|
+
keyId: string;
|
|
21
|
+
};
|
|
22
|
+
export declare type AscApiKeyResult = {
|
|
23
|
+
result: MinimalAscApiKey;
|
|
24
|
+
summary: AscApiKeySummary;
|
|
25
|
+
};
|
|
26
|
+
export declare function getAscApiKeyLocallyAsync(source: AscApiKeySource): Promise<AscApiKeyResult>;
|
|
27
|
+
export declare function getAscApiKeyPathAsync(source: AscApiKeySource): Promise<AscApiKeyPath>;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAscApiKeyPathAsync = exports.getAscApiKeyLocallyAsync = exports.AscApiKeySourceType = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs_extra_1 = (0, tslib_1.__importDefault)(require("fs-extra"));
|
|
6
|
+
const AscApiKeyUtils_1 = require("../../credentials/ios/actions/AscApiKeyUtils");
|
|
7
|
+
const log_1 = (0, tslib_1.__importDefault)(require("../../log"));
|
|
8
|
+
const files_1 = require("../utils/files");
|
|
9
|
+
var AscApiKeySourceType;
|
|
10
|
+
(function (AscApiKeySourceType) {
|
|
11
|
+
AscApiKeySourceType[AscApiKeySourceType["path"] = 0] = "path";
|
|
12
|
+
AscApiKeySourceType[AscApiKeySourceType["prompt"] = 1] = "prompt";
|
|
13
|
+
})(AscApiKeySourceType = exports.AscApiKeySourceType || (exports.AscApiKeySourceType = {}));
|
|
14
|
+
async function getAscApiKeyLocallyAsync(source) {
|
|
15
|
+
const ascApiKeyPath = await getAscApiKeyPathAsync(source);
|
|
16
|
+
const { keyP8Path, keyId, issuerId } = ascApiKeyPath;
|
|
17
|
+
const keyP8 = await fs_extra_1.default.readFile(keyP8Path, 'utf-8');
|
|
18
|
+
return {
|
|
19
|
+
result: { keyP8, keyId, issuerId },
|
|
20
|
+
summary: {
|
|
21
|
+
source: 'local',
|
|
22
|
+
path: keyP8Path,
|
|
23
|
+
keyId,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
exports.getAscApiKeyLocallyAsync = getAscApiKeyLocallyAsync;
|
|
28
|
+
async function getAscApiKeyPathAsync(source) {
|
|
29
|
+
switch (source.sourceType) {
|
|
30
|
+
case AscApiKeySourceType.path:
|
|
31
|
+
return await handlePathSourceAsync(source);
|
|
32
|
+
case AscApiKeySourceType.prompt:
|
|
33
|
+
return await handlePromptSourceAsync(source);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.getAscApiKeyPathAsync = getAscApiKeyPathAsync;
|
|
37
|
+
async function handlePathSourceAsync(source) {
|
|
38
|
+
const { keyP8Path } = source.path;
|
|
39
|
+
if (!(await (0, files_1.isExistingFileAsync)(keyP8Path))) {
|
|
40
|
+
log_1.default.warn(`File ${keyP8Path} doesn't exist.`);
|
|
41
|
+
return await getAscApiKeyPathAsync({ sourceType: AscApiKeySourceType.prompt });
|
|
42
|
+
}
|
|
43
|
+
return source.path;
|
|
44
|
+
}
|
|
45
|
+
async function handlePromptSourceAsync(_source) {
|
|
46
|
+
const ascApiKeyPath = await (0, AscApiKeyUtils_1.promptForAscApiKeyAsync)();
|
|
47
|
+
return await getAscApiKeyPathAsync({
|
|
48
|
+
sourceType: AscApiKeySourceType.path,
|
|
49
|
+
path: ascApiKeyPath,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -5,8 +5,10 @@ export default class IosSubmitCommand {
|
|
|
5
5
|
private ctx;
|
|
6
6
|
constructor(ctx: SubmissionContext<Platform.IOS>);
|
|
7
7
|
runAsync(): Promise<SubmissionFragment>;
|
|
8
|
+
private resolveCredentialSubmissionOptionsAsync;
|
|
8
9
|
private resolveSubmissionOptionsAsync;
|
|
9
10
|
private resolveAppSpecificPasswordSource;
|
|
11
|
+
private resolveAscApiKeySource;
|
|
10
12
|
private resolveArchiveSource;
|
|
11
13
|
private resolveAscAppIdentifierAsync;
|
|
12
14
|
private resolveAppleIdUsernameAsync;
|
|
@@ -6,12 +6,14 @@ const results_1 = require("@expo/results");
|
|
|
6
6
|
const chalk_1 = (0, tslib_1.__importDefault)(require("chalk"));
|
|
7
7
|
const getenv_1 = (0, tslib_1.__importDefault)(require("getenv"));
|
|
8
8
|
const wrap_ansi_1 = (0, tslib_1.__importDefault)(require("wrap-ansi"));
|
|
9
|
+
const errors_1 = require("../../credentials/errors");
|
|
9
10
|
const log_1 = (0, tslib_1.__importStar)(require("../../log"));
|
|
10
11
|
const prompts_1 = require("../../prompts");
|
|
11
12
|
const UserSettings_1 = (0, tslib_1.__importDefault)(require("../../user/UserSettings"));
|
|
12
13
|
const commons_1 = require("../commons");
|
|
13
14
|
const AppProduce_1 = require("./AppProduce");
|
|
14
15
|
const AppSpecificPasswordSource_1 = require("./AppSpecificPasswordSource");
|
|
16
|
+
const AscApiKeySource_1 = require("./AscApiKeySource");
|
|
15
17
|
const IosSubmitter_1 = (0, tslib_1.__importDefault)(require("./IosSubmitter"));
|
|
16
18
|
class IosSubmitCommand {
|
|
17
19
|
constructor(ctx) {
|
|
@@ -23,14 +25,30 @@ class IosSubmitCommand {
|
|
|
23
25
|
const submitter = new IosSubmitter_1.default(this.ctx, options);
|
|
24
26
|
return await submitter.submitAsync();
|
|
25
27
|
}
|
|
28
|
+
async resolveCredentialSubmissionOptionsAsync() {
|
|
29
|
+
// Fall back to app specific password if no ascApiKey defined
|
|
30
|
+
const ascApiKeySource = this.resolveAscApiKeySource();
|
|
31
|
+
const shouldUseAppSpecificPassword = !ascApiKeySource.ok && ascApiKeySource.enforceError() instanceof errors_1.MissingCredentialsError;
|
|
32
|
+
if (shouldUseAppSpecificPassword) {
|
|
33
|
+
return { appSpecificPasswordSource: this.resolveAppSpecificPasswordSource() };
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
return { ascApiKeySource };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
26
39
|
async resolveSubmissionOptionsAsync() {
|
|
27
40
|
const archiveSource = this.resolveArchiveSource();
|
|
28
|
-
const
|
|
41
|
+
const credentialsSource = await this.resolveCredentialSubmissionOptionsAsync();
|
|
42
|
+
const maybeAppSpecificPasswordSource = 'appSpecificPasswordSource' in credentialsSource
|
|
43
|
+
? credentialsSource.appSpecificPasswordSource
|
|
44
|
+
: null;
|
|
45
|
+
const maybeAscApiKeySource = 'ascApiKeySource' in credentialsSource ? credentialsSource.ascApiKeySource : null;
|
|
29
46
|
const ascAppIdentifier = await this.resolveAscAppIdentifierAsync();
|
|
30
47
|
const appleIdUsername = await this.resolveAppleIdUsernameAsync();
|
|
31
48
|
const errored = [
|
|
32
49
|
archiveSource,
|
|
33
|
-
|
|
50
|
+
...(maybeAppSpecificPasswordSource ? [maybeAppSpecificPasswordSource] : []),
|
|
51
|
+
...(maybeAscApiKeySource ? [maybeAscApiKeySource] : []),
|
|
34
52
|
ascAppIdentifier,
|
|
35
53
|
appleIdUsername,
|
|
36
54
|
].filter(r => !r.ok);
|
|
@@ -44,7 +62,16 @@ class IosSubmitCommand {
|
|
|
44
62
|
appleIdUsername: appleIdUsername.enforceValue(),
|
|
45
63
|
ascAppIdentifier: ascAppIdentifier.enforceValue(),
|
|
46
64
|
archiveSource: archiveSource.enforceValue(),
|
|
47
|
-
|
|
65
|
+
...(maybeAppSpecificPasswordSource
|
|
66
|
+
? {
|
|
67
|
+
appSpecificPasswordSource: maybeAppSpecificPasswordSource.enforceValue(),
|
|
68
|
+
}
|
|
69
|
+
: null),
|
|
70
|
+
...(maybeAscApiKeySource
|
|
71
|
+
? {
|
|
72
|
+
ascApiKeySource: maybeAscApiKeySource.enforceValue(),
|
|
73
|
+
}
|
|
74
|
+
: null),
|
|
48
75
|
};
|
|
49
76
|
}
|
|
50
77
|
resolveAppSpecificPasswordSource() {
|
|
@@ -64,6 +91,27 @@ class IosSubmitCommand {
|
|
|
64
91
|
});
|
|
65
92
|
}
|
|
66
93
|
}
|
|
94
|
+
resolveAscApiKeySource() {
|
|
95
|
+
const { ascApiKeyPath, ascApiKeyIssuerId, ascApiKeyId } = this.ctx.profile;
|
|
96
|
+
if (ascApiKeyPath && ascApiKeyIssuerId && ascApiKeyId) {
|
|
97
|
+
return (0, results_1.result)({
|
|
98
|
+
sourceType: AscApiKeySource_1.AscApiKeySourceType.path,
|
|
99
|
+
path: {
|
|
100
|
+
keyP8Path: ascApiKeyPath,
|
|
101
|
+
issuerId: ascApiKeyIssuerId,
|
|
102
|
+
keyId: ascApiKeyId,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
// interpret this to mean the user had some intention of passing in ASC Api key
|
|
107
|
+
if (ascApiKeyPath || ascApiKeyIssuerId || ascApiKeyId) {
|
|
108
|
+
log_1.default.warn(`ascApiKeyPath, ascApiKeyIssuerId and ascApiKeyId must all be defined in eas.json`);
|
|
109
|
+
return (0, results_1.result)({
|
|
110
|
+
sourceType: AscApiKeySource_1.AscApiKeySourceType.prompt,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return (0, results_1.result)(new errors_1.MissingCredentialsError('Set the ascApiKeyPath, ascApiKeyIssuerId and ascApiKeyId fields in eas.json.'));
|
|
114
|
+
}
|
|
67
115
|
resolveArchiveSource() {
|
|
68
116
|
try {
|
|
69
117
|
return (0, results_1.result)((0, commons_1.resolveArchiveSource)(this.ctx, eas_build_job_1.Platform.IOS));
|
|
@@ -93,6 +141,7 @@ class IosSubmitCommand {
|
|
|
93
141
|
}
|
|
94
142
|
}
|
|
95
143
|
async resolveAppleIdUsernameAsync() {
|
|
144
|
+
var _a;
|
|
96
145
|
if (this.ctx.profile.appleId) {
|
|
97
146
|
return (0, results_1.result)(this.ctx.profile.appleId);
|
|
98
147
|
}
|
|
@@ -100,6 +149,9 @@ class IosSubmitCommand {
|
|
|
100
149
|
if (envAppleId) {
|
|
101
150
|
return (0, results_1.result)(envAppleId);
|
|
102
151
|
}
|
|
152
|
+
if ((_a = this.ctx.credentialsCtx.appStore.authCtx) === null || _a === void 0 ? void 0 : _a.appleId) {
|
|
153
|
+
return (0, results_1.result)(this.ctx.credentialsCtx.appStore.authCtx.appleId);
|
|
154
|
+
}
|
|
103
155
|
// Get the email address that was last used and set it as
|
|
104
156
|
// the default value for quicker authentication.
|
|
105
157
|
const lastAppleId = await UserSettings_1.default.getAsync('appleId', null);
|
|
@@ -3,10 +3,12 @@ import { IosSubmissionConfigInput, SubmissionFragment } from '../../graphql/gene
|
|
|
3
3
|
import { ArchiveSource } from '../ArchiveSource';
|
|
4
4
|
import BaseSubmitter, { SubmissionInput } from '../BaseSubmitter';
|
|
5
5
|
import { AppSpecificPasswordSource } from './AppSpecificPasswordSource';
|
|
6
|
+
import { AscApiKeySource } from './AscApiKeySource';
|
|
6
7
|
export interface IosSubmissionOptions extends Pick<IosSubmissionConfigInput, 'appleIdUsername' | 'ascAppIdentifier'> {
|
|
7
8
|
projectId: string;
|
|
8
9
|
archiveSource: ArchiveSource;
|
|
9
|
-
appSpecificPasswordSource
|
|
10
|
+
appSpecificPasswordSource?: AppSpecificPasswordSource;
|
|
11
|
+
ascApiKeySource?: AscApiKeySource;
|
|
10
12
|
}
|
|
11
13
|
export default class IosSubmitter extends BaseSubmitter<Platform.IOS, IosSubmissionOptions> {
|
|
12
14
|
submitAsync(): Promise<SubmissionFragment>;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
|
+
const chalk_1 = (0, tslib_1.__importDefault)(require("chalk"));
|
|
4
5
|
const SubmissionMutation_1 = require("../../graphql/mutations/SubmissionMutation");
|
|
6
|
+
const formatFields_1 = (0, tslib_1.__importDefault)(require("../../utils/formatFields"));
|
|
5
7
|
const ArchiveSource_1 = require("../ArchiveSource");
|
|
6
8
|
const BaseSubmitter_1 = (0, tslib_1.__importDefault)(require("../BaseSubmitter"));
|
|
7
9
|
const summary_1 = require("../utils/summary");
|
|
8
10
|
const AppSpecificPasswordSource_1 = require("./AppSpecificPasswordSource");
|
|
11
|
+
const AscApiKeySource_1 = require("./AscApiKeySource");
|
|
9
12
|
class IosSubmitter extends BaseSubmitter_1.default {
|
|
10
13
|
async submitAsync() {
|
|
11
14
|
var _a;
|
|
@@ -27,28 +30,46 @@ class IosSubmitter extends BaseSubmitter_1.default {
|
|
|
27
30
|
}
|
|
28
31
|
async resolveSourceOptionsAsync() {
|
|
29
32
|
const archive = await (0, ArchiveSource_1.getArchiveAsync)(this.options.archiveSource);
|
|
30
|
-
const
|
|
33
|
+
const maybeAppSpecificPassword = this.options.appSpecificPasswordSource
|
|
34
|
+
? await (0, AppSpecificPasswordSource_1.getAppSpecificPasswordAsync)(this.options.appSpecificPasswordSource)
|
|
35
|
+
: null;
|
|
36
|
+
const maybeAppStoreConnectApiKey = this.options.ascApiKeySource
|
|
37
|
+
? await (0, AscApiKeySource_1.getAscApiKeyLocallyAsync)(this.options.ascApiKeySource)
|
|
38
|
+
: null;
|
|
31
39
|
return {
|
|
32
40
|
archive,
|
|
33
|
-
appSpecificPassword,
|
|
41
|
+
...(maybeAppSpecificPassword ? { appSpecificPassword: maybeAppSpecificPassword } : null),
|
|
42
|
+
...(maybeAppStoreConnectApiKey ? { ascApiKeyResult: maybeAppStoreConnectApiKey } : null),
|
|
34
43
|
};
|
|
35
44
|
}
|
|
36
|
-
async formatSubmissionConfigAsync(options, { archive, appSpecificPassword }) {
|
|
45
|
+
async formatSubmissionConfigAsync(options, { archive, appSpecificPassword, ascApiKeyResult }) {
|
|
37
46
|
const { appleIdUsername, ascAppIdentifier } = options;
|
|
38
47
|
return {
|
|
39
48
|
ascAppIdentifier,
|
|
40
49
|
appleIdUsername,
|
|
41
50
|
archiveUrl: archive.url,
|
|
42
51
|
appleAppSpecificPassword: appSpecificPassword,
|
|
52
|
+
...((ascApiKeyResult === null || ascApiKeyResult === void 0 ? void 0 : ascApiKeyResult.result)
|
|
53
|
+
? {
|
|
54
|
+
ascApiKey: {
|
|
55
|
+
keyP8: ascApiKeyResult === null || ascApiKeyResult === void 0 ? void 0 : ascApiKeyResult.result.keyP8,
|
|
56
|
+
keyIdentifier: ascApiKeyResult === null || ascApiKeyResult === void 0 ? void 0 : ascApiKeyResult.result.keyId,
|
|
57
|
+
issuerIdentifier: ascApiKeyResult === null || ascApiKeyResult === void 0 ? void 0 : ascApiKeyResult.result.issuerId,
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
: null),
|
|
43
61
|
};
|
|
44
62
|
}
|
|
45
|
-
prepareSummaryData(options, { archive }) {
|
|
63
|
+
prepareSummaryData(options, { archive, ascApiKeyResult }) {
|
|
46
64
|
const { appleIdUsername, ascAppIdentifier, projectId } = options;
|
|
47
65
|
// structuring order affects table rows order
|
|
48
66
|
return {
|
|
49
67
|
ascAppIdentifier,
|
|
50
68
|
appleIdUsername,
|
|
51
69
|
projectId,
|
|
70
|
+
...(ascApiKeyResult
|
|
71
|
+
? { formattedAscApiKey: formatServiceAccountSummary(ascApiKeyResult) }
|
|
72
|
+
: null),
|
|
52
73
|
...(0, summary_1.formatArchiveSourceSummary)(archive),
|
|
53
74
|
};
|
|
54
75
|
}
|
|
@@ -61,4 +82,27 @@ const SummaryHumanReadableKeys = {
|
|
|
61
82
|
archiveUrl: 'Archive URL',
|
|
62
83
|
archivePath: 'Archive Path',
|
|
63
84
|
formattedBuild: 'Build',
|
|
85
|
+
formattedAscApiKey: 'App Store Connect Api Key',
|
|
64
86
|
};
|
|
87
|
+
function formatServiceAccountSummary({ summary }) {
|
|
88
|
+
const { source, path, keyId } = summary;
|
|
89
|
+
const fields = [
|
|
90
|
+
{
|
|
91
|
+
label: 'Key Source',
|
|
92
|
+
value: source,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
label: 'Key Path',
|
|
96
|
+
value: path,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
label: 'Key ID',
|
|
100
|
+
value: keyId,
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
const filteredFields = fields.filter(({ value }) => value !== undefined && value !== null);
|
|
104
|
+
return ('\n' +
|
|
105
|
+
(0, formatFields_1.default)(filteredFields, {
|
|
106
|
+
labelFormat: label => ` ${chalk_1.default.dim(label)}:`,
|
|
107
|
+
}));
|
|
108
|
+
}
|
package/build/submit/submit.js
CHANGED
|
@@ -58,7 +58,7 @@ function printInstructionsForIosSubmission(submission) {
|
|
|
58
58
|
'- It usually takes about 5-10 minutes depending on how busy Apple servers are.',
|
|
59
59
|
// ascAppIdentifier should be always available for ios submissions but check it anyway
|
|
60
60
|
((_a = submission.iosConfig) === null || _a === void 0 ? void 0 : _a.ascAppIdentifier) &&
|
|
61
|
-
`- When it’s done, you can see your build here: ${(0, log_1.
|
|
61
|
+
`- When it’s done, you can see your build here: ${(0, log_1.link)(`https://appstoreconnect.apple.com/apps/${(_b = submission.iosConfig) === null || _b === void 0 ? void 0 : _b.ascAppIdentifier}/appstore/ios`)}`,
|
|
62
62
|
].join('\n');
|
|
63
63
|
log_1.default.addNewLineIfNone();
|
|
64
64
|
log_1.default.log(logMsg);
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import { AppPlatform, BuildFragment } from '../../graphql/generated';
|
|
2
|
-
export declare function
|
|
2
|
+
export declare function getRecentBuildsForSubmissionAsync(platform: AppPlatform, appId: string, { limit }?: {
|
|
3
|
+
limit?: number;
|
|
4
|
+
}): Promise<BuildFragment[]>;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.getRecentBuildsForSubmissionAsync = void 0;
|
|
4
4
|
const generated_1 = require("../../graphql/generated");
|
|
5
5
|
const BuildQuery_1 = require("../../graphql/queries/BuildQuery");
|
|
6
|
-
async function
|
|
7
|
-
|
|
8
|
-
limit
|
|
6
|
+
async function getRecentBuildsForSubmissionAsync(platform, appId, { limit = 1 } = {}) {
|
|
7
|
+
return await BuildQuery_1.BuildQuery.allForAppAsync(appId, {
|
|
8
|
+
limit,
|
|
9
9
|
filter: {
|
|
10
10
|
platform,
|
|
11
|
+
distribution: generated_1.DistributionType.Store,
|
|
11
12
|
status: generated_1.BuildStatus.Finished,
|
|
12
13
|
},
|
|
13
14
|
});
|
|
14
|
-
return build;
|
|
15
15
|
}
|
|
16
|
-
exports.
|
|
16
|
+
exports.getRecentBuildsForSubmissionAsync = getRecentBuildsForSubmissionAsync;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Platform } from '@expo/eas-build-job';
|
|
2
|
+
export declare type ProfileData<T> = {
|
|
3
|
+
profile: T;
|
|
4
|
+
platform: Platform;
|
|
5
|
+
profileName: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function getProfilesAsync<T>({ platforms, profileName: profileNameArg, readProfileAsync, }: {
|
|
8
|
+
platforms: Platform[];
|
|
9
|
+
profileName?: string | null;
|
|
10
|
+
readProfileAsync: (platform: Platform, profileName: string) => Promise<T>;
|
|
11
|
+
}): Promise<ProfileData<T>[]>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getProfilesAsync = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const log_1 = (0, tslib_1.__importDefault)(require("../log"));
|
|
6
|
+
async function getProfilesAsync({ platforms, profileName: profileNameArg,
|
|
7
|
+
// eslint-disable-next-line async-protect/async-suffix
|
|
8
|
+
readProfileAsync, }) {
|
|
9
|
+
const results = platforms.map(async function (platform) {
|
|
10
|
+
let profile;
|
|
11
|
+
let profileName = profileNameArg;
|
|
12
|
+
if (!profileName) {
|
|
13
|
+
try {
|
|
14
|
+
profile = await readProfileAsync(platform, 'production');
|
|
15
|
+
profileName = 'production';
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
try {
|
|
19
|
+
profile = await readProfileAsync(platform, 'release');
|
|
20
|
+
profileName = 'release';
|
|
21
|
+
log_1.default.warn('The default profile changed from "release" to "production". We detected that you still have a "release" build profile, so we are using it. Update eas.json to have a profile named "production" under the `build` key, or specify which profile you\'d like to use with the --profile flag. This fallback behavior will be removed in the next major version of EAS CLI.');
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
throw new Error('There is no profile named "production" in eas.json');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
profile = await readProfileAsync(platform, profileName);
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
profile,
|
|
33
|
+
profileName,
|
|
34
|
+
platform,
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
return await Promise.all(results);
|
|
38
|
+
}
|
|
39
|
+
exports.getProfilesAsync = getProfilesAsync;
|