🦀

【Rust/Github Actions】自動でクレートのバージョンアップをするワークフロー

2024/02/27に公開

はじめに

この記事では、GitHub Actionsを利用して、クレートのバージョン管理を自動化する方法について説明します。

Rust で独自のクレートを構築し公開している場合、適宜 Cargo.toml を更新しバージョンアップのためのコミットを入れる必要があります。場合によって、cargo publish で crates.io に更新をプッシュしたり、タグ打ちを行うケースもあります。このバージョンアップについて、Github Actions で自動化してみましょう。

完成状態

今回構築するワークフローの実装はこちらです。
いくつか特徴をピックアップしておくと、以下のようなツールを利用してワークフローを構築しています。

  • リリースのための変更操作 -> cargo-release crate
  • Git,GitHub への操作 -> Git & GitHub CLI
name: Release
on:
  workflow_dispatch:
    inputs:
      level_or_version:
        description: 'Parameter specifying update version, to use with `cargo release version` ex: `1.0.0`, `patch` etc'
        type: string
env:
  RUST_VERSION: 1.76.0
  DEFAULT_RELEASE_LEVEL: patch
  BASE_BRANCH_NAME: topic/version-up
jobs:
  release:
    runs-on: ubuntu-latest
    env:
      GH_TOKEN: ${{ secrets.PAT }}
    steps:
      - name: Checkout the source code
        uses: actions/checkout@v3
      - name: Install Rust
        run: |
          rustup update ${{ env.RUST_VERSION }} --no-self-update
          rustup default ${{ env.RUST_VERSION }}
      - name: Install cargo-release
        run: cargo install cargo-release --locked
      - name: Run cargo release
        run: |
          git config --global user.name "${{ github.actor }}"
          git config --global user.email "${{ github.actor }}@users.noreply.github.com"
          cargo release version --execute --no-confirm ${{ github.event.inputs.level_or_version || env.DEFAULT_RELEASE_LEVEL }}
          PKG_VER=$(cargo metadata --format-version=1 --no-deps | jq ".packages[0].version" | tr -d '"')
          git checkout -b ${{ env.BASE_BRANCH_NAME }}-$PKG_VER
          cargo release commit --execute --no-confirm
          git push -u origin HEAD
          gh pr create --fill
          gh pr merge --auto

Step By Step

前のセクションで完成したワークフローを展開しましたが、ここでは必要とするパーツごとに解説を加えながら完成系を作る流れを紹介していきます。

リポジトリチェックアウトなどテンプレート部分

まずは、自身のプロジェクトを cargo で操作できる前提部分を構築します。
リポジトリのチェックアウトは actions/checkout@vN で実行し、その後 Rust のインストールを行います。

name: Release
on: # TODO
env:
  RUST_VERSION: 1.76.0
jobs:
  release:
    runs-on: ubuntu-latest
  steps:
    - name: Checkout the source code
      uses: actions/checkout@v3
    - name: Install Rust
      run: |
        rustup update ${{ env.RUST_VERSION }} --no-self-update
        rustup default ${{ env.RUST_VERSION }}

最小限のバージョンアップの操作

バージョンアップのための修正/コミットには cargo-release を使用します

https://github.com/crate-ci/cargo-release

cargo-release とは、cargo プロジェクトのリリースに関する操作をコマンドでサポートするクレートの一つです。バージョンアップのための更新処理をコマンド一つで実行したり、changelogs の生成やタグ打ち、リリースまで実行することができます。

まずは cargo-release をインストールします。

      - name: Install Rust
        run: |
          rustup update ${{ env.RUST_VERSION }} --no-self-update
          rustup default ${{ env.RUST_VERSION }}
+      - name: Install cargo-release
+        run: cargo install cargo-release --locked

次にこの cargo-release を活用して、

    1. バージョンアップのための修正
    1. "1" の変更のコミット
    1. "2" を remote branch にプッシュ

という操作を実施します。

      - name: Install cargo-release
        run: cargo install cargo-release --locked
+      - name: Run cargo release
+        run: |
+          cargo release version --execute --no-confirm patch # 1)
+          cargo release commit --execute --no-confirm # 2)
+          cargo release push --execute --no-confirm # 3)

cargo releasecargo-release クレートを利用します。サブコマンド version で "バージョンアップのための修正" を行い commit で "変更のコミット"、 push で "リモートブランチへのプッシュ" を行います。

最低限実施したいことはこちらで満たすことができていますが、実際に運用しているリポジトリではいくつか問題があります。最たる問題の一つは、この方法ではデフォルトブランチに直接プッシュをしているということです。プライベートではないリポジトリの多くがデフォルトブランチを保護している場合が多くこのままでは利用できないことがほとんどでしょう。次のセクションでは "プルリクエストを介した更新" を行うように修正します。

プルリクエストを介した更新

直接デフォルトブランチにプッシュすることをやめて、プルリクエストを作成する方針とします。GitHub に対する操作のために GitHub CLI を利用します。GitHub Actions の環境では GitHub CLI がプリインストールされていて、特にインストールコマンド等は不要で使用することができます。

