#!/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 )