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.
Files changed (57) hide show
  1. package/README.md +29 -28
  2. package/build/build/android/UpdatesModule.js +4 -15
  3. package/build/build/android/configure.js +1 -1
  4. package/build/build/configure.js +12 -6
  5. package/build/build/ios/UpdatesModule.js +4 -16
  6. package/build/build/ios/configure.js +1 -1
  7. package/build/build/utils/appJson.d.ts +1 -0
  8. package/build/build/utils/appJson.js +13 -4
  9. package/build/build/utils/devClient.d.ts +4 -4
  10. package/build/build/utils/devClient.js +8 -16
  11. package/build/commands/branch/publish.js +14 -43
  12. package/build/commands/build/index.d.ts +1 -1
  13. package/build/commands/build/index.js +29 -24
  14. package/build/commands/submit.js +16 -11
  15. package/build/credentials/android/actions/SetupGoogleServiceAccountKey.js +3 -3
  16. package/build/credentials/context.d.ts +1 -1
  17. package/build/credentials/context.js +5 -5
  18. package/build/credentials/errors.d.ts +3 -0
  19. package/build/credentials/errors.js +7 -1
  20. package/build/credentials/ios/IosCredentialsProvider.js +1 -1
  21. package/build/credentials/ios/actions/AscApiKeyUtils.d.ts +5 -0
  22. package/build/credentials/ios/actions/AscApiKeyUtils.js +63 -0
  23. package/build/credentials/ios/actions/DistributionCertificateUtils.js +5 -5
  24. package/build/credentials/ios/actions/SetupTargetBuildCredentials.js +1 -1
  25. package/build/credentials/ios/appstore/AppStoreApi.d.ts +7 -1
  26. package/build/credentials/ios/appstore/AppStoreApi.js +17 -0
  27. package/build/credentials/ios/appstore/Credentials.js +3 -3
  28. package/build/credentials/ios/appstore/Credentials.types.d.ts +13 -0
  29. package/build/credentials/ios/appstore/ascApiKey.d.ts +9 -0
  30. package/build/credentials/ios/appstore/ascApiKey.js +99 -0
  31. package/build/credentials/ios/credentials.d.ts +17 -0
  32. package/build/credentials/ios/credentials.js +16 -1
  33. package/build/credentials/manager/ManageAndroid.js +1 -1
  34. package/build/credentials/manager/ManageIos.js +1 -1
  35. package/build/graphql/generated.d.ts +49 -113
  36. package/build/graphql/generated.js +24 -28
  37. package/build/log.d.ts +11 -1
  38. package/build/log.js +21 -10
  39. package/build/project/android/applicationId.d.ts +1 -1
  40. package/build/project/android/applicationId.js +7 -6
  41. package/build/project/ios/bundleIdentifier.d.ts +1 -1
  42. package/build/project/ios/bundleIdentifier.js +7 -6
  43. package/build/submit/ArchiveSource.d.ts +7 -2
  44. package/build/submit/ArchiveSource.js +94 -11
  45. package/build/submit/ios/AscApiKeySource.d.ts +28 -0
  46. package/build/submit/ios/AscApiKeySource.js +51 -0
  47. package/build/submit/ios/IosSubmitCommand.d.ts +2 -0
  48. package/build/submit/ios/IosSubmitCommand.js +55 -3
  49. package/build/submit/ios/IosSubmitter.d.ts +3 -1
  50. package/build/submit/ios/IosSubmitter.js +48 -4
  51. package/build/submit/submit.js +1 -1
  52. package/build/submit/utils/builds.d.ts +3 -1
  53. package/build/submit/utils/builds.js +6 -6
  54. package/build/utils/profiles.d.ts +11 -0
  55. package/build/utils/profiles.js +39 -0
  56. package/oclif.manifest.json +1 -1
  57. 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["prompt"] = 4] = "prompt";
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.getLatestBuildForSubmissionAsync)((0, AppPlatform_1.toAppPlatform)(source.platform), source.projectId);
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: 'Latest finished build from EAS',
139
- value: ArchiveSourceType.latest,
221
+ title: 'Select a build from EAS',
222
+ value: ArchiveSourceType.buildList,
140
223
  },
141
- { title: 'I have a url to the app archive', value: ArchiveSourceType.url },
224
+ { title: 'Provide a URL to the app archive', value: ArchiveSourceType.url },
142
225
  {
143
- title: 'Local app binary file',
226
+ title: 'Provide a path to a local app binary file',
144
227
  value: ArchiveSourceType.path,
145
228
  },
146
229
  {
147
- title: 'A build identified by a build ID',
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.latest: {
253
+ case ArchiveSourceType.buildList: {
171
254
  return getArchiveAsync({
172
255
  ...source,
173
- sourceType: ArchiveSourceType.latest,
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
- case ArchiveSourceType.prompt:
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 appSpecificPasswordSource = this.resolveAppSpecificPasswordSource();
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
- appSpecificPasswordSource,
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
- appSpecificPasswordSource: appSpecificPasswordSource.enforceValue(),
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: 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 appSpecificPassword = await (0, AppSpecificPasswordSource_1.getAppSpecificPasswordAsync)(this.options.appSpecificPasswordSource);
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
+ }
@@ -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.learnMore)(`https://appstoreconnect.apple.com/apps/${(_b = submission.iosConfig) === null || _b === void 0 ? void 0 : _b.ascAppIdentifier}/appstore/ios`, { learnMoreMessage: '' })}`,
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 getLatestBuildForSubmissionAsync(platform: AppPlatform, appId: string): Promise<BuildFragment | null>;
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.getLatestBuildForSubmissionAsync = void 0;
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 getLatestBuildForSubmissionAsync(platform, appId) {
7
- const [build] = await BuildQuery_1.BuildQuery.allForAppAsync(appId, {
8
- limit: 1,
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.getLatestBuildForSubmissionAsync = getLatestBuildForSubmissionAsync;
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;