Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

【R&D DevOps通信】Poetryでプライベートパッケージを扱う(GitHub, AWS CodeArtifact, GCP Artifact Registry)

研究開発部 Architectグループ ML PlatformチームのKAZYこと新井です。ちなみに名古屋にある中部支店に所属です。

今回はPoetryでプライベートパッケージ(ライブラリ)を扱うテーマです。

追記(2022/11)

Poetry 1.2で新たに追加された機能をプライベートパッケージの観点からまとめました。 合わせて御覧ください。 buildersbox.corp-sansan.com

目次

はじめに

PoetryとはPython用パッケージ管理ツールです。

github.com

Poetryはインストールするライブラリ管理としてだけではなく、パッケージ化や公開機能も持っています。 公開パッケージであれば大体PyPIに普段お世話になっている事は知っています。 しかし社内専用のような公開できないパッケージを作ったり使ったりしたい場合はどうすればいいのかわからなかったので調べました。

興味があった以下の3パターンについて書きます。

No コード管理の場所 パッケージ管理の場所
1 GitHub GitHub
2 GitHub AWS CodeArtifact
3 GitHub GCP Artifact Registry

GitHubを使っていない方でもPoetry + AWS CodeArtifactとかPoetry + GCP Artifact Registryを検討されている方には役に立つ記事になっていると思います。

調査結果

はじめに調査結果です。

私見も入っていると思いますがご容赦ください。

詳細な使い方は以降で紹介します。

No コード
管理
パッケージ管理 動作確認
Poetry Ver
1 GitHub GitHub - お手軽さ - AccessToken期限の柔軟さ
- バージョン管理
1.1.14
2 GitHub AWS CodeArtifact - AccessToken期限の柔軟さ
- パッケージっぽさ
- 設定の一手間 1.1.14
3 GitHub GCP Artifact Registry - AccessToken期限の柔軟さ
- パッケージっぽさ
- 設定の一手間
- Poetry1.2以降推奨*1
1.2.0b3

所感

GitHubでコード管理をしている場合はパッケージもGitHubで管理するのが圧倒的に楽かなと思いました。GitHubの認証ができている環境であればpyproject.tomlへの追記も必要がないのも嬉しいです。 パッケージのバージョンを指定したり、とにかく最新を持ってこられるようにしたりしたい場合はAWSのCodeArtifactやGCPのArtifact Registryに立てて利用するのが運用しやすそうです。GCPのArtifact Registryの場合はPoetryのバージョン1.2がリリースされた後に使うのが無難です。

Poetryでのパッケージ作成方法

そもそもどうやってPoetryを用いてパッケージを作るかという話をします。 すでにご存じの方は読み飛ばしてもらって結構です。

またほぼドキュメントからの引用になりますので詳しくは以下を参照ください。 python-poetry.org python-poetry.org

パッケージ作成

poetry new poetry-demo
poetry-demo
├── pyproject.toml
├── README.rst
├── poetry_demo
│   └── __init__.py
└── tests
    ├── __init__.py
    └── test_poetry_demo.py

パッケージのビルド

ビルドを行うとgz, whlと言ったファイルがdistというディレクトリととも作成されます。それらが次に紹介する公開の際に使用するファイルとなります。

poetry build
.
├── README.rst
├── dist
│   ├── poetry-demo-0.1.0.tar.gz
│   └── poetry_demo-0.1.0-py3-none-any.whl
├── poetry_demo
│   └── __init__.py
├── pyproject.toml
└── tests
    ├── __init__.py
    └── test_poetry_demo.py

パッケージの公開

ビルド時に作成されたdistディレクトリ以下の*.gz, *.whlファイルをリポジトリにアップロードします。デフォルトではPyPIに公開します。

poetry publish

プライベートパッケージへの公開

引数でリポジトリを指定することで独自に作成したリポジトリに公開することができます。プライベートリポジトリへの公開はこちらを使います。そしてAWSのCodeArtifactやGCPのArtifact Registryではこのリポジトリを建てることになります。

poetry publish --repository リポジトリ名

なるべく以上の流れで運用できることを目指します。

(おまけ) パッケージに含めたくないファイルの制御

パッケージする際に特定のファイルを含めたくないときは以下のような設定で制御できます。

特定のファイルをパッケージから除外したいとき。

[tool.poetry]
exclude = ["my_package/excluded.py"]

パッケージのソースコードをgitで管理している場合は.gitignoreの設定がパッケージにも反映されます。

python-poetry.org

AWS CodeArtifact

