UNPKG

eas-cli

Version:

EAS command line tool

149 lines (148 loc) 8.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.assertValidOptions = exports.syncCapabilitiesForEntitlementsAsync = exports.EXPO_NO_CAPABILITY_SYNC = void 0; const tslib_1 = require("tslib"); const apple_utils_1 = require("@expo/apple-utils"); const getenv_1 = tslib_1.__importDefault(require("getenv")); const util_1 = require("util"); const capabilityList_1 = require("./capabilityList"); const log_1 = tslib_1.__importDefault(require("../../../log")); exports.EXPO_NO_CAPABILITY_SYNC = getenv_1.default.boolish('EXPO_NO_CAPABILITY_SYNC', false); /** * Given an entitlements JSON object, synchronizes the remote capabilities for a bundle identifier. * * Example entitlements JSON: * ```js * { * 'com.apple.developer.healthkit': true, * 'com.apple.developer.in-app-payments': ['merchant.com.example.development'], * } * ``` * * @param bundleId bundle identifier object * @param entitlements JSON representation of the entitlements plist * @param additionalOptions Additional options to consider when syncing capabilities. * @returns */ async function syncCapabilitiesForEntitlementsAsync(bundleId, entitlements = {}, additionalOptions) { if (exports.EXPO_NO_CAPABILITY_SYNC) { return { enabled: [], disabled: [] }; } const currentCapabilities = await bundleId.getBundleIdCapabilitiesAsync(); if (log_1.default.isDebug) { log_1.default.log(`Current remote capabilities:\n${JSON.stringify(currentCapabilities, null, 2)}`); log_1.default.log(`\nCurrent local entitlements:\n${JSON.stringify(entitlements, null, 2)}`); } const { enabledCapabilityNames, request, remainingCapabilities } = getCapabilitiesToEnable(currentCapabilities, entitlements, additionalOptions); const { disabledCapabilityNames, request: modifiedRequest } = getCapabilitiesToDisable(bundleId, remainingCapabilities, request, entitlements); if (modifiedRequest.length) { log_1.default.debug(`Patch Request:`, (0, util_1.inspect)(modifiedRequest, { depth: null, colors: true })); try { await bundleId.updateBundleIdCapabilityAsync(modifiedRequest); } catch (error) { if (error.message.match(/bundle '[\w\d]+' cannot be deleted. Delete all the Apps/)) { log_1.default.error('Failed to patch capabilities:', (0, util_1.inspect)(modifiedRequest, { depth: null, colors: true })); throw new Error(`Unexpected error occurred while attempting to update capabilities for app "${bundleId.attributes.identifier}".\nCapabilities can be modified manually in the Apple developer console at https://developer-mdn.apple.com/account/resources/identifiers/bundleId/edit/${bundleId.id}.\nAuto capability syncing can be disabled with the environment variable \`EXPO_NO_CAPABILITY_SYNC=1\`.\n${error.message}`); } } } return { enabled: enabledCapabilityNames, disabled: disabledCapabilityNames }; } exports.syncCapabilitiesForEntitlementsAsync = syncCapabilitiesForEntitlementsAsync; function getCapabilitiesToEnable(currentRemoteCapabilities, entitlements, additionalOptions) { const enabledCapabilityNames = []; const request = []; const remainingCapabilities = [...currentRemoteCapabilities]; for (const [key, value] of Object.entries(entitlements)) { const staticCapabilityInfo = capabilityList_1.CapabilityMapping.find(capability => capability.entitlement === key); if (!staticCapabilityInfo) { if (log_1.default.isDebug) { log_1.default.log(`Skipping entitlement that is not supported by EAS: ${key}`); } continue; } assertValidOptions(staticCapabilityInfo, value); const existingIndex = remainingCapabilities.findIndex(existing => existing.isType(staticCapabilityInfo.capability)); const existingRemote = existingIndex > -1 ? remainingCapabilities[existingIndex] : null; const operation = staticCapabilityInfo.getSyncOperation({ existingRemote, entitlements, entitlementValue: value, additionalOptions, }); const { op } = operation; if (log_1.default.isDebug) { log_1.default.log(`Will ${op} remote capability: ${key} (${staticCapabilityInfo.name}).`); } if (op === 'enable') { enabledCapabilityNames.push(staticCapabilityInfo.name); request.push({ capabilityType: staticCapabilityInfo.capability, option: operation.option, }); } else if (op === 'skip' && existingIndex >= 0) { // Remove the item from the list of capabilities so we don't disable it in the next step. remainingCapabilities.splice(existingIndex, 1); } } return { enabledCapabilityNames, request, remainingCapabilities }; } function assertValidOptions(classifier, value) { if (!classifier.validateOptions(value)) { let reason = ''; if (classifier.capabilityIdPrefix) { // Assert string array matching prefix. ASC will throw if the IDs are invalid, this just saves some time. reason = ` Expected an array of strings, where each string is prefixed with "${classifier.capabilityIdPrefix}", ex: ["${classifier.capabilityIdPrefix}myapp"]`; } throw new Error(`iOS entitlement "${classifier.entitlement}" has invalid value "${value}".${reason}`); } } exports.assertValidOptions = assertValidOptions; function getCapabilitiesToDisable(bundleId, currentCapabilities, request, entitlements) { if (log_1.default.isDebug) { log_1.default.log(`Existing to disable: `, currentCapabilities.map(({ id }) => id)); } const disabledCapabilityNames = []; // Disable any extras that aren't present, this functionality is kinda unreliable because managed apps // might be enabling capabilities in modifiers. for (const existingCapability of currentCapabilities) { // GC and IAP are always enabled in apps by default so we should avoid modifying them. if (existingCapability.isType(apple_utils_1.CapabilityType.IN_APP_PURCHASE) || existingCapability.isType(apple_utils_1.CapabilityType.GAME_CENTER)) { continue; } if (existingCapability.attributes) { const adjustedType = getAdjustedCapabilityType(existingCapability, bundleId); if (adjustedType === apple_utils_1.CapabilityType.MDM_MANAGED_ASSOCIATED_DOMAINS && entitlements[capabilityList_1.associatedDomainsCapabilityType.entitlement]) { // MDM Managed Associated Domains is a special case, it should not be disabled if Associated Domains is enabled. continue; } // Only disable capabilities that we handle, // this enables devs to turn on capabilities outside of EAS without worrying about us disabling them. const staticCapabilityInfo = capabilityList_1.CapabilityMapping.find(capability => capability.capability === adjustedType); if (staticCapabilityInfo && !request.find(request => request.capabilityType && existingCapability.isType(request.capabilityType))) { request.push({ // @ts-expect-error capabilityType: adjustedType, option: apple_utils_1.CapabilityTypeOption.OFF, }); disabledCapabilityNames.push(staticCapabilityInfo.name); } } } return { disabledCapabilityNames, request }; } function getAdjustedCapabilityType(existingCapability, bundleId) { let adjustedType = existingCapability.attributes.capabilityType; if (!adjustedType) { if (process.env.NODE_ENV === 'test' && !existingCapability.id.startsWith(`${bundleId.id}_`)) { throw new Error(`Capability ID "${existingCapability.id}" does not start with the bundle ID "${bundleId.id}_". This is likely a test setup issue.`); } adjustedType = existingCapability.id.replace(`${bundleId.id}_`, ''); } return adjustedType; }