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.
234 lines
9.4 KiB
JavaScript
234 lines
9.4 KiB
JavaScript
const { withDangerousMod } = require('@expo/config-plugins');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
// 荣耀厂商推送通道的 Expo config plugin.
|
||
//
|
||
// 客户端职责(依据极光官方文档 https://docs.jiguang.cn/jpush/client/Android/android_3rd_guide):
|
||
// 1) 添加 cn.jiguang.sdk.plugin:honor 依赖
|
||
// 2) 注入 manifestPlaceholders: HONOR_APPID
|
||
// 3) 添加荣耀 Maven 仓库 https://developer.hihonor.com/repo 到 settings.gradle 和根 build.gradle
|
||
// 关键: 必须同时加到 settings.gradle 的 dependencyResolutionManagement 块(CI build 走这条路径),
|
||
// 和根 build.gradle 的 buildscript+allprojects(本地 build 兜底)。
|
||
// 4) 添加 Proguard 规则 (com.hihonor.push.** 保留 + 5 条 -keepattributes)
|
||
//
|
||
// 实现说明:所有文件改动统一用 withDangerousMod 直接读写磁盘。
|
||
// 原因:Expo SDK 56+ 的 withSettingsGradle / withProjectBuildGradle / withAppBuildGradle hook 链中,
|
||
// 第二个以后的 plugin 写回 modResults.contents 会被静默忽略。withDangerousMod 走文件系统串行执行,
|
||
// 多 vendor plugin 共存可靠。
|
||
const withHonorPush = (config, options = {}) => {
|
||
const {
|
||
jpushVersion = '6.1.0',
|
||
appId = '104562917',
|
||
} = options;
|
||
|
||
const honorRepoUrl = 'https://developer.hihonor.com/repo';
|
||
|
||
config = withDangerousMod(config, [
|
||
'android',
|
||
async (config) => {
|
||
const settingsGradlePath = path.join(
|
||
config.modRequest.platformProjectRoot,
|
||
'settings.gradle'
|
||
);
|
||
const rootBuildGradlePath = path.join(
|
||
config.modRequest.platformProjectRoot,
|
||
'build.gradle'
|
||
);
|
||
const appBuildGradlePath = path.join(
|
||
config.modRequest.platformProjectRoot,
|
||
'app',
|
||
'build.gradle'
|
||
);
|
||
const proguardFilePath = path.join(
|
||
config.modRequest.platformProjectRoot,
|
||
'app',
|
||
'proguard-rules.pro'
|
||
);
|
||
|
||
// --- 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}' }`;
|
||
|
||
if (!gradle.match(/buildscript\s*\{[\s\S]*?developer\.hihonor\.com\/repo/)) {
|
||
const bsRepoPattern = /(buildscript\s*\{[\s\S]*?repositories\s*\{)/;
|
||
if (bsRepoPattern.test(gradle)) {
|
||
gradle = gradle.replace(bsRepoPattern, `$1\n${repoLine}`);
|
||
changed = true;
|
||
}
|
||
}
|
||
|
||
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 {
|
||
const allProjectsBlock = `\nallprojects {\n repositories {${repoLine}\n }\n}\n`;
|
||
gradle = gradle + allProjectsBlock;
|
||
changed = true;
|
||
}
|
||
}
|
||
|
||
if (changed) {
|
||
fs.writeFileSync(rootBuildGradlePath, gradle);
|
||
console.log('[withHonorPush] updated root build.gradle (buildscript + allprojects repos)');
|
||
}
|
||
}
|
||
|
||
// --- 3. app/build.gradle: AAR 依赖 + manifestPlaceholders ---
|
||
if (fs.existsSync(appBuildGradlePath)) {
|
||
let gradle = fs.readFileSync(appBuildGradlePath, 'utf-8');
|
||
let changed = false;
|
||
|
||
if (!gradle.includes('cn.jiguang.sdk.plugin:honor')) {
|
||
const depsPattern = /dependencies\s*\{/;
|
||
if (depsPattern.test(gradle)) {
|
||
gradle = gradle.replace(
|
||
depsPattern,
|
||
`dependencies {\n implementation 'cn.jiguang.sdk.plugin:honor:${jpushVersion}'`
|
||
);
|
||
changed = true;
|
||
}
|
||
}
|
||
|
||
const manifestPlaceholderRegex = /manifestPlaceholders\s*=\s*\[([\s\S]*?)\]/;
|
||
const match = gradle.match(manifestPlaceholderRegex);
|
||
|
||
const requiredPlaceholders = {
|
||
HONOR_APPID: appId,
|
||
};
|
||
|
||
if (match) {
|
||
const existingBlock = match[1];
|
||
const existingEntries = {};
|
||
const entryRegex = /(\w+)\s*:\s*"([^"]*)"/g;
|
||
let entryMatch;
|
||
while ((entryMatch = entryRegex.exec(existingBlock)) !== null) {
|
||
existingEntries[entryMatch[1]] = entryMatch[2];
|
||
}
|
||
|
||
let placeholdersChanged = false;
|
||
for (const [key, value] of Object.entries(requiredPlaceholders)) {
|
||
if (existingEntries[key] !== value) {
|
||
existingEntries[key] = value;
|
||
placeholdersChanged = true;
|
||
}
|
||
}
|
||
|
||
if (placeholdersChanged) {
|
||
const entriesStr = Object.entries(existingEntries)
|
||
.map(([k, v]) => `${k}: "${v}"`)
|
||
.join(',\n ');
|
||
const newBlock = `\n manifestPlaceholders = [\n ${entriesStr}\n ]`;
|
||
gradle = gradle.replace(manifestPlaceholderRegex, newBlock.trim());
|
||
changed = true;
|
||
}
|
||
} else if (gradle.includes('REACT_NATIVE_RELEASE_LEVEL')) {
|
||
const entriesStr = Object.entries(requiredPlaceholders)
|
||
.map(([k, v]) => `${k}: "${v}"`)
|
||
.join(',\n ');
|
||
const block = `\n manifestPlaceholders = [\n ${entriesStr}\n ]\n`;
|
||
const lines = gradle.split('\n');
|
||
for (let i = 0; i < lines.length; i++) {
|
||
if (lines[i].includes('REACT_NATIVE_RELEASE_LEVEL')) {
|
||
lines.splice(i + 1, 0, block);
|
||
break;
|
||
}
|
||
}
|
||
gradle = lines.join('\n');
|
||
changed = true;
|
||
}
|
||
|
||
if (changed) {
|
||
fs.writeFileSync(appBuildGradlePath, gradle);
|
||
console.log('[withHonorPush] updated app/build.gradle (AAR + manifestPlaceholders)');
|
||
}
|
||
}
|
||
|
||
// --- 4. proguard-rules.pro ---
|
||
if (fs.existsSync(proguardFilePath)) {
|
||
let proguard = fs.readFileSync(proguardFilePath, 'utf-8');
|
||
if (!proguard.includes('-keep class com.hihonor.push.**')) {
|
||
const honorRules = `
|
||
# JPush Honor vendor channel — keep rules (https://docs.jiguang.cn/jpush/client/Android/android_3rd_guide)
|
||
-ignorewarnings
|
||
-keepattributes *Annotation*
|
||
-keepattributes Exceptions
|
||
-keepattributes InnerClasses
|
||
-keepattributes Signature
|
||
-keepattributes SourceFile,LineNumberTable
|
||
-keep class com.hihonor.push.**{*;}
|
||
`;
|
||
fs.writeFileSync(proguardFilePath, proguard + honorRules);
|
||
console.log('[withHonorPush] appended Honor Proguard rules to proguard-rules.pro');
|
||
}
|
||
}
|
||
|
||
return config;
|
||
},
|
||
]);
|
||
|
||
return config;
|
||
};
|
||
|
||
module.exports = withHonorPush;
|