awk 处理 Git 提交信息生成 Release Note

Posted 1bite

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了awk 处理 Git 提交信息生成 Release Note相关的知识,希望对你有一定的参考价值。

发布软件时通常都会写 Release Note,但每次从头手打也有点累,可以考虑从 Git 的提交历史中自动生成。本文将分享一些脚本,帮助处理 Git 提交历史并自动生成 Release Note 追加到 changelog.md 中。

发布软件时通常都会写 Release Note,但每次从头手打也有点累,可以考虑从 Git 的提交历史中自动生成。

Git 提交信息一般是三段式结构,段落之间使用空行隔开:

<subject>
// 空行
<body>
// 空行
<trailers>

subjectbody 自不必多说,trailers 中一般是一条条键 -- 值对构成的信息,键和值由冒号 : 分隔。比如 Git 的 Commiter: 就可以视为一条 trailer,Gerrit 代码审查通过后,也会在 Git 提交消息中添加 Reviewed-by:Tested-by: 之类的 trailer

现假设提交信息中的 trailers 包含 TypeIssue 字段,分别表示类别(NewFeature, BugFix 之类)和 BUG 编号(GitHub 上的 issue 编号或者内部系统的 BUG 编号):

Fixed a crash

- blahblah
- blahblahblah

Type: BugFix
Issue: issue-120

就可以结合 git logawk 生成 Rlease Note:

git log --format=\'%(trailers:key=Type,valueonly,separator=%x20)%x1c%s [%(trailers:key=Issue,valueonly,separator=,)]\' $FROM..HEAD | awk -F "\\034" \'
    
        gsub(/\\[\\]$/, "", $2);
        if (length($1) == 0) $1="MISC";
        if (!($1 in item_count)) item_count[$1]=0;
        items[$1, item_count[$1]]=$2;
        item_count[$1]++;
    
    END 
        for (k in item_count) 
            print k;
            k2=k;
            gsub(/./, "-", k2);
            print k2
            for (i=0; i<item_count[k]; i++) print "* "items[k, i];
            print ""
        
    \'

输出:

BugFix
------
* Fixed a typo
* Fixed a crash [issue-120]

NewFeature
----------
* Added some magic

选择使用 awk 处理 git log 的输出是因为 linux 跟 macOS 都带这个软件,不需要额外安装依赖。
Git 2.22.0 及以上版本才支持按键值筛选 trailers

在 Windows 系统上,可能没有办法直接运行 .sh 文件。但是如果安装了 Windows 版本的 Git,则会带一个 Git Bash(也带了 awk!),这样就可以在 Git Bash 中运行 .sh 文件,也可以创建一个 .cmd 文件包装一下:

@ECHO off
SETLOCAL

CALL :FIND-PATH GITEXE_PATH git.exe
IF "%GITEXE_PATH%"=="" (
    ECHO git.exe is not found on your system
    EXIT /B 2
)

CALL :GET-DIR-NAME GITEXE_DIR "%GITEXE_PATH%"
CALL :GET-DIR-NAME GITEXE_DIR "%GITEXE_DIR:~0,-1%"

@"%GITEXE_DIR%bin\\bash.exe" release-note.sh %*

:FIND-PATH
SET "%~1=%~f$PATH:2"
EXIT /B

:GET-DIR-NAME
SET "%~1=%~dp2"
EXIT /B

上面的脚本只能把 release note 生成出来,如果可以直接添加到 changelog.md 就好了,可以把脚本做如下改动。

一,添加版本信息。添加一个变量 PROJ_VER 表示当前项目版本,以 maven 项目为例:

PROJ_VER=`./mvnw help:evaluate -Dexpression=\'project.version\' -q -DforceStdout|tail -n 1`

然后将这个变量暴露给 awk,在 awk 命令后面添加 -v PROJ_VER PROJ_VER 即可。

二,将版本和日期打印出来。在 awk 程序中添加 BEGIN 块:

BEGIN 
  "date +%Y-%m-%d"|getline date;
  if (PROJ_VER == "") print "# "date;
  else print "# "PROJ_VER" "date;

三,在 change.md 头部追加生成的内容:

# 生成临时文件
tempf=`mktemp $TEMP/changelog.md.XXXX`
touch changelog.md

# 将生成的内容与原 changelog.md 中的内容合并,保存到临时文件中,然后用临时文件替换当前文件即可
... | cat - changelog.md > $tempf && mv $tempf changelog.md

这样一顿操作下来,生成的 changelog.md 如下:

# 1.2.3 2023-03-31
BugFix
------
* Fixed a typo
* Fixed a crash [issue-120]

NewFeature
----------
* Added some magic

gradle中自动生成git提交信息

