Skip to content

Hasenpfote/python-poetry-example

Repository files navigation

Lint Test codecov

pyenv + poetry + tox + pytest 環境構築例

要約

python で pyenv + poetry + tox + pytest に optional で

  • pytest の拡張

    pytest-mock + pytest-cov

  • linter/formatter

    black + isort + flake8 + mypy

  • API の文書化

    pdoc

  • 動的バージョニング

    poetry-dynamic-versioning

  • CI/CD

    gh-actions

特に注釈がなければ, 各種情報の背景は 2022年12月あたり.
本例では python 3.7 環境でエラーが発生するようにしている.

前提条件

  • OS は Linux または Windows を想定
  • 任意の python 環境は pyenv で構築
    Windows の場合は pyenv-win で代替
  • 仮想環境は poetry で構築
    グループ機能が便利なのでバージョンは 1.2 以上.
  • 本例では python 3.7 / 3.8 を想定
    3.8 は現行バージョンで, 3.7 は後方互換の最小バージョンとした開発を行っている状況など.
    ※ 3.7 未満は依存関係がカオスなので全力回避
  • テストは tox + pytest
  • 面倒なことはしない
flowchart TD

  subgraph system
    id11(python >= 3.7)
    id12(poetry >= 1.2)

    subgraph pyenv
      id21(python = 3.7)
      id22(python = 3.8)
    end

    subgraph project
      id31(.venv)
      subgraph .tox
        id41(py37)
        id42(py38)
      end
    end
  end

  id12 .-> id11
  id12 --> |poetry run python| id31
  id12 --> |poetry run tox| .tox
  id41 .-> id21
  id31 & id42 .-> id22

基本

準備

pyenv

インストール
for Linux
for Windows

選択肢が多いので Installation からお好みで.

Chocolatey 以外は環境変数の設定が必要になるので詳細を参考に.

python

インストール

候補を確認

$ pyenv install -l

とある環境での最新は 3.7.9 / 3.8.10 だったのでインストール

$ pyenv install 3.7.9
$ pyenv install 3.8.10

インストール済みバージョンの確認

$ pyenv versions

poetry

インストール

[2022/12/07] この時点での最新は 1.2.2 で ドキュメント より

System requirements

Poetry requires Python 3.7+. It is multi-platform and the goal is to make it work equally well on Linux, macOS and Windows.

予めシステムに python 3.7 以上をインストール要.

poetry を動作させるために必要であって, プロジェクトへの制約ではない.

Installation を参考にお好みで.

設定
プロジェクト下に仮想環境を構築

設定を確認

$ poetry config --list

virtualenvs.in-project = null なら下記を一度だけ実行

$ poetry config virtualenvs.in-project true

もしもプロジェクト毎に有効にしたいのであれば

$ poetry config virtualenvs.in-project true --local

好みの問題ではあるが, 本例では全体設定としておく.

手順

1. 新規プロジェクトを作成

任意の場所で

$ poetry new --src python-poetry-example

ディレクトリ

python-poetry-example
├─ pyproject.toml
├─ README.md
├─ src
│  └─ python_poetry_example
│      └─ __init__.py
└─tests
   └─ __init__.py

2. 使用する python を指定

プロジェクトルートに移動

$ cd python-poetry-example

以降は特に注釈がない限りはプロジェクトルートでコマンドを実行している

使用する python を指定

$ pyenv local 3.8.10 3.7.9

有効になっているバージョンを確認

$ python -V
Python 3.8.10

本例では 3.8.10 を現行バージョンと想定している.

特記事項

動作確認を行った環境では,

  • pyenv local 3.8.10 3.7.9 なら python -V3.8.10
  • pyenv local 3.7.9 3.8.10 なら python -V3.7.9

最初に指定をしたバージョンが有効になる.

仮想環境の構築にも影響があるので要注意.

ディレクトリ

python-poetry-example
├─ .python-version (New!)
├─ pyproject.toml
├─ README.md
├─ src
│  └─ python_poetry_example
│      └─ __init__.py
└─tests
   └─ __init__.py

3. pyproject.toml でバージョンを指定

以下のように指定をしておく.

[tool.poetry.dependencies]
python = "^3.7"

意味は 3.7 <= python version < 4.0 と若干緩め.

依存するモジュールによっては制限を厳しくする必要もある.

4. 仮想環境を構築

