4  versioning with GitHub Actions

Author

Ryo Nakagami

Published

2024-12-16

Modified

2024-12-24

Case 1: Poetry-managed Packageにおけるgit tag releaseの紹介

パッケージ開発をしているGitHub repositoryにおいて,mainへのPull Requestがmergedされたタイミングでgit tagを作成するためのワークフローを紹介します.

workflow descriptions

今回設定するworkflow gittag-release.yml は以下のような内容とします

  • トリガータイミング:
    • mainへのPull RequestがmergedされたタイミングでGitHub Actionsがキックする
  • git tagの参照先:
    • pyproject.tomlversionフィールドに記載されたsemantic versionをベースにgit tagを作成
    • すでに同一のgit tagが作成されている場合は,Tag v$PYPROJECT_VERSION already exists. No release needed.というメッセージがGitHub Actions logに表示される
    • mainへのPull Request作成時点でpyproject.tomlversionフィールドはアップデートされているとする
  • Release note:
    • PRの内容をベースにリリースノートを作成する

workflow file

上記の動作条件を満たすGitHub Actions workflow設定例として以下です.

gittag-release.yml
name: Git tag release of poetry-managed package

on:
  pull_request:
    branches: 
      - main
    types:
      - closed

jobs:
  release:
    if: >
        github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    steps:
      - name: Checkout main branch
        uses: actions/checkout@v4
        with:
          ref: main
          fetch-depth: 0
      
      - name: Install poetry
        uses: abatilo/actions-poetry@v2
      
      - name: Get Poetry version
        run: poetry --version
      
      # Made use of $GITHUB_ENV to pass the variable cleanly to subsequent steps.
      - name: Fetch version info from pyproject.toml
        run: |
          PYPROJECT_VERSION=$(poetry version --short)
          echo "PYPROJECT_VERSION=$PYPROJECT_VERSION" >> $GITHUB_ENV
          echo "The current pyproject version is v$PYPROJECT_VERSION"

      
      - name: Create Git Tag and GitHub release
        run: |
          TAG=$(git describe HEAD --tags --abbrev=0 2>/dev/null || echo "none")

          if [[ "$TAG" != "v$PYPROJECT_VERSION" ]]; then
            echo "Creating a new release with tag v$PYPROJECT_VERSION..."
            gh release create "v"$PYPROJECT_VERSION --generate-notes
          else
            echo "Tag v$PYPROJECT_VERSION already exists. No release needed."
          fi
          
        env:
          GH_TOKEN: ${{ github.token }}

gittag-release.ymlの解説

▶  on セクション

on:
  pull_request:
    branches: 
      - main
    types:
      - closed

で使用されているonはworkflowが開始するタイミングを記述するセクションとなります.push時のみというシングルイベントの場合は

on: push

と記述しますが,forkpushという2つのタイミングでキックしたい場合は

on: [push, fork]

と指定できます.今回は,

  • mainへのPull RequestがmergedされたタイミングでGitHub Actionsがキックする

というキック条件にしたいので,

条件 設定内容
pushではなく,pull request eventのタイミングでキック on: pull_requestと設定
branchはmainのみ branches:mainのみを指定
PRがclosedかつmergeされたタイミングでキックされる types: closedを指定

pull_requestpushといったeventに応じて設定できるtypesは異なってきます.eventに応じた指定可能typesGitHub Actions > Events that trigger workflowsを参照してください.

▶  Pull Request Mergedタイミングへの限定

workflowのキックタイミングは on: pull_requeststypes: closedで指定しましたが,これではPRがmergedされないでclosedされた場合も動いてしまいます.

  • PRがclosedかつmergeされたタイミングでキックされる

ことを確保するためには,jobsセクションで条件を絞る必要があります.その設定箇所が以下となります.

jobs:
  release:
    if: >
        github.event.pull_request.merged == true

if セクションでjobsが実行される条件をより細かく指定することができます.今回はPR mergedのみとしたいので github.event.pull_request.merged == trueと設定します,

if: >は改行を許容する形での記述のため(=今回は可読性のため)に使用したSyntaxとなりますが,実質的には

jobs
  release:
    if: github.event.pull_request.merged == true

でも同じ動作をします.

▶  fetch-depthの設定

fetch-depthはGitHub Actions workflowsがfetchするcommit logの数を指定する項目です. すべてのcommit logをfetchした上でjobが動作する必要がある場合には,featch-depth: 0と設定します.

基本的には,featch-depth: 1で十分ですが,過去のcommitを殺傷してchangelogを作成したい場合はfeatch-depth: 0と設定したりします. 今回は,gh release create --generate-notesコマンドを用いて,last releaseから最新のreleaseまでの差分commitをベースにリリースノートを作成したいので featch-depth: 0と設定します.

▶  pyproject.tomlから最新のsemantic versionを取得

poetry version --shortコマンドはpyproject.tomlのversion fieldを参照してsemantic versionを返すコマンドです. このコマンドを使用するため,jobsでpoetry setupを実行する必要があります.その設定箇所が以下です:

      - name: Install poetry
        uses: abatilo/actions-poetry@v2

その後,poetry version --short)を実行します.このとき,以下のように echo "PYPROJECT_VERSION=$PYPROJECT_VERSION" >> $GITHUB_ENVを忘れずに設定してください.

      # Made use of $GITHUB_ENV to pass the variable cleanly to subsequent steps.
      - name: Fetch version info from pyproject.toml
        run: |
          PYPROJECT_VERSION=$(poetry version --short)
          echo "PYPROJECT_VERSION=$PYPROJECT_VERSION" >> $GITHUB_ENV
          echo "The current pyproject version is v$PYPROJECT_VERSION"

ワークフロージョブ内で環境変数を後続のステップで利用可能にするには,その環境変数を定義し,その値をGITHUB_ENV環境ファイルに書き込む必要があります. 注意点として,デフォルトの環境変数である GITHUB_*RUNNER_* の値を上書きすることはできません.

▶  git tagの作成

GitHub CLIコマンドのgh release create <git-tag-version>を用いることで

  • <git-tag-version>のgit tag作成 & GitHub上でのRelease
  • --generate-notesオプションを利用することで,最後のreleaseからのcommitをベースにrelease noteを作成してくれます

このとき,GitHub repositoryへ直接git tagをpushする関係上,同一ステップ内部で

      env:
          GH_TOKEN: ${{ github.token }}

を設定する必要があります.github.tokenはデフォルトで設定されているpropertyで,リポジトリにインストールされた GitHub App の代わりに認証を受けるためのトークンです. デフォルト設定のまま実行してしまうとResource not accessible by integration errorに直面してしまいますが,こちらのノートの設定に従って 該当レポジトリのWorkflow permissionsにて,

  • Read and write permissions

のレベルを選択する必要がある点に注意してください.

References