ci(android): add Android NDK caching and installation to build workflow
This commit is contained in:
@@ -209,6 +209,23 @@ jobs:
|
|||||||
sed -i '/always-auth/d' ~/.npmrc
|
sed -i '/always-auth/d' ~/.npmrc
|
||||||
fi
|
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
|
- name: Cache Gradle
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
@@ -360,6 +377,7 @@ jobs:
|
|||||||
expo.gif.enabled=true
|
expo.gif.enabled=true
|
||||||
expo.webp.enabled=true
|
expo.webp.enabled=true
|
||||||
expo.webp.animated=false
|
expo.webp.animated=false
|
||||||
|
ndkVersion=27.1.12297006
|
||||||
expo.useLegacyPackaging=false
|
expo.useLegacyPackaging=false
|
||||||
PROPS_EOF
|
PROPS_EOF
|
||||||
|
|
||||||
|
|||||||
3
app.json
3
app.json
@@ -151,7 +151,8 @@
|
|||||||
"appKey": "2cbe4da12e2c24aa3dca4610",
|
"appKey": "2cbe4da12e2c24aa3dca4610",
|
||||||
"channel": "developer-default"
|
"channel": "developer-default"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"./plugins/withHuaweiPush"
|
||||||
],
|
],
|
||||||
"extra": {
|
"extra": {
|
||||||
"eas": {
|
"eas": {
|
||||||
|
|||||||
1
plugins/agconnect-services.json
Normal file
1
plugins/agconnect-services.json
Normal 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
110
plugins/withHuaweiPush.js
Normal 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;
|
||||||
@@ -17,19 +17,40 @@ const withJPush = (config, options = {}) => {
|
|||||||
|
|
||||||
// 1. Android: Inject manifestPlaceholders into app/build.gradle
|
// 1. Android: Inject manifestPlaceholders into app/build.gradle
|
||||||
config = withAppBuildGradle(config, (config) => {
|
config = withAppBuildGradle(config, (config) => {
|
||||||
const block = '\n manifestPlaceholders = [\n' +
|
|
||||||
' JPUSH_APPKEY: "' + appKey + '",\n' +
|
|
||||||
' JPUSH_CHANNEL: "' + channel + '"\n' +
|
|
||||||
' ]';
|
|
||||||
|
|
||||||
const contents = config.modResults.contents;
|
const contents = config.modResults.contents;
|
||||||
|
|
||||||
if (contents.includes('manifestPlaceholders')) {
|
const requiredPlaceholders = {
|
||||||
config.modResults.contents = contents.replace(
|
JPUSH_APPKEY: appKey,
|
||||||
/manifestPlaceholders\s*=\s*\[[\s\S]*?\]/,
|
JPUSH_CHANNEL: channel,
|
||||||
block.trim()
|
};
|
||||||
);
|
|
||||||
|
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')) {
|
} 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');
|
const lines = contents.split('\n');
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
if (lines[i].includes('REACT_NATIVE_RELEASE_LEVEL')) {
|
if (lines[i].includes('REACT_NATIVE_RELEASE_LEVEL')) {
|
||||||
|
|||||||
Reference in New Issue
Block a user