GitHub Actions と GitHub Packages を使って、継続的に社内向け gem をリリースする
こんにちは、 syu_cream です。普段は Ubie でプロダクト開発エンジニアをやっています。本日は業務で構築した社内向け gem の継続的なリリースを行う仕組みを紹介します。 GitHub Actions & Packages の組み合わせは問題にハマると原因究明が難しいことがあり、また意外にも世の中に情報が乏しいように思えます。この情報がひとつの参考になればと思います。
はじめに
Ubie では、以下記事で紹介しているように新規に開発するサービスを Go か Node.js で、通信プロトコルを GraphQL か gRPC で実装する方針を進めています。
最近、僕の所属チームでも新規サービスを追加したい状況に直面し、上記記事の基準を加味して NestJS による gRPC サーバを実装することにしました。現在 Ubie では Protocol Buffers による gRPC のサービスやメッセージの定義と付随するコード生成等のエコシステムは中央集権的なリポジトリが担っています。ここで NestJS で使いやすいコードは既に GitHub Packages で社内向け npm パッケージとして利用可能になっていました。しかし今回開発する gRPC サーバに対する想定クライアントとして昔から存在する Ruby on Rails 実装の REST API サーバがあり、他言語に従い Ruby 向けのコード生成も行いたくなりました。
このような経緯があり、本記事タイトルの「GItHub Packages を使って社内向け gem をリリースする」のを、属人性を排除しつつ簡素な構成で行うため GitHub Actions で完結させたい次第になりました。
GitHub Actions から社内向け gem をリリースする
今回まず Protocol Buffers, gRPC のコード生成をする必要があったのですが、ここは Buf を使いあまりハマることなく実現することができました。以下ページを参考に、 Ruby 向けの gRPC/Protocol Buffers プラグインを導入して buf generate するだけです(まだ複雑なことをしていないが故に問題にハマってないだけの可能性は否めないですが)
本題の gem のリリースですが、基本的には以下ドキュメントに従うことになります。
まず gemspec を用意します。(一部置換を含みつつ抜粋)今回 GitHub のリリースタグからバージョンコントロールしたいので、環境変数から注入可能にしています。また allowed_push_host
メタデータは gem push する先にを指定しておきます。 lib/**/*.rb
には buf generate で生成されたコードが配置されている想定です。
Gem::Specification.new do |spec| spec.name = "..." spec.version = ENV["PACKAGE_VERSION"] spec.authors = ["..."] spec.summary = "" spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0") spec.files = Dir["Gemfile", "lib/**/*.rb"] spec.require_paths = ["lib"] spec.metadata["allowed_push_host"] = "https://rubygems.pkg.github.com/<namespace>" spec.add_runtime_dependency "grpc", "~> 1.x" end
次に GitHub Actions のワークフローを組みます。実際に作成したものは以下のようなものになります。(一部置換を含みつつ抜粋)
gem push をするにあたり Actions に Packages に書き込みを行う権限が必要です。リポジトリの設定 ( Settings > Actions > General > Workflow permissions ) で書き込み権限が付与されているかワークフローの設定で permissions.packages: write
が必要になるはずです。 また公式ドキュメントを参考に gem push する際の認証情報を渡します。
ruby: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Setup Buf uses: bufbuild/buf-setup-action@v1 with: github_token: ${{ github.token }} - name: Setup Ruby uses: ruby/setup-ruby@v1 with: ruby-version: '2.7' bundler-cache: true - name: Generate code run: | buf generate .. - name: "Define PACKAGE_VERSION" run: | echo "PACKAGE_VERSION=${{ github.event.release.tag_name }}" >> $GITHUB_ENV - name: Build run: | gem build <gem name>.gemspec - name: Publish artifact run: | echo ":github: Bearer ${GITHUB_TOKEN}" >> ~/.gem/credentials chmod 600 ~/.gem/credentials gem push --key github --host https://rubygems.pkg.github.com/<namespace> <gem name>-${{env.PACKAGE_VERSION}}.gem env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
このワークフローではそのために明示的に PAT 払い出しをする必要がなく、 GITHUB_TOKEN で簡潔に構成できます。 それゆえ GITHUB_TOKEN 起因の認証エラーにハマることがあるかもしれませんが、トークンの渡し方や付与されている権限を確認しながらトラブルシュートしていくことが求められます。
社内向け gem を利用する
gem がリリースされたら、利用する側も作り込んで行きます。これも基本的に公式ドキュメントにあるように、 Gemfile にリリースされた gem への依存を書いて
source "https://rubygems.pkg.github.com/<namespace>" do gem "<gem name>", "<version>" end
bundle config で source にアクセスする時の認証情報をセットしておきます。これで bundle install でリリースされた社内向け gem をインストールできます。
$ bundle config https://rubygems.pkg.github.com/<namespace> <GitHub のユーザ名>:<GitHub の PAT>
このやり方は GitHub Actions 上で行いたい時障壁になるかもしれません。その場合は環境変数を介して渡すことも可能なようです。
- uses: ruby/setup-ruby@v1 with: ruby-version: ${{ env.RUBY_VERSION }} bundler-cache: true env: BUNDLE_RUBYGEMS__PKG__GITHUB__COM: ${{ secrets.GITHUB_TOKEN }}
おわりに
というわけで社内向け gem を GitHub Actions & Packages でリリース・利用する事例について紹介しました。 GitHub Actions、 Packages は上手く使えれば簡潔で使いやすい開発基盤を構築できると思うのですが、問題にハマるとストレスフルな状況になりがちかなと思います。本記事がひとつの事例としてお役に立てば幸いです。