eas-cli 0.30.0 → 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.
Files changed (81) hide show
  1. package/README.md +26 -26
  2. package/build/build/android/prepareJob.js +3 -3
  3. package/build/build/ios/prepareJob.js +3 -14
  4. package/build/build/utils/printBuildInfo.js +1 -1
  5. package/build/build/utils/updates.js +1 -1
  6. package/build/commands/branch/create.js +1 -4
  7. package/build/commands/branch/delete.js +1 -4
  8. package/build/commands/branch/list.js +1 -4
  9. package/build/commands/branch/publish.js +1 -4
  10. package/build/commands/branch/rename.js +1 -4
  11. package/build/commands/branch/view.js +1 -4
  12. package/build/commands/build/cancel.js +1 -2
  13. package/build/commands/build/configure.js +2 -2
  14. package/build/commands/build/index.js +3 -3
  15. package/build/commands/build/list.js +1 -2
  16. package/build/commands/build/view.js +1 -2
  17. package/build/commands/channel/create.js +1 -4
  18. package/build/commands/channel/edit.js +1 -4
  19. package/build/commands/channel/list.js +1 -4
  20. package/build/commands/channel/rollout.js +1 -4
  21. package/build/commands/channel/view.js +1 -4
  22. package/build/commands/config.js +1 -2
  23. package/build/commands/device/list.js +1 -2
  24. package/build/commands/device/view.js +1 -2
  25. package/build/commands/project/info.js +1 -4
  26. package/build/commands/project/init.js +1 -4
  27. package/build/commands/secret/create.js +1 -2
  28. package/build/commands/secret/delete.js +1 -2
  29. package/build/commands/secret/list.js +1 -2
  30. package/build/commands/submit.js +2 -5
  31. package/build/commands/webhook/create.js +1 -4
  32. package/build/commands/webhook/delete.js +1 -4
  33. package/build/commands/webhook/list.js +1 -4
  34. package/build/credentials/android/actions/SetupGoogleServiceAccountKey.js +3 -3
  35. package/build/credentials/android/credentials.d.ts +2 -0
  36. package/build/credentials/android/utils/googleServiceAccountKey.js +1 -0
  37. package/build/credentials/context.d.ts +1 -1
  38. package/build/credentials/context.js +5 -5
  39. package/build/credentials/errors.d.ts +3 -0
  40. package/build/credentials/errors.js +7 -1
  41. package/build/credentials/ios/actions/AscApiKeyUtils.d.ts +5 -0
  42. package/build/credentials/ios/actions/AscApiKeyUtils.js +63 -0
  43. package/build/credentials/ios/actions/DistributionCertificateUtils.js +5 -5
  44. package/build/credentials/ios/actions/SetupAdhocProvisioningProfile.d.ts +1 -0
  45. package/build/credentials/ios/actions/SetupAdhocProvisioningProfile.js +48 -4
  46. package/build/credentials/ios/appstore/AppStoreApi.d.ts +7 -1
  47. package/build/credentials/ios/appstore/AppStoreApi.js +17 -0
  48. package/build/credentials/ios/appstore/Credentials.js +3 -3
  49. package/build/credentials/ios/appstore/Credentials.types.d.ts +13 -0
  50. package/build/credentials/ios/appstore/ascApiKey.d.ts +9 -0
  51. package/build/credentials/ios/appstore/ascApiKey.js +99 -0
  52. package/build/credentials/ios/credentials.d.ts +17 -0
  53. package/build/credentials/ios/credentials.js +16 -1
  54. package/build/credentials/ios/utils/printCredentials.js +12 -7
  55. package/build/devices/context.js +1 -1
  56. package/build/graphql/generated.d.ts +24 -1
  57. package/build/graphql/generated.js +2 -0
  58. package/build/log.d.ts +11 -1
  59. package/build/log.js +21 -10
  60. package/build/project/projectUtils.d.ts +4 -1
  61. package/build/project/projectUtils.js +13 -3
  62. package/build/submit/ArchiveSource.d.ts +7 -2
  63. package/build/submit/ArchiveSource.js +91 -8
  64. package/build/submit/android/AndroidSubmitCommand.js +3 -8
  65. package/build/submit/android/AndroidSubmitter.js +31 -9
  66. package/build/submit/android/ServiceAccountSource.d.ts +24 -5
  67. package/build/submit/android/ServiceAccountSource.js +54 -75
  68. package/build/submit/context.d.ts +4 -0
  69. package/build/submit/context.js +7 -1
  70. package/build/submit/ios/AscApiKeySource.d.ts +28 -0
  71. package/build/submit/ios/AscApiKeySource.js +51 -0
  72. package/build/submit/ios/IosSubmitCommand.d.ts +2 -0
  73. package/build/submit/ios/IosSubmitCommand.js +51 -3
  74. package/build/submit/ios/IosSubmitter.d.ts +3 -1
  75. package/build/submit/ios/IosSubmitter.js +48 -4
  76. package/build/submit/submit.js +1 -1
  77. package/build/submit/utils/builds.d.ts +3 -1
  78. package/build/submit/utils/builds.js +6 -6
  79. package/build/utils/json.js +2 -6
  80. package/oclif.manifest.json +1 -1
  81. package/package.json +8 -7