3.8.10 ベースで構築.

$ poetry env use python

確認は

$ poetry env info

ディレクトリ

python-poetry-example
├─ .python-version
├─ pyproject.toml
├─ README.md
├─ .venv (New!)
│  └─ ...
├─ src
│  └─ python_poetry_example
│      └─ __init__.py
└─tests
   └─ __init__.py

5. コードを配置

適当な機能をまとめた utils.py と, そのテストコードを配置する.

ディレクトリ

python-poetry-example
├─ .python-version
├─ pyproject.toml
├─ README.md
├─ .venv
│  └─ ...
├─ src
│  └─ python_poetry_example
│      ├─ __init__.py
│      └─ utils.py (New!)
└─tests
   ├─ __init__.py
   └─ test_utils.py (New!)

6. テスト環境の準備

poetry add でも構わないが, 本例では諸事情から pyproject.toml に直接記述をする.

[tool.poetry.group.dev]
optional = true

[tool.poetry.group.dev.dependencies]
tox = "^3.27.1"

[tool.poetry.group.test]
optional = true

[tool.poetry.group.test.dependencies]
pytest = "^7.2.0"

[tool.pytest.ini_options]
addopts = [
  "--import-mode=importlib",
]
pythonpath = "src"
  • [2022/12/09] tox >= 4.0.0 で仮想環境を認識できない不具合を確認
    Linux Mint 21 / Windows10 共に, アクティブになっている1つの環境しか認識されない.
    本例では 3.7 が認識されずスキップされる.
    issue では Tox 4 breaks in CI/CD pipelines where user does not exist #2702 が似ている.
    4系で環境変数周りが大幅に変更された影響と思われるので様子見が必要.

インストール

$ poetry install --with dev

インストールでエラーがでるようならロックファイルの更新で対応

$ poetry update

確認

$ poetry show

7. tox.ini の配置

[tox]
envlist =
    py{37,38}
skipsdist = true
isolated_build = true

[testenv]
allowlist_externals =
    poetry
commands_pre =
    poetry install --with test -v
commands =
    poetry run pytest -v
  • py37 / py38pyenv local で指定されたバージョンを参照する tox 側のキーワード.
    もしも 3.11.x が必要なら py311 となる.

ディレクトリ

python-poetry-example
├─ .python-version
├─ poetry.lock
├─ pyproject.toml
├─ README.md
├─ tox.ini (New!)
├─ .venv
│  └─ ...
├─ src
│  └─ python_poetry_example
│      ├─ __init__.py
│      └─ utils.py
└─tests
   ├─ __init__.py
   └─ test_utils.py

8. テストを実行

$ poetry run tox

本例では python 3.7 環境でエラーが発生するようにしている.

ディレクトリ

python-poetry-example
├─ .python-version
├─ poetry.lock
├─ pyproject.toml
├─ README.md
├─ tox.ini
├─ .tox (New!)
│  └─ ...
├─ .venv
│  └─ ...
├─ src
│  └─ python_poetry_example
│      ├─ __pycache__
│      │  └─ ...
│      ├─ __init__.py
│      └─ utils.py
└─tests
   ├─ __pycache__
   │  └─ ...
   ├─ __init__.py
   └─ test_utils.py

各種ファイル

pyproject.toml

[tool.poetry]
name = "python-poetry-example"
version = "0.1.0"
description = ""
authors = ["name <email>"]
readme = "README.md"
packages = [{include = "python_poetry_example", from = "src"}]

[tool.poetry.dependencies]
python = "^3.7"

[tool.poetry.group.dev]
optional = true

[tool.poetry.group.dev.dependencies]
tox = "^3.27.1"

[tool.poetry.group.test]
optional = true

[tool.poetry.group.test.dependencies]
pytest = "^7.2.0"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.pytest.ini_options]
addopts = [
  "--import-mode=importlib",
]
pythonpath = "src"

tox.ini

[tox]
envlist =
    py{37,38}
skipsdist = true
isolated_build = true

[testenv]
allowlist_externals =
    poetry
commands_pre =
    poetry install --with test -v
commands =
    poetry run pytest -v

.gitignore

/.tox
/.venv
/.python-version
/poetry.lock

__pycache__/
  • .python-version は環境次第でマイナーバージョンに大きな差があるため.
  • poetry.lock はお好みで.
  • 最終的にはPython.gitignoreを元に調整.

