Help us understand the problem. What is going on with this article?

「GitLab CI」「GitHub Actions」CIを行う方法を比較

はじめに

先日、GitHubの料金体系が変更されましたね!

個人的にずっと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アプリのテストしデプロイします。ざっくり下記の様な処理を行います。

  1. git pushをトリガーとしてCIを実行
  2. テストを実行し、カバレッジを出力する。
  3. masterへのpushの場合、herokuへデプロイする。

それでは、GitLabとGitHubそれぞれの設定を見ていきます。

GitLabの設定

これらのフローをGitLabではステージ(stage)と表現されています。
デフォルトでは、build -> test -> deployというステージが用意されています。buildが成功したらtesttestが成功したらdeployが実行されます。
今回のサンプルでは、testdeployのステージのみを使用しました。下記、ステージに関する設定をピックアップしています。

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

urbsny
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした