786 lines
28 KiB
Python
Executable File
786 lines
28 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import logging
|
|
import os, os.path
|
|
import shutil
|
|
import time
|
|
import tarfile
|
|
|
|
from metafile import Metafile, MalformedMetafileError, NoPathDefinedError
|
|
from persoconf import Persoconf, FileInsteadOfDirError, \
|
|
PersoconfRootDoesNotExistError
|
|
|
|
# FUNCTIONS
|
|
########################################
|
|
def yes_no_question(question="Yes or no?", default=False):
|
|
prompt=""
|
|
if(default is True):
|
|
prompt = " [Y/n]"
|
|
else:
|
|
prompt = " [y/N]"
|
|
|
|
answer = input(question + prompt).lower()
|
|
|
|
if(len(answer) == 0):
|
|
return default
|
|
else:
|
|
answer = answer[0]
|
|
|
|
if(default is True):
|
|
if answer in ['n','0']:
|
|
return False
|
|
else:
|
|
if answer in ['y','o','1']:
|
|
return True
|
|
|
|
return default
|
|
|
|
def contractuser(path):
|
|
home = os.path.expanduser("~")
|
|
|
|
if path.startswith(home):
|
|
return path.replace(home, "~", 1) # Replace only one instance:
|
|
# the first one
|
|
else:
|
|
return path
|
|
|
|
def copy_file_or_directory(logger, path_dst, path_src, overwrite=True):
|
|
|
|
# Expand '~' as user's home dir
|
|
dst = os.path.expanduser(path_dst)
|
|
src = os.path.expanduser(path_src)
|
|
|
|
if overwrite:
|
|
|
|
# --------------------------------------
|
|
def replace_backup():
|
|
try:
|
|
logger.info("Putting backup back in place")
|
|
shutil.move(dst + ".bak", dst)
|
|
|
|
except FileNotFoundError:
|
|
logger.error("No backup of %s found. Oops" % dst)
|
|
return False
|
|
|
|
except PermissionError:
|
|
logger.error("Cannot replace backup %s because of permissions"
|
|
% (dst + ".bak"))
|
|
return False
|
|
|
|
except FileExistsError:
|
|
logger.error("%s has been created, you need to chose " \
|
|
"manually to keep it or to use the backup %s"
|
|
% (dst, dst + ".bak"))
|
|
return False
|
|
|
|
return True
|
|
# --------------------------------------
|
|
|
|
|
|
try:
|
|
logger.info("Moving old directory %s" % dst)
|
|
shutil.move(dst, dst + ".bak")
|
|
|
|
except FileNotFoundError:
|
|
logger.info("No file %s exists yet" % dst)
|
|
|
|
except PermissionError:
|
|
logger.error("Unable to write %s, please check permissions"
|
|
% (dst + ".bak"))
|
|
return False
|
|
|
|
except FileExistsError:
|
|
logger.error("%s already exists" % (dst + ".bak"))
|
|
return False
|
|
|
|
|
|
# No overwrite ? No problem.
|
|
else:
|
|
|
|
def replace_backup():
|
|
return True
|
|
|
|
|
|
# Copy the conf dir
|
|
# --------------------------------------
|
|
try:
|
|
logger.info("Copying directory %s" % src)
|
|
shutil.copytree(src, dst)
|
|
|
|
except FileNotFoundError:
|
|
logger.error("%s does not seem to exist" % src)
|
|
replace_backup()
|
|
return False
|
|
|
|
except PermissionError:
|
|
logger.error("Unable to write %s, please check permissions" % dst)
|
|
replace_backup()
|
|
return False
|
|
|
|
except FileExistsError:
|
|
logger.error("The directory '%s' already exists" % dst)
|
|
replace_backup()
|
|
return False
|
|
|
|
# It's not a dir, it's a file !
|
|
# --------------------------------------
|
|
except NotADirectoryError:
|
|
|
|
# Try not to overwrite an existing file.
|
|
# Note that if 'overwrite' was set to True, any existing file would
|
|
# have been moved at this point.
|
|
if os.path.exists(dst):
|
|
logger.error("The file %s already exists" % dst)
|
|
replace_backup()
|
|
return False
|
|
|
|
try:
|
|
logger.info("Copying file %s" % src)
|
|
shutil.copyfile(src, dst)
|
|
|
|
except PermissionError:
|
|
logger.error("Unable to write %s, please check permissions" % dst)
|
|
replace_backup()
|
|
return False
|
|
|
|
# Everything went well, time to remove the backup
|
|
# --------------------------------------
|
|
if overwrite:
|
|
delete_file_or_dir((dst + ".bak"), logger)
|
|
|
|
# The End.
|
|
return True
|
|
|
|
|
|
# ======================================
|
|
def delete_file_or_dir(path, logger=None):
|
|
|
|
# Set logger to '/dev/null' if it's not set
|
|
if logger is None:
|
|
logger = logging.getLogger("/dev/null")
|
|
logger.addHandler(logging.NullHandler())
|
|
|
|
# Try it as a dir first
|
|
try:
|
|
logger.info("Removing directory %s" % path)
|
|
shutil.rmtree(path)
|
|
|
|
except FileNotFoundError:
|
|
logger.warning("File %s seems to be already deleted" % path)
|
|
return False
|
|
except PermissionError:
|
|
logger.warning("Cannot remove dir %s, you will have to do it manually"
|
|
% path)
|
|
return False
|
|
|
|
# If not, try it as a file
|
|
except NotADirectoryError:
|
|
try:
|
|
logger.info("Removing file %s" % path)
|
|
os.remove(path)
|
|
|
|
except PermissionError:
|
|
logger.warning( "Cannot remove file %s, you'll have to do it " \
|
|
"manually"
|
|
% path )
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
# 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" )
|
|
|
|
parser_list.add_argument( "apps" ,
|
|
type=str ,
|
|
nargs="*" ,
|
|
default=[] ,
|
|
help="The apps to list ; defaults to all " \
|
|
"apps" )
|
|
|
|
parser_list.add_argument( "--show-files", "-f" ,
|
|
action="store_true" ,
|
|
help="Show the files for each app ; it is " \
|
|
"automatically set if you specify the apps " \
|
|
"to list." )
|
|
|
|
# Package
|
|
parser_package = subparsers.add_parser( "package" ,
|
|
help="Package a persoconf repository" )
|
|
|
|
parser_package.add_argument( "apps" ,
|
|
type=str ,
|
|
nargs="*" ,
|
|
default=[] ,
|
|
help="The apps to package ; defaults to all " \
|
|
"apps" )
|
|
|
|
parser_package.add_argument( "--pkgtype", "-t" ,
|
|
type=str ,
|
|
nargs="?" ,
|
|
default="tgz" ,
|
|
choices = [ "tgz" ] ,
|
|
help="The type of package to use" )
|
|
|
|
parser_package.add_argument( "--pkgname", "-p" ,
|
|
type=str ,
|
|
nargs="?" ,
|
|
default="persoconf." + (time.strftime("%Y%m%d")) ,
|
|
help="The name of the package to create" )
|
|
|
|
# Update
|
|
parser_update = subparsers.add_parser( "update" ,
|
|
help="Backup an app configuration in " \
|
|
"the persoconf directory with " \
|
|
"the configuration in use now" )
|
|
|
|
parser_update.add_argument( "app" ,
|
|
type=str ,
|
|
nargs="?" ,
|
|
help="The app to update" )
|
|
|
|
parser_update.add_argument( "files" ,
|
|
type=str ,
|
|
nargs="*" ,
|
|
default=[] ,
|
|
help="The files to update ; default to all " \
|
|
"added files" )
|
|
|
|
# Add
|
|
parser_add = subparsers.add_parser( "add" ,
|
|
help="Add an app to the persoconf " \
|
|
"directory, or add a config file " \
|
|
"to an existing app" )
|
|
|
|
parser_add.add_argument( "app" ,
|
|
type=str ,
|
|
help="The app to add, or the app to add a conf " \
|
|
"file to" )
|
|
|
|
parser_add.add_argument( "conf" ,
|
|
nargs="?" ,
|
|
type=str ,
|
|
help="The conf file or directory to add to the app" )
|
|
|
|
parser_add.add_argument( "--confname" ,
|
|
type=str ,
|
|
help="A special name to save the conf " \
|
|
"file/directory in the persoconf directory" )
|
|
|
|
# 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":
|
|
|
|
app_list = persoconf.list_apps(logger=log)
|
|
|
|
# If no app is given: list all apps
|
|
if args.apps == []:
|
|
log.info("Listing all apps")
|
|
for app in app_list:
|
|
# Print the app name
|
|
print(app)
|
|
|
|
# Print the files too if asked so
|
|
if args.show_files:
|
|
for f in app_list[app].files:
|
|
if os.path.isdir(persoconf.path + "/" + app + "/" + f):
|
|
print("\t%s/" % f)
|
|
else:
|
|
print("\t%s" % f)
|
|
|
|
# Some apps were specified, so we list them with their files
|
|
else:
|
|
for app in args.apps:
|
|
if app not in app_list:
|
|
log.warning("No such app: %s" % app)
|
|
continue
|
|
|
|
# Print the app name
|
|
print(app)
|
|
|
|
# Print the app files
|
|
for f in app_list[app].files:
|
|
if os.path.isdir(persoconf.path + "/" + app + "/" + f):
|
|
print("\t%s/" % f)
|
|
else:
|
|
print("\t%s" % f)
|
|
|
|
|
|
# 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":
|
|
|
|
# Check (and create) the app directory
|
|
appdir = persoconf.path + "/" + args.app
|
|
|
|
if os.path.isdir(appdir):
|
|
if args.conf is None:
|
|
log.warning("App '%s' already exists" % args.app)
|
|
exit()
|
|
|
|
elif os.path.exists(appdir):
|
|
log.error("The name '%s' is already used by a file in the persoconf directory" % args.app)
|
|
exit()
|
|
|
|
else:
|
|
log.info("Creating app '%s' in persoconf directory..." % args.app)
|
|
os.mkdir(appdir)
|
|
|
|
print("New app '%s' created in persoconf directory" % args.app)
|
|
|
|
if args.conf is None:
|
|
exit()
|
|
|
|
# Check that the conf file we are saving exists
|
|
if args.confname:
|
|
confname = args.confname
|
|
else:
|
|
confname = args.conf
|
|
|
|
# We need to remove trailing '/' because 'os.path.basename("toto/")'
|
|
# returns ''. And that's not cool.
|
|
while confname[-1] == "/":
|
|
confname = confname[:-1] # Remove trailing slashes
|
|
|
|
confname = os.path.basename(confname)
|
|
|
|
# Remove leading dots, so the name under which the conffile is saved is
|
|
# not hidden.
|
|
while confname[0] == ".":
|
|
confname = confname[1:] # Remove leading dots
|
|
|
|
confpath = appdir + "/" + confname
|
|
|
|
# Load app META file
|
|
try:
|
|
appmeta = Metafile(json_path = appdir + "/" + persoconf.metafile)
|
|
|
|
except FileNotFoundError:
|
|
log.info("Metafile %s does not exist for app %s" % (persoconf.metafile, args.app))
|
|
appmeta = Metafile( name=args.app )
|
|
appmeta.json_path = "/".join( [persoconf.path, args.app, persoconf.metafile] )
|
|
|
|
except MalformedMetafileError:
|
|
log.error("Malformed metafile %s in app %s" % (persoconf.metafile, args.app) )
|
|
exit(1)
|
|
|
|
# Check that the file is really new
|
|
absconf = contractuser( os.path.abspath(os.path.expanduser(args.conf)) )
|
|
|
|
try:
|
|
dstdic = appmeta.get_files_dst2src()
|
|
|
|
except MalformedMetafileError:
|
|
log.error("Malformed metafile %s in app %s" % (persoconf.metafile, args.app) )
|
|
exit(1)
|
|
|
|
if absconf in dstdic:
|
|
log.error("File %s already exists as %s in app %s" % (absconf, dstdic[absconf], args.app))
|
|
exit(1)
|
|
|
|
# Copy the file (or directory)
|
|
if copy_file_or_directory(log, path_dst=confpath, path_src=args.conf, overwrite=False):
|
|
log.info("New conf dir '%s' associated with app '%s'" % (confname, args.app))
|
|
else:
|
|
log.error("Failed to copy file or directory %s" % args.conf)
|
|
exit(1)
|
|
|
|
# Add the file to META: first update META data
|
|
log.debug("adding meta data for %s in %s" % (confname, args.app))
|
|
appmeta.add_file( confname, dest=absconf )
|
|
|
|
# Then write to the metafile
|
|
try:
|
|
log.debug("Writing to metafile")
|
|
appmeta.save()
|
|
|
|
except NoPathDefinedError:
|
|
log.error("Unable to save json Metafile for app %s : no path defined" % appmeta.name)
|
|
exit(1)
|
|
|
|
# UPDATE COMMAND
|
|
########################################
|
|
if args.command == "update":
|
|
|
|
# app == None => update all apps
|
|
apps_to_update = {}
|
|
if args.app is None:
|
|
apps_to_update = persoconf.list_apps(logger=log)
|
|
|
|
else:
|
|
try:
|
|
appmeta = Metafile( json_path = persoconf.path +"/"+
|
|
args.app +"/"+
|
|
persoconf.metafile )
|
|
|
|
apps_to_update[appmeta.name] = appmeta
|
|
|
|
except FileNotFoundError:
|
|
log.error( "Metafile %s does not exist for app %s"
|
|
% (persoconf.metafile, args.app) )
|
|
exit(1)
|
|
|
|
except MalformedMetafileError:
|
|
log.error( "Malformed metafile %s in app %s"
|
|
% (persoconf.metafile, args.app) )
|
|
exit(1)
|
|
|
|
|
|
for app in apps_to_update:
|
|
|
|
if args.files != []:
|
|
|
|
dstdic = apps_to_update[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_update[app].files:
|
|
|
|
if copy_file_or_directory(
|
|
log ,
|
|
path_dst = persoconf.path
|
|
+"/"+ app
|
|
+"/"+ f ,
|
|
path_src = apps_to_update[app].files[f]["dest"]
|
|
):
|
|
|
|
log.info( "Updated %s from %s"
|
|
% (f, apps_to_update[app].files[f]["dest"]) )
|
|
else:
|
|
log.warning(
|
|
"Failed to update %s from %s ; ignoring"
|
|
% ( f, apps_to_update[app].files[f]["dest"])
|
|
)
|
|
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 copy_file_or_directory(
|
|
log ,
|
|
path_dst = persoconf.path
|
|
+"/"+ app
|
|
+"/"+ dstdic[absf] ,
|
|
path_src = absf
|
|
):
|
|
log.info( "Updated %s from %s"
|
|
% (dstdic[absf], absf) )
|
|
|
|
else:
|
|
log.warning( "Failed to update %s from %s ; " \
|
|
"ignoring"
|
|
% (dstdic[absf], absf) )
|
|
continue
|
|
|
|
# 3) Otherwise, no idea what it is
|
|
else:
|
|
log.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_update[app].files:
|
|
if copy_file_or_directory(
|
|
log ,
|
|
path_dst = persoconf.path
|
|
+"/"+ app
|
|
+"/"+ f ,
|
|
path_src = apps_to_update[app].files[f]["dest"]
|
|
):
|
|
log.info( "Updated %s from %s"
|
|
% (f, apps_to_update[app].files[f]["dest"]) )
|
|
|
|
else:
|
|
log.warning( "Failed to update %s from %s ; ignoring"
|
|
% (f, apps_to_update[app].files[f]["dest"]) )
|
|
|
|
# PACKAGE COMMAND
|
|
########################################
|
|
if args.command == "package":
|
|
|
|
pkgname = args.pkgname
|
|
|
|
# If no app is given: package all apps
|
|
if args.apps == []:
|
|
log.info("Packaging all apps")
|
|
args.apps = persoconf.list_apps(logger=log)
|
|
|
|
# Some apps were specified, so we list them with their files
|
|
appmetas = []
|
|
for app in args.apps :
|
|
# Load app META file
|
|
try:
|
|
appmetas.append( Metafile(json_path = "/".join([persoconf.path, app, persoconf.metafile])) )
|
|
|
|
except FileNotFoundError:
|
|
log.warning("Metafile %s does not exist for app %s; ignoring" % (persoconf.metafile, app))
|
|
continue
|
|
|
|
except MalformedMetafileError:
|
|
log.warning("Malformed metafile %s in app %s; ignoring" % (persoconf.metafile, app) )
|
|
continue
|
|
|
|
# TODO check that the app exists
|
|
|
|
# Adds the app name if there is only one packaged
|
|
if len(appmetas) == 1:
|
|
pkgname += ("." + appmeta[0].name)
|
|
|
|
# Switch according to the pkgtype
|
|
if args.pkgtype == "tgz":
|
|
|
|
pkgname += ".tgz"
|
|
|
|
# Create a tar containing the files in the right place
|
|
pkg = tarfile.open(pkgname, "w:gz")
|
|
|
|
for appmeta in appmetas:
|
|
for f in appmeta.files:
|
|
filename = persoconf.path +"/"+ appmeta.name +"/"+ f
|
|
filedest = appmeta.files[f]["dest"]
|
|
|
|
# Remove possible 'home'. The final targz should be extracted
|
|
# at 'home.
|
|
if filedest.startswith("~/"):
|
|
filedest = filedest[2:]
|
|
|
|
# TODO save and restore owners and permissions
|
|
try:
|
|
pkg.add(filename, arcname=filedest)
|
|
log.info("Adding %s to package" % f)
|
|
except:
|
|
log.warning("Failed to add %s to tar package %s" % (f, pkgname))
|
|
|
|
|
|
pkg.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
########################################
|