拡張

基本で目的は果たしているものの, 心許ないので一手間を加える場合もある.

好みもあるので以降は optional となる.

拡張 1 - pytest の拡張

準備

./pyproject.toml

...
[tool.poetry.group.test]
optional = true

[tool.poetry.group.test.dependencies]
pytest = "^7.2.0"
pytest-mock = "^3.10.0"
pytest-cov = "^4.0.0"
...

./tox.ini

[tox]
envlist =
    clean
    py{37,38}
    report
skipsdist = true
isolated_build = true

[testenv]
allowlist_externals =
    poetry
commands_pre =
    poetry install --with test -v
commands =
    poetry run pytest --cov --cov-append --cov-report=term-missing -v

[testenv:clean]
deps = coverage
skip_install = true
commands_pre = # nop
commands =
    poetry run coverage erase

[testenv:report]
deps = coverage
skip_install = true
commands_pre = # nop
commands =
    poetry run coverage report -m
    poetry run coverage html

...

手順

  • インストール

    $ poetry install --with test
  • 実行

    $ poetry run tox -r -e clean,py37,py38,report

    並列で行う場合は,

    ./tox.ini

    ...
    [testenv]
    ...
    depends =
        py{37,38}: clean
        report: py{37,38}
    # Required if -p is enabled on Windows.
    # https://github.com/tox-dev/tox/pull/2641
    setenv =
        PYTHONIOENCODING=utf-8
    ...

    を追記し

    $ poetry run tox -r -e clean,py37,py38,report -p all

    順序は depends で解決するので適当でもよい.

拡張 2 - linter/formatter

構成

solution
  • black

    コードの整形に利用.

  • isort

    import の並び替えに利用.

  • flake8p

    構文チェックに利用.

    poetry 経由で利用するのでオリジナルは使えない.

    [2022/12/11] pflake8max-line-length が反映されなかったので選択肢を変更した.

    79 / 88 / 120 宗派のため.

  • mypy

    型ヒントに利用.

    取り敢えず組み込んでおき, いつでも optional から変更できるように配慮.

flowchart LR

  subgraph solution
    subgraph formatter
      black
      isort
      black .- isort
    end
    subgraph linter
      flake8
      mypy
    end
  end
環境

vscode 環境とそれ以外を考慮しつつ, 最終的にリポジトリへのコミットで同一性を担保する方向で.

  • solution の設定は pyproject.toml で一元管理
  • vscodepoetry で作成した仮想環境を利用するように設定
  • tox に確認用の環境を仕込んでおく
  • pre-commit でコミットをフックする
flowchart LR

  subgraph tox
    id_tox(solution)
  end

  subgraph poetry
    id_poetry(solution)
  end

  subgraph vscode
    id_vscode(solution)
  end

  subgraph pre-commit
    id_pre-commit(solution)
  end

  tox .-o poetry

  id_terminal(terminal)
  id_py(*.py)
  id_repo[(repo)]
  id_git(git)

  id_terminal .-> id_git
  vscode .-> id_git

  id_terminal --> |poetry run tox solution| id_tox --> |dry-run| id_py
  id_terminal ---> |poetry run solution| id_poetry --> |run| id_py
  id_vscode --> id_py

  id_py .-o id_repo
  id_pre-commit --> id_py
  id_git .-> |git commit| pre-commit .-> id_repo

準備

./pyproject.toml

...
[tool.poetry.group.dev.dependencies]
black = "^22.10.0"
isort = "^5.10.1"
Flake8-pyproject = "^1.2.2"
mypy = "^0.991"
pre-commit = "^2.20.0"
tox = "^3.27.1"

...
[tool.black]
line-length = 88
skip-string-normalization = true

[tool.isort]
profile = "black"
line_length = 88
multi_line_output = 3
include_trailing_comma = true

[tool.flake8]
max-line-length = 88
extend-ignore = ["E203", "E266", "W503",]
max-complexity = 10
extend-exclude = [".venv", "dist", ".github",]

[tool.mypy]
ignore_errors = true
#disallow_untyped_defs = true
#ignore_missing_imports = true
#no_implicit_optional = true
#show_error_context = true
#show_column_numbers = true
#warn_return_any = true
#warn_unused_ignores = true
#warn_redundant_casts = true
exclude = ["dist/",]

