Webpack Plugin 打包文件名写入 git commit hash
- Published on
场景
问题点:因为最近开始模块拆分的缘故,要将子项目 build 出来的 JS 动态插入到主项目中加载,但是存在的一个问题就是测试环境子项目迭代较快,并没有打 tag 提发布的完整流程,需要将测试发布的 JS 和代码版本关联上。
在规范的 git 管理和发布流程下自然是没有这个问题的。但问题出现点本身就是子项目测试环境,代码提交更新频繁,再加上子项目是本地打包,流程长的发布耗时不太有必要,这就导致了每个测试版本的版本信息丢失。总的说是为了快速开发和提测迭代产生一个版本管理的问题。
要做的就是需要将打包产物 JS 文件和当前的 git 版本信息关联起来,然后回查问题就能快速找到文件名对应的代码版本进行排错。
怎么关联?思路肯定就是在打包产物中留下 git 信息,比如:
- 将 git 提交信息写入到 JS (或者 html) 文件,然后页面上就能查找到
- 更直接的,将 git 提交写到生成的 JS 文件名上
方案
1. git-revision-webpack-plugin
webpack 插件: git-revision-webpack-plugin
按照文档配置之后打包,却没有得到预期的文件名,查看源码后发现有些 API 的调用和文档有了小的差别,应该是个别 API 的小版本差异导致的。库功能很全,但我们需要的仅仅是 git commit hash,其实是几行脚本就可以完成的,就暂不引入第三方库。
2. node 执行 git 命令获取信息
在 webpack.config.js 中执行一段 node, 获取提交 commitId,然后写到生成文件名的配置。
const { execSync } = require('child_process')
/*
* ...
*/
let fileName = '[hash:8]' // 默认8位hash
if (env != 'development') {
fileName = execSync('git rev-parse --short HEAD', { encoding: 'utf-8' }).replace('\n', '')
}
const config = {
// ...
output: {
filename: `${fileName}.js`,
//...
},
}
module.exports = config
git rev-parse --short HEAD
查看最近提交的短 commit id
如果是本地打包,还应该加上一个限制,只有提交之后才能build JS
,否则很可能打包之后的内容包含了一些未提交的临时修改,然后这些修改后来又被还原的话就无从查证了(但已经被 build 进去生效了)。git 获取存在 diff 差异的文件,未提交代码产生提交记录之前不能build
:
if (env != 'development') {
let gitlocalDiff = execSync('git diff --name-only', { encoding: 'utf-8' }).replace('\n', '')
if (gitlocalDiff) {
console.error(new Error("you should commit your files before execuating 'npm run build'"))
process.exit(-1)
}
commitID = execSync('git rev-parse --short HEAD', { encoding: 'utf-8' }).replace('\n', '')
}
git diff --name-only
查看本地修改文件名
然后就可以进行正常的流程了。commit
->build
-> 产生的 JS 以commitID为文件名
。
查找当前生效代码就是:查看 JS 文件名(获取git提交id)
-> 切到对应的 commitId
就是当前的打包代码了。
做成自定义插件 GitHashFileNamePlugin
做成插件的目的就是将git
获取commitId
的操作放在插件中进行,而不必在每个子项目的打包配置中写这些脚本。按照上面的思路,其实是通过插件修改webpack
配置中output
的filename
。
定义并实现:
const { execSync } = require('child_process')
class GitHashFileNamePlugin {
constructor(appName) {
this.appName = appName || 'app'
}
apply(compiler) {
compiler.hooks.afterResolvers.tap('GitHashFileNamePlugin', (compilation, callback) => {
// console.info("xxxx", compilation.options.output); // 需要改的是filename
const githash = execSync('git rev-parse --short HEAD', { encoding: 'utf-8' }).replace(
'\n',
''
)
compilation.options.output.filename = `${this.appName}-${githash}.js`
})
}
}
module.exports = GitHashFileNamePlugin
在解析配置后的钩子afterResolvers
中,获取到compilation
对象,修改其输出配置的filename
属性,值组成为${this.appName}-${githash}.js
, appName 由外部调用传入,这样一来生成的就是项目名-Commit Hash.js
的文件名。
webpack.config.js 引入后使用:
const GitHashFileNamePlugin = require('../plugins/githash')
module.exports = {
// ...
plugins: [
// ...
new GitHashFileNamePlugin('tars'),
],
}
这里传入的项目名为tars
, 测试一下,打包名为tars-504273f.js
。

执行 git log 查看最近的 commit:

文件名和提交短信息是完全对上,bingo~