パッケージ作成方法

パッケージの開発者は以下の流れを行うことになります。

  1. AWS CodeArtifactでプライベートリポジトリを作成
  2. pyproject.tomlにプライベートリポジトリの情報を記入
  3. Poetryのconfigに認証情報の設定
  4. パッケージのビルドとアップロード

順に説明します。

AWS CodeArtifactでプライベートリポジトリを作成

pypi-storeに設定します。 それ以外はよしなに設定ください。 特に迷うところはないと思います。

pyproject.tomlにプライベートリポジトリの情報を記入

先の章で説明した通りPoetryでパッケージを作成すると、pyproject.tomlというファイルができます。

公開予定のパッケージのpyproject.tomlに以下のCodeArtifactで作成したプライベートリポジトリの情報を追記することでプライベートリポジトリの場所をPoetryに教えます。

︙略
[[tool.poetry.source]]
name = "<任意のリポジトリ名>" # Poetry内での管理用リポジトリ名(code artifactと一致しなくても良いですがpoetry configで設定するものを合わせる)
url = "https://<Code Artifactで設定したドメイン>-<AWS account id>.d.codeartifact.<リージョン>.amazonaws.com/pypi/<CodeArtifactで設定したリポジトリ名>/simple"
︙略

Poetryのconfigに認証情報の設定

ここから先はAWSの認証ができる環境で行ってください。 Poetryのconfigにプライベートリポジトリへの認証のための情報を与えます。 コードアーティファクトのトークンは 15分から12時間 に設定できます(例では3600秒=1時間)。

# simpleがパスに入らないのがポイント
poetry config repositories.<任意のリポジトリ名> https://<Code Artifactで設定したドメイン>-<aws account id>.d.codeartifact.<リージョン>.amazonaws.com/pypi/<CodeArtifactで設定したリポジトリ名>

CODEARTIFACT_AUTH_TOKEN=`aws codeartifact get-authorization-token --domain <CodeArtifactで設定したドメイン> --domain-owner <AWS account id> --query authorizationToken --output text --duration-seconds 3600`
poetry config http-basic.<任意のリポジトリ名> aws ${CODEARTIFACT_AUTH_TOKEN}

パッケージのビルドとアップロード

認証情報が正しくセットされていれば以下のコマンドでパッケージをアップロードできます。

poetry build
poetry publish --repository <任意のリポジトリ名>

パッケージ追加方法

パッケージ作成と共通する部分が多いですが次のような流れになります。 1, 2はパッケージ作成時と全く同じです。

  1. pyproject.tomlにプライベートリポジトリの情報を記入
  2. Poetryのconfigに認証情報の設定
  3. パッケージの追加

pyproject.tomlにプライベートリポジトリの情報を記入

[[tool.poetry.source]]
name = "<任意のリポジトリ名>" # poetry内での管理用リポジトリ名(code artifactと一致しなくても良いですがpoetry configで設定するものを合わせる)
url = "https://<Code Artifactで設定したドメイン>-<AWS account id>.d.codeartifact.<リージョン>.amazonaws.com/pypi/<CodeArtifactで設定したリポジトリ名>/simple" #simpleを忘れないようにしてください。

Poetryのconfigに認証情報の設定

ここから先はAWSの認証ができる環境で行ってください。

# simpleがパスに入らないのがポイント
poetry config repositories.<任意のリポジトリ名> https://<Code Artifactで設定したドメイン>-<aws account id>.d.codeartifact.<リージョン>.amazonaws.com/pypi/<CodeArtifactで設定したリポジトリ名>

CODEARTIFACT_AUTH_TOKEN=`aws codeartifact get-authorization-token --domain <CodeArtifactで設定したドメイン> --domain-owner <AWS account id> --query authorizationToken --output text --duration-seconds 3600`
poetry config http-basic.<任意のリポジトリ名> aws ${CODEARTIFACT_AUTH_TOKEN}

パッケージの追加

認証情報が正しくセットされていれば以下のコマンドでパッケージを追加できます。

# 追加時
poetry add <パッケージ名>

# バージョン指定
poetry add <パッケージ名>==1.0.0

# インストール
poetry install

Github Actionsなどでの利用

AWSの認証をしてCodeArtifactのアクセストークンを取得できればインストールができます。 以下を用いれば良いでしょう。

github.com

GitHub ActionsでDockerのビルドをする場合はマルチステージビルドやsecretオプションを使ってトークンがイメージに残らないように気をつけインストールすると良いでしょう。

GCP Artifact Registry

