eas-cli 0.59.0 → 1.1.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 (57) hide show
  1. package/README.md +50 -47
  2. package/build/build/android/build.js +1 -1
  3. package/build/build/build.d.ts +1 -2
  4. package/build/build/build.js +6 -11
  5. package/build/build/runBuildAndSubmit.js +2 -0
  6. package/build/build/utils/printBuildInfo.js +2 -2
  7. package/build/build/utils/repository.js +4 -4
  8. package/build/commands/branch/create.js +1 -1
  9. package/build/commands/branch/delete.js +1 -1
  10. package/build/commands/branch/rename.js +2 -2
  11. package/build/commands/channel/create.js +1 -1
  12. package/build/commands/channel/delete.js +1 -1
  13. package/build/commands/channel/edit.js +3 -3
  14. package/build/commands/channel/rollout.js +3 -3
  15. package/build/commands/channel/view.js +1 -1
  16. package/build/commands/credentials.d.ts +3 -0
  17. package/build/commands/credentials.js +6 -1
  18. package/build/commands/metadata/pull.js +2 -2
  19. package/build/commands/project/init.js +3 -1
  20. package/build/commands/secret/list.js +1 -1
  21. package/build/commands/update/index.js +4 -4
  22. package/build/commands/update/list.js +1 -1
  23. package/build/credentials/android/actions/RemoveKeystore.js +1 -1
  24. package/build/credentials/errors.js +1 -1
  25. package/build/credentials/ios/actions/DistributionCertificateUtils.js +2 -2
  26. package/build/credentials/ios/actions/SetUpAdhocProvisioningProfile.js +1 -1
  27. package/build/credentials/ios/actions/SetUpInternalProvisioningProfile.js +2 -2
  28. package/build/credentials/ios/actions/SetUpProvisioningProfile.js +1 -1
  29. package/build/credentials/ios/actions/SetUpSubmissionCredentials.js +1 -1
  30. package/build/credentials/ios/appstore/contractMessages.js +1 -1
  31. package/build/credentials/ios/appstore/ensureAppExists.js +3 -3
  32. package/build/credentials/ios/appstore/pushKey.js +2 -2
  33. package/build/credentials/manager/SelectIosDistributionTypeGraphqlFromBuildProfile.js +1 -1
  34. package/build/credentials/manager/SelectPlatform.d.ts +2 -0
  35. package/build/credentials/manager/SelectPlatform.js +7 -12
  36. package/build/credentials/utils/promptForCredentials.js +1 -1
  37. package/build/devices/manager.js +1 -1
  38. package/build/graphql/client.js +1 -1
  39. package/build/graphql/generated.d.ts +35 -0
  40. package/build/metadata/config.d.ts +19 -0
  41. package/build/metadata/config.js +45 -1
  42. package/build/metadata/download.js +6 -5
  43. package/build/metadata/errors.js +2 -2
  44. package/build/metadata/upload.js +18 -21
  45. package/build/ora.js +1 -1
  46. package/build/project/metroConfig.js +1 -1
  47. package/build/project/projectUtils.d.ts +20 -2
  48. package/build/project/projectUtils.js +58 -32
  49. package/build/project/publish.js +8 -2
  50. package/build/submit/utils/errors.js +9 -9
  51. package/build/submit/utils/wait.js +1 -7
  52. package/build/update/utils.js +1 -1
  53. package/build/uploads.d.ts +1 -1
  54. package/build/uploads.js +64 -25
  55. package/build/vcs/clients/git.js +6 -2
  56. package/oclif.manifest.json +1 -1
  57. package/package.json +15 -14
@@ -48,7 +48,7 @@ async function startRolloutAsync({ channelName, branchName, percent, jsonFlag, p
48
48
  name: branchName,
49
49
  });
50
50
  if (!branch) {
51
- throw new Error(`Could not find a branch named "${branchName}". Please check what branches exist on this project with ${chalk_1.default.bold('eas branch:list')}.`);
51
+ throw new Error(`Could not find a branch named "${branchName}". Check which branches exist on this project with ${chalk_1.default.bold('eas branch:list')}.`);
52
52
  }
53
53
  const oldBranchId = currentBranchMapping.data[0].branchId;