maven有一个git-commit-id插件,可以在打包的时候自动生成git commit相关的信息。对于查看版本是否发布正常非常有意义。
很遗憾gradle却没有。只有自己动手了。

  1. 首先编写一个gradle文件version-commit.gradle,定义一个gradle的task用于生成git信息,使用默认任务的方式defaultTasks或者在processResources任务之后执行。因为processResources就是专门处理资源文件的,所以生成git信息的文件在processResources之后生成就很合理。
import java.text.SimpleDateFormat

//使用默认task的方式嵌入
//defaultTasks 'versionCommit'
task("versionCommit")
    versionCommit()

//在processResources之后执行
processResources.finalizedBy versionCommit

static def versionCommit() 
    Properties p = new Properties()
    p.put("branch", getGitBranch())
    p.put("commitId", getGitCommit())
    p.put("commitAuthor", getGitCommitAuthor())
    p.put("commitTime", getGitCommitTime())
    p.put("packageTime", getCurrentTime())
    p.store(new FileWriter("src/main/resources/version-commit.properties"), "generated by gradle git info plugin")

static def getGitCommit() 
    return 'git rev-parse --short HEAD'.execute().text.trim()


static def getGitCommitAuthor() 
    String commitId = getGitCommit();
    String cmd = "git log --pretty=format:%an $commitId -1";
    return cmd.execute().text.trim()

static def getGitCommitTime() 
    String commitId = getGitCommit();
    String cmd = "git log --pretty=format:%ai $commitId -1";
    return cmd.execute().text.trim();

static def getGitBranch() 
    return (System.getProperty("version") ?: "1.0-SNAPSHOT").replace("origin/", "")

static def getCurrentTime() 
    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())



  1. 然后在build.gradle中引入,在打包的时候就会在类路径下生成version-commit.properties,保存最后一次提交相关的信息。
apply from: "$rootDir/version-commit.gradle"
  1. 然后在项目启动的时候读取version-commit.properties再打印出来即可。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.util.Properties;

/**
 * 记录版本,每次发版打包的时候自动生成git相关信息
 * gradle 生成git信息保存在类路径的version-commit.txt中
 * @see https://blog.csdn.net/xxssyyyyssxx/article/details/128530853?spm=1001.2014.3001.5501
 */
public class VersionCommit 
    private static final Logger logger = LoggerFactory.getLogger(VersionCommit.class);
    private static final String VERSION_COMMIT_FILE_NAME = "version-commit.properties";

    //已实现的这些字段,还可以动态添加其他字段
    public static final String BRANCH        = "branch";
    public static final String COMMIT_ID     = "commitId";
    public static final String COMMIT_AUTHOR = "commitAuthor";
    public static final String COMMIT_TIME   = "commitTime";
    public static final String PACKAGE_TIME  = "packageTime";

    public static Map<String, String> fromFile()
        Properties v = getVersionCommitInfo();
        if(null != v)
            HashMap<String, String> map = new HashMap<>(v.size());
            for (String n : v.stringPropertyNames()) 
                map.put(n, v.getProperty(n));
            
            return map;
        
        return null;
    
    public static void print()
        try 
            Map<String, String> map = fromFile();
            if(null != map)
                logger.info("start up successfully with version-commit: \\r\\n ", map);
            
         catch (Exception e) 
            logger.error(e.getMessage(), e);
        
    

    private static Properties getVersionCommitInfo()
        String fileName = getCommitInfoFileName();
        try (InputStream inputStream = VersionCommit.class.getClassLoader().getResourceAsStream(fileName);
        ) 
            Properties properties = new Properties();
            properties.load(inputStream);
            return properties;
        catch (Exception e)
            //有异常的时候返回null
            return null;
        
    

    /**
     * 给一个修改的机会
     */
    private static String getCommitInfoFileName() 
        String fileName = VERSION_COMMIT_FILE_NAME;
        String v = System.getProperty("version.commit.file.name");
        if(StrUtil.isNotEmpty(v))
            fileName = v;
        
        return fileName;
    



效果如下:

start up successfully with version-commit: 
 commitId=9ca343594, commitTime=2023-01-01 17:00:00 +0800, branch=feature-123, packageTime=2023-01-01 18:00:00, commitAuthor=someone

  1. 如果还有其他信息需要输入到version-commit.properties中,在versionCommit方法中加入即可。

以上是关于awk 处理 Git 提交信息生成 Release Note的主要内容,如果未能解决你的问题,请参考以下文章

gradle中自动生成git提交信息

文本处理三剑客之awk(报告生成器)

linux命令之awk

如何自动接受 git 的默认提交信息?

更改 git 标签的日期(或基于它的 GitHub Release)

在 TypeScript 中编码时,生成的 .js 文件是不是应该提交给 git?