ほとんどAWS CodeArtifactと同じです。しかしPoetryのバージョンが1.2以降が必要なのでご注意ください。 github.com

パッケージ作成方法

パッケージの開発者は以下の流れを行うことになります。

  1. GCP Artifact Registryでプライベートリポジトリを作成
  2. Poetryのバージョン1.2のインストールとをkeyringsのライブラリをインストール
  3. pyproject.tomlにプライベートリポジトリの情報を記入
  4. Poetryのconfigに認証情報の設定
  5. パッケージのビルドとアップロード

順に説明します。

パッケージの追加方法

formatをPythonに設定します。 それ以外はよしなに設定ください。 特に迷うところはないと思います。

Poetryのバージョン1.2のインストールとをkeyringsのライブラリをインストール

poetry self addはバージョン1.2移行で使えるコマンドです。

curl -sSL https://install.python-poetry.org | POETRY_VERSION=1.2.0b3 python -

poetry self add keyrings.google-artifactregistry-auth==1.0.0

pyproject.tomlにプライベートリポジトリの情報を記入

[[tool.poetry.source]]
name = "<任意のリポジトリ名>"
url = "https://<リージョン>-python.pkg.dev/<プロジェクトID>/<Artifact Registoryのrepository名>/simple" #simpleを忘れないようにしてください。

Poetryのconfigに認証情報の設定

GOOGLE_APPLICATION_CREDENTIALSという環境変数を経由して認証のための情報を渡しているのがAWS CodeArtifactとの違いです。

export GOOGLE_APPLICATION_CREDENTIALS="<クレデンシャルのあるパス>/<ファイル名>.json"
poetry config repositories.<任意のリポジトリ名> https://<リージョン>-python.pkg.dev/<プロジェクトID>/<Artifact Registoryのrepository名>

パッケージのビルドとアップロード

poetry build
poetry publish --repository <任意のリポジトリ名>

パッケージ追加方法

パッケージ作成方法と2, 3, 4 は全く同じです。 その後以下のコマンドでパッケージを追加します。

# 追加時
poetry add <パッケージ名>

# バージョン指定
poetry add <パッケージ名>==1.0.0

# インストール
poetry install

GitHub Actionsなどでの利用

認証自体はこちらを使えば良いでしょう。 github.com

クレデンシャルはWorkload Identity連携などを用いて期限付きのものを発行するとよりセキュアかなと思います。

cloud.google.com

GitHub ActionsでDockerのビルドをする場合はマルチステージビルドやsecretオプションを使ってクレデンシャルがイメージに残らないように気をつけインストールすると良いでしょう。

GitHub

パッケージの作成方法

Poetryで作成したプロジェクトをそのままGitHubにプッシュします。pyproject.tomlがルートに来るようにしてください。 CodeArtifactやArtifact Registoryを用いた方法に比べて非常に簡単です。

パッケージの追加方法

対象リポジトリへの認証がされている環境下では以下のコマンドでインストールします。バージョンを指定するのではなく、ブランチやタグを指定する形になります。特定のブランチの中でパッケージのバージョンが上がった場合はpoetry updateで更新できますがタグを指定している場合は更新の都度poetry addすることになると思います。

とりあえず最新を使いたい場合はXXXXブランチを使いましょうなど、規則を決めておくと使う側が困らなくて良さそうです。

# GitHubにログイン
gh auth login

poetry add git+https://github.com/<accountやorganization>/<リポジトリ>#<ブランチorタグorコミットハッシュ>

パーソナルアクセストークンを用いたパッケージ追加について

GitHubのパーソナルアクセストークンを用いればプライベートパッケージを以下のコマンドで追加できますがオススメしません。

# パーソナルアクセストークン
PAT=gha_XXXXXXXXX

poetry add git+https://${PAT}@github.com/<accountやorganization>/<リポジトリ>#<ブランチorタグorコミットハッシュ>

上記のコマンドでパッケージを追加するとパーソナルアクセストークンがpyproject.tomlとpoetry.lockのファイルに書き込まれてしまうからです。

特にpoetry.lockファイルはあまり見ていない人が多くうっかりアップロードしてしまう恐れがあります。

そのためGitHub Cliでログインするか、vscodeであればgithub拡張でログインするのが良いと思います。

# GitHub Cliでのログイン
gh auth login

cli.github.com

GitHub Actionsでインストール方法

パッケージの作成や、認証済みの環境でのパッケージの追加は非常に簡単ですが、GitHub Actionsからパッケージのインストールを行う場合はひと手間必要です。

