ローカルファイルに記載した日付と一致するかどうかを判定して、祝日などにCIをスキップできるシンプルなPythonスクリプトの実装方法と使用方法を記載します。
背景
現在CIで使用しているクラウドサービス上のVMは、稼働時間で課金されるため、休日や祝日などの使用しない日は起動させたくない。しかし、クラウドサービスで設定できるのは、土日などの決まった曜日だけなので、祝日や特定の日付などに起動をスキップできなかった。
内閣府のデータを使用して祝日かどうかを判定できるメンテナンスフリーなツールを公開してくださっている方がすでにいますが、会社独自の休日への対応や、外部のネットワークに依存せずに祝日を判定したかった。
そこで、オフラインで判定できるスクリプトを作成しました。
動作確認環境
- python 2.6.6, 3.7.1
- CentOS release 6.8
- Windows 10 version 1809
実装方法
以下のisholiday.pyとholidays.txtを同一ディレクトリに配置します。
"""
Check whether holiday or not based on holidays.txt.
"""
import sys, os
def get_holidays_list():
    holidays_list = []
    target_path = os.path.join(os.path.dirname(__file__), 'holidays.txt')
    with open(target_path, 'r') as f:
        for row in f:
            holidays_list.append(row.strip())
    return holidays_list
def is_holiday(date):
    if date in get_holidays_list():
        return True
    else:
        return False
if __name__ == '__main__':
    if len(sys.argv) >= 2:
        if is_holiday(sys.argv[1]):
            print("It's a holiday!")
            sys.exit(0)
        else:
            print("It's a business day!")
            sys.exit(1)
    else:
        print('Usage: python isHoliday.py `date --iso-8601`')
        sys.exit(2)
2018-07-16
2018-09-17
2018-09-24
2018-10-08
2018-11-23
2018-12-24
2018-12-31
2019-01-01
2019-01-02
2019-01-03
2019-01-14
2019-02-11
2019-03-21
使用方法
python isholiday.py <日付>
<日付>はYYYY-MM-DD形式で指定します。
例:
python isholiday.py `date --iso-8601`
実行した日がholidays.txtに含まれている場合は終了コード0を、
含まれていない場合は1を返します。
CIから呼び出す場合:
# !/bin/sh
python /path/to/isholiday.py `date --iso-8601`
if [ "$?" = "0" ]; then
  echo "Skip holiday!" 
  exit 0
fi
新しい祝日を追加する方法
holidays.txtにYYYY-MM-DD形式で追加してください。
不要な行は削除してしまって構いません。
注意:
2018-07-16の行は後述のテストで使っているため、削除してしまうと、テストが失敗します
テストの実行方法
python -m unittest tests.py
import unittest
import isholiday
class TestGetHolidaysList(unittest.TestCase):
    def test_file_read(self):
        self.assertTrue(len(isholiday.get_holidays_list()) > 0)
class TestIsHoliday(unittest.TestCase):
    def test_holiday(self):
        self.assertTrue(isholiday.is_holiday('2018-07-16'))
    def test_not_holiday(self):
        self.assertFalse(isholiday.is_holiday('2018-07-17'))
if __name__ == '__main__':
    unittest.main()
問題点
この実装方法では、毎年holidays.txtを更新する必要があります。そのため、部署異動などでメンテナンスする人がいなくなったりすると、CIをスキップできなくなります。(可能な限りシンプルに実装していますので、後任の方への引き継ぎは比較的容易だと思います)
