UNPKG

eas-cli

Version:
153 lines (152 loc) 8.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.warnIfAndroidPackageDefinedInAppConfigForBareWorkflowProject = exports.isApplicationIdValid = exports.getApplicationIdAsync = exports.getApplicationIdFromBareAsync = exports.AmbiguousApplicationIdError = exports.ensureApplicationIdIsDefinedForManagedProjectAsync = exports.INVALID_APPLICATION_ID_MESSAGE = void 0; const tslib_1 = require("tslib"); const config_1 = require("@expo/config"); const config_plugins_1 = require("@expo/config-plugins"); const eas_build_job_1 = require("@expo/eas-build-job"); const assert_1 = tslib_1.__importDefault(require("assert")); const chalk_1 = tslib_1.__importDefault(require("chalk")); const fs_extra_1 = tslib_1.__importDefault(require("fs-extra")); const nullthrows_1 = tslib_1.__importDefault(require("nullthrows")); const gradleUtils = tslib_1.__importStar(require("./gradleUtils")); const appJson_1 = require("../../build/utils/appJson"); const env_1 = tslib_1.__importDefault(require("../../env")); const log_1 = tslib_1.__importStar(require("../../log")); const projectUtils_1 = require("../../project/projectUtils"); const prompts_1 = require("../../prompts"); const workflow_1 = require("../workflow"); exports.INVALID_APPLICATION_ID_MESSAGE = `Invalid format of Android applicationId. Only alphanumeric characters, '.' and '_' are allowed, and each '.' must be followed by a letter.`; async function ensureApplicationIdIsDefinedForManagedProjectAsync({ graphqlClient, projectDir, projectId, exp, vcsClient, nonInteractive, }) { const workflow = await (0, workflow_1.resolveWorkflowAsync)(projectDir, eas_build_job_1.Platform.ANDROID, vcsClient); (0, assert_1.default)(workflow === eas_build_job_1.Workflow.MANAGED, 'This function should be called only for managed projects'); try { return await getApplicationIdAsync(projectDir, exp, vcsClient, { moduleName: gradleUtils.DEFAULT_MODULE_NAME, }); } catch { return await configureApplicationIdAsync({ graphqlClient, projectDir, projectId, exp, nonInteractive, }); } } exports.ensureApplicationIdIsDefinedForManagedProjectAsync = ensureApplicationIdIsDefinedForManagedProjectAsync; class AmbiguousApplicationIdError extends Error { constructor(message) { super(message ?? 'Could not resolve applicationId.'); } } exports.AmbiguousApplicationIdError = AmbiguousApplicationIdError; async function getApplicationIdFromBareAsync(projectDir, gradleContext) { const errorMessage = 'Could not read applicationId from Android project.'; if (gradleContext) { const buildGradle = await gradleUtils.getAppBuildGradleAsync(projectDir); const applicationIdSuffix = gradleUtils.resolveConfigValue(buildGradle, 'applicationIdSuffix', gradleContext.flavor); if (applicationIdSuffix) { throw new Error('"applicationIdSuffix" in app/build.gradle is not supported.'); } const applicationId = gradleUtils.resolveConfigValue(buildGradle, 'applicationId', gradleContext.flavor); return (0, nullthrows_1.default)(applicationId, errorMessage); } else { // should return value only if productFlavors are not used const buildGradlePath = config_plugins_1.AndroidConfig.Paths.getAppBuildGradleFilePath(projectDir); const buildGradle = await fs_extra_1.default.readFile(buildGradlePath, 'utf8'); const matchResult = buildGradle.match(/applicationId ['"](.*)['"]/); if (buildGradle.match(/applicationIdSuffix/)) { throw new Error('"applicationIdSuffix" in app/build.gradle is not supported.'); } if (buildGradle.match(/productFlavors/)) { throw new AmbiguousApplicationIdError('Failed to autodetect applicationId in multi-flavor project.'); } return (0, nullthrows_1.default)(matchResult?.[1], errorMessage); } } exports.getApplicationIdFromBareAsync = getApplicationIdFromBareAsync; async function getApplicationIdAsync(projectDir, exp, vcsClient, gradleContext) { if (env_1.default.overrideAndroidApplicationId) { return env_1.default.overrideAndroidApplicationId; } const workflow = await (0, workflow_1.resolveWorkflowAsync)(projectDir, eas_build_job_1.Platform.ANDROID, vcsClient); if (workflow === eas_build_job_1.Workflow.GENERIC) { warnIfAndroidPackageDefinedInAppConfigForBareWorkflowProject(projectDir, exp); return await getApplicationIdFromBareAsync(projectDir, gradleContext); } else { const applicationId = config_plugins_1.AndroidConfig.Package.getPackage(exp); if (!applicationId || !isApplicationIdValid(applicationId)) { if (applicationId) { log_1.default.warn(exports.INVALID_APPLICATION_ID_MESSAGE); } throw new Error(`Specify "android.package" in ${(0, projectUtils_1.getProjectConfigDescription)(projectDir)} and run this command again.`); } else { return applicationId; } } } exports.getApplicationIdAsync = getApplicationIdAsync; async function configureApplicationIdAsync({ graphqlClient, projectDir, projectId, exp, nonInteractive, }) { if (nonInteractive) { throw new Error(`The "android.package" is required to be set in app config when running in non-interactive mode. ${(0, log_1.learnMore)('https://docs.expo.dev/versions/latest/config/app/#package')}`); } const paths = (0, config_1.getConfigFilePaths)(projectDir); // we can't automatically update app.config.js if (paths.dynamicConfigPath) { throw new Error(`"android.package" is not defined in your app.config.js and we can't update this file programmatically. Add the value on your own and run this command again.`); } (0, assert_1.default)(paths.staticConfigPath, 'app.json must exist'); log_1.default.addNewLineIfNone(); log_1.default.log(`${chalk_1.default.bold(`📝 Android application id`)} ${chalk_1.default.dim((0, log_1.learnMore)('https://expo.fyi/android-package'))}`); const suggestedAndroidApplicationId = await getSuggestedApplicationIdAsync(graphqlClient, exp, projectId); const { packageName } = await (0, prompts_1.promptAsync)({ name: 'packageName', type: 'text', message: `What would you like your Android application id to be?`, initial: suggestedAndroidApplicationId, validate: value => (isApplicationIdValid(value) ? true : exports.INVALID_APPLICATION_ID_MESSAGE), }); const rawStaticConfig = (0, appJson_1.readAppJson)(paths.staticConfigPath); rawStaticConfig.expo = { ...rawStaticConfig.expo, android: { ...rawStaticConfig.expo?.android, package: packageName }, }; await fs_extra_1.default.writeJson(paths.staticConfigPath, rawStaticConfig, { spaces: 2 }); exp.android = { ...exp.android, package: packageName }; return packageName; } function isApplicationIdValid(applicationId) { return /^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)+$/.test(applicationId); } exports.isApplicationIdValid = isApplicationIdValid; let warnPrinted = false; function warnIfAndroidPackageDefinedInAppConfigForBareWorkflowProject(projectDir, exp) { if (config_plugins_1.AndroidConfig.Package.getPackage(exp) && !warnPrinted) { log_1.default.warn(`Specified value for "android.package" in ${(0, projectUtils_1.getProjectConfigDescription)(projectDir)} is ignored because an ${chalk_1.default.bold('android')} directory was detected in the project.\n` + 'EAS Build will use the value found in the native code.'); warnPrinted = true; } } exports.warnIfAndroidPackageDefinedInAppConfigForBareWorkflowProject = warnIfAndroidPackageDefinedInAppConfigForBareWorkflowProject; async function getSuggestedApplicationIdAsync(graphqlClient, exp, projectId) { // Attempt to use the ios bundle id first since it's convenient to have them aligned. const maybeBundleId = config_plugins_1.IOSConfig.BundleIdentifier.getBundleIdentifier(exp); if (maybeBundleId && isApplicationIdValid(maybeBundleId)) { return maybeBundleId; } else { // the only callsite is heavily interactive const account = await (0, projectUtils_1.getOwnerAccountForProjectIdAsync)(graphqlClient, projectId); // It's common to use dashes in your node project name, strip them from the suggested package name. const possibleId = `com.${account.name}.${exp.slug}`.split('-').join(''); if (isApplicationIdValid(possibleId)) { return possibleId; } } return undefined; }