eas-cli 0.56.0 → 0.59.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 (93) hide show
  1. package/README.md +98 -49
  2. package/build/branch/queries.d.ts +5 -0
  3. package/build/branch/queries.js +87 -0
  4. package/build/build/android/build.js +17 -3
  5. package/build/build/android/graphql.js +2 -0
  6. package/build/build/android/prepareJob.js +5 -0
  7. package/build/build/android/syncProjectConfiguration.d.ts +3 -4
  8. package/build/build/android/syncProjectConfiguration.js +2 -3
  9. package/build/build/android/version.d.ts +11 -0
  10. package/build/build/android/version.js +71 -1
  11. package/build/build/build.js +1 -1
  12. package/build/build/context.d.ts +5 -0
  13. package/build/build/createContext.d.ts +3 -1
  14. package/build/build/createContext.js +8 -1
  15. package/build/build/ios/build.js +17 -1
  16. package/build/build/ios/graphql.js +2 -0
  17. package/build/build/ios/prepareJob.js +5 -0
  18. package/build/build/ios/syncProjectConfiguration.d.ts +3 -4
  19. package/build/build/ios/syncProjectConfiguration.js +2 -3
  20. package/build/build/ios/version.d.ts +13 -0
  21. package/build/build/ios/version.js +72 -1
  22. package/build/build/local.d.ts +2 -2
  23. package/build/build/local.js +7 -7
  24. package/build/build/metadata.d.ts +1 -0
  25. package/build/build/metadata.js +32 -6
  26. package/build/build/runBuildAndSubmit.d.ts +1 -0
  27. package/build/build/runBuildAndSubmit.js +24 -3
  28. package/build/build/utils/printBuildInfo.d.ts +1 -1
  29. package/build/build/utils/printBuildInfo.js +7 -13
  30. package/build/commandUtils/pagination.d.ts +13 -0
  31. package/build/commandUtils/pagination.js +42 -0
  32. package/build/commands/branch/list.d.ts +3 -4
  33. package/build/commands/branch/list.js +6 -69
  34. package/build/commands/branch/view.d.ts +3 -0
  35. package/build/commands/branch/view.js +15 -63
  36. package/build/commands/build/index.d.ts +1 -0
  37. package/build/commands/build/index.js +9 -0
  38. package/build/commands/build/version/set.d.ts +0 -1
  39. package/build/commands/build/version/set.js +2 -3
  40. package/build/commands/build/version/sync.d.ts +0 -1
  41. package/build/commands/build/version/sync.js +2 -3
  42. package/build/commands/submit.js +4 -1
  43. package/build/commands/update/index.d.ts +2 -2
  44. package/build/commands/update/index.js +22 -6
  45. package/build/commands/update/list.js +13 -18
  46. package/build/credentials/ios/actions/SetUpAdhocProvisioningProfile.js +2 -2
  47. package/build/devices/actions/create/action.d.ts +5 -2
  48. package/build/devices/actions/create/action.js +12 -2
  49. package/build/devices/actions/create/developerPortalMethod.d.ts +6 -0
  50. package/build/devices/actions/create/developerPortalMethod.js +90 -0
  51. package/build/devices/manager.js +4 -4
  52. package/build/graphql/generated.d.ts +424 -61
  53. package/build/graphql/generated.js +9 -1
  54. package/build/graphql/mutations/PublishMutation.d.ts +2 -4
  55. package/build/graphql/queries/BranchQuery.d.ts +2 -1
  56. package/build/graphql/queries/BranchQuery.js +26 -0
  57. package/build/graphql/queries/BuildQuery.d.ts +4 -1
  58. package/build/graphql/queries/BuildQuery.js +19 -0
  59. package/build/graphql/queries/PublishQuery.d.ts +2 -1
  60. package/build/graphql/queries/PublishQuery.js +16 -0
  61. package/build/graphql/queries/UpdateQuery.d.ts +6 -8
  62. package/build/graphql/queries/UpdateQuery.js +27 -23
  63. package/build/graphql/types/Build.d.ts +1 -0
  64. package/build/graphql/types/Build.js +16 -1
  65. package/build/metadata/apple/config/reader.d.ts +3 -2
  66. package/build/metadata/apple/config/reader.js +22 -5
  67. package/build/metadata/apple/config/writer.d.ts +3 -2
  68. package/build/metadata/apple/config/writer.js +22 -6
  69. package/build/metadata/apple/tasks/app-version.d.ts +5 -1
  70. package/build/metadata/apple/tasks/app-version.js +109 -8
  71. package/build/metadata/apple/tasks/index.d.ts +6 -1
  72. package/build/metadata/apple/tasks/index.js +7 -2
  73. package/build/metadata/apple/types.d.ts +3 -7
  74. package/build/metadata/upload.js +6 -1
  75. package/build/project/publish.d.ts +3 -1
  76. package/build/project/publish.js +17 -8
  77. package/build/project/remoteVersionSource.d.ts +5 -3
  78. package/build/project/remoteVersionSource.js +18 -9
  79. package/build/submit/submit.d.ts +2 -1
  80. package/build/submit/submit.js +3 -2
  81. package/build/update/queries.d.ts +3 -0
  82. package/build/update/queries.js +72 -0
  83. package/build/update/utils.d.ts +14 -0
  84. package/build/update/utils.js +26 -9
  85. package/build/utils/expodash/chunk.d.ts +1 -0
  86. package/build/utils/expodash/chunk.js +16 -0
  87. package/build/utils/queries.d.ts +25 -0
  88. package/build/utils/queries.js +67 -0
  89. package/build/vcs/clients/gitNoCommit.d.ts +1 -0
  90. package/build/vcs/clients/gitNoCommit.js +14 -0
  91. package/oclif.manifest.json +1 -1
  92. package/package.json +27 -25
  93. package/schema/metadata-0.json +38 -233