@@ -1,67 +1,85 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getServiceAccountAsync = exports.ServiceAccountSourceType = void 0;
3
+ exports.getServiceAccountFromCredentialsServiceAsync = exports.getServiceAccountKeyPathAsync = exports.getServiceAccountKeyResultAsync = exports.ServiceAccountSourceType = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const chalk_1 = (0, tslib_1.__importDefault)(require("chalk"));
6
- const fast_glob_1 = (0, tslib_1.__importDefault)(require("fast-glob"));
7
6
  const fs_extra_1 = (0, tslib_1.__importDefault)(require("fs-extra"));
8
- const path_1 = (0, tslib_1.__importDefault)(require("path"));
7
+ const nullthrows_1 = (0, tslib_1.__importDefault)(require("nullthrows"));
8
+ const SetupGoogleServiceAccountKey_1 = require("../../credentials/android/actions/SetupGoogleServiceAccountKey");
9
+ const googleServiceAccountKey_1 = require("../../credentials/android/utils/googleServiceAccountKey");
9
10
  const log_1 = (0, tslib_1.__importStar)(require("../../log"));
10
- const projectUtils_1 = require("../../project/projectUtils");
11
11
  const prompts_1 = require("../../prompts");
12
- const filterAsync_1 = require("../../utils/filterAsync");
12
+ const Account_1 = require("../../user/Account");
13
13
  const files_1 = require("../utils/files");
14
14
  var ServiceAccountSourceType;
15
15
  (function (ServiceAccountSourceType) {
16
16
  ServiceAccountSourceType[ServiceAccountSourceType["path"] = 0] = "path";
17
17
  ServiceAccountSourceType[ServiceAccountSourceType["prompt"] = 1] = "prompt";
18
- ServiceAccountSourceType[ServiceAccountSourceType["detect"] = 2] = "detect";
19
- // credentialsService,
20
- // ...
18
+ ServiceAccountSourceType[ServiceAccountSourceType["credentialsService"] = 2] = "credentialsService";
21
19
  })(ServiceAccountSourceType = exports.ServiceAccountSourceType || (exports.ServiceAccountSourceType = {}));
