eas-cli
Version:
EAS command line tool
153 lines (152 loc) • 8.72 kB
JavaScript
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;
}
;