使用Github Actions构建适用于Windows的Wheel文件

我正在维护一个名为mysqlclient的Python客户端库,它使用libmysqlclient。

这个库为 Windows 用户提供了一个静态链接的 Binary Wheel,可以使他们更容易地使用 libmysqlclient。我尝试使用 Github Actions 来自动构建这个 Binary Wheel。

如果是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つずつダウンロードしなくていいのも楽だと思います。
image.png其中有一些在AppVeyor上可能也能实现同样的功能。这仅仅是我个人的想法。

准备用于构建的仓库为了避免污染库本体的存储库,我创建了一个名为mysqlclient-build的用于构建的另一个存储库,并在那里进行试错。这样可以在不破坏库存储库的情况下进行试错。

name: Build windows wheels
on:
  push:
    branches:
      - master
  create:

尽管后续会提到,但是由于此操作会对检出的目录产生影响,可能会有些麻烦。因此,我觉得在库本身仓库中的拉取请求中进行试错也是可行的选择。

构建依赖库
mysqlclient 使用 libmysqlclient,但在 Windows 的 Wheel 中使用的是 MariaDB Connector/C,它与 MySQL Connector/C 兼容。

这是因为它可以构建为使用 Windows 的 API 而不是 OpenSSL 进行与 MySQL 的 SSL 连接、caching_sha2_password 的 SHA256 计算等操作,并适用于静态链接的二进制文件分发。

然而,MariaDB Connector/C的二进制发行版中,认证等插件是以DLL的形式存在的。如果可能的话,我想要将其打包成单一二进制文件,所以我会自己构建MariaDB Connector/C。

由于每次构建mysqlclient时构建这个依赖库会花费很长时间,所以我们使用缓存操作。缓存操作会在作业成功时将指定目录缓存起来,所以我们将构建到MariaDB Connector/C的状态提交并成功后进行缓存。这样,在尝试构建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 操作中,我们会 checkout mysqlclient。默认情况下,会将工作目录 checkout 到包含 workflow 的存储库(在本例中为 mysqlclient-build),但是我们可以使用 with: 来将其他存储库 checkout 到另一个路径。

在这里有一个陷阱,如果您在 path: mysqlclient 中进行检出,检出的目标将不是 ./mysqlclient,而是 ../mysqlclient。默认的工作目录是用于检出该存储库的目录,当要检出另一个存储库时,应该在其旁边创建一个目录进行检出,而不是在其下面。因此,在检出后的步骤中,我们指定 working-directory: ../mysqlclient。

在构建完成后,可以使用名为upload-artifact的操作将wheel文件上传到Github,然后确保能够实际安装并通过导入进行使用。

如果可能的话,我本来想进行测试直到操作完成,但由于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"

bannerAds