1#!/bin/bash 2## Author LinkinStar / HermanChen 3 4# solve the space by IFS 5OLDIFS=$IFS 6IFS=`echo -en "\n\b"` 7echo -en $IFS 8 9declare -g changelog_file="CHANGELOG.md" 10declare -g git_repo_rootdir 11declare -g current_tag 12declare -g previous_tag 13declare -g prev_changelog 14declare -g prev_changelog_version 15declare -g prev_tag 16declare -g curr_tag 17 18# changelog version define 19declare -g version_tag 20 21function log_filter_and_print() { 22 local title=$1 23 local prefix=$2 24 local -a msgs=() 25 local msg_cnt=0 26 local prefix_len=${#prefix} 27 28 # filter log and find the matched prefix number 29 for msg in ${orig_logs[@]} 30 do 31 if [[ $msg == "$prefix"* ]]; then 32 msgs[msg_cnt]=$msg 33 let msg_cnt++ 34 35 echo "$prefix commit $msg_cnt - ${msg}" 36 fi 37 done 38 39 if [ $msg_cnt -gt 0 ]; then 40 echo -e "### $title" >> ${changelog_file} 41 42 for msg in ${msgs[@]} 43 do 44 # check log mode 45 local pos=$prefix_len 46 local log 47 48 # message mode: prefix: xxxx 49 log=$(echo $msg | grep -i "^$prefix:") 50 #echo "pos $pos log $log" 51 if [ "$log" != "" ]; then 52 let pos++ 53 fi 54 55 # remove extra space 56 log=$(echo ${msg:$pos} | sed -e 's/^[ ]*//g' | sed -e 's/[ ]*$//g') 57 echo -e "- $log" >> ${changelog_file} 58 done 59 60 echo -e "" >> ${changelog_file} 61 fi 62} 63 64usage="changelog helper script 65 use -t / --version to create a new tag commit and update CHANGELOG.md 66 by insert new version on top, 67 68 the basic commit message should be structured as follows: 69 <type>[optional scope]: <description> 70 [optional body] 71 [optional footer(s)] 72 73 Type 74 Must be one of the following: 75 feat: A new feature 76 fix: A bug fix 77 docs: Documentation only changes 78 style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) 79 refactor: A code change that neither fixes a bug nor adds a feature 80 perf: A code change that improves performance 81 test: Adding missing or correcting existing tests 82 chore: Changes to the build process or auxiliary tools and libraries such as documentation generation 83 84 https://www.conventionalcommits.org/zh-hans/v1.0.0/ 85 86 .option('-t, --version', 'create a version tag with changelog update') 87 .option('-f, --file [file]', 'file to write to, defaults to ./CHANGELOG.md, use - for stdout', './CHANGELOG.md') 88 .option('-u, --repo-url [url]', 'specify the repo URL for commit links') 89" 90 91while [ $# -gt 0 ]; do 92 case $1 in 93 --help | -h) 94 echo "$usage" 95 exit 1 96 ;; 97 --version | -t) 98 version_tag=$2 99 shift 100 ;; 101 --file | -f) 102 changelog_file=$2 103 echo "save changelog to file ${changelog_file}" 104 shift 105 ;; 106 --repo-url | -u) 107 git_repo_rootdir=$2 108 echo "set repo rootdir to ${git_repo_rootdir}" 109 shift 110 ;; 111 *) 112 # skip invalid option 113 shift 114 ;; 115 esac 116 shift 117done 118 119# check git command 120if ! command -v git >/dev/null; then 121 >&2 echo "ERROR: git command is not available" 122 echo "$usage" 123 exit 1 124fi 125 126# if repo root is not set set sefault repo root 127if [ -z $git_repo_rootdir ]; then 128 git_repo_rootdir=$(git rev-parse --show-toplevel 2>/dev/null) 129fi 130 131# check repo root valid or not 132if [ -z ${git_repo_rootdir} ] || [ ! -d ${git_repo_rootdir} ]; then 133 echo "ERROR: can not found repo root" 134 echo "$usage" 135 exit 1 136fi 137 138# cd to rootdir 139cd $git_repo_rootdir 140 141# check version tag exist or not 142if [ -z ${version_tag} ]; then 143 echo "ERROR: can not run without a new version tag" 144 echo "$usage" 145 exit 1 146fi 147 148tag_log=$(git tag -v ${version_tag} | grep object) 149if [ ! -z ${tag_log} ]; then 150 echo "ERROR: can not run with an existing tag ${version_tag}" 151 echo "$usage" 152 exit 1 153fi 154 155# create new tag with empty commit 156git commit --allow-empty -s -m "docs: Update ${version_tag} CHANGELOG.md" 157tag_date=`date '+%Y-%m-%d'` 158git tag ${version_tag} -m "${version_tag} version at ${tag_date}" 159 160# if changelog file is not exist then create it 161if [[ ! -f ${changelog_file} ]]; then 162 touch ${changelog_file} 163else 164 # get old changelog 165 prev_changelog=$(cat $changelog_file) 166 prev_changelog_version=$(echo $prev_changelog | grep -E "## *.*.* *" | head -1 | awk '{ print $2 }') 167 168 rm -rf ${changelog_file} 169 touch ${changelog_file} 170fi 171 172# get current version info 173curr_ver=$(git describe --tags --long) 174curr_tag=$(git describe --tags --abbrev=0) 175prev_tag=$(git describe --tags --abbrev=0 "$curr_tag"^) 176date="$(date '+%Y-%m-%d')" 177 178echo "curr ver: ${curr_ver}" 179echo "curr tag: ${curr_tag}" 180echo "prev tag: ${prev_tag}" 181 182# dump log between two tags 183orig_logs=$(git log --pretty="%s" --no-merges $curr_tag...$prev_tag) 184echo "commits: from ${prev_tag} to ${curr_tag}" 185echo "${orig_logs}" 186 187# check unreleased version number 188unreleased_count=$(echo ${curr_ver} | grep -E "## *.*.* *" | head -1 | awk -F "-" '{ printf $2 }') 189echo "unreleased version count: ${unreleased_count}" 190 191# write unreleased information 192if [ "$unreleased_count" ] && [ $unreleased_count -gt 0 ]; then 193 echo -e "## Unreleased (${date})\n" >> ${changelog_file} 194 195 unreleased_logs=$(git log --pretty="%s" --no-merges HEAD...$curr_tag) 196 for msg in ${unreleased_logs[@]} 197 do 198 echo -e "- ${msg}" >> ${changelog_file} 199 done 200 201 echo -e "" >> ${changelog_file} 202else 203 echo -e "## $curr_tag ($date)" >> ${changelog_file} 204fi 205 206# write taged information 207log_filter_and_print "Feature" "feat" 208log_filter_and_print "Fix" "fix" 209log_filter_and_print "Docs" "docs" 210log_filter_and_print "Style" "Style" 211log_filter_and_print "Refactor" "refactor" 212log_filter_and_print "Perf" "perf" 213log_filter_and_print "Test" "test" 214log_filter_and_print "Chore" "chore" 215 216echo -e "${prev_changelog}" >> ${changelog_file} 217 218# add CHANGELOG.md 219git add ${changelog_file} 220git commit --amend -C ${curr_ver} 221# update tag message 222git tag -f ${version_tag} -m "${version_tag} version at ${tag_date}" 223 224IFS=$OLDIFS 225