Kubernetes native なワークフローエンジン Argo workflow を動かす

Argo workflow は Cloud Native Computing Foundation にホストされている Kubernetes native なワークフローエンジンです。

github.com

Kubeflow でも機械学習のパイプラインエンジンとして使用されています。

blog.kondoumh.com

公式の manifest でインストールできます。

https://github.com/argoproj/argo-workflows/tree/master/manifests

Helm charts もコミュニティベースでメンテナンスされています。

argo-helm/charts/argo at master · argoproj/argo-helm · GitHub

今回は Docker Desktop の Kubernetes 環境に argo namespace を作成して quick-start 用の manifest を適用してみました。

$ kubectl create namespace argo
$ kubectl apply -n argo -f https://raw.githubusercontent.com/argoproj/argo-workflows/stable/manifests/quick-start-postgres.yaml

argo-server と workflow-controller が起動し、ストレージ用の minio と DB サーバ postgres も起動されました。

$ kubectl get po,deploy,svc -n argo            
NAME                                      READY   STATUS    RESTARTS   AGE
pod/argo-server-5b86d9f84b-vjhhq          1/1     Running   4          23m
pod/minio-58977b4b48-ts9zn                1/1     Running   0          23m
pod/postgres-6b5c55f477-ss24g             1/1     Running   0          23m
pod/workflow-controller-d9cbfcc86-jkf8j   1/1     Running   2          23m

NAME                                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/argo-server           1/1     1            1           23m
deployment.apps/minio                 1/1     1            1           23m
deployment.apps/postgres              1/1     1            1           23m
deployment.apps/workflow-controller   1/1     1            1           23m

NAME                                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/argo-server                   ClusterIP   10.106.203.74   <none>        2746/TCP   23m
service/minio                         ClusterIP   10.96.161.227   <none>        9000/TCP   23m
service/postgres                      ClusterIP   10.98.163.40    <none>        5432/TCP   23m
service/workflow-controller-metrics   ClusterIP   10.102.138.81   <none>        9090/TCP   23m

CRD もインストールされました。

$ kubectl get crd | grep argo
NAME                                   CREATED AT
clusterworkflowtemplates.argoproj.io   2021-05-06T03:32:28Z
cronworkflows.argoproj.io              2021-05-06T03:32:28Z
workfloweventbindings.argoproj.io      2021-05-06T03:32:28Z
workflows.argoproj.io                  2021-05-06T03:32:28Z
workflowtemplates.argoproj.io          2021-05-06T03:32:28Z

Argo Server の Web UI を使うために port-forward します。

kubectl -n argo port-forward deployment/argo-server 2746:2746

Chrome で https://localhost:2746 に接続すると NET::ERR_CERT_INVALID と自己証明書のエラーで接続できないので、thisisunsafe の呪文をタイプして接続しました。

f:id:kondoumh:20210507083016p:plain

まだワークフローは何も登録されていない状態です。

Argo workflow は kubectl コマンドでも操作できますが、Argo CLI に便利コマンドが用意されているのでインストールします。

$ curl -sLO https://github.com/argoproj/argo/releases/download/v3.0.2/argo-linux-amd64.gz
$ gunzip argo-linux-amd64.gz
$ chmod +x argo-linux-amd64
$ mv ./argo-linux-amd64 /usr/local/bin/argo

サンプルワークフローをデプロイしてみます。hello-world- というプレフィクス付きのワークフローを生成します。whalesay という template が定義されており、コンテナイメージ whalesay を起動して cowsay コマンドを叩くものです。メモリと CPU リソースの limit も指定しています。この template を entrypoint に指定することで起動します。

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: hello-world-
  labels:
    workflows.argoproj.io/archive-strategy: "false"
spec:
  entrypoint: whalesay
  templates:
  - name: whalesay
    container:
      image: docker/whalesay:latest
      command: [cowsay]
      args: ["hello world"]
      resources:
        limits:
          memory: 32Mi
          cpu: 100m
