eas-cli 16.32.0 → 18.0.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 (50) hide show
  1. package/README.md +94 -92
  2. package/build/branch/queries.js +6 -1
  3. package/build/build/createContext.js +5 -3
  4. package/build/build/queries.js +13 -1
  5. package/build/build/runBuildAndSubmit.js +2 -0
  6. package/build/build/utils/devClient.d.ts +1 -0
  7. package/build/build/utils/devClient.js +3 -2
  8. package/build/build/utils/repository.js +3 -3
  9. package/build/build/utils/resourceClass.d.ts +1 -1
  10. package/build/build/utils/resourceClass.js +2 -2
  11. package/build/channel/queries.js +10 -1
  12. package/build/commandUtils/new/templates/AGENTS.md +0 -1
  13. package/build/commands/account/login.d.ts +1 -0
  14. package/build/commands/account/login.js +7 -2
  15. package/build/commands/env/create.js +5 -2
  16. package/build/commands/env/list.js +1 -5
  17. package/build/commands/update/index.js +2 -2
  18. package/build/commands/upload.js +3 -3
  19. package/build/credentials/ios/api/graphql/mutations/AppleDistributionCertificateMutation.js +1 -3
  20. package/build/devices/queries.js +23 -2
  21. package/build/graphql/mutations/EnvironmentVariableMutation.js +1 -4
  22. package/build/graphql/mutations/PublishMutation.js +1 -4
  23. package/build/graphql/mutations/UserPreferencesMutation.js +1 -3
  24. package/build/graphql/mutations/WorkflowRevisionMutation.js +2 -10
  25. package/build/graphql/queries/AppVersionQuery.js +1 -5
  26. package/build/log.js +1 -1
  27. package/build/ora.js +1 -1
  28. package/build/project/discourageExpoGoForProdAsync.d.ts +4 -0
  29. package/build/project/discourageExpoGoForProdAsync.js +46 -0
  30. package/build/rollout/actions/ManageRollout.js +5 -1
  31. package/build/rollout/actions/SelectRuntime.js +10 -1
  32. package/build/update/queries.js +28 -2
  33. package/build/user/SessionManager.d.ts +3 -2
  34. package/build/user/SessionManager.js +6 -6
  35. package/build/user/expoBrowserAuthFlowLauncher.d.ts +3 -0
  36. package/build/user/{expoSsoLauncher.js → expoBrowserAuthFlowLauncher.js} +21 -47
  37. package/build/user/fetchSessionSecretAndUserFromBrowserAuthFlow.d.ts +7 -0
  38. package/build/user/fetchSessionSecretAndUserFromBrowserAuthFlow.js +14 -0
  39. package/build/utils/prompts.js +3 -3
  40. package/build/utils/usage/checkForOverages.js +4 -2
  41. package/build/worker/mutations.js +2 -8
  42. package/oclif.manifest.json +8 -48
  43. package/package.json +60 -63
  44. package/build/commands/submit/upload-to-asc.d.ts +0 -12
  45. package/build/commands/submit/upload-to-asc.js +0 -217
  46. package/build/submit/ios/AscApiClient.d.ts +0 -247
  47. package/build/submit/ios/AscApiClient.js +0 -287
  48. package/build/user/expoSsoLauncher.d.ts +0 -3
  49. package/build/user/fetchSessionSecretAndSsoUser.d.ts +0 -5
  50. package/build/user/fetchSessionSecretAndSsoUser.js +0 -17
@@ -20,7 +20,12 @@ const queries_1 = require("../utils/queries");
20
20
  exports.BRANCHES_LIMIT = 50;
