背景
随着前端业务的迭代,代码规模的扩大,难免会抽出一些公用组件或者底层抽象逻辑的独立仓库,以npm包的形式在我们的主项目中使用,由此必然会涉及包的发布,为了不让我们的发布过于混乱和难以管理,我们实践了一些较为便捷的方法来帮助包管理的规范;(注: 此实践基于团队人员较多,且日常迭代上线需要经常变动发布这些npm包,不一定通用)
依赖说明
默认都有Node, Npm, Git基础环境,另外还需要:
- shelljs 用来执行命令,非必须,child_process有类似功能
- standard-version 版本管理及CHANGELOG
核心问题与解决思路
所有包都发布到latest域下
正常的业务开发是分为TEST
,BETA
,LIVE
等不同的环境,我们的问题就在于如何让我们的发布更加隔离,首先想到的就是npm的tag
,具体见文档;简而言之我们可以通过npm publish --tag xxx
来指定发布在对应的tag下;此外我们还会用到npm view
参考这个命令;
版本号冲突
standard-version
给我们提供了自动递增版本号的功能,但这就意味这所人都基于release的分支开发在test环境发布会撞车,你先bump
了版本并在你的分支发布了,我后面发布就会冲突
关于这个问题,我们想让发布不冲突,就需要对每个人做一个隔离,比如每个人的名字都做一个tag,你发你的我发我的,或者根据当前任务的需求来,用task单号做隔离;
(当然你可以自己改版本号发布,这又会带来更不规范的版本号管理问题,不是很推荐,具体见语义化版本)
提MR/PR的时候版本区域冲突
上面的版本号冲突问题可知,在提MR/PR的时候分支的版本号各不相同,会带来冲突;
由此我们想在非live环境其实不需要改版本号,只是我们测试而已,我们只有上LIVE
的时候会需要一个版本号的升级;所以想到的就是先发布后回退package.json
的文件变化;
主体流程
说了那么多还是会有点混乱,直接看下流程:
具体实现
// publish.js
const shell = require('shelljs')
const { join } = require('path')
const XXX_REG = /xxx-\d+/i // 需求分支的命名遵循一定规则,便于区分
const rootPath = process.cwd()
const pkg = require(`${join(rootPath, 'package.json')}`)
const packageName = pkg.name
const version = pkg.version // 以这个为大版本,如1.1.0
try {
const alpha = 'alpha'
const changefiles = shell.exec('git ls-files -m').stdout.trim() // 检查更新的文件
if (changefiles.length) {
throw new Error('本地含有未提交文件')
}
// 拿到当前的git分支,计算出当前名字
const gitBranchName = shell.exec('git branch | grep "*"').stdout
const isTestFeature = XXX_REG.test(gitBranchName)
const featureName = isTestFeature && XXX_REG.exec(gitBranchName)[0].toLowerCase()
// 计算出当前需求分支对应的tag, 如 xxx-123-alpha
const tag = `${featureName}-${alpha}`
// 这个命令看对应的tag下是否有发布产物了
const latestVersion = shell.exec(`npm view ${packageName} version --tag=${tag}`).stdout
// 计算新版本,有旧的就递增,没有就发布0版本
let newVersion = ''
if (latestVersion.includes(tag)) {
const splitVersions = latestVersion.split(`-${tag}.`)
const latestMainVersion = splitVersions[0]
const latestNum = Number(splitVersions[1])
newVersion = `${latestMainVersion}-${tag}.${latestNum + 1}`
} else {
newVersion = `${version}-${tag}.0`
}
// 正式发布
if (isTestFeature && featureName) {
const versionCommand = `npm run release -- --release-as ${newVersion} --skip.changelog --skip.tag --skip.commit`
const publishCommand = `npm publish --registry https://company.com --tag ${tag}` // 发布到内网
shell.exec(`${versionCommand} && ${publishCommand}`)
shell.exec('git checkout -- package.json') // 回退 standard-version带来的package.json的变化
}
} catch (e) {
console.log(e)
process.exit(1)
}
// package.json
{
"name": "ureponame",
"version": "1.1.0",
...
"scripts": {
"release": "standard-version",
"publish-local": "node build/publish",
"prepublishOnly": "npm run build"
},
...
}
使用及说明
利用简短的js脚本和git/npm命令来帮助我们发布,使用上较为方便直接运行npm run publish-local
即可;
关于BETA
,LIVE
的发布在上面没有体现,本着能不动手动发布就不手动发布的原则我们将这些逻辑接入了Gitlab的CI,在master
,release
分支上自动发布;执行的逻辑大体类似不过多了一些CI的逻辑,后面有时间再专门讨论上线部分;
以上是对与npm包更为便捷的一个小探索,此需求建立在团队的高频变更发布与上线,团队之间情况不尽相同,如有好的点子还请留言交流学习~