🚀

Azure Pipelines で Python の自作パッケージのテスト&ビルド自動化

2024/02/12に公開

読む前の注意

自動化において Azure Pipelines を使用するが、初めて利用する場合、Microsoft がホストしているサーバーでパイプラインを動かすには以下のフォームから利用申請を送る必要がある。(無料枠の場合)

https://aka.ms/azpipelines-parallelism-request

このリクエストの回答・承認が2-3営業日後とのことなので Azure Pipelines がすぐに使えるわけではないので注意。

もし、この申請前にパイプラインを動かそうとすると、以下のエラーがでてパイプラインが失敗する。

##[error]No hosted parallelism has been purchased or granted. To request a free parallelism grant, please fill out the following form https://aka.ms/azpipelines-parallelism-request

https://learn.microsoft.com/ja-jp/azure/devops/pipelines/licensing/concurrent-jobs?view=azure-devops&tabs=ms-hosted
https://qiita.com/Hachiyou-Renge/items/2083f2ce9e8b38558805

はじめに

これは以下の記事の続きである。本記事は以下の記事のcatlibという自作パッケージについて、テストやビルドを自動化するものである。
https://zenn.dev/takanari_dev/articles/2024-02-06-azure-devops-python-package

やりたいこと

  • mainブランチへのマージ時に自作 Python パッケージのテストを自動的に行う
  • タグを切ったときに Azure Artifacts へのデプロイを自動的に行う

テスト自動化

パイプラインの準備

ローカル環境のリポジトリ直下から.azure-pipelines/test.ymlでファイルを作り、以下のパイプラインを記述する。なお、pytestおよびpytest-covが導入済みであることを前提に書いている。

.azure-pipelines/test.yml
trigger:
  branches:
    include:
      - main

pool:
  vmImage: "ubuntu-latest"

jobs:
  - job: "Test"
    steps:
      - task: UsePythonVersion@0
        inputs:
          versionSpec: "3.12"
        displayName: "Install Python"

      - script: |
          pip install poetry
          poetry --version
        displayName: "Install Poetry"

      - script: |
          poetry install
          poetry add pytest-azurepipelines --dev
        displayName: "Install Dependencies"

      - script: poetry run pytest tests --doctest-modules --junitxml=junit/test-results.xml --cov=./catlib --cov-report=xml
        displayName: "Test"

      - task: PublishTestResults@2
        condition: succeededOrFailed()
        inputs:
          testResultsFiles: "**/test-*.xml"
          testRunTitle: "Publish test results for Python $(python.version)"
        displayName: "Publish Test Results"

      - task: PublishCodeCoverageResults@1
        inputs:
          codeCoverageTool: Cobertura
          summaryFileLocation: "$(System.DefaultWorkingDirectory)/**/coverage.xml"
        displayName: "Publish Code Coverage Results"

基本的な流れは以下のようである。

  1. mainブランチにマージされたタイミングで発火する。
  2. Ubuntu 仮想環境を用意する。
  3. そこに Python と Poetry をインストール。
  4. poetry installコマンドでpyproject.tomlにある依存関係をインストールする。 poetry add pytest-azurepipelines --devというのがあるが、公式ドキュメントにそのような記述があり、おそらくpytestの出力フォーマットに必要なのだろうと思われる。(未調査)
  5. テストを実行し、テスト結果とコードカバレッジ結果をxml形式で吐き出す。
  6. PublishTestResults@2タスクでテスト結果を Azure Pipelines の UI 上で見れるようにする。
  7. PublishCodeCoverageResults@1タスクでコードカバレッジ結果を Azure Pipelines の UI 上で見れるようにする。

ちなみに VSCode で作業している場合は、MicroSoft 公式の拡張があるので、導入することをオススメする。
https://marketplace.visualstudio.com/items?itemName=ms-azure-devops.azure-pipelines

Azure Pipelines に登録する

.azure-pipelines/test.ymlのファイルをmainブランチにマージしてみると、実はパイプラインが発火しない。これは Azure Pipelines にこのファイルを登録してからでないと動作しないからである。なので、今回作成したファイルを登録する。

Azure DevOps のプロジェクトに入り、右のサービスリストから [Pipelines] > [Pipelines] のページを開く。
そのページにはおそらく [Create Pipeline] というのが表示されており、そのボタンを押す。

  1. Connect
    What is your code?と聞かれるので [Azure Repos Git] を選択する。

  2. Select
    Select a repositoryで自作パッケージのリポジトリ(catlib)を選択する。

  3. Configure
    Configure your pipelineではすでにあるパイプラインを参照するので [Existing Azure Pipelines YAML file] を選択し、Path を .azure-pipelines/test.ymlにする。

  4. Review
    [Run] を押して実行するか、[Run] の隣にマークがあり、それを押すと [Save] があるので、セーブするだけでもよい。

実行

どこか適当にファイルを編集しmainブランチに Push するときに発火する。あるいは Azure Pipelines からパイプラインを選ぶとその中に [Run Pipeline] のボタンがあるのでそれを押せば実行される。
しばらくたつとパイプラインの処理が終了し、結果が見れる状態になる。一つの実行結果を見るとテストやカバレッジの結果が見られるようになっていることがわかる。

カバレッジの結果の例

Azure Artifacts へのデプロイ自動化

