mysqlclient という Python 用の libmysqlclient を利用したクライアントライブラリをメンテナンスしています。
このライブラリでは Windows ユーザーが簡単に利用できるように libmysqlclient を静的リンクした Binary Wheel を提供しています。
この Binary Wheel のビルドを Github Actions を利用して自動化してみました。
Github Actions 初心者なのでもっといいやり方があるかもしれませんが、だれかの参考になれば。
感想
先に感想から書いておきます。すでに他のプロジェクトで AppVeyor を使って Windows 用の Wheel を自動ビルドした経験があったのですが、それよりも圧倒的に快適でした。
- VM起動が早い -- AppVeyor に比べて push してからVMが立ち上がるまでの時間が圧倒的に短いように感じました。 AppVeyor を使っていたときは push するたびに数分待っていたのですが、 Github Actions ではサクサクと試行錯誤することができました。
- Cache 万歳 -- 今回はビルドに数分かかる依存ライブラリがあったのですが、それをcacheすることで本体の試行錯誤をサクサクと進めることができました。
- step ごとに違うシェルを選べる -- cmd, PowerShell, そして Git for Windows の bash を、 step ごとに選択して書くことができます。 cmd や PowerShell には慣れていないので、どうやるのか迷った時にすぐに bash に逃げれるのは楽でした。
- Artifact が楽 -- ビルドした Wheel を Artifact として登録し、ダウンロードすることができます。これは AppVeyor でもできたのですが、Github Actionsの方がダウンロードするまでも楽ですし、zip化されているので複数のファイルを1つずつダウンロードしなくていいのも楽だと思います。
(このうちいくつかは実際にはAppVeyorでも同じことができるかもしれません。あくまでも個人的な感想です。)
ビルド用のリポジトリを用意
ライブラリ本体のリポジトリを使う代わりに、ビルド用の別のリポジトリ mysqlclient-build を作ってそこで試行錯誤していきました。これによりライブラリのリポジトリを汚すことなく試行錯誤することができます。
name: Build windows wheels
on:
push:
branches:
- master
create:
後述しますが、これによりチェックアウトするディレクトリが影響を受けてしまい若干面倒だったので、ライブラリ本体のリポジトリのプルリクエストで試行錯誤するのでもよかった気がします。
依存ライブラリのビルド
mysqlclient は libmysqlclient を利用しているのですが、Windows用Wheelでは MySQL Connector/C ではなくて互換性のある MariaDB Connector/C を利用しています。
これは MySQL への SSL 接続や、 caching_sha2_password で使う sha256 の計算などに OpenSSL を使わず Windows の API を利用するようにビルドすることができ、静的リンクしたバイナリ配布に適しているからです。
しかし、バイナリ配布されている MariaDB Connector/C は認証などのプラグインが DLL になっています。できればシングルバイナリにしたいので自分で MariaDB Connector/C をビルドします。
mysqlclient をビルドするたびにこの依存ライブラリをビルドすると時間がかかるので cache アクションを使います。 cache アクションは job が成功したときに指定したディレクトリをキャッシュしてくれるので、一旦 MariaDB Connector/C のビルドまでの状態で push して成功させ、キャッシュしておきます。これで mysqlclient をビルドするための試行錯誤をするときは、ビルド済みの MariaDB Connector/C を利用することができました。
jobs:
build:
runs-on: windows-latest
env:
CONNECTOR_VERSION: "3.1.5"
steps:
- name: Cache Connector
id: cache-connector
uses: actions/cache@v1
with:
path: c:/mariadb-connector
key: mariadb-connector-3.1.5-win
- name: Download and Unzip Connector
if: steps.cache-connector.outputs.cache-hit != 'true'
shell: bash
run: |
curl -LO "https://downloads.mariadb.com/Connectors/c/connector-c-${CONNECTOR_VERSION}/mariadb-connector-c-${CONNECTOR_VERSION}-src.zip"
unzip "mariadb-connector-c-${CONNECTOR_VERSION}-src.zip" -d c:/
mv "c:/mariadb-connector-c-${CONNECTOR_VERSION}-src" c:/mariadb-connector-src
- name: Build Connector
if: steps.cache-connector.outputs.cache-hit != 'true'
shell: cmd
working-directory: c:/mariadb-connector-src
run: |
mkdir build
cd build
cmake -A x64 .. -DCMAKE_BUILD_TYPE=Release -DCLIENT_PLUGIN_DIALOG=static -DCLIENT_PLUGIN_SHA256_PASSWORD=static -DCLIENT_PLUGIN_CACHING_SHA2_PASSWORD=static
cmake --build . -j 8 --config Release
cmake -DCMAKE_INSTALL_PREFIX=c:/mariadb-connector -DCMAKE_INSTALL_COMPONENT=Development -DCMAKE_BUILD_TYPE=Release -P cmake_install.cmake
mysqlclient のビルド
まずは checkout アクションで mysqlclient をチェックアウトします。デフォルトでは workflow があるリポジトリ(今回の場合は mysqlclient-build)を作業ディレクトリにチェックアウトするのですが、 with:
を使って別のリポジトリを別のパスにチェックアウトすることができます。
ここで一つ落とし穴があったのですが、 path: mysqlclient
を指定してチェックアウトした場合、チェックアウト先は ./mysqlclient
ではなく ../mysqlclient
になります。デフォルトの作業ディレクトリはそのリポジトリをチェックアウトするためのディレクトリで、別のリポジトリをチェックアウトするときはその下ではなく隣にディレクトリを作ってチェックアウトするという形になるからです。なのでチェックアウトした以降の step では working-directory: ../mysqlclient
を指定しています。
ビルドした後は upload-artifact というアクションを使って wheel を Github にアップロードし、そのあとその wheel を実際にインストールして import できることを確認しておきます。
できれば動作までテストしたかったのですが、 windows-latest では MySQL Server がインストールされていないので今回はやっていません。 Docker が使えるようなので、 Docker を使って MySQL Server をセットアップできないか今後挑戦してみます。
- name: Checkout mysqlclient
uses: actions/checkout@v1
with:
repository: PyMySQL/mysqlclient-python
ref: master
fetch-depth: 10
path: mysqlclient
- name: Site Config
shell: bash
working-directory: ../mysqlclient
run: |
pwd
find .
cat <<EOF >site.cfg
[options]
static = True
connector = C:/mariadb-connector
EOF
cat site.cfg
- name: Build wheels
shell: cmd
working-directory: ../mysqlclient
run: |
py -3.8 -m pip install -U setuptools wheel pip
py -3.8 setup.py bdist_wheel
py -3.7 -m pip install -U setuptools wheel pip
py -3.7 setup.py bdist_wheel
py -3.6 -m pip install -U setuptools wheel pip
py -3.6 setup.py bdist_wheel
- name: Upload Wheel
uses: actions/upload-artifact@v1
with:
name: win-wheels
path: ../mysqlclient/dist
- name: Check wheels
shell: bash
working-directory: ../mysqlclient/dist
run: |
ls -la
py -3.8 -m pip install mysqlclient-1.4.6-cp38-cp38-win_amd64.whl
py -3.8 -c "import MySQLdb"
py -3.7 -m pip install mysqlclient-1.4.6-cp37-cp37m-win_amd64.whl
py -3.7 -c "import MySQLdb"
py -3.6 -m pip install mysqlclient-1.4.6-cp36-cp36m-win_amd64.whl
py -3.6 -c "import MySQLdb"