google/zx让前端可以使用typescript随心所欲的写脚本

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 使用zx编写脚本,可以随心所欲的使用nodejs模块类库,我的demo使用mysql类库去将日志写入到mysql数据库, 当然你可以调用fs-extra,写入到本地文件当中,选择性很多了

image.png


自从遇到了google/zx,很快我就将原先使用shell脚本的代码,全部转换到了google/zx


相当于使用前端js来写脚本了,对前端来说实在太友好了


使用zx编写脚本,可以随心所欲的使用nodejs模块类库,我的demo使用mysql类库去将日志写入到mysql数据库, 当然你可以调用fs-extra,写入到本地文件当中,选择性很多了


使用mjs模块,可以像使用前端js代码一样轻松使用 import和 export


引用zx中的$符号,可以随意使用shell脚本中的所有命令


同时使用typescript来写zx的脚本,很是不错哟,不过在import时需要写成 ’./xxx.mjs‘,其实调用的就是mts


可以在代码中随意调用 npm、 yarn、 pnpm等,以及git pull 、git push 等等。


可以在代码中更好的编写脚本的逻辑控制,虽然在shell脚本中页可以实现,但对于前端工程师来说,用js这简直就是so easy的事情。


完美的使用await async来控制 同步和异步代码的执行顺序


1、之前通过shell脚本写的打包脚本


脚本入口文件地址github.com/aehyok/2022…克隆下来,有兴趣的可以下载下来看看,cd到本目录下就可以进行测试,将十五个应用编译后的文件自动复制到测试环境


sh build-all.sh  -v 2.5.1.006 -p awpqc  |tee build-log.txt
-v  2.5.1.006 版本号设置
-p awpqc 分别代表五个项目


2、我通过zx进行了重写


如今我用google/zx(TypeScript)写的代码入口地址:github.com/aehyok/2022…


先说一下背景:


  • PC:主应用 + 八个子应用(全部是vue3)
  • APP: 主应用(vue2) + 两个子应用(vue3)
  • Other: P、Q、W 其他三个都是一个应用就完事的
  • 算一下,9+3+3=15个应用 我这脚本就是为了方便打包这15个应用


3、怎么做的呢


  • 十五个应用每次都到对应的目录去yarn build,或者pnpm build,会相当麻烦,而且会有遗漏的可能。


  • 项目在我本地都是可以运行的,所以这里我省去了, npm install 或者 yarn 或者pnpm i 。


  • 通过我的小demo,便可以通过一个命令 pnpm build:all,将上面说的15个应用全部打包完毕,放到指定服务器上。


  • 然后可以查看log日志,确认报错信息,或者直接线上测试查看发布情况。如果有个别的子应用出现问题,可以单独去打包子应用即可。


  • 对每个子应用的打包目录,做了调整,共同打包到一个release目录,将这个目录作为一个代码仓库,方便回退和记录的。


  • 过程:(这里面暂时使用的命令工具为yarn)


  • 1、到指定的git仓库目录,拉取代码 cd path、git pull
  • 2、安装依赖 (此步骤省略) yarn
  • 3、代码仓库只有一个应用的直接编译应用 yarn build
  • 4、代码仓库包含多个应用的 需要先到指定的子应用目录 cd path, 再进行yarn build
  • 5、编译结束,根据版本号version 给代码仓库打上tag标签  git tag  git push
  • 6、将编译后的文件拷贝到服务器指定的目录  scp -r
  • 7、所有的代码仓库都编译上传完毕以后,要将release代码仓库进行  git add . git commit git push,然后再打上对应的tag标签,推送到服务器


4、实际操作(根据上面的过程进行一步一步的代码实现)


  • 1、拉取代码


// 精简版代码(不包括写日志)
export const gitPullBy = async(name: string, path: string) => {
    try {
        const gitPullInfo = await $`cd ${path}; git pull;`;
        if (gitPullInfo.exitCode === 0) {
            console.log('git pull 拉取代码成功')
        }
    } catch {
    }
};


  • 2、安装依赖


这一步按照道理,拉取代码后,要进行pnpm i,安装依赖的过程。暂时通过手动挡处理:保证了项目在本地开发环境能够运行,也就是说依赖肯定安装好了,要不然都跑不起来,自动化任务或者脚本里,难道能判断依赖是否安装过,或者说有新增依赖,可以通过什么方式来监测吗?


  • 3、直接编译子应用


// 精简版代码
export const yarnBuildBy = async (path) => {
    try {
        const buildInfo = await $` cd ${path};yarn build;`;
        if (buildInfo.exitCode === 0) {
            console.log(`yarn build end success`);
        }
    } catch(error) {
    }
}


  • 4、多个子应用批量编译