22
- async function getServiceAccountAsync(source) {
20
+ async function getServiceAccountKeyResultAsync(ctx, source, androidApplicationIdentifier) {
21
+ if (source.sourceType === ServiceAccountSourceType.credentialsService) {
22
+ return await getServiceAccountFromCredentialsServiceAsync(ctx, androidApplicationIdentifier);
23
+ }
24
+ else {
25
+ return await getServiceAccountLocallyAsync(source);
26
+ }
27
+ }
28
+ exports.getServiceAccountKeyResultAsync = getServiceAccountKeyResultAsync;
29
+ async function getServiceAccountLocallyAsync(source) {
30
+ const serviceAccountKeyPath = await getServiceAccountKeyPathAsync(source);
31
+ const serviceAccountKey = (0, googleServiceAccountKey_1.readAndValidateServiceAccountKey)(serviceAccountKeyPath);
32
+ return {
33
+ result: { googleServiceAccountKeyJson: await fs_extra_1.default.readFile(serviceAccountKeyPath, 'utf-8') },
34
+ summary: {
35
+ source: 'local',
36
+ path: serviceAccountKeyPath,
37
+ email: serviceAccountKey.client_email,
38
+ },
39
+ };
40
+ }
41
+ async function getServiceAccountKeyPathAsync(source) {
23
42
  switch (source.sourceType) {
24
43
  case ServiceAccountSourceType.path:
25
44
  return await handlePathSourceAsync(source);
26
45
  case ServiceAccountSourceType.prompt:
27
46
  return await handlePromptSourceAsync(source);
28
- case ServiceAccountSourceType.detect:
29
- return await handleDetectSourceAsync(source);
47
+ case ServiceAccountSourceType.credentialsService: {
48
+ throw new Error(`ServiceAccountSource ${source} does not return a path.`);
49
+ }
30
50
  }
31
51
  }
32
- exports.getServiceAccountAsync = getServiceAccountAsync;
52
+ exports.getServiceAccountKeyPathAsync = getServiceAccountKeyPathAsync;
53
+ async function getServiceAccountFromCredentialsServiceAsync(ctx, androidApplicationIdentifier) {
54
+ const appLookupParams = {
55
+ account: (0, nullthrows_1.default)((0, Account_1.findAccountByName)(ctx.user.accounts, ctx.accountName), `You do not have access to account: ${ctx.accountName}`),
56
+ projectName: ctx.projectName,
57
+ androidApplicationIdentifier,
58
+ };
59
+ const setupGoogleServiceAccountKeyAction = new SetupGoogleServiceAccountKey_1.SetupGoogleServiceAccountKey(appLookupParams);
60
+ const androidAppCredentials = await setupGoogleServiceAccountKeyAction.runAsync(ctx.credentialsCtx);
61
+ const googleServiceAccountKey = (0, nullthrows_1.default)(androidAppCredentials.googleServiceAccountKeyForSubmissions, 'Credentials Service must provide a valid GoogleServiceAccountKey');
62
+ return {
63
+ result: {
64
+ googleServiceAccountKeyId: googleServiceAccountKey.id,
65
+ },
66
+ summary: {
67
+ source: 'EAS servers',
68
+ email: googleServiceAccountKey.clientEmail,
69
+ },
70
+ };
71
+ }
72
+ exports.getServiceAccountFromCredentialsServiceAsync = getServiceAccountFromCredentialsServiceAsync;
33
73
  async function handlePathSourceAsync(source) {
34
74
  if (!(await (0, files_1.isExistingFileAsync)(source.path))) {
35
75
  log_1.default.warn(`File ${source.path} doesn't exist.`);
36
- return await getServiceAccountAsync({ sourceType: ServiceAccountSourceType.prompt });
76
+ return await getServiceAccountKeyPathAsync({ sourceType: ServiceAccountSourceType.prompt });
37
77
  }
38
78
  return source.path;
39
79
  }
40
- async function handleDetectSourceAsync(_source) {
41
- var _a;
42
- const projectDir = (_a = (await (0, projectUtils_1.findProjectRootAsync)())) !== null && _a !== void 0 ? _a : process.cwd();
43
- const foundFilePaths = await (0, fast_glob_1.default)('**/*.json', {
44
- cwd: projectDir,
45
- ignore: ['app.json', 'package*.json', 'tsconfig.json', 'node_modules'],
46
- });
47
- const googleServiceFiles = await (0, filterAsync_1.filterAsync)(foundFilePaths.map(file => path_1.default.join(projectDir, file)), fileIsGoogleServicesAsync);
48
- if (googleServiceFiles.length > 1) {
49
- const selectedPath = await displayPathChooserAsync(googleServiceFiles, projectDir);
50
- if (selectedPath !== false) {
51
- return selectedPath;
52
- }
53
- }
54
- else if (googleServiceFiles.length === 1) {
55
- const [detectedPath] = googleServiceFiles;
56
- if (await confirmDetectedPathAsync(detectedPath)) {
57
- return detectedPath;
58
- }
59
- }
60
- return await getServiceAccountAsync({ sourceType: ServiceAccountSourceType.prompt });
61
- }
62
80
  async function handlePromptSourceAsync(_source) {
63
81
  const path = await askForServiceAccountPathAsync();
64
- return await getServiceAccountAsync({
82
+ return await getServiceAccountKeyPathAsync({
65
83
  sourceType: ServiceAccountSourceType.path,
66
84
  path,
67
85
  });
@@ -90,42 +108,3 @@ async function askForServiceAccountPathAsync() {
90
108
  });
91
109
  return filePath;
92
110
  }
93
- async function displayPathChooserAsync(paths, projectDir) {
94
- const choices = paths.map(f => ({
95
- value: f,
96
- title: f.startsWith(projectDir) ? path_1.default.relative(projectDir, f) : f,
97
- }));
98
- choices.push({
99
- title: 'None of the above',
100
- value: false,
101
- });
102
- log_1.default.log('Multiple Google Service Account JSON keys have been found inside your project directory.');
103
- const { selectedPath } = await (0, prompts_1.promptAsync)({
104
- name: 'selectedPath',
105
- type: 'select',
106
- message: 'Choose the key you want to use for this submission:',
107
- choices,
108
- });
109
- log_1.default.addNewLineIfNone();
110
- return selectedPath;
111
- }
112
- async function confirmDetectedPathAsync(path) {
113
- log_1.default.log(`A Google Service Account JSON key has been found at\n ${chalk_1.default.underline(path)}`);
114
- const { confirmed } = await (0, prompts_1.promptAsync)({
115
- name: 'confirmed',
116
- type: 'confirm',
117
- message: 'Would you like to use this file?',
118
- initial: true,
119
- });
120
- log_1.default.addNewLineIfNone();
121
- return confirmed;
122
- }
123
- async function fileIsGoogleServicesAsync(path) {
124
- try {
125
- const jsonFile = await fs_extra_1.default.readJson(path);
126
- return jsonFile.type === 'service_account';
127
- }
128
- catch (e) {
129
- return false;
130
- }
131
- }
@@ -2,7 +2,9 @@ import { ExpoConfig } from '@expo/config';
2
2
  import { Platform } from '@expo/eas-build-job';
3
3
  import { SubmitProfile } from '@expo/eas-json';
4
4
  import { CredentialsContext } from '../credentials/context';
5
+ import { Actor } from '../user/User';
5
6
  export interface SubmissionContext<T extends Platform> {
7
+ accountName: string;
6
8
  archiveFlags: SubmitArchiveFlags;
7
9
  credentialsCtx: CredentialsContext;
8
10
  exp: ExpoConfig;
@@ -11,6 +13,8 @@ export interface SubmissionContext<T extends Platform> {
11
13
  profile: SubmitProfile<T>;
12
14
  projectDir: string;
13
15
  projectId: string;
16
+ projectName: string;
17
+ user: Actor;
14
18
  }
15
19
  export interface SubmitArchiveFlags {
16
20
  latest?: boolean;
@@ -3,20 +3,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createSubmissionContextAsync = void 0;
4
4
  const context_1 = require("../credentials/context");
5
5
  const expoConfig_1 = require("../project/expoConfig");
6
+ const projectUtils_1 = require("../project/projectUtils");
6
7
  const actions_1 = require("../user/actions");
7
8
  async function createSubmissionContextAsync(params) {
8
9
  const { projectDir, nonInteractive } = params;
9
10
  const exp = (0, expoConfig_1.getExpoConfig)(projectDir, { env: params.env });
10
11
  const { env, ...rest } = params;
12
+ const user = await (0, actions_1.ensureLoggedInAsync)();
13
+ const projectName = exp.slug;
14
+ const accountName = (0, projectUtils_1.getProjectAccountName)(exp, user);
11
15
  let credentialsCtx = params.credentialsCtx;
12
16
  if (!credentialsCtx) {
13
- const user = await (0, actions_1.ensureLoggedInAsync)();
14
17
  credentialsCtx = new context_1.CredentialsContext({ projectDir, user, exp, nonInteractive });
15
18
  }
16
19
  return {
17
20
  ...rest,
21
+ accountName,
18
22
  credentialsCtx,
19
23
  exp,
24
+ projectName,
25
+ user,
20
26
  };
21
27
  }
22
28
  exports.createSubmissionContextAsync = createSubmissionContextAsync;
@@ -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));
@@ -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;
@@ -10,9 +10,7 @@ function enableJsonOutput() {
10
10
  return;
11
11
  }
12
12
  stdoutWrite = process.stdout.write;
13
- process.stdout.write = (...args) => {
14
- return process.stderr.write.call(null, args);
15
- };
13
+ process.stdout.write = process.stderr.write.bind(process.stderr);
16
14
  }
17
15
  exports.enableJsonOutput = enableJsonOutput;
18
16
  function printJsonOnlyOutput(value) {
@@ -22,9 +20,7 @@ function printJsonOnlyOutput(value) {
22
20
  log_1.default.log(JSON.stringify(sanitizeValue(value), null, 2));
23
21
  }
24
22
  finally {
25
- process.stdout.write = (...args) => {
26
- return process.stderr.write.call(null, args);
27
- };
23
+ process.stdout.write = process.stderr.write.bind(process.stderr);
28
24
  }
29
25
  }
30
26
  exports.printJsonOnlyOutput = printJsonOnlyOutput;