同一オーガニゼーション内であってもGitHub Actionsからは他のリポジトリにアクセスできません。 そのためアクセス権を持ったトークンを発行してからパッケージをインストールします。

トークンの発行にはGitHub Appを用いた方法とパーソナルアクセストークンを用いる方法があります。 次の観点からパーソナルアクセストークンは用いずGitHub Appを利用することをお勧めします。

  • 個人に紐づくため退職時などアカウントがプライベートなスループから外れた際使えなくなる
  • 期限付きのトークンを都度発行するのは運用コストが高い
  • 期限なしのトークンをパッケージ利用者ごと渡すと漏洩時にリスクが高い

GitHub Appのトークンは8時間有効だそうです(期限なしに設定はできますが、細かい有効期限設定は見つけられませんでした)。

インストールまでの流れ

  1. GitHub App作成 (1度だけで良い)
  2. GitHub Appからパッケージのあるリポジトリへのアクセス権をもつトークンを取得
  3. 取得したトークンを使用する設定
  4. インストール

GitHub App作成

GitHub Appの作り方自体は他の記事に譲ります。

zenn.dev

zenn.dev

以下の権限を付与したAppを作成してください。

  • Contentsにread-only権限
  • パッケージのあるリポジトリへのアクセス権限
  • パッケージを参照するリポジトリへのアクセス権限
  • 発行するトークンは期限付き

そしてGitHub Appにidとプライベートキーがあるのでこれらをパッケージをインストールしたいリポジトリのsecretsにそれぞれ、secrets.APP_ID, secrets.PRIVATE_KEYとしてパッケージを利用するリポジトリに保存しておきます*2

GitHub Actionsのyamlファイル

github-app-tokenを利用して先程secretsに保存したAPP_IDとPRIVATE_KEYからトークンを発行して、それをgitの設定に利用させるようにしてからインストールします。

︙略
      - name: Generate github token
        id: generate_token
        uses: tibdex/github-app-token@v1
        with:
          app_id: ${{ secrets.APP_ID }}
          private_key: ${{ secrets.PRIVATE_KEY }}
      - name: Install 
        run: |
          git config --global url."https://x-access-token:${ACCESS_TOKEN}@github.com/organization名".InsteadOf https://github.com/organization名
          poetry install
        env:
          ACCESS_TOKEN: ${{ steps.generate_token.outputs.token }}

︙略

Dockerfileをビルドしたい場合は次のように行います。

︙略
      - name: Generate github token
        id: generate_token
        uses: tibdex/github-app-token@v1
        with:
          app_id: ${{ secrets.APP_ID }}
          private_key: ${{ secrets.PRIVATE_KEY }}

      - name: docker build
        env:
          ACCESS_TOKEN: ${{ steps.generate_token.outputs.token }}
          DOCKER_BUILDKIT: 1
        run: |
          docker build --secret id=ACCESS_TOKEN . 
︙略

こちらがDockerfileです。 期限付きのトークンではありますが、マルチステージビルドやsecretオプションを使って情報をイメージに残さないようにしましょう。ここではsecretのオプションを使っています。

︙略
RUN --mount=type=secret,id=ACCESS_TOKEN \
          git config --global url."https://x-access-token:${ACCESS_TOKEN}@github.com/organization名".InsteadOf https://github.com/organization名
︙略

1リポジトリで複数のパッケージを管理する

プライベートで作るパッケージ群を一つのリポジトリにしたいこともあると思いますが現状のpoetry 1.1系ではできないようです。

先日対応させるためのPRがmasterにマージされたので今後使えるようになることを期待しましょう。

github.com

ちなみにpipではsubdirectoryを指定することで以下のように複数パッケージを1つのリポジトリで管理できます。

pip install "git+https://github.com/XXXX/XXXXXXX@BRANCH#subdirectory=パッケージのあるディレクトリ"

おわりに

Pythonでパッケージを公開しようと思ったことがなかったのでとても勉強になりました。GitHubでの運用が思った以上に便利でびっくりしました。

アナウンス

求人

私の所属するML Platformチームを含む、研究開発部Architectグループでは一緒に働く仲間を募集しています。最近募集の給与レンジが上がりました。私の所属する中部支社勤務も上がりましたのでオススメです。是非一緒に働きましょう。

R&D MLOps/DevOpsエンジニア / Sansan株式会社

*1:2022/08/15現在β版です。

*2:オーガニゼーションシークレットに入れておくと管理しやすいです

© Sansan, Inc.