⚙️

GitHub Actionsに導かれし、CircleCI使いの者たちのための導きの書

2021/09/21に公開

はじめに

普段、CircleCIさまに大変お世話になっているのですが、とある要件でGitHub Actionsを使う機会があり、使ってみるとCircleCIでいう所のこれかと思った部分が結構あったのでまとめることにしました
CircleCIのyamlをGitHub Actionsのyamlで書くことで、CircleCI使いの人たちがGitHub Actionsの概要をざっくりと理解する参考になれば幸いです

GitHub Actionsに導かれた瞬間
GitHub Actionsに導かれた瞬間

ちなみにドラゴンクエスト4は未プレイです

記事のターゲット

  • GitHub Actionsの基本的な部分をざっくりと知りたいCircleCI使いの人

サンプルコード全容

今回はフロントエンドのリポジトリで、任意のブランチ名でプルリクが作成された際に下記処理の実行をするワークフローをサンプルコードとします

  • yarn(v3)でdependenciesのインストール
  • テスト (並列で実行)
  • lint (並列で実行)
  • audit (並列で実行)
CircleCI
.circleci/config.yml
version: 2.1
executors:
  default:
    working_directory: /home/circleci/src/
    docker:
      - image: cimg/node:14.17.6

jobs:
  setup:
    executor: default
    steps:
      - checkout
      # REF:(nus3) https://circleci.com/docs/ja/2.0/yarn/#caching
      - restore_cache:
          name: Restore Yarn Package Cache
          keys:
            - yarn-packages-{{ checksum "yarn.lock" }}
      - run:
          name: Install Dependencies
          command: yarn --immutable
      - save_cache:
          name: Save Yarn Package Cache
          key: yarn-packages-{{ checksum "yarn.lock" }}
          paths:
            - ~/.cache/yarn
      - persist_to_workspace:
          root: .
          paths:
            - node_modules/*
  test:
    executor: default
    steps:
      - checkout
      - attach_workspace:
          at: .
      - run:
          name: Run jest
          command: yarn test # jestの実行を抽象化したコマンド
  lint:
    executor: default
    steps:
      - checkout
      - attach_workspace:
          at: .
      - run:
          name: Run lint
          command: yarn lint # eslintの実行を抽象化したコマンド
      - run:
          name: Run check type
          command: yarn check-type # TypeScriptの型チェックを抽象化したコマンド
  audit:
    executor: default
    steps:
      - checkout
      - attach_workspace:
          at: .
      - run:
          name: Run audit
          command: yarn audit # dependenciesに脆弱性がないかのチェックを抽象化したコマンド

workflows:
  version: 2
  run_all:
    jobs:
      - setup:
          filters:
            branches:
              # もっと色々書き方はあると思いますが今回はこれで許してください
              only:
                - /feature\/.*/
                - /improved\/.*/
                - /hotfix\/.*/
                - main
                - develop
      - test:
          requires:
            - setup
          filters:
            branches:
              only:
                - /feature\/.*/
                - /improved\/.*/
                - /hotfix\/.*/
                - main
                - develop
      - lint:
          requires:
            - setup
          filters:
            branches:
              only:
                - /feature\/.*/
                - /improved\/.*/
                - /hotfix\/.*/
                - main
                - develop
      - audit:
          requires:
            - setup
          filters:
            branches:
              only:
                - /feature\/.*/
                - /improved\/.*/
                - /hotfix\/.*/
                - main
                - develop
GitHub Actions
.github/workflows/run_all.yml
name: Run test and lint
on:
  pull_request:
    branches:
      - feature/**
      - improved/**
      - hotfix/**
      - main
      - develop
    types: [opened, synchronize]

jobs:
  setup:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Use Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '14.17.6'
      - name: Cache node modules
        uses: actions/cache@v2
        env:
          cache-name: cache-node-modules
        # REF: (nus3) https://dev.to/mpocock1/how-to-cache-nodemodules-in-github-actions-with-yarn-24eh
        # REF: (nus3) https://zenn.dev/link/comments/71195269e61d4f
        with:
          path: node_modules
          key: ${{ runner.os }}-modules-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }}
      - name: Install dependencies
        run: yarn --immutable
  test:
    runs-on: ubuntu-latest
    needs:
      - setup
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Use Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '14.17.6'
      # REF:(nus3) https://dev.classmethod.jp/articles/github-actions-parallel-deploy/
      - name: Restore node modules
        uses: actions/cache@v2
        env:
          cache-name: cache-node-modules
        with:
          path: node_modules
          key: ${{ runner.os }}-modules-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }}
      - name: Run jest
        run: yarn test
  lint:
    runs-on: ubuntu-latest
    needs:
      - setup
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Use Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '14.17.6'
      - name: Restore node modules
        uses: actions/cache@v2
        env:
          cache-name: cache-node-modules
        with:
          path: node_modules
          key: ${{ runner.os }}-modules-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }}
      - name: Run lint
        run: yarn lint
      - name: Run check type
        run: yarn check-type
  audit:
    runs-on: ubuntu-latest
    needs:
      - setup
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Use Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '14.17.6'
      - name: Restore node modules
        uses: actions/cache@v2
        env:
          cache-name: cache-node-modules
        with:
          path: node_modules
          key: ${{ runner.os }}-modules-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }}
      - name: Run audit
        run: yarn audit

CircleCIでいう所の.circleci/config.ymlの置き場所

.github/workflows/

CircleCI使いの皆さんはご存知.circleci/config.ymlにCircleCIで実行するCICDを定義しますが、GitHub Actionsの場合、.github/workflows/配下に任意の名前のymlファイルで定義します
.github/workflows/に複数ファイルを作成した場合はそれぞれ個別のワークフローとして並列で実行されます

CircleCIでいう所のconfig.ymlの構成

CircleCIの場合、一例ですが

  • version
  • executors
  • jobs
    • steps
  • workflows
    • jobs

のような順にconfig.ymlで定義します

version:
executors:
jobs:
  {job-name}:
    executor:
    steps:
      -
workflows:
  version:
  {workflow-name}:
    jobs:
      - {job-name}:
        requires:
        filters:

GitHub Actionsでは

  • name
  • on
  • jobs
    • runs-on
    • step

のような順に定義します

name: # このワークフローの名前
on: # ≒ CircleCIのworkflowsやfilters
jobs: # ≒ CircleCIのjobs
  {job-name}:
    runs-on: # ≒ CircleCIのdocker image
    steps:
      - name:

CircleCIでいう所のworkflowsfilters

タグやブランチ名など特定の条件のときにワークフローのjobを実行するときはfiltersで定義していましたが、GitHub Actionsの場合、onで定義します

CircleCIの場合

workflows:
  version: 2
  {workflow_name}:
    jobs:
      - setup:
          filters:
            branches:
              only:
                - /feature\/.*/

GitHub Actionsの場合

