refactor(push): unify file modification approach and improve dependency resolution
Simplify Honor push plugin to use withDangerousMod consistently for all file modifications, replacing the withSettingsGradle approach that silently drops writes in Expo SDK 56+ multi-vendor plugin scenarios. Enhance Huawei push plugin to create missing dependencyResolutionManagement block instead of only appending to existing ones, ensuring CI build paths work reliably when Gradle's dependencyResolutionManagement takes precedence. Remove inline settings.gradle/build.gradle generation from CI workflow, relying on expo prebuild for Gradle file generation.
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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.**')) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user