概要
コード変更を行った際のエビデンスとしてWinMergeの比較レポートを使用して出力しているが、数十ファイルの変更があった際に、レポート出力するには手間と時間がかかるため自動出力したい。
実現方法の検討
WinMergeはコマンドラインを使って比較レポートを出力することができる。
比較元と比較先のフォルダを指定することで、フォルダ内のファイルを対象に比較レポートを作るツールを作る。
課題
変更ないものは比較レポートが不要。
比較方法はとりあえず fcコマンドで確認して不一致のものを対象にしてみる。
このためWinMergeで利用できる比較条件のオプションは指定できない。
※今のところ困っていないのでスルー
作成した比較レポートをExcelにまとめて出力できるようにもしたい。
サンプルコード
事前準備として、下記コードと同一フォルダに「empty.txt」名で空ファイルを用意する(新規ファイルとの比較用)。
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()
# パスチェック
# for path in args:
# if os.path.exists(path) == False:
# print(f'{path} が見つかりません')
# result = ResultCode.NG
# パスチェック
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()