eas-cli 0.35.0 → 0.38.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 (106) hide show
  1. package/README.md +37 -37
  2. package/build/analytics/common.d.ts +8 -0
  3. package/build/analytics/common.js +19 -0
  4. package/build/analytics/events.d.ts +43 -0
  5. package/build/analytics/events.js +51 -0
  6. package/build/{analytics.d.ts → analytics/rudderstackClient.d.ts} +0 -0
  7. package/build/{analytics.js → analytics/rudderstackClient.js} +2 -2
  8. package/build/build/android/build.d.ts +2 -1
  9. package/build/build/android/build.js +11 -9
  10. package/build/build/build.d.ts +0 -2
  11. package/build/build/build.js +19 -29
  12. package/build/build/configure.js +0 -1
  13. package/build/build/context.d.ts +19 -11
  14. package/build/build/context.js +0 -96
  15. package/build/build/createContext.d.ts +13 -0
  16. package/build/build/createContext.js +114 -0
  17. package/build/build/ios/UpdatesModule.js +3 -2
  18. package/build/build/ios/build.d.ts +2 -1
  19. package/build/build/ios/build.js +24 -19
  20. package/build/build/ios/credentials.js +3 -3
  21. package/build/build/ios/version.js +7 -3
  22. package/build/build/local.js +28 -26
  23. package/build/build/metadata.d.ts +1 -12
  24. package/build/build/metadata.js +24 -19
  25. package/build/build/types.d.ts +0 -1
  26. package/build/build/utils/devClient.d.ts +1 -2
  27. package/build/build/utils/repository.js +1 -0
  28. package/build/build/validate.d.ts +3 -3
  29. package/build/commandUtils/EasCommand.js +4 -4
  30. package/build/commands/branch/publish.js +24 -2
  31. package/build/commands/build/configure.js +2 -0
  32. package/build/commands/build/index.js +12 -17
  33. package/build/commands/channel/create.d.ts +6 -0
  34. package/build/commands/channel/create.js +2 -0
  35. package/build/commands/secret/create.js +0 -6
  36. package/build/commands/secret/delete.js +0 -6
  37. package/build/commands/secret/list.js +0 -6
  38. package/build/commands/submit.js +2 -11
  39. package/build/commands/webhook/create.js +1 -2
  40. package/build/commands/webhook/list.js +1 -1
  41. package/build/commands/webhook/update.js +1 -2
  42. package/build/credentials/android/actions/RemoveFcm.js +4 -4
  43. package/build/credentials/android/utils/keystore.js +67 -32
  44. package/build/credentials/ios/actions/AscApiKeyUtils.js +8 -7
  45. package/build/credentials/ios/actions/AssignAscApiKey.js +1 -1
  46. package/build/credentials/ios/actions/CreateAscApiKey.js +2 -2
  47. package/build/credentials/ios/actions/RemoveAscApiKey.js +6 -6
  48. package/build/credentials/ios/actions/SetUpAscApiKey.js +8 -8
  49. package/build/credentials/ios/actions/SetUpSubmissionCredentials.js +3 -3
  50. package/build/credentials/ios/appstore/ascApiKey.js +12 -12
  51. package/build/credentials/ios/appstore/bundleIdCapabilities.js +11 -1
  52. package/build/credentials/ios/appstore/entitlements.d.ts +2 -2
  53. package/build/credentials/ios/appstore/entitlements.js +28 -21
  54. package/build/credentials/ios/credentials.js +2 -2
  55. package/build/credentials/ios/utils/printCredentials.js +1 -1
  56. package/build/credentials/manager/AndroidActions.js +3 -3
  57. package/build/credentials/manager/IosActions.js +5 -5
  58. package/build/credentials/manager/ManageIos.js +4 -4
  59. package/build/graphql/generated.d.ts +146 -54
  60. package/build/graphql/generated.js +1 -0
  61. package/build/graphql/mutations/KeystoreGenerationUrlMutation.d.ts +3 -0
  62. package/build/graphql/mutations/KeystoreGenerationUrlMutation.js +23 -0
  63. package/build/project/android/applicationId.d.ts +6 -0
  64. package/build/project/android/applicationId.js +39 -29
  65. package/build/project/android/gradle.js +3 -2
  66. package/build/project/android/gradleUtils.d.ts +1 -0
  67. package/build/project/ios/bundleIdentifier.d.ts +6 -1
  68. package/build/project/ios/bundleIdentifier.js +29 -18
  69. package/build/submit/BaseSubmitter.d.ts +20 -4
  70. package/build/submit/BaseSubmitter.js +34 -1
  71. package/build/submit/android/AndroidSubmitCommand.d.ts +1 -2
  72. package/build/submit/android/AndroidSubmitCommand.js +23 -35
  73. package/build/submit/android/AndroidSubmitter.d.ts +12 -8
  74. package/build/submit/android/AndroidSubmitter.js +30 -21
  75. package/build/submit/android/ServiceAccountSource.d.ts +3 -2
  76. package/build/submit/android/ServiceAccountSource.js +16 -4
  77. package/build/submit/context.d.ts +4 -0
  78. package/build/submit/context.js +16 -1
  79. package/build/submit/ios/AppSpecificPasswordSource.d.ts +8 -1
  80. package/build/submit/ios/AppSpecificPasswordSource.js +44 -4
  81. package/build/submit/ios/CredentialsServiceSource.d.ts +9 -5
  82. package/build/submit/ios/CredentialsServiceSource.js +22 -5
  83. package/build/submit/ios/IosSubmitCommand.d.ts +2 -2
  84. package/build/submit/ios/IosSubmitCommand.js +46 -53
  85. package/build/submit/ios/IosSubmitter.d.ts +17 -7
  86. package/build/submit/ios/IosSubmitter.js +55 -29
  87. package/build/submit/submit.js +13 -4
  88. package/build/user/User.js +1 -1
  89. package/build/{build/ios → utils}/plist.d.ts +1 -1
  90. package/build/{build/ios → utils}/plist.js +8 -2
  91. package/build/utils/profiles.d.ts +9 -4
  92. package/build/utils/profiles.js +14 -7
  93. package/build/vcs/clients/git.d.ts +2 -1
  94. package/build/vcs/clients/git.js +87 -6
  95. package/build/vcs/vcs.d.ts +4 -3
  96. package/build/vcs/vcs.js +3 -3
  97. package/build/webhooks/input.d.ts +2 -2
  98. package/build/webhooks/input.js +19 -2
  99. package/oclif.manifest.json +1 -1
  100. package/package.json +10 -10
  101. package/build/build/utils/analytics.d.ts +0 -22
  102. package/build/build/utils/analytics.js +0 -28
  103. package/build/project/isEasEnabledForProject.d.ts +0 -8
  104. package/build/project/isEasEnabledForProject.js +0 -33
  105. package/build/submit/android/AndroidPackageSource.d.ts +0 -17
  106. package/build/submit/android/AndroidPackageSource.js +0 -27
