WinMergeで比較レポートを自動出力する

## 概要
コード変更を行った際のエビデンスとしてWinMergeの比較レポートを使用して出力しているが、数十ファイルの変更があった際に、レポート出力するには手間と時間がかかるため自動出力したい。

## 実現方法の検討
WinMergeはコマンドラインを使って比較レポートを出力することができる。
比較元と比較先のフォルダを指定することで、フォルダ内のファイルを対象に比較レポートを作るツールを作る。

## 課題
変更ないものは比較レポートが不要。
比較方法はとりあえず fcコマンドで確認して不一致のものを対象にしてみる。
このためWinMergeで利用できる比較条件のオプションは指定できない。
※今のところ困っていないのでスルー

作成した比較レポートをExcelにまとめて出力できるようにもしたい。

## サンプルコード
事前準備として、下記コードと同一フォルダに「empty.txt」名で空ファイルを用意する（新規ファイルとの比較用）。

```python
import argparse
import os
import glob
import copy
# import openpyxl
import subprocess
from enum import Enum

# 定数定義
WINMERGE_PATH = r'"C:\Program Files\WinMerge\WinMergeU.exe"'
FILECOMPARE_PATH = f'fc'
EMPTY_FILE_PATH = r'.\empty.txt'


class ResultCode(Enum):
    """ 戻り値 """
    OK = 0
    NG = -1


class CommandParam():
    """ コマンドパラメータクラス """
    path_before: str
    path_after: str
    path_output: str


def main():
    """
    自動WinMerge比較レポート出力メイン処理
    """
    # コマンドラインチェック
    command_param = CommandParam()
    ret = check_commandline(command_param)

    # ファイル一覧を取得
    if ret == ResultCode.OK:
        # 変更後のファイル一覧を作成する
        # (削除されたファイルの比較レポートは不要、新規作成したファイルの比較レポートは必要)
        file_list = []
        ret = get_file_list(command_param.path_after, file_list)

    # WinMergeを使用して比較レポートを生成する
    if ret == ResultCode.OK:
        ret = run_winmerge(command_param.path_before,
                           command_param.path_after, command_param.path_output, file_list)


def check_commandline(param: CommandParam) -> ResultCode:
    """ 
    コマンドラインチェック

    Args:
        param (class)       : (out)コマンドパラメータ

    returns:
        ResultCode.OK       : 正常
        ResultCode.NG       : 引数エラー
    """
    result = ResultCode.OK

    # コマンドライン パラメータ
    parser = argparse.ArgumentParser(
        description='Aout WinMerge Tool', add_help=True)
    parser.add_argument('arg1', help='変更前のパス')
    parser.add_argument('arg2', help='変更後のパス')
    parser.add_argument('arg3', help='出力先のパス')
    args = parser.parse_args()

    # パスチェック
    if os.path.exists(args.arg1) == False:
        print(f'{args.arg1} が見つかりません')
        result = ResultCode.NG

    # パスチェック
    if os.path.exists(args.arg2) == False:
        print(f'{args.arg2} が見つかりません')
        result = ResultCode.NG

    # パスチェック
    if os.path.exists(args.arg3) == False:
        print(f'{args.arg3} が見つかりません')
        result = ResultCode.NG

    if result == ResultCode.OK:
        param.path_before = args.arg1
        param.path_after = args.arg2
        param.path_output = args.arg3

    return result


def get_file_list(path_after: str,
                  file_list: list) -> ResultCode:
    """
    ファイル一覧を取得

    Args:
        path_after (str)    : (in) 変更後のパス
        file_list (list)    : (out)ファイル一覧

    returns:
        ResultCode.OK       : 正常
        ResultCode.NG       : 引数エラー
    """
    result = ResultCode.OK

    # ファイル一覧を作成する
    path_filter = os.path.join(path_after, "*.*")
    for path_and_filename in glob.glob(path_filter):
        # ファイル名のみリストに登録する
        file_list.append(os.path.split(path_and_filename)[1])

    return result


def run_winmerge(path_before: str,
                 path_after: str,
                 path_output: str,
                 file_list: list) -> ResultCode:
    """
    WinMergeを実行する

    Args:
        path_before (str)   : (in) 変更前のパス
        path_after (str)    : (in) 変更後のパス
        path_output (str)   : (in) 出力先のパス
        file_list (list)    : (out)ファイル一覧

    returns:
        ResultCode.OK       : 正常
        ResultCode.NG       : 引数エラー
    """
    result: ResultCode = ResultCode.OK

    for filename in file_list:
        # 比較元・比較先のファイルパス作成
        filepath_before = os.path.join(path_before, filename)
        filepath_after = os.path.join(path_after, filename)
        filepath_output = os.path.join(path_output, filename + '.html')

        # 比較元に比較先と同名のファイルがあるかチェック
        if os.path.isfile(filepath_before) == False:
            # ファイルがない(＝ 新規作成のファイル）場合、空ファイルを指定する
            filepath_before = EMPTY_FILE_PATH

        # ファイル比較して不一致なら比較レポートを生成する
        ret = file_compare(filepath_before, filepath_after)
        if ret == False:
            # WinMergeを起動して、比較レポートを生成する
            command = f"{WINMERGE_PATH} {filepath_before} {filepath_after} " \
                f"/noninteractive /u /or {filepath_output}"
            ret = subprocess.run(command, shell=True)

    return result


def file_compare(path1: str,
                 path2: str) -> bool:
    """
    ファイル比較

    Args:
        path1 (str)         : (in) 比較するファイルパス(1)
        path2 (str)         : (in) 比較するファイルパス(2)

    returns:
        True                : 一致
        False               : 不一致
    """
    result = True

    # ファイル比較
    command = f"{FILECOMPARE_PATH} {path1} {path2}"
    ret = subprocess.run(command, capture_output=True, text=True, shell=True)
    lines = ret.stdout.splitlines()
    if lines[1] != 'FC: 相違点は検出されませんでした':
        result = False

    return result


if __name__ == "__main__":
    main()
```