persoconf/persoconf/main.py

280 lines
9.9 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import logging
import os, os.path
import shutil
import time
import tarfile
from utils import yes_no_question, contractuser, \
copy_file_or_directory, delete_file_or_dir
from metafile import Metafile, MalformedMetafileError, NoPathDefinedError
from persoconf import Persoconf, FileInsteadOfDirError, \
PersoconfRootDoesNotExistError
import commands.list, commands.package, commands.update, commands.add
# ARGPARSE
########################################
# Argument parsing using argparse
parser = argparse.ArgumentParser()
parser.add_argument( "--rootdir" ,
type=str ,
default="~/.config/persoconf/" ,
help="The persoconf directory to use" )
parser.add_argument( "--metafile" ,
type=str ,
default="META" ,
help="The name of the metadata files" )
parser.add_argument( "--verbose", "-v" ,
action="store_true" ,
help="Spout out more logs" )
subparsers = parser.add_subparsers( dest="command" ,
title="commands" )
# Help
parser_help = subparsers.add_parser( "help" ,
help="Print this help" )
# List
parser_list = subparsers.add_parser( "list" ,
help="List backuped apps" )
commands.list.init(parser_list)
# Package
parser_package = subparsers.add_parser( "package" ,
help="Package a persoconf repository" )
commands.package.init(parser_package)
# Update
parser_update = subparsers.add_parser( "update" ,
help="Backup an app configuration in " \
"the persoconf directory with " \
"the configuration in use now" )
commands.update.init(parser_update)
# Add
parser_add = subparsers.add_parser( "add" ,
help="Add an app to the persoconf " \
"directory, or add a config file " \
"to an existing app" )
commands.add.init(parser_add)
# Delete
parser_del = subparsers.add_parser( "delete" ,
help="Delete an app from the persoconf " \
"directory, or a config file from " \
"an existing app" )
parser_del.add_argument( "app" ,
type=str ,
help="The app to delete, or the app to delete a " \
"conf file from" )
parser_del.add_argument( "files" ,
nargs="*" ,
type=str ,
help="The conf files or directories to delete from " \
"the app" )
parser_del.add_argument( "--force-yes", "-y" ,
action="store_true" ,
help="Don't ask silly questions" )
# Parse arguments
args = parser.parse_args()
# Transform paths
args.rootdir = os.path.expanduser(args.rootdir)
# LOGGING
########################################
log = logging.getLogger("persoconf")
log.setLevel(logging.DEBUG)
# Console handler
log_handler_console = logging.StreamHandler()
log_handler_console.setLevel(logging.WARNING)
# Let's adjust the console handler to the verbosity level required
if(args.verbose):
log_handler_console.setLevel(logging.DEBUG)
log_formatter_console = logging.Formatter("%(name)s:%(levelname)s: %(message)s")
log_handler_console.setFormatter(log_formatter_console)
log.addHandler(log_handler_console)
# SCRIPT
########################################
# If we were asked for help, let's give some
if args.command in [ "help", None ]:
parser.print_help()
exit()
# Try to create the Persoconf
try:
persoconf = Persoconf(path=args.rootdir, metafile=args.metafile)
# If the place is already taken, there's nothing we can do
except FileInsteadOfDirError:
log.error("'%s' exists but is a file" % str(args.rootdir))
# Otherwise, maybe we can create the directory ?
except PersoconfRootDoesNotExistError:
log.warning("'%s' does not exist" % str(args.rootdir))
# Ask before creating the directory
if not yes_no_question( "Do you want to create the persoconf directory " \
"'%s'?"
% str(args.rootdir) ) :
log.info("The persoconf directory was not created")
exit()
# Create the directory
log.info("Creating persoconf directory...")
os.mkdir(args.rootdir)
log.info("New persoconf directory created at '%s'" % args.rootdir)
# Finally retry to create the Persoconf
try:
persoconf = Persoconf(args.rootdir, metafile=args.metafile)
except Error as err:
log.error("Something very unexpected occured, I'm sorry")
log.error("Error dump follows")
log.error(str(err))
exit()
# LIST COMMAND
########################################
if args.command == "list":
commands.list.run(args, persoconf, log)
# DELETE COMMAND
########################################
if args.command == "delete":
# Does this app really exists ?
try:
appmeta = Metafile( json_path = persoconf.path
+"/"+ args.app
+"/"+ persoconf.metafile )
except FileNotFoundError:
log.error( "App %s does not seem to exist" % args.app )
exit(1)
except MalformedMetafileError:
log.error( "Failed to load metafile from app %s" % args.app )
exit(1)
# Does the user want to remove files or the entire app ?
if args.files == []:
# Delete the entire app
if ( args.force_yes
or yes_no_question("Really delete app '%s'?" % appmeta.name )
):
shutil.rmtree( persoconf.path +"/"+ appmeta.name )
log.warning("App %s was deleted" % appmeta.name)
else:
log.warning("Nothing was deleted")
exit(0)
else:
# Delete all required files (or directories)
if ( not args.force_yes
and not yes_no_question( "Delete all files from following list: " \
"%s from app %s?"
% (str(args.files),appmeta.name))
):
log.warning("Nothing was deleted")
exit(0)
# 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.
dstdic = appmeta.get_files_dst2src() # Get that here for optimization
for f in args.files:
# 1) It's probably a confname
if f in appmeta.files:
if delete_file_or_dir( logger = log ,
path = persoconf.path
+"/"+ appmeta.name
+"/"+ f ):
# File deleted, let's delete the entry in Metafile
appmeta.del_file(f)
appmeta.save()
else:
log.warning( "Something wrong occured while deleting " \
"file %s"
% f )
continue
# 2) If not, it must be a real filename
else:
absf = contractuser(os.path.abspath(os.path.expanduser(f)))
if absf in dstdic:
if delete_file_or_dir( logger = log ,
path = persoconf.path
+"/"+ appmeta.name
+"/"+ dstdir[absf] ):
# File deleted, let's delete the entry in Metafile
appmeta.del_file(dstdir[absf])
appmeta.save()
else:
log.warning( "Something wrong occured while " \
"deleting file %s"
% f )
continue
# 3) Otherwise, no idea what it is
else:
log.warning( "Cannot find file %s in app %s data; " \
"ignoring it"
% (f, appmeta.name) )
continue
# ADD COMMAND
########################################
if args.command == "add":
commands.add.run(args, persoconf, log)
# UPDATE COMMAND
########################################
if args.command == "update":
commands.update.run(args, persoconf, log)
# PACKAGE COMMAND
########################################
if args.command == "package":
commands.package.run(args, persoconf, log)
########################################