1.この記事を書こうと思った背景
- Github Actionsでは、
schedule
( schedule イベントやスケジュール実行ともいうがここでは、 schedule とする。)というイベントが用意されている- https://docs.github.com/ja/actions/using-workflows/events-that-trigger-workflows#schedule
- 他のイベントとしては、
push
やworkflow_dispatch
などがある
- scheduleは、POSIX 規格の crontab の構文 で表記するのだが、日時の条件指定と曜日の指定はAND判定されるかと思いきや、OR判定されるようだと分かり、Github feedback や Support などで問い合わせた
- 問い合わせた結果、丁寧に教えていただいたので共有したい
- 日時の条件指定と曜日の指定のAND判定というのは、たとえば、第1月曜日の午前2:00に schedule を実行したい場合、下記のような書き方を指している(ただし、この書き方ではAND判定とならないので要注意!)
on:
schedule:
- cron: '0 17 1-7 * 1'
2.長いので結論を書く
Github Actions の cron で採用されている POSIX 規格の crontab の構文 では日時と曜日が指定されている場合、日時と曜日のOR判定となることが正しい
if either the month or day of month is specified as an element or list, and the day of week is also specified as an element or list, then
any day matching either the month and day of month, or the day of week
, shall be matched.(Google翻訳) 月または日のいずれかが要素またはリストとして指定され、曜日も要素またはリストとして指定されている場合、
月と日、または曜日のいずれかに一致する任意の日
、一致するものとします。- 出所:POSIX 規格の crontab の構文
- つまり、冒頭で一例として書いた cron の場合、以下のいずれかの条件に合致する場合、Schedule event は実行されることとなる
- 毎月1日〜7日の17:00(UTC)
- 毎週月曜日の17:00(UTC)
on:
schedule:
- cron: '0 17 1-7 * 1'
それでも Workflow を日時と曜日いずれも指定して実行する方法はある!
- Github Actions の schedule では日時だけ指定し、曜日指定は Job で date コマンドの結果を使うようにする
- 採用を見送ったこと
success()
を使って後続の Job の実行制御をする- Job が成功した場合のみ、後続の Job を実行する
- job と step どちらにも使える実行を制御する書き方、片方にしか使えない書き方の整理(こちらは別記事にて)
3.日時指定と曜日指定のやりかたの解説
- 日時指定はscheduleでやる
on:
schedule:
- cron: 0 17 1-7 * * # At 02:00 am on every day-of-month from 1 through 7. (JST)
- 曜日指定は Job で date コマンドの結果を使う
- date コマンドの結果が実行したい曜日である場合のみ、後続の Job を実行するようにした
jobs:
date:
name: Run date command
runs-on: ubuntu-latest
steps:
- run: echo "::set-output name=today::$(LANG=C date '+%A')"
id: day-of-week
outputs:
day-of-week: ${{ steps.day-of-week.outputs.today }}
# Run only Monday
run-only-monday:
name: Playground Job
runs-on: ubuntu-latest
needs: date
# 直前の "date" Job で outputs を使って date コマンドの結果を保持し、実行したい Monday (月曜日)である場合のみ "run-only-monday" Job を実行
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idoutputs
if: needs.date.outputs.day-of-week == 'Monday'
steps:
- name: echo day-of-week
run: echo ${{ needs.date.outputs.day-of-week }}
# Job レベルで実行制御しているので複数の step をまとめて実行するかしないか決めている
- name: echo hoge
run: echo "hoge"
3.やってみたけど採用を見送ったこと
success()
を使って後続のJobの実行制御をする
jobs:
check-if-today-is-monday:
name: Check if today is Monday
runs-on: ubuntu-latest
steps:
- run: if [ $(date +%u) -ne 1 ]; then exit 1; fi ## このstepが失敗すれば (exit 1 となれば)、後続の step と job は実行されない
- name: Run only Monday
if: success()
run: echo "hoge"
# Run only Monday
run-only-monday:
name: Playground Job
runs-on: ubuntu-latest
needs: check-if-today-is-monday
if: success()
steps:
- name: echo hoge
run: echo "hoge"
3-1.採用を見送ったワケ
なぜ success()
を使って後続の Job の実行制御をする方法の採用を見送ったか?それは Action の成否を確認するために Job のログを追わなければならないからだ。これはめんどくさい。
そこでActionの実行結果の見通しを損なわいようにするために、Jobの実行結果に応じて後続のJobを実行制御をすることにした。
# 後続のJobを実行制御をする箇所のみ抜粋
# Run only Monday
run-only-monday:
name: Playground Job
runs-on: ubuntu-latest
needs: date
# 直前の "date" Job で outputs を使って date コマンドの結果を保持し、実行したい Monday (月曜日)である場合のみ "run-only-monday" Job を実行
if: needs.date.outputs.day-of-week == 'Monday'
4.Workflow 全体
まとめとして、Workflow 全体を書いておく。
name: Complex cron expression
on:
schedule:
- cron: 0 17 1-7 * * # At 02:00 am on every day-of-month from 1 through 7. (JST)
jobs:
date:
name: Run date command
runs-on: ubuntu-latest
steps:
# if 文を使って date コマンドの結果を判定せず、 date '+%A' を使えばいい
# - run: if [ $(date +%u) -eq 4 ]; then echo "::set-output name=today::Monday"; fi
- run: echo "::set-output name=today::$(LANG=C date '+%A')"
id: day-of-week
outputs:
day-of-week: ${{ steps.day-of-week.outputs.today }}
# Run only Monday
run-only-monday:
name: Playground Job
runs-on: ubuntu-latest
needs: date
# 直前の "date" Job で outputs を使って date コマンドの結果を保持し、実行したい Monday (月曜日)である場合のみ "run-only-monday" Job を実行
if: needs.date.outputs.day-of-week == 'Monday'
steps:
- name: echo day-of-week
run: echo ${{ needs.date.outputs.day-of-week }}
- name: echo hoge
run: echo "hoge"
5.備考
- cron のタイムゾーンは UTC
- cron で実行時刻を決める際、crontab.guru を使うのだけど、cron のタイムゾーンは UTC(GMT) である点に注意。以下のような自分のタイムゾーンと UTC との変換ツールを使って cron の実行時刻を決めるのがよいと思う。
- https://www.jisakeisan.com/
- 以下の記事によると、時間と曜日指定ができるらしい
- github - Schedule on weekdays - Stack Overflow
- 金曜日19:15(JST)に動くことは確認できた
- https://github.com/gkzz/actions-playground/actions/runs/2003546225
- 日にちを指定せず、時間と曜日はAND判定できる!
on:
schedule:
# on weekday
- cron: '15 10 * * 1-5' # At 07:15 pm on every day-of-week from Monday through Friday.