CI/CD何もわからん奴がCircleCIでPythonリポジトリのビルドを通すまで

はじめに

CircleCIを使ってみました。
前提知識としては、「CI/CD聞いたことあるけどどうしたらいいんだろう?」というレベルです。

まずはビルドを通すのを目指してやってみましたが、PythonでのWeb開発の知識も浅く、謎に詰まってしまったので書き残します。

CircleCIとは?

テストやデプロイの自動化をしてくれるクラウドのCIツールです。
自前でサーバを用意する必要があるJenkinsと違い、クラウドなので手軽に始められる点が魅力。

クラウドのCIツールは他にもTravisCIなどがあります。

今回はこちらの記事を参考に、まずはCircleCIを使ってみます。

新規登録からProjectの追加、ビルド実行まで

まずは、新規登録ページからGitHubアカウントでサインアップします。

次に、ビルドするプロジェクトを、GitHubリポジトリから選択します。
開発途中のWebアプリ(Python+Flask)のリポジトリをビルドしてみることにしました。

ビルドの設定は、CircleCIが用意している設定のテンプレートを使います。
本来なら言語を判定しておすすめ設定を出してくれるのですが、私のプロジェクトではそれが出なかったため、手動でPythonを選択。

Add Config

その後、対象リポジトリにcircle-project-setupというブランチが切られてそこでテストビルドが開始されます。

ビルド実行 -> Failed

ビルド実行すると、Failedとなりました。
ひとつひとつ解決していきます。

Failed

ERROR: Could not open requirements file: [Errno 2] No such file or directory: ‘requirements.txt’

まず、Restoring CacheInstall Dependenciesの両方のプロセスで、以下のエラーメッセージ。

ERROR: Could not open requirements file: [Errno 2] No such file or directory: 'requirements.txt'

requirements.txtが無いとのこと。

requirements.txtはPythonで使用しているライブラリとバージョンのリストを書き出したもので、Pythonアプリをデプロイする上でサーバに置く必要があるものです。
これを設置していなかったことが原因でした。

requirement.txtを書き出す

以下のコマンドで書き出します。

pip freeze > requirements.txt

リポジトリ直下で実行し、書き出されたファイルをcommit、pushすると、再度ビルドがはじまりました。

Could not find a version that satisfies the requirement anaconda-client==1.7.2

Restoring Cacheは通ったものの、次のエラーが。

ERROR: Could not find a version that satisfies the requirement anaconda-client==1.7.2 (from -r requirements.txt (line 2)) (from versions: 1.1.1, 1.2.2)
ERROR: No matching distribution found for anaconda-client==1.7.2 (from -r requirements.txt (line 2))

メッセージ自体は、「要求したバージョンに合うディストリビューションが無いよ」というメッセージです。

ただ、anaconda-clientは本アプリでは不要です。
ここでの根本の問題は、ビルドに不要なライブラリが多分に含まれていることでした。
システムのPython環境でpip freezeをしたために、システムに入れているライブラリがすべてrequirements.txtに書き出されてしまっていました。

解決策としては、専用の仮想環境を構築し、そこで再度freezeを実行します。

# 仮想環境構築
conda create -n circle_env
conda activate circle_env

# 必要と思われる最小限をインストール
conda install python
conda install flask

# freeze
pip freeze > requirements.txt

書き出されたrequirementts.txtを再度commit、pushします。

/bin/bash: ./manage.py: No such file or directory

前項のエラーは解消されましたが、次のエラーが。

./manage.py test
/bin/bash: ./manage.py: No such file or directory

Exited with code exit status 127

これはもう、そのままですね。

テンプレートではmanage.pyというスクリプトを実行するように設定されていますが、そのようなファイルは用意していません。
そもそもビルドの前に走らせるテストコードも書いていません。

今回の目的は一度ビルドを通すことなので、簡単なテストコードを作成し、config.ymlを更新します。

tests/test.py

import unittest

class SampleTest(unittest.TestCase):
    def setUp(self):
        pass

    def test_sample_method(self):
        expected = 1
        actual = 1
        self.assertEqual(expected, actual)

if __name__ == "__main__":
    unittest.main()

.circleci/config.yml

version: 2.1

orbs:
  python: circleci/python@0.2.1

jobs:
  build-and-test:
    executor: python/default
    steps:
      - checkout
      - python/load-cache
      - python/install-deps
      - python/save-cache
      - run:
          command: python tests/test.py
          name: Test

workflows:
  main:
    jobs:
      - build-and-test

※具体的には、 run: のあたりを書き換えてます。

上記変更をcommit、pushします。

無事ビルド成功!

ここまでやってようやくビルドが成功。
success
(何もしていないけれど、SUCCESSの文字が気持ち良い)

まとめ

CIというよりPythonのデプロイで詰まった感がすごいのですが、ひとまず通すことができました。
この試行錯誤の中で、「Pushすると自動でテストが走る」良さをすでに体感しています。これは良い。きちんと導入して回していけるようにしたいです。

参考