eas-cli 2.4.1 → 2.5.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 (79) hide show
  1. package/README.md +47 -47
  2. package/build/analytics/{events.d.ts → AnalyticsManager.d.ts} +32 -3
  3. package/build/analytics/AnalyticsManager.js +188 -0
  4. package/build/analytics/common.d.ts +6 -7
  5. package/build/analytics/common.js +5 -5
  6. package/build/build/build.js +21 -21
  7. package/build/build/context.d.ts +3 -2
  8. package/build/build/createContext.d.ts +3 -1
  9. package/build/build/createContext.js +7 -5
  10. package/build/build/local.js +1 -1
  11. package/build/build/metadata.js +1 -1
  12. package/build/build/runBuildAndSubmit.d.ts +2 -1
  13. package/build/build/runBuildAndSubmit.js +5 -2
  14. package/build/commandUtils/EasCommand.d.ts +19 -2
  15. package/build/commandUtils/EasCommand.js +31 -17
  16. package/build/commandUtils/context/AnalyticsContextField.d.ts +5 -0
  17. package/build/commandUtils/context/AnalyticsContextField.js +10 -0
  18. package/build/commandUtils/context/ContextField.d.ts +2 -0
  19. package/build/commands/analytics.js +3 -3
  20. package/build/commands/build/index.d.ts +1 -0
  21. package/build/commands/build/index.js +3 -2
  22. package/build/commands/build/inspect.d.ts +1 -0
  23. package/build/commands/build/inspect.js +3 -2
  24. package/build/commands/credentials.d.ts +1 -0
  25. package/build/commands/credentials.js +3 -2
  26. package/build/commands/metadata/pull.d.ts +1 -0
  27. package/build/commands/metadata/pull.js +4 -1
  28. package/build/commands/metadata/push.d.ts +1 -0
  29. package/build/commands/metadata/push.js +4 -1
  30. package/build/commands/project/init.d.ts +3 -0
  31. package/build/commands/project/init.js +88 -4
  32. package/build/commands/secret/create.js +5 -5
  33. package/build/commands/secret/push.d.ts +16 -0
  34. package/build/commands/secret/push.js +149 -0
  35. package/build/commands/submit.d.ts +2 -1
  36. package/build/commands/submit.js +3 -1
  37. package/build/commands/update/index.js +2 -2
  38. package/build/commands/update/view.js +1 -14
  39. package/build/credentials/android/actions/CreateKeystore.js +3 -3
  40. package/build/credentials/android/utils/keystore.d.ts +2 -1
  41. package/build/credentials/android/utils/keystore.js +7 -7
  42. package/build/credentials/context.d.ts +3 -0
  43. package/build/credentials/context.js +1 -0
  44. package/build/credentials/ios/actions/AscApiKeyUtils.js +1 -1
  45. package/build/credentials/ios/appstore/AppStoreApi.d.ts +2 -1
  46. package/build/credentials/ios/appstore/AppStoreApi.js +2 -2
  47. package/build/credentials/ios/appstore/ascApiKey.d.ts +3 -2
  48. package/build/credentials/ios/appstore/ascApiKey.js +7 -7
  49. package/build/credentials/manager/HelperActions.d.ts +2 -0
  50. package/build/credentials/manager/ManageAndroid.js +1 -0
  51. package/build/credentials/manager/ManageIos.js +1 -0
  52. package/build/credentials/manager/SelectPlatform.d.ts +3 -1
  53. package/build/credentials/manager/SelectPlatform.js +2 -1
  54. package/build/metadata/context.d.ts +4 -0
  55. package/build/metadata/context.js +1 -0
  56. package/build/metadata/download.js +2 -2
  57. package/build/metadata/upload.js +2 -2
  58. package/build/metadata/utils/telemetry.d.ts +2 -2
  59. package/build/metadata/utils/telemetry.js +3 -4
  60. package/build/project/projectUtils.js +1 -3
  61. package/build/submit/BaseSubmitter.d.ts +4 -4
  62. package/build/submit/BaseSubmitter.js +8 -8
  63. package/build/submit/android/AndroidSubmitter.js +7 -7
  64. package/build/submit/context.d.ts +4 -2
  65. package/build/submit/context.js +6 -5
  66. package/build/submit/ios/IosSubmitter.js +7 -7
  67. package/build/submit/submit.js +6 -6
  68. package/build/update/utils.d.ts +1 -0
  69. package/build/update/utils.js +11 -1
  70. package/build/user/SessionManager.d.ts +4 -1
  71. package/build/user/SessionManager.js +10 -13
  72. package/build/utils/expodash/intersection.d.ts +1 -0
  73. package/build/utils/expodash/intersection.js +8 -0
  74. package/build/vcs/clients/git.js +13 -2
  75. package/oclif.manifest.json +1 -1
  76. package/package.json +3 -2
  77. package/build/analytics/events.js +0 -59
  78. package/build/analytics/rudderstackClient.d.ts +0 -7
  79. package/build/analytics/rudderstackClient.js +0 -112
