kakakakakku blog

Weekly Tech Blog: Keep on Learning!

GitHub Actions でワークフローの同時実行を防ぐ concurrency 設定

GitHub Actions ではデフォルトの挙動として同じワークフローの複数のジョブを同時実行できる.無駄に待つ必要がないという意味ではメリットがあるけど,ワークフローによっては同時実行したくないこともあると思う.

GitHub Actions でワークフローが複数トリガーされてしまって慌てて止めたという経験もあったりする😅例えばワークフローの実行時間が長く,完了する前に次のコミットをプッシュしてしまったり,ワークフローの実行が完了する前にプルリクエストをマージしてしまったり💨

concurrency 設定

GitHub Actions ではコンカレンシー (concurrency) という設定があって,ワークフローの同時実行を制御できる.今回はワークフローレベルで試すけど,ジョブレベルで細かく制御することもできる❗️個人的にはとりあえず設定しておいても良さそうかなと思う.

docs.github.com

concurrency サンプル1(待機)

以下の例では GitHub ドキュメントを参考に concurrency.group${{ github.workflow }}-${{ github.ref }} を設定したため concurrency-refs/pull/1/merge のような名前になり「プルリクエストレベルで」同時実行を制御することになる💡そして concurrency.cancel-in-progressfalse を設定しているため,後続のトリガーはキャンセルにならず実行待機になってから実行される🛑

name: concurrency

on:
  workflow_dispatch:
  push:
    branches:
      - develop
  pull_request:
    branches:
      - develop

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: false

jobs:
  sleep:
    runs-on: ubuntu-latest
    steps:
      - name: Sleep
        run: sleep 60

concurrency サンプル2(キャンセル)

concurrency.cancel-in-progresstrue を設定すると,実行中のワークフローをキャンセルして最新のワークフローを実行できる🙆‍♂ 例えばデプロイ関連のワークフローであれば最終的に後に実行されたワークフローでデプロイされるため,ワークフローの無駄な実行を回避できて良いと思う👌

name: concurrency

on:
  workflow_dispatch:
  push:
    branches:
      - develop
  pull_request:
    branches:
      - develop

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  sleep:
    runs-on: ubuntu-latest
    steps:
      - name: Sleep
        run: sleep 60

自信を持って pytest を活用するためのノウハウが凝縮された「テスト駆動 Python 第2版」を読んだ

「テスト駆動 Python 第2版」を読んだ📕

仕事で pytest を使ってて,もっと自信を持って書けるようになりたいな〜と思っていたら本書を見つけてさっそく読んでみた.pytest の機能・記法・設定・Tips などの理解が深まって本当に読んで良かった❗️フィクスチャ・パラメータ化・モック・プラグイン活用など,今まで何となく書いてたところを自信を持って書けるようになって,仕事で pytest を書くのが楽しくなった🦄

もちろん pytest の公式ドキュメントを読むべきだし,本書の内容の多くは公式ドキュメントにも載っているとは思うけど,本書の翻訳はとても読みやすく,pytest の全体像をサッと把握できて,また Cards というサンプルアプリケーションを題材に実際に pytest を試しながら読み進められるから本書を読む価値はあると思う.個人的には本当に読んで良かったな〜と感じてる👍

docs.pytest.org

目次

目次をザッと見て,気になる Chapter があったら読んでみると良いかと👌

  • Part.1: pytest の主力機能
    • Chapter.1: はじめての pytest
    • Chapter.2: テスト関数を書く
    • Chapter.3: pytest のフィクスチャ
    • Chapter.4: 組み込みフィクスチャ
    • Chapter.5: パラメータ化
    • Chapter.6: マーカー
  • Part.2: プロジェクトに取り組む
    • Chapter.7: 戦略
    • Chapter.8: 設定ファイル
    • Chapter.9: カバレッジ
    • Chapter.10: モック
    • Chapter.11: tox と継続的インテグレーション
    • Chapter.12: スクリプトとアプリケーションのテスト
    • Chapter.13: テストの失敗をデバッグする
  • Part.3: ブースターロケット
    • Chapter.14: サードパーティプラグイン
    • Chapter.15: プラグインの作成
    • Chapter.16: 高度なパラメータ化