...

mypyignore_errors = true で実質的に無効にしているので注意.

./tox.ini

[tox]
envlist =
    py{37,38}
    black
    isort
    flake8
    mypy
skipsdist = true
isolated_build = true

...

[testenv:black]
deps = black
commands_pre = # nop
commands =
    poetry run black . --check --diff --color

[testenv:isort]
deps =
    isort
    colorama
commands_pre = # nop
commands =
    poetry run isort . --check --diff --color

[testenv:flake8]
deps = Flake8-pyproject
commands_pre = # nop
commands =
    poetry run flake8p .

[testenv:mypy]
deps = mypy
commands_pre = # nop
commands =
    poetry run mypy .

./.pre-commit-config.yaml

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: check-added-large-files
      - id: check-toml
      - id: check-yaml
      - id: end-of-file-fixer
      - id: mixed-line-ending
        args: [--fix=lf]
      - id: trailing-whitespace
        args: [--markdown-linebreak-ext=md]
  - repo: https://github.com/psf/black
    rev: 22.12.0
    hooks:
      - id: black
        language_version: python3
  - repo: https://github.com/pycqa/isort
    rev: 5.10.1
    hooks:
      - id: isort
  - repo: https://github.com/pycqa/flake8
    rev: 6.0.0
    hooks:
      - id: flake8
        additional_dependencies: [Flake8-pyproject]
  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v0.991
    hooks:
      - id: mypy

手順

  • インストール

    $ poetry install --with dev
  • vscode 環境以外で直接実行

    $ poetry run black .
    $ poetry run isort .
    $ poetry run flake8 .
    $ poetry run mypy .
  • vscode 環境以外で tox 経由で実行

    $ poetry run tox black
    $ poetry run tox isort
    $ poetry run tox flake8
    $ poetry run tox mypy
  • pre-commit を編集時に適用する

    $ poetry run pre-commit install

補足

  • 実行パス . の由来や除外するディレクトリ
    • black
      デフォルト動作は .gitignore を利用する.
      思想が垣間見れるのは, ココココ.
    • isort
      Compatibility with black を謳っているので追従している.
    • flake8
      何もしない.
    • mypy
      デフォルト動作は隠しディレクトリを除外する.

拡張 3 - API の文書化

ライブラリ色の強いプロジェクトでは API の文書化が必要なので, ここでは pdoc を利用した方法を示す.

sphinx でも大差はない.

準備

./pyproject.toml

...
[tool.poetry.group.docs]
optional = true

[tool.poetry.group.docs.dependencies]
pdoc = "^12.3.0"
tomli = {version = "^2.0.1", python = "<3.11"}
...

./docs/make.py

from pathlib import Path

import pdoc

try:
    import tomllib
except ModuleNotFoundError:
    import tomli as tomllib

if __name__ == '__main__':
    here = Path(__file__).parent
    project_root_dir = here / '..'

    toml_path = project_root_dir / 'pyproject.toml'
    with open(toml_path, mode='rb') as f:
        toml_dict = tomllib.load(f)
        project_name = toml_dict['tool']['poetry']['name']
        module_name = toml_dict['tool']['poetry']['packages'][0]['include']
        version = toml_dict['tool']['poetry']['version']

    # Render docs
    pdoc.render.configure(
        docformat='google',
        footer_text=f'{project_name} {version}',
    )

    pdoc.pdoc(
        project_root_dir / 'src' / module_name,
        output_directory=project_root_dir / 'docs/build',
    )

宗教上の理由がある場合は下記を参照.

...use numpydoc or Google docstrings?

手順

  • インストール

    $ poetry install --with docs
  • 実行

    $ poetry run python ./docs/make.py

拡張 4 - 動的バージョニング

VCS を基点に動的なバージョン管理を行う.
poetry-dynamic-versioning を利用する.

利用するのはデフォルト動作で, poetry build を実行している間だけ有効であることに注意.

flowchart LR

  id_1(start)
  id_2(end)

  subgraph sdist/wheel
    subgraph targets
      id_t1[pyproject.toml]
      id_t2[__init__.py]
      id_t3[__version__.py]
      id_t3 .-o id_t2
    end
  end

  subgraph versioning
    id_v1[git tag v0.1.0]
    subgraph build
      id_b1[poetry build]
    end
    id_v1 --> build
  end

  build .-> sdist/wheel
  id_1 ==> versioning ==> id_2

