记一次用 Git hook 提效

in Program with 0 comment
迫于公司提交代码的要求越来越繁琐,需要写一堆信息。要命的是没有自动化工具,天天各种人肉手写 txt,改日期,改格式。经常会因为 txt 中漏写或者漏改日期导致 commit 被打回。烦不胜烦,忍无可忍,决定自己动手写一个自动化工具,把所有的格式化信息自动填入。

问题是什么?

我想要自动生成的是一个叫 ”测试重点” 的东西,这个文件由:头信息、提交信息组成。

其中 ”头信息“ 的格式是这样子的:

======================2020-09-19========================
### begin:2020-09-14
### end:2020-09-19

可以看到整个 “头信息” 都是可以自动生成的,只需要计算出本周开始和结束的日期即可。

“提交信息” 是这样子的:

---

### author:张三
### ISSUEID:AAA-111
### 模块:模块A
### 重点:操作xx发现xx,现象是xx,预期是xx。

结合我司的情况,其中 ISSUEID模块 是可以从 commit-msg 中获取的。author 可以从 git config 中获取。唯一需要填写的就是 重点 这个条目。

那么就可以梳理出解决这个问题的思路了:

通过解析 commit-msggit config,可以得到四项中的三项,剩下的一项本来我也想着从 commit-msg 中获取,但是发现对应不上,那就只能人肉填写了。最后将生成的文件和代码一起提交就行。

选择什么工具?

一开始想到的是 IDE 插件,但是实在懒得写 UI。转念一想,Git hook 非常适合这种场景:

  1. 有自动切入点,不需要手动调用
  2. 可以无障碍获取 Git 的配置和 commit-msg

实现思路

分析一下我们的目的,可以总结一些特征点。

  1. 文件夹以月为单位(一个月一个文件夹)
  2. 文件以周为单位(一份文件记录一周的测试重点)
  3. 文件的主要内容可以从 commit-msg 中获取

所以我们就需要考虑以下几点:

  1. 文件会多次写入,需要考虑文件头,首次写入和后续写入的区别需要注意
  2. 需要计算一周开始和结束的日期
  3. commit-msg 中获取信息,需要用正则来筛选
  4. 选用 Git hook,那么用 shell 来实现是比较方便的
  5. Git hook 有很多时机,经过思考认为 commit-msg 是比较合适的。

代码实现

从代码实现看看怎么落实这些思路。

通过计算日期得出文件名

year=$(date "+%Y");
sub_dir=$(date "+%Y-%m");
file_name=${project_name}_$(date "+%Y%m")$(expr $(expr $(date "+%d") + $(expr 6 - $(date "+%w")))).txt;

dir_path=test_focus/${year}/${sub_dir}
file_path=${dir_path}/${file_name}

创建文件

创建文件前先判断文件是否存在,不存在的话就创建文件并写入文件头,否则的话就什么都不做。

create_file() {
    tmp_file_header_1="======================%s========================"
    tmp_file_header_2="### begin:%s"
    tmp_file_header_3="### end:%s"

    if [ -f ${file_path} ];then
        echo "File exist."
        else
        echo "Create file"
        mkdir -p ${dir_path}
        touch ${file_path}
        
        var_begin=$(date "+%Y-%m")-$(expr $(expr $(date "+%d") - $(date "+%w")) + 1)
        var_end=$(date "+%Y-%m")-$(expr $(expr $(date "+%d") + $(expr 6 - $(date "+%w"))))

        file_header_1=$(printf $tmp_file_header_1 $var_end)
        file_header_2=$(printf "$tmp_file_header_2" $var_begin)
        file_header_3=$(printf "$tmp_file_header_3" $var_end)

        echo ${file_header_1}>>${file_path}
        echo ${file_header_2}>>${file_path}
        echo ${file_header_3}>>${file_path}
        echo "">>${file_path}
        echo "">>${file_path}
    fi
}

写入测试重点

可以看到代码流程:

  1. 先使用正则从 commit-msg 中得到 ISSUEID
  2. 再用正则从 commit-msg 中获取到代码修改的模块
  3. 最后将信息按照规定的格式添加到文件中
get_issue() {
    issue_str=$(echo "$1" | egrep -E  '(ISSUE#([A-Za-z0-9]+-[0-9]+))' -o)
    str_arry=($(echo $issue_str | awk -F# '{print $1,$2}'))
    echo ${str_arry[1]}
}

get_module() {
    module_str=$(echo "$1" | egrep -E  '((\w+):(\w+))' -o)
    str_arry=($(echo $module_str | awk -F: '{print $1,$2}'))
    echo ${str_arry[1]}
}

add_content() {
    issue=$(get_issue $commit_msg)
    module=$(get_module $commit_msg)
    author=$(git config user.name)

    content_1="### author:$author"
    content_2="### ISSUEID:$issue"
    content_3="### 模块:$module"
    content_4="### 重点:"

    echo "---">>${file_path}
    echo "">>${file_path}
    echo ${content_1}>>${file_path}
    echo ${content_2}>>${file_path}
    echo ${content_3}>>${file_path}
    echo ${content_4}>>${file_path}
    echo "">>${file_path}
}

到这里主要实现就结束了,但是还存在一些瑕疵。

局限性

由于借助了 Git hook 的机制实现,所以存在一些瑕疵:

也正是因为这些瑕疵,导致另一个致命的问题

这个问题有很多解决办法,我采用的是给 commit-msg 中添加标志,如果有这个标志 hook 就执行,否则 hook 不执行。

这样在第一次提交的时候打上标记,生成文件。

填写好重点后,第二次提交用 --amend 命令取出标记,正式提交。

但是上面的两点局限性还是存在的,没有办法避免。

最后

其实这个代码方案对于别人来说可借鉴的意义并不大,分享出来主要是聊聊思路。如果有人遇到别的问题的时候,可以从这里获得一丝丝灵感,那就是最好的。

代码

老规矩,代码在 https://gist.github.com/T-Oner/585b0d321113ac0fd6f42a39817a792f

Responses