本書で使う Cards アプリケーションのコードは原著サイトから ZIP でダウンロードできる.

pragprog.com

Chapter.4

pytest で一時的なディレクトリを作れる組み込みフィクスチャ tmp_path(function スコープ)と tmp_path_factory(session スコープ)はさっそく使えそうだった.

docs.pytest.org

他にも Chapter.4 で紹介されてた組み込みフィクスチャは便利で capsys は別途試して簡単にまとめた.

kakakakakku.hatenablog.com

Chapter.8

pytest.initestpaths を設定するのは明確にはなるけど冗長だよなぁ〜と思っていたらtestpaths を指定しておくと pytest 開始時の時間を少し節約できる」と書いてあって発見だった👀ある程度プロジェクトの規模が大きくないと差は出なさそうではあるけど知れて良かった情報の一つだった👍

[pytest]
testpaths = tests

docs.pytest.org

Chapter.12

スクリプトとテストコードを src ディレクトリと tests ディレクトリに分割したら import できずにハマるというのはよくあると思う💨僕自身も最初ハマって先輩に相談して教えてもらった経緯もあったりする.本書の第2版では Chapter.12 に Python の検索パスの解説が追加されていて良かった❗️本書を読んで pytest の import にハマる人が減ると良いなぁ〜 \( 'ω')/

.
├── pytest.ini
├── src
│   └── hello.py
└── tests
    └── test_hello.py

本書では pytest.inipythonpath を設定する例が紹介されていた.

[pytest]
pythonpath = src

もちろん pytest.ini ではなく pyproject.toml に設定することもできる👌

[tool.pytest.ini_options]
pythonpath = "src"

Chapter.14

pytest プラグインは今までほとんど活用できてなかった💨 pytest サイトの Plugin List を見ながら気になったプラグインをさっそく導入してみて便利❗️

github.com

github.com

github.com

github.com

Chapter.16

@pytest.mark.parametrize でパラメータ化するのは普段から使っているけど,idids にテスト識別子を設定するという Tips は今まで活用できてなかった.確かにパラメータ化したテストで失敗すると判別しにくく感じるときもあった.さっそく仕事でも使うようにした👌

docs.pytest.org

その他

他に読書メモに残したことを箇条書きにしておく❗️

  • pytest --setup-show でフィクスチャの実行順をログに出力できる
  • pytest --tb=no でテスト失敗時のトレースバックを非表示にできる
  • pytest --showlocals でテスト失敗時にローカル変数を表示できる
  • conftest.py でフィクスチャを共有できる
  • @pytest.fixture の名前を変更できる
  • requests をモックできる Responses が便利そう

誤植

出版社サイトに掲載されていない誤植を見つけたのでメモしておく📝

  • P.120: 対処できるしょうか。対処できるでしょうか。
  • P.122: 簡単かかもしれないが簡単かもしれないが

www.shoeisha.co.jp

X ポスト🔗

pytest の capsys で stdout(標準出力)と stderr(標準エラー)をテストする

pytest の capsys を使うと Python スクリプトで出力する stdout(標準出力)と stderr(標準エラー)をテストできる❗️関数の実行結果ではなく,その途中に出力するログに着目したい場面もあって便利〜 \( 'ω')/

docs.pytest.org

👾 src/app.py

hello() 関数は HelloWorld! を stdout と stderr に出力して,version() 関数は Python バージョンを stdout に出力する.サンプルコードなので特に意味はないけど今回はこの関数をテスト対象にする💡

import platform
import sys


def hello():
    print('Hello')
    print('World!', file=sys.stderr)


def version():
    print(platform.python_version())

👾 tests/test_app.py

テストコードでは capsys.readouterr() を使って stdout と stderr を取得して簡単に assert できる👌

from app import hello, version


def test_main(capsys):
    hello()
    captured = capsys.readouterr()
    assert captured.out == 'Hello\n'
    assert captured.err == 'World!\n'


def test_version(capsys):
    version()
    captured = capsys.readouterr()
    assert captured.out == '3.12.2\n'

テスト実行

