eas-cli 0.31.1 → 0.32.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/commands/submit.js +1 -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/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/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/graphql/generated.d.ts +24 -1
- package/build/graphql/generated.js +2 -0
- package/build/log.d.ts +11 -1
- package/build/log.js +21 -10
- package/build/submit/ArchiveSource.d.ts +7 -2
- package/build/submit/ArchiveSource.js +91 -8
- 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 +51 -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/oclif.manifest.json +1 -1
- package/package.json +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.provisioningProfileSchema = exports.pushKeySchema = exports.distributionCertificateSchema = exports.getAppLookupParams = void 0;
|
|
3
|
+
exports.provisioningProfileSchema = exports.pushKeySchema = exports.ascApiKeyMetadataSchema = exports.distributionCertificateSchema = exports.getAppLookupParams = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const log_1 = (0, tslib_1.__importDefault)(require("../../log"));
|
|
6
6
|
const p12Certificate_1 = require("./utils/p12Certificate");
|
|
@@ -50,6 +50,21 @@ exports.distributionCertificateSchema = {
|
|
|
50
50
|
return answers;
|
|
51
51
|
},
|
|
52
52
|
};
|
|
53
|
+
exports.ascApiKeyMetadataSchema = {
|
|
54
|
+
name: 'App Store Connect API Key',
|
|
55
|
+
questions: [
|
|
56
|
+
{
|
|
57
|
+
field: 'keyId',
|
|
58
|
+
type: 'string',
|
|
59
|
+
question: 'Key ID:',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
field: 'issuerId',
|
|
63
|
+
type: 'string',
|
|
64
|
+
question: 'Issuer ID:',
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
53
68
|
exports.pushKeySchema = {
|
|
54
69
|
name: 'Apple Push Notifications service key',
|
|
55
70
|
questions: [
|
|
@@ -312,12 +312,15 @@ export declare type Offer = {
|
|
|
312
312
|
trialLength?: Maybe<Scalars['Int']>;
|
|
313
313
|
type: OfferType;
|
|
314
314
|
features?: Maybe<Array<Maybe<Feature>>>;
|
|
315
|
+
prerequisite?: Maybe<OfferPrerequisite>;
|
|
315
316
|
};
|
|
316
317
|
export declare enum OfferType {
|
|
317
318
|
/** Term subscription */
|
|
318
319
|
Subscription = "SUBSCRIPTION",
|
|
319
320
|
/** Advanced Purchase of Paid Resource */
|
|
320
|
-
Prepaid = "PREPAID"
|
|
321
|
+
Prepaid = "PREPAID",
|
|
322
|
+
/** Addon, or supplementary subscription */
|
|
323
|
+
Addon = "ADDON"
|
|
321
324
|
}
|
|
322
325
|
export declare enum Feature {
|
|
323
326
|
/** Top Tier Support */
|
|
@@ -329,6 +332,11 @@ export declare enum Feature {
|
|
|
329
332
|
/** Funds support for open source development */
|
|
330
333
|
OpenSource = "OPEN_SOURCE"
|
|
331
334
|
}
|
|
335
|
+
export declare type OfferPrerequisite = {
|
|
336
|
+
__typename?: 'OfferPrerequisite';
|
|
337
|
+
type: Scalars['String'];
|
|
338
|
+
stripeIds: Array<Scalars['String']>;
|
|
339
|
+
};
|
|
332
340
|
export declare type Snack = Project & {
|
|
333
341
|
__typename?: 'Snack';
|
|
334
342
|
id: Scalars['ID'];
|
|
@@ -1206,6 +1214,7 @@ export declare type UserPermission = {
|
|
|
1206
1214
|
};
|
|
1207
1215
|
export declare type Billing = {
|
|
1208
1216
|
__typename?: 'Billing';
|
|
1217
|
+
id: Scalars['ID'];
|
|
1209
1218
|
payment?: Maybe<PaymentDetails>;
|
|
1210
1219
|
subscription?: Maybe<SubscriptionDetails>;
|
|
1211
1220
|
/** History of invoices */
|
|
@@ -1236,6 +1245,8 @@ export declare type Address = {
|
|
|
1236
1245
|
export declare type SubscriptionDetails = {
|
|
1237
1246
|
__typename?: 'SubscriptionDetails';
|
|
1238
1247
|
id: Scalars['ID'];
|
|
1248
|
+
planId?: Maybe<Scalars['String']>;
|
|
1249
|
+
addons: Array<AddonDetails>;
|
|
1239
1250
|
name?: Maybe<Scalars['String']>;
|
|
1240
1251
|
nextInvoice?: Maybe<Scalars['DateTime']>;
|
|
1241
1252
|
cancelledAt?: Maybe<Scalars['DateTime']>;
|
|
@@ -1244,6 +1255,12 @@ export declare type SubscriptionDetails = {
|
|
|
1244
1255
|
trialEnd?: Maybe<Scalars['DateTime']>;
|
|
1245
1256
|
status?: Maybe<Scalars['String']>;
|
|
1246
1257
|
};
|
|
1258
|
+
export declare type AddonDetails = {
|
|
1259
|
+
__typename?: 'AddonDetails';
|
|
1260
|
+
id: Scalars['ID'];
|
|
1261
|
+
planId: Scalars['String'];
|
|
1262
|
+
name: Scalars['String'];
|
|
1263
|
+
};
|
|
1247
1264
|
export declare type Charge = {
|
|
1248
1265
|
__typename?: 'Charge';
|
|
1249
1266
|
id: Scalars['ID'];
|
|
@@ -2473,10 +2490,16 @@ export declare type CreateIosSubmissionInput = {
|
|
|
2473
2490
|
export declare type IosSubmissionConfigInput = {
|
|
2474
2491
|
appleAppSpecificPasswordId?: Maybe<Scalars['String']>;
|
|
2475
2492
|
appleAppSpecificPassword?: Maybe<Scalars['String']>;
|
|
2493
|
+
ascApiKey?: Maybe<AscApiKeyInput>;
|
|
2476
2494
|
archiveUrl?: Maybe<Scalars['String']>;
|
|
2477
2495
|
appleIdUsername: Scalars['String'];
|
|
2478
2496
|
ascAppIdentifier: Scalars['String'];
|
|
2479
2497
|
};
|
|
2498
|
+
export declare type AscApiKeyInput = {
|
|
2499
|
+
keyP8: Scalars['String'];
|
|
2500
|
+
keyIdentifier: Scalars['String'];
|
|
2501
|
+
issuerIdentifier: Scalars['String'];
|
|
2502
|
+
};
|
|
2480
2503
|
export declare type CreateAndroidSubmissionInput = {
|
|
2481
2504
|
appId: Scalars['ID'];
|
|
2482
2505
|
config: AndroidSubmissionConfigInput;
|
|
@@ -13,6 +13,8 @@ var OfferType;
|
|
|
13
13
|
OfferType["Subscription"] = "SUBSCRIPTION";
|
|
14
14
|
/** Advanced Purchase of Paid Resource */
|
|
15
15
|
OfferType["Prepaid"] = "PREPAID";
|
|
16
|
+
/** Addon, or supplementary subscription */
|
|
17
|
+
OfferType["Addon"] = "ADDON";
|
|
16
18
|
})(OfferType = exports.OfferType || (exports.OfferType = {}));
|
|
17
19
|
var Feature;
|
|
18
20
|
(function (Feature) {
|
package/build/log.d.ts
CHANGED
|
@@ -18,10 +18,20 @@ export default class Log {
|
|
|
18
18
|
private static updateIsLastLineNewLine;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
+
* Prints a link for given URL, using text if provided, otherwise text is just the URL.
|
|
22
|
+
* Format links as dim (unless disabled) and with an underline.
|
|
23
|
+
*
|
|
24
|
+
* @example https://expo.dev
|
|
25
|
+
*/
|
|
26
|
+
export declare function link(url: string, { text, dim }?: {
|
|
27
|
+
text?: string;
|
|
28
|
+
dim?: boolean;
|
|
29
|
+
}): string;
|
|
30
|
+
/**
|
|
31
|
+
* Provide a consistent "Learn more" link experience.
|
|
21
32
|
* Format links as dim (unless disabled) with an underline.
|
|
22
33
|
*
|
|
23
34
|
* @example Learn more: https://expo.dev
|
|
24
|
-
* @param url
|
|
25
35
|
*/
|
|
26
36
|
export declare function learnMore(url: string, { learnMoreMessage: maybeLearnMoreMessage, dim, }?: {
|
|
27
37
|
learnMoreMessage?: string;
|
package/build/log.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.learnMore = void 0;
|
|
3
|
+
exports.learnMore = exports.link = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const chalk_1 = (0, tslib_1.__importDefault)(require("chalk"));
|
|
6
6
|
const figures_1 = (0, tslib_1.__importDefault)(require("figures"));
|
|
@@ -79,19 +79,30 @@ exports.default = Log;
|
|
|
79
79
|
Log.isDebug = (0, getenv_1.boolish)('EXPO_DEBUG', false);
|
|
80
80
|
Log.isLastLineNewLine = false;
|
|
81
81
|
/**
|
|
82
|
-
*
|
|
82
|
+
* Prints a link for given URL, using text if provided, otherwise text is just the URL.
|
|
83
|
+
* Format links as dim (unless disabled) and with an underline.
|
|
83
84
|
*
|
|
84
|
-
* @example
|
|
85
|
-
* @param url
|
|
85
|
+
* @example https://expo.dev
|
|
86
86
|
*/
|
|
87
|
-
function
|
|
87
|
+
function link(url, { text = url, dim = true } = {}) {
|
|
88
|
+
let output;
|
|
88
89
|
// Links can be disabled via env variables https://github.com/jamestalmage/supports-hyperlinks/blob/master/index.js
|
|
89
90
|
if (terminal_link_1.default.isSupported) {
|
|
90
|
-
|
|
91
|
-
return dim ? chalk_1.default.dim(text) : text;
|
|
91
|
+
output = (0, terminal_link_1.default)(text, url);
|
|
92
92
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
else {
|
|
94
|
+
output = `${text === url ? '' : text + ': '}${chalk_1.default.underline(url)}`;
|
|
95
|
+
}
|
|
96
|
+
return dim ? chalk_1.default.dim(output) : output;
|
|
97
|
+
}
|
|
98
|
+
exports.link = link;
|
|
99
|
+
/**
|
|
100
|
+
* Provide a consistent "Learn more" link experience.
|
|
101
|
+
* Format links as dim (unless disabled) with an underline.
|
|
102
|
+
*
|
|
103
|
+
* @example Learn more: https://expo.dev
|
|
104
|
+
*/
|
|
105
|
+
function learnMore(url, { learnMoreMessage: maybeLearnMoreMessage, dim = true, } = {}) {
|
|
106
|
+
return link(url, { text: maybeLearnMoreMessage !== null && maybeLearnMoreMessage !== void 0 ? maybeLearnMoreMessage : 'Learn more', dim });
|
|
96
107
|
}
|
|
97
108
|
exports.learnMore = learnMore;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { Platform } from '@expo/eas-build-job';
|
|
2
2
|
import { BuildFragment } from '../graphql/generated';
|
|
3
|
+
export declare const BUILD_LIST_ITEM_COUNT = 4;
|
|
3
4
|
export declare enum ArchiveSourceType {
|
|
4
5
|
url = 0,
|
|
5
6
|
latest = 1,
|
|
6
7
|
path = 2,
|
|
7
8
|
buildId = 3,
|
|
8
|
-
|
|
9
|
+
buildList = 4,
|
|
10
|
+
prompt = 5
|
|
9
11
|
}
|
|
10
12
|
interface ArchiveSourceBase {
|
|
11
13
|
sourceType: ArchiveSourceType;
|
|
@@ -28,6 +30,9 @@ interface ArchiveBuildIdSource extends ArchiveSourceBase {
|
|
|
28
30
|
sourceType: ArchiveSourceType.buildId;
|
|
29
31
|
id: string;
|
|
30
32
|
}
|
|
33
|
+
interface ArchiveBuildListSource extends ArchiveSourceBase {
|
|
34
|
+
sourceType: ArchiveSourceType.buildList;
|
|
35
|
+
}
|
|
31
36
|
interface ArchivePromptSource extends ArchiveSourceBase {
|
|
32
37
|
sourceType: ArchiveSourceType.prompt;
|
|
33
38
|
}
|
|
@@ -36,7 +41,7 @@ export interface Archive {
|
|
|
36
41
|
source: ArchiveSource;
|
|
37
42
|
url?: string;
|
|
38
43
|
}
|
|
39
|
-
export declare type ArchiveSource = ArchiveUrlSource | ArchiveLatestSource | ArchivePathSource | ArchiveBuildIdSource | ArchivePromptSource;
|
|
44
|
+
export declare type ArchiveSource = ArchiveUrlSource | ArchiveLatestSource | ArchivePathSource | ArchiveBuildIdSource | ArchiveBuildListSource | ArchivePromptSource;
|
|
40
45
|
export declare function getArchiveAsync(source: ArchiveSource): Promise<Archive>;
|
|
41
46
|
export declare function isUuidV4(s: string): boolean;
|
|
42
47
|
export {};
|
|
@@ -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,8 +218,8 @@ 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: 'Selected build from EAS',
|
|
222
|
+
value: ArchiveSourceType.buildList,
|
|
140
223
|
},
|
|
141
224
|
{ title: 'I have a url to the app archive', value: ArchiveSourceType.url },
|
|
142
225
|
{
|
|
@@ -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));
|
|
@@ -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>;
|