はじめに
先日、GitHubの料金体系が変更されましたね!
Today we’re announcing free private repositories with unlimited collaborators for teams with GitHub Free, and reducing the price of our paid Team plan to $4 per user/month. All of the core GitHub features are now free for everyone.
— GitHub (@github) April 14, 2020
Learn more: https://t.co/fQ3r2ABtTR pic.twitter.com/HHOyG5ypgU
個人的にずっとGitLabをメインに利用していましたが、今後はGitHubメインで使おうと考えています。
そこで、GitLab CIで行っていたワークフローをGitHub Actionsへ移行しました。
GitLab CIからGitHub Actionsへ、GitHub ActionsからGitLab CIへ移行を考えている方の参考になれば幸いです。
サンプル
今回サンプルで作成した、GitLab CIとGitHub Actionsの設定ファイルはこちらです。
https://gist.github.com/urbsny/ddb634e5f677e1d4d312f4a86916adc8
CIで行うワークフローについて
Djangoで作成されたwebアプリのテストしデプロイします。ざっくり下記の様な処理を行います。
-
git push
をトリガーとしてCIを実行 - テストを実行し、カバレッジを出力する。
- masterへのpushの場合、herokuへデプロイする。
それでは、GitLabとGitHubそれぞれの設定を見ていきます。
GitLabの設定
これらのフローをGitLabではステージ(stage)と表現されています。
デフォルトでは、build
-> test
-> deploy
というステージが用意されています。build
が成功したらtest
、test
が成功したらdeploy
が実行されます。
今回のサンプルでは、test
とdeploy
のステージのみを使用しました。下記、ステージに関する設定をピックアップしています。
test:
script:
- echo TODO
deploy-staging:
stage: deploy
only:
- master
script:
- echo TODO
deploy-production:
stage: deploy
only:
- tags
- /^v[0-9]+\.[0-9]+\.[0-9]+$/
script:
- echo TODO
test:
は毎回実行されます。 deploy-staging:
はmasterへのpushの場合のみ実行されます。 deploy-production:
はタグ打ちした場合のみ実行されます。
test:
ではstage:
を指定していません。ステージ名とキー同じ場合はステージの指定は不要なようです。
デフォルトで用意されているステージを使用せず、独自に定義することも可能です。詳しくは、下記を参照しましょう。
https://docs.gitlab.com/ee/ci/yaml/#stages
GitHubの設定
一方GitHubでは、それぞれのフローをジョブ(job)として表現しています。ただ、GitLabと違いデフォルトのステージのようなものはありません。
GitHubでは、 needs:
でステップの依存関係を指定する必要があります。
name: CI
on:
push:
branches:
- '*'
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo TODO
deploy-staging:
needs: test
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
steps:
- run: echo TODO
deploy-production:
needs: test
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- run: echo TODO
また、トリガーとなるアクションも on:
で定義する必要があります。サンプルではブランチ全般とv1.0.0等のバージョンタグの場合に限定しています。詳しくは、下記を参照しましょう。
https://help.github.com/ja/actions/reference/workflow-syntax-for-github-actions
特定の条件で実行したジョブの条件は、 if:
で定義します。if:
で使用可能な関数や変数はこちらを参照しましょう。
https://help.github.com/ja/actions/reference/context-and-expression-syntax-for-github-actions
テストフロー (Djangoでのテスト)
Pythonの実行環境でDjangoのテストを実行します。その際にPostgreSQLとRedisを使用する為、その設定も必要になります。また、テストのカバレッジを出力し、ダウンロードできるようにしています。
GitLabの設定
GitLabで特定のPythonのバージョンでテストを行う為には、dockerコンテナ上で実行するのが手っ取り早いと思います。 image:
でdockerイメージを指定します。
test:
image: python:3.8.2
services:
- name: postgres:12.2
alias: database
- name: redis:5.0.8
alias: cache
variables:
POSTGRES_PASSWORD: xxxxxxxx
DATABASE_URL: postgres://postgres:xxxxxxxx@database:5432/postgres
REDIS_URL: redis://cache:6379
SAMPLE_VARIABLE: 1
script:
- python -m pip install --upgrade pip
- pip install pipenv
- pipenv install -d
- pipenv run collectstatic
- pipenv run testcoverage
- pipenv run coveragehtml
artifacts:
paths:
- htmlcov/
PostgreSQLとRedisの設定を services:
で定義します。この中の alias:
で設定された値とアプリ側のPostgreSQLやRedisのホスト名を一致させる必要があります 。aliasを使用しないことも可能ですが、その場合は下記のようにサービスのイメージ名を指定します。
services:
- postgres:12.2
- redis:5.0.8
variables:
DATABASE_URL: postgres://postgres:xxxxxxxx@postgres:5432/postgres
REDIS_URL: redis://redis:6379
必要な環境変数は、 variables:
で定義します。今回のサンプルにある DATABASE_URL:
REDIS_URL:
SAMPLE_VARIABLE:
はサンプルのwebアプリが利用する特有の定義です。ただし、POSTGRES_PASSWORD:
はPostgreSQLのサービスが使用する環境変数になります。適当な値を設定し、アプリ側のPostgreSQLのパスワードと一致させましょう。
テストに必要な処理は、script:
に記載します。サンプルではPipenvを使用しPipfileに独自に定義したスクリプトを実行しています。requirements.txtを使用する場合は下記の様なスクリプトになるかと思います。
script:
- python -m pip install --upgrade pip
- pip install -r requirements.txt
- ./manage.py collectstatic --noinput
- pytest -n auto --cov
- coverage html
最後に、coverage html
コマンドで出力されたカバレッジ結果をダウンロードできるようにします。./htmlcov
配下に出力される為、下記のような定義になります。
artifacts:
paths:
- htmlcov/
GitHubの設定
GitHubでは特定のPythonのバージョンでテストを行う上で、dockerコンテナを使用しなくても簡単に環境を構築できます。uses: actions/setup-python@v1
がその為の処理になります。この場合、PostgreSQLやRedisのホストをlocalhost
にする必要があります。
test:
runs-on: ubuntu-latest
services:
database:
image: postgres:12.2
env:
POSTGRES_PASSWORD: xxxxxxxx
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
cache:
image: redis:5.0.8
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
DATABASE_URL: postgres://postgres:xxxxxxxx@localhost:5432/postgres
REDIS_URL: redis://localhost:6379
SAMPLE_VARIABLE: 1
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v1
with:
python-version: 3.8.2
- run: python -m pip install --upgrade pip
- run: pip install pipenv
- run: pipenv install -d
- run: pipenv run collectstatic
- run: pipenv run testcoverage
- run: pipenv run coveragehtml
- uses: actions/upload-artifact@v1
with:
name: coverage
path: htmlcov/
dockerコンテナで実行する場合は、container:
を指定し、ホストの指定をlocalhostからservices:
で指定した名前にします。
runs-on: ubuntu-latest
container: python:3.8.2
services:
database:
image: postgres:12.2
env:
POSTGRES_PASSWORD: xxxxxxxx
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
cache:
image: redis:5.0.8
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
env:
DATABASE_URL: postgres://postgres:xxxxxxxx@database:5432/postgres
REDIS_URL: redis://cache:6379
steps:
- uses: actions/checkout@v2
- run: python -m pip install --upgrade pip
デプロイフロー (Herokuへのデプロイ)
HerokuへのデプロイにはDplというGemを使用しています。
Herokuへのデプロイに使用する機微情報はソースにコミットせずに、それぞれの管理画面から設定した環境変数で利用します。サンプルでは、HEROKU_APP
HEROKU_API_KEY
という値を設定しています。
GitLabの設定
deploy-staging:
stage: deploy
script:
- apt-get update -qy
- apt-get install -y ruby-dev
- gem install dpl
- dpl --provider=heroku --app=$HEROKU_APP --api-key=$HEROKU_API_KEY
環境変数の設定は、再度メニューのSettings > CI/CDを開きVariablesのメニューから追加できます。
https://docs.gitlab.com/ee/ci/variables/README.html#via-the-ui
GitHubの設定
deploy-staging:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: sudo apt-get update -qy
- run: sudo apt-get install -y ruby-dev
- run: sudo gem install dpl
- run: sudo dpl --provider=heroku --app=$HEROKU_APP --api-key=$HEROKU_API_KEY
env:
HEROKU_APP: ${{ secrets.HEROKU_APP }}
HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
環境変数の設定は、下記のサイトを参考にします。
https://help.github.com/ja/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets
最後に
個人的にはGitLab CIの設定の方が好みでしたが、GitHub Actionsは公開されているActionsを使用すれば簡単に色々できそうです。
https://github.com/marketplace?type=actions
GitLabのRepository mirroringをしようすれば、それぞれの環境で別々のCIを回すなんてこともできそうですね。
https://docs.gitlab.com/ee/user/project/repository/repository_mirroring.html