22
20

More than 3 years have passed since last update.

CircleCIのワークフローを書き換えながら学ぶGitHub Actions入門

Posted at

はじめに

最近、CircleCIのワークフローの一部をGitHub Actionsに書き換えました。

同様の作業をこれから行う人に向けて、本記事では

「最初にこのあたりを押さえておけば割とスムースに書き換えができるのでは?」

と思われるポイントをまとめました。

本記事では、以下の観点でCircleCIとGitHub Actionsを比較しながら、GitHub Actionsのワークフローファイルの書き方を簡単に解説していきます。

  • ワークフローファイルのパス
  • ジョブの構成
  • ソースコードのチェックアウト
  • 任意のコマンドの実行
  • キャッシュ
  • 環境変数

本記事で触れられていない点に関しては、公式のリファレンスを見るか、Boothで販売されている

が非常におすすめですので、そちらを読んでいただければと思います。

(私個人の力量ではActionsを理解するのに公式リファレンスだけでは辛かったので、上記の本には非常にお世話になりました。CircleCIの方は、公式リファレンスだけでとても分かりやすいんですけどね。。。)

1. ワークフローファイルのパス

CircleCI, GitHub Actionsそれぞれのワークフローファイルのパスは以下になります。

CircleCI GitHub Actions
.circleci/config.yml .github/workflows/test.yml
.github/workflows/deploy.yml
など任意の名前

2. ジョブの構成

2.1. CircleCIの場合

CircleCIでは、jobsでジョブを定義し、workflowsでジョブの実行順や条件を定義しています。

.circleci/config.yml
version: 2.1
jobs:
  build:
    # 略
  test:
    # 略
  deploy:
    # 略

workflows:
  version: 2
  build-test-deploy:
    jobs:
      - build
      - test:
          requires:
            - build
      - deploy:
          requires:
            - test
          filters:
            branches:
              only:
                - master          

requiresで、先行ジョブを定義します。

その他、今回のサンプルではfilters, branches, onlyを使って、masterブランチでのみdeployジョブが動くようにしています。

2.2. GitHub Actionsの場合 その1

Actionsでは、CircleCIでいうところのworkflowsにあたるものは無いようなのでjobsだけでジョブの実行順などを定義していきます。

.github/workflows/build-test.yml
name: build-test

on: [pull_request] # on: pull_request でも良い(指定したいイベントがひとつだけなら)

jobs:
  build:
    # 略
  test:
    needs: [build] # needs: build でも良い(指定したいジョブがひとつだけなら)
    # 略
.github/workflows/build-test-deploy.yml
name: build-test-deploy

on:
  push: # イベントに対して更に条件を追加する場合は、[]を使った指定はできない
    branches:
      - master

jobs:
  build:
    # 略
  test:
    needs: [build]
    # 略
  deploy:
    needs: [test]
    # 略

2.2.1. on

onでは、Actionsを動かすトリガーとなるイベントを定義できるので、今回のサンプルでは

  • PRを立てた時にbuild, testジョブを動かす(このPRのブランチにpushした時にも動きます)

  • masterブランチにpushした時に、build, test, deployジョブを動かす(PRをmasterブランチにマージした時も動きます)

としました。

2.2.2. needs

needsで先行ジョブを定義しています。

2.3. GitHub Actionsの場合 その2

先ほどの例ではワークフローを2つに分けていますが、build, testジョブの内容が同じであれば記述として冗長になってしまいます。

ワークフローを分けないのであれば、以下の構成が考えられるかと思います。

.github/workflows/build-test-deploy.yml
name: build-test-deploy

on: 
  pull_request:
  push:
    branches:
      - master # masterにマージした時にもワークフローを動かすため

jobs:
  build:
    # 略
  test:
    needs: [build]
    # 略
  deploy:
    needs: [test]
    if: github.ref == 'refs/heads/master' # masterにマージした時のみdeployジョブを動かすため
    # 略

2.3.1. if

GitHub Actionsでは、ジョブやステップに対し、ifで実行条件を定義できます。

このifを使って、deployジョブが動くのを、masterにマージした時のみとしています。

2.3.2. github.ref / github.head_ref / github.base_ref

今回は、github.refというコンテキストを利用して、masterマージ時のみdeployジョブが動くようにしました。

似たようなコンテキストとして、github.head_ref, github.base_refがあります。

その値についてはrefs/...が先頭に付いたり付かなかったりするので、状況別にまとめました。参考にしてください。

プロパティ プルリクエスト時の値 masterへマージした時の値
github.ref refs/pull/PR番号/merge refs/heads/master
github.head_ref PRのブランチ名
(feature/hoge など。refs/...は付かない。)
無し
github.base_ref PRでマージする予定のブランチ名
(master など。refs/...は付かない。)
無し

3. ソースコードのチェックアウト

3.1. CircleCIの場合

CircleCIでは、checkoutコマンドでGitHubのソースコードをチェックアウトします。

.circleci/config.yml
version: 2.1
executors:
  default:
    docker: # 略
jobs:
  build:
   executor: default
   steps:
     - checkout
   # 略

3.2. GitHub Actionsの場合

GitHub Actionsでは、actions/checkoutを使用してGitHubのソースコードをチェックアウトします。

github/workflows/xxx.yml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

3.3. git branchおよびgit logの結果の違い

CircleCIのcheckoutコマンドと、GitHub Actionsのactions/checkoutでは、チェックアウトのされ方に違いがあります。

CircleCIのチェックアウト後に、git branchgit logを実施した結果の例は以下になります。

$ git branch
* feature/hogeなどPRのブランチ名
  master
$ git log --oneline -5
xxxxxxx (HEAD -> feature/hoge, origin/feature/hoge, master) コミットメッセージ
xxxxxxx コミットメッセージ
xxxxxxx コミットメッセージ
xxxxxxx コミットメッセージ
xxxxxxx コミットメッセージ

対して、GitHub Actionsでのチェックアウト後に、git branchgit logを実施した結果の例は以下になります。

$ git branch
* (HEAD detached at pull/PR番号/merge)
$ git log --oneline -5
xxxxxxx Merge プルリクエストのHEADのコミットハッシュ into masterブランチのコミットハッシュ

GitHub Actionsでのチェックアウトではデフォルトで--depth=1が指定されているので、CircleCIのチェックアウトのように過去のコミット履歴は取得できません。

そのため、例えば

$ git diff --name-only origin/master...HEAD

で、masterとプルリクエスト間で差分のあるファイル名を出力しようとしても、以下のエラーになります。

fatal: ambiguous argument 'origin/master...HEAD': unknown revision or path not in the working tree.

もし、GitHub Actionsでも過去のコミット履歴を利用して何らかの処理をしたい場合は、チェックアウト後に追加のステップでgit fetch --prune --unshallowを行えば良いかと思います。

github/workflows/xxx.yml
    steps:
      - uses: actions/checkout@v2
      - run: git fetch --prune --unshallow

4. 任意のコマンドの実行

4.1. CircleCIの場合

runでコマンドを実行します。

.circleci/config.yml
   steps:
     - run: echo "Hello World"       

nameなどを定義する場合は、以下の記述になります。

.circleci/config.yml
   steps:
     - run:
         name: Greeting # CircleCIの実行結果の画面にステップ名として表示される
         command: echo "Hello World"       

複数コマンドを実行する場合は、以下の記述になります。

.circleci/config.yml
   steps:
     - run: |
        echo "One"       
        echo "Two"
.circleci/config.yml
   steps:
     - run:
         name: Count
         command: |      
           echo "One"       
           echo "Two"

4.2. GitHub Actionsの場合

GitHub Actionsもrunでコマンドを実行します。

github/workflows/xxx.yml
   steps:
     - run: echo "Hello World"       

nameなどを定義する場合は、以下の記述になります。

github/workflows/xxx.yml
   steps:
     - name: Greeting # GitHub Actionsの実行結果の画面にステップ名として表示される
       run : echo "Hello World"       

複数コマンドを実行する場合は、以下の記述になります。

github/workflows/xxx.yml
   steps:
     - name: Count
       run: |      
         echo "One"       
         echo "Two"

5. キャッシュ

5.1. CircleCIの場合

CircleCIでは、restore_cachesave_cacheコマンドでキャッシュのリストアと保存を行います。

.circleci/config.yml
# 略
    steps:
      # 略
      - restore_cache:
          key: npm-v1-{{ checksum "package-lock.json" }}
      - run:
          name: if there is no node_modules, npm ci
          command: |
            if [ ! -d node_modules ]; then
              npm ci
            fi
      - save_cache:
          key: npm-v1-{{ checksum "package-lock.json" }}
          paths:
            - node_modules

5.2. GitHub Actionsの場合

GitHub Actionsでは、actions/cacheでキャッシュのリストアと保存を行います。

github/workflows/xxx.yml
# 略
    steps:
      # 略
      - name: cache npm
        uses: actions/cache@v1.1.2
        with:
          path: node_modules
          key: npm-v1-${{ hashFiles('package-lock.json') }}
      - name: if there is no node_modules, npm ci
        run: |
          if [ ! -d node_modules ]; then
            npm ci
          fi

なお、上記のサンプルでは、キャッシュからnode_modulesを復元できたかどうかをnode_modulesの有無で判定し、復元できている場合はnpm ciを実行しないようにしています。

actions/cacheではキャッシュヒットした場合に、cache-hit'true'を出力するので、これを利用する場合は以下のように書き直せます。

github/workflows/xxx.yml
# 略
    steps:
      # 略
      - name: cache npm
        id: cache-npm # ステップを特定するためのidを定義
        uses: actions/cache@v1.1.2
        with:
          path: node_modules
          key: npm-v1-${{ hashFiles('package-lock.json') }}
      - name: npm ci
        if: steps.cache-npm.outputs.cache-hit != 'true' # キャッシュヒットした時のみnpm ciを実行する
        run: npm ci

5.2.1. キャッシュの保存のタイミングについて

CircleCIではキャッシュのリストア/保存のステップをそれぞれ記述しましたが、GitHub Actionsではキャッシュをリストアしたいタイミングにactions/cacheを使用するステップを記述するのみとなり、保存のステップは記述しません。

キャッシュの保存は、actions/cacheステップが存在するジョブの最後にPost + actions/cacheのステップ名というステップ名で実行されます。

ただし、ジョブが正常終了しなかった場合は、キャッシュを保存するステップは実行されません。

そのため、ビルドのためのジョブを存在させず、テストジョブ内で

  • actions/cacheの利用とビルド
  • テスト

を行なっていると、テストが通らない限り、キャッシュが保存されません。

github/workflows/xxx.yml
# 略
jobs:
  # build: # ビルドジョブ無し
  test:
    steps:
      # 略
      - name: cache npm # テストが通らないと、ジョブの最後のキャッシュ保存を行なってくれない
        # 略
      - name: npm ci
        # 略
      - name: npm test
        run: npm test

もし、これを防ぎたい場合は、ビルドのためのジョブを定義し、ビルドジョブとテストジョブそれぞれで、キャッシュのkeyは同じにした上で、actions/cacheの利用とビルドを行えば良いかと思います。

github/workflows/xxx.yml
# 略
jobs:
  build:
    steps:
      # 略
      - name: cache npm
        id: cache-npm
        uses: actions/cache@v1.1.2
        with:
          path: node_modules
          key: npm-v1-${{ hashFiles('package-lock.json') }}
      - name: npm ci
        # 略
  test:
    needs: [build]
    steps:
      # 略
      - name: cache npm
        id: cache-npm
        uses: actions/cache@v1.1.2
        with:
          path: node_modules
          key: npm-v1-${{ hashFiles('package-lock.json') }}
      - name: npm ci
        # 略
      - name: npm test
        run: npm test

なお、キャッシュの保存のステップは以下のような処理結果になります。

Cache saved successfully
Post job cleanup.
/bin/tar -cz -f /home/runner/work/_temp/9fa1f088-a867-4efe-80eb- 
132944ceba50/cache.tgz -C /home/runner/work/リポジトリ名/リポジトリ名/node_modules .
Cache saved successfully

リストア時にキャッシュヒットした場合は、キャッシュは上書き保存されません。

Post job cleanup.
Cache hit occurred on the primary key npm-v1-433b1cb5f6b93173ee6214d276272b0b7fc0c4c8b3e0e3770d29c98035416d4e, not saving cache.

5.2.2. 単一ファイルのキャッシュ

CircleCIでは、pathsにディレクトリだけでなくファイル名を指定し、それをキャッシュすることができますが、actions/cacheではディレクトリしかキャッシュできません。

GitHub Actionsで単一のファイルをキャッシュする場合は、退避用のディレクトリを作って、そこにファイルをコピーした上でディレクトリごとキャッシュし、リストアされた時にはそのディレクトリから本来のパスにコピーする、といった作業が必要になるかと思います。

6. 環境変数

6.1. CircleCIの場合

CircleCIでは、Project SettingsEnvironment Variables画面で環境変数を設定します。

CircleCI Project Settings Environment Variables

ワークフローファイル上で、環境変数を新規に設定する場合は、environmentに記述します。

.circleci/config.yml
version: 2.1
jobs:
  # 略
  deploy:
    environment:
      AWS_DEFAULT_REGION: ap-northeast-1
    # 略

前述の画面で設定した環境変数の値を上書きする場合も同様の方法となります。

6.2. GitHub Actionsの場合

GitHub Actionsでは、GitHubのSettingsSecrets画面で、いったん秘密情報として設定します。

GitHub Settings Secrets

その上で、ワークフローファイルで、envに改めて環境変数として設定します。

秘密情報は、${{ secrets.秘密情報のキー }}と記述することで取り出せます。

.github/workflows/xxx.yml
# 略
jobs:
  # 略
  deploy:
    needs: [test]
    env:
      AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
      AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
      AWS_DEFAULT_REGION: ap-northeast-1    

なお、envはワークフロー全体やステップ単位に適用することもできます。

.github/workflows/xxx.yml
# 略
env:
  # 略

jobs:
  # 略
.github/workflows/xxx.yml
# 略
jobs:
  steps:
    - name: s3 upload
      env:
        # 略
      run: # 略

おまけ(SSH)

GitHub Actionsの実行環境にSSHで入って調査したりデバッグをしたい場合は、以下の記事を参考にしてください。

22
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
20