persoconf/commands/check.py

188 lines
6.4 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os, os.path
import filecmp
import subprocess
from metafile import Metafile, MalformedMetafileError
# -----------------------------------------------------------------------------
def init(parser):
"""Initialize check subcommand"""
parser.add_argument( "app",
type=str,
nargs="?",
help="The app to check" )
parser.add_argument( "files",
type=str,
nargs="*",
default=[],
help="The files to check ; default to all " \
"added files" )
parser.add_argument( "-d", "--diff",
action="store_true",
help="Print the diff between the saved and the new " \
"files" )
# -----------------------------------------------------------------------------
def run(args, persoconf, logger):
logger.debug("Starting 'check' command")
difftool = None
if args.diff:
difftool = os.environ.get('DIFFTOOL', 'vimdiff')
logger.info("Selecting %s as diff tool" % difftool)
# app == None => check all apps
apps_to_check = {}
if args.app is None:
apps_to_check = persoconf.list_apps(logger=logger)
else:
try:
appmeta = Metafile(
json_path = os.path.join( persoconf.path,
args.app,
persoconf.metafile )
)
apps_to_check[appmeta.name] = appmeta
except FileNotFoundError:
logger.error( "Metafile %s does not exist for app %s"
% (persoconf.metafile, args.app) )
exit(1)
except MalformedMetafileError:
logger.error( "Malformed metafile %s in app %s"
% (persoconf.metafile, args.app) )
exit(1)
for app in apps_to_check:
logger.info( "App %s:" % app )
if args.files != []:
dstdic = apps_to_check[app].get_files_dst2src() # Get that here
# for optimization
# The filenames can be confnames or real files names. For each
# we'll assume first that it's a confname, then that it's a file
# name if that fails.
for f in args.files:
# 1) It's probably a confname
if f in apps_to_check[app].files:
persoconf_backup_path = os.path.join( persoconf.path,
app,
f )
original_file_path = apps_to_check[app].files[f]["dest"]
_compare_and_log( original_file_path,
persoconf_backup_path,
logger,
difftool )
# 2) If not, it must be a real filename
else:
absf = contractuser(os.path.abspath(os.path.expanduser(f)))
if absf in dstdic:
persoconf_backup_path = os.path.join( persoconf.path,
app,
dstdic[absf] )
original_file_path = absf
_compare_and_log( original_file_path,
persoconf_backup_path,
logger,
difftool )
# 3) Otherwise, no idea what it is
else:
logger.warning( "Cannot find file %s in app data; " \
"ignoring it"
% f )
continue
# If they were no 'file' args, it means we need to update all files in
# the app
else:
for f in apps_to_check[app].files:
persoconf_backup_path = os.path.join( persoconf.path,
app,
f )
original_file_path = apps_to_check[app].files[f]["dest"]
_compare_and_log( original_file_path,
persoconf_backup_path,
logger,
difftool )
# -----------------------------------------------------------------------------
def _compare_and_log( original_file_path,
persoconf_backup_path,
logger,
difftool = None ):
"""Compare a file with its persoconf backup and log the result (at info
level if the files are matching, warning level if they are not)."""
resolved_ofp = os.path.expanduser(original_file_path)
resolved_pbp = os.path.expanduser(persoconf_backup_path)
is_dir = os.path.isdir( resolved_ofp )
if is_dir:
dcmp = filecmp.dircmp(resolved_ofp, resolved_pbp)
if ( len(dcmp.diff_files) > 0
or len(dcmp.left_only) > 0
or len(dcmp.right_only) > 0 ):
logger.warning( "Dir %s/ was modified"
% original_file_path )
else:
logger.info( "No modification on %s/"
% original_file_path )
else:
try:
if filecmp.cmp(resolved_ofp, resolved_pbp) :
logger.info( "No modification on %s"
% original_file_path )
else:
logger.warning( "File %s was modified"
% original_file_path )
if difftool:
subprocess.call([ difftool, resolved_pbp, resolved_ofp ])
except FileNotFoundError as err:
if err.filename == resolved_pbp:
logger.warning( "File %s was modified (no backup file)"
% original_file_path )
else:
logger.warning( "File %s was deleted!"
% original_file_path )