on:
  pull_request:
    branches:
      - feature/**
    types: [opened, synchronize]

onとtypesでワークフローをトリガーするGitHubのイベント(プルリクエストが作られた・issueが更新されたなど)を指定することで、そのイベントが実行されたときにワークフローを実行することができます

指定できるイベント一覧
スケジュールしたイベント
schedule

手動イベント
workflow_dispatch
repository_dispatch

webhook イベント
check_run
check_suite
create
delete
deployment
deployment_status
fork
gollum
issue_comment
issues
label
milestone
page_build
project
project_card
project_column
public
pull_request
pull_request_review
pull_request_review_comment
pull_request_target
push
registry_package
release
status
Watch
workflow_run

https://docs.github.com/ja/actions/reference/events-that-trigger-workflows

参考リンク

CircleCIでいう所のdockerimage

executorjobsの配下にあるdockerimageの指定はruns-onで指定する
また、Node.jsのセットアップなどはorbsのようなものがuses: {repository-name}で使えて、セットアップ用のリポジトリを公式が公開してるので、それを使ってセットアップする

CircleCIの場合

executors:
  {executor-name}:
    working_directory: /home/circleci/src/
    docker:
      - image: cimg/node:14.17.6

jobs:
  {job-name}:
    executor: {executor-name}

GitHub Actionsの場合

jobs:
  setup:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Use Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '14.17.6'

GitHub Actionsで使えるsetupのaction
公式が公開してるsetupのactionなど

参考リンク

CircleCIでいう所のcheckout

config.ymlで見ない日はないんじゃないでしょうか
ソースコードをチェックアウトするためのステップですね

CircleCIの場合
steps配下でcheckoutを記載するだけ

jobs:
  setup:
    executor: default
    steps:
      - checkout

GitHub Actionsの場合
actions/checkout@v2を使う

jobs:
  setup:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

CircleCIでいう所のorbs

circleci/aws-cliだったりcircleci/slackといったorbsには大変お世話になっていたんですが、GitHub Actionsではjobsstepsの中
usesプロパティで使いたいGitHubのリポジトリを指定して使う感じになります
使い方はorbsと似てるのですんなり使えるかと

CircleCIの場合
awsのs3のorbsを使った例

orbs:
  aws-s3: circleci/aws-s3@3.0.0

jobs:
  deploy_s3:
    executor: default
    steps:
      - checkout
      - attach_workspace:
          at: .
      - run: yarn build
      - aws-s3/sync:
          arguments: --delete
          from: ./{path}
          to: s3://{bucket-name}

GitHub Actionsの場合
GitHubのReleaseをを作るactions/create-releaseを使った例

jobs:
  {job-name}:
    runs-on: ubuntu-latest
    steps:
      - name: Publish release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: {tag-name}
          release_name: {release-name}
          body: {body}

uses: {repository-name}で使いたいactionを指定して、with配下で指定したactionに必要なプロパティを渡す感じです

参考リンク

CircleCIでいう所のworkflowsrequires

Jobに依存関係がある場合はCircleCIではworkflowsのrequiresで定義してましたよね
GitHub Actionsではneedsて定義します

CircleCIの場合
※関係ある部分しか記載していません

workflows:
  version: 2
  {workflow-name}:
    jobs:
      - setup:
      - test:
          requires:
            - setup
      - lint:
          requires:
            - setup
      - audit:
          requires:
            - setup

GitHub Actionsの場合
※関係ある部分しか記載していません

jobs:
  setup:
    steps:
      -
  test:
    needs:
      - setup
    steps:
      -
  lint:
    needs:
      - setup
    steps:
      -
  audit:
    needs:
      - setup
    steps:
      -

今回のサンプルではsetupのjobが実行された後に並列でtest, lint, auditのjobが実行されるように定義しています

参考リンク

GitHub Actions独自のもの

(もしかしたらCircleCIにも似たようなものがあるかもしれやせん)

式構文

${{ }}で囲うことで式として評価される

例: プルリクエストがマージされた後、かつ、head_refのブランチ名にrelease/が含まれている場合はjobを実行するみたいな条件式として評価する

jobs:
  create-prerelease:
    runs-on: ubuntu-latest
    if: ${{ github.event.pull_request.merged == true && contains(github.head_ref, 'release/') }}

コンテキスト・関数など

https://docs.github.com/ja/actions/reference/context-and-expression-syntax-for-github-actions#example-expression-in-an-if-conditional

github.actorgithub.eventなど(githubだけじゃなくjobやenvのコンテキストもあるよ)、ワークフローやランナー、ジョブに関する情報を取得するためのもの

また式構文のサンプルで記載したcontainsなど、GitHub Actionsが標準で用意してる組み込みの関数もあります

環境変数

https://docs.github.com/ja/actions/reference/environment-variables

GitHub Actions内で使える環境変数たち

ワークフローコマンド

https://docs.github.com/ja/actions/reference/workflow-commands-for-github-actions

ワークフロー内で使えるコマンド
基本的には::{command-name}でコマンドを呼び出す

例: set-outputコマンドを使って、別のstepに値を渡す
※関係ある部分しか記載していません

jobs:
  create-release:
    steps:
      - name: set package version
        id: package-version
        # versionというプロパティにpackage.jsonに記載されているversionの値をoutputする
        run: node -p -e '`::set-output name=version::${require("./package.json").version}`'
      - name: Bump version and push tag
        id: tag_version
        uses: mathieudutour/github-tag-action@v5
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          release_branches: main
          pre_release_branches: pre.*
          # package-versionでversionプロパティにset-outputした値を呼び出す
          custom_tag: ${{ steps.package-version.outputs.version }}

例のコードでは肝心な部分が見づらいかもしれませんが、::set-output name=version::{version-value}を使って、version情報をoutputにsetしています
別のステップからはsteps.{step-id}.outputs.versionで値にアクセスできます

GitHubで編集を提案

Discussion