準備

Installation

"0.0.0" の由来は

Poetry's typical version setting is still required in [tool.poetry], but you are encouraged to use version = "0.0.0" as a standard placeholder.

./pyproject.toml

[tool.poetry]
...
version = "0.0.0"
...
[build-system]
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"]
build-backend = "poetry_dynamic_versioning.backend"

[tool.poetry-dynamic-versioning]
enable = true
vcs = "git"
style = "pep440"

[tool.poetry-dynamic-versioning.substitution]
folders = [
  { path = "src" }
]
...

./src/python_poetry_example/__version__.py

__version__ = '0.0.0'

./src/python_poetry_example/__init__.py

from .__version__ import __version__

手順

  • プラグインのインストール

    $ poetry self add "poetry-dynamic-versioning[plugin]"
  • プラグインのアンインストール

    $ poetry self remove poetry-dynamic-versioning
  • バージョニング

    1. タグをつける

      v0.0.0 から v0.1.0 にする場合

      $ git tag v0.1.0
    2. ビルドを実行する

      $ poetry build

    ./dist 内に含まれる対象/関連ファイルのみ影響を受ける.

    よってリリース後の python_poetry_example.__version__ などは意図した動作になる.

連携

  • 拡張 3 - API の文書化 と連携する場合について

    poetry-dynamic-versioning を手動で実行する方法も考えられるがとても煩雑になる.

    よって VCS からタグを取得する方向で済ませる.

    flowchart LR
    
      subgraph versioning
        id_v1[git tag v0.1.0]
      end
    
      subgraph documentation
        id_d1[poetry run python ./docs/make.py]
      end
    
      build[(./docs/build)]
    
      versioning ==> documentation
      id_d1 .-> build
    

    ./pyproject.toml

    ...
    [tool.poetry.group.docs]
    optional = true
    
    [tool.poetry.group.docs.dependencies]
    pdoc = "^12.3.0"
    tomli = {version = "^2.0.1", python = "<3.11"}
    GitPython = "^3.1.29"
    ...

    ./docs/make.py

    import os
    from pathlib import Path
    
    import git
    import pdoc
    
    try:
        import tomllib
    except ModuleNotFoundError:
        import tomli as tomllib
    
    if __name__ == '__main__':
        here = Path(__file__).parent
        project_root_dir = here / '..'
    
        toml_path = project_root_dir / 'pyproject.toml'
        with open(toml_path, mode='rb') as f:
            toml_dict = tomllib.load(f)
            project_name = toml_dict['tool']['poetry']['name']
            module_name = toml_dict['tool']['poetry']['packages'][0]['include']
            version = toml_dict['tool']['poetry']['version']
    
        if version == '0.0.0':
            repo_dir = project_root_dir / '.git'
            if os.path.isdir(repo_dir):
                repo = git.Repo(repo_dir)
                if repo.tags:
                    tags = sorted(repo.tags, key=lambda t: t.commit.committed_datetime)
                    latest_tag = tags[-1]
                    version = str(latest_tag)
                    if version.startswith('v'):
                        version = version[1:]
    
        # Render docs
        pdoc.render.configure(
            footer_text=f'{project_name} {version}',
        )
    
        pdoc.pdoc(
            project_root_dir / 'src' / module_name,
            output_directory=project_root_dir / 'docs/build',
        )

拡張 5 - CI/CD

ここでは GitHub Actions を利用した例となる.

デプロイ先は

flowchart LR

  event_1(on: push)
  event_2(on: pull_request)
  event_3(on: release)
  event_4(on: workflow_dispatch)

  subgraph test.yml
  direction LR
    id_t1[black\ndry-run]
    id_t2[isort\ndry-run]
    lint-pr
    lint-local
    mypy
    test
    id_t1 --> id_t2
    id_t2 --> lint-local --> mypy
    id_t2 --> lint-pr --> mypy
    mypy --> test
  end

  subgraph testpypi.yml
    testpypi_1[build]
    --> testpypi_2[deploy]
  end

  subgraph docs.yml
    docs_1[build]
    --> docs_2[deploy]
  end

  event_1 ==> test.yml
  event_2 ==> test.yml
  event_3 ==> testpypi.yml
  testpypi_2 .-> o1[(TestPyPI)]
  testpypi.yml ====> |on: workflow_call| docs.yml
  event_4 ==> docs.yml
  docs_2 .-> o2[(gh-pages)]

