Skip to content

Commit 23303d4

Browse files
OS-pedrogustavobilrojcesarmobilemarkemer
authored
chore(cli): Update migrate command for Cap 8 (#8217)
Co-authored-by: jcesarmobile <[email protected]> Co-authored-by: Mark Anderson <[email protected]>
1 parent a32216a commit 23303d4

File tree

2 files changed

+70
-135
lines changed

2 files changed

+70
-135
lines changed

cli/src/android/update.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import {
1919
} from '../plugin';
2020
import type { Plugin } from '../plugin';
2121
import { copy as copyTask } from '../tasks/copy';
22-
import { patchOldCapacitorPlugins } from '../tasks/migrate';
2322
import { readdirp, convertToUnixPath } from '../util/fs';
2423
import { resolveNode } from '../util/node';
2524
import { extractTemplate } from '../util/template';
@@ -39,7 +38,6 @@ export async function updateAndroid(config: Config): Promise<void> {
3938
await writePluginsJson(config, capacitorPlugins);
4039
await removePluginsNativeFiles(config);
4140
const cordovaPlugins = plugins.filter((p) => getPluginType(p, platform) === PluginType.Cordova);
42-
await patchOldCapacitorPlugins(config);
4341
if (cordovaPlugins.length > 0) {
4442
await copyPluginsNativeFiles(config, cordovaPlugins);
4543
}

cli/src/tasks/migrate.ts

Lines changed: 70 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,13 @@ import { join } from 'path';
33
import { rimraf } from 'rimraf';
44
import { coerce, gte, lt } from 'semver';
55

6-
import { getAndroidPlugins } from '../android/common';
76
import c from '../colors';
87
import { getCoreVersion, runTask, checkJDKMajorVersion } from '../common';
98
import type { Config } from '../definitions';
109
import { fatal } from '../errors';
1110
import { getMajoriOSVersion } from '../ios/common';
1211
import { logger, logPrompt, logSuccess } from '../log';
13-
import { getPlugins } from '../plugin';
1412
import { deleteFolderRecursive } from '../util/fs';
15-
import { resolveNode } from '../util/node';
1613
import { checkPackageManager } from '../util/spm';
1714
import { runCommand } from '../util/subprocess';
1815
import { extractTemplate } from '../util/template';
@@ -46,10 +43,10 @@ const plugins = [
4643
'@capacitor/text-zoom',
4744
'@capacitor/toast',
4845
];
49-
const coreVersion = '^7.0.0';
50-
const pluginVersion = '^7.0.0';
51-
const gradleVersion = '8.11.1';
52-
const iOSVersion = '14';
46+
const coreVersion = 'next';
47+
const pluginVersion = 'next';
48+
const gradleVersion = '8.14.3';
49+
const iOSVersion = '15';
5350
let installFailed = false;
5451

5552
export async function migrateCommand(config: Config, noprompt: boolean, packagemanager: string): Promise<void> {
@@ -58,14 +55,14 @@ export async function migrateCommand(config: Config, noprompt: boolean, packagem
5855
}
5956

6057
const capMajor = await checkCapacitorMajorVersion(config);
61-
if (capMajor < 6) {
62-
fatal('Migrate can only be used on Capacitor 6, please use the CLI in Capacitor 6 to upgrade to 6 first');
58+
if (capMajor < 7) {
59+
fatal('Migrate can only be used on Capacitor 7, please use the CLI in Capacitor 7 to upgrade to 7 first');
6360
}
6461

6562
const jdkMajor = await checkJDKMajorVersion();
6663

6764
if (jdkMajor < 21) {
68-
logger.warn('Capacitor 7 requires JDK 21 or higher. Some steps may fail.');
65+
logger.warn('Capacitor 8 requires JDK 21 or higher. Some steps may fail.');
6966
}
7067

7168
const variablesAndClasspaths:
@@ -86,13 +83,13 @@ export async function migrateCommand(config: Config, noprompt: boolean, packagem
8683
};
8784

8885
const monorepoWarning =
89-
'Please note this tool is not intended for use in a mono-repo environment, please check out the Ionic vscode extension for this functionality.';
86+
'Please note this tool is not intended for use in a mono-repo environment, you should migrate manually instead. Refer to https://capacitorjs.com/docs/next/updating/8-0';
9087

9188
logger.info(monorepoWarning);
9289

9390
const { migrateconfirm } = noprompt
9491
? { migrateconfirm: 'y' }
95-
: await logPrompt(`Capacitor 7 sets a deployment target of iOS ${iOSVersion} and Android 15 (SDK 35). \n`, {
92+
: await logPrompt(`Capacitor 8 sets a deployment target of iOS ${iOSVersion} and Android 16 (SDK 36). \n`, {
9693
type: 'text',
9794
name: 'migrateconfirm',
9895
message: `Are you sure you want to migrate? (Y/n)`,
@@ -188,8 +185,8 @@ export async function migrateCommand(config: Config, noprompt: boolean, packagem
188185
}
189186

190187
if (allDependencies['@capacitor/android'] && existsSync(config.android.platformDirAbs)) {
191-
// AndroidManifest.xml add navigation"
192-
await runTask(`Migrating AndroidManifest.xml by adding navigation to Activity configChanges.`, () => {
188+
// AndroidManifest.xml add "density"
189+
await runTask(`Migrating AndroidManifest.xml by adding density to Activity configChanges.`, () => {
193190
return updateAndroidManifest(join(config.android.srcMainDirAbs, 'AndroidManifest.xml'));
194191
});
195192

@@ -222,10 +219,14 @@ export async function migrateCommand(config: Config, noprompt: boolean, packagem
222219
} else {
223220
logger.warn('Skipped upgrading gradle wrapper files');
224221
}
225-
await runTask(`Migrating build.gradle file.`, () => {
222+
await runTask(`Migrating root build.gradle file.`, () => {
226223
return updateBuildGradle(join(config.android.platformDirAbs, 'build.gradle'), variablesAndClasspaths);
227224
});
228225

226+
await runTask(`Migrating app build.gradle file.`, () => {
227+
return updateAppBuildGradle(join(config.android.appDirAbs, 'build.gradle'));
228+
});
229+
229230
// Variables gradle
230231
await runTask(`Migrating variables.gradle file.`, () => {
231232
return (async (): Promise<void> => {
@@ -274,18 +275,18 @@ export async function migrateCommand(config: Config, noprompt: boolean, packagem
274275
}
275276
}
276277
const pluginVariables: { [key: string]: string } = {
277-
firebaseMessagingVersion: '24.1.0',
278+
firebaseMessagingVersion: '25.0.1',
278279
playServicesLocationVersion: '21.3.0',
279-
androidxBrowserVersion: '1.8.0',
280-
androidxMaterialVersion: '1.12.0',
281-
androidxExifInterfaceVersion: '1.3.7',
282-
androidxCoreKTXVersion: '1.12.0',
283-
googleMapsPlayServicesVersion: '18.2.0',
284-
googleMapsUtilsVersion: '3.8.2',
285-
googleMapsKtxVersion: '5.0.0',
286-
googleMapsUtilsKtxVersion: '5.0.0',
287-
kotlinxCoroutinesVersion: '1.7.3',
288-
coreSplashScreenVersion: '1.0.1',
280+
androidxBrowserVersion: '1.9.0',
281+
androidxMaterialVersion: '1.13.0',
282+
androidxExifInterfaceVersion: '1.4.1',
283+
androidxCoreKTXVersion: '1.17.0',
284+
googleMapsPlayServicesVersion: '19.2.0',
285+
googleMapsUtilsVersion: '3.19.1',
286+
googleMapsKtxVersion: '5.2.1',
287+
googleMapsUtilsKtxVersion: '5.2.1',
288+
kotlinxCoroutinesVersion: '1.10.2',
289+
coreSplashScreenVersion: '1.2.0',
289290
};
290291
for (const variable of Object.keys(pluginVariables)) {
291292
await updateFile(config, variablesPath, `${variable} = '`, `'`, pluginVariables[variable], true);
@@ -294,14 +295,6 @@ export async function migrateCommand(config: Config, noprompt: boolean, packagem
294295
});
295296

296297
rimraf.sync(join(config.android.appDirAbs, 'build'));
297-
298-
if (!installFailed) {
299-
await runTask('Migrating package from Manifest to build.gradle in Capacitor plugins', () => {
300-
return patchOldCapacitorPlugins(config);
301-
});
302-
} else {
303-
logger.warn('Skipped migrating package from Manifest to build.gradle in Capacitor plugins');
304-
}
305298
}
306299

307300
// Write all breaking changes
@@ -373,11 +366,15 @@ async function installLatestLibs(dependencyManager: string, runInstall: boolean,
373366

374367
async function writeBreakingChanges() {
375368
const breaking = [
376-
'@capacitor/app',
377-
'@capacitor/device',
378-
'@capacitor/haptics',
369+
'@capacitor/action-sheet',
370+
'@capacitor/barcode-scanner',
371+
'@capacitor/browser',
372+
'@capacitor/camera',
373+
'@capacitor/google-maps',
374+
'@capacitor/push-notifications',
375+
'@capacitor/screen-orientation',
379376
'@capacitor/splash-screen',
380-
'@capacitor/statusbar',
377+
'@capacitor/status-bar',
381378
];
382379
const broken = [];
383380
for (const lib of breaking) {
@@ -387,7 +384,7 @@ async function writeBreakingChanges() {
387384
}
388385
if (broken.length > 0) {
389386
logger.info(
390-
`IMPORTANT: Review https://capacitorjs.com/docs/next/updating/7-0#plugins for breaking changes in these plugins that you use: ${broken.join(
387+
`IMPORTANT: Review https://capacitorjs.com/docs/next/updating/8-0#plugins for breaking changes in these plugins that you use: ${broken.join(
391388
', ',
392389
)}.`,
393390
);
@@ -464,59 +461,6 @@ async function updateGradleWrapperFiles(platformDir: string) {
464461
);
465462
}
466463

467-
async function movePackageFromManifestToBuildGradle(manifestFilename: string, buildGradleFilename: string) {
468-
const manifestText = readFile(manifestFilename);
469-
const buildGradleText = readFile(buildGradleFilename);
470-
471-
if (!manifestText) {
472-
logger.error(`Could not read ${manifestFilename}. Check its permissions and if it exists.`);
473-
return;
474-
}
475-
476-
if (!buildGradleText) {
477-
logger.error(`Could not read ${buildGradleFilename}. Check its permissions and if it exists.`);
478-
return;
479-
}
480-
481-
const namespaceExists = new RegExp(/\s+namespace\s+/).test(buildGradleText);
482-
if (namespaceExists) {
483-
logger.error('Found namespace in build.gradle already, skipping migration');
484-
return;
485-
}
486-
487-
let packageName: string;
488-
const manifestRegEx = new RegExp(/package="([^"]+)"/);
489-
const manifestResults = manifestRegEx.exec(manifestText);
490-
491-
if (manifestResults === null) {
492-
logger.error(`Unable to update Android Manifest. Package not found.`);
493-
return;
494-
} else {
495-
packageName = manifestResults[1];
496-
}
497-
498-
let manifestReplaced = manifestText;
499-
500-
manifestReplaced = manifestReplaced.replace(manifestRegEx, '');
501-
502-
if (manifestText == manifestReplaced) {
503-
logger.error(`Unable to update Android Manifest: no changes were detected in Android Manifest file`);
504-
return;
505-
}
506-
507-
let buildGradleReplaced = buildGradleText;
508-
509-
buildGradleReplaced = setAllStringIn(buildGradleText, 'android {', '\n', `\n namespace "${packageName}"`);
510-
511-
if (buildGradleText == buildGradleReplaced) {
512-
logger.error(`Unable to update buildGradleText: no changes were detected in Android Manifest file`);
513-
return;
514-
}
515-
516-
writeFileSync(manifestFilename, manifestReplaced, 'utf-8');
517-
writeFileSync(buildGradleFilename, buildGradleReplaced, 'utf-8');
518-
}
519-
520464
async function updateBuildGradle(
521465
filename: string,
522466
variablesAndClasspaths: {
@@ -548,6 +492,24 @@ async function updateBuildGradle(
548492
writeFileSync(filename, replaced, 'utf-8');
549493
}
550494

495+
async function updateAppBuildGradle(filename: string) {
496+
const txt = readFile(filename);
497+
if (!txt) {
498+
return;
499+
}
500+
let replaced = txt;
501+
502+
const gradlePproperties = ['compileSdk', 'namespace', 'ignoreAssetsPattern'];
503+
for (const prop of gradlePproperties) {
504+
// Use updated Groovy DSL syntax with " = " assignment
505+
const regex = new RegExp(`(^\\s*${prop})\\s+(?!=)(.+)$`, 'gm');
506+
replaced = replaced.replace(regex, (_match, key, value) => {
507+
return `${key} = ${value.trim()}`;
508+
});
509+
}
510+
writeFileSync(filename, replaced, 'utf-8');
511+
}
512+
551513
async function updateFile(
552514
config: Config,
553515
filename: string,
@@ -621,48 +583,23 @@ async function updateAndroidManifest(filename: string) {
621583
return;
622584
}
623585

624-
if (txt.includes('navigation')) {
586+
if (txt.includes('|density') || txt.includes('density|')) {
625587
return; // Probably already updated
626588
}
627-
const replaced = txt.replace(
628-
'android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"',
629-
'android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation"',
630-
);
631-
632-
writeFileSync(filename, replaced, 'utf-8');
633-
}
589+
// Since navigation was an optional change in Capacitor 7, attempting to add density and/or navigation
590+
const replaced = txt
591+
.replace(
592+
'android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation"',
593+
'android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation|density"',
594+
)
595+
.replace(
596+
'android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"',
597+
'android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation|density"',
598+
);
634599

635-
export async function patchOldCapacitorPlugins(config: Config): Promise<void[]> {
636-
const allPlugins = await getPlugins(config, 'android');
637-
const androidPlugins = await getAndroidPlugins(allPlugins);
638-
return await Promise.all(
639-
androidPlugins.map(async (p) => {
640-
if (p.manifest?.android?.src) {
641-
const buildGradlePath = resolveNode(config.app.rootDir, p.id, p.manifest.android.src, 'build.gradle');
642-
const manifestPath = resolveNode(
643-
config.app.rootDir,
644-
p.id,
645-
p.manifest.android.src,
646-
'src',
647-
'main',
648-
'AndroidManifest.xml',
649-
);
650-
if (buildGradlePath && manifestPath) {
651-
const gradleContent = readFile(buildGradlePath);
652-
if (!gradleContent?.includes('namespace')) {
653-
if (plugins.includes(p.id)) {
654-
logger.warn(
655-
`You are using an outdated version of ${p.id}, update the plugin to version ${pluginVersion}`,
656-
);
657-
} else {
658-
logger.warn(
659-
`${p.id}@${p.version} doesn't officially support Capacitor ${coreVersion} yet, doing our best moving it's package to build.gradle so it builds`,
660-
);
661-
}
662-
movePackageFromManifestToBuildGradle(manifestPath, buildGradlePath);
663-
}
664-
}
665-
}
666-
}),
667-
);
600+
if (!replaced.includes('|density')) {
601+
logger.error(`Unable to add 'density' to 'android:configChanges' in ${filename}. Try adding it manually`);
602+
} else {
603+
writeFileSync(filename, replaced, 'utf-8');
604+
}
668605
}

0 commit comments

Comments
 (0)