@@ -14,7 +14,7 @@ class MetadataPush extends EasCommand_1.default {
14
14
  async runAsync() {
15
15
  log_1.default.warn('EAS Metadata is in beta and subject to breaking changes.');
16
16
  const { flags } = await this.parse(MetadataPush);
17
- const { loggedIn: { actor, graphqlClient }, projectConfig: { exp, projectId, projectDir }, } = await this.getContextAsync(MetadataPush, {
17
+ const { loggedIn: { actor, graphqlClient }, projectConfig: { exp, projectId, projectDir }, analytics, } = await this.getContextAsync(MetadataPush, {
18
18
  nonInteractive: false,
19
19
  });
20
20
  // this command is interactive (all nonInteractive flags passed to utility functions are false)
@@ -24,10 +24,12 @@ class MetadataPush extends EasCommand_1.default {
24
24
  projectDir,
25
25
  user: actor,
26
26
  graphqlClient,
27
+ analytics,
27
28
  nonInteractive: false,
28
29
  });
29
30
  const metadataCtx = await (0, context_2.createMetadataContextAsync)({
30
31
  credentialsCtx,
32
+ analytics,
31
33
  projectDir,
32
34
  exp,
33
35
  profileName: flags.profile,
@@ -56,4 +58,5 @@ MetadataPush.flags = {
56
58
  MetadataPush.contextDefinition = {
57
59
  ..._a.ContextOptions.ProjectConfig,
58
60
  ..._a.ContextOptions.LoggedIn,
61
+ ..._a.ContextOptions.Analytics,
59
62
  };
@@ -12,6 +12,9 @@ export default class ProjectInit extends EasCommand {
12
12
  loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
13
13
  };
14
14
  private static saveProjectIdAndLogSuccessAsync;
15
+ private static modifyExpoConfigAsync;
16
+ private static ensureOwnerSlugConsistencyAsync;
17
+ private static setExplicitIDAsync;
15
18
  private static initializeWithExplicitIDAsync;
16
19
  private static initializeWithInteractiveSelectionAsync;
17
20
  runAsync(): Promise<void>;
@@ -2,6 +2,7 @@
2
2
  var _a;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const tslib_1 = require("tslib");
5
+ const config_1 = require("@expo/config");
5
6
  const core_1 = require("@oclif/core");
6
7
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
8
  const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
@@ -11,6 +12,7 @@ const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasComm
11
12
  const getProjectIdAsync_1 = require("../../commandUtils/context/contextUtils/getProjectIdAsync");
12
13
  const generated_1 = require("../../graphql/generated");
13
14
  const AppMutation_1 = require("../../graphql/mutations/AppMutation");
15
+ const AppQuery_1 = require("../../graphql/queries/AppQuery");
14
16
  const log_1 = tslib_1.__importDefault(require("../../log"));
15
17
  const ora_1 = require("../../ora");
16
18
  const expoConfig_1 = require("../../project/expoConfig");
@@ -22,7 +24,77 @@ class ProjectInit extends EasCommand_1.default {
22
24
  await (0, getProjectIdAsync_1.saveProjectIdToAppConfigAsync)(projectDir, projectId);
23
25
  log_1.default.withTick(`Project successfully linked (ID: ${chalk_1.default.bold(projectId)}) (modified app.json)`);
24
26
  }
25
- static async initializeWithExplicitIDAsync(projectId, projectDir, { force, nonInteractive }) {
27
+ static async modifyExpoConfigAsync(projectDir, modifications) {
28
+ const result = await (0, config_1.modifyConfigAsync)(projectDir, modifications);
29
+ switch (result.type) {
30
+ case 'success':
31
+ break;
32
+ case 'warn': {
33
+ log_1.default.warn();
34
+ log_1.default.warn(`Warning: Your project uses dynamic app configuration, and cannot be automatically modified.`);
35
+ log_1.default.warn(chalk_1.default.dim('https://docs.expo.dev/workflow/configuration/#dynamic-configuration-with-appconfigjs'));
36
+ log_1.default.warn();
37
+ log_1.default.warn(`To complete the setup process, add the following in your ${chalk_1.default.bold((0, config_1.getProjectConfigDescription)(projectDir))}:`);
38
+ log_1.default.warn();
39
+ log_1.default.warn(chalk_1.default.bold(JSON.stringify(modifications, null, 2)));
40
+ log_1.default.warn();
41
+ throw new Error(result.message);
42
+ }
43
+ case 'fail':
44
+ throw new Error(result.message);
45
+ default:
46
+ throw new Error('Unexpected result type from modifyConfigAsync');
47
+ }
48
+ }
49
+ static async ensureOwnerSlugConsistencyAsync(graphqlClient, projectId, projectDir, { force, nonInteractive }) {
50
+ const exp = (0, expoConfig_1.getExpoConfig)(projectDir);
51
+ const appForProjectId = await AppQuery_1.AppQuery.byIdAsync(graphqlClient, projectId);
52
+ const correctOwner = appForProjectId.ownerAccount.name;
53
+ const correctSlug = appForProjectId.slug;
54
+ if (exp.owner && exp.owner !== correctOwner) {
55
+ if (force) {
56
+ await this.modifyExpoConfigAsync(projectDir, { owner: correctOwner });
57
+ }
58
+ else {
59
+ const message = `Project owner (${correctOwner}) does not match the value configured in the "owner" field (${exp.owner}).`;
60
+ if (nonInteractive) {
61
+ throw new Error(`Project config error: ${message} Use --force flag to overwrite.`);
62
+ }
63
+ const confirm = await (0, prompts_1.confirmAsync)({
64
+ message: `${message}. Do you wish to overwrite it?`,
65
+ });
66
+ if (!confirm) {
67
+ throw new Error('Aborting');
68
+ }
69
+ await this.modifyExpoConfigAsync(projectDir, { owner: correctOwner });
70
+ }
71
+ }
72
+ else if (!exp.owner) {
73
+ await this.modifyExpoConfigAsync(projectDir, { owner: correctOwner });
74
+ }
75
+ if (exp.slug && exp.slug !== correctSlug) {
76
+ if (force) {
77
+ await this.modifyExpoConfigAsync(projectDir, { slug: correctSlug });
78
+ }
79
+ else {
80
+ const message = `Project slug (${correctSlug}) does not match the value configured in the "slug" field (${exp.slug}).`;
81
+ if (nonInteractive) {
82
+ throw new Error(`Project config error: ${message} Use --force flag to overwrite.`);
83
+ }
84
+ const confirm = await (0, prompts_1.confirmAsync)({
85
+ message: `${message}. Do you wish to overwrite it?`,
86
+ });
87
+ if (!confirm) {
88
+ throw new Error('Aborting');
89
+ }
90
+ await this.modifyExpoConfigAsync(projectDir, { slug: correctSlug });
91
+ }
92
+ }
93
+ else if (!exp.slug) {
94
+ await this.modifyExpoConfigAsync(projectDir, { slug: correctSlug });
95
+ }
96
+ }
97
+ static async setExplicitIDAsync(projectId, projectDir, { force, nonInteractive }) {
26
98
  var _c, _d;
27
99
  const exp = (0, expoConfig_1.getExpoConfig)(projectDir);
28
100
  const existingProjectId = (_d = (_c = exp.extra) === null || _c === void 0 ? void 0 : _c.eas) === null || _d === void 0 ? void 0 : _d.projectId;
@@ -46,12 +118,21 @@ class ProjectInit extends EasCommand_1.default {
46
118
  message: `Project is already linked to a different ID: ${chalk_1.default.bold(existingProjectId)}. Do you wish to overwrite it?`,
47
119
  });
48
120
  if (!confirm) {
49
- log_1.default.log('Aborting');
50
- return;
121
+ throw new Error('Aborting');
51
122
  }
52
123
  await ProjectInit.saveProjectIdAndLogSuccessAsync(projectDir, projectId);
53
124
  }
54
125
  }
126
+ static async initializeWithExplicitIDAsync(graphqlClient, projectId, projectDir, { force, nonInteractive }) {
127
+ await this.setExplicitIDAsync(projectId, projectDir, {
128
+ force,
129
+ nonInteractive,
130
+ });
131
+ await this.ensureOwnerSlugConsistencyAsync(graphqlClient, projectId, projectDir, {
132
+ force,
133
+ nonInteractive,
134
+ });
135
+ }
55
136
  static async initializeWithInteractiveSelectionAsync(graphqlClient, actor, projectDir) {
56
137
  var _c, _d, _e;
57
138
  const exp = (0, expoConfig_1.getExpoConfig)(projectDir);
@@ -141,7 +222,10 @@ class ProjectInit extends EasCommand_1.default {
141
222
  const { flags: { id, force, 'non-interactive': nonInteractive }, } = await this.parse(ProjectInit);
142
223
  const { loggedIn: { actor, graphqlClient }, projectDir, } = await this.getContextAsync(ProjectInit, { nonInteractive });
143
224
  if (id) {
144
- await ProjectInit.initializeWithExplicitIDAsync(id, projectDir, { force, nonInteractive });
225
+ await ProjectInit.initializeWithExplicitIDAsync(graphqlClient, id, projectDir, {
226
+ force,
227
+ nonInteractive,
228
+ });
145
229
  }
146
230
  else {
147
231
  await ProjectInit.initializeWithInteractiveSelectionAsync(graphqlClient, actor, projectDir);
@@ -91,7 +91,7 @@ class EnvironmentSecretCreate extends EasCommand_1.default {
91
91
  // eslint-disable-next-line async-protect/async-suffix
92
92
  validate: async (secretValue) => {
93
93
  if (!secretValue) {
94
- return 'Secret value may not be empty.';
94
+ return validationMessage;
95
95
  }
96
96
  if (secretType === EnvironmentSecret_1.SecretType.FILE) {
97
97
  const secretFilePath = path_1.default.resolve(secretValue);
@@ -126,10 +126,10 @@ class EnvironmentSecretCreate extends EasCommand_1.default {
126
126
  throw new Error(`Could not create secret with name ${name} on project with id ${projectId}`);
127
127
  }
128
128
  if (secretType === EnvironmentSecret_1.SecretType.STRING) {
129
- log_1.default.withTick(`️Created a new secret ${chalk_1.default.bold(name)} with value ${chalk_1.default.bold(secretValue)} on project ${chalk_1.default.bold(projectDisplayName)}.`);
129
+ log_1.default.withTick(`Created a new secret ${chalk_1.default.bold(name)} with value ${chalk_1.default.bold(secretValue)} on project ${chalk_1.default.bold(projectDisplayName)}.`);
130
130
  }
131
131
  else {
132
- log_1.default.withTick(`️Created a new secret ${chalk_1.default.bold(name)} from file ${chalk_1.default.bold(secretFilePath)} on project ${chalk_1.default.bold(projectDisplayName)}.`);
132
+ log_1.default.withTick(`Created a new secret ${chalk_1.default.bold(name)} from file ${chalk_1.default.bold(secretFilePath)} on project ${chalk_1.default.bold(projectDisplayName)}.`);
133
133
  }
134
134
  }
135
135
  else if (scope === EnvironmentSecretsQuery_1.EnvironmentSecretScope.ACCOUNT) {
@@ -146,10 +146,10 @@ class EnvironmentSecretCreate extends EasCommand_1.default {
146
146
  throw new Error(`Could not create secret with name ${name} on account with id ${ownerAccount.id}`);
147
147
  }
148
148
  if (secretType === EnvironmentSecret_1.SecretType.STRING) {
149
- log_1.default.withTick(`️Created a new secret ${chalk_1.default.bold(name)} with value ${chalk_1.default.bold(secretValue)} on account ${chalk_1.default.bold(ownerAccount.name)}.`);
149
+ log_1.default.withTick(`Created a new secret ${chalk_1.default.bold(name)} with value ${chalk_1.default.bold(secretValue)} on account ${chalk_1.default.bold(ownerAccount.name)}.`);
150
150
  }
151
151
  else {
152
- log_1.default.withTick(`️Created a new secret ${chalk_1.default.bold(name)} from file ${chalk_1.default.bold(secretFilePath)} on account ${chalk_1.default.bold(ownerAccount.name)}.`);
152
+ log_1.default.withTick(`Created a new secret ${chalk_1.default.bold(name)} from file ${chalk_1.default.bold(secretFilePath)} on account ${chalk_1.default.bold(ownerAccount.name)}.`);
153
153
  }
154
154
  }
155
155
  }
@@ -0,0 +1,16 @@
1
+ import EasCommand from '../../commandUtils/EasCommand';
2
+ import { EnvironmentSecretScope } from '../../graphql/queries/EnvironmentSecretsQuery';
3
+ export default class EnvironmentSecretPush extends EasCommand {
4
+ static description: string;
5
+ static flags: {
6
+ 'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
+ scope: import("@oclif/core/lib/interfaces").OptionFlag<EnvironmentSecretScope>;
8
+ 'env-file': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
9
+ force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ static contextDefinition: {
12
+ loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
13
+ projectConfig: import("../../commandUtils/context/ProjectConfigContextField").default;
14
+ };
15
+ runAsync(): Promise<void>;
16
+ }
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ var _a;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const tslib_1 = require("tslib");
5
+ const core_1 = require("@oclif/core");
6
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
+ const dotenv_1 = tslib_1.__importDefault(require("dotenv"));
8
+ const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
9
+ const path_1 = tslib_1.__importDefault(require("path"));
10
+ const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
11
+ const flags_1 = require("../../commandUtils/flags");
12
+ const generated_1 = require("../../graphql/generated");
13
+ const EnvironmentSecretMutation_1 = require("../../graphql/mutations/EnvironmentSecretMutation");
14
+ const EnvironmentSecretsQuery_1 = require("../../graphql/queries/EnvironmentSecretsQuery");
15
+ const log_1 = tslib_1.__importDefault(require("../../log"));
16
+ const ora_1 = require("../../ora");
17
+ const projectUtils_1 = require("../../project/projectUtils");
18
+ const prompts_1 = require("../../prompts");
19
+ const intersection_1 = tslib_1.__importDefault(require("../../utils/expodash/intersection"));
20
+ class EnvironmentSecretPush extends EasCommand_1.default {
21
+ async runAsync() {
22
+ const { flags: { scope, force, 'env-file': maybeEnvFilePath, 'non-interactive': nonInteractive }, } = await this.parse(EnvironmentSecretPush);
23
+ const { projectConfig: { projectId }, loggedIn: { graphqlClient }, } = await this.getContextAsync(EnvironmentSecretPush, {
24
+ nonInteractive,
25
+ });
26
+ const projectDisplayName = await (0, projectUtils_1.getDisplayNameForProjectIdAsync)(graphqlClient, projectId);
27
+ const ownerAccount = await (0, projectUtils_1.getOwnerAccountForProjectIdAsync)(graphqlClient, projectId);
28
+ const envFilePath = await resolveEnvFilePathAsync(maybeEnvFilePath, nonInteractive);
29
+ if (!(await fs_extra_1.default.pathExists(envFilePath))) {
30
+ throw new Error(`File "${envFilePath}" does not exist`);
31
+ }
32
+ const newSecrets = dotenv_1.default.parse(await fs_extra_1.default.readFile(envFilePath));
33
+ await ensureSecretsDontExistOnServerAsync(graphqlClient, { projectId, scope, force }, newSecrets);
34
+ await createSecretsAsync(graphqlClient, {
35
+ scope,
36
+ accountId: ownerAccount.id,
37
+ accountName: ownerAccount.name,
38
+ projectId,
39
+ projectDisplayName,
40
+ }, newSecrets);
41
+ log_1.default.withTick(`Created the following secrets on ${scope} ${chalk_1.default.bold(scope === EnvironmentSecretsQuery_1.EnvironmentSecretScope.ACCOUNT ? ownerAccount.name : projectDisplayName)}:`);
42
+ for (const secretName of Object.keys(newSecrets)) {
43
+ log_1.default.log(`- ${secretName}`);
44
+ }
45
+ }
46
+ }
47
+ exports.default = EnvironmentSecretPush;
48
+ _a = EnvironmentSecretPush;
49
+ EnvironmentSecretPush.description = 'read environment secrets from env file and store on the server';
50
+ EnvironmentSecretPush.flags = {
51
+ scope: core_1.Flags.enum({
52
+ description: 'Scope for the secrets',
53
+ options: [EnvironmentSecretsQuery_1.EnvironmentSecretScope.ACCOUNT, EnvironmentSecretsQuery_1.EnvironmentSecretScope.PROJECT],
54
+ default: EnvironmentSecretsQuery_1.EnvironmentSecretScope.PROJECT,
55
+ }),
56
+ 'env-file': core_1.Flags.string({
57
+ description: 'Env file with secrets',
58
+ }),
59
+ force: core_1.Flags.boolean({
60
+ description: 'Delete and recreate existing secrets',
61
+ default: false,
62
+ }),
63
+ ...flags_1.EASNonInteractiveFlag,
64
+ };
65
+ EnvironmentSecretPush.contextDefinition = {
66
+ ..._a.ContextOptions.ProjectConfig,
67
+ ..._a.ContextOptions.LoggedIn,
68
+ };
69
+ async function resolveEnvFilePathAsync(maybeEnvFilePath, nonInteractive) {
70
+ if (maybeEnvFilePath) {
71
+ return maybeEnvFilePath;
72
+ }
73
+ const validationMessage = 'Env file must be passed.';
74
+ if (nonInteractive) {
75
+ throw new Error(validationMessage);
76
+ }
77
+ const { envFilePath } = await (0, prompts_1.promptAsync)({
78
+ type: 'text',
79
+ name: 'envFilePath',
80
+ message: 'Path to the env file with secrets:',
81
+ // eslint-disable-next-line async-protect/async-suffix
82
+ validate: async (secretValueRaw) => {
83
+ if (!secretValueRaw) {
84
+ return validationMessage;
85
+ }
86
+ const envFilePath = path_1.default.resolve(secretValueRaw);
87
+ if (!(await fs_extra_1.default.pathExists(envFilePath))) {
88
+ return `File "${envFilePath}" does not exist.`;
89
+ }
90
+ return true;
91
+ },
92
+ });
93
+ return envFilePath;
94
+ }
95
+ async function ensureSecretsDontExistOnServerAsync(graphqlClient, { projectId, scope, force }, secrets) {
96
+ const serverSecrets = await readAllSecretsFromServerAsync(graphqlClient, projectId, scope);
97
+ const commonSecretNames = findCommonSecretNames(serverSecrets, secrets);
98
+ if (commonSecretNames.length > 0) {
99
+ if (!force) {
100
+ log_1.default.log(`This ${scope} already has environment secrets with the following names:`);
101
+ for (const name of commonSecretNames) {
102
+ log_1.default.log(`- ${name}`);
103
+ }
104
+ log_1.default.error('Run with --force flag to proceed');
105
+ core_1.Errors.exit(1);
106
+ }
107
+ else {
108
+ const spinner = (0, ora_1.ora)('Deleting secrets already present on server...').start();
109
+ const commonSecretNameSet = new Set(commonSecretNames);
110
+ const commonServerSecrets = serverSecrets.filter(({ name }) => commonSecretNameSet.has(name));
111
+ await deleteSecretsAsync(graphqlClient, commonServerSecrets);
112
+ spinner.succeed();
113
+ }
114
+ }
115
+ }
116
+ async function readAllSecretsFromServerAsync(graphqlClient, projectId, scope) {
117
+ const { appSecrets, accountSecrets } = await EnvironmentSecretsQuery_1.EnvironmentSecretsQuery.byAppIdAsync(graphqlClient, projectId);
118
+ return scope === EnvironmentSecretsQuery_1.EnvironmentSecretScope.ACCOUNT ? accountSecrets : appSecrets;
119
+ }
120
+ function findCommonSecretNames(serverSecrets, newSecrets) {
121
+ const serverSecretKeys = serverSecrets.map(({ name }) => name);
122
+ const newSecretKeys = Object.keys(newSecrets);
123
+ const commonKeys = (0, intersection_1.default)(serverSecretKeys, newSecretKeys);
124
+ return commonKeys;
125
+ }
126
+ async function deleteSecretsAsync(graphqlClient, secrets) {
127
+ const promises = secrets.map(secret => EnvironmentSecretMutation_1.EnvironmentSecretMutation.deleteAsync(graphqlClient, secret.id));
128
+ await Promise.all(promises);
129
+ }
130
+ async function createSecretsAsync(graphqlClient, { scope, accountId, accountName, projectId, projectDisplayName, }, secrets) {
131
+ const promises = [];
132
+ const spinner = (0, ora_1.ora)(`Creating secrets on ${scope} ${chalk_1.default.bold(scope === EnvironmentSecretsQuery_1.EnvironmentSecretScope.ACCOUNT ? accountName : projectDisplayName)}...`).start();
133
+ for (const [secretName, secretValue] of Object.entries(secrets)) {
134
+ if (scope === EnvironmentSecretsQuery_1.EnvironmentSecretScope.ACCOUNT) {
135
+ promises.push(EnvironmentSecretMutation_1.EnvironmentSecretMutation.createForAccountAsync(graphqlClient, { name: secretName, value: secretValue, type: generated_1.EnvironmentSecretType.String }, accountId));
136
+ }
137
+ else {
138
+ promises.push(EnvironmentSecretMutation_1.EnvironmentSecretMutation.createForAppAsync(graphqlClient, { name: secretName, value: secretValue, type: generated_1.EnvironmentSecretType.String }, projectId));
139
+ }
140
+ }
141
+ try {
142
+ await Promise.all(promises);
143
+ spinner.succeed();
144
+ }
145
+ catch (err) {
146
+ spinner.fail();
147
+ throw err;
148
+ }
149
+ }
@@ -14,9 +14,10 @@ export default class Submit extends EasCommand {
14
14
  'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
15
15
  };
16
16
  static contextDefinition: {
17
+ analytics: import("../commandUtils/context/AnalyticsContextField").default;
17
18
  projectDir: import("../commandUtils/context/ProjectDirContextField").default;
18
- projectConfig: import("../commandUtils/context/ProjectConfigContextField").default;
19
19
  loggedIn: import("../commandUtils/context/LoggedInContextField").default;
20
+ projectConfig: import("../commandUtils/context/ProjectConfigContextField").default;
20
21
  };
21
22
  runAsync(): Promise<void>;
22
23
  private sanitizeFlags;
@@ -18,7 +18,7 @@ const statuspageService_1 = require("../utils/statuspageService");
18
18
  class Submit extends EasCommand_1.default {
19
19
  async runAsync() {
20
20
  const { flags: rawFlags } = await this.parse(Submit);
21
- const { loggedIn: { actor, graphqlClient }, projectConfig: { exp, projectId, projectDir }, } = await this.getContextAsync(Submit, {
21
+ const { loggedIn: { actor, graphqlClient }, projectConfig: { exp, projectId, projectDir }, analytics, } = await this.getContextAsync(Submit, {
22
22
  nonInteractive: false,
23
23
  });
24
24
  const flags = this.sanitizeFlags(rawFlags);
@@ -42,6 +42,7 @@ class Submit extends EasCommand_1.default {
42
42
  nonInteractive: flagsWithPlatform.nonInteractive,
43
43
  actor,
44
44
  graphqlClient,
45
+ analytics,
45
46
  exp,
46
47
  projectId,
47
48
  });
@@ -140,4 +141,5 @@ Submit.contextDefinition = {
140
141
  ..._a.ContextOptions.LoggedIn,
141
142
  ..._a.ContextOptions.ProjectConfig,
142
143
  ..._a.ContextOptions.ProjectDir,
144
+ ..._a.ContextOptions.Analytics,
143
145
  };
@@ -114,14 +114,14 @@ class UpdatePublish extends EasCommand_1.default {
114
114
  }
115
115
  if (!hasExpoUpdates) {
116
116
  const install = await (0, prompts_1.confirmAsync)({
117
- message: `You are creating an update which requires ${chalk_1.default.bold('expo-updates')} to be installed in your app.\n Do you want EAS CLI to install it for you?`,
117
+ message: (0, chalk_1.default) `The module {cyan expo-updates} must be installed to load EAS updates in-app. Install?`,
118
118
  instructions: 'The command will abort unless you agree.',
119
119
  });
120
120
  if (install) {
121
121
  await (0, projectUtils_1.installExpoUpdatesAsync)(projectDir);
122
122
  }
123
123
  else {
124
- core_1.Errors.error(`Install ${chalk_1.default.bold('expo-updates')} manually and come back later.`, {
124
+ core_1.Errors.error(`Install ${chalk_1.default.bold('expo-updates')} and try again.`, {
125
125
  exit: 1,
126
126
  });
127
127
  }
@@ -3,13 +3,11 @@ var _a;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const tslib_1 = require("tslib");
5
5
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
6
- const cli_table3_1 = tslib_1.__importDefault(require("cli-table3"));
7
6
  const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
8
7
  const flags_1 = require("../../commandUtils/flags");
9
8
  const UpdateQuery_1 = require("../../graphql/queries/UpdateQuery");
10
9
  const log_1 = tslib_1.__importDefault(require("../../log"));
11
10
  const utils_1 = require("../../update/utils");
12
- const formatFields_1 = tslib_1.__importDefault(require("../../utils/formatFields"));
13
11
  const json_1 = require("../../utils/json");
14
12
  class UpdateView extends EasCommand_1.default {
15
13
  async runAsync() {
@@ -24,19 +22,8 @@ class UpdateView extends EasCommand_1.default {
24
22
  (0, json_1.printJsonOnlyOutput)(updateGroupDescription);
25
23
  }
26
24
  else {
27
- const groupTable = new cli_table3_1.default({
28
- head: [...utils_1.UPDATE_COLUMNS],
29
- wordWrap: true,
30
- });
31
25
  log_1.default.log(chalk_1.default.bold('Update group:'));
32
- log_1.default.log((0, formatFields_1.default)([{ label: 'ID', value: updateGroupDescription.group }]));
33
- groupTable.push([
34
- updateGroupDescription.message,
35
- updateGroupDescription.runtimeVersion,
36
- updateGroupDescription.group,
37
- updateGroupDescription.platforms,
38
- ]);
39
- log_1.default.log(groupTable.toString());
26
+ log_1.default.log((0, utils_1.formatUpdateGroup)(updateGroupDescription));
40
27
  }
41
28
  }
42
29
  }
@@ -16,19 +16,19 @@ class CreateKeystore {
16
16
  throw new Error(`New keystore cannot be created in non-interactive mode.`);
17
17
  }
18
18
  const projectId = ctx.projectId;
19
- const keystore = await this.provideOrGenerateAsync(ctx.graphqlClient, projectId);
19
+ const keystore = await this.provideOrGenerateAsync(ctx.graphqlClient, ctx.analytics, projectId);
20
20
  const keystoreFragment = await ctx.android.createKeystoreAsync(ctx.graphqlClient, this.account, keystore);
21
21
  log_1.default.succeed('Created keystore');
22
22
  return keystoreFragment;
23
23
  }
24
- async provideOrGenerateAsync(graphqlClient, projectId) {
24
+ async provideOrGenerateAsync(graphqlClient, analytics, projectId) {
25
25
  const providedKeystore = await (0, promptForCredentials_1.askForUserProvidedAsync)(credentials_1.keystoreSchema);
26
26
  if (providedKeystore) {
27
27
  const providedKeystoreWithType = (0, keystoreNew_1.getKeystoreWithType)(providedKeystore);
28
28
  (0, keystoreNew_1.validateKeystore)(providedKeystoreWithType);
29
29
  return providedKeystoreWithType;
30
30
  }
31
- return await (0, keystore_1.generateRandomKeystoreAsync)(graphqlClient, projectId);
31
+ return await (0, keystore_1.generateRandomKeystoreAsync)(graphqlClient, analytics, projectId);
32
32
  }
33
33
  }
34
34
  exports.CreateKeystore = CreateKeystore;
@@ -1,4 +1,5 @@
1
+ import { Analytics } from '../../../analytics/AnalyticsManager';
1
2
  import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
2
3
  import { KeystoreWithType } from '../credentials';
3
4
  export declare function keytoolCommandExistsAsync(): Promise<boolean>;
4
- export declare function generateRandomKeystoreAsync(graphqlClient: ExpoGraphqlClient, projectId: string): Promise<KeystoreWithType>;
5
+ export declare function generateRandomKeystoreAsync(graphqlClient: ExpoGraphqlClient, analytics: Analytics, projectId: string): Promise<KeystoreWithType>;
@@ -8,7 +8,7 @@ const crypto_1 = tslib_1.__importDefault(require("crypto"));
8
8
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
9
9
  const path_1 = tslib_1.__importDefault(require("path"));
10
10
  const uuid_1 = require("uuid");
11
- const events_1 = require("../../../analytics/events");
11
+ const AnalyticsManager_1 = require("../../../analytics/AnalyticsManager");
12
12
  const fetch_1 = tslib_1.__importDefault(require("../../../fetch"));
13
13
  const generated_1 = require("../../../graphql/generated");
14
14
  const KeystoreGenerationUrlMutation_1 = require("../../../graphql/mutations/KeystoreGenerationUrlMutation");
@@ -31,17 +31,17 @@ var KeystoreCreateStep;
31
31
  KeystoreCreateStep["Fail"] = "fail";
32
32
  KeystoreCreateStep["Success"] = "success";
33
33
  })(KeystoreCreateStep || (KeystoreCreateStep = {}));
34
- async function generateRandomKeystoreAsync(graphqlClient, projectId) {
34
+ async function generateRandomKeystoreAsync(graphqlClient, analytics, projectId) {
35
35
  const keystoreData = {
36
36
  keystorePassword: crypto_1.default.randomBytes(16).toString('hex'),
37
37
  keyPassword: crypto_1.default.randomBytes(16).toString('hex'),
38
38
  keyAlias: crypto_1.default.randomBytes(16).toString('hex'),
39
39
  };
40
- return await createKeystoreAsync(graphqlClient, keystoreData, projectId);
40
+ return await createKeystoreAsync(graphqlClient, analytics, keystoreData, projectId);
41
41
  }
42
42
  exports.generateRandomKeystoreAsync = generateRandomKeystoreAsync;
43
- async function createKeystoreAsync(graphqlClient, keystoreParams, projectId) {
44
- events_1.Analytics.logEvent(events_1.BuildEvent.ANDROID_KEYSTORE_CREATE, {
43
+ async function createKeystoreAsync(graphqlClient, analytics, keystoreParams, projectId) {
44
+ analytics.logEvent(AnalyticsManager_1.BuildEvent.ANDROID_KEYSTORE_CREATE, {
45
45
  project_id: projectId,
46
46
  step: KeystoreCreateStep.Attempt,
47
47
  type: generated_1.AndroidKeystoreType.Jks,
@@ -63,7 +63,7 @@ async function createKeystoreAsync(graphqlClient, keystoreParams, projectId) {
63
63
  showKeytoolDetectionMsg: !localAttempt,
64
64
  });
65
65
  }
66
- events_1.Analytics.logEvent(events_1.BuildEvent.ANDROID_KEYSTORE_CREATE, {
66
+ analytics.logEvent(AnalyticsManager_1.BuildEvent.ANDROID_KEYSTORE_CREATE, {
67
67
  project_id: projectId,
68
68
  step: KeystoreCreateStep.Success,
69
69
  type: generated_1.AndroidKeystoreType.Jks,
@@ -71,7 +71,7 @@ async function createKeystoreAsync(graphqlClient, keystoreParams, projectId) {
71
71
  return keystore;
72
72
  }
73
73
  catch (error) {
74
- events_1.Analytics.logEvent(events_1.BuildEvent.ANDROID_KEYSTORE_CREATE, {
74
+ analytics.logEvent(AnalyticsManager_1.BuildEvent.ANDROID_KEYSTORE_CREATE, {
75
75
  project_id: projectId,
76
76
  step: KeystoreCreateStep.Fail,
77
77
  reason: error.message,
@@ -1,6 +1,7 @@
1
1
  import { ExpoConfig } from '@expo/config';
2
2
  import { Env } from '@expo/eas-build-job';
3
3
  import { EasJson } from '@expo/eas-json';
4
+ import { Analytics } from '../analytics/AnalyticsManager';
4
5
  import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
5
6
  import { Actor } from '../user/User';
6
7
  import * as AndroidGraphqlClient from './android/api/GraphqlClient';
@@ -19,6 +20,7 @@ export declare class CredentialsContext {
19
20
  readonly projectDir: string;
20
21
  readonly user: Actor;
21
22
  readonly graphqlClient: ExpoGraphqlClient;
23
+ readonly analytics: Analytics;
22
24
  readonly easJsonCliConfig?: EasJson['cli'];
23
25
  private shouldAskAuthenticateAppStore;
24
26
  private projectInfo;
@@ -29,6 +31,7 @@ export declare class CredentialsContext {
29
31
  projectDir: string;
30
32
  user: Actor;
31
33
  graphqlClient: ExpoGraphqlClient;
34
+ analytics: Analytics;
32
35
  env?: Env;
33
36
  });
34
37
  static getExpoConfigInProject(projectDir: string, { env }?: {
@@ -22,6 +22,7 @@ class CredentialsContext {
22
22
  this.projectDir = options.projectDir;
23
23
  this.user = options.user;
24
24
  this.graphqlClient = options.graphqlClient;
25
+ this.analytics = options.analytics;
25
26
  this.nonInteractive = (_a = options.nonInteractive) !== null && _a !== void 0 ? _a : false;
26
27
  this.projectInfo = options.projectInfo;
27
28
  }
@@ -71,7 +71,7 @@ async function provideOrGenerateAscApiKeyAsync(ctx, purpose) {
71
71
  exports.provideOrGenerateAscApiKeyAsync = provideOrGenerateAscApiKeyAsync;
72
72
  async function generateAscApiKeyAsync(ctx, purpose) {
73
73
  await ctx.appStore.ensureAuthenticatedAsync();
74
- const ascApiKey = await ctx.appStore.createAscApiKeyAsync({
74
+ const ascApiKey = await ctx.appStore.createAscApiKeyAsync(ctx.analytics, {
75
75
  nickname: getAscApiKeyName(purpose),
76
76
  });
77
77
  return await getMinimalAscApiKeyAsync(ascApiKey);
@@ -1,5 +1,6 @@
1
1
  /// <reference types="@expo/apple-utils/ts-declarations/expo__app-store" />
2
2
  import { ProfileType } from '@expo/app-store';
3
+ import { Analytics } from '../../../analytics/AnalyticsManager';
3
4
  import { AscApiKey, AscApiKeyInfo, DistributionCertificate, DistributionCertificateStoreInfo, ProvisioningProfile, ProvisioningProfileStoreInfo, PushKey, PushKeyStoreInfo } from './Credentials.types';
4
5
  import { Options as AuthenticateOptions } from './authenticate';
5
6
  import { AuthCtx, AuthenticationMode, UserAuthCtx } from './authenticateTypes';
@@ -26,7 +27,7 @@ export default class AppStoreApi {
26
27
  createOrReuseAdhocProvisioningProfileAsync(udids: string[], bundleIdentifier: string, distCertSerialNumber: string, profileType: ProfileType): Promise<ProvisioningProfile>;
27
28
  listAscApiKeysAsync(): Promise<AscApiKeyInfo[]>;
28
29
  getAscApiKeyAsync(keyId: string): Promise<AscApiKeyInfo | null>;
29
- createAscApiKeyAsync({ nickname }: {
30
+ createAscApiKeyAsync(analytics: Analytics, { nickname }: {
30
31
  nickname: string;
31
32
  }): Promise<AscApiKey>;
32
33
  revokeAscApiKeyAsync(keyId: string): Promise<AscApiKeyInfo>;
@@ -93,9 +93,9 @@ class AppStoreApi {
93
93
  const userCtx = await this.ensureUserAuthenticatedAsync();
94
94
  return await (0, ascApiKey_1.getAscApiKeyAsync)(userCtx, keyId);
95
95
  }
96
- async createAscApiKeyAsync({ nickname }) {
96
+ async createAscApiKeyAsync(analytics, { nickname }) {
97
97
  const userCtx = await this.ensureUserAuthenticatedAsync();
98
- return await (0, ascApiKey_1.createAscApiKeyAsync)(userCtx, { nickname });
98
+ return await (0, ascApiKey_1.createAscApiKeyAsync)(analytics, userCtx, { nickname });
99
99
  }
100
100
  async revokeAscApiKeyAsync(keyId) {
101
101
  const userCtx = await this.ensureUserAuthenticatedAsync();
@@ -1,5 +1,6 @@
1
1
  /// <reference types="@expo/apple-utils/ts-declarations/expo__app-store" />
2
2
  import { ApiKey, ApiKeyProps } from '@expo/apple-utils';
3
+ import { Analytics } from '../../../analytics/AnalyticsManager';
3
4
  import { AscApiKey, AscApiKeyInfo } from './Credentials.types';
4
5
  import { AuthCtx, UserAuthCtx } from './authenticateTypes';
5
6
  /**
@@ -18,7 +19,7 @@ export declare function getAscApiKeyAsync(userAuthCtx: UserAuthCtx, keyId: strin
18
19
  * the resource does not exist. We retry with exponential backoff until the key propagates and
19
20
  * is available for download.
20
21
  * */
21
- export declare function downloadWithRetryAsync(key: ApiKey, { minTimeout, retries, factor, }?: {
22
+ export declare function downloadWithRetryAsync(analytics: Analytics, key: ApiKey, { minTimeout, retries, factor, }?: {
22
23
  minTimeout?: number;
23
24
  retries?: number;
24
25
  factor?: number;
@@ -27,7 +28,7 @@ export declare function downloadWithRetryAsync(key: ApiKey, { minTimeout, retrie
27
28
  * Create an App Store Connect API Key.
28
29
  * **Does not support App Store Connect API (CI).**
29
30
  */
30
- export declare function createAscApiKeyAsync(userAuthCtx: UserAuthCtx, { nickname, allAppsVisible, roles, keyType, }: Partial<Pick<ApiKeyProps, 'nickname' | 'roles' | 'allAppsVisible' | 'keyType'>>): Promise<AscApiKey>;
31
+ export declare function createAscApiKeyAsync(analytics: Analytics, userAuthCtx: UserAuthCtx, { nickname, allAppsVisible, roles, keyType, }: Partial<Pick<ApiKeyProps, 'nickname' | 'roles' | 'allAppsVisible' | 'keyType'>>): Promise<AscApiKey>;
31
32
  /**
32
33
  * Revoke an App Store Connect API Key.
33
34
  * **Does not support App Store Connect API (CI).**