GitHub CLI is preinstalled on all GitHub-hosted runners. For each step that uses GitHub CLI, you must set an environment variable called GH_TOKEN to a token with the required scopes.

Source: https://docs.github.com/en/actions/using-workflows/using-github-cli-in-workflows

以下のように修正をしましょう。

  release:
    runs-on: ubuntu-latest
+    env:
+      GH_TOKEN: ${{ secrets.PAT }}
    steps:
...
      - name: Install cargo-release
        run: cargo install cargo-release --locked
      - name: Run cargo release
        run: |
+          git config --global user.name "${{ github.actor }}"
+          git config --global user.email "${{ github.actor }}@users.noreply.github.com"
          cargo release version --execute --no-confirm patch
+          PKG_VER=$(cargo metadata --format-version=1 --no-deps | jq ".packages[0].version" | tr -d '"')
+          git checkout -b topic/version-up-$PKG_VER
          cargo release commit --execute --no-confirm
-          cargo release push --execute --no-confirm
+          git push -u origin HEAD
+          gh pr create --fill
+          gh pr merge --auto

まずは GitHub CLI を使用するために Access Token を環境変数 GH_TOKEN に設定する必要があります。今回はシークレット PAT に設定した Personal Access Token を使用することにします。このアクセストークンを job 全体で利用可能にしておきます。

+    env:
+      GH_TOKEN: ${{ secrets.PAT }}

アクセストークン自体の設定方法は以下を参考にしてください。

Managing your personal access tokens - GitHub Docs
Using secrets in GitHub Actions - GitHub Docs

次に git コマンドを直接利用します。更新のための新規ブランチを作成し、変更をプッシュするまで行います。cargo metadata を利用することで更新後のバージョン情報を取得し、そのバージョン情報をブランチ名称に適用することでブランチの衝突を避けるようにします。

        run: |
+          git config --global user.name "${{ github.actor }}"
+          git config --global user.email "${{ github.actor }}@users.noreply.github.com"
          cargo release version --execute --no-confirm patch
+          PKG_VER=$(cargo metadata --format-version=1 --no-deps | jq ".packages[0].version" | tr -d '"')
+          git checkout -b topic/version-up-$PKG_VER
          cargo release commit --execute --no-confirm
-          cargo release push --execute --no-confirm
+          git push -u origin HEAD

最後に GitHub CLI を利用して、プルリクエストの操作を行います。gh pr create でプルリクエストの作成をします。オプション --fill で自動でタイトルやボディを設定してくれます。

+          gh pr create --fill

gh pr create | GitHub CLI | Take GitHub to the command line

gh pr merge でプルリクエストのマージの操作をすることができます。オプション --auto により、設定された CI の checks が完了したことを確認した上でマージさせることができます。

+          gh pr merge --auto

gh pr merge | GitHub CLI | Take GitHub to the command line

参考: Github Actionsを使ってプルリクエストを自動でマージしたい - そうきたか

改善: パラメータの変数化

ハードコーディングされている固定値の部分に関して、ここで変数を利用するようにします。これによってなんらか更新をしたい場合に修正箇所を容易に把握できるようなり、ワークフローを利用しようとする人に対してもヒントとなり得ます。

env:
  RUST_VERSION: 1.76.0
+  DEFAULT_RELEASE_LEVEL: patch
+  BASE_BRANCH_NAME: topic/version-up
jobs:
  release:
    ...
    steps:
      ...
      - name: Run cargo release
      run: |
        ...
+        cargo release version --execute --no-confirm ${{ env.DEFAULT_RELEASE_LEVEL }}
        ...
+        git checkout -b ${{ env.BASE_BRANCH_NAME }}-$PKG_VER
        ...

改善: workflow_dispatch の利用

今回のワークフローは手動で発火可能に設定することにします。そのために workflow_dispatch イベントを利用します。

https://docs.github.com/en/actions/using-workflows/manually-running-a-workflow

name: Release
+on:
+  workflow_dispatch:
+    inputs:
+      level_or_version:
+        description: 'Parameter specifying update version, to use with `cargo release version` ex: `1.0.0`, `patch` etc'
+        type: string

また inputs パラメータを使用することで、実行時にパラメータ指定できるようにしています。今回は level_or_version でバージョンをどのように更新できるか string 型で指定できるようにしています。このパラメータに設定された値は下記のように job 側で使用できるようにしましょう。

- cargo release version --execute --no-confirm ${{ env.DEFAULT_RELEASE_LEVEL }}
+ cargo release version --execute --no-confirm ${{ github.event.inputs.level_or_version || env.DEFAULT_RELEASE_LEVEL }}

終わりに

この記事では、Rust プロジェクトでのバージョン管理を自動化するための GitHub Actions ワークフローを実装する方法について説明しました。ワークフローの構築には cargo-release crate や GitHub CLI を活用しました。

このように GitHub Actions を使用してクレートのバージョン管理を自動化することで、これらにかかる時間をクレートの開発に回すことができると思います!

Discussion