通过promise.all并发一起执行多个字应用的编译


// 精简版代码
export const yarnBuildChildList = async(list) => {
    try {
        const result =await Promise.all(
            list.map((item) => {
            return $`cd ${item}; yarn build`;
            })
        )
        if(result) {
            console.log('all', 'result')
        }
    } catch {
    }
}


  • 5、编译结束,根据版本号打tag标签


  • 首先根据版本号判断是否已经存在相对应的version tag
  • 如果存在的话,先删除标签;如果不存在,直接打tag标签
  • 最后git push 推送到代码仓库即可


// 全过程代码
export const gitTag = async () => {
    const { name } = global.project
    const path = baseUrl + name
    // 判断是否已经存在tag标签
    const isExist = await isExistTag(path)
    console.log(isExist, 'isExist')
    if (isExist) {
        // 如果存在泽进行删除tag标签的操作
        let isSuccess = await deleteTag(path);
        if (isSuccess) {
        await addTag(path, isExist);
        } else {
        oneLogger(`delete tag [${global.version}] error`);
        }
    } else {
        await addTag(path,isExist);
    }
};
const isExistTag = async (path) => {
    const result = await $` cd ${path};git tag;`;
    console.log("判断是否存在tag", result);
    if (result && result.exitCode === 0) {
        let array = result.stdout.split("\n");
        if (array.length > 0 && array.includes(global.version)) {
            return true;
        }
        return false;
    }
};
const deleteTag = async (path) => {
    oneLogger(`delete tag [${global.version}] start`);
    const result = await $` cd ${path}; 
                                git tag -d ${global.version}; 
                                git push origin :refs/tags/${global.version}`;
    if (result.exitCode === 0) {
        oneLogger(`delete tag [${global.version}] end success`);
        return true;
    }
    return false;
};
/**
*
* @param {*} path 路径
* @param {*} isExist 0为不存在,直接创建的;1为已存在删除的,重新创建
*/
const addTag = async (path, isExist) => {
    const result = await $`cd ${path};
                        git tag -a ${global.version} -m 'chore:version ${global.version}版本号'; 
                        git push origin ${global.version};`;
    if (result && result.exitCode === 0) {
        if (isExist) {
        oneLogger(`re create tag [${global.version}] success`);
        } else {
        oneLogger(`create tag [${global.version}] success`);
        }
    }
};
//调用写入mysql日志的
const oneLogger = (info) => {
    console.log(info);
    const { name } = global.project
    writerLog(name, info, global.version);
};


  • 6、拷贝文件到指定服务器


这里如果想更方便一些,可以把本地的ssh生成的公钥拷贝到相应的linux服务器的ssh目录


export const copyFile = async(path: string) => {
    try {
        const result = await $`scp -r /e/work/git/dvs-2.x/release/cms/${path}/* root@139.9.184.171:/usr/local/sunlight/dvs/dvs-uis/${path}/`
        if(result.exitCode === 0) {
            oneLogger(`copy file  [${global.version}] end success`)
        }
        else {
            console.log("fail", $`$?`);
        }
    } catch {
        oneLogger(`copy file [${global.version}] end error`)
    }
}
const oneLogger = (info) => {
    console.log(info);
    const { name } = global.project
    writerLog(name, info, global.version);
};


  • 7、整理所有项目编译后的文件


对于打包生成的文件进行release目录整理,当然你可以全部编译打包完,再一起copy到指定的服务器,根据需要都可以去调整。


// 这一步有打包的代码需要进行git push
export const gitPushBy = async(name: string, path: string) => {
    try {
        await writerLog(name, `git push start`, global.version);
        // const message=`build:前端${name} -- commit-version:${global.version}`
        const message=`build:前端app、qrocde、wechat、park、console(child)commit-version:${global.version}`
        const result = await $`cd ${path}; git add . ; sleep 2; git commit -m ${message}; git push origin;`
        if(result && result.exitCode === 0 ) {
            await writerLog(name, `git push end success`, global.version);
        } else {
            await writerLog(name, `git push error: ${result.stderr}`, global.version); 
        }
    } catch (error){
        console.log(error, 'error')
        if(error.stdout.includes('nothing to commit, working tree clean')) {
            await writerLog(name, `git push nothing to commit`, global.version);
        }
        await writerLog(name, `git push error`, global.version);
    }
}


5、在使用过程中的问题


  • 1、 pre...和post命令的使用


"prebuild:all": "ts-node-esm pull.mts -v 4.5.3.007",
"build:all": "concurrently \"pnpm build:app\"  \"pnpm build:pc\"  \"pnpm build:wechat\"  \"pnpm build:park\"  \"pnpm build:qrcode\" ",
"postbuild:all": "ts-node-esm push.mts -v 4.5.3.007",


在执行命令时执行 yarn build:all,那么会先执行命令 yarn prebuild:all, 执行完以后,才会真正执行yarn build:all, 执行完yarn:build:all以后,会执行yarn: postbuild:all。


也就是pre为提前执行的钩子,post为结束后执行的钩子。但是如果使用pnpm执行 就不会执行pre和post钩子。(这里还没找到问题!!!!!)


  • 2、concurrently,并行运行脚本,使用前先安装依赖 pnpm i concurrently


"build:all": "concurrently \"pnpm build:app\"  \"pnpm build:pc\"  \"pnpm build:wechat\"  \"pnpm build:park\"  \"pnpm build:qrcode\" ",


  • 3、在命令行中 & 为并行, && 串行,在pnpm安装的依赖中行不行,所以使用了concurrently


"build": "ts-node-esm index.mts -v 4.5.3.007 -p app & ts-node-esm index.mts -v 4.5.3.007 -p pc & ts-node-esm index.mts -v 4.5.3.007 -p wechat & ts-node-esm index.mts -v 4.5.3.007 -p park   & ts-node-esm index.mts -v 4.5.3.007 -p qrcode",


  • 这样使用还是不能并行,pnpm build,或者yarn build,都一样(这里也没找到问题!!!!!)


6、执行日志管理


我这里暂时是通过mysql库,写入的数据库日志


  • 1、实例化数据库链接对象


let _conn = mysql.createConnection({
    // 创建mysql实例,这是我的个人数据库可以访问,尽量只去查看
    host: "139.159.245.209",
    port: "3306",
    user: "meta",
    password: "meta",
    database: "meta",
});


  • 2、通过实例链接数据库


_conn.connect(function (err) {
    if (err) {
    console.log("fail to connect db", err.stack);
    throw err;
    }
    // 这里正真连接上数据库了。
    console.log(
    "链接成功--db state %s and threadID %s",
    _conn.state,
    _conn.threadId
    );
    logDbStat();  //此为记录数据库链接的线程和状态
});
const logDbState = function () {
    console.log(
    "db state %s and threadID %s",
    _conn.state,
    _conn.threadId
    );
};


  • 3、用完后关闭链接


const close = (conn) => {
    conn.end((err) => {
        if (err) {
        console.log("error ", err);
        } else {
        console.log("关闭成功", conn.state, conn.threadId);
        }
    });
};


  • 4、最后返回一个Promise 方便处理和调用


return new Promise((resolve, reject) => {
    try {
    let sqlParamsList = [sql];
    if (params) {
        sqlParamsList.push(params);
    }
    _conn.query(...sqlParamsList, (err, res) => {
        if (err) {
        reject(err);
        } else {
        resolve(res);
        close(_conn);
        }
    });
    } catch (error) {
    reject(error);
    }
});


  • 5、封装方法进行调用


export const writerLog = async (project, content, version) => {
    let id = shortid.generate()
    await executeSql("INSERT INTO CiCdLog values(?,?,?,?,null,null)", [id,
        project,
        content,
        version,
    ]);
};


  • 6、在使用的地方进行import导入,然后调用即可


import { writerLog } from "./sql-helper.mjs"
// 传递三个参数即可
await writerLog(name, `git push error`, global.version);


7、测试命令行编译


一个pc代码仓库,一个主应用和两个子应用,脚本所在代码仓库链接地址github.com/aehyok/2022…


yarn build


执行yarn build的时候,会先调用 prebuild命令


然后执行完yarn build之后,会调用postbuild命令


通过代码产生的写入日志,如下截图所示


image.png


8、总结