@@ -4,7 +4,7 @@ const tslib_1 = require("tslib");
4
4
  const eas_json_1 = require("@expo/eas-json");
5
5
  const command_1 = require("@oclif/command");
6
6
  const semver_1 = (0, tslib_1.__importDefault)(require("semver"));
7
- const analytics_1 = require("../analytics");
7
+ const rudderstackClient_1 = require("../analytics/rudderstackClient");
8
8
  const log_1 = (0, tslib_1.__importDefault)(require("../log"));
9
9
  const projectUtils_1 = require("../project/projectUtils");
10
10
  const User_1 = require("../user/User");
@@ -25,7 +25,7 @@ class EasCommand extends command_1.Command {
25
25
  async run() {
26
26
  var _a;
27
27
  eas_json_1.EasJsonReader.setLog(log_1.default);
28
- await (0, analytics_1.initAsync)();
28
+ await (0, rudderstackClient_1.initAsync)();
29
29
  await this.applyCliConfigAsync();
30
30
  if (this.requiresAuthentication) {
31
31
  const { flags } = this.parse();
@@ -35,7 +35,7 @@ class EasCommand extends command_1.Command {
35
35
  else {
36
36
  await (0, User_1.getUserAsync)();
37
37
  }
38
- (0, analytics_1.logEvent)(analytics_1.AnalyticsEvent.ACTION, {
38
+ (0, rudderstackClient_1.logEvent)(rudderstackClient_1.AnalyticsEvent.ACTION, {
39
39
  // id is assigned by oclif in constructor based on the filepath:
40
40
  // commands/submit === submit, commands/build/list === build:list
41
41
  action: `eas ${this.id}`,
@@ -44,7 +44,7 @@ class EasCommand extends command_1.Command {
44
44
  }
45
45
  // eslint-disable-next-line async-protect/async-suffix
46
46
  async finally(err) {
47
- await (0, analytics_1.flushAsync)();
47
+ await (0, rudderstackClient_1.flushAsync)();
48
48
  return super.finally(err);
49
49
  }
50
50
  async applyCliConfigAsync() {
@@ -23,7 +23,8 @@ const utils_1 = require("../../update/utils");
23
23
  const uniqBy_1 = (0, tslib_1.__importDefault)(require("../../utils/expodash/uniqBy"));
24
24
  const formatFields_1 = (0, tslib_1.__importDefault)(require("../../utils/formatFields"));
25
25
  const vcs_1 = require("../../vcs");
26
- const create_1 = require("./create");
26
+ const create_1 = require("../channel/create");
27
+ const create_2 = require("./create");
27
28
  const list_1 = require("./list");
28
29
  const view_1 = require("./view");
29
30
  exports.defaultPublishPlatforms = ['android', 'ios'];
@@ -46,6 +47,24 @@ async function getUpdateGroupAsync({ group, }) {
46
47
  .toPromise());
47
48
  return updatesByGroup;
48
49
  }
50
+ async function ensureChannelExistsAsync({ appId, branchId, channelName, }) {
51
+ var _a;
52
+ try {
53
+ await (0, create_1.createUpdateChannelOnAppAsync)({
54
+ appId,
55
+ channelName,
56
+ branchId,
57
+ });
58
+ log_1.default.withTick(`Created a channel: ${chalk_1.default.bold(channelName)} pointed at branch: ${chalk_1.default.bold(channelName)}.`);
59
+ }
60
+ catch (e) {
61
+ const isIgnorableError = ((_a = e.graphQLErrors) === null || _a === void 0 ? void 0 : _a.length) === 1 &&
62
+ e.graphQLErrors[0].extensions.errorCode === 'CHANNEL_ALREADY_EXISTS';
63
+ if (!isIgnorableError) {
64
+ throw e;
65
+ }
66
+ }
67
+ }
49
68
  async function ensureBranchExistsAsync({ appId, name: branchName, }) {
50
69
  const { app } = await (0, view_1.viewUpdateBranchAsync)({
51
70
  appId,
@@ -54,9 +73,12 @@ async function ensureBranchExistsAsync({ appId, name: branchName, }) {
54
73
  const updateBranch = app === null || app === void 0 ? void 0 : app.byId.updateBranchByName;
55
74
  if (updateBranch) {
56
75
  const { id, updates } = updateBranch;
76
+ await ensureChannelExistsAsync({ appId, branchId: id, channelName: branchName });
57
77
  return { id, updates };
58
78
  }
59
- const newUpdateBranch = await (0, create_1.createUpdateBranchOnAppAsync)({ appId, name: branchName });
79
+ const newUpdateBranch = await (0, create_2.createUpdateBranchOnAppAsync)({ appId, name: branchName });
80
+ log_1.default.withTick(`Created branch: ${chalk_1.default.bold(branchName)}`);
81
+ await ensureChannelExistsAsync({ appId, branchId: newUpdateBranch.id, channelName: branchName });
60
82
  return { id: newUpdateBranch.id, updates: [] };
61
83
  }
62
84
  class BranchPublish extends EasCommand_1.default {
@@ -9,12 +9,14 @@ const log_1 = (0, tslib_1.__importStar)(require("../../log"));
9
9
  const platform_1 = require("../../platform");
10
10
  const projectUtils_1 = require("../../project/projectUtils");
11
11
  const prompts_1 = require("../../prompts");
12
+ const vcs_1 = require("../../vcs");
12
13
  class BuildConfigure extends EasCommand_1.default {
13
14
  async runAsync() {
14
15
  var _a;
15
16
  const { flags } = this.parse(BuildConfigure);
16
17
  log_1.default.log('💡 The following process will configure your iOS and/or Android project to be compatible with EAS Build. These changes only apply to your local project files and you can safely revert them at any time.');
17
18
  log_1.default.newLine();
19
+ await (0, vcs_1.getVcsClient)().ensureRepoExistsAsync();
18
20
  const platform = (_a = flags.platform) !== null && _a !== void 0 ? _a : (await promptForPlatformAsync());
19
21
  await (0, configure_1.configureAsync)({
20
22
  platform,
@@ -13,7 +13,7 @@ const nullthrows_1 = (0, tslib_1.__importDefault)(require("nullthrows"));
13
13
  const build_1 = require("../../build/android/build");
14
14
  const build_2 = require("../../build/build");
15
15
  const configure_1 = require("../../build/configure");
16
- const context_1 = require("../../build/context");
16
+ const createContext_1 = require("../../build/createContext");
17
17
  const build_3 = require("../../build/ios/build");
18
18
  const devClient_1 = require("../../build/utils/devClient");
19
19
  const printBuildInfo_1 = require("../../build/utils/printBuildInfo");
@@ -23,11 +23,10 @@ const generated_1 = require("../../graphql/generated");
23
23
  const AppPlatform_1 = require("../../graphql/types/AppPlatform");
24
24
  const log_1 = (0, tslib_1.__importStar)(require("../../log"));
25
25
  const platform_1 = require("../../platform");
26
- const isEasEnabledForProject_1 = require("../../project/isEasEnabledForProject");
27
26
  const metroConfig_1 = require("../../project/metroConfig");
28
27
  const projectUtils_1 = require("../../project/projectUtils");
29
28
  const prompts_1 = require("../../prompts");
30
- const context_2 = require("../../submit/context");
29
+ const context_1 = require("../../submit/context");
31
30
  const submit_1 = require("../../submit/submit");
32
31
  const urls_1 = require("../../submit/utils/urls");
33
32
  const easCli_1 = require("../../utils/easCli");
@@ -41,7 +40,6 @@ class Build extends EasCommand_1.default {
41
40
  this.metroConfigValidated = false;
42
41
  }
43
42
  async runAsync() {
44
- var _a;
45
43
  const { flags: rawFlags } = this.parse(Build);
46
44
  if (rawFlags.json) {
47
45
  (0, json_1.enableJsonOutput)();
@@ -54,13 +52,11 @@ class Build extends EasCommand_1.default {
54
52
  await (0, repository_1.ensureRepoIsCleanAsync)(flags.nonInteractive);
55
53
  await (0, configure_1.ensureProjectConfiguredAsync)(projectDir, requestedPlatform);
56
54
  const platforms = (0, platform_1.toPlatforms)(requestedPlatform);
57
- const easJsonReader = new eas_json_1.EasJsonReader(projectDir);
58
55
  const buildProfiles = await (0, profiles_1.getProfilesAsync)({
56
+ type: 'build',
57
+ projectDir,
59
58
  platforms,
60
59
  profileName: flags.profile,
61
- async readProfileAsync(platform, profileName) {
62
- return await easJsonReader.readBuildProfileAsync(platform, profileName);
63
- },
64
60
  });
65
61
  await (0, devClient_1.ensureExpoDevClientInstalledForDevClientBuildsAsync)({
66
62
  projectDir,
@@ -92,7 +88,7 @@ class Build extends EasCommand_1.default {
92
88
  for (const startedBuild of startedBuilds) {
93
89
  const submission = await this.prepareAndStartSubmissionAsync({
94
90
  build: startedBuild.build,
95
- credentialsCtx: (0, nullthrows_1.default)((_a = buildCtxByPlatform[startedBuild.build.platform]) === null || _a === void 0 ? void 0 : _a.credentialsCtx),
91
+ buildCtx: (0, nullthrows_1.default)(buildCtxByPlatform[startedBuild.build.platform]),
96
92
  flags,
97
93
  moreBuilds: startedBuilds.length > 1,
98
94
  projectDir,
@@ -154,12 +150,12 @@ class Build extends EasCommand_1.default {
154
150
  wait: flags['wait'],
155
151
  clearCache: flags['clear-cache'],
156
152
  json: flags['json'],
157
- autoSubmit: flags['auto-submit'],
153
+ autoSubmit: flags['auto-submit'] || flags['auto-submit-with-profile'] !== undefined,
158
154
  submitProfile: (_a = flags['auto-submit-with-profile']) !== null && _a !== void 0 ? _a : profile,
159
155
  };
160
156
  }
161
157
  async prepareAndStartBuildAsync({ projectDir, flags, moreBuilds, buildProfile, }) {
162
- const buildCtx = await (0, context_1.createBuildContextAsync)({
158
+ const buildCtx = await (0, createContext_1.createBuildContextAsync)({
163
159
  buildProfileName: buildProfile.profileName,
164
160
  clearCache: flags.clearCache,
165
161
  buildProfile: buildProfile.profile,
@@ -178,9 +174,6 @@ class Build extends EasCommand_1.default {
178
174
  await (0, metroConfig_1.validateMetroConfigForManagedWorkflowAsync)(buildCtx);
179
175
  this.metroConfigValidated = true;
180
176
  }
181
- if (!buildCtx.local && !(await (0, isEasEnabledForProject_1.isEasEnabledForProjectAsync)(buildCtx.projectId))) {
182
- (0, errors_1.error)(isEasEnabledForProject_1.EAS_UNAVAILABLE_MESSAGE, { exit: 1 });
183
- }
184
177
  const build = await this.startBuildAsync(buildCtx);
185
178
  return {
186
179
  build,
@@ -197,11 +190,12 @@ class Build extends EasCommand_1.default {
197
190
  }
198
191
  return await sendBuildRequestAsync();
199
192
  }
200
- async prepareAndStartSubmissionAsync({ build, credentialsCtx, flags, moreBuilds, projectDir, buildProfile, }) {
193
+ async prepareAndStartSubmissionAsync({ build, buildCtx, flags, moreBuilds, projectDir, buildProfile, }) {
194
+ var _a, _b, _c;
201
195
  const easJsonReader = new eas_json_1.EasJsonReader(projectDir);
202
196
  const platform = (0, AppPlatform_1.toPlatform)(build.platform);
203
197
  const submitProfile = await easJsonReader.readSubmitProfileAsync(platform, flags.submitProfile);
204
- const submissionCtx = await (0, context_2.createSubmissionContextAsync)({
198
+ const submissionCtx = await (0, context_1.createSubmissionContextAsync)({
205
199
  platform,
206
200
  projectDir,
207
201
  projectId: build.project.id,
@@ -209,7 +203,8 @@ class Build extends EasCommand_1.default {
209
203
  archiveFlags: { id: build.id },
210
204
  nonInteractive: flags.nonInteractive,
211
205
  env: buildProfile.profile.env,
212
- credentialsCtx,
206
+ credentialsCtx: buildCtx.credentialsCtx,
207
+ applicationIdentifier: (_b = (_a = buildCtx.android) === null || _a === void 0 ? void 0 : _a.applicationId) !== null && _b !== void 0 ? _b : (_c = buildCtx.ios) === null || _c === void 0 ? void 0 : _c.bundleIdentifier,
213
208
  });
214
209
  if (moreBuilds) {
215
210
  log_1.default.newLine();
@@ -1,4 +1,10 @@
1
1
  import EasCommand from '../../commandUtils/EasCommand';
2
+ import { CreateUpdateChannelOnAppMutation } from '../../graphql/generated';
3
+ export declare function createUpdateChannelOnAppAsync({ appId, channelName, branchId, }: {
4
+ appId: string;
5
+ channelName: string;
6
+ branchId: string;
7
+ }): Promise<CreateUpdateChannelOnAppMutation>;
2
8
  export default class ChannelCreate extends EasCommand {
3
9
  static hidden: boolean;
4
10
  static description: string;
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createUpdateChannelOnAppAsync = void 0;
3
4
  const tslib_1 = require("tslib");
4
5
  const config_1 = require("@expo/config");
5
6
  const command_1 = require("@oclif/command");
@@ -36,6 +37,7 @@ async function createUpdateChannelOnAppAsync({ appId, channelName, branchId, })
36
37
  })
37
38
  .toPromise());
38
39
  }
40
+ exports.createUpdateChannelOnAppAsync = createUpdateChannelOnAppAsync;
39
41
  class ChannelCreate extends EasCommand_1.default {
40
42
  async runAsync() {
41
43
  let { args: { name: channelName }, flags: { json: jsonFlag }, } = this.parse(ChannelCreate);
@@ -8,7 +8,6 @@ const EasCommand_1 = (0, tslib_1.__importDefault)(require("../../commandUtils/Ea
8
8
  const EnvironmentSecretMutation_1 = require("../../graphql/mutations/EnvironmentSecretMutation");
9
9
  const EnvironmentSecretsQuery_1 = require("../../graphql/queries/EnvironmentSecretsQuery");
10
10
  const log_1 = (0, tslib_1.__importDefault)(require("../../log"));
11
- const isEasEnabledForProject_1 = require("../../project/isEasEnabledForProject");
12
11
  const projectUtils_1 = require("../../project/projectUtils");
13
12
  const prompts_1 = require("../../prompts");
14
13
  const Account_1 = require("../../user/Account");
@@ -23,11 +22,6 @@ class EnvironmentSecretCreate extends EasCommand_1.default {
23
22
  const accountName = await (0, projectUtils_1.getProjectAccountNameAsync)(exp);
24
23
  const { slug } = exp;
25
24
  const projectId = await (0, projectUtils_1.getProjectIdAsync)(exp);
26
- if (!(await (0, isEasEnabledForProject_1.isEasEnabledForProjectAsync)(projectId))) {
27
- (0, isEasEnabledForProject_1.warnEasUnavailable)();
28
- process.exitCode = 1;
29
- return;
30
- }
31
25
  if (!scope) {
32
26
  const validationMessage = 'Secret scope may not be empty.';
33
27
  ({ scope } = await (0, prompts_1.promptAsync)({
@@ -8,7 +8,6 @@ const EasCommand_1 = (0, tslib_1.__importDefault)(require("../../commandUtils/Ea
8
8
  const EnvironmentSecretMutation_1 = require("../../graphql/mutations/EnvironmentSecretMutation");
9
9
  const EnvironmentSecretsQuery_1 = require("../../graphql/queries/EnvironmentSecretsQuery");
10
10
  const log_1 = (0, tslib_1.__importDefault)(require("../../log"));
11
- const isEasEnabledForProject_1 = require("../../project/isEasEnabledForProject");
12
11
  const projectUtils_1 = require("../../project/projectUtils");
13
12
  const prompts_1 = require("../../prompts");
14
13
  class EnvironmentSecretDelete extends EasCommand_1.default {
@@ -17,11 +16,6 @@ class EnvironmentSecretDelete extends EasCommand_1.default {
17
16
  const { exp } = (0, config_1.getConfig)(projectDir, { skipSDKVersionRequirement: true });
18
17
  const projectId = await (0, projectUtils_1.getProjectIdAsync)(exp);
19
18
  const projectAccountName = await (0, projectUtils_1.getProjectAccountNameAsync)(exp);
20
- if (!(await (0, isEasEnabledForProject_1.isEasEnabledForProjectAsync)(projectId))) {
21
- (0, isEasEnabledForProject_1.warnEasUnavailable)();
22
- process.exitCode = 1;
23
- return;
24
- }
25
19
  let { flags: { id }, } = this.parse(EnvironmentSecretDelete);
26
20
  let secret;
27
21
  if (!id) {
@@ -8,7 +8,6 @@ const dateformat_1 = (0, tslib_1.__importDefault)(require("dateformat"));
8
8
  const EasCommand_1 = (0, tslib_1.__importDefault)(require("../../commandUtils/EasCommand"));
9
9
  const EnvironmentSecretsQuery_1 = require("../../graphql/queries/EnvironmentSecretsQuery");
10
10
  const log_1 = (0, tslib_1.__importDefault)(require("../../log"));
11
- const isEasEnabledForProject_1 = require("../../project/isEasEnabledForProject");
12
11
  const projectUtils_1 = require("../../project/projectUtils");
13
12
  class EnvironmentSecretList extends EasCommand_1.default {
14
13
  async runAsync() {
@@ -16,11 +15,6 @@ class EnvironmentSecretList extends EasCommand_1.default {
16
15
  const { exp } = (0, config_1.getConfig)(projectDir, { skipSDKVersionRequirement: true });
17
16
  const projectId = await (0, projectUtils_1.getProjectIdAsync)(exp);
18
17
  const projectAccountName = await (0, projectUtils_1.getProjectAccountNameAsync)(exp);
19
- if (!(await (0, isEasEnabledForProject_1.isEasEnabledForProjectAsync)(projectId))) {
20
- (0, isEasEnabledForProject_1.warnEasUnavailable)();
21
- process.exitCode = 1;
22
- return;
23
- }
24
18
  if (!projectDir) {
25
19
  throw new Error("Please run this command inside your project's directory");
26
20
  }
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const config_1 = require("@expo/config");
5
- const eas_json_1 = require("@expo/eas-json");
6
5
  const command_1 = require("@oclif/command");
7
6
  const errors_1 = require("@oclif/errors");
8
7
  const chalk_1 = (0, tslib_1.__importDefault)(require("chalk"));
@@ -10,7 +9,6 @@ const EasCommand_1 = (0, tslib_1.__importDefault)(require("../commandUtils/EasCo
10
9
  const AppPlatform_1 = require("../graphql/types/AppPlatform");
11
10
  const log_1 = (0, tslib_1.__importStar)(require("../log"));
12
11
  const platform_1 = require("../platform");
13
- const isEasEnabledForProject_1 = require("../project/isEasEnabledForProject");
14
12
  const projectUtils_1 = require("../project/projectUtils");
15
13
  const context_1 = require("../submit/context");
16
14
  const submit_1 = require("../submit/submit");
@@ -23,19 +21,12 @@ class Submit extends EasCommand_1.default {
23
21
  const projectDir = await (0, projectUtils_1.findProjectRootAsync)();
24
22
  const { exp } = (0, config_1.getConfig)(projectDir, { skipSDKVersionRequirement: true });
25
23
  const projectId = await (0, projectUtils_1.getProjectIdAsync)(exp);
26
- if (!(await (0, isEasEnabledForProject_1.isEasEnabledForProjectAsync)(projectId))) {
27
- (0, isEasEnabledForProject_1.warnEasUnavailable)();
28
- process.exitCode = 1;
29
- return;
30
- }
31
24
  const platforms = (0, platform_1.toPlatforms)(flags.requestedPlatform);
32
- const easJsonReader = new eas_json_1.EasJsonReader(projectDir);
33
25
  const submissionProfiles = await (0, profiles_1.getProfilesAsync)({
26
+ type: 'submit',
27
+ projectDir,
34
28
  platforms,
35
29
  profileName: flags.profile,
36
- async readProfileAsync(platform, profileName) {
37
- return await easJsonReader.readSubmitProfileAsync(platform, profileName);
38
- },
39
30
  });
40
31
  const submissions = [];
41
32
  for (const submissionProfile of submissionProfiles) {
@@ -32,8 +32,7 @@ WebhookCreate.description = 'Create a webhook on the current project.';
32
32
  WebhookCreate.flags = {
33
33
  event: command_1.flags.enum({
34
34
  description: 'Event type that triggers the webhook',
35
- options: [generated_1.WebhookType.Build],
36
- default: generated_1.WebhookType.Build,
35
+ options: [generated_1.WebhookType.Build, generated_1.WebhookType.Submit],
37
36
  }),
38
37
  url: command_1.flags.string({
39
38
  description: 'Webhook URL',
@@ -43,6 +43,6 @@ WebhookList.description = 'List webhooks on the current project.';
43
43
  WebhookList.flags = {
44
44
  event: command_1.flags.enum({
45
45
  description: 'Event type that triggers the webhook',
46
- options: [generated_1.WebhookType.Build],
46
+ options: [generated_1.WebhookType.Build, generated_1.WebhookType.Submit],
47
47
  }),
48
48
  };
@@ -35,8 +35,7 @@ WebhookUpdate.flags = {
35
35
  }),
36
36
  event: command_1.flags.enum({
37
37
  description: 'Event type that triggers the webhook',
38
- options: [generated_1.WebhookType.Build],
39
- default: generated_1.WebhookType.Build,
38
+ options: [generated_1.WebhookType.Build, generated_1.WebhookType.Submit],
40
39
  }),
41
40
  url: command_1.flags.string({
42
41
  description: 'Webhook URL',
@@ -11,23 +11,23 @@ class RemoveFcm {
11
11
  }
12
12
  async runAsync(ctx) {
13
13
  if (ctx.nonInteractive) {
14
- throw new Error("Deleting an FCM Api Key is a destructive operation. Start the CLI without the '--non-interactive' flag to delete the credentials.");
14
+ throw new Error("Deleting an FCM API Key is a destructive operation. Start the CLI without the '--non-interactive' flag to delete the credentials.");
15
15
  }
16
16
  const appCredentials = await ctx.android.getAndroidAppCredentialsWithCommonFieldsAsync(this.app);
17
17
  const fcm = appCredentials === null || appCredentials === void 0 ? void 0 : appCredentials.androidFcm;
18
18
  if (!fcm) {
19
- log_1.default.warn(`There is no valid FCM Api Key defined for ${(0, GraphqlClient_1.formatProjectFullName)(this.app)}, ${this.app.androidApplicationIdentifier}`);
19
+ log_1.default.warn(`There is no valid FCM API Key defined for ${(0, GraphqlClient_1.formatProjectFullName)(this.app)}, ${this.app.androidApplicationIdentifier}`);
20
20
  return;
21
21
  }
22
22
  const confirm = await (0, prompts_1.confirmAsync)({
23
- message: 'Permanently delete the FCM Api Key from Expo servers?',
23
+ message: 'Permanently delete the FCM API Key from Expo servers?',
24
24
  initial: false,
25
25
  });
26
26
  if (!confirm) {
27
27
  return;
28
28
  }
29
29
  await ctx.android.deleteFcmAsync(fcm);
30
- log_1.default.succeed('FCM Api Key removed');
30
+ log_1.default.succeed('FCM API Key removed');
31
31
  }
32
32
  }
33
33
  exports.RemoveFcm = RemoveFcm;
@@ -3,32 +3,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateRandomKeystoreAsync = exports.keytoolCommandExistsAsync = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const spawn_async_1 = (0, tslib_1.__importDefault)(require("@expo/spawn-async"));
6
+ const chalk_1 = (0, tslib_1.__importDefault)(require("chalk"));
6
7
  const crypto_1 = (0, tslib_1.__importDefault)(require("crypto"));
7
8
  const fs_extra_1 = (0, tslib_1.__importDefault)(require("fs-extra"));
9
+ // (dsokal) We actually want to use node-fetch but the change is in progress.
10
+ // See https://github.com/expo/eas-cli/issues/32 for context.
11
+ // eslint-disable-next-line no-restricted-imports
12
+ const node_fetch_1 = (0, tslib_1.__importDefault)(require("node-fetch"));
8
13
  const path_1 = (0, tslib_1.__importDefault)(require("path"));
9
14
  const uuid_1 = require("uuid");
10
- const analytics_1 = (0, tslib_1.__importStar)(require("../../../build/utils/analytics"));
15
+ const events_1 = require("../../../analytics/events");
11
16
  const generated_1 = require("../../../graphql/generated");
17
+ const KeystoreGenerationUrlMutation_1 = require("../../../graphql/mutations/KeystoreGenerationUrlMutation");
12
18
  const log_1 = (0, tslib_1.__importDefault)(require("../../../log"));
19
+ const ora_1 = require("../../../ora");
13
20
  const paths_1 = require("../../../utils/paths");
14
21
  async function keytoolCommandExistsAsync() {
15
22
  try {
16
23
  await (0, spawn_async_1.default)('keytool');
24
+ return true;
17
25
  }
18
26
  catch (error) {
19
27
  return false;
20
28
  }
21
- return true;
22
29
  }
23
30
  exports.keytoolCommandExistsAsync = keytoolCommandExistsAsync;
24
- async function ensureKeytoolCommandExistsAsync() {
25
- if (!(await keytoolCommandExistsAsync())) {
26
- log_1.default.error('keytool is required to run this command, make sure you have it installed?');
27
- log_1.default.warn('keytool is a part of OpenJDK: https://openjdk.java.net/');
28
- log_1.default.warn('Also make sure that keytool is in your PATH after installation.');
29
- throw new Error('keytool not found');
30
- }
31
- }
32
31
  var KeystoreCreateStep;
33
32
  (function (KeystoreCreateStep) {
34
33
  KeystoreCreateStep["Attempt"] = "attempt";
@@ -44,17 +43,38 @@ async function generateRandomKeystoreAsync(projectId) {
44
43
  return await createKeystoreAsync(keystoreData, projectId);
45
44
  }
46
45
  exports.generateRandomKeystoreAsync = generateRandomKeystoreAsync;
47
- async function createKeystoreAsync(credentials, projectId) {
48
- analytics_1.default.logEvent(analytics_1.Event.ANDROID_KEYSTORE_CREATE, {
46
+ async function createKeystoreAsync(keystoreParams, projectId) {
47
+ events_1.Analytics.logEvent(events_1.BuildEvent.ANDROID_KEYSTORE_CREATE, {
49
48
  project_id: projectId,
50
49
  step: KeystoreCreateStep.Attempt,
51
50
  type: generated_1.AndroidKeystoreType.Jks,
52
51
  });
53
52
  try {
54
- await ensureKeytoolCommandExistsAsync();
53
+ let keystore;
54
+ let localAttempt = false;
55
+ if (await keytoolCommandExistsAsync()) {
56
+ try {
57
+ localAttempt = true;
58
+ keystore = await createKeystoreLocallyAsync(keystoreParams);
59
+ }
60
+ catch {
61
+ log_1.default.error('Failed to generate keystore locally. Falling back to cloud generation.');
62
+ }
63
+ }
64
+ if (!keystore) {
65
+ keystore = await createKeystoreInCloudAsync(keystoreParams, {
66
+ showKeytoolDetectionMsg: !localAttempt,
67
+ });
68
+ }
69
+ events_1.Analytics.logEvent(events_1.BuildEvent.ANDROID_KEYSTORE_CREATE, {
70
+ project_id: projectId,
71
+ step: KeystoreCreateStep.Success,
72
+ type: generated_1.AndroidKeystoreType.Jks,
73
+ });
74
+ return keystore;
55
75
  }
56
76
  catch (error) {
57
- analytics_1.default.logEvent(analytics_1.Event.ANDROID_KEYSTORE_CREATE, {
77
+ events_1.Analytics.logEvent(events_1.BuildEvent.ANDROID_KEYSTORE_CREATE, {
58
78
  project_id: projectId,
59
79
  step: KeystoreCreateStep.Fail,
60
80
  reason: error.message,
@@ -62,6 +82,8 @@ async function createKeystoreAsync(credentials, projectId) {
62
82
  });
63
83
  throw error;
64
84
  }
85
+ }
86
+ async function createKeystoreLocallyAsync(keystoreParams) {
65
87
  await fs_extra_1.default.mkdirp((0, paths_1.getTmpDirectory)());
66
88
  const keystorePath = path_1.default.join((0, paths_1.getTmpDirectory)(), `${(0, uuid_1.v4)()}-keystore.jks`);
67
89
  try {
@@ -71,13 +93,13 @@ async function createKeystoreAsync(credentials, projectId) {
71
93
  '-storetype',
72
94
  'JKS',
73
95
  '-storepass',
74
- credentials.keystorePassword,
96
+ keystoreParams.keystorePassword,
75
97
  '-keypass',
76
- credentials.keyPassword,
98
+ keystoreParams.keyPassword,
77
99
  '-keystore',
78
100
  keystorePath,
79
101
  '-alias',
80
- credentials.keyAlias,
102
+ keystoreParams.keyAlias,
81
103
  '-keyalg',
82
104
  'RSA',
83
105
  '-keysize',
@@ -87,27 +109,40 @@ async function createKeystoreAsync(credentials, projectId) {
87
109
  '-dname',
88
110
  `CN=,OU=,O=,L=,S=,C=US`,
89
111
  ]);
90
- analytics_1.default.logEvent(analytics_1.Event.ANDROID_KEYSTORE_CREATE, {
91
- project_id: projectId,
92
- step: KeystoreCreateStep.Success,
93
- type: generated_1.AndroidKeystoreType.Jks,
94
- });
95
112
  return {
96
- ...credentials,
113
+ ...keystoreParams,
97
114
  keystore: await fs_extra_1.default.readFile(keystorePath, 'base64'),
98
115
  type: generated_1.AndroidKeystoreType.Jks,
99
116
  };
100
117
  }
101
- catch (error) {
102
- analytics_1.default.logEvent(analytics_1.Event.ANDROID_KEYSTORE_CREATE, {
103
- project_id: projectId,
104
- step: KeystoreCreateStep.Fail,
105
- reason: error.message,
106
- type: generated_1.AndroidKeystoreType.Jks,
107
- });
108
- throw error;
109
- }
110
118
  finally {
111
119
  await fs_extra_1.default.remove(keystorePath);
112
120
  }
113
121
  }
122
+ async function createKeystoreInCloudAsync(keystoreParams, { showKeytoolDetectionMsg = true } = {}) {
123
+ if (showKeytoolDetectionMsg) {
124
+ log_1.default.log(`Detected that you do not have ${chalk_1.default.bold('keytool')} installed locally.`);
125
+ }
126
+ const spinner = (0, ora_1.ora)('Generating keystore in the cloud...').start();
127
+ try {
128
+ const url = await KeystoreGenerationUrlMutation_1.KeystoreGenerationUrlMutation.createKeystoreGenerationUrlAsync();
129
+ const response = await (0, node_fetch_1.default)(url, {
130
+ method: 'POST',
131
+ body: JSON.stringify(keystoreParams),
132
+ headers: { 'Content-Type': 'application/json' },
133
+ });
134
+ const result = await response.json();
135
+ spinner.succeed();
136
+ return {
137
+ type: generated_1.AndroidKeystoreType.Jks,
138
+ keystore: result.keystoreBase64,
139
+ keystorePassword: result.keystorePassword,
140
+ keyAlias: result.keyAlias,
141
+ keyPassword: result.keyPassword,
142
+ };
143
+ }
144
+ catch (err) {
145
+ spinner.fail();
146
+ throw err;
147
+ }
148
+ }
@@ -46,14 +46,14 @@ async function getMinimalAscApiKeyAsync(ascApiKey) {
46
46
  exports.getMinimalAscApiKeyAsync = getMinimalAscApiKeyAsync;
47
47
  async function provideOrGenerateAscApiKeyAsync(ctx, purpose) {
48
48
  if (ctx.nonInteractive) {
49
- return await generateAscApiKeyAsync(ctx, purpose);
49
+ throw new Error(`A new App Store Connect API Key cannot be created in non-interactive mode.`);
50
50
  }
51
51
  const userProvided = await promptForAscApiKeyAsync(ctx);
52
52
  if (!userProvided) {
53
53
  return await generateAscApiKeyAsync(ctx, purpose);
54
54
  }
55
55
  if (!ctx.appStore.authCtx) {
56
- log_1.default.warn('Unable to validate App Store Connect Api Key, you are not authenticated with Apple.');
56
+ log_1.default.warn('Unable to validate App Store Connect API Key, you are not authenticated with Apple.');
57
57
  return userProvided;
58
58
  }
59
59
  const isValidAndTracked = await (0, validateAscApiKey_1.isAscApiKeyValidAndTrackedAsync)(ctx, userProvided);
@@ -61,7 +61,7 @@ async function provideOrGenerateAscApiKeyAsync(ctx, purpose) {
61
61
  return userProvided;
62
62
  }
63
63
  const useUserProvided = await (0, prompts_1.confirmAsync)({
64
- message: `App Store Connect Api Key with ID ${userProvided.keyId} is not valid on Apple's servers. Proceed anyway?`,
64
+ message: `App Store Connect API Key with ID ${userProvided.keyId} is not valid on Apple's servers. Proceed anyway?`,
65
65
  });
66
66
  if (useUserProvided) {
67
67
  return userProvided;
@@ -70,6 +70,7 @@ async function provideOrGenerateAscApiKeyAsync(ctx, purpose) {
70
70
  }
71
71
  exports.provideOrGenerateAscApiKeyAsync = provideOrGenerateAscApiKeyAsync;
72
72
  async function generateAscApiKeyAsync(ctx, purpose) {
73
+ //console.log('debug');
73
74
  await ctx.appStore.ensureAuthenticatedAsync();
74
75
  const ascApiKey = await ctx.appStore.createAscApiKeyAsync({
75
76
  nickname: getAscApiKeyName(purpose),
@@ -97,7 +98,7 @@ async function promptForKeyP8AndIdAsync() {
97
98
  const { keyP8Path } = await (0, prompts_1.promptAsync)({
98
99
  type: 'text',
99
100
  name: 'keyP8Path',
100
- message: 'Path to App Store Connect Api Key:',
101
+ message: 'Path to App Store Connect API Key:',
101
102
  initial: 'AuthKey_ABCD.p8',
102
103
  // eslint-disable-next-line async-protect/async-suffix
103
104
  validate: async (filePath) => {
@@ -146,10 +147,10 @@ async function selectAscApiKeysFromAccountAsync(ctx, account, { filterDifferentA
146
147
  if (ascApiKeysForAccount.length === 0) {
147
148
  if (filterDifferentAppleTeam) {
148
149
  const maybeAppleTeamId = (_a = ctx.appStore.authCtx) === null || _a === void 0 ? void 0 : _a.team.id;
149
- log_1.default.warn(`There are no App Store Connect Api Keys in your EAS account${maybeAppleTeamId ? ` matching Apple Team ID: ${maybeAppleTeamId}.` : '.'}`);
150
+ log_1.default.warn(`There are no App Store Connect API Keys in your EAS account${maybeAppleTeamId ? ` matching Apple Team ID: ${maybeAppleTeamId}.` : '.'}`);
150
151
  }
151
152
  else {
152
- log_1.default.warn(`There are no App Store Connect Api Keys available in your EAS account.`);
153
+ log_1.default.warn(`There are no App Store Connect API Keys available in your EAS account.`);
153
154
  }
154
155
  return null;
155
156
  }
@@ -161,7 +162,7 @@ async function selectAscApiKeysAsync(ascApiKeys) {
161
162
  const { chosenAscApiKey } = await (0, prompts_1.promptAsync)({
162
163
  type: 'select',
163
164
  name: 'chosenAscApiKey',
164
- message: 'Select an Api Key from the list:',
165
+ message: 'Select an API Key from the list:',
165
166
  choices: sortedAscApiKeys.map(ascApiKey => ({
166
167
  title: formatAscApiKey(ascApiKey),
167
168
  value: ascApiKey,
@@ -22,7 +22,7 @@ class AssignAscApiKey {
22
22
  else {
23
23
  throw new Error(`${purpose} is not yet supported.`);
24
24
  }
25
- log_1.default.succeed(`App Store Connect Api Key assigned to ${this.app.projectName}: ${this.app.bundleIdentifier} for ${purpose}.`);
25
+ log_1.default.succeed(`App Store Connect API Key assigned to ${this.app.projectName}: ${this.app.bundleIdentifier} for ${purpose}.`);
26
26
  return updatedAppCredentials;
27
27
  }
28
28
  }
@@ -10,11 +10,11 @@ class CreateAscApiKey {
10
10
  }
11
11
  async runAsync(ctx, purpose) {
12
12
  if (ctx.nonInteractive) {
13
- throw new Error(`A new App Store Connect Api Key cannot be created in non-interactive mode.`);
13
+ throw new Error(`A new App Store Connect API Key cannot be created in non-interactive mode.`);
14
14
  }
15
15
  const ascApiKey = await (0, AscApiKeyUtils_1.provideOrGenerateAscApiKeyAsync)(ctx, purpose);
16
16
  const result = await ctx.ios.createAscApiKeyAsync(this.account, ascApiKey);
17
- log_1.default.succeed('Created App Store Connect Api Key');
17
+ log_1.default.succeed('Created App Store Connect API Key');
18
18
  return result;
19
19
  }
20
20
  }