21
21
  async function selectBranchOnAppAsync(graphqlClient, { projectId, promptTitle, displayTextForListItem, paginatedQueryOptions, }) {
22
22
  if (paginatedQueryOptions.nonInteractive) {
23
- throw new Error('Unable to select a branch in non-interactive mode.');
23
+ const branches = await queryBranchesOnProjectAsync(graphqlClient, paginatedQueryOptions.limit ?? exports.BRANCHES_LIMIT, paginatedQueryOptions.offset, projectId);
24
+ if (branches.length === 0) {
25
+ throw new Error(`No branches found for project "${projectId}".`);
26
+ }
27
+ const branchList = branches.map(b => b.name).join('\n ');
28
+ throw new Error(`Unable to select a branch in non-interactive mode. Use the --branch flag to specify the branch. Available branches:\n ${branchList}`);
24
29
  }
25
30
  const selectedBranch = await (0, queries_1.paginatedQueryWithSelectPromptAsync)({
26
31
  limit: paginatedQueryOptions.limit ?? exports.BRANCHES_LIMIT,
@@ -21,8 +21,10 @@ async function createBuildContextAsync({ buildProfileName, buildProfile, easJson
21
21
  env,
22
22
  });
23
23
  const projectName = exp.slug;
24
- const account = await (0, projectUtils_1.getOwnerAccountForProjectIdAsync)(graphqlClient, projectId);
25
- const workflow = await (0, workflow_1.resolveWorkflowAsync)(projectDir, platform, vcsClient);
24
+ const [account, workflow] = await Promise.all([
25
+ (0, projectUtils_1.getOwnerAccountForProjectIdAsync)(graphqlClient, projectId),
26
+ (0, workflow_1.resolveWorkflowAsync)(projectDir, platform, vcsClient),
27
+ ]);
26
28
  const accountId = account.id;
27
29
  const runFromCI = getenv_1.default.boolish('CI', false);
28
30
  const developmentClient = buildProfile.developmentClient ??
@@ -61,7 +63,7 @@ async function createBuildContextAsync({ buildProfileName, buildProfile, easJson
61
63
  local: localBuildOptions.localBuildMode === local_1.LocalBuildMode.LOCAL_BUILD_PLUGIN,
62
64
  };
63
65
  analytics.logEvent(AnalyticsManager_1.BuildEvent.BUILD_COMMAND, analyticsEventProperties);
64
- const resourceClass = await (0, resourceClass_1.resolveBuildResourceClassAsync)(buildProfile, platform, resourceClassFlag);
66
+ const resourceClass = (0, resourceClass_1.resolveBuildResourceClass)(buildProfile, platform, resourceClassFlag);
65
67
  const commonContext = {
66
68
  accountId,
67
69
  accountName: account.name,
@@ -45,7 +45,19 @@ async function listAndRenderBuildsOnAppAsync(graphqlClient, { projectId, project
45
45
  }
46
46
  async function listAndSelectBuildOnAppAsync(graphqlClient, { projectId, title, filter, paginatedQueryOptions, selectPromptDisabledFunction, selectPromptWarningMessage, }) {
47
47
  if (paginatedQueryOptions.nonInteractive) {
48
- throw new Error('Unable to select a build in non-interactive mode.');
48
+ const builds = await BuildQuery_1.BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
49
+ appId: projectId,
50
+ limit: paginatedQueryOptions.limit ?? exports.BUILDS_LIMIT,
51
+ offset: paginatedQueryOptions.offset,
52
+ filter,
53
+ });
54
+ if (builds.length === 0) {
55
+ throw new Error('No builds found for this project.');
56
+ }
57
+ const buildList = builds
58
+ .map(b => `${b.id} (platform: ${b.platform}, status: ${b.status}${b.buildProfile ? `, profile: ${b.buildProfile}` : ''})`)
59
+ .join('\n ');
60
+ throw new Error(`Unable to select a build in non-interactive mode. Available builds:\n ${buildList}`);
49
61
  }
50
62
  else {
51
63
  const selectedBuild = await (0, queries_1.paginatedQueryWithSelectPromptAsync)({
@@ -27,6 +27,7 @@ const AppPlatform_1 = require("../graphql/types/AppPlatform");
27
27
  const log_1 = tslib_1.__importStar(require("../log"));
28
28
  const platform_1 = require("../platform");
29
29
  const customBuildConfig_1 = require("../project/customBuildConfig");
30
+ const discourageExpoGoForProdAsync_1 = require("../project/discourageExpoGoForProdAsync");
30
31
  const expoSdk_1 = require("../project/expoSdk");
31
32
  const metroConfig_1 = require("../project/metroConfig");
32
33
  const projectUtils_1 = require("../project/projectUtils");
@@ -64,6 +65,7 @@ async function runBuildAndSubmitAsync({ graphqlClient, analytics, vcsClient, pro
64
65
  profileName: flags.profile ?? undefined,
65
66
  projectDir,
66
67
  });
68
+ await (0, discourageExpoGoForProdAsync_1.discourageExpoGoForProdAsync)(buildProfiles, projectDir, vcsClient);
67
69
  for (const buildProfile of buildProfiles) {
68
70
  if (buildProfile.profile.image && ['default', 'stable'].includes(buildProfile.profile.image)) {
69
71
  log_1.default.warn(`The "image" field in the build profile "${buildProfile.profileName}" is set to "${buildProfile.profile.image}". This tag is deprecated and will be removed in the future. Use other images or tags listed here: https://docs.expo.dev/build-reference/infrastructure/`);
@@ -6,3 +6,4 @@ export declare function ensureExpoDevClientInstalledForDevClientBuildsAsync({ pr
6
6
  nonInteractive?: boolean;
7
7
  buildProfiles?: ProfileData<'build'>[];
8
8
  }): Promise<void>;
9
+ export declare function isExpoDevClientInstalled(projectDir: string): boolean;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ensureExpoDevClientInstalledForDevClientBuildsAsync = ensureExpoDevClientInstalledForDevClientBuildsAsync;
4
+ exports.isExpoDevClientInstalled = isExpoDevClientInstalled;
4
5
  const tslib_1 = require("tslib");
5
6
  const eas_build_job_1 = require("@expo/eas-build-job");
6
7
  const core_1 = require("@oclif/core");
@@ -14,7 +15,7 @@ const workflow_1 = require("../../project/workflow");
14
15
  const prompts_1 = require("../../prompts");
15
16
  const expoCli_1 = require("../../utils/expoCli");
16
17
  async function ensureExpoDevClientInstalledForDevClientBuildsAsync({ projectDir, vcsClient, nonInteractive = false, buildProfiles = [], }) {
17
- if (await isExpoDevClientInstalledAsync(projectDir)) {
18
+ if (isExpoDevClientInstalled(projectDir)) {
18
19
  return;
19
20
  }
20
21
  const buildProfilesWithDevelopmentClientRequired = buildProfiles.filter(buildProfile => buildProfile.profile.developmentClient);
@@ -71,7 +72,7 @@ async function ensureExpoDevClientInstalledForDevClientBuildsAsync({ projectDir,
71
72
  }
72
73
  }
73
74
  }
74
- async function isExpoDevClientInstalledAsync(projectDir) {
75
+ function isExpoDevClientInstalled(projectDir) {
75
76
  try {
76
77
  (0, resolve_from_1.default)(projectDir, 'expo-dev-client/package.json');
77
78
  return true;
@@ -12,7 +12,7 @@ const tslib_1 = require("tslib");
12
12
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
13
13
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
14
14
  const path_1 = tslib_1.__importDefault(require("path"));
15
- const tar_1 = tslib_1.__importDefault(require("tar"));
15
+ const tar = tslib_1.__importStar(require("tar"));
16
16
  const uuid_1 = require("uuid");
17
17
  const log_1 = tslib_1.__importStar(require("../../log"));
18
18
  const ora_1 = require("../../ora");
@@ -78,7 +78,7 @@ async function makeProjectMetadataFileAsync(archivePath) {
78
78
  await fs_extra_1.default.mkdirp((0, paths_1.getTmpDirectory)());
79
79
  const metadataLocation = path_1.default.join((0, paths_1.getTmpDirectory)(), `${(0, uuid_1.v4)()}-eas-build-metadata.json`);
80
80
  const archiveContent = [];
81
- await tar_1.default.list({
81
+ await tar.list({
82
82
  file: archivePath,
83
83
  onentry: (entry) => {
84
84
  if (entry.type === 'File' && !entry.path.includes('.git/')) {
@@ -108,7 +108,7 @@ async function makeProjectTarballAsync(vcsClient) {
108
108
  (0, timer_1.startTimer)(compressTimerLabel);
109
109
  try {
110
110
  await vcsClient.makeShallowCopyAsync(shallowClonePath);
111
- await tar_1.default.create({ cwd: shallowClonePath, file: tarPath, prefix: 'project', gzip: true }, [
111
+ await tar.create({ cwd: shallowClonePath, file: tarPath, prefix: 'project', gzip: true }, [
112
112
  '.',
113
113
  ]);
114
114
  }
@@ -1,4 +1,4 @@
1
1
  import { Platform } from '@expo/eas-build-job';
2
2
  import { BuildProfile, ResourceClass } from '@expo/eas-json';
3
3
  import { BuildResourceClass } from '../../graphql/generated';
4
- export declare function resolveBuildResourceClassAsync<T extends Platform>(profile: BuildProfile<T>, platform: Platform, resourceClassFlag?: ResourceClass): Promise<BuildResourceClass>;
4
+ export declare function resolveBuildResourceClass<T extends Platform>(profile: BuildProfile<T>, platform: Platform, resourceClassFlag?: ResourceClass): BuildResourceClass;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveBuildResourceClassAsync = resolveBuildResourceClassAsync;
3
+ exports.resolveBuildResourceClass = resolveBuildResourceClass;
4
4
  const tslib_1 = require("tslib");
5
5
  const eas_build_job_1 = require("@expo/eas-build-job");
6
6
  const eas_json_1 = require("@expo/eas-json");
@@ -20,7 +20,7 @@ const androidResourceClassToBuildResourceClassMapping = {
20
20
  [eas_json_1.ResourceClass.LARGE]: generated_1.BuildResourceClass.AndroidLarge,
21
21
  [eas_json_1.ResourceClass.MEDIUM]: generated_1.BuildResourceClass.AndroidMedium,
22
22
  };
23
- async function resolveBuildResourceClassAsync(profile, platform, resourceClassFlag) {
23
+ function resolveBuildResourceClass(profile, platform, resourceClassFlag) {
24
24
  const profileResourceClass = profile.resourceClass;
25
25
  if (profileResourceClass && resourceClassFlag && resourceClassFlag !== profileResourceClass) {
26
26
  log_1.default.warn(`Build profile specifies the "${profileResourceClass}" resource class but you passed "${resourceClassFlag}" to --resource-class.\nUsing "${resourceClassFlag}" as the override.`);
@@ -28,7 +28,16 @@ const queries_2 = require("../utils/queries");
28
28
  exports.CHANNELS_LIMIT = 25;
29
29
  async function selectChannelOnAppAsync(graphqlClient, { projectId, selectionPromptTitle, paginatedQueryOptions, }) {
30
30
  if (paginatedQueryOptions.nonInteractive) {
31
- throw new Error('Unable to select a channel in non-interactive mode.');
31
+ const channels = await queryChannelsOnAppAsync(graphqlClient, {
32
+ appId: projectId,
33
+ limit: paginatedQueryOptions.limit ?? exports.CHANNELS_LIMIT,
34
+ offset: paginatedQueryOptions.offset,
35
+ });
36
+ if (channels.length === 0) {
37
+ throw new Error(`No channels found for project "${projectId}".`);
38
+ }
39
+ const channelList = channels.map(c => c.name).join('\n ');
40
+ throw new Error(`Unable to select a channel in non-interactive mode. Use the --channel flag to specify the channel. Available channels:\n ${channelList}`);
32
41
  }
33
42
  const updateChannel = await (0, queries_2.paginatedQueryWithSelectPromptAsync)({
34
43
  limit: paginatedQueryOptions.limit ?? exports.CHANNELS_LIMIT,
@@ -153,7 +153,6 @@ If there are errors in **Expo Go** or the project is not running, create a **dev
153
153
  When working on this project:
154
154
 
155
155
  1. **Always start by consulting the appropriate documentation**:
156
-
157
156
  - For general Expo questions: https://docs.expo.dev/llms-full.txt
158
157
  - For EAS/deployment questions: https://docs.expo.dev/llms-eas.txt
159
158
  - For SDK/API questions: https://docs.expo.dev/llms-sdk.txt
@@ -4,6 +4,7 @@ export default class AccountLogin extends EasCommand {
4
4
  static aliases: string[];
5
5
  static flags: {
6
6
  sso: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
+ browser: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
8
  };
8
9
  static contextDefinition: {
9
10
  sessionManager: import("../../commandUtils/context/SessionManagementContextField").default;
@@ -17,13 +17,18 @@ class AccountLogin extends EasCommand_1.default {
17
17
  char: 's',
18
18
  default: false,
19
19
  }),
20
+ browser: core_1.Flags.boolean({
21
+ description: 'Login with your browser',
22
+ char: 'b',
23
+ default: false,
24
+ }),
20
25
  };
21
26
  static contextDefinition = {
22
27
  ...this.ContextOptions.MaybeLoggedIn,
23
28
  ...this.ContextOptions.SessionManagment,
24
29
  };
25
30
  async runAsync() {
26
- const { flags: { sso }, } = await this.parse(AccountLogin);
31
+ const { flags: { sso, browser }, } = await this.parse(AccountLogin);
27
32
  const { sessionManager, maybeLoggedIn: { actor }, } = await this.getContextAsync(AccountLogin, { nonInteractive: false });
28
33
  if (sessionManager.getAccessToken()) {
29
34
  throw new Error('EXPO_TOKEN is set in your environment, and is being used for all EAS authentication. To use username/password authentication, unset EXPO_TOKEN in your environment and re-run the command.');
@@ -37,7 +42,7 @@ class AccountLogin extends EasCommand_1.default {
37
42
  core_1.Errors.error('Aborted', { exit: 1 });
38
43
  }
39
44
  }
40
- await sessionManager.showLoginPromptAsync({ sso });
45
+ await sessionManager.showLoginPromptAsync({ sso, browser });
41
46
  log_1.default.log('Logged in');
42
47
  }
43
48
  }
@@ -54,7 +54,10 @@ class EnvCreate extends EasCommand_1.default {
54
54
  const { projectId, loggedIn: { graphqlClient }, } = await this.getContextAsync(EnvCreate, {
55
55
  nonInteractive: validatedFlags['non-interactive'],
56
56
  });
57
- const { name, value, scope, 'non-interactive': nonInteractive, environment: environments, visibility, force, type, fileName, } = await this.promptForMissingFlagsAsync(validatedFlags, args, { graphqlClient, projectId });
57
+ const { name, value, scope, 'non-interactive': nonInteractive, environment: environments, visibility, force, type, fileName, } = await this.promptForMissingFlagsAsync(validatedFlags, args, {
58
+ graphqlClient,
59
+ projectId,
60
+ });
58
61
  const [projectDisplayName, ownerAccount] = await Promise.all([
59
62
  (0, projectUtils_1.getDisplayNameForProjectIdAsync)(graphqlClient, projectId),
60
63
  (0, projectUtils_1.getOwnerAccountForProjectIdAsync)(graphqlClient, projectId),
@@ -186,7 +189,7 @@ class EnvCreate extends EasCommand_1.default {
186
189
  fileName = path_1.default.basename(environmentFilePath);
187
190
  }
188
191
  value = environmentFilePath ? await fs_extra_1.default.readFile(environmentFilePath, 'base64') : value;
189
- let newEnvironments = environments ? environments : environment ? [environment] : undefined;
192
+ let newEnvironments = environments ?? (environment ? [environment] : undefined);
190
193
  if (!newEnvironments) {
191
194
  newEnvironments = await (0, prompts_2.promptVariableEnvironmentAsync)({
192
195
  nonInteractive,
@@ -110,11 +110,7 @@ class EnvList extends EasCommand_1.default {
110
110
  });
111
111
  }
112
112
  sanitizeInputs(flags, { environment }) {
113
- const environments = flags.environment
114
- ? flags.environment
115
- : environment
116
- ? [environment]
117
- : undefined;
113
+ const environments = flags.environment ?? (environment ? [environment] : undefined);
118
114
  return {
119
115
  ...flags,
120
116
  'non-interactive': flags['non-interactive'] ?? false,
@@ -313,11 +313,11 @@ class UpdatePublish extends EasCommand_1.default {
313
313
  return {
314
314
  ...info,
315
315
  expoUpdatesRuntimeFingerprintSource: info.expoUpdatesRuntimeFingerprint
316
- ? (await (0, maybeUploadFingerprintAsync_1.maybeUploadFingerprintAsync)({
316
+ ? ((await (0, maybeUploadFingerprintAsync_1.maybeUploadFingerprintAsync)({
317
317
  hash: info.runtimeVersion,
318
318
  fingerprint: info.expoUpdatesRuntimeFingerprint,
319
319
  graphqlClient,
320
- })).fingerprintSource ?? null
320
+ })).fingerprintSource ?? null)
321
321
  : null,
322
322
  };
323
323
  }));
@@ -8,7 +8,7 @@ const fast_glob_1 = tslib_1.__importDefault(require("fast-glob"));
8
8
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
9
9
  const node_stream_zip_1 = tslib_1.__importDefault(require("node-stream-zip"));
10
10
  const path_1 = tslib_1.__importDefault(require("path"));
11
- const tar_1 = tslib_1.__importDefault(require("tar"));
11
+ const tar = tslib_1.__importStar(require("tar"));
12
12
  const uuid_1 = require("uuid");
13
13
  const url_1 = require("../build/utils/url");
14
14
  const EasCommand_1 = tslib_1.__importDefault(require("../commandUtils/EasCommand"));
@@ -214,7 +214,7 @@ async function uploadAppArchiveAsync(graphqlClient, originalPath) {
214
214
  const tarPath = path_1.default.join((0, paths_1.getTmpDirectory)(), `${(0, uuid_1.v4)()}.tar.gz`);
215
215
  const parentPath = path_1.default.dirname(originalPath);
216
216
  const folderName = path_1.default.basename(originalPath);
217
- await tar_1.default.create({ cwd: parentPath, file: tarPath, gzip: true }, [folderName]);
217
+ await tar.create({ cwd: parentPath, file: tarPath, gzip: true }, [folderName]);
218
218
  filePath = tarPath;
219
219
  }
220
220
  const fileSize = (await fs_extra_1.default.stat(filePath)).size;
@@ -306,7 +306,7 @@ async function extractAppMetadataAsync(buildPath, platform) {
306
306
  try {
307
307
  let fingerprintHashPromise;
308
308
  let infoPlistPromise;
309
- await tar_1.default.list({
309
+ await tar.list({
310
310
  file: buildPath,
311
311
  // eslint-disable-next-line async-protect/async-suffix
312
312
  onentry: entry => {
@@ -43,9 +43,7 @@ exports.AppleDistributionCertificateMutation = {
43
43
  async deleteAppleDistributionCertificateAsync(graphqlClient, appleDistributionCertificateId) {
44
44
  await (0, client_1.withErrorHandlingAsync)(graphqlClient
45
45
  .mutation((0, graphql_tag_1.default) `
46
- mutation DeleteAppleDistributionCertificateMutation(
47
- $appleDistributionCertificateId: ID!
48
- ) {
46
+ mutation DeleteAppleDistributionCertificateMutation($appleDistributionCertificateId: ID!) {
49
47
  appleDistributionCertificate {
50
48
  deleteAppleDistributionCertificate(id: $appleDistributionCertificateId) {
51
49
  id
@@ -18,7 +18,18 @@ exports.TEAMS_LIMIT = 50;
18
18
  exports.DEVICES_LIMIT = 50;
19
19
  async function selectAppleTeamOnAccountAsync(graphqlClient, { accountName, selectionPromptTitle, paginatedQueryOptions, }) {
20
20
  if (paginatedQueryOptions.nonInteractive) {
21
- throw new Error('Unable to select an Apple team in non-interactive mode.');
21
+ const teams = await AppleTeamQuery_1.AppleTeamQuery.getAllForAccountAsync(graphqlClient, {
22
+ accountName,
23
+ limit: paginatedQueryOptions.limit ?? exports.TEAMS_LIMIT,
24
+ offset: paginatedQueryOptions.offset,
25
+ });
26
+ if (teams.length === 0) {
27
+ throw new Error(`No Apple teams found for account ${accountName}.`);
28
+ }
29
+ const teamList = teams
30
+ .map(t => t.appleTeamName ? `${t.appleTeamName} (${t.appleTeamIdentifier})` : t.appleTeamIdentifier)
31
+ .join('\n ');
32
+ throw new Error(`Unable to select an Apple team in non-interactive mode. Use the --apple-team-id flag to specify the team. Available Apple teams for account ${accountName}:\n ${teamList}`);
22
33
  }
23
34
  else {
24
35
  const selectedAppleTeam = await (0, queries_1.paginatedQueryWithSelectPromptAsync)({
@@ -47,7 +58,17 @@ async function selectAppleTeamOnAccountAsync(graphqlClient, { accountName, selec
47
58
  }
48
59
  async function selectAppleDeviceOnAppleTeamAsync(graphqlClient, { accountName, appleTeamIdentifier, selectionPromptTitle, paginatedQueryOptions, }) {
49
60
  if (paginatedQueryOptions.nonInteractive) {
50
- throw new Error('Unable to select an Apple device in non-interactive mode.');
61
+ const devices = await AppleDeviceQuery_1.AppleDeviceQuery.getAllForAppleTeamAsync(graphqlClient, {
62
+ accountName,
63
+ appleTeamIdentifier,
64
+ limit: paginatedQueryOptions.limit ?? exports.DEVICES_LIMIT,
65
+ offset: paginatedQueryOptions.offset,
66
+ });
67
+ if (devices.length === 0) {
68
+ throw new Error(`No devices found on Apple team ${appleTeamIdentifier} for account ${accountName}.`);
69
+ }
70
+ const deviceList = devices.map(d => (0, DeviceUtils_1.formatDeviceLabel)(d)).join('\n ');
71
+ throw new Error(`Unable to select an Apple device in non-interactive mode. Available devices on Apple team ${appleTeamIdentifier}:\n ${deviceList}`);
51
72
  }
52
73
  else {
53
74
  const selectedAppleDevice = await (0, queries_1.paginatedQueryWithSelectPromptAsync)({
@@ -86,10 +86,7 @@ exports.EnvironmentVariableMutation = {
86
86
  $appId: ID!
87
87
  ) {
88
88
  environmentVariable {
89
- createBulkEnvironmentVariablesForApp(
90
- environmentVariablesData: $input
91
- appId: $appId
92
- ) {
89
+ createBulkEnvironmentVariablesForApp(environmentVariablesData: $input, appId: $appId) {
93
90
  id
94
91
  }
95
92
  }
@@ -48,10 +48,7 @@ exports.PublishMutation = {
48
48
  async setCodeSigningInfoAsync(graphqlClient, updateId, codeSigningInfo) {
49
49
  const data = await (0, client_1.withErrorHandlingAsync)(graphqlClient
50
50
  .mutation((0, graphql_tag_1.default) `
51
- mutation SetCodeSigningInfoMutation(
52
- $updateId: ID!
53
- $codeSigningInfo: CodeSigningInfoInput!
54
- ) {
51
+ mutation SetCodeSigningInfoMutation($updateId: ID!, $codeSigningInfo: CodeSigningInfoInput!) {
55
52
  update {
56
53
  setCodeSigningInfo(updateId: $updateId, codeSigningInfo: $codeSigningInfo) {
57
54
  id
@@ -9,9 +9,7 @@ exports.UserPreferencesMutation = {
9
9
  async markCliDoneInOnboardingUserPreferencesAsync(graphqlClient, userPreferencesData) {
10
10
  const data = await (0, client_1.withErrorHandlingAsync)(graphqlClient
11
11
  .mutation((0, graphql_tag_1.default) `
12
- mutation MarkCliDoneInOnboardingUserPreferencesMutation(
13
- $preferences: UserPreferencesInput!
14
- ) {
12
+ mutation MarkCliDoneInOnboardingUserPreferencesMutation($preferences: UserPreferencesInput!) {
15
13
  me {
16
14
  setPreferences(preferences: $preferences) {
17
15
  onboarding {
@@ -17,17 +17,9 @@ var WorkflowRevisionMutation;
17
17
  async function getOrCreateWorkflowRevisionFromGitRefAsync(graphqlClient, { appId, fileName, gitRef, }) {
18
18
  const data = await (0, client_1.withErrorHandlingAsync)(graphqlClient
19
19
  .mutation((0, graphql_tag_1.default) `
20
- mutation GetOrCreateWorkflowRevisionFromGitRef(
21
- $appId: ID!
22
- $fileName: String!
23
- $gitRef: String!
24
- ) {
20
+ mutation GetOrCreateWorkflowRevisionFromGitRef($appId: ID!, $fileName: String!, $gitRef: String!) {
25
21
  workflowRevision {
26
- getOrCreateWorkflowRevisionFromGitRef(
27
- appId: $appId
28
- fileName: $fileName
29
- gitRef: $gitRef
30
- ) {
22
+ getOrCreateWorkflowRevisionFromGitRef(appId: $appId, fileName: $fileName, gitRef: $gitRef) {
31
23
  id
32
24
  yamlConfig
33
25
  blobSha
@@ -8,11 +8,7 @@ exports.AppVersionQuery = {
8
8
  async latestVersionAsync(graphqlClient, appId, platform, applicationIdentifier) {
9
9
  const data = await (0, client_1.withErrorHandlingAsync)(graphqlClient
10
10
  .query((0, graphql_tag_1.default) `
11
- query LatestAppVersion(
12
- $appId: String!
13
- $platform: AppPlatform!
14
- $applicationIdentifier: String!
15
- ) {
11
+ query LatestAppVersion($appId: String!, $platform: AppPlatform!, $applicationIdentifier: String!) {
16
12
  app {
17
13
  byId(appId: $appId) {
18
14
  id
package/build/log.js CHANGED
@@ -8,7 +8,7 @@ const figures_1 = tslib_1.__importDefault(require("figures"));
8
8
  const getenv_1 = require("getenv");
9
9
  const log_symbols_1 = tslib_1.__importDefault(require("log-symbols"));
10
10
  const terminal_link_1 = tslib_1.__importDefault(require("terminal-link"));
11
- // eslint-disable-next-line import/no-extraneous-dependencies
11
+ // eslint-disable-next-line eslint-import/no-extraneous-dependencies
12
12
  const nodeDebug = require('debug')('eas:log:debug');
13
13
  class Log {
14
14
  static isDebug = (0, getenv_1.boolish)('EXPO_DEBUG', false);
package/build/ora.js CHANGED
@@ -22,7 +22,7 @@ const isCi = (0, getenv_1.boolish)('CI', false);
22
22
  * @returns
23
23
  */
24
24
  function ora(options) {
25
- const inputOptions = typeof options === 'string' ? { text: options } : options ?? {};
25
+ const inputOptions = typeof options === 'string' ? { text: options } : (options ?? {});
26
26
  const disabled = log_1.default.isDebug || !process.stdin.isTTY || isCi;
27
27
  const spinner = (0, ora_1.default)({
28
28
  // Ensure our non-interactive mode emulates CI mode.
@@ -0,0 +1,4 @@
1
+ import type { ProfileData } from '../utils/profiles';
2
+ import type { Client } from '../vcs/vcs';
3
+ export declare function discourageExpoGoForProdAsync(buildProfiles: ProfileData<'build'>[] | undefined, projectDir: string, vcsClient: Client): Promise<void>;
4
+ export declare function detectExpoGoProdBuildAsync(buildProfiles: ProfileData<'build'>[] | undefined, projectDir: string, vcsClient: Client): Promise<boolean>;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.discourageExpoGoForProdAsync = discourageExpoGoForProdAsync;
4
+ exports.detectExpoGoProdBuildAsync = detectExpoGoProdBuildAsync;
5
+ const tslib_1 = require("tslib");
6
+ const eas_build_job_1 = require("@expo/eas-build-job");
7
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
8
+ const getenv_1 = tslib_1.__importDefault(require("getenv"));
9
+ const workflow_1 = require("./workflow");
10
+ const devClient_1 = require("../build/utils/devClient");
11
+ const log_1 = tslib_1.__importStar(require("../log"));
12
+ const suppressionEnvVarName = 'EAS_BUILD_NO_EXPO_GO_WARNING';
13
+ async function discourageExpoGoForProdAsync(buildProfiles, projectDir, vcsClient) {
14
+ try {
15
+ const isExpoGoProdBuild = await detectExpoGoProdBuildAsync(buildProfiles, projectDir, vcsClient);
16
+ if (!isExpoGoProdBuild) {
17
+ return;
18
+ }
19
+ log_1.default.newLine();
20
+ log_1.default.warn(`⚠️ Detected that your app uses Expo Go for development, this is not recommended when building production apps.`);
21
+ log_1.default.warn((0, log_1.learnMore)('https://expo.fyi/why-not-build-expo-go-for-production', {
22
+ dim: false,
23
+ }));
24
+ log_1.default.warn(chalk_1.default.dim(`To suppress this warning, set ${chalk_1.default.bold(`${suppressionEnvVarName}=true`)}.`));
25
+ log_1.default.newLine();
26
+ }
27
+ catch (err) {
28
+ log_1.default.isDebug && log_1.default.warn('Error detecting whether Expo Go is used:', err);
29
+ }
30
+ }
31
+ async function detectExpoGoProdBuildAsync(buildProfiles, projectDir, vcsClient) {
32
+ const shouldSuppressWarning = getenv_1.default.boolish(suppressionEnvVarName, false);
33
+ const isProductionBuild = buildProfiles?.map(it => it.profileName).includes('production');
34
+ if (shouldSuppressWarning || !isProductionBuild) {
35
+ return false;
36
+ }
37
+ const hasExpoDevClient = (0, devClient_1.isExpoDevClientInstalled)(projectDir);
38
+ if (hasExpoDevClient) {
39
+ return false;
40
+ }
41
+ return await checkIfManagedWorkflowAsync(projectDir, vcsClient);
42
+ }
43
+ async function checkIfManagedWorkflowAsync(projectDir, vcsClient) {
44
+ const workflows = await (0, workflow_1.resolveWorkflowPerPlatformAsync)(projectDir, vcsClient);
45
+ return workflows.android === eas_build_job_1.Workflow.MANAGED && workflows.ios === eas_build_job_1.Workflow.MANAGED;
46
+ }
@@ -30,7 +30,11 @@ class ManageRollout {
30
30
  async runAsync(ctx) {
31
31
  const { nonInteractive } = ctx;
32
32
  if (nonInteractive) {
33
- throw new Error(`rollout selection cannot be run in non-interactive mode.`);
33
+ throw new Error(`Rollout selection cannot be run in non-interactive mode. Available actions: ${[
34
+ ManageRolloutActions.EDIT,
35
+ ManageRolloutActions.END,
36
+ ManageRolloutActions.VIEW,
37
+ ].join(', ')}.`);
34
38
  }
35
39
  const channelObject = await this.getChannelObjectAsync(ctx);
36
40
  (0, utils_1.printRollout)(channelObject);
@@ -43,7 +43,16 @@ class SelectRuntime {
43
43
  const { nonInteractive, graphqlClient, app } = ctx;
44
44
  const { projectId } = app;
45
45
  if (nonInteractive) {
46
- throw new utils_1.NonInteractiveError(`runtime selection cannot be run in non-interactive mode.`);
46
+ const runtimes = await this.getNewestRuntimeAsync(graphqlClient, {
47
+ appId: projectId,
48
+ branchName: this.branchInfo.name,
49
+ anotherBranchIdToIntersectRuntimesBy: this.options.anotherBranchToIntersectRuntimesBy?.id,
50
+ });
51
+ if (runtimes.edges.length === 0) {
52
+ throw new utils_1.NonInteractiveError(`No ${this.printedType} versions found on branch "${this.branchInfo.name}".`);
53
+ }
54
+ const runtimeList = runtimes.edges.map(e => e.node.version).join('\n ');
55
+ throw new utils_1.NonInteractiveError(`Runtime selection cannot be run in non-interactive mode. Available ${this.printedType} versions on branch "${this.branchInfo.name}":\n ${runtimeList}`);
47
56
  }
48
57
  const newestRuntimeConnection = await this.getNewestRuntimeAsync(graphqlClient, {
49
58
  appId: projectId,
@@ -78,7 +78,16 @@ async function listAndRenderUpdateGroupsOnBranchAsync(graphqlClient, { projectId
78
78
  }
79
79
  async function selectRuntimeAndGetLatestUpdateGroupForEachPublishPlatformOnBranchAsync(graphqlClient, { projectId, branchName, paginatedQueryOptions, }) {
80
80
  if (paginatedQueryOptions.nonInteractive) {
81
- throw new Error('Unable to select an update in non-interactive mode.');
81
+ const runtimes = await RuntimeQuery_1.RuntimeQuery.getRuntimesOnBranchAsync(graphqlClient, {
82
+ appId: projectId,
83
+ name: branchName,
84
+ first: exports.RUNTIME_VERSIONS_LIMIT,
85
+ });
86
+ if (runtimes.edges.length === 0) {
87
+ throw new Error(`No runtime versions found on branch "${branchName}".`);
88
+ }
89
+ const runtimeList = runtimes.edges.map(e => e.node.version).join('\n ');
90
+ throw new Error(`Unable to select a runtime in non-interactive mode. Available runtime versions on branch "${branchName}":\n ${runtimeList}`);
82
91
  }
83
92
  const runtimeVersion = await selectRuntimeOnBranchAsync(graphqlClient, {
84
93
  appId: projectId,
@@ -112,7 +121,24 @@ async function selectRuntimeAndGetLatestUpdateGroupForEachPublishPlatformOnBranc
112
121
  }
113
122
  async function selectUpdateGroupOnBranchAsync(graphqlClient, { projectId, branchName, paginatedQueryOptions, }) {
114
123
  if (paginatedQueryOptions.nonInteractive) {
115
- throw new Error('Unable to select an update in non-interactive mode.');
124
+ const updateGroups = await queryUpdateGroupsOnBranchAsync(graphqlClient, {
125
+ appId: projectId,
126
+ branchName,
127
+ limit: paginatedQueryOptions.limit ?? exports.UPDATE_GROUPS_LIMIT,
128
+ offset: paginatedQueryOptions.offset,
129
+ });
130
+ if (updateGroups.length === 0) {
131
+ throw new Error(`No update groups found on branch "${branchName}".`);
132
+ }
133
+ const updateList = updateGroups
134
+ .map(group => {
135
+ const first = group[0];
136
+ return first
137
+ ? `${first.group} (runtime: ${first.runtimeVersion}, message: ${first.message ?? 'N/A'})`
138
+ : 'unknown';
139
+ })
140
+ .join('\n ');
141
+ throw new Error(`Unable to select an update in non-interactive mode. Use the --group flag to specify the update group. Available update groups on branch "${branchName}":\n ${updateList}`);
116
142
  }
117
143
  const updateGroup = await (0, queries_1.paginatedQueryWithSelectPromptAsync)({
118
144
  limit: paginatedQueryOptions.limit ?? exports.UPDATE_GROUPS_LIMIT,
@@ -39,12 +39,13 @@ export default class SessionManager {
39
39
  *
40
40
  * @deprecated Should not be used outside of context functions, except in the AccountLogin command.
41
41
  */
42
- showLoginPromptAsync({ nonInteractive, printNewLine, sso, }?: {
42
+ showLoginPromptAsync({ nonInteractive, printNewLine, sso, browser, }?: {
43
43
  nonInteractive?: boolean | undefined;
44
44
  printNewLine?: boolean | undefined;
45
45
  sso?: boolean | undefined;
46
+ browser?: boolean | undefined;
46
47
  }): Promise<void>;
47
- private ssoLoginAsync;
48
+ private browserLoginAsync;
48
49
  private loginAsync;
49
50
  /**
50
51
  * Prompt for an OTP with the option to cancel the question by answering empty (pressing return key).