$ argo submit -n argo --watch https://raw.githubusercontent.com/argoproj/argo-workflows/master/examples/hello-world.yaml

Name:                hello-world-mg6x7
Namespace:           argo
ServiceAccount:      default
Status:              Succeeded
Conditions:          
 PodRunning          False
 Completed           True
Created:             Tue May 11 12:27:50 +0900 (1 minute ago)
Started:             Tue May 11 12:27:50 +0900 (1 minute ago)
Finished:            Tue May 11 12:29:25 +0900 (now)
Duration:            1 minute 35 seconds
Progress:            1/1
ResourcesDuration:   35s*(100Mi memory),35s*(1 cpu)

STEP                  TEMPLATE  PODNAME            DURATION  MESSAGE
 ✔ hello-world-mg6x7  whalesay  hello-world-mg6x7  1m 

1分ほどでコンテナ作成から実行までが完了しました。

最新のジョブの状態やログを Argo CLI で確認できます。

$ argo list -n argo @latest
NAME                STATUS      AGE   DURATION   PRIORITY
hello-world-mg6x7   Succeeded   23m   1m         0

$ argo logs -n argo @latest
hello-world-mg6x7:  _____________ 
hello-world-mg6x7: < hello world >
hello-world-mg6x7:  ------------- 
hello-world-mg6x7:     \
hello-world-mg6x7:      \
hello-world-mg6x7:       \     
hello-world-mg6x7:                     ##        .            
hello-world-mg6x7:               ## ## ##       ==            
hello-world-mg6x7:            ## ## ## ##      ===            
hello-world-mg6x7:        /""""""""""""""""___/ ===        
hello-world-mg6x7:   ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~   
hello-world-mg6x7:        \______ o          __/            
hello-world-mg6x7:         \    \        __/             
hello-world-mg6x7:           \____\______/

もちろん Web UI でも結果を閲覧できます。

f:id:kondoumh:20210511123323p:plain

f:id:kondoumh:20210511123343p:plain

ジョブの詳細画面からログ出力を確認できます。

f:id:kondoumh:20210511124600p:plain

f:id:kondoumh:20210511124623p:plain

タスクの依存関係を dependencies で指定するサンプルです。

# The following workflow executes a diamond workflow
# 
#   A
#  / \
# B   C
#  \ /
#   D
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: dag-diamond-
spec:
  entrypoint: diamond
  templates:
  - name: diamond
    dag:
      tasks:
      - name: A
        template: echo
        arguments:
          parameters: [{name: message, value: A}]
      - name: B
        dependencies: [A]
        template: echo
        arguments:
          parameters: [{name: message, value: B}]
      - name: C
        dependencies: [A]
        template: echo
        arguments:
          parameters: [{name: message, value: C}]
      - name: D
        dependencies: [B, C]
        template: echo
        arguments:
          parameters: [{name: message, value: D}]

  - name: echo
    inputs:
      parameters:
      - name: message
    container:
      image: alpine:3.7
      command: [echo, "{{inputs.parameters.message}}"]

CLI の実行結果

 ✔ dag-diamond-vqswz  diamond                                             
 ├─✔ A                echo      dag-diamond-vqswz-4245884217  16s         
 ├─✔ B                echo      dag-diamond-vqswz-4195551360  6s          
 ├─✔ C                echo      dag-diamond-vqswz-4212328979  6s          
 └─✔ D                echo      dag-diamond-vqswz-1249778     5s 

Web UI での表示

f:id:kondoumh:20210515083827p:plain

以上、ローカルでの Argo 環境構築と動作確認でした。

GitHub CLI に GitHub Actions 用コマンドが追加された

1.0 では対応してませんでした。

blog.kondoumh.com

今月の中旬にリリースされた v1.9.0 で run, workflow, actions のコマンド群が提供されました。

github.blog

actions でコマンド一覧が表示できます。

$ gh actions

f:id:kondoumh:20210429150834p:plain

run はワークフロー実行結果や詳細を見るためのコマンド。workflow はワークフローファイルの管理や起動のめのコマンドのようです。

リポジトリのワークフロー実行結果一覧を取得するには run list サブコマンドを使用します。

$ gh run list

f:id:kondoumh:20210429143053p:plain

ワークフロー起動時のコミット、ワークフロー名、ブランチ、トリガー、run ID が取得されます。

実行中のワークフローをウォッチするには run watch サブコマンドを使用します。

$ gh run watch

f:id:kondoumh:20210429145055p:plain

実行中のジョブを選択できて、選択するとワークフローのジョブごとにステップの実行結果が順次出力されていきます。Web UI だとステップごとの結果はジョブをクリックしないと見れませんので一覧性は高くなっています。

f:id:kondoumh:20210429145247p:plain

run view サブコマンドで run ID を指定して実行結果の詳細を取得できます。

$ gh run view <run ID>

f:id:kondoumh:20210429150136p:plain

Web UI もよくできているのででエラー箇所を見るのは簡単ですが、CLI を使うと grep などと組み合わせて出力を加工できるのでこれはこれで便利です。

この他ワークフローを再実行する run rerun や ワークフロー実行により生成された Artifact をダウンロードする run download などのサブコマンドも提供されています。

ワークフローファイルの一覧は workflow list サブコマンドで取得できます。

$ gh workflow list

f:id:kondoumh:20210429152131p:plain

ワークフロー名を指定して実行できます。

$ gh workflow run <workflow name>

f:id:kondoumh:20210429152523p:plain

実行後は run watch で実行状態を監視できます。

今のところ実行できるのは workflow_dispatch トリガーを持つワークフローだけです。このトリガーを持たないワークフローを実行しようとするとエラーになります。

f:id:kondoumh:20210429153048p:plain

実行後は run watch で実行状態を監視できます。

ブラウザでポチポチしなくてもワークフローの状態を調べることができるようになって便利になりました。

ワークフロー一覧はもう少し属性情報を出して欲しいですね。workflow view サブコマンドで詳細が出ますが、YAML の解析が大変なのかも? あとworkflow_dispatch だけでなく webhook イベントでトリガーするrepository_dispatch の実行にも対応して欲しいところです。

GitHub リリースページのドラフトを自動生成してくれる Release Drafter を導入する

リリースページに ChangeLog というかリリースノートを書くのはかなり面倒です。 Release Drafter を使うと PR 単位でいい感じに リリースページのドラフトを生成してくれます。

Release Drafter の GitHub App は簡単に導入できますが、何やら Deprecated にしようという議論があるようです。

GitHub Apps - Release Drafter · GitHub

github.com

GitHub Actions で release-drafter 用のワークフローを使う方が無難かなと思います。

github.com

README にある通り、設定用の release-drafter.yml とワークフロー用の release-drafter.yml をそれぞれリポジトリの .github.github/workflows に配置するだけ。

設定用の release-drafter.yml。PR にラベルをつけておくと、Features や Bug Fixes に振り分けてくれます。ChangeLog に入れたくないような修正については除外用のラベルを指定することもできます。

name-template: 'v$NEXT_PATCH_VERSION'
tag-template: 'v$NEXT_PATCH_VERSION'
categories:
  - title: '🚀 Features'
    labels:
      - 'feature'
      - 'enhancement'
  - title: '🐛 Bug Fixes'
    labels:
      - 'fix'
      - 'bugfix'
      - 'bug'
  - title: '🧰 Maintenance'
    labels:
      - 'chore'
change-title-escapes: '\<*_&'
exclude-labels:
  - 'exclude from changelog'
template: |
   ## Changes

   $CHANGES

ワークフロー用の release-drafter.yml です。

name: Release Drafter

on:
  push:
    branches:
      - master
  pull_request:
    types: [opened, reopened, synchronize]

jobs:
  update_release_draft:
    runs-on: ubuntu-latest
    steps:
      - uses: release-drafter/release-drafter@v5
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

PR が作成・マージされる度に Draft が更新されていきます。

f:id:kondoumh:20210326150153p:plain

template に 'v$NEXT_PATCH_VERSION' を指定しているので、0.1.0 がリリースされている状態だと 0.1.1 のドラフトができます。

f:id:kondoumh:20210326150331p:plain

Draft を Edit して実際に作成したリリースページ。

f:id:kondoumh:20210326150353p:plain

手作業でリリース時にあたふたやってたことが継続的にオートメーション化された感じですね。PR のタイトルをちゃんと付けるようにすれば、ほとんど修正は不要でしょう。

テレワーク環境 Early 2021

去年は色々試行錯誤してましたが、ようやく落ち着いてきました。

blog.kondoumh.com

ずっと ASUS の 27inch WQHD ディスプレイ使ってます。

scrapbox.io

8年も前のモデルで HDMI 入力端子が1個しかないので、テレワーク端末のノート PC と MacBook Pro をスイッチするのに HDMI セレクタを導入しました。

REALFORCE と Microsoft Ergonomic Mouse を共用するため USB 切り替え器も久々に導入。

ノート PC も MacBook もクラムシェルにして画面を閉じ、入出力デバイスは同一という環境になりました。

WQHD は Chrome と VS Code を並べて表示するのに十分な解像度で、1画面だけなので首もほとんど動かさずに作業できます。客先でもWUXGA (1920 x 1200) 程度のディスプレイが貸与されることがほとんどなので、自宅の方が画面は広いです。PC では StrokePlus、MacBook では BetterTouch tool で仮想画面の切り替えをマウスでできるように設定してますが、ほとんど切り替えることはありません。

ディスプレイ横には iPad Pro 11inch を高めの位置にマウントしてます。このアームスタンドを導入してみました。iPad Pro 11 だと重さでけっこう揺れるので机ではなく別の台に固定してます。

作業しながら登録している YouTube チャネルを垂れ流したりしてますが、Magic Keyboard / Trackpad をペアリングして、Slack や Twitter なども iPad でやるようにしてます。ChangeLog メモも Textastic で書くようにしたのでテレワーク端末で作業しててもすぐ書けます。

iPad で映画見るときとかゲームする時は USB-C / DisplayPort 変換ケーブルを使ってます。iPad がコントローラー的な画面になり、コンテンツが 27inch で見れてなかなかいいです。

ということで、仕事してるのか個人プロジェクトやってるのか遊んでるのかは側から見ると全く変わらない感じになってしまってます。

Teams や Zoom は支給された iPhone 7 から使うことが多いですが、画面共有して共同作業する時は PC 画面を占有されないよう iPad でやることもあります。ワイヤレスヘッドセットが増えました。

f:id:kondoumh:20210304083932j:plain

Netlify Functions の Zero configuration と Netlify CLI

Netlify Functions は AWS Lambda を基盤とした API のプラットフォームです。

blog.kondoumh.com

利用は非常に簡単ですが、最近さらに Zero configuration という仕組みが導入されました。規定のディレクトリ (netlify/functions) にコードを配置するだけで、コミットしたら Netlify が自動的に Functions をビルド・デプロイしてくれます。

www.netlify.com

これまでは、netlify-lambda を devDependencies に追加して、netlify.toml で build command や functions のディレクトリなどを指定していました。

[build]
  command = "npm run build"
  functions = "resource/api"

試しに functions のコードを netlify/functions に移動し、netlify.toml を消し、package.json の build script は 文字列を echo するだけのものにして git commit / push してみました。

npm run build は実行されますが、これとは別に Functions bundling が開始され、netlify/functions 配下の hello.js が検出され bundle されました。

f:id:kondoumh:20210224074821p:plain

ちゃんとデプロイされました。

f:id:kondoumh:20210224123158p:plain

これで、Zero configuration の動作は確認できました。

これまで、Functions のローカル開発は netlify-lambda serve <dir> を実行することで行っていました。しかしこの実行には netlify.toml の作成が必須であり、Zero configuration と矛盾します。*1

netlify-lambda のドキュメントを読んでみました。

github.com

netlify-lambda serve (legacy command)

This command is pretty much superceded by Netlify Dev. We only keep it around for legacy/backward compatibility support reasons.

どうやら serve は legacy command という位置付けのようです(create-react-app などと共に使用するようなユースケースのため)。 そして Netlify Dev に置き換えられるとのことです。

www.netlify.com

Netlify Dev ってなんぞっていうところですが、文字通り Netlify を使った開発をローカルで行うための環境でした。

docs.netlify.com

Netlify CLI をインストールして Netlify にログインした状態で使います。

インストール

% npm install netlify-cli -g

ログイン

% netlify login

ブラウザが開くので Authorize をクリックするとログイン状態になります。

f:id:kondoumh:20210224203415p:plain

プロジェクトディレクトリで netlify dev を叩くと

f:id:kondoumh:20210224203921p:plain

localhost:8888 でプロジェクトのコンテンツが Serving されているようです。Netlify Functions の規定のパスは、.netlify/functions/ なので、API を呼び出してみると結果が取得できました。

f:id:kondoumh:20210224213107p:plain

Netlify CLI には他にも多くのサブコマンドが実装されています。package.json に何も書かなくてもよいので、まさに Zero configuration です。

Vue を使う開発でも、Vue の開発用サーバと netlify dev を別 port で動かし proxy の設定だけしておけば、netlify-lambda は要らないだろうなと思っています。

*1:netlify/functions 以外のディレクトリに配置したい場合飲み netlify.toml を作成することになっています。

GitHub Actions ワークフローでジョブ毎のステータスを Slack 通知する

前回のフォローアップ的な内容です。

blog.kondoumh.com

複数ジョブからなるワークフローの最後に Slack などに通知する時、ワークフロー全体の成功・失敗だけでなく、どのジョブで失敗したかを通知したくなります。通知のリンクからワークフローの実行結果を開けば分かりますが、通知時点で分かる方がよいでしょう。

ジョブ毎にステータスをワークフローレベルの変数に記録して最後に通知したいところですが、そのようなグローバル変数は提供されていません。そのかわり、job.outputs を使えばジョブの出力を後続ジョブに伝搬できます。

https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idoutputs

各ジョブに必ず実行される最終スッテップを定義して、echo "::set-output name=<output_name>::<output of job>"job.status を記録します。ジョブの outputs${{ steps.<step_id>.outputs.<output_name> }} の形式で output を定義します。以下の例では、ジョブ名とジョブステータスを message という名前で記録して outputs/done に保持するようにしています。

  Build:
    runs-on: ubuntu-latest
    name: Build
    outputs:
      done: ${{ steps.check.outputs.message }}
    steps:
    - name: Build
      run: echo Build
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Build ${{ job.status }}"

全ジョブの結果出力用ジョブを書いてみます。needs で対象の全ジョブを指定します。${{ needs.<job_name>.outputs.<output_name> }} で記録したジョブの出力が取れます。

  Aggregate:
    if: ${{ always() }}
    runs-on: ubuntu-latest
    needs: [Build, TestA, TestB, Deploy]
    steps:
    - name: Aggregate
      run: |
        echo ${{ needs.Build.outputs.done }}
        echo ${{ needs.TestA.outputs.done }}
        echo ${{ needs.TestB.outputs.done }}
        echo ${{ needs.Deploy.outputs.done }}

出力結果。

f:id:kondoumh:20210130144146p:plain

文字だと地味なので Slack への通知は見やすいようにステータスを絵文字に変換してみます。

  Build:
    runs-on: ubuntu-latest
    name: Build
    outputs:
      done: ${{ steps.check.outputs.message }}
    steps:
    - name: Build
      run: echo Build
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Build ${{ (job.status == 'success' && '✅') || '❌' }}"

Slack 通知用のジョブ。

  Notify_succeed:
    if: ${{ success() }}
    runs-on: ubuntu-latest
    needs: [Build, TestA, TestB, Deploy]
    steps:
    - name: Notify to Slack channel
      uses: rtCamp/action-slack-notify@v2
      env:
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        SLACK_USERNAME: GitHUb Actions
        SLACK_TITLE: 'Workflow #${{ github.run_number }} succeeded'
        SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
        SLACK_MESSAGE: '${{ needs.Build.outputs.done }} ${{ needs.TestA.outputs.done }} ${{ needs.TestB.outputs.done }} ${{ needs.Deploy.outputs.done }}'

実行結果。Deploy は Test B の失敗により実行されません。

f:id:kondoumh:20210130141441p:plain

Slack の通知です。Test B で失敗したことが分かります。

f:id:kondoumh:20210130140714p:plain

ワークフロー定義の全体を掲載しておきます。

name: Multi Stage Build
on:
  push:

jobs:
  Build:
    runs-on: ubuntu-latest
    name: Build
    outputs:
      done: ${{ steps.check.outputs.message }}
    steps:
    - name: Build
      run: echo Build
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Build ${{ (job.status == 'success' && '✅') || '❌' }}"

  TestA:
    runs-on: ubuntu-latest
    needs: Build
    outputs:
      done: ${{ steps.check.outputs.message }}
    name: Run Test A
    steps:
    - name: Test A
      run: echo Test A
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Test A ${{ (job.status == 'success' && '✅') || '❌' }}"

  TestB:
    runs-on: ubuntu-latest
    needs: Build
    outputs:
      done: ${{ steps.check.outputs.message }}
    name: Run Test B
    steps:
    - name: Test B
      run: |
        echo Test B
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Test B ${{ (job.status == 'success' && '✅') || '❌' }}"

  Deploy:
    runs-on: ubuntu-latest
    needs: [TestA, TestB]
    outputs:
      done: ${{ steps.check.outputs.message }}
    steps:
    - name: Deploy
      run: echo Deploy
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Deploy ${{ (job.status == 'success' && '✅') || '❌' }}"

  Notify_succeed:
    if: ${{ success() }}
    runs-on: ubuntu-latest
    needs: [Build, TestA, TestB, Deploy]
    steps:
    - name: Notify to Slack channel
      uses: rtCamp/action-slack-notify@v2
      env:
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        SLACK_USERNAME: GitHUb Actions
        SLACK_TITLE: 'Workflow #${{ github.run_number }} succeeded'
        SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
        SLACK_MESSAGE: '${{ needs.Build.outputs.done }} ${{ needs.TestA.outputs.done }} ${{ needs.TestB.outputs.done }} ${{ needs.Deploy.outputs.done }}'

  Notify_failure:
    if: ${{ failure() }}
    runs-on: ubuntu-latest
    needs: [Build, TestA, TestB, Deploy]
    steps:
    - name: Notify to Slack channel
      uses: rtCamp/action-slack-notify@v2
      env:
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        SLACK_USERNAME: GitHUb Actions
        SLACK_TITLE: 'Workflow #${{ github.run_number }} failed'
        SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
        SLACK_COLOR: danger
        SLACK_MESSAGE: '${{ needs.Build.outputs.done }} ${{ needs.TestA.outputs.done }} ${{ needs.TestB.outputs.done }} ${{ needs.Deploy.outputs.done }}'

GitHub Actions ワークフローで複数のジョブ実行を制御する

GitHub Actions ワークフローでは、複数のジョブを順次実行・並列実行・条件実行することができます。各ジョブは異なるマシン(Runner) もしくは コンテナイメージで実行されます。

順次実行

何も指定しなければ、ジョブは並列に実行されます。順次実行するには、needs キーワードで、先行のジョブを指定します。

name: Sequential Jobs
on:
  push:

jobs:
  Build1:
    runs-on: ubuntu-latest
    steps:
    - run: echo Build1

  Build2:
    runs-on: ubuntu-latest
    needs: Build1
    steps:
    - run: echo Build2

実行結果

f:id:kondoumh:20210122075032p:plain

並列実行 (フォーク)

ジョブを実行後に複数のジョブを並列実行するには、各ジョブの needs に分岐元のジョブを指定します。

name: Fork Jobs
on:
  push:

jobs:
  Setup:
    runs-on: ubuntu-latest
    steps:
    - run: echo Setup

  Build1:
    runs-on: ubuntu-latest
    needs: Setup
    steps:
    - run: echo Build1

  Build2:
    runs-on: ubuntu-latest
    needs: Setup
    steps:
    - run: echo Build2

実行結果

f:id:kondoumh:20210122075441p:plain

並列実行 (ジョイン)

並列ジョブをジョインして最終の処理を行うには needs にジョインしたいジョブを列挙します。

name: Join Jobs
on:
  push:

jobs:
  Build1:
    runs-on: ubuntu-latest
    steps:
    - run: echo Build1

  Build2:
    runs-on: ubuntu-latest
    steps:
    - run: echo Build2

  Teardown:
    runs-on: ubuntu-latest
    needs: [Build1, Build2]
    steps:
    - run: echo Teardown

実行結果

f:id:kondoumh:20210122075806p:plain

フォークとジョインの組合せ

CI でビルド後に複数のテストを並列実行して成功したらデプロイするような例です。

name: Deploy
on:
  push:
    paths:
    - .github/workflows/deploy.yml

jobs:
  Build:
    runs-on: ubuntu-latest
    steps:
    - run: echo Build

  TestA:
    runs-on: ubuntu-latest
    needs: Build
    steps:
    - run: echo Test A

  TestB:
    runs-on: ubuntu-latest
    needs: Build
    name: Run Test B
    steps:
    - run: echo Test B

  Deploy:
    runs-on: ubuntu-latest
    needs: [TestA, TestB]
    steps:
    - run: echo Deploy

実行結果

f:id:kondoumh:20210122085107p:plain

条件実行 (ワークフローの最後に結果を通知)

GitHub Actions ではワークフローの状態は内包するジョブの失敗・成功に依存します。1つでもジョブが失敗していれば、ワークフローの状態は失敗です。ワークフローの状態は success / failure などの関数で取得できるため、ジョブに if 条件文で実行条件を指定します。

  Deploy:
    runs-on: ubuntu-latest
    needs: [TestA, TestB]
    steps:
    - name: Deploy
      run: echo Deploy

  Notify_succeed:
    if: ${{ success() }}
    runs-on: ubuntu-latest
    needs: Deploy
    steps:
    - name: Notify to Slack channel
      uses: rtCamp/action-slack-notify@v2
      env:
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        SLACK_USERNAME: GitHUb Actions
        SLACK_TITLE: Workflow Succeeded
        SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
        SLACK_MESSAGE: 'Run number : #${{ github.run_number }}'

  Notify_failure:
    if: ${{ failure() }}
    runs-on: ubuntu-latest
    needs: Deploy
    steps:
    - name: Notify to Slack channel
      uses: rtCamp/action-slack-notify@v2
      env:
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        SLACK_USERNAME: GitHUb Actions
        SLACK_TITLE: Workflow failed
        SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
        SLACK_COLOR: danger
        SLACK_MESSAGE: 'Run number : #${{ github.run_number }}'

Slack への通知に、以下の Action を利用しました。

github.com

失敗時の実行結果

f:id:kondoumh:20210121235049p:plain

通知の例

f:id:kondoumh:20210122001019p:plain

成功時の実行結果

f:id:kondoumh:20210122000707p:plain

通知の例

f:id:kondoumh:20210122000916p:plain