準備

./github/python-version.txt

3.8

各ファイルで用いる, 開発上の現行バージョンを指定.

./github/workflows/test.yml

name: Test

on: [ push, pull_request ]

permissions:
  contents: read

jobs:
  black:
    if: true
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version-file: .github/python-version.txt
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install 'poetry>=1.2'
          poetry install --with dev --no-interaction
      - name: Lint with black
        run: |
          poetry run tox -e black

  isort:
    if: true
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version-file: .github/python-version.txt
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install 'poetry>=1.2'
          poetry install --with dev --no-interaction
      - name: Lint with isort
        run: |
          poetry run tox -e isort

  lint-pr:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: TrueBrain/actions-flake8@v2

  lint-local:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version-file: .github/python-version.txt
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install 'poetry>=1.2'
          poetry install --with dev --no-interaction
      - name: Lint with flake8
        run: |
          poetry run tox -e flake8

  mypy:
    if: true
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version-file: .github/python-version.txt
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install 'poetry>=1.2'
          poetry install --with dev --no-interaction
      - name: Lint with mypy
        run: |
          poetry run tox -e mypy

  test:
    runs-on: ubuntu-20.04
    strategy:
      fail-fast: false
      matrix:
        python-version: ['3.7', '3.8']
    steps:
    - uses: actions/checkout@v3
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        python -m pip install 'poetry>=1.2'
        poetry install --with dev --no-interaction
    - name: Test with pytest
      run: |
        poetry run tox -e py

test は対象範囲が変わる都度, 設定を変更する必要がある.

./github/workflows/testpypi.yml

name: Publish the package to TestPyPI

on:
  workflow_dispatch:
  release:
    types: [published]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
      with:
        fetch-depth: 0
    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version-file: .github/python-version.txt
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        python -m pip install 'poetry>=1.2'
        poetry self add 'poetry-dynamic-versioning[plugin]'
    - name: Build
      run: |
        poetry build
    - name: publish
      env:
        TESTPYPI_API_TOKEN: ${{ secrets.TESTPYPI_API_TOKEN }}
      run: |
        poetry config repositories.test-pypi https://test.pypi.org/legacy/
        poetry config pypi-token.test-pypi $TESTPYPI_API_TOKEN
        poetry publish -r test-pypi

  docs:
    if: true # true if documentation is needed.
    needs: deploy
    permissions:
      contents: read
      pages: write
      id-token: write
    uses: ./.github/workflows/docs.yml

docs は API の文書化が不要なら false に設定.

./github/workflows/docs.yml

# Workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages

# build the documentation whenever there are new commits on main
on:
  workflow_dispatch:
  workflow_call:

# security: restrict permissions for CI jobs.
permissions:
  contents: read

jobs:
  # Build the documentation and upload the static HTML files as an artifact.
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - uses: actions/setup-python@v4
        with:
          python-version-file: .github/python-version.txt
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install 'poetry>=1.2'
          poetry install --with docs --no-interaction
      - name: Build
        run: |
          poetry run python ./docs/make.py
      - uses: actions/upload-pages-artifact@v1
        with:
          path: docs/build/

  # Deploy the artifact to GitHub pages.
  # This is a separate job so that only actions/deploy-pages has the necessary permissions.
  deploy:
    needs: build
    runs-on: ubuntu-latest
    permissions:
      pages: write
      id-token: write
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - id: deployment
        uses: actions/deploy-pages@v1

手順

  • on: push

    $ git push
  • on: pull_request

    PR

  • on: release

    $ git tag v0.1.0
    $ git push origin v0.1.0

    v0.1.0 をリリース

    Managing releases in a repository

  • on: workflow_dispatch

    デバッグ用に手動でイベントをキック.

補足

  • on: workflow_dispatch

    docs.yml を手動で実行すると main ブランチ(default)が渡される.

  • on: workflow_call

    testpypi.yml 経由で実行されると on: releasev0.1.0 ブランチが渡される.

    gh-pages のブランチ保護ルールに後者が利用できるように追記が必要.
    例えば v* などとしておく.

キャッシュ

Poetry や依存関係のキャッシュを行うことでアクションの実行時間を低減する.
分量が多いため issue で扱う.

参考