cadb
Version:
安卓/鸿蒙系统截图/录屏工具
365 lines (343 loc) • 11 kB
JavaScript
/**
* Created by Niki on 2025/2/18 15:51.
* Email: m13296644326@163.com
*/
const program = require('commander');
const fs = require('fs');
const _ = require('lodash');
const {
ctripHarmony,
logRed,
logGreen,
ztripHarmony,
shellConfigs,
} = require('../common/constants');
const {
collectUserInput,
processFile,
loadOrCollectProjectPathBatch,
listenAnyKeyInput,
} = require('../common/utils');
const path = require('path');
const JSON5 = require('json5');
const {table: TableConsole} = require('table');
const {buildHar} = require('../common/utils/harBuildUtil');
const execSync = require('child_process').execSync;
let ctripHarmonyPath = '';
let ztripHarmonyPath = '';
const ctripBuildProfileFile = 'build-profile.json5';
// 比如CTFoundation/build/default/outputs/default/CTFoundation.har
const harOutputDir = 'build/default/outputs/default';
const ztripTmpHarDir = 'libs/._tmp_copy';
program
.option('-c --noCache', 'drop local project path cache')
.option('-n, --name <name>', 'module name of the har')
.action(args => {
run(args);
})
.parse(process.argv);
async function run(args) {
await loadLocalProjectPath(args);
const targetHarItem = await getTargetHarItem(args);
if (!targetHarItem) {
quit();
return;
}
await beforeBuildTask(targetHarItem);
const harPath = await doBuildHar(targetHarItem);
if (!harPath) {
quit();
return;
}
revertBuildOption();
const newHarPath = moveHar2Ztrip(harPath);
const modifySuccess = await modifyPackageJson(newHarPath, targetHarItem);
if (!modifySuccess) {
quit();
return;
}
quit();
}
function quit() {
setTimeout(() => {
process.exit();
}, 100);
}
async function loadLocalProjectPath(args) {
const {noCache} = args;
const pathList = await loadOrCollectProjectPathBatch(
[ctripHarmony, ztripHarmony],
noCache,
);
ctripHarmonyPath = pathList[0];
ztripHarmonyPath = pathList[1];
}
/**
* 返回的对象的结构:
* name: 携程项目build-profile.json5中的模块名, 比如CTFoundation
* srcPath: 携程项目build-profile.json5中的模块相对路径, 比如./common/CTFoundation
* moduleName: 解析后的项目名, 比如 ctfoundation
* libraryName: 依赖名, 比如 @ctcommon/ctfoundation
* versionName: 依赖版本名, 比如 8.78.4-beta.20250214105531
* versionCode: 依赖纯版本号, 比如 8.78.4, 可能为空
*
* a.如果指定了module, 则校验后去编译指定module的har
* b.如果未指定module, 则展示选择栏
* @param args
* @returns {Promise<null>}
*/
async function getTargetHarItem(args) {
const {name} = args;
const buildProfile = readJson5(
path.join(ctripHarmonyPath, ctripBuildProfileFile),
);
const {modules} = buildProfile;
let targetHarItem = null;
const ztripLibraryList = loadZtripLibraryList();
const moduleWithZtrip = modules
.filter(item => {
return ztripLibraryList.some(
t => t.moduleName.toLowerCase() === item.name.toLowerCase(),
);
})
.map(item => {
const find = _.find(ztripLibraryList, t => {
return t.moduleName.toLowerCase() === item.name.toLowerCase();
});
if (!find) {
return item;
}
return {
...item,
...find,
};
});
if (name) {
const find = _.find(
moduleWithZtrip,
t => t.name.toLowerCase() === name.toLowerCase(),
);
if (find) {
targetHarItem = find;
} else {
logRed('未找到指定module, 请传入正确的module name');
process.exit();
}
} else {
printModuleList(moduleWithZtrip);
console.log('\r\n请输入要编译的module的序号: ');
const inputContent = await collectUserInput();
if (!_.isNumber(Number(inputContent))) {
logRed('请输入数字');
process.exit();
}
const inputIndex = parseInt(inputContent, 10);
if (inputIndex < 0 || inputIndex >= moduleWithZtrip.length) {
logRed('Index out of bounds exception');
process.exit();
}
targetHarItem = moduleWithZtrip[inputIndex];
}
return targetHarItem;
}
/**
* 1.校验ctrip项目的版本号, 建议等于当前依赖的版本
* 2.修改build-profile.json5, 干掉buildOption配置, 以关闭har字节码编译模式
*/
async function beforeBuildTask(targetHarItem) {
checkCtripVersionAndBranch(targetHarItem);
removeBuildOption();
}
/* 校验ctrip项目的版本号和分支 */
async function checkCtripVersionAndBranch(targetHarItem) {
const {versionCode: lbVersionCode, name: lbName} = targetHarItem;
if (lbVersionCode) {
const projectVersionCode = readCtripProjectVersion();
console.log(`ctrip项目的版本号: ${projectVersionCode}`);
if (projectVersionCode !== lbVersionCode) {
const ctripBranchName = execSync('git rev-parse --abbrev-ref HEAD', {
...shellConfigs,
cwd: ctripHarmonyPath,
}).trim();
logRed(
`\r\n风险提示: ztrip_harmony中${lbName}的版本号为${lbVersionCode}, 但是ctrip_harmony项目版本号为${projectVersionCode}, 分支为 ${ctripBranchName}`,
);
console.log('版本号不一致可能导致编译失败, 若继续执行请输入y:');
const {controlCPress, key} = await listenAnyKeyInput();
if (controlCPress) {
console.log('quit task');
process.exit();
} else {
if (key.toLowerCase() !== 'y') {
console.log('quit task');
process.exit();
}
}
} else {
logGreen('依赖版本与ctrip_harmony项目版本号一致');
}
} else {
logRed(`从ztrip_harmony中未找到${lbName}的版本号`);
}
}
/* 干掉buildOption配置 */
function removeBuildOption() {
const buildProfilePath = path.join(ctripHarmonyPath, ctripBuildProfileFile);
// 先复制原文件到bak, 以供编译完成后回滚
const bakPath = path.join(ctripHarmonyPath, `${ctripBuildProfileFile}-bak`);
if (fs.existsSync(bakPath)) {
fs.unlink(bakPath);
}
fs.copyFileSync(buildProfilePath, bakPath);
const buildProfile = readJson5(buildProfilePath);
const {
app: {products},
} = buildProfile;
_.find(products, {name: 'default'}).buildOption = {};
fs.writeFileSync(
path.join(ctripHarmonyPath, ctripBuildProfileFile),
JSON.stringify(buildProfile, null, 2),
);
console.log(`已移除${buildProfilePath}中的buildOption配置`);
}
function revertBuildOption() {
const buildProfilePath = path.join(ctripHarmonyPath, ctripBuildProfileFile);
const bakPath = path.join(ctripHarmonyPath, `${ctripBuildProfileFile}-bak`);
if (!fs.existsSync(bakPath)) {
console.log('未找到bak文件, 无法回滚');
return;
}
fs.copyFileSync(bakPath, buildProfilePath);
fs.unlinkSync(bakPath);
console.log(`已回滚${buildProfilePath}中的buildOption配置`);
}
async function doBuildHar(targetHarItem) {
const success = await buildHar(ctripHarmonyPath, targetHarItem.name);
if (!success) {
logRed('编译失败, 请检查');
return '';
}
logGreen('编译成功');
return path.join(
ctripHarmonyPath,
targetHarItem.srcPath,
harOutputDir,
`${targetHarItem.name}.har`,
);
}
function moveHar2Ztrip(harPath) {
const targetDir = path.join(ztripHarmonyPath, ztripTmpHarDir);
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir);
}
const projectVersionCode = readCtripProjectVersion();
// 拼接版本号
const extName = path.extname(harPath);
const pureName = path.basename(harPath, extName);
const newName = `${pureName}_${projectVersionCode}${extName}`;
const targetHarPath = path.join(targetDir, newName);
fs.copyFileSync(harPath, targetHarPath);
logGreen(`har已复制至: ${targetHarPath}`);
return targetHarPath;
}
async function modifyPackageJson(newHarPath, targetHarItem) {
const relativePath = `./${path.join(
ztripTmpHarDir,
path.basename(newHarPath),
)}`;
const localVersion = `file:${relativePath}`;
const packageJsonPath = path.join(ztripHarmonyPath, 'oh-package.json5');
console.log('\r\n开始写入新版本: \r\n');
let success = false;
await processFile(packageJsonPath, line => {
const trimLine = line.trim();
if (/(^(['"])(@[^'"/]+\/[^'"/]+))/.test(trimLine)) {
const lbName = RegExp.$1.replace('"', '').replace("'", '');
// console.log('lbName', lbName);
if (lbName.includes('/')) {
const baseName = lbName.split('/')[1];
if (baseName.toLowerCase() === targetHarItem.name.toLowerCase()) {
if (success) {
logRed('module name存在重复匹配');
process.exit();
}
success = true;
const left = line.split(trimLine)[0];
console.log('module name匹配成功:');
console.log(`${lbName}: -> ${localVersion}`);
return `${left}"${lbName}": "${localVersion}",`;
}
}
}
return line;
});
if (!success) {
logRed('module name匹配失败');
}
return success;
}
/**
* moduleName: 解析后的项目名, 比如 ctfoundation
* libraryName: 依赖名, 比如 @ctcommon/ctfoundation
* versionName: 依赖版本名, 比如 8.78.4-beta.20250214105531
* versionCode: 依赖纯版本号, 比如 8.78.4, 可能为空
*/
function loadZtripLibraryList() {
// 读取ztrip的package.json5, 仅展示ztrip里面包含的依赖
const packageJsonZtrip = readJson5(
path.join(ztripHarmonyPath, 'oh-package.json5'),
);
const {overrides} = packageJsonZtrip;
return Object.keys(overrides)
.filter(key => {
return key.startsWith('@ct') && key.includes('/');
})
.map(key => {
let versionCode = '';
const versionName = overrides[key];
if (versionName.startsWith('file:')) {
// 不带后缀的文件名
const fileName = path.basename(versionName, path.extname(versionName));
if (fileName.includes('_')) {
versionCode = _.last(fileName.split('_'));
}
} else {
versionCode = versionName.split('-')[0];
}
return {
libraryName: key,
moduleName: key.split('/')[1],
versionName,
versionCode,
};
});
}
function readCtripProjectVersion() {
const appConfigPath = path.join(ctripHarmonyPath, 'AppScope/app.json5');
console.log(`从${appConfigPath}中读取版本号`);
const {
app: {versionName},
} = readJson5(appConfigPath);
return versionName;
}
function printModuleList(modules) {
console.log(`\r\n${ctripHarmony}当前可编译的module: `);
const tableHead = ['Index', 'Name', 'Path', 'Version in Ztrip'];
const list = [tableHead];
modules.forEach((item, index) => {
const {name, srcPath, versionName} = item;
list.push([index, name, srcPath, versionName]);
});
const output = TableConsole(list);
console.log(output);
}
function readJson5(filePath) {
const json5Str = fs.readFileSync(filePath, 'utf8');
try {
return JSON5.parse(json5Str);
} catch (err) {
console.log(err);
process.exit();
}
return null;
}