diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 7824862..ca10a20 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -129,134 +129,28 @@ jobs: - name: Configure Gradle with signing run: | cd android - + # Decode Android signing keystore echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > app/withyou-release-key.keystore - - # Update settings.gradle - cat > settings.gradle << 'SETTINGS_EOF' - pluginManagement { - repositories { - maven { url 'https://developer.huawei.com/repo/' } - google() - mavenCentral() - gradlePluginPortal() - } - def reactNativeGradlePlugin = new File( - providers.exec { - workingDir(rootDir) - commandLine("node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })") - }.standardOutput.asText.get().trim() - ).getParentFile().absolutePath - includeBuild(reactNativeGradlePlugin) - - def expoPluginsPath = new File( - providers.exec { - workingDir(rootDir) - commandLine("node", "--print", "require.resolve('expo-modules-autolinking/package.json', { paths: [require.resolve('expo/package.json')] })") - }.standardOutput.asText.get().trim(), - "../android/expo-gradle-plugin" - ).absolutePath - includeBuild(expoPluginsPath) - } - - plugins { - id("com.facebook.react.settings") - id("expo-autolinking-settings") - id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0" - } - - dependencyResolutionManagement { - repositories { - maven { url 'https://developer.huawei.com/repo/' } - google() - mavenCentral() - maven { url 'https://www.jitpack.io' } - } - } - - extensions.configure(com.facebook.react.ReactSettingsExtension) { ex -> - if (System.getenv('EXPO_USE_COMMUNITY_AUTOLINKING') == '1') { - ex.autolinkLibrariesFromCommand() - } else { - ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand) - } - } - expoAutolinking.useExpoModules() - - rootProject.name = '威友' - - expoAutolinking.useExpoVersionCatalog() - - include ':app' - includeBuild(expoAutolinking.reactNativeGradlePlugin) - SETTINGS_EOF - - # Update build.gradle - cat > build.gradle << 'BUILD_EOF' - // Top-level build file where you can add configuration options common to all sub-projects/modules. - - buildscript { - repositories { - maven { url 'https://developer.huawei.com/repo/' } - google() - mavenCentral() - } - dependencies { - classpath('com.huawei.agconnect:agcp:1.9.1.301') { - exclude group: 'com.huawei.agconnect', module: 'agconnect-apms-plugin' - } - classpath('com.google.gms:google-services:4.4.4') - classpath('com.android.tools.build:gradle:8.12.0') - classpath('com.facebook.react:react-native-gradle-plugin') - classpath('org.jetbrains.kotlin:kotlin-gradle-plugin') - } - } - - allprojects { - repositories { - maven { url 'https://developer.huawei.com/repo/' } - google() - mavenCentral() - maven { url 'https://www.jitpack.io' } - } - } - - apply plugin: "expo-root-project" - apply plugin: "com.facebook.react.rootproject" - BUILD_EOF - - # Update gradle.properties (without secrets for better cache hit rate) - cat > gradle.properties << 'PROPS_EOF' + + # Append signing properties to gradle.properties (secrets appended after prebuild for better cache hit rate) + cat >> gradle.properties << 'SIGNING_PROPS' + MYAPP_UPLOAD_STORE_FILE=withyou-release-key.keystore + MYAPP_UPLOAD_STORE_PASSWORD=${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + MYAPP_UPLOAD_KEY_ALIAS=withyou-key + MYAPP_UPLOAD_KEY_PASSWORD=${{ secrets.ANDROID_KEY_PASSWORD }} + SIGNING_PROPS + + # CI build tuning (low-memory container): force single-worker, reduce JVM heap + cat >> gradle.properties << 'CI_PROPS' org.gradle.daemon=false org.gradle.parallel=false org.gradle.configureondemand=false org.gradle.workers.max=1 org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -XX:+UseG1GC -XX:ReservedCodeCacheSize=256m -XX:+HeapDumpOnOutOfMemoryError kotlin.daemon.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -XX:+UseG1GC -XX:ReservedCodeCacheSize=256m - android.enableJetifier=false - android.useAndroidX=true - hermesEnabled=true - reactNativeArchitectures=arm64-v8a - newArchEnabled=true - edgeToEdgeEnabled=true - expo.gif.enabled=true - expo.webp.enabled=true - expo.webp.animated=false - ndkVersion=27.1.12297006 - expo.useLegacyPackaging=false - systemProp.org.gradle.internal.http.connectionTimeout=30000 - systemProp.org.gradle.internal.http.socketTimeout=30000 - PROPS_EOF - - # Append signing properties (secrets appended, not cached) - cat >> gradle.properties << SIGNING_PROPS - MYAPP_UPLOAD_STORE_FILE=withyou-release-key.keystore - MYAPP_UPLOAD_STORE_PASSWORD=${{ secrets.ANDROID_KEYSTORE_PASSWORD }} - MYAPP_UPLOAD_KEY_ALIAS=withyou-key - MYAPP_UPLOAD_KEY_PASSWORD=${{ secrets.ANDROID_KEY_PASSWORD }} - SIGNING_PROPS - + CI_PROPS + # Verify signing config in app/build.gradle echo "=== app/build.gradle signing section ===" grep -A 20 'signingConfigs' app/build.gradle || echo "signingConfigs not found" diff --git a/plugins/withHonorPush.js b/plugins/withHonorPush.js index c443884..5a38f4e 100644 --- a/plugins/withHonorPush.js +++ b/plugins/withHonorPush.js @@ -1,7 +1,4 @@ -const { - withSettingsGradle, - withDangerousMod, -} = require('@expo/config-plugins'); +const { withDangerousMod } = require('@expo/config-plugins'); const fs = require('fs'); const path = require('path'); @@ -11,13 +8,14 @@ const path = require('path'); // 1) 添加 cn.jiguang.sdk.plugin:honor 依赖 // 2) 注入 manifestPlaceholders: HONOR_APPID // 3) 添加荣耀 Maven 仓库 https://developer.hihonor.com/repo 到 settings.gradle 和根 build.gradle -// 注意: 必须加到根 build.gradle 的 allprojects.repositories,因为 honor:6.1.0 的传递依赖 -// com.hihonor.mcs:push 是运行时依赖(通过 allprojects 解析,不走 buildscript)。 +// 关键: 必须同时加到 settings.gradle 的 dependencyResolutionManagement 块(CI build 走这条路径), +// 和根 build.gradle 的 buildscript+allprojects(本地 build 兜底)。 // 4) 添加 Proguard 规则 (com.hihonor.push.** 保留 + 5 条 -keepattributes) // -// 实现说明:build.gradle 改动统一用 withDangerousMod 直接读写磁盘文件。 -// 原因:Expo SDK 56+ 的 withProjectBuildGradle / withAppBuildGradle hook 链中,第二个以后的 -// plugin 写回 modResults.contents 会被静默忽略,导致多个 vendor plugin 只能写入第一个的修改。 +// 实现说明:所有文件改动统一用 withDangerousMod 直接读写磁盘。 +// 原因:Expo SDK 56+ 的 withSettingsGradle / withProjectBuildGradle / withAppBuildGradle hook 链中, +// 第二个以后的 plugin 写回 modResults.contents 会被静默忽略。withDangerousMod 走文件系统串行执行, +// 多 vendor plugin 共存可靠。 const withHonorPush = (config, options = {}) => { const { jpushVersion = '6.1.0', @@ -26,53 +24,13 @@ const withHonorPush = (config, options = {}) => { const honorRepoUrl = 'https://developer.hihonor.com/repo'; - // 1. settings.gradle: 添加荣耀 Maven 仓库 - config = withSettingsGradle(config, (config) => { - let contents = config.modResults.contents; - - if (!contents.includes('developer.hihonor.com/repo')) { - const repoLine = ` maven { url '${honorRepoUrl}' }`; - const hasRepositoriesInPM = /pluginManagement\s*\{[\s\S]*?repositories\s*\{/.test(contents); - - if (hasRepositoriesInPM) { - config.modResults.contents = contents.replace( - /(pluginManagement\s*\{[\s\S]*?repositories\s*\{)/, - `$1\n ${repoLine}` - ); - } else if (contents.includes('pluginManagement')) { - config.modResults.contents = contents.replace( - /(pluginManagement\s*\{)/, - `$1\n repositories {\n gradlePluginPortal()\n google()\n mavenCentral()\n ${repoLine}\n }` - ); - } else { - config.modResults.contents = - `pluginManagement {\n repositories {\n ${repoLine}\n }\n}\n\n` + - config.modResults.contents; - } - } - - // Gradle 9+ layout - if ( - !config.modResults.contents.match( - /dependencyResolutionManagement[\s\S]*developer\.hihonor\.com\/repo/ - ) - ) { - const depMgmtPattern = /dependencyResolutionManagement\s*\{[\s\S]*?repositories\s*\{/; - if (depMgmtPattern.test(config.modResults.contents)) { - config.modResults.contents = config.modResults.contents.replace( - depMgmtPattern, - `$&\n maven { url '${honorRepoUrl}' }` - ); - } - } - - return config; - }); - - // 2. 根 build.gradle: 添加荣耀 Maven 仓库到 buildscript + allprojects (用 withDangerousMod 直接改文件) config = withDangerousMod(config, [ 'android', async (config) => { + const settingsGradlePath = path.join( + config.modRequest.platformProjectRoot, + 'settings.gradle' + ); const rootBuildGradlePath = path.join( config.modRequest.platformProjectRoot, 'build.gradle' @@ -88,13 +46,68 @@ const withHonorPush = (config, options = {}) => { 'proguard-rules.pro' ); - // --- 2a. 根 build.gradle: 仓库注入 --- + // --- 1. settings.gradle: pluginManagement + dependencyResolutionManagement --- + if (fs.existsSync(settingsGradlePath)) { + let settings = fs.readFileSync(settingsGradlePath, 'utf-8'); + let settingsChanged = false; + const repoLine = ` maven { url '${honorRepoUrl}' }`; + + // pluginManagement.repositories + if (!settings.match(/pluginManagement\s*\{[\s\S]*?developer\.hihonor\.com\/repo/)) { + const hasRepositoriesInPM = /pluginManagement\s*\{[\s\S]*?repositories\s*\{/.test(settings); + if (hasRepositoriesInPM) { + settings = settings.replace( + /(pluginManagement\s*\{[\s\S]*?repositories\s*\{)/, + `$1\n ${repoLine}` + ); + settingsChanged = true; + } else if (settings.includes('pluginManagement')) { + settings = settings.replace( + /(pluginManagement\s*\{)/, + `$1\n repositories {\n gradlePluginPortal()\n google()\n mavenCentral()\n ${repoLine}\n }` + ); + settingsChanged = true; + } else { + settings = + `pluginManagement {\n repositories {\n ${repoLine}\n }\n}\n\n` + settings; + settingsChanged = true; + } + } + + // dependencyResolutionManagement (Gradle 7+, takes precedence over allprojects in CI) + if (!settings.match(/dependencyResolutionManagement[\s\S]*developer\.hihonor\.com\/repo/)) { + const depMgmtPattern = /dependencyResolutionManagement\s*\{[\s\S]*?repositories\s*\{/; + if (depMgmtPattern.test(settings)) { + settings = settings.replace( + depMgmtPattern, + `$&\n maven { url '${honorRepoUrl}' }` + ); + settingsChanged = true; + } else { + // Block doesn't exist — fallback: create with Huawei + Honor + standard repos. + const block = `\ndependencyResolutionManagement {\n repositories {\n maven { url 'https://developer.huawei.com/repo/' }\n maven { url '${honorRepoUrl}' }\n google()\n mavenCentral()\n maven { url 'https://www.jitpack.io' }\n }\n}\n`; + const pluginsBlockEnd = /(\nplugins\s*\{[\s\S]*?\n\})/; + if (pluginsBlockEnd.test(settings)) { + settings = settings.replace(pluginsBlockEnd, `$1\n${block}`); + } else { + settings = settings + block; + } + settingsChanged = true; + } + } + + if (settingsChanged) { + fs.writeFileSync(settingsGradlePath, settings); + console.log('[withHonorPush] updated settings.gradle (pluginManagement + dependencyResolutionManagement)'); + } + } + + // --- 2. 根 build.gradle: buildscript + allprojects repositories --- if (fs.existsSync(rootBuildGradlePath)) { let gradle = fs.readFileSync(rootBuildGradlePath, 'utf-8'); let changed = false; const repoLine = ` maven { url '${honorRepoUrl}' }`; - // 注入到 buildscript.repositories(用于解析 buildscript classpath) if (!gradle.match(/buildscript\s*\{[\s\S]*?developer\.hihonor\.com\/repo/)) { const bsRepoPattern = /(buildscript\s*\{[\s\S]*?repositories\s*\{)/; if (bsRepoPattern.test(gradle)) { @@ -103,14 +116,12 @@ const withHonorPush = (config, options = {}) => { } } - // 注入到 allprojects.repositories(用于解析运行时依赖如 com.hihonor.mcs:push) if (!gradle.match(/allprojects\s*\{[\s\S]*?developer\.hihonor\.com\/repo/)) { const allProjectsPattern = /(allprojects\s*\{[\s\S]*?repositories\s*\{)/; if (allProjectsPattern.test(gradle)) { gradle = gradle.replace(allProjectsPattern, `$1\n${repoLine}`); changed = true; } else { - // 没有 allprojects 块时新建 const allProjectsBlock = `\nallprojects {\n repositories {${repoLine}\n }\n}\n`; gradle = gradle + allProjectsBlock; changed = true; @@ -123,12 +134,11 @@ const withHonorPush = (config, options = {}) => { } } - // --- 2b. app/build.gradle: AAR 依赖 + manifestPlaceholders --- + // --- 3. app/build.gradle: AAR 依赖 + manifestPlaceholders --- if (fs.existsSync(appBuildGradlePath)) { let gradle = fs.readFileSync(appBuildGradlePath, 'utf-8'); let changed = false; - // 注入 cn.jiguang.sdk.plugin:honor 依赖 if (!gradle.includes('cn.jiguang.sdk.plugin:honor')) { const depsPattern = /dependencies\s*\{/; if (depsPattern.test(gradle)) { @@ -140,7 +150,6 @@ const withHonorPush = (config, options = {}) => { } } - // 注入 manifestPlaceholders: HONOR_APPID const manifestPlaceholderRegex = /manifestPlaceholders\s*=\s*\[([\s\S]*?)\]/; const match = gradle.match(manifestPlaceholderRegex); @@ -174,7 +183,6 @@ const withHonorPush = (config, options = {}) => { changed = true; } } else if (gradle.includes('REACT_NATIVE_RELEASE_LEVEL')) { - // 没有 manifestPlaceholders block 时新建 const entriesStr = Object.entries(requiredPlaceholders) .map(([k, v]) => `${k}: "${v}"`) .join(',\n '); @@ -196,7 +204,7 @@ const withHonorPush = (config, options = {}) => { } } - // --- 2c. proguard-rules.pro --- + // --- 4. proguard-rules.pro --- if (fs.existsSync(proguardFilePath)) { let proguard = fs.readFileSync(proguardFilePath, 'utf-8'); if (!proguard.includes('-keep class com.hihonor.push.**')) { diff --git a/plugins/withHuaweiPush.js b/plugins/withHuaweiPush.js index 367389e..a213350 100644 --- a/plugins/withHuaweiPush.js +++ b/plugins/withHuaweiPush.js @@ -42,14 +42,33 @@ const withHuaweiPush = (config, options = {}) => { } } - // Ensure Huawei Maven repo in dependencyResolutionManagement (Gradle 9+) + // Ensure Huawei Maven repo in dependencyResolutionManagement (Gradle 7+). + // CI build paths require this block (Gradle dependencyResolutionManagement + // takes precedence over allprojects.repositories when configured). + // Create the block if it doesn't exist, otherwise inject Huawei into it. if (!config.modResults.contents.match(/dependencyResolutionManagement[\s\S]*developer\.huawei\.com\/repo/)) { const depMgmtPattern = /dependencyResolutionManagement\s*\{[\s\S]*?repositories\s*\{/; if (depMgmtPattern.test(config.modResults.contents)) { + // Block exists — append Huawei to its repositories config.modResults.contents = config.modResults.contents.replace( depMgmtPattern, `$&\n maven { url 'https://developer.huawei.com/repo/' }` ); + } else { + // Block doesn't exist — create it after the `plugins { ... }` block + // (or after pluginManagement if no plugins block) so it sits where + // Gradle expects it. + const block = `\ndependencyResolutionManagement {\n repositories {\n maven { url 'https://developer.huawei.com/repo/' }\n google()\n mavenCentral()\n maven { url 'https://www.jitpack.io' }\n }\n}\n`; + const pluginsBlockEnd = /(\nplugins\s*\{[\s\S]*?\n\})/; + if (pluginsBlockEnd.test(config.modResults.contents)) { + config.modResults.contents = config.modResults.contents.replace( + pluginsBlockEnd, + `$1\n${block}` + ); + } else { + // Fallback: append to end + config.modResults.contents = config.modResults.contents + block; + } } }