Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 98 additions & 19 deletions src/index.ts
Comment thread
jcesarmobile marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,23 @@ const variables = {
kotlinxCoroutinesVersion: '1.11.0',
};

// AGP 9 changed the defaults of these properties and Android Studio's AGP Upgrade Assistant
// writes them into gradle.properties as compatibility shims. Capacitor apps need none of them,
// most are deprecated and will be removed in AGP 10, and some (android.builtInKotlin,
// android.sdk.defaultTargetSdkToCompileSdkIfUnset) break a Capacitor 9 build.
const agpMigrationAssistantProperties = [
'android.builtInKotlin',
'android.defaults.buildfeatures.resvalues',
'android.dependency.useConstraints',
'android.enableAppCompileTimeRClass',
'android.newDsl',
'android.r8.optimizedResourceShrinking',
'android.r8.strictFullModeForKeepRules',
'android.sdk.defaultTargetSdkToCompileSdkIfUnset',
'android.uniquePackageNames',
'android.usesSdkInManifest.disallowed',
];

process.on('unhandledRejection', (error) => {
process.stderr.write(`ERR: ${error}\n`);
process.exit(1);
Expand Down Expand Up @@ -183,6 +200,16 @@ export const run = async (): Promise<void> => {
logger.info('Updating Android files');

logger.info('Updating gradle files');
const buildGradle = join(androidDir, 'build.gradle');

let gradleText = readFileSync(buildGradle, 'utf-8');
gradleText = gradleText.replace(/\s*jcenter\(\)/g, '');
gradleText = gradleText.replace(`proguard-android.txt`, `proguard-android-optimize.txt`);
writeFileSync(buildGradle, gradleText, { encoding: 'utf-8' });

// gradle.properties
await updateGradleProperties(join(androidDir, 'gradle.properties'));

await runCommand(
'./gradlew',
['wrapper', '--distribution-type', 'all', '--gradle-version', gradleVersion, '--warning-mode', 'all'],
Expand All @@ -206,7 +233,7 @@ export const run = async (): Promise<void> => {
'com.android.tools.build:gradle': AGPVersion,
'com.google.gms:google-services': gmsVersion,
};
await updateBuildGradle(join(androidDir, 'build.gradle'), variablesAndClasspaths);
await updateBuildGradle(buildGradle, variablesAndClasspaths);
}
}

Expand All @@ -230,7 +257,10 @@ export const run = async (): Promise<void> => {
` from: "${coreVersion}"`,
);
let packageSwiftText = readFileSync(packageSwift, 'utf-8');
packageSwiftText = packageSwiftText.replace(/^[ \t]*\.product\(name:\s*"Cordova",\s*package:\s*"capacitor-swift-pm"\),?\n?/m, '');
packageSwiftText = packageSwiftText.replace(
/^[ \t]*\.product\(name:\s*"Cordova",\s*package:\s*"capacitor-swift-pm"\),?\n?/m,
'',
);
writeFileSync(packageSwift, packageSwiftText, { encoding: 'utf-8' });
await updatePodspec(dir, pluginJSON);
}
Expand All @@ -241,18 +271,46 @@ export const run = async (): Promise<void> => {
if (prettierUpdatedFromV2) {
logger.info('');
logger.info('⚠️ Note: Prettier has been updated from v2 to v3.');
logger.info('We recommend running your formatting and linting scripts to ensure your codebase is formatted correctly.');
logger.info(
'We recommend running your formatting and linting scripts to ensure your codebase is formatted correctly.',
);
}
};

async function updateGradleProperties(filename: string): Promise<void> {
const txt = readFile(filename);
if (!txt) {
return;
}

const replaced = removeAGPMigrationProperties(txt);
if (replaced !== txt) {
writeFileSync(filename, replaced, 'utf-8');
}
}

function removeAGPMigrationProperties(txt: string): string {
return txt
.split('\n')
.filter((line) => {
const property = line.split('=')[0].trim();
if (!agpMigrationAssistantProperties.includes(property)) {
return true;
}
logger.info(`Removed ${property} from gradle.properties.`);
return false;
})
.join('\n');
}

function updatePodspec(dir: string, pluginJSON: any) {
const podspecFile = pluginJSON.files.find((file: string) => file.includes('.podspec'));
let txt = readFile(join(dir, podspecFile));
if (!txt) {
return false;
}
txt = txt.replace('s.ios.deployment_target =', 's.ios.deployment_target =');
const prevIOS = Number(iOSVersion)-1;
const prevIOS = Number(iOSVersion) - 1;
txt = txt.replace(`s.ios.deployment_target = '${prevIOS}.0'`, `s.ios.deployment_target = '${iOSVersion}.0'`);
writeFileSync(podspecFile, txt, { encoding: 'utf-8' });
}
Expand Down Expand Up @@ -318,28 +376,49 @@ async function updateBuildGradle(

gradleFile = updateDeprecatedPropertySyntax(gradleFile);
gradleFile = updateKotlinOptions(gradleFile);
gradleFile = gradleFile.replace(/^\s*ext\.kotlin_version = project\.hasProperty\("kotlin_version"\) \? rootProject\.ext\.kotlin_version : '\d+\.\d+\.\d+'\n?/m, '');
gradleFile = gradleFile.replace(/^\s*ext \{\n\s*kotlin_version = project\.hasProperty\("kotlin_version"\) \? rootProject\.ext\.kotlin_version : '[\d.]+'\n\s*\}\n?/gm, '');
gradleFile = gradleFile.replace(/^\s*classpath "org\.jetbrains\.kotlin:kotlin-gradle-plugin:\$kotlin_version"\n?/m, '');
gradleFile = gradleFile.replace(`apply plugin: 'kotlin-android'\n`,'');
gradleFile = gradleFile.replace(
/^\s*ext\.kotlin_version = project\.hasProperty\("kotlin_version"\) \? rootProject\.ext\.kotlin_version : '\d+\.\d+\.\d+'\n?/m,
'',
);
gradleFile = gradleFile.replace(
/^\s*ext \{\n\s*kotlin_version = project\.hasProperty\("kotlin_version"\) \? rootProject\.ext\.kotlin_version : '[\d.]+'\n\s*\}\n?/gm,
'',
);
gradleFile = gradleFile.replace(
/^\s*classpath "org\.jetbrains\.kotlin:kotlin-gradle-plugin:\$kotlin_version"\n?/m,
'',
);
if (!gradleFile.includes(`project.extensions.findByName('kotlin')`)) {
gradleFile = gradleFile.replace(`apply plugin: 'kotlin-android'\n`, '');
} else {
gradleFile = gradleFile.replace(
/\s*if\s*\(\s*project\.extensions\.findByName\('kotlin'\)\s*==\s*null\s*\)\s*\{\s*apply\s+plugin:\s*'kotlin-android'\s*\}*/gm,
'',
);
}

gradleFile = gradleFile.replace(`apply plugin: 'org.jetbrains.kotlin.android'\n`, '');
gradleFile = gradleFile.replace(/^\s*implementation "org\.jetbrains\.kotlin:kotlin-stdlib:\$kotlin_version"\n?/gm, '')
gradleFile = gradleFile.replace(/^\s*targetSdkVersion project\.hasProperty\('targetSdkVersion'\) \? rootProject\.ext\.targetSdkVersion : \d+\n?/m, '');
gradleFile = gradleFile.replace(`implementation "androidx.core:core-ktx:$androidxCoreVersion"`, `implementation "androidx.core:core:$androidxCoreVersion"`)
gradleFile = gradleFile.replace(
/^\s*implementation "org\.jetbrains\.kotlin:kotlin-stdlib:\$kotlin_version"\n?/gm,
'',
);
gradleFile = gradleFile.replace(
/^\s*targetSdkVersion project\.hasProperty\('targetSdkVersion'\) \? rootProject\.ext\.targetSdkVersion : \d+\n?/m,
'',
);
gradleFile = gradleFile.replace(
`implementation "androidx.core:core-ktx:$androidxCoreVersion"`,
`implementation "androidx.core:core:$androidxCoreVersion"`,
);
if (gradleFile.includes(`androidx.core:core-ktx:1`)) {
gradleFile = setAllStringIn(gradleFile, `androidx.core:core`, `'`, `:${variables.androidxCoreVersion}`)
gradleFile = setAllStringIn(gradleFile, `androidx.core:core`, `'`, `:${variables.androidxCoreVersion}`);
}

writeFileSync(filename, gradleFile, 'utf-8');
}

function updateDeprecatedPropertySyntax(gradleFile: string): string {
const propertiesToUpdate = [
'namespace',
'compileSdk',
'url',
'abortOnError',
];
const propertiesToUpdate = ['namespace', 'compileSdk', 'url', 'abortOnError'];

let result = gradleFile;

Expand Down Expand Up @@ -376,7 +455,7 @@ function updateKotlinOptions(gradleFile: string): string {
}
}

result = result.replace(/\n\s*kotlinOptions\s*\{[\s\S]*?\}\s*\n/g, '\n');
result = result.replace(/\n\s*kotlinOptions\s*\{[\s\S]*?\}\s*\n/g, '\n');

const kotlinBlockRegex = /\nkotlin\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/;
const kotlinBlockMatch = kotlinBlockRegex.exec(result);
Expand Down