由于使用到的 CI/CD 工具可能会更换,对应的学习成本也相应增加,但是 Node.js 其实可以帮助我们实现这些工具的大部分功能,包括操作文件、执行 cmd 等等。 所以我们如果把大部分的打包或集成操作使用 Node.js 去实现,那么无论工具如何更换,我们只需学习如何使用该工具执行 npm 即可,从而大大降低迁移与学习成本。 当然这只是我最近迁移时的一些解决方案与想法,如果有大佬指教一些其他的方式,那自然是更好啦哈哈哈~
Flutter 根据安卓版本打包 Demo
代码
代码语言:javascript复制const fs = require('fs');
const nodeCmd = require('node-cmd');
let arguments = process.argv.slice(2); // 获取命令行传入参数
let targetBranch = arguments[0] || 11; // 需要打包的对应安卓版本
let shouldBuild = arguments[1] == undefined ? true : arguments[1] == 'true'; // 是否需要打包
let menusStation = arguments[2] == undefined ? 'changzhou' : arguments[2]; // 菜单地址
let filePath = arguments[3] || './pubspec.yaml'; // pubspec 配置文件位置
let buildFilePath = arguments[4] || './android/app/build.gradle'; // build.gradle 配置文件位置
let menusConfigPath = arguments[5] || './lib/configuration/menus.dart'; // menus.dart 配置文件位置
console.table({ arguments, targetBranch, shouldBuild, filePath, buildFilePath });
/// 读取对应的安卓目录配置文件
fs.readFile(buildFilePath, 'utf8', function (err, data) {
if (err) return console.log(`%c出错啦!${data}`, 'color:red;');
let result = data.replace(/minSdkVersionsd{2}/g, `minSdkVersion ${targetBranch == 11 ? 26 : 21}`);
console.log('正在修改安卓 build 配置文件......');
fs.writeFile(buildFilePath, result, 'utf8', function (err) {
if (err) return console.log(`%c出错啦!${data}`, 'color:red;');
setMenus();
});
});
/// 设置对应版本的菜单
function setMenus() {
fs.readFile(menusConfigPath, 'utf8', function (err, data) {
if (err) return console.log(`%c出错啦!${data}`, 'color:red;');
let result = data.replace(
/staticsListsmenuLists=sX_MENU_w ;/g,
`static List menuList = X_MENU_${menusStation};`
);
console.log(`正在修改菜单 build 配置文件 [X_MENU_${menusStation}]......`);
fs.writeFile(menusConfigPath, result, 'utf8', function (err) {
if (err) return console.log(`%c出错啦!${data}`, 'color:red;');
buildStart();
});
});
}
/// 开始打包
function buildStart() {
fs.readFile(filePath, 'utf8', function (err, data) {
if (err) return console.log(`%c出错啦!${data}`, 'color:red;');
let result = data.replace(/_android_d{1,2}_scan/g, `_android_${targetBranch}_scan`); // 修改对呀安卓版本的 SDK 插件版本
console.log('正在修改 Flutter 配置文件......');
fs.writeFile(filePath, result, 'utf8', function (err) {
if (err) return console.log(`%c出错啦!${data}`, 'color:red;');
getFlutterPackages();
});
});
}
/// 获取 flutter 相关插件
function getFlutterPackages() {
console.log('执行下载命令 flutter pub get');
nodeCmd.run('flutter pub get', (err, data, stderr) => {
if (err) return console.log(`%c出错啦!${data}`, 'color:red;');
shouldBuild && buildFlutterApk();
});
}
/// 打包
function buildFlutterApk() {
console.log('打包中... flutter build apk --target-platform android-arm64');
nodeCmd.run('flutter build apk --target-platform android-arm64', (err, data, stderr) => {
if (err) return console.log(`%c出错啦!${data}`, 'color:red;');
console.log(`%c${data} %c成功,请检查 apk 文件!`, 'color:green;', 'color:chocolate;');
});
}
// 这样的话,我们只需要每次切换 CI/CD 工具时,学会使用 node 执行这个脚本即可。
其他
- 以上脚本既可以用于本地打包,也可以设置在推送时自动运行。
- 另外如果我们还要集成到服务端的不同目录,也可以使用 Node.js 去实现文件复制或者移动。
- 如果需要在
commit
或者push
前进行一些操作,我们还可以使用package.json-scripts
定义一些钩子来实现。
prepublish: 在包发布之前运行,也会在 npm install 安装到本地时运行。
publish,postpublish: 包被发布之后运行
preinstall: 包被安装前运行
install,postinstall: 包被安装后运行
preuninstall,uninstall: 包被卸载前运行
postuninstall: 包被卸载后运行
preversion: bump 包版本前运行
postversion: bump 包版本后运行
pretest,test,posttest: 通过 npm test 命令运行
prestop,stop,poststop: 通过 npm stop 命令运行
prestart,start,poststart: 通过 npm start 命令运行
prerestart,restart,postrestart: 通过 npm restart 运行
需要复制指定目录提交到某个仓库的 Demo
代码
代码语言:javascript复制const path = require('path');
const fs = require('fs');
const isLocalPublish = process.argv[2] === 'true';
const targetDir = process.argv[3] ?? './test';
const pkg = require(path.resolve('package.json'));
const nodeCmd = require('node-cmd');
// git clone url[请设置带 token 的地址,或者先设置 SSH。]
// 将对外目录 git 仓库拉取到本地
/**
* 判断目录是否存在,不存在则创建目录。
*/
const isDirExist = (path) => {
// fs.access(path, function (err) {
// if (err) {
// // 目录不存在时创建目录
// fs.mkdirSync(path);
// }
// });
if (!fs.existsSync(path)) {
fs.mkdirSync(path);
}
};
/*
* 复制目录、子目录,及其中的文件。
* @param src {String} 要复制的目录
* @param target {String} 复制到目标目录
*/
const copyDir = (err, src, target) => {
if (err) {
console.log({ 'copyDir error': err });
return;
}
fs.readdir(src, function (err, paths) {
if (err) {
console.log({ 'copyDir error': err });
return;
}
paths.forEach(function (path) {
let _src = src '/' path;
let _target = target '/' path;
fs.stat(_src, function (err, stat) {
if (err) {
console.log({ 'copyDir error': err });
return;
}
// 判断是文件还是目录
if (stat.isFile()) {
fs.writeFileSync(_target, fs.readFileSync(_src));
} else if (stat.isDirectory()) {
// 当是目录是,递归复制。
isDirExist(_target);
copyDir(null, _src, _target);
}
});
});
});
};
/**
* 复制文件
* @param {*} src
* @param {*} target
*/
const copyFile = (src, target) => {
try {
fs.writeFileSync(target, fs.readFileSync(src));
} catch (e) {
console.log({ 'copyFile error': e });
try {
fs.createReadStream(src).pipe(fs.createWriteStream(target)); // 大文件复制
} catch (err) {
console.log({ 'copyBigFile error': err });
}
}
};
/**
* 获取发布的所有对外文件
* @param {*} targetDir
*/
const getSubmitFiles = (targetDir) => {
isDirExist(targetDir);
['dir1', 'dir2', 'dir3'].forEach(function (srcDir) {
let copyTargetDir = `${targetDir}/${srcDir}`;
isDirExist(copyTargetDir);
copyDir(null, srcDir, copyTargetDir);
});
['package.json', 'README.md', 'README.en.md'].forEach(function (file) {
copyFile(file, `${targetDir}/${file}`);
});
publishPackage();
};
/**
* 发布包
*/
const publishPackage = () => {
const dateObj = new Date();
const versionNo = `${dateObj.getMilliseconds()}${dateObj.getSeconds()}${dateObj.getMinutes()}${dateObj.getHours()}${dateObj.getDate()}${
dateObj.getMonth() 1
}${dateObj.getFullYear()}`;
const versionId = `(${pkg.version}) ${versionNo}`;
nodeCmd.run(
`cd ${targetDir} && git checkout main && git config --global user.name "doubleam" && git config --global user.email "admin@biugle.cn" && git status && git add -A . && git commit -m "Auto publish, Version ${versionId}." && git push origin main -f`,
(err, data, stderr) => {
if (err) return console.log(`%c 提交出错啦!${data}`, 'color:red;');
if (isLocalPublish) {
// 需要有 .npmrc 文件,才可自动登录并执行 npm publish。或者设置 CI/CD 专用 token。 https://docs.npmjs.com/using-private-packages-in-a-ci-cd-workflow
nodeCmd.run('npm publish', (err, data, stderr) => {
if (err) return console.log(`%c 发布版本出错啦!${data}`, 'color:red;');
console.log(data);
nodeCmd.run(`cd ../ && rimraf ${targetDir}`, (err, data, stderr) => {
if (err) return console.log(`%c 删除文档出错啦!${data}`, 'color:red;');
console.log(data);
});
});
}
}
);
};
getSubmitFiles(targetDir);
其他
- 适用于我们部分源码不方便公开的情况,我们可以设置一个目录来暴露生产包,但是保留源码的私有性。
- 为了简化命令参数,我们可以预先写好放到
package.json-scripts
中去,方便直接使用npm run xxx
执行。 - 以上内容仅供参考 (0.0)