zx可以处理很多的事情,这里我就不一一列举了,有兴趣的可以继续深入学习了解探索一下,或许也会发现新的大陆。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
3月前
|
JavaScript 前端开发 编译器
TypeScript:一场震撼前端开发的效率风暴!颠覆想象,带你领略前所未有的编码传奇!
【8月更文挑战第22天】TypeScript 凭借其强大的静态类型系统和丰富的工具支持,已成为前端开发的优选语言。它通过类型检查帮助开发者早期发现错误,显著提升了代码质量和维护性。例如,定义函数时明确参数类型,能在编译阶段捕获类型不匹配的问题。TypeScript 还提供自动补全功能,加快编码速度。与 Angular、React 和 Vue 等框架的无缝集成进一步提高了开发效率,使 TypeScript 成为现代前端开发中不可或缺的一部分。
41 1
|
3月前
|
JavaScript 前端开发 安全
【技术革新】Vue.js + TypeScript:如何让前端开发既高效又安心?
【8月更文挑战第30天】在使用Vue.js构建前端应用时,结合TypeScript能显著提升代码质量和开发效率。TypeScript作为JavaScript的超集,通过添加静态类型检查帮助早期发现错误,减少运行时问题。本文通过具体案例展示如何在Vue.js项目中集成TypeScript,并利用其类型系统提升代码质量。首先,使用Vue CLI创建支持TypeScript的新项目,然后构建一个简单的待办事项应用,通过定义接口描述数据结构并在组件中使用类型注解,确保代码符合预期并提供更好的编辑器支持。
84 0
|
3月前
|
资源调度 JavaScript 前端开发
Vue3+TypeScript前端项目新纪元:揭秘高效事件总线Mitt,轻松驾驭组件间通信的艺术!
【8月更文挑战第3天】Vue3结合TypeScript强化了类型安全与组件化开发。面对大型应用中复杂的组件通信挑战,可通过引入轻量级事件发射器Mitt实现事件总线模式。Mitt易于集成,通过简单几步即可完成安装与配置:安装Mitt、创建事件总线实例、并在组件中使用`emit`与`on`方法发送及监听事件。此外,利用TypeScript的强大类型系统确保事件处理器正确无误。这种方式有助于保持代码整洁、解耦组件,同时提高应用的可维护性和扩展性。不过,在大规模项目中需谨慎使用,以防事件流过于复杂难以管理。
109 1
|
3月前
|
开发框架 JSON 缓存
基于SqlSugar的开发框架循序渐进介绍(22)-- Vue3+TypeScript的前端工作流模块中实现统一的表单编辑和表单详情查看处理
基于SqlSugar的开发框架循序渐进介绍(22)-- Vue3+TypeScript的前端工作流模块中实现统一的表单编辑和表单详情查看处理
|
3月前
|
开发框架 前端开发 JavaScript
基于SqlSugar的开发框架循序渐进介绍(18)-- 基于代码生成工具Database2Sharp,快速生成Vue3+TypeScript的前端界面和Winform端界面
基于SqlSugar的开发框架循序渐进介绍(18)-- 基于代码生成工具Database2Sharp,快速生成Vue3+TypeScript的前端界面和Winform端界面
|
3月前
|
开发框架 前端开发 JavaScript
在基于vue-next-admin的Vue3+TypeScript前端项目中,为了使用方便全局挂载对象接口
在基于vue-next-admin的Vue3+TypeScript前端项目中,为了使用方便全局挂载对象接口
|
3月前
|
开发框架 前端开发 JavaScript
在Vue3+TypeScript 前端项目中使用事件总线Mitt
在Vue3+TypeScript 前端项目中使用事件总线Mitt
|
3月前
|
JavaScript 前端开发 安全
解锁Vue3与TypeScript的完美搭档:getCurrentInstance带你高效打造未来前端
【8月更文挑战第21天】Vue3的getCurrentInstance方法作为Composition API的一部分,让开发者能在组件内访问实例。结合TypeScript,可通过定义组件实例类型实现更好的代码提示与类型检查,提升开发效率与代码质量。例如,定义一个带有特定属性(如myData)的组件实例类型,可以在setup中获取并安全地修改这些属性。这种方式确保了一致性和减少了运行时错误,使开发更加高效和安全。
133 0
|
4月前
|
前端开发 jenkins 持续交付
不是Jenkins玩不起,而是脚本更有性价比,在1Panel中使用Node搭建前端自动化
不是Jenkins玩不起,而是脚本更有性价比,在1Panel中使用Node搭建前端自动化
174 2
|
5月前
|
前端开发 JavaScript 安全
微前端架构采用 TypeScript 提升开发效率和代码可靠性
【6月更文挑战第12天】微前端架构采用 TypeScript 提升开发效率和代码可靠性。TypeScript 的类型安全防止了微前端间的类型错误,智能提示与自动补全加速开发,重构支持简化代码更新。通过定义公共接口和使用 TypeScript 编写微前端,确保通信一致性与代码质量。在构建流程中集成 TypeScript,保证构建正确性。总之,TypeScript 在微前端架构中扮演关键角色,推荐用于大型前端项目。
71 4

热门文章

最新文章