ci(android): add Android NDK caching and installation to build workflow

This commit is contained in:
lafay
2026-05-14 01:25:54 +08:00
parent 1d9c312c6c
commit 4308077232
5 changed files with 162 additions and 11 deletions

View File

@@ -209,6 +209,23 @@ jobs:
sed -i '/always-auth/d' ~/.npmrc
fi
- name: Cache Android NDK
uses: actions/cache@v4
id: cache-ndk
with:
path: /opt/android/ndk/27.0.12077973
key: ndk-27.0.12077973-v1
- name: Install Android NDK
if: steps.cache-ndk.outputs.cache-hit != 'true'
run: |
echo "Existing NDK versions:"
ls /opt/android/ndk/ 2>/dev/null || echo "No NDK dir"
echo "Installing NDK 27.0.12077973..."
yes | sdkmanager --install "ndk;27.0.12077973"
echo "NDK after install:"
ls /opt/android/ndk/
- name: Cache Gradle
uses: actions/cache@v4
with:
@@ -360,6 +377,7 @@ jobs:
expo.gif.enabled=true
expo.webp.enabled=true
expo.webp.animated=false
ndkVersion=27.1.12297006
expo.useLegacyPackaging=false
PROPS_EOF

View File

@@ -151,7 +151,8 @@
"appKey": "2cbe4da12e2c24aa3dca4610",
"channel": "developer-default"
}
]
],
"./plugins/withHuaweiPush"
],
"extra": {
"eas": {

View File

@@ -0,0 +1 @@
{"agcgw":{"url":"connect-drcn.dbankcloud.cn","backurl":"connect-drcn.hispace.hicloud.com","websocketurl":"connect-ws-drcn.hispace.dbankcloud.cn","websocketbackurl":"connect-ws-drcn.hispace.dbankcloud.com"},"agcgw_all":{"SG":"connect-dra.dbankcloud.cn","SG_back":"connect-dra.hispace.hicloud.com","CN":"connect-drcn.dbankcloud.cn","CN_back":"connect-drcn.hispace.hicloud.com","RU":"connect-drru.hispace.dbankcloud.ru","RU_back":"connect-drru.hispace.dbankcloud.cn","DE":"connect-dre.dbankcloud.cn","DE_back":"connect-dre.hispace.hicloud.com"},"websocketgw_all":{"SG":"connect-ws-dra.hispace.dbankcloud.cn","SG_back":"connect-ws-dra.hispace.dbankcloud.com","CN":"connect-ws-drcn.hispace.dbankcloud.cn","CN_back":"connect-ws-drcn.hispace.dbankcloud.com","RU":"connect-ws-drru.hispace.dbankcloud.ru","RU_back":"connect-ws-drru.hispace.dbankcloud.cn","DE":"connect-ws-dre.hispace.dbankcloud.cn","DE_back":"connect-ws-dre.hispace.dbankcloud.com"},"client":{"cp_id":"30086000618737115","product_id":"101653523864112478","client_id":"1949443370896080512","client_secret":"CA50EF8C69075FAEFECF2D9F0F3ED101DC3264E0BFE1CC5E5B0272E7FF8215A1","project_id":"101653523864112478","app_id":"117704941","api_key":"DgEDAIkxmMyXBT6vYYW8ZWWXBMJweDbdmmKLYo5MvgH2lV8jPSIZt5WVpjd0/nZ0dk9gAwHf+dWIySILr3QQd7W80H8NNvuCUAQPig==","package_name":"cn.qczlit.withyou"},"oauth_client":{"client_id":"117704941","client_type":1},"app_info":{"app_id":"117704941","package_name":"cn.qczlit.withyou"},"service":{"analytics":{"collector_url":"datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn","collector_url_cn":"datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn","collector_url_de":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn","collector_url_ru":"datacollector-drru.dt.dbankcloud.ru,datacollector-drru.dt.hicloud.com","collector_url_sg":"datacollector-dra.dt.hicloud.com,datacollector-dra.dt.dbankcloud.cn","resource_id":"p1","channel_id":""},"ml":{"mlservice_url":"ml-api-drcn.ai.dbankcloud.com,ml-api-drcn.ai.dbankcloud.cn"},"cloudstorage":{"storage_url":"https://agc-storage-drcn.platform.dbankcloud.cn","storage_url_ru":"https://agc-storage-drru.cloud.huawei.ru","storage_url_sg":"https://ops-dra.agcstorage.link","storage_url_de":"https://ops-dre.agcstorage.link","storage_url_cn":"https://agc-storage-drcn.platform.dbankcloud.cn","storage_url_ru_back":"https://agc-storage-drru.cloud.huawei.ru","storage_url_sg_back":"https://agc-storage-dra.cloud.huawei.asia","storage_url_de_back":"https://agc-storage-dre.cloud.huawei.eu","storage_url_cn_back":"https://agc-storage-drcn.cloud.huawei.com.cn"},"search":{"url":"https://search-drcn.cloud.huawei.com"},"edukit":{"edu_url":"edukit.cloud.huawei.com.cn","dh_url":"edukit.cloud.huawei.com.cn"}},"region":"CN","configuration_version":"3.0","appInfos":[{"package_name":"cn.qczlit.withyou","client":{"app_id":"117704941"},"oauth_client":{"client_id":"117704941","client_type":1},"app_info":{"app_id":"117704941","package_name":"cn.qczlit.withyou"}}]}

110
plugins/withHuaweiPush.js Normal file
View File

@@ -0,0 +1,110 @@
const {
withAppBuildGradle,
withProjectBuildGradle,
withDangerousMod,
withAndroidManifest,
} = require('@expo/config-plugins');
const fs = require('fs');
const path = require('path');
const withHuaweiPush = (config, options = {}) => {
const { jpushVersion = '5.8.0' } = options;
config = withProjectBuildGradle(config, (config) => {
const contents = config.modResults.contents;
if (!contents.includes('developer.huawei.com/repo')) {
config.modResults.contents = contents.replace(
/(repositories\s*\{)/g,
(match) => {
return match + `\n maven { url 'https://developer.huawei.com/repo/' }`;
}
);
}
return config;
});
config = withProjectBuildGradle(config, (config) => {
const contents = config.modResults.contents;
if (!contents.includes('com.huawei.agconnect:agcp')) {
if (contents.includes('buildscript')) {
const depsPattern = /(buildscript\s*\{[\s\S]*?dependencies\s*\{)/;
if (depsPattern.test(contents)) {
config.modResults.contents = contents.replace(
depsPattern,
`$1\n classpath 'com.huawei.agconnect:agcp:1.9.1.301'`
);
}
}
}
return config;
});
config = withAppBuildGradle(config, (config) => {
const contents = config.modResults.contents;
if (!contents.includes('com.huawei.agconnect')) {
const applyPluginPattern = /apply plugin:\s*['"]com\.google\.gms\.google-services['"]/;
if (applyPluginPattern.test(contents)) {
config.modResults.contents = contents.replace(
applyPluginPattern,
`apply plugin: 'com.google.gms.google-services'\napply plugin: 'com.huawei.agconnect'`
);
} else {
const lastApplyPlugin = /apply plugin:[^\n]*(?:\n)/g;
const matches = contents.match(lastApplyPlugin);
if (matches && matches.length > 0) {
const lastMatch = matches[matches.length - 1];
const lastIndex = contents.lastIndexOf(lastMatch);
config.modResults.contents =
contents.slice(0, lastIndex + lastMatch.length) +
"apply plugin: 'com.huawei.agconnect'\n" +
contents.slice(lastIndex + lastMatch.length);
} else {
config.modResults.contents = contents + "\napply plugin: 'com.huawei.agconnect'\n";
}
}
}
if (!contents.includes('cn.jiguang.sdk.plugin:huawei')) {
const depsPattern = /dependencies\s*\{/;
if (depsPattern.test(config.modResults.contents)) {
config.modResults.contents = config.modResults.contents.replace(
depsPattern,
`dependencies {\n implementation 'cn.jiguang.sdk.plugin:huawei:${jpushVersion}'`
);
}
}
return config;
});
config = withDangerousMod(config, [
'android',
async (config) => {
const appDir = path.join(config.modRequest.platformProjectRoot, 'app');
const srcFile = path.join(config.modRequest.projectRoot, 'plugins', 'agconnect-services.json');
const destFile = path.join(appDir, 'agconnect-services.json');
if (fs.existsSync(srcFile)) {
fs.copyFileSync(srcFile, destFile);
console.log('[withHuaweiPush] copied agconnect-services.json to android/app/');
} else {
console.warn('[withHuaweiPush] agconnect-services.json not found at project root');
}
return config;
},
]);
config = withAndroidManifest(config, (config) => {
return config;
});
return config;
};
module.exports = withHuaweiPush;

View File

@@ -17,19 +17,40 @@ const withJPush = (config, options = {}) => {
// 1. Android: Inject manifestPlaceholders into app/build.gradle
config = withAppBuildGradle(config, (config) => {
const block = '\n manifestPlaceholders = [\n' +
' JPUSH_APPKEY: "' + appKey + '",\n' +
' JPUSH_CHANNEL: "' + channel + '"\n' +
' ]';
const contents = config.modResults.contents;
if (contents.includes('manifestPlaceholders')) {
config.modResults.contents = contents.replace(
/manifestPlaceholders\s*=\s*\[[\s\S]*?\]/,
block.trim()
);
const requiredPlaceholders = {
JPUSH_APPKEY: appKey,
JPUSH_CHANNEL: channel,
};
const manifestPlaceholderRegex = /manifestPlaceholders\s*=\s*\[([\s\S]*?)\]/;
const match = contents.match(manifestPlaceholderRegex);
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];
}
for (const [key, value] of Object.entries(requiredPlaceholders)) {
existingEntries[key] = value;
}
const entriesStr = Object.entries(existingEntries)
.map(([k, v]) => `${k}: "${v}"`)
.join(',\n ');
const newBlock = `\n manifestPlaceholders = [\n ${entriesStr}\n ]`;
config.modResults.contents = contents.replace(manifestPlaceholderRegex, newBlock.trim());
} else if (contents.includes('REACT_NATIVE_RELEASE_LEVEL')) {
const entriesStr = Object.entries(requiredPlaceholders)
.map(([k, v]) => `${k}: "${v}"`)
.join(',\n ');
const block = '\n manifestPlaceholders = [\n ' + entriesStr + '\n ]';
const lines = contents.split('\n');
for (let i = 0; i < lines.length; i++) {
if (lines[i].includes('REACT_NATIVE_RELEASE_LEVEL')) {