パイプラインの準備

次に.azure-pipelines/deploy.ymlを作成し、以下のようなパイプラインを記述する。

.azure-pipelines/deploy.yml
trigger:
  tags:
    include:
      - "v*"

pool:
  vmImage: "ubuntu-latest"

jobs:
  - job: "Release"

    variables:
      PROJECT: $(System.TeamProject) # 組織スコープの場合不要
      FEED: catlib

    steps:
      - task: UsePythonVersion@0
        inputs:
          versionSpec: "3.12"
        displayName: "Install Python"

      - script: |
          pip install twine
          twine --version
        displayName: "Install Twine"

      - script: |
          pip install poetry
          poetry --version
        displayName: "Install Poetry"

      - script: |
          poetry install --no-dev
        displayName: "Install Dependencies"

      - script: |
          poetry build -f wheel
        displayName: "Build"

      - task: TwineAuthenticate@1
        inputs:
          artifactFeed: $(PROJECT)/$(FEED) # 組織スコープの場合 $(PROJECT)/ が不要

      - script: |
          twine upload -r $(FEED) --config-file $(PYPIRC_PATH) dist/*.whl
        displayName: "Publish to $(PROJECT)/$(FEED)"
  1. vから始まるタグ(想定はv1.0.0など)が作られたタイミングで発火する。
  2. Ubuntu 仮想環境を用意する。
  3. そこに Python, Twine, Poetry をインストール。
  4. poetry installコマンドでpyproject.tomlにある依存関係をインストールする。--no-devオプションで開発用のライブラリについてはインストールしないようにしている。
  5. wheel形式でビルドする。
  6. TwineAuthenticate@1タスクによって twineで Azure Artifacts へデプロイするための認証を行う。(このとき認証情報が書かれた.pypircファイルが配置され、そのパスがPYPIRC_PATH変数に入っている。)
  7. twineにより Azure Artifacts にデプロイする。

前回の記事での手動デプロイではpoetry publishを利用したが、そのときに PAT (Personal Access Token) を使っていた。しかし Azure DevOps ではこの PAT に必ず有効期限があるため、メンテナンスをしなくてはならなくなる。それを回避するためにはTwineAuthenticate@1タスクを使う必要があった。そのためデプロイにはpoetryではなく、twineというツールを使う。

ちなみに PAT をシークレット変数(GitHub でいうsecrets)として登録しpoetry publishするパイプラインは以下に載せておく。

Twine を使わない場合のパイプライン

以下のドキュメントを参考にcatlib-release変数グループを作成し、AZURE_PATとして PAT の値を入力した。
https://learn.microsoft.com/ja-jp/azure/devops/pipelines/process/set-secret-variables?view=azure-devops&tabs=yaml%2Cbash

.azure-pipelines/deploy.yml
trigger:
  tags:
    include:
      - "v*"

variables:
  - group: catlib-release

pool:
  vmImage: "ubuntu-latest"

jobs:
  - job: "Release"
    steps:
      - task: UsePythonVersion@0
        inputs:
          versionSpec: "3.12"
        displayName: "Install Python"

      - script: |
          pip install poetry
          poetry --version
        displayName: "Install Poetry"

      - script: |
          poetry install --no-dev
        displayName: "Install Dependencies"

      - script: |
          poetry build -f wheel
        displayName: "Build"

      - script: |
          poetry publish -r azure
        env:
          POETRY_REPOSITORIES_AZURE_URL: <フィードのアップロード用URL>
          POETRY_HTTP_BASIC_AZURE_USERNAME: "auto-deployer"
          POETRY_HTTP_BASIC_AZURE_PASSWORD: $(AZURE_PAT)
        displayName: "Publish to Azure Artifacts Feed"

poetry publish -r azureのところで環境変数がでてくるが、poetryの config は環境変数から与えることもできる。
https://python-poetry.org/docs/configuration/

Azure Pipelines に登録する

.azure-pipelines/test.ymlの時と同じように [New Pipeline] ボタンから登録する。
ちなみにそれぞれのパイプラインの名前は [Pipelines] のページから変更できるのでわかりやすい名前に変えておくとよい。

Azure Artifacts に Azure Pipelines からデプロイできるようにする

これで実行してみたいところだが、このまま実行しても実はうまくいかない。これは Azure Pipelines の実行系にフィードに対しての write 権限がないためである。

フィードの<プロジェクト名> Build Service (<組織ID>)に対してContributorのロールを付与することで解決できる。
以下の公式ドキュメントを参考にして設定すればよいが、少しわかりにくいのが [Permissions] のところで、すでにリストにある User/Group でも [Add users/groups] からロールを変えることである。

https://learn.microsoft.com/ja-jp/azure/devops/artifacts/feeds/feed-permissions?view=azure-devops#pipelines-permissions

実行

pyproject.tomlファイルのversionの値を変更してから、パイプラインの実行を行う。もしその値が変わってなければデプロイに失敗するので注意。

Azure Artifacts 上のフィード内のバージョンが変更されていれば成功である。

今後の課題

タグを切ったときのバージョンの値とpyproject.tomlで記載したバージョンの値が一致するか検証してからデプロイしたい。
prタグで Pull Request 時に発火するトリガーがあるのでこれも試してみたい。たとえばそのタイミングでテストを行うとか。

Discussion