GitHub Actions による deb パッケージ作成の自動化
こんにちは.NTT の新井です.
「CheckInstall による Slurm の deb パッケージ化」という記事において,Ubuntu 上で手軽に tarball から deb パッケージを作成する方法をご紹介しました.今回はその続編として,GitHub Actions でパッケージ作成スクリプトを実行することによるパッケージ化とリリースの方法を紹介します.また,その応用でプルリクエスト作成時のテストも自動化します.
GitHub Actions
GitHub Actions はソフトウェア開発における定型的な処理 (ワークフロー) の実行を GitHub 上で自動化する仕組み,あるいはサービスです.GitHub のリポジトリに対する push やプルリクエスト作成等のイベントを契機として,GitHub がホストする VM 上でユーザが定義した処理を行うことができます.これは例えば継続的インテグレーション (CI) や継続的デプロイメント (CD) のために使用されます.また,GitHub Actions は無料です (参考).
GitHub Actions の処理は次のような階層化された概念で表現されます:
- ワークフロー:1つ以上のジョブから構成される処理のまとまり.イベントにより起動される.
- ジョブ:同じ環境で実行される (=ファイルシステムを共有する) ステップの集合.
- ステップ:ジョブ環境におけるコマンド実行または1つのアクション.
- アクション:GitHub Marketplace などで提供されるポータブルな機能.例えばリポジトリのチェックアウトやリリースの作成など.
GitHub Actions を用いたパッケージ化には我々のチームにおいて次のような利点があります:
- パッケージ作成環境の違いによるパッケージ内容の変化を防げます.パッケージの内容は作成環境にインストールされているライブラリなどによって変化する場合があります.GitHub Actions が提供する環境を使うことで常に同じパッケージを作成できます.(もちろん GitHub 側が環境を変更することもあり得るので,そこは注意が必要ですが.)
- プロジェクトで使用するファイルを GitHub 上に集約できます.「AWS CodeBuildでAnsibleのCIをしてみた」や「Private RepositoryのGitLFSとAWS CodeBuildを連携する」でご紹介したように,我々のチームでは GitHub と AWS CodeBuild の連携により GPU クラスタの構築スクリプト (Ansible Playbook) をテストしています.テストのためにはインターネットからアクセス可能な場所に内製パッケージを置く必要があります.ローカルでパッケージ化してアップロードするよりも,GitHub 上でパッケージ化しそのままアクセス可能にするほうがシンプルです.
リポジトリの構成
GitHub Actions はリポジトリ単位で設定します.具体的にはリポジトリの直下に.github/workflows
ディレクトリを作成し,その中に YAML ファイルでワークフローを定義します.ここではそのファイルを.github/workflows/main.yml
としましょう.また「CheckInstall による Slurm の deb パッケージ化」では諸事情により gist でパッケージ化スクリプト群を公開していますが,これらをリポジトリに入れることにします.つまり,以下のようなディレクトリ構成のリポジトリがあるものとして説明を始めます.
.
├── .git/
│ └── […]
├── .github/
│ └── workflows/
│ └── main.yml
├── build.sh
├── description-pak
├── include-list
├── postinstall-pak
├── preinstall-pak
└── preremove-pak
ワークフローファイルの記述
ここでは次のようなワークフローを定義することにします:
- バージョンを示すタグが push されたとき,パッケージ化スクリプト
build.sh
を実行し,作成されたパッケージをリリースする.
「バージョンを示すタグ」は GitHub のフィルタパターンにおいてv[0–9]+.[0–9]+.[0-9]+*
にマッチするタグと定義します.例えばv0.12.3-rc4
などはこのパターンにマッチします.
まずは結論としてこの処理を行うmain.yml
ファイルを示します.
main.yml
をこの通りに変更し,add,commit,tag 作成,push を行います.すると GitHub 上で自動的にワークフローの実行が開始されます.
git add .github/workflows/main.yml
git commit
git tag v20.02.2
git push origin v20.02.2
続いてmain.yml
の内容を簡単にご説明します.詳細は GitHub ヘルプをご確認ください.
name: Main workflow
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+*'
ワークフロー名とトリガの定義です.ここでは指定のパターンにマッチする名前のタグが push されたとき起動されるように指定しています.Push 以外にもプルリクエストの作成など様々なイベントをトリガとして設定できます (参考).
jobs:
release:
name: Release
runs-on: ubuntu-18.04
steps:
このワークフローにおける唯一のジョブrelease
の定義です.runs-on
ではジョブの実行環境を指定します (参考).steps
にジョブを構成するステップを列挙します.
- name: Check out the repository
uses: actions/checkout@v2
- name: Run the packaging script
run: sudo bash build.sh
- name: Create a release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
body: This release is created by GitHub Actions.
draft: false
prerelease: false
- name: Upload the package
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: slurm_20.02.2-1_amd64.deb
asset_path: ./slurm_20.02.2-1_amd64.deb
asset_content_type: application/vnd.debian.binary-package
ステップはコマンドまたはアクションです.例えば1番目のステップではアクション actions/checkout@v2 を使用してリポジトリの内容を取得します.git
コマンドでも同じことはできますが,このアクションではプライベートリポジトリへアクセスするために必要なトークンの処理や,ワークフローを起動したタグやブランチの自動選択などが自動的に行われます.一方,3番目と4番目のアクションでは環境変数でトークンを渡していることに注意してください.
ちなみに,このワークフローではリリースするファイル名slurm_20.02.2-1_amd64.deb
がハードコーディングされているため,Slurm のバージョンアップに合わせて変更が必要になります.このあたりをスマートにする方法は要検討です.
テストの追加
前述のワークフローにより GitHub Actions 上でパッケージ化とリリースが可能になりました.しかしそこまでやるのであれば,パッケージ作成スクリプトのテストとパッケージの動作確認もプルリクエストの作成時に実施したいものです.その場合,例えば次のようなワークフローの設計になります.
- プルリクエストの編集 (作成を含む),またはバージョンを示すタグの push があったとき,パッケージを作成する.
- プルリクエストの編集があったとき,パッケージを実際にインストールし,Slurm の動作をテストする.
- バージョンを示すタグの push があったとき,パッケージをリリースする.
これらをジョブとして定義し,1つのワークフローで実施します.トリガが異なるにも関わらずなぜ1つのワークフローにするのでしょうか.それは現状の GitHub Actions においてワークフロー間の依存関係を定義できないためです (参考1,2).パッケージ作成には時間がかかるので,完了するまで他の処理を止めなければなりません.ジョブ間であれば依存関係を定義できるので,ジョブの実行開始条件としてトリガを確認することにより処理順序を守らせます.具体的には次のようなワークフローを定義します.なおテストのために実行しているtest.sh
の内容は省略します.
needs: build
によってジョブ間の依存関係を定義しています.さらに if: github.event_name == …
でワークフローのトリガを確認しています.build
は常に実行されますが,test
はpull_request
のとき,release
はpush
のときにのみ実行されます.また,異なるジョブの間ではファイルシステムが共有されないことに注意が必要です.生成されたパッケージをアーティファクト (成果物) として登録することによりジョブ間で共有しています.
おわりに
この記事では GitHub Actions を用いたパッケージ作成とリリース,およびテストを行う方法についてご紹介しました.
私たち NTT は OSS を活用した研究開発を共に行う仲間を募集しています.ぜひ弊社ソフトウェアイノベーションセンタ紹介ページや,採用情報ページをご覧ください.