cadb
Version:
安卓/鸿蒙系统截图/录屏工具
298 lines (284 loc) • 8.55 kB
JavaScript
/**
* Created by Niki on 2024/10/12 15:24.
* Email: m13296644326@163.com
*/
const program = require('commander');
const {execFileSync} = require('child_process');
const {
shellConfigs,
logRed,
logGreen,
ctripHarmony,
ztripHarmony,
ctripAndroid,
ztripAndroid,
} = require('../common/constants');
const {
isHarmonyDeviceConnected,
isAndroidDeviceConnected,
chooseAndroidRealDeviceId,
hdcPath,
adbPath,
chooseHarmonyRealDeviceId,
loadOrCollectProjectPathBatch,
} = require('../common/utils');
const path = require('path');
const os = require('os');
const fs = require('fs');
const _ = require('lodash');
const moment = require('moment');
// 安装包路径
let appointedFilePath = '';
let extraInstallCmd = '';
// TODO: 实现从项目安装debug安装包; 并指定安装路径
program
// 使用中括号表示可选参数, 尖括号表示必选参数
.arguments('[filePath]')
.option('-ca --ctripA', 'install ctrip android apk')
.option('-za --ztripA', 'install ztrip android apk')
.option('-ch --ctripH', 'install ctrip harmony hap')
.option('-zh --ztripH', 'install ztrip harmony hap')
.option('-c, --noCache', 'drop local project path cache')
.action(async (filePath, optionArgs) => {
await parseParams(filePath, optionArgs);
run();
})
.parse(process.argv);
async function parseParams(filePath, optionArgs) {
if (!filePath && _.isEmpty(optionArgs)) {
return;
}
if (filePath) {
appointedFilePath = filePath;
} else {
let keyName = '';
const {ctripA, ztripA, ctripH, ztripH, noCache} = optionArgs;
if (ctripA) {
keyName = ctripAndroid;
} else if (ztripA) {
keyName = ztripAndroid;
} else if (ctripH) {
keyName = ctripHarmony;
} else if (ztripH) {
keyName = ztripHarmony;
}
if (!keyName) {
return;
}
const pathList = await loadOrCollectProjectPathBatch([keyName], noCache);
const projectPath = pathList[0];
const apkPathMap = require('../config/apk_path_map_config.json');
const {
apkPath: relativeApkPath,
apkPathBak: relativeApkPathBak,
extraCmd = '',
} = apkPathMap[keyName];
appointedFilePath = path.join(projectPath, relativeApkPath);
if (!fs.existsSync(appointedFilePath)) {
if (!relativeApkPathBak) {
logRed(`安装包路径不存在: ${appointedFilePath}`);
process.exit();
return;
}
// 使用备用路径
appointedFilePath = path.join(projectPath, relativeApkPathBak);
}
console.log('安装包路径: ', appointedFilePath);
extraInstallCmd = extraCmd;
}
}
async function run() {
const ok = checkPathIfNeed();
if (!ok) {
return;
}
const consumed = doAndroidInstall();
if (!consumed) {
console.log('\r\n尝试连接鸿蒙设备');
await doHarmonyInstall();
}
}
async function doHarmonyInstall() {
let dList = isHarmonyDeviceConnected();
if (!dList) {
return false;
}
console.log('\r\n开始安装hap至鸿蒙设备\r\n');
const newestFileInfo = getNewestBundleFileInfo('hap');
// 仅自动模式拦截
if (!newestFileInfo && !appointedFilePath) {
return false;
}
printBundleInfo(newestFileInfo);
const realDeviceId = await chooseHarmonyRealDeviceId(dList);
executeHarmonyInstall(newestFileInfo, realDeviceId);
return true;
}
function doAndroidInstall() {
let dList = isAndroidDeviceConnected();
if (!dList) {
return false;
}
console.log('\r\n开始安装apk至安卓设备\r\n');
const newestFileInfo = getNewestBundleFileInfo('apk');
// 仅自动模式拦截
if (!newestFileInfo && !appointedFilePath) {
return false;
}
printBundleInfo(newestFileInfo);
executeAndroidInstall(newestFileInfo, chooseAndroidRealDeviceId(dList));
return true;
}
function checkPathIfNeed() {
if (!appointedFilePath) {
return true;
}
if (!fs.existsSync(appointedFilePath)) {
logRed(`文件不存在: ${appointedFilePath}`);
return false;
}
if (
!appointedFilePath.endsWith('apk') &&
!appointedFilePath.endsWith('hap')
) {
logRed(`文件后缀不正确: ${appointedFilePath}`);
return false;
}
return true;
}
function executeHarmonyInstall(fileInfo, realDeviceCmd = '') {
console.log('\r\neverything is ok, performing install task\r\n');
const cmdInstall = ['install', fileInfo?.filePath || appointedFilePath];
if (realDeviceCmd) {
cmdInstall.unshift(...realDeviceCmd.split(' '));
}
let resultHM = execFileSync(hdcPath, cmdInstall, shellConfigs);
console.log('execute: ', `hdc ${cmdInstall.join(' ')}\r\n`);
console.log(resultHM);
if (resultHM.includes('successfully.')) {
logGreen('安装成功\r\n');
} else {
logRed('安装失败\r\n');
}
}
function executeAndroidInstall(fileInfo, realDeviceCmd = '') {
console.log('\r\neverything is ok, performing install task\r\n');
const cmdInstall = [
'install',
extraInstallCmd,
fileInfo?.filePath || appointedFilePath,
];
if (realDeviceCmd) {
cmdInstall.unshift(...realDeviceCmd.split(' '));
}
let result = execFileSync(adbPath, cmdInstall, shellConfigs);
console.log('execute: ', `adb ${cmdInstall.join(' ')}\r\n`);
console.log(result);
if (result.includes('Success')) {
logGreen('安装成功\r\n');
} else {
logRed('安装失败\r\n');
}
}
// ZhiXing_V10.9.0_10-14_16-48_hgb_Release_PROD_19553812.hap
// ZhiXing_V10.9.0_10-15_11-30_10037_PROD_19558710.apk
// 例子2: ZhiXing_V10.9.0_hgb_Release_PROD_19568481.hap
/**
* 获取一个小时内的最新的安装文件
*/
function getNewestBundleFileInfo(fileType) {
let newestFilePath;
let newestFileName;
if (!appointedFilePath) {
const bundleFileList = fs
.readdirSync(getDefaultDownloadPath())
.filter(bFileName => {
return (
bFileName.endsWith(`.${fileType}`) &&
/.+_V\d+\.\d+\.\d+_.+/.test(bFileName)
);
})
.sort((a, b) => {
return (
fs.statSync(path.join(getDefaultDownloadPath(), b)).mtimeMs -
fs.statSync(path.join(getDefaultDownloadPath(), a)).mtimeMs
);
});
if (_.isEmpty(bundleFileList)) {
logRed(`${getDefaultDownloadPath()}目录下未找到${fileType}文件\r\n`);
return null;
}
newestFilePath = path.join(getDefaultDownloadPath(), bundleFileList[0]);
newestFileName = bundleFileList[0];
} else {
newestFilePath = appointedFilePath;
newestFileName = path.basename(appointedFilePath);
}
console.log(`file: ${newestFileName}`);
// 一个小时之内的文件; 暂时不做校验
/*if (Date.now() - fs.statSync(newestFilePath).mtimeMs > 1000 * 60 * 60) {
logRed(
`${getDefaultDownloadPath()}目录下未找到1小时内的${fileType}文件\r\n`,
);
return null;
}*/
// 例子1: ZhiXing_App_V10.8.8_09-29_14-11_Release_PROD_19515986.hap
// 例子2: ZhiXing_V10.9.0_hgb_Release_PROD_19568481.hap
if (/(.+)_(V\d+\.\d+\.\d+)_(.+)/.test(newestFileName)) {
const rest = RegExp.$3;
const appName = RegExp.$1;
const versionName = RegExp.$2;
let otherInfo = null;
if (/(\d{2}-\d{2}_\d{2}-\d{2})_.+/.test(rest)) {
const [, , buildMode = '', env = '', buildId = ''] = rest.split('_');
otherInfo = {
env,
buildMode,
buildId: buildId.replace(`.${fileType}`, ''),
};
} else if (/([a-zA-Z]+)_.+/.test(rest)) {
const [, buildMode = '', env = '', buildId = ''] = rest.split('_');
otherInfo = {
env,
buildMode,
buildId: buildId.replace(`.${fileType}`, ''),
};
}
if (!otherInfo) {
return null;
}
return {
appName,
versionName,
filePath: newestFilePath,
createTime: fs.statSync(newestFilePath).mtimeMs,
...otherInfo,
};
}
return null;
}
function printBundleInfo(fileInfo) {
if (_.isEmpty(fileInfo)) {
return;
}
const tableData = {};
if (fileInfo.appName.toLowerCase().includes('zhixing')) {
tableData['应用名称'] = '智行';
}
if (fileInfo.appName.toLowerCase().includes('ctrip')) {
tableData['应用名称'] = '携程';
}
tableData['版本号'] = fileInfo.versionName;
tableData['环境'] = fileInfo.env;
tableData['构建模式'] = fileInfo.buildMode;
tableData['下载时间'] = `${moment(
fileInfo.createTime,
null,
'zh-cn',
).fromNow()}`;
// tableData['构建ID'] = fileInfo.buildId;
console.table(tableData);
}
function getDefaultDownloadPath() {
return path.resolve(os.homedir(), 'Downloads');
}