テストできた👌

$ pytest .
===================================================== test session starts =====================================================
(中略)
configfile: pytest.ini
collected 2 items

tests/test_app.py ..                                                                                                    [100%]

====================================================== 2 passed in 0.01s ======================================================

参考資料

capsys「テスト駆動 Python 第2版」の CHAPTER 4 にも載ってた📕

Terraform で無料利用枠の VPC IP Address Manager (IPAM) を設定する

2023年11月から VPC IP Address Manager (IPAM)「無料枠利用枠」が追加されて Public IP Insights などの機能が無料で使えるようになった💡そして,2024年2月から課金対象になった IPv4 の最適化のために Public IP Insights を使いたいという場面もあると思う.

aws.amazon.com

aws.amazon.com

Terraform で試す

実は Terraform AWS Provider では今まで aws_vpc_ipamtier はサポートされていなかった.今日(2024年3月29日)にリリースされた v5.43.0 でついにサポートされた❗️待ってました〜 \( 'ω')/

github.com

👾 ipam.tf

設定自体は簡単で aws_vpc_ipamtier = "free" を追加すれば OK👌デフォルトは advanced なので注意しておくと良さそう.あと今回は operating_regions にバージニア北部リージョンを設定した💡もちろん複数リージョンを設定することもできる.

resource "aws_vpc_ipam" "main" {
  tier = "free"
  operating_regions {
    region_name = "us-east-1"
  }
}

関連記事

AWS CDK で VPC IP Address Manager (IPAM) を設定する記事は過去に書いているので参考まで〜📝

kakakakakku.hatenablog.com

AWS CDK で外部パッケージを含む Python の AWS Lambda 関数をデプロイする

AWS CDK で外部パッケージを含む Python の AWS Lambda 関数をデプロイする場合,requirements.txt から依存関係を解決して,デプロイするアセットとして ZIP にまとめる(バンドルする)必要がある💡

今回は aws-cdk-lib.aws_lambda module@aws-cdk/aws-lambda-python-alpha module を使う方法を試す❗️

前提

今回はサンプルとして requests に依存したコードを以下のディレクトリ構成で置いてある前提とする \( 'ω')/

functions/requests
├── app.py
└── requirements.txt

aws_lambda module を使う

まず,AWS CDK で AWS Lambda 関数をデプロイするときによく使う aws_lambda module では Code.fromAsset のオプションとして BundlingOptions を設定できる.

docs.aws.amazon.com

実装としてはザッとこんな感じになる 👾

Python のコンテナ内で pip install コマンドを実行して /asset-output ディレクトリに依存関係をインストールしたら,後はそのまま ZIP にまとめて(バンドルして)デプロイされる👌

new aws_lambda.Function(this, 'PythonFunction', {
  functionName: 'sandbox-python-function',
  runtime: aws_lambda.Runtime.PYTHON_3_12,
  handler: 'app.lambda_handler',
  code: aws_lambda.Code.fromAsset(path.join(__dirname, '../functions/requests'), {
    bundling: {
      image: aws_lambda.Runtime.PYTHON_3_12.bundlingImage,
      command: [
        'bash', '-c',
        'pip install -r requirements.txt -t /asset-output && cp -au . /asset-output'
      ]
    }
  })
})

aws-lambda-python-alpha module を使う

まだ alpha ではあるけど aws-lambda-python-alpha module を使うと entry に指定したディレクトリにある requirements.txtPipfile から自動的に依存関係を解決してくれる👌

実装としてはザッとこんな感じになる 👾

new aws_lambda_python_alpha.PythonFunction(this, 'PythonFunctionAlpha', {
  functionName: 'sandbox-python-function-alpha',
  runtime: aws_lambda.Runtime.PYTHON_3_12,
  index: 'app.py',
  handler: 'lambda_handler',
  entry: path.join(__dirname, '../functions/requests'),
})

aws_lambda.Function のプロパティもサポートされてるし,Lambda Layer も簡単にデプロイできるし便利〜 \( 'ω')/

docs.aws.amazon.com

デプロイ確認

期待通りに requirements.txt に定義した requests をデプロイできている❗️