eas-cli 3.8.1 → 3.9.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 (42) hide show
  1. package/README.md +79 -54
  2. package/build/build/android/prepareJob.js +0 -1
  3. package/build/build/build.js +1 -1
  4. package/build/build/ios/prepareJob.js +0 -1
  5. package/build/build/runBuildAndSubmit.js +1 -1
  6. package/build/commandUtils/context/DynamicProjectConfigContextField.js +1 -1
  7. package/build/commandUtils/context/contextUtils/findProjectDirAndVerifyProjectSetupAsync.js +1 -1
  8. package/build/commands/build/index.js +1 -1
  9. package/build/commands/build/internal.js +3 -0
  10. package/build/commands/build/resign.js +1 -1
  11. package/build/commands/build/version/set.js +1 -1
  12. package/build/commands/build/version/sync.js +1 -1
  13. package/build/commands/channel/rollout.d.ts +0 -2
  14. package/build/commands/channel/rollout.js +44 -18
  15. package/build/commands/config.js +1 -1
  16. package/build/commands/metadata/lint.js +1 -1
  17. package/build/commands/metadata/pull.js +1 -1
  18. package/build/commands/metadata/push.js +1 -1
  19. package/build/commands/submit.js +1 -1
  20. package/build/commands/update/index.d.ts +0 -2
  21. package/build/commands/update/index.js +31 -136
  22. package/build/commands/update/republish.js +1 -1
  23. package/build/commands/update/roll-back-to-embedded.d.ts +22 -0
  24. package/build/commands/update/roll-back-to-embedded.js +253 -0
  25. package/build/credentials/ios/IosCredentialsProvider.js +1 -1
  26. package/build/credentials/manager/SelectBuildProfileFromEasJson.js +1 -1
  27. package/build/graphql/generated.d.ts +139 -22
  28. package/build/graphql/generated.js +0 -2
  29. package/build/graphql/types/Update.js +1 -0
  30. package/build/project/customBuildConfig.js +19 -4
  31. package/build/project/publish.d.ts +31 -0
  32. package/build/project/publish.js +147 -2
  33. package/build/update/configure.js +1 -1
  34. package/build/update/utils.d.ts +2 -0
  35. package/build/update/utils.js +30 -13
  36. package/build/utils/code-signing.d.ts +4 -3
  37. package/build/utils/code-signing.js +23 -5
  38. package/build/utils/expoCli.js +3 -2
  39. package/build/utils/expodash/areSetsEqual.d.ts +1 -0
  40. package/build/utils/expodash/areSetsEqual.js +6 -0
  41. package/oclif.manifest.json +1 -1
  42. package/package.json +6 -5
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getCustomBuildConfigPath = exports.validateCustomBuildConfigAsync = void 0;
4
4
  const tslib_1 = require("tslib");
5
+ const steps_1 = require("@expo/steps");
5
6
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
6
7
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
7
8
  const path_1 = tslib_1.__importDefault(require("path"));