@@ -1,6 +1,11 @@
1
1
  import { MetadataContext } from '../../context';
2
2
  import { AppleTask } from '../task';
3
+ import { AppVersionOptions } from './app-version';
4
+ declare type AppleTaskOptions = {
5
+ version?: AppVersionOptions['version'];
6
+ };
3
7
  /**
4
8
  * List of all eligible tasks to sync local store configuration to the App store.
5
9
  */
6
- export declare function createAppleTasks(_ctx: MetadataContext): AppleTask[];
10
+ export declare function createAppleTasks(_ctx: MetadataContext, { version }?: AppleTaskOptions): AppleTask[];
11
+ export {};
@@ -8,7 +8,12 @@ const app_version_1 = require("./app-version");
8
8
  /**
9
9
  * List of all eligible tasks to sync local store configuration to the App store.
10
10
  */
11
- function createAppleTasks(_ctx) {
12
- return [new app_version_1.AppVersionTask(), new app_info_1.AppInfoTask(), new age_rating_1.AgeRatingTask(), new app_review_detail_1.AppReviewDetailTask()];
11
+ function createAppleTasks(_ctx, { version } = {}) {
12
+ return [
13
+ new app_version_1.AppVersionTask({ version }),
14
+ new app_info_1.AppInfoTask(),
15
+ new age_rating_1.AgeRatingTask(),
16
+ new app_review_detail_1.AppReviewDetailTask(),
17
+ ];
13
18
  }
14
19
  exports.createAppleTasks = createAppleTasks;
@@ -2,6 +2,7 @@
2
2
  import type { AgeRatingDeclarationProps } from '@expo/apple-utils';
3
3
  export declare type AppleLocale = string;
4
4
  export interface AppleMetadata {
5
+ version?: string;
5
6
  copyright?: string;
6
7
  info?: Record<AppleLocale, AppleInfo>;
7
8
  categories?: AppleCategory;
@@ -14,13 +15,8 @@ export declare type AppleAdvisory = Partial<AgeRatingDeclarationProps>;
14
15
  /** Apps can define up to two categories, or categories with up to two subcategories */
15
16
  export declare type AppleCategory = (string | string[])[];
16
17
  export interface AppleRelease {
17
- isPhasedReleaseEnabled?: boolean;
18
- shouldResetRatings?: boolean;
19
- autoReleaseDate?: number | string;
20
- automaticRelease?: boolean;
21
- usesThirdPartyContent?: boolean;
22
- /** Alternative to setting `ITSAppUsesNonExemptEncryption` in the binary's `Info.plist`. */
23
- usesNonExemptEncryption?: boolean;
18
+ automaticRelease?: boolean | string;
19
+ phasedRelease?: boolean;
24
20
  }
25
21
  export interface AppleInfo {
26
22
  title: string;
@@ -17,6 +17,7 @@ const telemetry_1 = require("./utils/telemetry");
17
17
  * Note, only App Store is supported at this time.
18
18
  */
19
19
  async function uploadMetadataAsync(metadataCtx) {
20
+ var _a;
20
21
  const filePath = path_1.default.resolve(metadataCtx.projectDir, metadataCtx.metadataPath);
21
22
  if (!(await fs_extra_1.default.pathExists(filePath))) {
22
23
  throw new errors_1.MetadataValidationError(`Store configuration file not found "${filePath}"`);
@@ -41,7 +42,11 @@ async function uploadMetadataAsync(metadataCtx) {
41
42
  log_1.default.log('Uploading App Store configuration...');
42
43
  const errors = [];
43
44
  const config = (0, config_1.createAppleReader)(fileData);
44
- const tasks = (0, tasks_1.createAppleTasks)(metadataCtx);
45
+ const tasks = (0, tasks_1.createAppleTasks)(metadataCtx, {
46
+ // We need to resolve a different version as soon as possible.
47
+ // This version is the parent model of all changes we are going to push.
48
+ version: (_a = config.getVersion()) === null || _a === void 0 ? void 0 : _a.versionString,
49
+ });
45
50
  const taskCtx = { app };
46
51
  for (const task of tasks) {
47
52
  try {
@@ -76,6 +76,8 @@ declare type AssetUploadResult = {
76
76
  assetCount: number;
77
77
  uniqueAssetCount: number;
78
78
  uniqueUploadedAssetCount: number;
79
+ assetLimitPerUpdateGroup: number;
79
80
  };
80
- export declare function uploadAssetsAsync(assetsForUpdateInfoGroup: CollectedAssets, updateSpinnerText?: (totalAssets: number, missingAssets: number) => void): Promise<AssetUploadResult>;
81
+ export declare function uploadAssetsAsync(assetsForUpdateInfoGroup: CollectedAssets, projectId: string, updateSpinnerText?: (totalAssets: number, missingAssets: number) => void): Promise<AssetUploadResult>;
82
+ export declare function isUploadedAssetCountAboveWarningThreshold(uploadedAssetCount: number, assetLimitPerUpdateGroup: number): boolean;
81
83
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.uploadAssetsAsync = exports.filterOutAssetsThatAlreadyExistAsync = exports.collectAssetsAsync = exports.loadMetadata = exports.resolveInputDirectoryAsync = exports.buildBundlesAsync = exports.buildUnsortedUpdateInfoGroupAsync = exports.convertAssetToUpdateInfoGroupFormatAsync = exports.getStorageKeyForAssetAsync = exports.getStorageKey = exports.getBase64URLEncoding = exports.guessContentTypeFromExtension = exports.MetadataJoi = void 0;
3
+ exports.isUploadedAssetCountAboveWarningThreshold = exports.uploadAssetsAsync = exports.filterOutAssetsThatAlreadyExistAsync = exports.collectAssetsAsync = exports.loadMetadata = exports.resolveInputDirectoryAsync = exports.buildBundlesAsync = exports.buildUnsortedUpdateInfoGroupAsync = exports.convertAssetToUpdateInfoGroupFormatAsync = exports.getStorageKeyForAssetAsync = exports.getStorageKey = exports.getBase64URLEncoding = exports.guessContentTypeFromExtension = exports.MetadataJoi = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const json_file_1 = tslib_1.__importDefault(require("@expo/json-file"));
6
6
  const crypto_1 = tslib_1.__importDefault(require("crypto"));
@@ -184,7 +184,7 @@ async function filterOutAssetsThatAlreadyExistAsync(uniqueAssetsWithStorageKey)
184
184
  return missingAssets;
185
185
  }
186
186
  exports.filterOutAssetsThatAlreadyExistAsync = filterOutAssetsThatAlreadyExistAsync;
187
- async function uploadAssetsAsync(assetsForUpdateInfoGroup, updateSpinnerText) {
187
+ async function uploadAssetsAsync(assetsForUpdateInfoGroup, projectId, updateSpinnerText) {
188
188
  let assets = [];
189
189
  let platform;
190
190
  for (platform in assetsForUpdateInfoGroup) {
@@ -208,12 +208,15 @@ async function uploadAssetsAsync(assetsForUpdateInfoGroup, updateSpinnerText) {
208
208
  const { specifications } = await PublishMutation_1.PublishMutation.getUploadURLsAsync(missingAssets.map(ma => ma.contentType));
209
209
  updateSpinnerText === null || updateSpinnerText === void 0 ? void 0 : updateSpinnerText(totalAssets, missingAssets.length);
210
210
  const assetUploadPromiseLimit = (0, promise_limit_1.default)(15);
211
- await Promise.all(missingAssets.map((missingAsset, i) => {
212
- assetUploadPromiseLimit(async () => {
213
- const presignedPost = JSON.parse(specifications[i]);
214
- await (0, uploads_1.uploadWithPresignedPostAsync)(missingAsset.path, presignedPost);
215
- });
216
- }));
211
+ const [assetLimitPerUpdateGroup] = await Promise.all([
212
+ PublishQuery_1.PublishQuery.getAssetLimitPerUpdateGroupAsync(projectId),
213
+ missingAssets.map((missingAsset, i) => {
214
+ assetUploadPromiseLimit(async () => {
215
+ const presignedPost = JSON.parse(specifications[i]);
216
+ await (0, uploads_1.uploadWithPresignedPostAsync)(missingAsset.path, presignedPost);
217
+ });
218
+ }),
219
+ ]);
217
220
  let timeout = 1;
218
221
  while (missingAssets.length > 0) {
219
222
  const timeoutPromise = new Promise(resolve => setTimeout(resolve, Math.min(timeout * 1000, 5000))); // linear backoff
@@ -226,6 +229,12 @@ async function uploadAssetsAsync(assetsForUpdateInfoGroup, updateSpinnerText) {
226
229
  assetCount: assets.length,
227
230
  uniqueAssetCount: uniqueAssets.length,
228
231
  uniqueUploadedAssetCount,
232
+ assetLimitPerUpdateGroup,
229
233
  };
230
234
  }
231
235
  exports.uploadAssetsAsync = uploadAssetsAsync;
236
+ function isUploadedAssetCountAboveWarningThreshold(uploadedAssetCount, assetLimitPerUpdateGroup) {
237
+ const warningThreshold = Math.floor(assetLimitPerUpdateGroup * 0.75);
238
+ return uploadedAssetCount > warningThreshold;
239
+ }
240
+ exports.isUploadedAssetCountAboveWarningThreshold = isUploadedAssetCountAboveWarningThreshold;
@@ -1,6 +1,8 @@
1
1
  import { ExpoConfig } from '@expo/config';
2
2
  import { Platform } from '@expo/eas-build-job';
3
- import { EasJsonReader } from '@expo/eas-json';
4
- export declare function ensureRemoteVersionPolicyAsync(projectDir: string, easJsonReader: EasJsonReader): Promise<void>;
5
- export declare function validateAppConfigForRemoteVersionPolicyAsync(exp: ExpoConfig): Promise<void>;
3
+ import { EasJson, EasJsonReader } from '@expo/eas-json';
4
+ import { ProfileData } from '../utils/profiles';
5
+ export declare function ensureVersionSourceIsRemoteAsync(projectDir: string, easJsonReader: EasJsonReader): Promise<void>;
6
+ export declare function validateBuildProfileVersionSettings(profileInfo: ProfileData<'build'>, cliConfig: EasJson['cli']): void;
7
+ export declare function validateAppConfigForRemoteVersionSource(exp: ExpoConfig, platform: Platform): void;
6
8
  export declare function getBuildVersionName(platform: Platform): string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getBuildVersionName = exports.validateAppConfigForRemoteVersionPolicyAsync = exports.ensureRemoteVersionPolicyAsync = void 0;
3
+ exports.getBuildVersionName = exports.validateAppConfigForRemoteVersionSource = exports.validateBuildProfileVersionSettings = exports.ensureVersionSourceIsRemoteAsync = void 0;
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");
@@ -8,7 +8,7 @@ const chalk_1 = tslib_1.__importDefault(require("chalk"));
8
8
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
9
9
  const log_1 = tslib_1.__importDefault(require("../log"));
10
10
  const prompts_1 = require("../prompts");
11
- async function ensureRemoteVersionPolicyAsync(projectDir, easJsonReader) {
11
+ async function ensureVersionSourceIsRemoteAsync(projectDir, easJsonReader) {
12
12
  const easJsonCliConfig = await easJsonReader.getCliConfigAsync();
13
13
  if ((easJsonCliConfig === null || easJsonCliConfig === void 0 ? void 0 : easJsonCliConfig.appVersionSource) === eas_json_1.AppVersionSource.REMOTE) {
14
14
  return;
@@ -27,20 +27,29 @@ async function ensureRemoteVersionPolicyAsync(projectDir, easJsonReader) {
27
27
  await fs_extra_1.default.writeFile(easJsonPath, `${JSON.stringify(easJson, null, 2)}\n`);
28
28
  log_1.default.withTick('Updated eas.json');
29
29
  }
30
- exports.ensureRemoteVersionPolicyAsync = ensureRemoteVersionPolicyAsync;
31
- async function validateAppConfigForRemoteVersionPolicyAsync(exp) {
30
+ exports.ensureVersionSourceIsRemoteAsync = ensureVersionSourceIsRemoteAsync;
31
+ function validateBuildProfileVersionSettings(profileInfo, cliConfig) {
32
+ if ((cliConfig === null || cliConfig === void 0 ? void 0 : cliConfig.appVersionSource) !== eas_json_1.AppVersionSource.REMOTE) {
33
+ return;
34
+ }
35
+ if (profileInfo.profile.autoIncrement === 'version') {
36
+ throw new Error(`${chalk_1.default.bold('{"autoIncrement": "version"}')} is not supported when app version source is set to remote.`);
37
+ }
38
+ }
39
+ exports.validateBuildProfileVersionSettings = validateBuildProfileVersionSettings;
40
+ function validateAppConfigForRemoteVersionSource(exp, platform) {
32
41
  var _a, _b, _c;
33
42
  if (typeof exp.runtimeVersion === 'object' && ((_a = exp.runtimeVersion) === null || _a === void 0 ? void 0 : _a.policy) === 'nativeVersion') {
34
43
  throw new Error(`${chalk_1.default.bold('nativeVersion')} policy for ${chalk_1.default.bold('runtimeVersion')} is currently not supported when version source is set to remote. Switch policy e.g. to ${chalk_1.default.bold('appVersion')} or define version explicitly.`);
35
44
  }
36
- if (((_b = exp.ios) === null || _b === void 0 ? void 0 : _b.buildNumber) !== undefined) {
37
- throw new Error(`${chalk_1.default.bold('ios.buildNumber')} field in app config is not supported when version source is set to remote, remove it and re-run the command.`);
45
+ if (platform === eas_build_job_1.Platform.IOS && ((_b = exp.ios) === null || _b === void 0 ? void 0 : _b.buildNumber) !== undefined) {
46
+ log_1.default.warn(`${chalk_1.default.bold('ios.buildNumber')} field in app config is ignored when version source is set to remote, but this value will still be in the manifest available via ${chalk_1.default.bold('expo-constants')}. It's recommended to remove this value from app config.`);
38
47
  }
39
- if (((_c = exp.android) === null || _c === void 0 ? void 0 : _c.versionCode) !== undefined) {
40
- throw new Error(`${chalk_1.default.bold('android.versionCode')} field in app config is not supported when version source is set to remote, remove it and re-run the command.`);
48
+ if (platform === eas_build_job_1.Platform.ANDROID && ((_c = exp.android) === null || _c === void 0 ? void 0 : _c.versionCode) !== undefined) {
49
+ log_1.default.warn(`${chalk_1.default.bold('android.versionCode')} field in app config is ignored when version source is set to remote, but this value will still be in the manifest available via ${chalk_1.default.bold('expo-constants')}. It's recommended to remove this value from app config.`);
41
50
  }
42
51
  }
43
- exports.validateAppConfigForRemoteVersionPolicyAsync = validateAppConfigForRemoteVersionPolicyAsync;
52
+ exports.validateAppConfigForRemoteVersionSource = validateAppConfigForRemoteVersionSource;
44
53
  function getBuildVersionName(platform) {
45
54
  if (platform === eas_build_job_1.Platform.ANDROID) {
46
55
  return 'versionCode';
@@ -4,4 +4,5 @@ import { SubmissionContext } from './context';
4
4
  export declare function submitAsync<T extends Platform>(ctx: SubmissionContext<T>): Promise<SubmissionFragment>;
5
5
  export declare function waitToCompleteAsync(submissions: SubmissionFragment[], { verbose }?: {
6
6
  verbose?: boolean;
7
- }): Promise<void>;
7
+ }): Promise<SubmissionFragment[]>;
8
+ export declare function exitWithNonZeroCodeIfSomeSubmissionsDidntFinish(submissions: SubmissionFragment[]): void;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.waitToCompleteAsync = exports.submitAsync = void 0;
3
+ exports.exitWithNonZeroCodeIfSomeSubmissionsDidntFinish = exports.waitToCompleteAsync = exports.submitAsync = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const eas_build_job_1 = require("@expo/eas-build-job");
6
6
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
@@ -49,7 +49,7 @@ async function waitToCompleteAsync(submissions, { verbose = false } = {}) {
49
49
  log_1.default.newLine();
50
50
  }
51
51
  }
52
- exitWithNonZeroCodeIfSomeSubmissionsDidntFinish(completedSubmissions);
52
+ return completedSubmissions;
53
53
  }
54
54
  exports.waitToCompleteAsync = waitToCompleteAsync;
55
55
  function printInstructionsForAndroidSubmission(submission) {
@@ -79,3 +79,4 @@ function exitWithNonZeroCodeIfSomeSubmissionsDidntFinish(submissions) {
79
79
  process.exit(1);
80
80
  }
81
81
  }
82
+ exports.exitWithNonZeroCodeIfSomeSubmissionsDidntFinish = exitWithNonZeroCodeIfSomeSubmissionsDidntFinish;
@@ -0,0 +1,3 @@
1
+ import { PaginatedQueryOptions } from '../commandUtils/pagination';
2
+ export declare const UPDATES_LIMIT = 50;
3
+ export declare function listAndRenderUpdatesOnBranchByNameAsync(projectId: string, branchName: string, options: PaginatedQueryOptions): Promise<void>;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listAndRenderUpdatesOnBranchByNameAsync = exports.UPDATES_LIMIT = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
6
+ const cli_table3_1 = tslib_1.__importDefault(require("cli-table3"));
7
+ const UpdateQuery_1 = require("../graphql/queries/UpdateQuery");
8
+ const log_1 = tslib_1.__importDefault(require("../log"));
9
+ const formatFields_1 = tslib_1.__importDefault(require("../utils/formatFields"));
10
+ const json_1 = require("../utils/json");
11
+ const queries_1 = require("../utils/queries");
12
+ const utils_1 = require("./utils");
13
+ exports.UPDATES_LIMIT = 50;
14
+ async function listAndRenderUpdatesOnBranchByNameAsync(projectId, branchName, options) {
15
+ var _a, _b;
16
+ if (options.nonInteractive) {
17
+ const updates = await queryUpdateGroupsForBranchAsync((_a = options.limit) !== null && _a !== void 0 ? _a : exports.UPDATES_LIMIT, options.offset, projectId, branchName);
18
+ renderUpdateGroups(updates, options, branchName);
19
+ }
20
+ else {
21
+ await (0, queries_1.paginatedQueryWithConfirmPromptAsync)({
22
+ limit: (_b = options.limit) !== null && _b !== void 0 ? _b : exports.UPDATES_LIMIT,
23
+ offset: options.offset,
24
+ queryToPerform: (limit, offset) => queryUpdateGroupsForBranchAsync(limit, offset, projectId, branchName),
25
+ promptOptions: {
26
+ title: 'Load more branches?',
27
+ renderListItems: updates => renderUpdateGroups(updates, options, branchName),
28
+ },
29
+ });
30
+ }
31
+ }
32
+ exports.listAndRenderUpdatesOnBranchByNameAsync = listAndRenderUpdatesOnBranchByNameAsync;
33
+ async function queryUpdateGroupsForBranchAsync(limit, offset, projectId, branchName) {
34
+ const { app } = await UpdateQuery_1.UpdateQuery.viewBranchAsync({
35
+ appId: projectId,
36
+ name: branchName,
37
+ limit,
38
+ offset,
39
+ });
40
+ const updateBranch = app === null || app === void 0 ? void 0 : app.byId.updateBranchByName;
41
+ if (!updateBranch) {
42
+ throw new Error(`Could not find branch "${branchName}"`);
43
+ }
44
+ return updateBranch.updates;
45
+ }
46
+ function renderUpdateGroups(updates, { json }, branchName) {
47
+ var _a, _b;
48
+ const branch = { name: branchName, id: (_b = (_a = updates[0]) === null || _a === void 0 ? void 0 : _a.branch.id) !== null && _b !== void 0 ? _b : 'N/A' };
49
+ if (json) {
50
+ (0, json_1.printJsonOnlyOutput)({ ...branch, currentPage: updates });
51
+ }
52
+ else {
53
+ const updateGroupsTable = new cli_table3_1.default({
54
+ head: utils_1.UPDATE_COLUMNS,
55
+ wordWrap: true,
56
+ });
57
+ updateGroupsTable.push(...(0, utils_1.getUpdateGroupsWithPlatforms)(updates).map(update => [
58
+ (0, utils_1.formatUpdate)(update),
59
+ update.runtimeVersion,
60
+ update.group,
61
+ update.platforms,
62
+ ]));
63
+ log_1.default.addNewLineIfNone();
64
+ log_1.default.log(chalk_1.default.bold('Branch:'));
65
+ log_1.default.log((0, formatFields_1.default)([
66
+ { label: 'Name', value: branch.name },
67
+ { label: 'ID', value: branch.id },
68
+ ]));
69
+ log_1.default.addNewLineIfNone();
70
+ log_1.default.log(updateGroupsTable.toString());
71
+ }
72
+ }
@@ -4,7 +4,16 @@ import { RequestedPlatform } from '../platform';
4
4
  export declare type FormatUpdateParameter = Pick<Update, 'id' | 'createdAt' | 'message'> & {
5
5
  actor?: Maybe<Pick<User, 'username' | 'id'> | Pick<Robot, 'firstName' | 'id'>>;
6
6
  };
7
+ export declare type UpdateGroupDescription = FormatUpdateParameter & {
8
+ branch: string;
9
+ group: string;
10
+ platforms: string;
11
+ runtimeVersion: string;
12
+ };
7
13
  export declare const UPDATE_COLUMNS: string[];
14
+ export declare function getUpdateGroupsWithPlatforms<UpdateFragment extends Pick<Update, 'platform' | 'group'>>(updates: UpdateFragment[]): (UpdateFragment & {
15
+ platforms: string;
16
+ })[];
8
17
  export declare function getPlatformsForGroup({ group, updates, }: {
9
18
  group: string;
10
19
  updates: {
@@ -12,5 +21,10 @@ export declare function getPlatformsForGroup({ group, updates, }: {
12
21
  platform: string;
13
22
  }[];
14
23
  }): string;
24
+ export declare function formatPlatformForUpdateGroup(updateGroup: {
25
+ group: string;
26
+ platform: string;
27
+ }[]): string;
28
+ export declare function truncateString(originalMessage: string, length?: number): string;
15
29
  export declare function formatUpdate(update: FormatUpdateParameter): string;
16
30
  export declare function ensureValidVersions(exp: ExpoConfig, platform: RequestedPlatform): void;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ensureValidVersions = exports.formatUpdate = exports.getPlatformsForGroup = exports.UPDATE_COLUMNS = void 0;
3
+ exports.ensureValidVersions = exports.formatUpdate = exports.truncateString = exports.formatPlatformForUpdateGroup = exports.getPlatformsForGroup = exports.getUpdateGroupsWithPlatforms = exports.UPDATE_COLUMNS = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const timeago_js_1 = require("@expo/timeago.js");
6
6
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
@@ -14,22 +14,39 @@ exports.UPDATE_COLUMNS = [
14
14
  'Update group ID',
15
15
  'Update platforms',
16
16
  ];
17
+ function getUpdateGroupsWithPlatforms(updates) {
18
+ return Object.values((0, groupBy_1.default)(updates, updates => updates.group)).map(updateGroup => ({
19
+ ...updateGroup[0],
20
+ platforms: formatPlatformForUpdateGroup(updateGroup),
21
+ }));
22
+ }
23
+ exports.getUpdateGroupsWithPlatforms = getUpdateGroupsWithPlatforms;
17
24
  function getPlatformsForGroup({ group, updates, }) {
18
25
  const groupedUpdates = (0, groupBy_1.default)(updates, update => update.group);
19
- if (Object.keys(groupedUpdates).length === 0) {
20
- return 'N/A';
21
- }
22
- return groupedUpdates[group]
23
- .map(update => update.platform)
24
- .sort()
25
- .join(', ');
26
+ return formatPlatformForUpdateGroup(groupedUpdates[group]);
26
27
  }
27
28
  exports.getPlatformsForGroup = getPlatformsForGroup;
29
+ function formatPlatformForUpdateGroup(updateGroup) {
30
+ return updateGroup.length === 0
31
+ ? 'N/A'
32
+ : updateGroup
33
+ .map(update => update.platform)
34
+ .sort()
35
+ .join(', ');
36
+ }
37
+ exports.formatPlatformForUpdateGroup = formatPlatformForUpdateGroup;
38
+ function truncateString(originalMessage, length = 512) {
39
+ if (originalMessage.length > length) {
40
+ return originalMessage.substring(0, length - 3) + '...';
41
+ }
42
+ return originalMessage;
43
+ }
44
+ exports.truncateString = truncateString;
28
45
  function formatUpdate(update) {
29
46
  if (!update) {
30
47
  return 'N/A';
31
48
  }
32
- const message = update.message ? `"${update.message}" ` : '';
49
+ const message = update.message ? `"${truncateString(update.message)}" ` : '';
33
50
  return `${message}(${(0, timeago_js_1.format)(update.createdAt, 'en_US')} by ${(0, User_1.getActorDisplayName)(update.actor)})`;
34
51
  }
35
52
  exports.formatUpdate = formatUpdate;
@@ -0,0 +1 @@
1
+ export default function chunk<T>(list: T[], size?: number): T[][];
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function chunk(list, size = 1) {
4
+ const result = [];
5
+ for (let i = 0; i < list.length; i++) {
6
+ const isFirstElementInChunk = i % size === 0;
7
+ if (isFirstElementInChunk) {
8
+ result.push([list[i]]);
9
+ }
10
+ else {
11
+ result[result.length - 1].push(list[i]);
12
+ }
13
+ }
14
+ return result;
15
+ }
16
+ exports.default = chunk;
@@ -0,0 +1,25 @@
1
+ declare type BasePaginatedQueryArgs<QueryReturnType extends Record<string, any>> = {
2
+ limit: number;
3
+ offset: number;
4
+ queryToPerform: (limit: number, offset: number) => Promise<QueryReturnType[]>;
5
+ };
6
+ declare type PaginatedQueryWithConfirmPromptArgs<QueryReturnType extends Record<string, any>> = BasePaginatedQueryArgs<QueryReturnType> & {
7
+ promptOptions: {
8
+ readonly title: string;
9
+ renderListItems: (currentPage: QueryReturnType[]) => void;
10
+ };
11
+ };
12
+ declare type PaginatedQueryWithSelectPromptArgs<QueryReturnType extends Record<string, any>> = BasePaginatedQueryArgs<QueryReturnType> & {
13
+ promptOptions: {
14
+ readonly title: string;
15
+ createDisplayTextForSelectionPromptListItem: (queryItem: QueryReturnType) => string;
16
+ getIdentifierForQueryItem: (queryItem: QueryReturnType) => string;
17
+ };
18
+ };
19
+ export declare function paginatedQueryWithConfirmPromptAsync<QueryReturnType extends Record<string, any>>(queryArgs: PaginatedQueryWithConfirmPromptArgs<QueryReturnType>): Promise<void>;
20
+ /**
21
+ * Returns an array of item(s) where the id is equal to the id of the user's selected item
22
+ * If no items are available for a user to select, this will return an empty array.
23
+ */
24
+ export declare function paginatedQueryWithSelectPromptAsync<QueryReturnType extends Record<string, any>>(queryArgs: PaginatedQueryWithSelectPromptArgs<QueryReturnType>): Promise<QueryReturnType | void>;
25
+ export {};
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.paginatedQueryWithSelectPromptAsync = exports.paginatedQueryWithConfirmPromptAsync = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const prompts_1 = require("../prompts");
6
+ const uniqBy_1 = tslib_1.__importDefault(require("./expodash/uniqBy"));
7
+ const fetchMoreValue = '_fetchMore';
8
+ async function paginatedQueryWithConfirmPromptAsync(queryArgs) {
9
+ return await paginatedQueryWithConfirmPromptInternalAsync(queryArgs, []);
10
+ }
11
+ exports.paginatedQueryWithConfirmPromptAsync = paginatedQueryWithConfirmPromptAsync;
12
+ async function paginatedQueryWithConfirmPromptInternalAsync({ limit, offset, queryToPerform, promptOptions, }, accumulator) {
13
+ // query an extra item to determine if there are more pages left
14
+ const paginatedItems = await queryToPerform(limit + 1, offset);
15
+ const areMorePagesAvailable = paginatedItems.length > limit;
16
+ // drop that extra item used for pagination from our render logic
17
+ const currentPage = paginatedItems.slice(0, limit);
18
+ const newAccumulator = [...accumulator, ...currentPage];
19
+ promptOptions.renderListItems(currentPage);
20
+ if (!areMorePagesAvailable) {
21
+ return;
22
+ }
23
+ if (await (0, prompts_1.confirmAsync)({ message: promptOptions.title })) {
24
+ return await paginatedQueryWithConfirmPromptInternalAsync({
25
+ limit,
26
+ offset: offset + limit,
27
+ queryToPerform,
28
+ promptOptions,
29
+ }, newAccumulator);
30
+ }
31
+ }
32
+ /**
33
+ * Returns an array of item(s) where the id is equal to the id of the user's selected item
34
+ * If no items are available for a user to select, this will return an empty array.
35
+ */
36
+ async function paginatedQueryWithSelectPromptAsync(queryArgs) {
37
+ return await paginatedQueryWithSelectPromptInternalAsync(queryArgs, []);
38
+ }
39
+ exports.paginatedQueryWithSelectPromptAsync = paginatedQueryWithSelectPromptAsync;
40
+ async function paginatedQueryWithSelectPromptInternalAsync({ limit, offset, queryToPerform, promptOptions, }, accumulator) {
41
+ // query an extra item to determine if there are more pages left
42
+ const paginatedItems = await queryToPerform(limit + 1, offset);
43
+ const areMorePagesAvailable = paginatedItems.length > limit;
44
+ // drop that extra item used for pagination from our render logic
45
+ const currentPage = paginatedItems.slice(0, limit);
46
+ const newAccumulator = [...accumulator, ...currentPage];
47
+ const selectionPromptListItems = (0, uniqBy_1.default)(newAccumulator, queryItem => promptOptions.getIdentifierForQueryItem(queryItem)).map(queryItem => ({
48
+ title: promptOptions.createDisplayTextForSelectionPromptListItem(queryItem),
49
+ value: promptOptions.getIdentifierForQueryItem(queryItem),
50
+ }));
51
+ if (areMorePagesAvailable) {
52
+ selectionPromptListItems.push({ title: 'Next page...', value: fetchMoreValue });
53
+ }
54
+ if (selectionPromptListItems.length === 0) {
55
+ return;
56
+ }
57
+ const valueOfUserSelectedListItem = await (0, prompts_1.selectAsync)(promptOptions.title, selectionPromptListItems);
58
+ if (valueOfUserSelectedListItem === fetchMoreValue) {
59
+ return await paginatedQueryWithSelectPromptInternalAsync({
60
+ limit,
61
+ offset: offset + limit,
62
+ queryToPerform,
63
+ promptOptions,
64
+ }, newAccumulator);
65
+ }
66
+ return newAccumulator.find(items => promptOptions.getIdentifierForQueryItem(items) === valueOfUserSelectedListItem);
67
+ }
@@ -4,4 +4,5 @@ export default class GitNoCommitClient extends GitClient {
4
4
  getRootPathAsync(): Promise<string>;
5
5
  makeShallowCopyAsync(destinationPath: string): Promise<void>;
6
6
  isFileIgnoredAsync(filePath: string): Promise<boolean>;
7
+ trackFileAsync(file: string): Promise<void>;
7
8
  }
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const spawn_async_1 = tslib_1.__importDefault(require("@expo/spawn-async"));
5
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
5
6
  const path_1 = tslib_1.__importDefault(require("path"));
7
+ const log_1 = tslib_1.__importDefault(require("../../log"));
6
8
  const local_1 = require("../local");
7
9
  const git_1 = tslib_1.__importDefault(require("./git"));
8
10
  class GitNoCommitClient extends git_1.default {
@@ -23,5 +25,17 @@ class GitNoCommitClient extends git_1.default {
23
25
  await ignore.initIgnoreAsync();
24
26
  return ignore.ignores(filePath);
25
27
  }
28
+ async trackFileAsync(file) {
29
+ try {
30
+ await super.trackFileAsync(file);
31
+ }
32
+ catch {
33
+ // In the no commit workflow it doesn't matter if we fail to track changes,
34
+ // so we can ignore if this throws an exception
35
+ log_1.default.warn(`Unable to track ${chalk_1.default.bold(path_1.default.basename(file))} in Git. Proceeding without tracking.`);
36
+ log_1.default.warn(` Reason: the command ${chalk_1.default.bold(`"git add ${file}"`)} exited with an error.`);
37
+ log_1.default.newLine();
38
+ }
39
+ }
26
40
  }
27
41
  exports.default = GitNoCommitClient;