前言
兄弟们,这个东西是真的真的好用,为什么需要自动化发布脚本呢? 如果你们目前的项目是这样push代码的,就有大问题:
git add -A
git commit -m "xxx"
git push
- 首先,你push版本前是不是要手动修改package.json里的版本号?是不是很麻烦?
- 其次,代码如果要打tag,你是不是要手动git tag vXXX还要git push origin tag把tag单独推到git仓库?
- 还有,你commit不符合angular规范咋办(比如修改bug是'fix:' 开头, 新增功能是'feat:' 开头)
- 还有,修改了commit之后如何自动生成changelog?
- 最重要的是你如果自己写bash 脚本,如何保证你代码的拓展性,比如我要加减一个流程,你必须面向过程编码去改bash脚本,而不能轻易的抽象出一个可配置化的流程脚本。
项目地址,开源不易,跪谢start
我后面还会出在很多开源组件库的基础上封装一套自己的react组件库和各种工具轮子,不论这些你是否用得到(我自己写的@mx-design/cli来启动项目真的很好用,有时候跑别的项目的demo都是用的这个自己造的轮子)我觉得一起学习交流的意义还是很大的
看看这个npm包咋样
这是我自己写的一个可配置化的node发布脚本,先看看效果:
我在自己项目的根目录建立一个scripts文件夹,下面建立一个release.ts 脚本
import defaultRelaseProcess from '@mx-design/release';
defaultRelaseProcess();
package.json
"scripts": {
// 不用担心ts-node依赖,@mx-design/release包会帮你安装
"release": "ts-node ./scripts/release.ts"
},
"devDependencies": {
"@mx-design/release": "2.2.19",
},
然后命令行界面到你的项目根目录,输入:
npm run release
然后你的命令行界面会显示如下: 1、询问你要升级的版本
看到了吧,你可以自由选择是主版本升级,还是次版本,或者其他版本,都是符合semver规范的。
2、选择后会让你填写commit信息,如果你的commit的信息不符合angular提交规范,会报错提示你不符合规范,并且终止脚本。(整体交互很舒服,比如你git push的时候,我们会显示正在push loading,push成功,就会覆盖当时正在push的文字,颜色标绿并显示对钩的图标)
交互loading如下:
如果命令执行错误,显示如下(国内github环境非常差,经常git push失败,用vpn还是一样的):
git push成功效果如下:
3、这里release的主要流程完成如下:
流程包括:
- 选择升级版本 ->
- 修改package.json的版本号 ->
- 填写commit信息(校验格式,不通过终止) ->
- 推送到代码仓库(推送失败会帮你把package.json里的版本回退) ->
- 生成changeLog ->
- build打包(这里执行的是npm run build 所以你的scripts里必须有build命令,要不执行失败) ->
- 发布包(执行的 npm publish --access=public 命令) ->
- 打tag并且推送到git仓库
自定义流程
上面几个流程是这个包自带流程顺序,你可以配自己的顺序,
import { getNextVersion, gitPush, setChangelog, build, publishNpm, addTag, compose } from '@mx-design/release';
// getNextVersion 获取下个版本号函数
// updateVersion 修改版本号 返回值是回退版本号的函数
// gitPush push代码函数
// setChangelog 设置changeLog函数
// build 执行npm run build函数
// publish 执行npm publish函数
// addTag 打tag函数
// compose函数组合器
// 使用方法,这些函数顺序可以自己换位置,或者增删减
const middle = [getNextVersion, updateVersion, gitPush, setChangelog, build, publishNpm, addTag];
function execRelease() {
compose(middle);
}
寻求共建 + 源码分析
本项目随时欢迎任何有兴趣的同学加入,评论区写自己github账号,或者issue上留言,我会第一时间加你到核心开发成员列表。
这个项目还缺少一些东西,比如eslint、stylelint校验,自定义changelog位置,增加单元测试,加入git workflow 等等,我没那么多时间做,因为今年还有很多任务排着呢。。。
源码分析
目录含义:
- 根目录
- src
- addTag
- index.ts // 打tag函数
- build
- build.ts // build项目函数
- config // 配置文件夹
- functions 工具函数
- contstants 常量
- gitPush
- index.ts // push到git函数
- publishNpm
- index.ts // publish 整个项目的函数
- selectNextVersion
- index.ts 选择下个版本号函数
- setChangeLog
- 打changeLog函数
package.json
tsconfig.json
... 普通文件没啥好讲的
核心中间件执行器compose,这个函数的灵感来源于koa框架,说白了就是一个中间件组合函数,并且能共享数据,代码如下,后面我们会举个例子,来看它的执行机制:
export function compose(middleware) {
// 共享数据仓库,每个函数都可以往里面加数据,所有函数都可以访问到里面的数据
const otherOptions = {};
// 函数发射器,一个函数执行完毕调用next(),就执行下一个函数
function dispatch(index, otherOptions) {
// 每次从middleware中间件里面选取一个函数执行,直到执行了全部函数
if (index == middleware.length) return;
const currMiddleware = middleware[index];
// 执行函数,传入了一下一个函数的dispatch,已经更新共享数据
currMiddleware((addOptions) => {
dispatch(++index, { ...otherOptions, ...addOptions });
}, otherOptions).catch((error) => {
console.log('💣 发布失败,失败原因:', error);
});
}
dispatch(0, otherOptions);
}
middleware写法
// 获取版本号函数,固定参数第一个是next,表示调用middleware里下一个函数
const getNextVersion = async (next) => {
// _selectNextVersion会查看你package.json里的version参数是啥版本
const nextVersion = await _selectNextVersion();
// 缓存老的package.json数据,为了后面可以回退版本有老版本数据做准备
const originPackageJson = getOriginPackageJson();
// next传入的数据都会放到共享数据仓库,下面的函数都可以拿到这些数据
next({
nextVersion,
originVersion: originPackageJson.version,
originPackageJson,
});
};
// 更新版本号方法
const updateVersion = async (next, otherOptions) => {
if (!otherOptions?.nextVersion) {
throw new Error('请传入package.json新版本号');
}
// 返回一个回退版本号的方法,上面的函数共享的originPackageJson数据就是给他用的
// basicCatchError如果命令执行失败,就打印失败原因,并且返回false
const backVersionFn = await _updateVersion(otherOptions.nextVersion, otherOptions.originPackageJson).catch(
basicCatchError,
);
// 把回退版本的函数共享出来,给下面需要用的地方自己调用
next({ backVersionFn });
};
案例说明
compose([getNextVersion, updateVersion])
首先compose会调用
- dispatch(0, otherOptions)
- 0代表compose传参的middleware数组里的第一项
- otherOptions代表共享数据,目前是空对象
接着, 第一个函数调用
- getNextVersion((addOptions) => { dispatch(++index, { ...otherOptions, ...addOptions }); }, otherOptions);
- 看第一个参数,我们是这么写的
- const getNextVersion = async (next) => {
- 也就是说第一个参数next调用后,实际上是调用dispatch函数,dispatch函数的参数index + 1,就调用了middleware数组里,第二个函数了
- dispatch第二个参数就是把next想共享的数据发下去
好了源码分析完了,我今年主要是任务是写react pc和移动端组件库,之前写了cli打包和开发的脚手架,文章如下: juejin.cn/post/707518…
这个项目也是,想当核心贡献者,直接留言,把github账号说下,第一时间加你进来,多谢点个star哦 现在完成了发布脚本,接着就是搭建组件库展示网站了,也是自己写,没有用现成的dumi或者storybook。