@@ -9,10 +10,24 @@ async function validateCustomBuildConfigAsync(projectDir, profile) {
9
10
  if (!profile.config) {
10
11
  return;
11
12
  }
12
- const relativeBuildConfigPath = getCustomBuildConfigPath(profile.config);
13
- const buildConfigPath = path_1.default.join(projectDir, relativeBuildConfigPath);
14
- if (!(await fs_extra_1.default.pathExists(buildConfigPath))) {
15
- throw new Error(`Custom build configuration file ${chalk_1.default.bold(relativeBuildConfigPath)} does not exist.`);
13
+ const relativeConfigPath = getCustomBuildConfigPath(profile.config);
14
+ const configPath = path_1.default.join(projectDir, relativeConfigPath);
15
+ if (!(await fs_extra_1.default.pathExists(configPath))) {
16
+ throw new Error(`Custom build configuration file ${chalk_1.default.bold(relativeConfigPath)} does not exist.`);
17
+ }
18
+ try {
19
+ await (0, steps_1.readAndValidateBuildConfigAsync)(configPath, { skipNamespacedFunctionsCheck: true });
20
+ }
21
+ catch (err) {
22
+ if (err instanceof steps_1.errors.BuildConfigYAMLError) {
23
+ throw new Error(`Custom build configuration file ${chalk_1.default.bold(relativeConfigPath)} contains invalid YAML.\n\n${err.message}`);
24
+ }
25
+ else if (err instanceof steps_1.errors.BuildConfigError) {
26
+ throw new Error(`Custom build configuration file ${chalk_1.default.bold(relativeConfigPath)} contains invalid configuration. Please check the docs!\n${err.message}`);
27
+ }
28
+ else {
29
+ throw err;
30
+ }
16
31
  }
17
32
  }
18
33
  exports.validateCustomBuildConfigAsync = validateCustomBuildConfigAsync;
@@ -2,7 +2,9 @@
2
2
  import { ExpoConfig, Platform } from '@expo/config';
3
3
  import Joi from 'joi';
4
4
  import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
5
+ import { PaginatedQueryOptions } from '../commandUtils/pagination';
5
6
  import { PartialManifestAsset } from '../graphql/generated';
7
+ import { RequestedPlatform } from '../platform';
6
8
  export type ExpoCLIExportPlatformFlag = Platform | 'all';
7
9
  type Metadata = {
8
10
  version: number;
@@ -106,4 +108,33 @@ type AssetUploadResult = {
106
108
  };
107
109
  export declare function uploadAssetsAsync(graphqlClient: ExpoGraphqlClient, assetsForUpdateInfoGroup: CollectedAssets, projectId: string, updateSpinnerText?: (totalAssets: number, missingAssets: number) => void): Promise<AssetUploadResult>;
108
110
  export declare function isUploadedAssetCountAboveWarningThreshold(uploadedAssetCount: number, assetLimitPerUpdateGroup: number): boolean;
111
+ export declare function getBranchNameForCommandAsync({ graphqlClient, projectId, channelNameArg, branchNameArg, autoFlag, nonInteractive, paginatedQueryOptions, }: {
112
+ graphqlClient: ExpoGraphqlClient;
113
+ projectId: string;
114
+ channelNameArg: string | undefined;
115
+ branchNameArg: string | undefined;
116
+ autoFlag: boolean;
117
+ nonInteractive: boolean;
118
+ paginatedQueryOptions: PaginatedQueryOptions;
119
+ }): Promise<string>;
120
+ export declare function getUpdateMessageForCommandAsync({ updateMessageArg, autoFlag, nonInteractive, jsonFlag, }: {
121
+ updateMessageArg: string | undefined;
122
+ autoFlag: boolean;
123
+ nonInteractive: boolean;
124
+ jsonFlag: boolean;
125
+ }): Promise<string>;
126
+ export declare const defaultPublishPlatforms: Platform[];
127
+ export declare function getRequestedPlatform(platform: ExpoCLIExportPlatformFlag): RequestedPlatform | null;
128
+ /** Get runtime versions grouped by platform. Runtime version is always `null` on web where the platform is always backwards compatible. */
129
+ export declare function getRuntimeVersionObjectAsync(exp: ExpoConfig, platforms: Platform[], projectDir: string): Promise<{
130
+ platform: string;
131
+ runtimeVersion: string;
132
+ }[]>;
133
+ export declare function getRuntimeToPlatformMappingFromRuntimeVersions(runtimeVersions: {
134
+ platform: string;
135
+ runtimeVersion: string;
136
+ }[]): {
137
+ runtimeVersion: string;
138
+ platforms: string[];
139
+ }[];
109
140
  export {};
@@ -1,23 +1,36 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isUploadedAssetCountAboveWarningThreshold = exports.uploadAssetsAsync = exports.filterOutAssetsThatAlreadyExistAsync = exports.collectAssetsAsync = exports.getOriginalPathFromAssetMap = exports.getAssetHashFromPath = exports.loadAssetMapAsync = exports.filterExportedPlatformsByFlag = exports.loadMetadata = exports.resolveInputDirectoryAsync = exports.buildBundlesAsync = exports.buildUnsortedUpdateInfoGroupAsync = exports.convertAssetToUpdateInfoGroupFormatAsync = exports.getStorageKeyForAssetAsync = exports.getStorageKey = exports.getBase64URLEncoding = exports.guessContentTypeFromExtension = exports.MetadataJoi = void 0;
3
+ exports.getRuntimeToPlatformMappingFromRuntimeVersions = exports.getRuntimeVersionObjectAsync = exports.getRequestedPlatform = exports.defaultPublishPlatforms = exports.getUpdateMessageForCommandAsync = exports.getBranchNameForCommandAsync = exports.isUploadedAssetCountAboveWarningThreshold = exports.uploadAssetsAsync = exports.filterOutAssetsThatAlreadyExistAsync = exports.collectAssetsAsync = exports.getOriginalPathFromAssetMap = exports.getAssetHashFromPath = exports.loadAssetMapAsync = exports.filterExportedPlatformsByFlag = 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
+ const config_plugins_1 = require("@expo/config-plugins");
6
+ const eas_build_job_1 = require("@expo/eas-build-job");
5
7
  const json_file_1 = tslib_1.__importDefault(require("@expo/json-file"));
8
+ const assert_1 = tslib_1.__importDefault(require("assert"));
9
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
6
10
  const crypto_1 = tslib_1.__importDefault(require("crypto"));
7
11
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
8
12
  const joi_1 = tslib_1.__importDefault(require("joi"));
9
13
  const mime_1 = tslib_1.__importDefault(require("mime"));
14
+ const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
10
15
  const path_1 = tslib_1.__importDefault(require("path"));
11
16
  const promise_limit_1 = tslib_1.__importDefault(require("promise-limit"));
17
+ const queries_1 = require("../branch/queries");
18
+ const utils_1 = require("../branch/utils");
12
19
  const generated_1 = require("../graphql/generated");
13
20
  const PublishMutation_1 = require("../graphql/mutations/PublishMutation");
14
21
  const PublishQuery_1 = require("../graphql/queries/PublishQuery");
15
- const log_1 = tslib_1.__importDefault(require("../log"));
22
+ const log_1 = tslib_1.__importStar(require("../log"));
23
+ const platform_1 = require("../platform");
24
+ const prompts_1 = require("../prompts");
25
+ const getBranchNameFromChannelNameAsync_1 = require("../update/getBranchNameFromChannelNameAsync");
26
+ const utils_2 = require("../update/utils");
16
27
  const uploads_1 = require("../uploads");
17
28
  const expoCli_1 = require("../utils/expoCli");
18
29
  const chunk_1 = tslib_1.__importDefault(require("../utils/expodash/chunk"));
19
30
  const filter_1 = require("../utils/expodash/filter");
20
31
  const uniqBy_1 = tslib_1.__importDefault(require("../utils/expodash/uniqBy"));
32
+ const vcs_1 = require("../vcs");
33
+ const workflow_1 = require("./workflow");
21
34
  const fileMetadataJoi = joi_1.default.object({
22
35
  assets: joi_1.default.array()
23
36
  .required()
@@ -319,3 +332,135 @@ function isUploadedAssetCountAboveWarningThreshold(uploadedAssetCount, assetLimi
319
332
  return uploadedAssetCount > warningThreshold;
320
333
  }
321
334
  exports.isUploadedAssetCountAboveWarningThreshold = isUploadedAssetCountAboveWarningThreshold;
335
+ async function getBranchNameForCommandAsync({ graphqlClient, projectId, channelNameArg, branchNameArg, autoFlag, nonInteractive, paginatedQueryOptions, }) {
336
+ if (channelNameArg && branchNameArg) {
337
+ throw new Error('Cannot specify both --channel and --branch. Specify either --channel, --branch, or --auto.');
338
+ }
339
+ if (channelNameArg) {
340
+ return await (0, getBranchNameFromChannelNameAsync_1.getBranchNameFromChannelNameAsync)(graphqlClient, projectId, channelNameArg);
341
+ }
342
+ if (branchNameArg) {
343
+ return branchNameArg;
344
+ }
345
+ if (autoFlag) {
346
+ return await (0, utils_1.getDefaultBranchNameAsync)();
347
+ }
348
+ else if (nonInteractive) {
349
+ throw new Error('Must supply --channel, --branch or --auto when in non-interactive mode.');
350
+ }
351
+ else {
352
+ let branchName;
353
+ try {
354
+ const branch = await (0, queries_1.selectBranchOnAppAsync)(graphqlClient, {
355
+ projectId,
356
+ promptTitle: `Which branch would you like to roll back to embedded on?`,
357
+ displayTextForListItem: updateBranch => ({
358
+ title: `${updateBranch.name} ${chalk_1.default.grey(`- current update: ${(0, utils_2.formatUpdateMessage)(updateBranch.updates[0])}`)}`,
359
+ }),
360
+ paginatedQueryOptions,
361
+ });
362
+ branchName = branch.name;
363
+ }
364
+ catch {
365
+ // unable to select a branch (network error or no branches for project)
366
+ const { name } = await (0, prompts_1.promptAsync)({
367
+ type: 'text',
368
+ name: 'name',
369
+ message: 'No branches found. Provide a branch name:',
370
+ initial: await (0, utils_1.getDefaultBranchNameAsync)(),
371
+ validate: value => (value ? true : 'Branch name may not be empty.'),
372
+ });
373
+ branchName = name;
374
+ }
375
+ (0, assert_1.default)(branchName, 'Branch name must be specified.');
376
+ return branchName;
377
+ }
378
+ }
379
+ exports.getBranchNameForCommandAsync = getBranchNameForCommandAsync;
380
+ async function getUpdateMessageForCommandAsync({ updateMessageArg, autoFlag, nonInteractive, jsonFlag, }) {
381
+ var _a, _b;
382
+ let updateMessage = updateMessageArg;
383
+ if (!updateMessageArg && autoFlag) {
384
+ updateMessage = (_a = (await (0, vcs_1.getVcsClient)().getLastCommitMessageAsync())) === null || _a === void 0 ? void 0 : _a.trim();
385
+ }
386
+ if (!updateMessage) {
387
+ if (nonInteractive) {
388
+ throw new Error('Must supply --message or use --auto when in non-interactive mode');
389
+ }
390
+ const validationMessage = 'publish message may not be empty.';
391
+ if (jsonFlag) {
392
+ throw new Error(validationMessage);
393
+ }
394
+ const { updateMessageLocal } = await (0, prompts_1.promptAsync)({
395
+ type: 'text',
396
+ name: 'updateMessageLocal',
397
+ message: `Provide an roll back message:`,
398
+ initial: (_b = (await (0, vcs_1.getVcsClient)().getLastCommitMessageAsync())) === null || _b === void 0 ? void 0 : _b.trim(),
399
+ validate: (value) => (value ? true : validationMessage),
400
+ });
401
+ updateMessage = updateMessageLocal;
402
+ }
403
+ (0, assert_1.default)(updateMessage, 'Update message must be specified.');
404
+ const truncatedMessage = (0, utils_2.truncateString)(updateMessage, 1024);
405
+ if (truncatedMessage !== updateMessage) {
406
+ log_1.default.warn('Update message exceeds the allowed 1024 character limit. Truncating message...');
407
+ }
408
+ return updateMessage;
409
+ }
410
+ exports.getUpdateMessageForCommandAsync = getUpdateMessageForCommandAsync;
411
+ exports.defaultPublishPlatforms = ['android', 'ios'];
412
+ function getRequestedPlatform(platform) {
413
+ switch (platform) {
414
+ case 'android':
415
+ return platform_1.RequestedPlatform.Android;
416
+ case 'ios':
417
+ return platform_1.RequestedPlatform.Ios;
418
+ case 'web':
419
+ return null;
420
+ case 'all':
421
+ return platform_1.RequestedPlatform.All;
422
+ default:
423
+ throw new Error(`Unsupported platform: ${platform}`);
424
+ }
425
+ }
426
+ exports.getRequestedPlatform = getRequestedPlatform;
427
+ /** Get runtime versions grouped by platform. Runtime version is always `null` on web where the platform is always backwards compatible. */
428
+ async function getRuntimeVersionObjectAsync(exp, platforms, projectDir) {
429
+ var _a, _b;
430
+ for (const platform of platforms) {
431
+ if (platform === 'web') {
432
+ continue;
433
+ }
434
+ const isPolicy = typeof ((_b = (_a = exp[platform]) === null || _a === void 0 ? void 0 : _a.runtimeVersion) !== null && _b !== void 0 ? _b : exp.runtimeVersion) === 'object';
435
+ if (isPolicy) {
436
+ const isManaged = (await (0, workflow_1.resolveWorkflowAsync)(projectDir, platform)) ===
437
+ eas_build_job_1.Workflow.MANAGED;
438
+ if (!isManaged) {
439
+ throw new Error('Runtime version policies are only supported in the managed workflow. In the bare workflow, runtime version needs to be set manually.');
440
+ }
441
+ }
442
+ }
443
+ return [...new Set(platforms)].map(platform => {
444
+ if (platform === 'web') {
445
+ return { platform: 'web', runtimeVersion: 'UNVERSIONED' };
446
+ }
447
+ return {
448
+ platform,
449
+ runtimeVersion: (0, nullthrows_1.default)(config_plugins_1.Updates.getRuntimeVersion(exp, platform), `Unable to determine runtime version for ${platform_1.requestedPlatformDisplayNames[platform]}. ${(0, log_1.learnMore)('https://docs.expo.dev/eas-update/runtime-versions/')}`),
450
+ };
451
+ });
452
+ }
453
+ exports.getRuntimeVersionObjectAsync = getRuntimeVersionObjectAsync;
454
+ function getRuntimeToPlatformMappingFromRuntimeVersions(runtimeVersions) {
455
+ const runtimeToPlatformMapping = [];
456
+ for (const runtime of runtimeVersions) {
457
+ const platforms = runtimeVersions
458
+ .filter(({ runtimeVersion }) => runtimeVersion === runtime.runtimeVersion)
459
+ .map(({ platform }) => platform);
460
+ if (!runtimeToPlatformMapping.find(item => item.runtimeVersion === runtime.runtimeVersion)) {
461
+ runtimeToPlatformMapping.push({ runtimeVersion: runtime.runtimeVersion, platforms });
462
+ }
463
+ }
464
+ return runtimeToPlatformMapping;
465
+ }
466
+ exports.getRuntimeToPlatformMappingFromRuntimeVersions = getRuntimeToPlatformMappingFromRuntimeVersions;
@@ -166,7 +166,7 @@ async function ensureEASUpdateIsConfiguredInEasJsonAsync(projectDir) {
166
166
  return;
167
167
  }
168
168
  try {
169
- const easJsonAccessor = new eas_json_1.EasJsonAccessor(projectDir);
169
+ const easJsonAccessor = eas_json_1.EasJsonAccessor.fromProjectPath(projectDir);
170
170
  await easJsonAccessor.readRawJsonAsync();
171
171
  easJsonAccessor.patch(easJsonRawObject => {
172
172
  const easBuildProfilesWithChannels = Object.keys(easJsonRawObject.build).reduce((acc, profileNameKey) => {
@@ -19,6 +19,8 @@ export type FormattedUpdateGroupDescription = {
19
19
  group: string;
20
20
  platforms: string;
21
21
  runtimeVersion: string;
22
+ codeSigningKey: string | undefined;
23
+ isRollBackToEmbedded: boolean;
22
24
  };
23
25
  export type FormattedBranchDescription = {
24
26
  branch: string;
@@ -20,10 +20,13 @@ exports.UPDATE_COLUMNS = [
20
20
  ];
21
21
  exports.UPDATE_COLUMNS_WITH_BRANCH = ['Branch', ...exports.UPDATE_COLUMNS];
22
22
  function formatUpdateGroup(update) {
23
+ var _a;
23
24
  return (0, formatFields_1.default)([
24
25
  { label: 'Platforms', value: update.platforms },
25
26
  { label: 'Runtime Version', value: update.runtimeVersion },
26
27
  { label: 'Message', value: update.message },
28
+ { label: 'Code Signing Key', value: (_a = update.codeSigningKey) !== null && _a !== void 0 ? _a : 'N/A' },
29
+ { label: 'Is Roll Back to Embedded', value: update.isRollBackToEmbedded ? 'Yes' : 'No' },
27
30
  { label: 'Group ID', value: update.group },
28
31
  ]);
29
32
  }
@@ -116,30 +119,42 @@ function getUpdateGroupJsonInfo(updateGroups) {
116
119
  runtimeVersion: update.runtimeVersion,
117
120
  platform: update.platform,
118
121
  manifestPermalink: update.manifestPermalink,
122
+ isRollBackToEmbedded: update.isRollBackToEmbedded,
119
123
  gitCommitHash: update.gitCommitHash,
120
124
  }));
121
125
  }
122
126
  exports.getUpdateGroupJsonInfo = getUpdateGroupJsonInfo;
123
127
  function getUpdateGroupDescriptions(updateGroups) {
124
- return updateGroups.map(updateGroup => ({
125
- message: formatUpdateMessage(updateGroup[0]),
126
- runtimeVersion: updateGroup[0].runtimeVersion,
127
- group: updateGroup[0].group,
128
- platforms: formatPlatformForUpdateGroup(updateGroup),
129
- }));
128
+ return updateGroups.map(updateGroup => {
129
+ var _a;
130
+ return ({
131
+ message: formatUpdateMessage(updateGroup[0]),
132
+ runtimeVersion: updateGroup[0].runtimeVersion,
133
+ isRollBackToEmbedded: updateGroup[0].isRollBackToEmbedded,
134
+ codeSigningKey: (_a = updateGroup[0].codeSigningInfo) === null || _a === void 0 ? void 0 : _a.keyid,
135
+ group: updateGroup[0].group,
136
+ platforms: formatPlatformForUpdateGroup(updateGroup),
137
+ });
138
+ });
130
139
  }
131
140
  exports.getUpdateGroupDescriptions = getUpdateGroupDescriptions;
132
141
  function getUpdateGroupDescriptionsWithBranch(updateGroups) {
133
- return updateGroups.map(updateGroup => ({
134
- branch: updateGroup[0].branch.name,
135
- message: formatUpdateMessage(updateGroup[0]),
136
- runtimeVersion: updateGroup[0].runtimeVersion,
137
- group: updateGroup[0].group,
138
- platforms: formatPlatformForUpdateGroup(updateGroup),
139
- }));
142
+ return updateGroups.map(updateGroup => {
143
+ var _a;
144
+ return ({
145
+ branch: updateGroup[0].branch.name,
146
+ message: formatUpdateMessage(updateGroup[0]),
147
+ runtimeVersion: updateGroup[0].runtimeVersion,
148
+ isRollBackToEmbedded: updateGroup[0].isRollBackToEmbedded,
149
+ codeSigningKey: (_a = updateGroup[0].codeSigningInfo) === null || _a === void 0 ? void 0 : _a.keyid,
150
+ group: updateGroup[0].group,
151
+ platforms: formatPlatformForUpdateGroup(updateGroup),
152
+ });
153
+ });
140
154
  }
141
155
  exports.getUpdateGroupDescriptionsWithBranch = getUpdateGroupDescriptionsWithBranch;
142
156
  function getBranchDescription(branch) {
157
+ var _a;
143
158
  if (branch.updates.length === 0) {
144
159
  return { branch: branch.name };
145
160
  }
@@ -149,6 +164,8 @@ function getBranchDescription(branch) {
149
164
  update: {
150
165
  message: formatUpdateMessage(latestUpdate),
151
166
  runtimeVersion: latestUpdate.runtimeVersion,
167
+ isRollBackToEmbedded: latestUpdate.isRollBackToEmbedded,
168
+ codeSigningKey: (_a = latestUpdate.codeSigningInfo) === null || _a === void 0 ? void 0 : _a.keyid,
152
169
  group: latestUpdate.group,
153
170
  platforms: getPlatformsForGroup({
154
171
  group: latestUpdate.group,
@@ -2,7 +2,7 @@ import { ExpoConfig } from '@expo/config';
2
2
  import { pki as PKI } from 'node-forge';
3
3
  import { Response } from '../fetch';
4
4
  import { PartialManifest } from '../graphql/generated';
5
- type CodeSigningInfo = {
5
+ export type CodeSigningInfo = {
6
6
  privateKey: PKI.rsa.PrivateKey;
7
7
  certificate: PKI.Certificate;
8
8
  codeSigningMetadata: {
@@ -19,6 +19,7 @@ export declare function getKeyAndCertificateFromPathsAsync({ codeSigningCertific
19
19
  certificate: PKI.Certificate;
20
20
  }>;
21
21
  export declare function getManifestBodyAsync(res: Response): Promise<string | null>;
22
- export declare function signManifestBody(body: string, codeSigningInfo: CodeSigningInfo): string;
22
+ export declare function getDirectiveBodyAsync(res: Response): Promise<string | null>;
23
+ export declare function signBody(body: string, codeSigningInfo: CodeSigningInfo): string;
23
24
  export declare function checkManifestBodyAgainstUpdateInfoGroup(manifestResponseBody: string, partialManifest: PartialManifest): void;
24
- export {};
25
+ export declare function checkDirectiveBodyAgainstUpdateInfoGroup(directiveResponseBody: string): void;
@@ -1,12 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkManifestBodyAgainstUpdateInfoGroup = exports.signManifestBody = exports.getManifestBodyAsync = exports.getKeyAndCertificateFromPathsAsync = exports.getCodeSigningInfoAsync = void 0;
3
+ exports.checkDirectiveBodyAgainstUpdateInfoGroup = exports.checkManifestBodyAgainstUpdateInfoGroup = exports.signBody = exports.getDirectiveBodyAsync = exports.getManifestBodyAsync = exports.getKeyAndCertificateFromPathsAsync = exports.getCodeSigningInfoAsync = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const code_signing_certificates_1 = require("@expo/code-signing-certificates");
6
6
  const multipart_body_parser_1 = require("@expo/multipart-body-parser");
7
7
  const fast_deep_equal_1 = tslib_1.__importDefault(require("fast-deep-equal"));
8
8
  const fs_1 = require("fs");
9
9
  const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
10
+ const areSetsEqual_1 = tslib_1.__importDefault(require("./expodash/areSetsEqual"));
10
11
  async function getCodeSigningInfoAsync(config, privateKeyPath) {
11
12
  var _a, _b;
12
13
  const codeSigningCertificatePath = (_a = config.updates) === null || _a === void 0 ? void 0 : _a.codeSigningCertificate;
@@ -61,7 +62,7 @@ async function getKeyAndCertificateFromPathsAsync({ codeSigningCertificatePath,
61
62
  };
62
63
  }
63
64
  exports.getKeyAndCertificateFromPathsAsync = getKeyAndCertificateFromPathsAsync;
64
- async function getManifestBodyAsync(res) {
65
+ async function getMultipartBodyPartAsync(res, partName) {
65
66
  var _a;
66
67
  const contentType = res.headers.get('content-type');
67
68
  if (!contentType) {
@@ -69,14 +70,21 @@ async function getManifestBodyAsync(res) {
69
70
  }
70
71
  const bodyBuffer = await res.arrayBuffer();
71
72
  const multipartParts = await (0, multipart_body_parser_1.parseMultipartMixedResponseAsync)(contentType, Buffer.from(bodyBuffer));
72
- const manifestPart = multipartParts.find(part => (0, multipart_body_parser_1.isMultipartPartWithName)(part, 'manifest'));
73
+ const manifestPart = multipartParts.find(part => (0, multipart_body_parser_1.isMultipartPartWithName)(part, partName));
73
74
  return (_a = manifestPart === null || manifestPart === void 0 ? void 0 : manifestPart.body) !== null && _a !== void 0 ? _a : null;
74
75
  }
76
+ async function getManifestBodyAsync(res) {
77
+ return await getMultipartBodyPartAsync(res, 'manifest');
78
+ }
75
79
  exports.getManifestBodyAsync = getManifestBodyAsync;
76
- function signManifestBody(body, codeSigningInfo) {
80
+ async function getDirectiveBodyAsync(res) {
81
+ return await getMultipartBodyPartAsync(res, 'directive');
82
+ }
83
+ exports.getDirectiveBodyAsync = getDirectiveBodyAsync;
84
+ function signBody(body, codeSigningInfo) {
77
85
  return (0, code_signing_certificates_1.signBufferRSASHA256AndVerify)(codeSigningInfo.privateKey, codeSigningInfo.certificate, Buffer.from(body, 'utf-8'));
78
86
  }
79
- exports.signManifestBody = signManifestBody;
87
+ exports.signBody = signBody;
80
88
  function assertAssetParity(manifestResponseBodyAssetJSON, partialManifestAsset) {
81
89
  const baseErrorMessage = `Code signing manifest integrity error: Manifest asset tamper detected for asset: ${partialManifestAsset.bundleKey}; field: `;
82
90
  if (manifestResponseBodyAssetJSON.hash !== partialManifestAsset.fileSHA256) {
@@ -114,3 +122,13 @@ function checkManifestBodyAgainstUpdateInfoGroup(manifestResponseBody, partialMa
114
122
  }
115
123
  }
116
124
  exports.checkManifestBodyAgainstUpdateInfoGroup = checkManifestBodyAgainstUpdateInfoGroup;
125
+ function checkDirectiveBodyAgainstUpdateInfoGroup(directiveResponseBody) {
126
+ const directiveResponseBodyJSON = JSON.parse(directiveResponseBody);
127
+ if (!(0, areSetsEqual_1.default)(new Set(Object.keys(directiveResponseBodyJSON)), new Set(['extra', 'type', 'parameters']))) {
128
+ throw new Error('Code signing directive integrity error: Unexpected keys');
129
+ }
130
+ if (directiveResponseBodyJSON.type !== 'rollBackToEmbedded') {
131
+ throw new Error('Code signing directive integrity error: Incorrect directive type');
132
+ }
133
+ }
134
+ exports.checkDirectiveBodyAgainstUpdateInfoGroup = checkDirectiveBodyAgainstUpdateInfoGroup;
@@ -5,7 +5,7 @@ const tslib_1 = require("tslib");
5
5
  const spawn_async_1 = tslib_1.__importDefault(require("@expo/spawn-async"));
6
6
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
7
  const getenv_1 = require("getenv");
8
- const resolve_from_1 = tslib_1.__importDefault(require("resolve-from"));
8
+ const resolve_from_1 = tslib_1.__importStar(require("resolve-from"));
9
9
  const semver_1 = tslib_1.__importDefault(require("semver"));
10
10
  const log_1 = tslib_1.__importDefault(require("../log"));
11
11
  const memoize_1 = require("./expodash/memoize");
@@ -52,7 +52,8 @@ function shouldUseVersionedExpoCLIExpensive(projectDir, exp) {
52
52
  exports.shouldUseVersionedExpoCLIExpensive = shouldUseVersionedExpoCLIExpensive;
53
53
  exports.shouldUseVersionedExpoCLI = (0, memoize_1.memoize)(shouldUseVersionedExpoCLIExpensive);
54
54
  async function expoCommandAsync(projectDir, args, { silent = false } = {}) {
55
- const expoCliPath = (0, resolve_from_1.default)(projectDir, 'expo/bin/cli.js');
55
+ var _a;
56
+ const expoCliPath = (_a = (0, resolve_from_1.silent)(projectDir, 'expo/bin/cli')) !== null && _a !== void 0 ? _a : (0, resolve_from_1.default)(projectDir, 'expo/bin/cli.js');
56
57
  const spawnPromise = (0, spawn_async_1.default)(expoCliPath, args, {
57
58
  stdio: ['inherit', 'pipe', 'pipe'], // inherit stdin so user can install a missing expo-cli from inside this command
58
59
  });
@@ -0,0 +1 @@
1
+ export default function areSetsEqual<T>(a: Set<T>, b: Set<T>): boolean;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function areSetsEqual(a, b) {
4
+ return a.size === b.size && [...a].every(value => b.has(value));
5
+ }
6
+ exports.default = areSetsEqual;