semantic-release + git-cz でライブラリを良い感じに Semantic Versioning する

手動でバージョン管理して npm publish するのが手間なので、eslint-plugin-atomic-design の作業ついでに、以前より気になってた semantic-release を導入しました。

適切なフォーマットで書かれた commit log を見て、変更に応じた Semantic Versioning を自動で行います。 release や(npm package なら)npm publish する際に自力で計算する必要がなくなるので、

  • npm version + npm publish
  • GitHub の Release
  • CHANGELOG.md 生成

上記の様な release 作業自体を CI 連携することで完全自動化できます。また、これに付随して適切な commit をするモチベになります。OSS やるなら(たとえ committer が自分ひとりでも)入れたほうが圧倒的に良いでしょう。

GitHub release

*OSS リポジトリで見たことある感じの、ちゃんとした GitHub release

導入と設定も簡単で、semantic-release-cli を使えば npmGitHub で必要な Token を発行・取得して (Circle | Travis) CI の環境変数に設定までやってくれます。

で、それを試したら CircleCI に登録するところでコケて死んだので、自力で各サービスの Token 再取得・再生性して環境変数に突っ込み .circleci/config.yml 追記して。結局手作業になりました。紹介しておいて台無しですが、大した手間じゃないんではじめから手作業でいいです。

プラグイン毎に細かい設定もできるのですが、

package.json
{
  "release": {
    "plugins": [
      "@semantic-release/commit-analyzer",
      "@semantic-release/release-notes-generator",
      "@semantic-release/changelog",
      "@semantic-release/github",
      "@semantic-release/npm",
      "@semantic-release/git"
    ]
  }
}

今のところはただ並べただけでいけてます。

機械的にバージョン管理する仕組み上、Conventional commit messages が必須要件になります。決まったフォーマット message をいちいち手書きするのは、ミスったときのことを考えると消耗するので、素直に commitizen で導入した git-cz を使うようにしました。特に不便がなく、開発体験が良くなってます。おすすめです。


導入方法

せっかくなんで導入方法書いときます。

1. semantic-release の準備

semantic-release をプロジェクトの devDependencies に install

$ npm i -D semantic-release

Plugin の導入

Plugin list をみて、やりたいことに応じた Token なりを用意します。

ほかにも色々あります。

  • @semantic-release/exec

    • 任意の step で任意のシェルコマンドを実行できる
    • 正味これ使えば npm でも GitHub などリポジトリ等関係ない操作もできる

Community plugins みると、リリース先の対象が Chrome Web Store - Extension(わかる)、DockerHub(!)、Maven central(!!!!?!?!??!!!) …色々あります。

いくつかの Plugin は依存関係あるので注意。

各連携サービスの Token 取得 & CI の環境変数に設定

上記で導入した Plugin ごとに必要な Token を用意して、利用する CI 側の環境変数に設定します。必要な Token と環境変数名は 各 Plugin のリポジトリの README.md を見てください。

CI の設定

あとは、CI 側で npx semantic-release を実行するように job の設定をするだけです。

下記は CircleCI の例で、 master の変更を test して、通ったら release するようにしてます。 この辺、 release branch で運用するなりはお好みで。

.circleci/config.yml
# https://github.com/RyoNkmr/eslint-plugin-atomic-design/blob/e3a0fc5646addb1025892d0e4e635306bb51ba44/.circleci/config.yml
# 略
  release:
    <<: *defaults
    steps:
      - checkout
      - run: npm ci
      - run: npx semantic-release

workflows:
  version: 2
  test_and_release:
    jobs:
      - test
      - release:
          requires:
            - test
          filters:
            branches:
              only: master

2. git-cz の導入と設定

commitizen (git-cz CLI tool) を global install する

commit のフォーマットを作るのは git-cz で、commitizen 自体はその導入と設定を楽にする CLI tool です。これを global install しておきます。

$ npm install -g commitizen

プロジェクトを commitizen friendly にする

  • もしプロジェクトが npm を使ってるなら:
    commitizen init cz-conventional-changelog --save-dev --save-exact
  • yarn を使ってるなら:
    commitizen init cz-conventional-changelog --yarn --dev --exact

やってることは

  1. devDependenciescz-conventional-changelog adapter を追加 & install
  2. package.jsonconfig.commitizen に上記 adapter への path を通す

これで、 git cz すると対話的 UI で commit できるようになります。

利用する

git add した後に git commit する代わりに git cz します。下記のようないい感じのやつがでるはずです:

$ git cz
cz-cli@3.1.1, cz-conventional-changelog@2.1.0


Line 1 will be cropped at 100 characters. All other lines will be wrapped after 100 characters.

? Select the type of change that you're committing: (Use arrow keys)
❯ feat:     A new feature
  fix:      A bug fix
  docs:     Documentation only changes
  style:    Changes that do not affect the meaning of the code (white-space, formatting, missin
g semi-colons, etc)
  refactor: A code change that neither fixes a bug nor adds a feature
  perf:     A code change that improves performance
(Move up and down to reveal more choices)

@ymmoootnuxt-jsonld 手伝ってて気づいたんですが、OSS にするなら git commit hook したいので設定すると吉っぽいです。

git hook できれば何でも良いんですが、ここでは husky を使います。

$ npm i -D husky

あとは設定するだけ

packge.json
  "husky": {
    "hooks": {
      "prepare-commit-msg": "exec < /dev/tty && git cz --hook"
    }
  }

もしくは、
直接 .git/hooks/prepare-commit-msg を書いても OK

.git/hooks/prepare-commit-msg
#!/bin/bash
exec < /dev/tty
npx git-cz --hook

実行権限をつけるのを忘れずに

$ chmod 755 .git/hooks/prepare-commit-msg

あとは普通に git commit すると git cz が勝手に発動します。

余談

  • 設定足りなくて CHANGELOG.md 生成したのに push し損ねててはずかしい

    • 足したから多分次回 release でなおるはず。試さないとわからん
  • semantic-release-cliinquirer てやつ使って yeoman みたいなのやってる

    • って調べたら yeoman 自体 inquirer 使ってた
  • グッパイ平成、ハロー令和