Git タグを自動更新 〜マージされるトピックブランチ毎に更新ルールを変える〜

## はじめに
どうも！生産技術部のエンジニアです。Gitのタグを自動更新できる様にGitLab CIに設定してみました。マージされるトピックブランチ毎に更新ルールを変えられるスクリプトを組みましたので紹介します。

GitLabサーバを一から構築される方は、以下からご覧ください。
[「proxy環境下でDocker Composeを用いてCentOS7上にGitLab Dockerを作成」](https://qiita.com/TomoyukiSugiyama/items/ba7ef0ebab8242dbacb0)

## 前提条件
GitLab、GitLabRunnerの導入が実施済みであること。

## 対象のブランチモデル
以下のブランチモデルを利用し、masterブランチにトピックブランチがマージされた時に、タグの更新を行います。利用するブランチモデルについては[ここ][link-mygitflow]を参照ください。

![生技用gitflow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451049/f7cec2e0-c408-5c36-ec54-ea0b2aa64105.png)

Developブランチからマージされた場合とそれ以外のブランチからマージされた場合で分けて、タグの更新ルールを以下の様に設定します。

```bash:
Merge branch 'develop' into 'master'
1.0.0 -> 2.0.0
Merge branch 'hotfix' into 'master'
1.0.0 -> 1.0.1
```

[link-mygitflow]:https://qiita.com/TomoyukiSugiyama/items/f9d1f5bcc0a0a35de903

## 自動更新スクリプトの作成
作成するスクリプトについて紹介します。作成するファイルは、

* GitLab CI用の設定ファイル(`.gitlab-ci.yml`)、
* マージイベント時に、起動するスクリプト(`release.sh`)、
* タグの更新ルールを設定するスクリプト(`version_up.sh`)

の３つです。

### GitLab CIの設定
`.gitlab-ci.yml`には、`release_job`にリリース用のスクリプトを追加し、リリース用のスクリプト内で、タグ更新用のスクリプトを起動します。masterブランチにマージされた際にタグ更新を実施するように、masterブランチのみに対して設定します。

```yaml:.gitlab-ci.yml
stages:
  - develop
  - release
  - hotfix
  
develop_job:
  stage: develop
  script:
    - echo "develop"
  only:
    - develop

release_job:
  stage: release
  before_script:
    - chmod u+x scripts/release.sh 
  script:
    - scripts/release.sh
  only:
    - master

hotfix_job:
  stage: hotfix
  script:
    - echo "hotfix"
  only:
    - hotfix
```
### マージされたトピックブランチを判別
`release.sh`では、マージされたトピックブランチが`develop`かそれ以外かを判別します。マージされた時のコミットメッセージをログ出力し、トピックブランチの名前を切り出しています。`git log --grep "<pattern>"`みたいなのを使えばもう少しスッキリ書けるのかもしれないです。切り出した名前が`develop`だった場合はメジャーアップデート、それ以外の場合はバグフィックスできる様に、`version_up.sh`に`major/bugfix`を引数に与えています。

***下記サンプルは更新されるタグを表示するまでで、実際にタグ付けはしていません。***

```bash:release.sh
#!/bin/bash
chmod u+x scripts/version_up.sh

# マージ時のgit logからトピックブランチのブランチ名を切り出す
merged_branch_name=$(git log --pretty=oneline --abbrev-commit --merges -n 1 | awk '{print $4}' | sed 's/'\''//g')

# ブランチ名がdevelopだった場合はメジャーアップデート、それ以外はバグフィックス
if [ $merged_branch_name = "develop" ]; then
  git tag -l | tail -n 1 | scripts/version_up.sh major
else
  git tag -l | tail -n 1 |  scripts/version_up.sh bugfix
fi
```

### タグの更新ルールを設定
タグの更新スクリプト(`version_up.sh`)は、[神速様の「gitでタグ名に日付を含めているのはダサい」][link-gittag]を参考に作成させて頂きました。更新ルールは、メジャーアップデート、マイナーアップデート、バグフィックスのみとしました。

```bash:version_up.sh
#!/bin/sh
version=`awk '{print $1}' < /dev/stdin`
command=$1
major=`echo $version | awk -F '.' '{print $1}'`
minor=`echo $version | awk -F '.' '{print $2}'`
bugfix=`echo $version | awk -F '.' '{print $3}'`

if [ -z "$version" ]; then
  echo "USAGE: echo 1.0.0 | version_up.sh [major|minor|bugfix]"
  exit 1
fi

if [ "$command" = '' ]; then
    bugfix=`expr $bugfix + 1`
elif [ "$command" = 'major' ]; then
  major=`expr $major + 1`
  minor=0
  bugfix=0
elif [ "$command" = 'minor' ]; then
  minor=`expr $minor + 1`
  bugfix=0
elif [ "$command" = 'bugfix' ]; then
  bugfix=`expr $bugfix + 1`
fi

echo ${major}.${minor}.${bugfix}

```

[link-gittag]:https://sinsoku.hatenablog.com/entry/2014/06/01/195735

## 最後に
タグの自動更新するスクリプトが完成しました。テストに使用したリポジトリは[ここ][link-mygitlab]にあります。もっとスマートなスクリプトがあれば教えてください。

[link-mygitlab]:https://gitlab.com/iyama_yuki/gitlab-ci-scripts

## ご参考
* [神速様の「gitでタグ名に日付を含めているのはダサい」](https://sinsoku.hatenablog.com/entry/2014/06/01/195735)