📝

JavaScript ActionからDocker Container Actionへの移行

2025/02/20に公開

はじめに

もともとTypeScriptで書かれていたJavaScript Actionをメンテしていたのですが、モチベがなくなっていき、1年ほど放置していました。
ただ、最近読み終わったGitHub CI/CD実践ガイドという本でDocker Container Actionを知ったので、Rustで実装しなおしてみました。
この記事では、どのようにDocker Container Actionを実装するかについてや、ビルドしたDockerイメージの保存先であるGitHub Container Registryを使う方法などを解説していきます。

今回移行したリポジトリは以下となります。

https://github.com/Takashicc/slack-approval

Actionの自作について

https://docs.github.com/en/actions/sharing-automations/creating-actions/about-custom-actions

Actionを自作する方法はいくつかの方法があり、現在は以下の3つの方法があります。

  • Composite Action
  • JavaScript Action
  • Docker container Action

元々使っていたものはJavaScript Actionで、ロジックをTypeScriptで書いてそれをJavaScriptにトランスパイルして、出来上がったものをエントリーポイントと設定していました。

action.yml
runs:
  using: 'node20'
  main: 'dist/index.js'

これに比べてDocker Container Actionは、DockerfileまたはDockerイメージをエントリーポイントとして設定します。

action.yml
runs:
  using: "docker"
  image: "docker://ghcr.io/takashicc/slack-approval:2.0.0" # or image: Dockerfile

Docker Container Actionは、どんな言語でも書けるメリットがありますが、デメリットがいくつかあります。

  • imageにDockerfileを指定した場合、アクションを使う側が毎回Dockerfileビルドから行うため、ビルド時間が長い場合はDockerイメージを指定させたほうがいいです。
  • Dockerイメージを指定させる場合も、GitHub Runnerのアーキテクチャによっては実行できない場合があるため、マルチアーキテクチャ対応したDockerイメージを作る必要があります。
  • Linux Runnerでのみ動作するため、MacやWindowsで動作させたい場合は使えません。(Dockerなのに...?)

Docker Container Actionのセットアップ

基本的には以下のドキュメントに載っている通りとなります。

https://docs.github.com/en/actions/sharing-automations/creating-actions/creating-a-docker-container-action

action.yml

自作アクションの名前や入力値などを設定するファイルです。
usingにはdockerを指定し、imageに後述するGitHub Container RegistryにアップロードしたDockerイメージを指定します。

https://github.com/Takashicc/slack-approval/blob/v2.0.0/action.yml

Dockerfile

自作アクションのエントリーポイントとなるDockerfileです。

https://github.com/Takashicc/slack-approval/blob/v2.0.0/Dockerfile

cargo-chefやsccacheを使ってキャッシュを活用したビルドができるようにしています。初回はビルドに時間がかかりますが、2回目以降はキャッシュが使えるため、ビルド時間が短縮できます。

GitHub Container Registry

https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry

GitHub Container RegistryはGitHubが提供しているサービスで、コンテナイメージをプッシュ、プルなどができます。
GitHubのウェブページからは直接作成できないため、まずはローカルでDockerfileをビルドしてDockerイメージを作成し、それをGitHub Container Registryにプッシュする必要があります。

# Dockerfileをビルド
# GitHub Container Registryにアップロードするタグ名に大文字は使えないため、小文字に変換しています
$ export USER=$(gh config get -h github.com user | awk '{print tolower($0)}')
$ export REPO=example
$ docker build -t ghcr.io/${USER}/${REPO}:latest .

# GitHub Packagesへのアクセスを有効化
$ gh auth refresh --scopes write:packages
# GitHub Container Registryにログイン
$ gh auth token | docker login ghcr.io -u ${USER} --password-stdin
# ビルドしたDockerイメージをプッシュ
$ docker push ghcr.io/${USER}/${REPO}:latest

プッシュが完了すると、GitHubのPackagesページから確認できます。(https://github.com/<OWNER>?tab=packages)

クリックすると詳細ページに遷移するので、右下にあるPackage settingsをクリックします。

GitHub Packagesとリポジトリは論理的に独立したリソースですが、Manage Actions accessからリンクさせることができます。

また、デフォルトだとPrivateなので、Change package visibilityからPublicに変更します。

これでGitHub Container Registryのセットアップは完了です。

ビルドワークフロー

タグがプッシュされたらマルチアーキテクチャ対応したDockerイメージをビルドして、GitHub Container Registryにプッシュするワークフローです。
手動ビルドさせたい場合のために、workflow_dispatchも追加しています。

strategy.matrixを使ってamd64とarm64のビルドを並列で実行しています。
ロジックをRustで実装していた当時はarm runnerが有料のものを使うか、Self-hosted runnerしか手段がなかったのですが、最近になってレビュー版が発表されて使えるようになりました。

https://github.com/Takashicc/slack-approval/blob/v2.0.0/.github/workflows/publish.yml

マルチアーキテクチャ対応イメージを作る方法については以下が参考になりました。
https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners

tagprを使ったリリース半自動化

マニュアルでタグのプッシュは間違えてしまうことがあるため、tagprを使ってリリースを半自動化しています。

https://github.com/Songmu/tagpr

詳細については以下の記事が詳しいですが、軽く解説します。

https://songmu.jp/riji/entry/2022-09-05-tagpr.html

以下のファイルでリリースの際にバージョン値を書き換える対象のファイルを指定したりなどの設定し、

https://github.com/Takashicc/slack-approval/blob/v2.0.0/.tagpr

tagprが追従すべき対象のブランチを指定した以下のようなワークフローを用意してあげます。

https://github.com/Takashicc/slack-approval/blob/v2.0.0/.github/workflows/tagpr.yml

そうすると、追従対象に指定したブランチにコミットがあるたびに上記のワークフローが動き、以下のようなPRが作成されます。
バージョン番号は前回リリースされたバージョンからパッチバージョンをインクリメントしたものがデフォルトで決まり、PR内でラベルをtagpr:majortagpr:minorなどを追加することで、任意のバージョンを設定することができます。

https://github.com/Takashicc/slack-approval/pull/123

その後、適切なタイミングで上記のPRをマージすると、tagprによってtagがプッシュされ、ビルドワークフローが動き、GitHub Container RegistryにDockerイメージがプッシュされます。

まとめ

ちょっとセットアップが大変ですが、好きな言語で書きたい方にはおすすめです。
エラーハンドリングなど見直すべきところがいくつかあるので、これから改善していこうと思います。

GitHubで編集を提案
ambr Tech Blog

Discussion