54
54
  if (branch.id === oldBranchId) {
@@ -114,7 +114,7 @@ async function endRolloutAsync({ channelName, branchName, jsonFlag, projectId, c
114
114
  name: branchName,
115
115
  });
116
116
  if (!branch) {
117
- throw new Error(`Could not find a branch named "${branchName}". Please check what branches exist on this project with ${chalk_1.default.bold('eas branch:list')}.`);
117
+ throw new Error(`Could not find a branch named "${branchName}". Check which branches exist on this project with ${chalk_1.default.bold('eas branch:list')}.`);
118
118
  }
119
119
  switch (branch.id) {
120
120
  case newBranch.id:
@@ -176,7 +176,7 @@ class ChannelRollout extends EasCommand_1.default {
176
176
  channelName: channelName,
177
177
  });
178
178
  if (!channel) {
179
- throw new Error(`Could not find a channel named "${channelName}". Please check what channels exist on this project with ${chalk_1.default.bold('eas channel:list')}.`);
179
+ throw new Error(`Could not find a channel named "${channelName}". Check which channels exist on this project with ${chalk_1.default.bold('eas channel:list')}.`);
180
180
  }
181
181
  const { branchMapping: currentBranchMapping, isRollout } = (0, view_1.getBranchMapping)(channel.branchMapping);
182
182
  if (currentBranchMapping.data.length === 0) {
@@ -119,7 +119,7 @@ class ChannelView extends EasCommand_1.default {
119
119
  ({ name: channelName } = await (0, prompts_1.promptAsync)({
120
120
  type: 'text',
121
121
  name: 'name',
122
- message: 'Please name the channel:',
122
+ message: 'Provide a channel name:',
123
123
  validate: value => (value ? true : validationMessage),
124
124
  }));
125
125
  }
@@ -1,5 +1,8 @@
1
1
  import EasCommand from '../commandUtils/EasCommand';
2
2
  export default class Credentials extends EasCommand {
3
3
  static description: string;
4
+ static flags: {
5
+ platform: import("@oclif/core/lib/interfaces").OptionFlag<string>;
6
+ };
4
7
  runAsync(): Promise<void>;
5
8
  }
@@ -1,12 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
+ const core_1 = require("@oclif/core");
4
5
  const EasCommand_1 = tslib_1.__importDefault(require("../commandUtils/EasCommand"));
5
6
  const SelectPlatform_1 = require("../credentials/manager/SelectPlatform");
6
7
  class Credentials extends EasCommand_1.default {
7
8
  async runAsync() {
8
- await new SelectPlatform_1.SelectPlatform().runAsync();
9
+ const { flags } = await this.parse(Credentials);
10
+ await new SelectPlatform_1.SelectPlatform(flags.platform).runAsync();
9
11
  }
10
12
  }
11
13
  exports.default = Credentials;
12
14
  Credentials.description = 'manage credentials';
15
+ Credentials.flags = {
16
+ platform: core_1.Flags.enum({ char: 'p', options: ['android', 'ios'] }),
17
+ };
@@ -38,11 +38,11 @@ class MetadataPull extends EasCommand_1.default {
38
38
  const filePath = await (0, download_1.downloadMetadataAsync)(metadataCtx);
39
39
  const relativePath = path_1.default.relative(process.cwd(), filePath);
40
40
  log_1.default.addNewLineIfNone();
41
- log_1.default.log(`🎉 Your store configuration is ready.
41
+ log_1.default.log(`🎉 Your store config is ready.
42
42
 
43
43
  - Update the ${chalk_1.default.bold(relativePath)} file to prepare the app information.
44
44
  - Run ${chalk_1.default.bold('eas submit')} or manually upload a new app version to the app stores.
45
- - Once the app is uploaded, run ${chalk_1.default.bold('eas metadata:push')} to sync the store configuration.
45
+ - Once the app is uploaded, run ${chalk_1.default.bold('eas metadata:push')} to sync the store config.
46
46
  - ${(0, log_1.learnMore)('https://docs.expo.dev/eas-metadata/introduction/')}`);
47
47
  }
48
48
  catch (error) {
@@ -15,7 +15,9 @@ class ProjectInit extends EasCommand_1.default {
15
15
  log_1.default.error(`app.json is already linked to project with ID: ${chalk_1.default.bold((_d = (_c = exp.extra) === null || _c === void 0 ? void 0 : _c.eas) === null || _d === void 0 ? void 0 : _d.projectId)}`);
16
16
  return;
17
17
  }
18
- await (0, projectUtils_1.setProjectIdAsync)(projectDir);
18
+ const projectId = await (0, projectUtils_1.fetchProjectIdFromServerAsync)(exp);
19
+ await (0, projectUtils_1.saveProjectIdToAppConfigAsync)(projectDir, projectId);
20
+ log_1.default.withTick(`Linked app.json to project with ID ${chalk_1.default.bold(projectId)}`);
19
21
  }
20
22
  }
21
23
  exports.default = ProjectInit;
@@ -16,7 +16,7 @@ class EnvironmentSecretList extends EasCommand_1.default {
16
16
  const projectId = await (0, projectUtils_1.getProjectIdAsync)(exp);
17
17
  const projectAccountName = await (0, projectUtils_1.getProjectAccountNameAsync)(exp);
18
18
  if (!projectDir) {
19
- throw new Error("Please run this command inside your project's directory");
19
+ throw new Error("Run this command inside your project's directory");
20
20
  }
21
21
  const secrets = await EnvironmentSecretsQuery_1.EnvironmentSecretsQuery.allAsync(projectAccountName, projectId);
22
22
  const table = new cli_table3_1.default({
@@ -153,7 +153,7 @@ class UpdatePublish extends EasCommand_1.default {
153
153
  ({ name: branchName } = await (0, prompts_1.promptAsync)({
154
154
  type: 'text',
155
155
  name: 'name',
156
- message: 'No branches found. Creating a new one. Please name the new branch:',
156
+ message: 'No branches found. Provide a branch name:',
157
157
  initial: (await (0, vcs_1.getVcsClient)().getBranchNameAsync()) ||
158
158
  `branch-${Math.random().toString(36).substr(2, 4)}`,
159
159
  validate: value => (value ? true : validationMessage),
@@ -229,7 +229,7 @@ class UpdatePublish extends EasCommand_1.default {
229
229
  ({ publishMessage: message } = await (0, prompts_1.promptAsync)({
230
230
  type: 'text',
231
231
  name: 'publishMessage',
232
- message: `Please enter an update message.`,
232
+ message: `Provide an update message.`,
233
233
  initial: `Republish "${oldMessage}" - group: ${group}`,
234
234
  validate: (value) => (value ? true : validationMessage),
235
235
  }));
@@ -250,7 +250,7 @@ class UpdatePublish extends EasCommand_1.default {
250
250
  ({ publishMessage: message } = await (0, prompts_1.promptAsync)({
251
251
  type: 'text',
252
252
  name: 'publishMessage',
253
- message: `Please enter an update message.`,
253
+ message: `Provide an update message.`,
254
254
  initial: (_c = (await (0, vcs_1.getVcsClient)().getLastCommitMessageAsync())) === null || _c === void 0 ? void 0 : _c.trim(),
255
255
  validate: (value) => (value ? true : validationMessage),
256
256
  }));
@@ -529,7 +529,7 @@ async function checkEASUpdateURLIsSetAsync(exp) {
529
529
  const projectId = await (0, projectUtils_1.getProjectIdAsync)(exp);
530
530
  const expectedURL = (0, api_1.getEASUpdateURL)(projectId);
531
531
  if (configuredURL !== expectedURL) {
532
- throw new Error(`The update URL is incorrectly configured for EAS Update. Please set updates.url to ${expectedURL} in your app.json.`);
532
+ throw new Error(`The update URL is incorrectly configured for EAS Update. Set updates.url to ${expectedURL} in your ${chalk_1.default.bold('app.json')}.`);
533
533
  }
534
534
  }
535
535
  const truncatePublishUpdateMessage = (originalMessage) => {
@@ -47,7 +47,7 @@ class BranchView extends EasCommand_1.default {
47
47
  ({ name: branchInteractive } = await (0, prompts_1.promptAsync)({
48
48
  type: 'text',
49
49
  name: 'name',
50
- message: 'Please enter the name of the branch whose updates you wish to view:',
50
+ message: 'Provide the name of the branch whose updates you wish to view:',
51
51
  initial: (_a = (await (0, vcs_1.getVcsClient)().getBranchNameAsync())) !== null && _a !== void 0 ? _a : undefined,
52
52
  validate: (value) => (value ? true : validationMessage),
53
53
  }));
@@ -35,7 +35,7 @@ class RemoveKeystore {
35
35
  log_1.default.newLine();
36
36
  log_1.default.warn(`Clearing your Android build credentials from our build servers is a ${chalk_1.default.bold('PERMANENT and IRREVERSIBLE action.')}`);
37
37
  log_1.default.warn(chalk_1.default.bold('Android Keystore must be identical to the one previously used to submit your app to the Google Play Store.'));
38
- log_1.default.warn('Please read https://docs.expo.dev/distribution/building-standalone-apps/#if-you-choose-to-build-for-android for more info before proceeding.');
38
+ log_1.default.warn('Read https://docs.expo.dev/distribution/building-standalone-apps/#if-you-choose-to-build-for-android for more info before proceeding.');
39
39
  log_1.default.newLine();
40
40
  log_1.default.warn(chalk_1.default.bold('Your Keystore will be backed up to your current directory if you continue.'));
41
41
  log_1.default.newLine();
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.UnsupportedCredentialsChoiceError = exports.MissingCredentialsError = exports.MissingCredentialsNonInteractiveError = void 0;
4
4
  class MissingCredentialsNonInteractiveError extends Error {
5
5
  constructor(message) {
6
- super(message !== null && message !== void 0 ? message : 'Credentials are not set up. Please run this command again in interactive mode.');
6
+ super(message !== null && message !== void 0 ? message : 'Credentials are not set up. Run this command again in interactive mode.');
7
7
  }
8
8
  }
9
9
  exports.MissingCredentialsNonInteractiveError = MissingCredentialsNonInteractiveError;
@@ -96,8 +96,8 @@ async function selectValidDistributionCertificateAsync(ctx, appLookupParams) {
96
96
  exports.selectValidDistributionCertificateAsync = selectValidDistributionCertificateAsync;
97
97
  const APPLE_DIST_CERTS_TOO_MANY_GENERATED_ERROR = `
98
98
  You can have only ${chalk_1.default.underline('three')} Apple Distribution Certificates generated on your Apple Developer account.
99
- Please revoke the old ones or reuse existing from your other apps.
100
- Please remember that Apple Distribution Certificates are not application specific!
99
+ Revoke the old ones or reuse existing from your other apps.
100
+ Remember that Apple Distribution Certificates are not application specific!
101
101
  `;
102
102
  async function provideOrGenerateDistributionCertificateAsync(ctx) {
103
103
  if (!ctx.nonInteractive) {
@@ -34,7 +34,7 @@ class SetUpAdhocProvisioningProfile {
34
34
  return (0, nullthrows_1.default)(await (0, BuildCredentialsUtils_1.getBuildCredentialsAsync)(ctx, this.app, generated_1.IosDistributionType.AdHoc));
35
35
  }
36
36
  else {
37
- throw new errors_1.MissingCredentialsNonInteractiveError('Provisioning profile is not configured correctly. Please run this command again in interactive mode.');
37
+ throw new errors_1.MissingCredentialsNonInteractiveError('Provisioning profile is not configured correctly. Run this command again in interactive mode.');
38
38
  }
39
39
  }
40
40
  const currentBuildCredentials = await (0, BuildCredentialsUtils_1.getBuildCredentialsAsync)(ctx, this.app, generated_1.IosDistributionType.AdHoc);
@@ -59,7 +59,7 @@ class SetUpInternalProvisioningProfile {
59
59
  }
60
60
  else {
61
61
  if (adhocBuildCredentialsExist && enterpriseBuildCredentialsExist) {
62
- throw new Error(`You're in non-interactive mode. You have set up both adhoc and universal distribution credentials. Please set the 'enterpriseProvisioning' property (to 'adhoc' or 'universal') in eas.json to choose the credentials to use.`);
62
+ throw new Error(`You're in non-interactive mode. You have set up both adhoc and universal distribution credentials. Set the 'enterpriseProvisioning' property (to 'adhoc' or 'universal') in eas.json to choose the credentials to use.`);
63
63
  }
64
64
  else if (adhocBuildCredentialsExist) {
65
65
  return await this.setupAdhocProvisioningProfileAsync(ctx);
@@ -68,7 +68,7 @@ class SetUpInternalProvisioningProfile {
68
68
  return await this.setupUniversalProvisioningProfileAsync(ctx);
69
69
  }
70
70
  else {
71
- throw new Error(`You're in non-interactive mode. EAS CLI couldn't find any credentials suitable for internal distribution. Please run again in interactive mode.`);
71
+ throw new Error(`You're in non-interactive mode. EAS CLI couldn't find any credentials suitable for internal distribution. Run this command again in interactive mode.`);
72
72
  }
73
73
  }
74
74
  }
@@ -48,7 +48,7 @@ class SetUpProvisioningProfile {
48
48
  return (0, nullthrows_1.default)(await (0, BuildCredentialsUtils_1.getBuildCredentialsAsync)(ctx, this.app, this.distributionType));
49
49
  }
50
50
  if (ctx.nonInteractive) {
51
- throw new errors_1.MissingCredentialsNonInteractiveError('Provisioning profile is not configured correctly. Please run this command again in interactive mode.');
51
+ throw new errors_1.MissingCredentialsNonInteractiveError('Provisioning profile is not configured correctly. Run this command again in interactive mode.');
52
52
  }
53
53
  const currentProfile = await (0, BuildCredentialsUtils_1.getProvisioningProfileAsync)(ctx, this.app, this.distributionType);
54
54
  if (!currentProfile) {
@@ -38,7 +38,7 @@ class SetUpSubmissionCredentials {
38
38
  }
39
39
  async promptForAppSpecificPasswordAsync() {
40
40
  log_1.default.addNewLineIfNone();
41
- log_1.default.log(`Please enter your Apple app-specific password.`);
41
+ log_1.default.log(`Enter your Apple app-specific password.`);
42
42
  log_1.default.log((0, log_1.learnMore)('https://expo.fyi/apple-app-specific-password'));
43
43
  log_1.default.warn((0, wrap_ansi_1.default)(`This option will be deprecated soon. You will still be able to provide your password with the ${chalk_1.default.bold('EXPO_APPLE_APP_SPECIFIC_PASSWORD')} environment variable.`, process.stdout.columns || 80));
44
44
  const { appSpecificPassword } = await (0, prompts_1.promptAsync)({
@@ -59,7 +59,7 @@ async function getRequiredContractMessagesAsync(context) {
59
59
  // There is a small chance that this could result in a false positive if the messages are extraneous, so we'll also
60
60
  // prompt the user to open an issue so we can address the new contract state if it ever appears.
61
61
  // TODO: Maybe a silent analytic would be better
62
- log_1.default.error(`\nUnexpected Apple developer contract status "${status}". Please open an issue on https://github.com/expo/eas-cli`);
62
+ log_1.default.error(`\nUnexpected Apple developer contract status "${status}". Open an issue on https://github.com/expo/eas-cli`);
63
63
  log_1.default.newLine();
64
64
  return { messages: (_b = (await getContractMessagesAsync(context))) !== null && _b !== void 0 ? _b : [], isFatal: false };
65
65
  }
@@ -36,7 +36,7 @@ async function ensureBundleIdExistsWithNameAsync(authCtx, { name, bundleIdentifi
36
36
  }
37
37
  catch (err) {
38
38
  if (err.message.match(/An App ID with Identifier '(.*)' is not available/)) {
39
- spinner.fail(`The bundle identifier ${chalk_1.default.bold(bundleIdentifier)} is not available to team "${authCtx.team.name}" (${authCtx.team.id}), please change it in your app config and try again.`);
39
+ spinner.fail(`The bundle identifier ${chalk_1.default.bold(bundleIdentifier)} is not available to team "${authCtx.team.name}" (${authCtx.team.id}), change it in your app config and try again.`);
40
40
  }
41
41
  else {
42
42
  spinner.fail(`Failed to register bundle identifier ${chalk_1.default.dim(bundleIdentifier)}`);
@@ -123,11 +123,11 @@ async function ensureAppExistsAsync(userAuthCtx, { name, language, companyName,
123
123
  }
124
124
  catch (error) {
125
125
  if (error.message.match(/An App ID with Identifier '(.*)' is not available/)) {
126
- throw new Error(`\nThe bundle identifier "${bundleIdentifier}" is not available to provider "${(_a = userAuthCtx.authState) === null || _a === void 0 ? void 0 : _a.session.provider.name}. Please change it in your app config and try again.\n`);
126
+ throw new Error(`\nThe bundle identifier "${bundleIdentifier}" is not available to provider "${(_a = userAuthCtx.authState) === null || _a === void 0 ? void 0 : _a.session.provider.name}. Change it in your app config and try again.\n`);
127
127
  }
128
128
  spinner.fail(`Failed to create App Store app ${chalk_1.default.dim(name)}`);
129
129
  error.message +=
130
- '\nPlease visit https://appstoreconnect.apple.com and resolve any warnings, then try again.';
130
+ '\nVisit https://appstoreconnect.apple.com and resolve any warnings, then try again.';
131
131
  throw error;
132
132
  }
133
133
  }
@@ -11,8 +11,8 @@ const authenticate_1 = require("./authenticate");
11
11
  const { MaxKeysCreatedError } = apple_utils_1.Keys;
12
12
  exports.APPLE_KEYS_TOO_MANY_GENERATED_ERROR = `
13
13
  You can have only ${chalk_1.default.underline('two')} Apple Keys generated on your Apple Developer account.
14
- Please revoke the old ones or reuse existing from your other apps.
15
- Please remember that Apple Keys are not application specific!
14
+ Revoke the old ones or reuse existing from your other apps.
15
+ Remember that Apple Keys are not application specific!
16
16
  `;
17
17
  /**
18
18
  * List all existing push keys on Apple servers.
@@ -39,7 +39,7 @@ class SelectIosDistributionTypeGraphqlFromBuildProfile {
39
39
  return generated_1.IosDistributionType.AdHoc;
40
40
  }
41
41
  if (ctx.nonInteractive) {
42
- throw new Error('Unable to determine type of internal distribution. Please run this command in interactive mode.');
42
+ throw new Error('Unable to determine type of internal distribution. Run this command in interactive mode.');
43
43
  }
44
44
  // ask the user as a last resort
45
45
  const { iosDistributionTypeGraphql } = await (0, prompts_1.promptAsync)({
@@ -1,3 +1,5 @@
1
1
  export declare class SelectPlatform {
2
+ private readonly flagPlatform?;
3
+ constructor(flagPlatform?: string | undefined);
2
4
  runAsync(): Promise<void>;
3
5
  }
@@ -1,24 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SelectPlatform = void 0;
4
- const prompts_1 = require("../../prompts");
4
+ const platform_1 = require("../../platform");
5
5
  const ManageAndroid_1 = require("./ManageAndroid");
6
6
  const ManageIos_1 = require("./ManageIos");
7
7
  class SelectPlatform {
8
+ constructor(flagPlatform) {
9
+ this.flagPlatform = flagPlatform;
10
+ }
8
11
  async runAsync() {
9
- const { platform } = await (0, prompts_1.promptAsync)({
10
- type: 'select',
11
- name: 'platform',
12
- message: 'Select platform',
13
- choices: [
14
- { value: 'android', title: 'Android' },
15
- { value: 'ios', title: 'iOS' },
16
- ],
17
- });
12
+ const platform = await (0, platform_1.selectPlatformAsync)(this.flagPlatform);
18
13
  if (platform === 'ios') {
19
- return await new ManageIos_1.ManageIos(new SelectPlatform(), process.cwd()).runAsync();
14
+ return await new ManageIos_1.ManageIos(new SelectPlatform(platform), process.cwd()).runAsync();
20
15
  }
21
- return await new ManageAndroid_1.ManageAndroid(new SelectPlatform(), process.cwd()).runAsync();
16
+ return await new ManageAndroid_1.ManageAndroid(new SelectPlatform(platform), process.cwd()).runAsync();
22
17
  }
23
18
  }
24
19
  exports.SelectPlatform = SelectPlatform;
@@ -14,7 +14,7 @@ const EXPERT_PROMPT = () => {
14
14
  }
15
15
  log_1.default.warn(`
16
16
  In this mode, we won't be able to make sure that your credentials are valid.
17
- Please double check that you're uploading valid files for your app otherwise you may encounter strange errors!
17
+ Double check that you're uploading valid files for your app otherwise you may encounter strange errors!
18
18
  When building for IOS make sure you've created your App ID on the Apple Developer Portal, that your App ID
19
19
  is in app.json as \`bundleIdentifier\`, and that the provisioning profile you
20
20
  upload matches that Team ID and App ID.
@@ -17,7 +17,7 @@ Internal distribution means that you won't need upload your app archive to App S
17
17
  Your app archive (.ipa) will be installable on your equipment as long as you sign your application with an adhoc provisiong profile.
18
18
  The provisioning profile needs to contain the UDIDs (unique identifiers) of your iPhones and iPads.
19
19
 
20
- First of all, please choose the Expo account under which you want to register your devices.
20
+ First of all, choose the Expo account under which you want to register your devices.
21
21
  Later, authenticate with Apple and choose your desired Apple Team (if your Apple ID has access to multiple teams).`;
22
22
  class DeviceManager {
23
23
  constructor(ctx) {
@@ -47,7 +47,7 @@ async function withErrorHandlingAsync(promise) {
47
47
  const { data, error } = await promise;
48
48
  if (error) {
49
49
  if (error.graphQLErrors.some(e => { var _a; return (_a = e === null || e === void 0 ? void 0 : e.extensions) === null || _a === void 0 ? void 0 : _a.isTransient; })) {
50
- log_1.default.error(`We've encountered a transient error, please try again shortly.`);
50
+ log_1.default.error(`We've encountered a transient error. Try again shortly.`);
51
51
  }
52
52
  throw error;
53
53
  }
@@ -102,6 +102,8 @@ export declare type Account = {
102
102
  createdAt: Scalars['DateTime'];
103
103
  /** Environment secrets for an account */
104
104
  environmentSecrets: Array<EnvironmentSecret>;
105
+ /** GitHub App installations for an account */
106
+ githubAppInstallations: Array<GitHubAppInstallation>;
105
107
  /** Android credentials for account */
106
108
  googleServiceAccountKeys: Array<GoogleServiceAccountKey>;
107
109
  id: Scalars['ID'];
@@ -1815,6 +1817,10 @@ export declare type CreateEnvironmentSecretInput = {
1815
1817
  name: Scalars['String'];
1816
1818
  value: Scalars['String'];
1817
1819
  };
1820
+ export declare type CreateGitHubAppInstallationInput = {
1821
+ accountId: Scalars['ID'];
1822
+ installationIdentifier: Scalars['Int'];
1823
+ };
1818
1824
  export declare type CreateIosSubmissionInput = {
1819
1825
  appId: Scalars['ID'];
1820
1826
  archiveUrl?: InputMaybe<Scalars['String']>;
@@ -2011,6 +2017,31 @@ export declare type GetSignedAssetUploadSpecificationsResult = {
2011
2017
  __typename?: 'GetSignedAssetUploadSpecificationsResult';
2012
2018
  specifications: Array<Scalars['String']>;
2013
2019
  };
2020
+ export declare type GitHubAppInstallation = {
2021
+ __typename?: 'GitHubAppInstallation';
2022
+ account: Account;
2023
+ actor?: Maybe<Actor>;
2024
+ id: Scalars['ID'];
2025
+ installationIdentifier: Scalars['Int'];
2026
+ };
2027
+ export declare type GitHubAppInstallationMutation = {
2028
+ __typename?: 'GitHubAppInstallationMutation';
2029
+ /** Create a GitHub App installation for an Account */
2030
+ createGitHubAppInstallationForAccount: GitHubAppInstallation;
2031
+ /** Delete a GitHub App installation by ID */
2032
+ deleteGitHubAppInstallation: GitHubAppInstallation;
2033
+ };
2034
+ export declare type GitHubAppInstallationMutationCreateGitHubAppInstallationForAccountArgs = {
2035
+ githubAppInstallationData: CreateGitHubAppInstallationInput;
2036
+ };
2037
+ export declare type GitHubAppInstallationMutationDeleteGitHubAppInstallationArgs = {
2038
+ githubAppInstallationId: Scalars['ID'];
2039
+ };
2040
+ export declare type GitHubAppQuery = {
2041
+ __typename?: 'GitHubAppQuery';
2042
+ appIdentifier: Scalars['String'];
2043
+ clientIdentifier: Scalars['String'];
2044
+ };
2014
2045
  export declare type GoogleServiceAccountKey = {
2015
2046
  __typename?: 'GoogleServiceAccountKey';
2016
2047
  account: Account;
@@ -2611,6 +2642,8 @@ export declare type RootMutation = {
2611
2642
  emailSubscription: EmailSubscriptionMutation;
2612
2643
  /** Mutations that create and delete EnvironmentSecrets */
2613
2644
  environmentSecret: EnvironmentSecretMutation;
2645
+ /** Mutations for GitHub App installations */
2646
+ githubAppInstallation: GitHubAppInstallationMutation;
2614
2647
  /** Mutations that modify a Google Service Account Key */
2615
2648
  googleServiceAccountKey: GoogleServiceAccountKeyMutation;
2616
2649
  /** Mutations that modify the build credentials for an iOS app */
@@ -2648,6 +2681,8 @@ export declare type RootMutationBuildJobArgs = {
2648
2681
  };
2649
2682
  export declare type RootQuery = {
2650
2683
  __typename?: 'RootQuery';
2684
+ /** Top-level query object for querying GitHub App information and resources it has access to. */
2685
+ GitHubApp: GitHubAppQuery;
2651
2686
  /**
2652
2687
  * This is a placeholder field
2653
2688
  * @deprecated Not used.
@@ -8,6 +8,25 @@ export interface MetadataConfig {
8
8
  /** All App Store related configuration */
9
9
  apple?: AppleMetadata;
10
10
  }
11
+ /**
12
+ * Get the static configuration file path, based on the metadata context.
13
+ * This uses any custom name provided, but swaps out the extension for `.json`.
14
+ */
15
+ export declare function getStaticConfigFilePath({ projectDir, metadataPath, }: {
16
+ projectDir: string;
17
+ metadataPath: string;
18
+ }): string;
19
+ /**
20
+ * Load the store configuration from a metadata context.
21
+ * This can load `.json` and `.js` config files, using `require`.
22
+ * It throws MetadataValidationErrors when the file doesn't exist, or contains errors.
23
+ * The user is prompted to try anyway when errors are found.
24
+ */
25
+ export declare function loadConfigAsync({ projectDir, metadataPath, skipValidation, }: {
26
+ projectDir: string;
27
+ metadataPath: string;
28
+ skipValidation?: boolean;
29
+ }): Promise<MetadataConfig>;
11
30
  /**
12
31
  * Run the JSON Schema validation to normalize defaults and flag early config errors.
13
32
  * This includes validating the known store limitations for every configurable property.
@@ -1,11 +1,55 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createAppleWriter = exports.createAppleReader = exports.validateConfig = void 0;
3
+ exports.createAppleWriter = exports.createAppleReader = exports.validateConfig = exports.loadConfigAsync = exports.getStaticConfigFilePath = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const ajv_1 = tslib_1.__importDefault(require("ajv"));
6
6
  const assert_1 = tslib_1.__importDefault(require("assert"));
7
+ const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
8
+ const path_1 = tslib_1.__importDefault(require("path"));
7
9
  const reader_1 = require("./apple/config/reader");
8
10
  const writer_1 = require("./apple/config/writer");
11
+ const errors_1 = require("./errors");
12
+ /**
13
+ * Resolve the dynamic config from the user.
14
+ * It supports methods, async methods, or objects (json).
15
+ */
16
+ async function resolveDynamicConfigAsync(configFile) {
17
+ const userConfigOrFunction = await Promise.resolve().then(() => tslib_1.__importStar(require(configFile))).then(file => { var _a; return (_a = file.default) !== null && _a !== void 0 ? _a : file; });
18
+ return typeof userConfigOrFunction === 'function'
19
+ ? await userConfigOrFunction()
20
+ : userConfigOrFunction;
21
+ }
22
+ /**
23
+ * Get the static configuration file path, based on the metadata context.
24
+ * This uses any custom name provided, but swaps out the extension for `.json`.
25
+ */
26
+ function getStaticConfigFilePath({ projectDir, metadataPath, }) {
27
+ const configFile = path_1.default.join(projectDir, metadataPath);
28
+ const configExtension = path_1.default.extname(configFile);
29
+ return path_1.default.join(projectDir, `${path_1.default.basename(configFile, configExtension)}.json`);
30
+ }
31
+ exports.getStaticConfigFilePath = getStaticConfigFilePath;
32
+ /**
33
+ * Load the store configuration from a metadata context.
34
+ * This can load `.json` and `.js` config files, using `require`.
35
+ * It throws MetadataValidationErrors when the file doesn't exist, or contains errors.
36
+ * The user is prompted to try anyway when errors are found.
37
+ */
38
+ async function loadConfigAsync({ projectDir, metadataPath, skipValidation = false, }) {
39
+ const configFile = path_1.default.join(projectDir, metadataPath);
40
+ if (!(await fs_extra_1.default.pathExists(configFile))) {
41
+ throw new errors_1.MetadataValidationError(`Metadata store config file not found: "${configFile}"`);
42
+ }
43
+ const configData = await resolveDynamicConfigAsync(configFile);
44
+ if (!skipValidation) {
45
+ const { valid, errors: validationErrors } = validateConfig(configData);
46
+ if (!valid) {
47
+ throw new errors_1.MetadataValidationError(`Metadata store config errors found`, validationErrors);
48
+ }
49
+ }
50
+ return configData;
51
+ }
52
+ exports.loadConfigAsync = loadConfigAsync;
9
53
  /**
10
54
  * Run the JSON Schema validation to normalize defaults and flag early config errors.
11
55
  * This includes validating the known store limitations for every configurable property.
@@ -17,20 +17,21 @@ const telemetry_1 = require("./utils/telemetry");
17
17
  * Note, only App Store is supported at this time.
18
18
  */
19
19
  async function downloadMetadataAsync(metadataCtx) {
20
- const filePath = path_1.default.resolve(metadataCtx.projectDir, metadataCtx.metadataPath);
20
+ const filePath = (0, config_1.getStaticConfigFilePath)(metadataCtx);
21
21
  const fileExists = await fs_extra_1.default.pathExists(filePath);
22
22
  if (fileExists) {
23
+ const filePathRelative = path_1.default.relative(metadataCtx.projectDir, filePath);
23
24
  const overwrite = await (0, prompts_1.confirmAsync)({
24
- message: `Do you want to overwrite the existing store configuration "${metadataCtx.metadataPath}"?`,
25
+ message: `Do you want to overwrite the existing "${filePathRelative}"?`,
25
26
  });
26
27
  if (!overwrite) {
27
- throw new errors_1.MetadataValidationError(`Store configuration already exists at "${filePath}"`);
28
+ throw new errors_1.MetadataValidationError(`Store config already exists at "${filePath}"`);
28
29
  }
29
30
  }
30
31
  const { app, auth } = await (0, context_1.ensureMetadataAppStoreAuthenticatedAsync)(metadataCtx);
31
32
  const { unsubscribeTelemetry, executionId } = (0, telemetry_1.subscribeTelemetry)(events_1.MetadataEvent.APPLE_METADATA_DOWNLOAD, { app, auth });
32
33
  log_1.default.addNewLineIfNone();
33
- log_1.default.log('Downloading App Store configuration...');
34
+ log_1.default.log('Downloading App Store config...');
34
35
  const errors = [];
35
36
  const config = (0, config_1.createAppleWriter)();
36
37
  const tasks = (0, tasks_1.createAppleTasks)(metadataCtx);
@@ -52,7 +53,7 @@ async function downloadMetadataAsync(metadataCtx) {
52
53
  }
53
54
  }
54
55
  try {
55
- await fs_extra_1.default.writeJson(filePath, config.toSchema(), { spaces: 2 });
56
+ await fs_extra_1.default.writeJSON(filePath, config.toSchema(), { spaces: 2 });
56
57
  }
57
58
  finally {
58
59
  unsubscribeTelemetry();
@@ -75,8 +75,8 @@ function handleMetadataError(error) {
75
75
  log_1.default.error(error.errors.map(err => err.message).join('\n\n'));
76
76
  }
77
77
  log_1.default.newLine();
78
- log_1.default.log('Please check the logs for any configuration issues.');
79
- log_1.default.log('If this issue persists, please open a new issue at:');
78
+ log_1.default.log('Check the logs for any configuration issues.');
79
+ log_1.default.log('If this issue persists, open a new issue at:');
80
80
  // TODO: add execution ID to the issue template link
81
81
  log_1.default.log((0, log_1.link)('https://github.com/expo/eas-cli'));
82
82
  return;
@@ -2,8 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.uploadMetadataAsync = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
6
- const path_1 = tslib_1.__importDefault(require("path"));
7
5
  const events_1 = require("../analytics/events");
8
6
  const log_1 = tslib_1.__importDefault(require("../log"));
9
7
  const prompts_1 = require("../prompts");
@@ -18,30 +16,13 @@ const telemetry_1 = require("./utils/telemetry");
18
16
  */
19
17
  async function uploadMetadataAsync(metadataCtx) {
20
18
  var _a;
21
- const filePath = path_1.default.resolve(metadataCtx.projectDir, metadataCtx.metadataPath);
22
- if (!(await fs_extra_1.default.pathExists(filePath))) {
23
- throw new errors_1.MetadataValidationError(`Store configuration file not found "${filePath}"`);
24
- }
25
- const fileData = await fs_extra_1.default.readJson(filePath);
26
- const { valid, errors: validationErrors } = (0, config_1.validateConfig)(fileData);
27
- if (!valid) {
28
- const error = new errors_1.MetadataValidationError(`Store configuration errors found`, validationErrors);
29
- (0, errors_1.logMetadataValidationError)(error);
30
- log_1.default.newLine();
31
- log_1.default.warn('Without further updates, the current store configuration may fail to be synchronized with the App Store or pass App Store review.');
32
- const attempt = await (0, prompts_1.confirmAsync)({
33
- message: 'Do you still want to push the store configuration?',
34
- });
35
- if (!attempt) {
36
- throw error;
37
- }
38
- }
19
+ const storeConfig = await loadConfigWithValidationPromptAsync(metadataCtx);
39
20
  const { app, auth } = await (0, context_1.ensureMetadataAppStoreAuthenticatedAsync)(metadataCtx);
40
21
  const { unsubscribeTelemetry, executionId } = (0, telemetry_1.subscribeTelemetry)(events_1.MetadataEvent.APPLE_METADATA_UPLOAD, { app, auth });
41
22
  log_1.default.addNewLineIfNone();
42
23
  log_1.default.log('Uploading App Store configuration...');
43
24
  const errors = [];
44
- const config = (0, config_1.createAppleReader)(fileData);
25
+ const config = (0, config_1.createAppleReader)(storeConfig);
45
26
  const tasks = (0, tasks_1.createAppleTasks)(metadataCtx, {
46
27
  // We need to resolve a different version as soon as possible.
47
28
  // This version is the parent model of all changes we are going to push.
@@ -71,3 +52,19 @@ async function uploadMetadataAsync(metadataCtx) {
71
52
  return { appleLink: `https://appstoreconnect.apple.com/apps/${app.id}/appstore` };
72
53
  }
73
54
  exports.uploadMetadataAsync = uploadMetadataAsync;
55
+ async function loadConfigWithValidationPromptAsync(metadataCtx) {
56
+ try {
57
+ return await (0, config_1.loadConfigAsync)(metadataCtx);
58
+ }
59
+ catch (error) {
60
+ if (error instanceof errors_1.MetadataValidationError) {
61
+ (0, errors_1.logMetadataValidationError)(error);
62
+ log_1.default.newLine();
63
+ log_1.default.warn('Without further updates, the current store configuration can fail to be synchronized with the App Store or pass App Store review.');
64
+ if (await (0, prompts_1.confirmAsync)({ message: 'Do you still want to push the store configuration?' })) {
65
+ return await (0, config_1.loadConfigAsync)({ ...metadataCtx, skipValidation: true });
66
+ }
67
+ }
68
+ throw error;
69
+ }
70
+ }