mirror of
https://github.com/deadc0de6/dotdrop.git
synced 2026-02-16 23:09:11 +00:00
dedicated importer class
This commit is contained in:
@@ -9,7 +9,6 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from concurrent import futures
|
from concurrent import futures
|
||||||
import shutil
|
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from dotdrop.options import Options
|
from dotdrop.options import Options
|
||||||
@@ -18,9 +17,9 @@ from dotdrop.templategen import Templategen
|
|||||||
from dotdrop.installer import Installer
|
from dotdrop.installer import Installer
|
||||||
from dotdrop.updater import Updater
|
from dotdrop.updater import Updater
|
||||||
from dotdrop.comparator import Comparator
|
from dotdrop.comparator import Comparator
|
||||||
from dotdrop.utils import get_tmpdir, removepath, strip_home, \
|
from dotdrop.importer import Importer
|
||||||
uniq_list, patch_ignores, dependencies_met, get_file_perm, \
|
from dotdrop.utils import get_tmpdir, removepath, \
|
||||||
get_default_file_perms
|
uniq_list, patch_ignores, dependencies_met
|
||||||
from dotdrop.linktypes import LinkTypes
|
from dotdrop.linktypes import LinkTypes
|
||||||
from dotdrop.exceptions import YamlException, UndefinedException
|
from dotdrop.exceptions import YamlException, UndefinedException
|
||||||
|
|
||||||
@@ -430,135 +429,26 @@ def cmd_importer(o):
|
|||||||
ret = True
|
ret = True
|
||||||
cnt = 0
|
cnt = 0
|
||||||
paths = o.import_path
|
paths = o.import_path
|
||||||
|
importer = Importer(o.profile, o.conf, o.dotpath, o.diff_command,
|
||||||
|
dry=o.dry, safe=o.safe, debug=o.debug,
|
||||||
|
keepdot=o.keepdot)
|
||||||
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
if o.debug:
|
r = importer.import_path(path, import_as=o.import_as,
|
||||||
LOG.dbg('trying to import {}'.format(path))
|
import_link=o.import_link,
|
||||||
if not os.path.exists(path):
|
import_mode=o.import_mode)
|
||||||
LOG.err('\"{}\" does not exist, ignored!'.format(path))
|
if r < 0:
|
||||||
ret = False
|
ret = False
|
||||||
continue
|
elif r > 0:
|
||||||
dst = path.rstrip(os.sep)
|
|
||||||
dst = os.path.abspath(dst)
|
|
||||||
|
|
||||||
if o.safe:
|
|
||||||
# ask for symlinks
|
|
||||||
realdst = os.path.realpath(dst)
|
|
||||||
if dst != realdst:
|
|
||||||
msg = '\"{}\" is a symlink, dereference it and continue?'
|
|
||||||
if not LOG.ask(msg.format(dst)):
|
|
||||||
continue
|
|
||||||
|
|
||||||
src = strip_home(dst)
|
|
||||||
if o.import_as:
|
|
||||||
# handle import as
|
|
||||||
src = os.path.expanduser(o.import_as)
|
|
||||||
src = src.rstrip(os.sep)
|
|
||||||
src = os.path.abspath(src)
|
|
||||||
src = strip_home(src)
|
|
||||||
if o.debug:
|
|
||||||
LOG.dbg('import src for {} as {}'.format(dst, src))
|
|
||||||
|
|
||||||
strip = '.' + os.sep
|
|
||||||
if o.keepdot:
|
|
||||||
strip = os.sep
|
|
||||||
src = src.lstrip(strip)
|
|
||||||
|
|
||||||
# get the permission
|
|
||||||
perm = get_file_perm(dst)
|
|
||||||
|
|
||||||
# set the link attribute
|
|
||||||
linktype = o.import_link
|
|
||||||
if linktype == LinkTypes.LINK_CHILDREN and \
|
|
||||||
not os.path.isdir(path):
|
|
||||||
LOG.err('importing \"{}\" failed!'.format(path))
|
|
||||||
ret = False
|
|
||||||
continue
|
|
||||||
|
|
||||||
if o.debug:
|
|
||||||
LOG.dbg('import dotfile: src:{} dst:{}'.format(src, dst))
|
|
||||||
|
|
||||||
# test no other dotfile exists with same
|
|
||||||
# dst for this profile but different src
|
|
||||||
dfs = o.conf.get_dotfile_by_dst(dst)
|
|
||||||
if dfs:
|
|
||||||
invalid = False
|
|
||||||
for df in dfs:
|
|
||||||
profiles = o.conf.get_profiles_by_dotfile_key(df.key)
|
|
||||||
profiles = [x.key for x in profiles]
|
|
||||||
if o.profile in profiles and \
|
|
||||||
not o.conf.get_dotfile_by_src_dst(src, dst):
|
|
||||||
# same profile
|
|
||||||
# different src
|
|
||||||
LOG.err('duplicate dotfile for this profile')
|
|
||||||
ret = False
|
|
||||||
invalid = True
|
|
||||||
break
|
|
||||||
if invalid:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# prepare hierarchy for dotfile
|
|
||||||
srcf = os.path.join(o.dotpath, src)
|
|
||||||
overwrite = not os.path.exists(srcf)
|
|
||||||
if os.path.exists(srcf):
|
|
||||||
overwrite = True
|
|
||||||
if o.safe:
|
|
||||||
c = Comparator(debug=o.debug, diff_cmd=o.diff_command)
|
|
||||||
diff = c.compare(srcf, dst)
|
|
||||||
if diff != '':
|
|
||||||
# files are different, dunno what to do
|
|
||||||
LOG.log('diff \"{}\" VS \"{}\"'.format(dst, srcf))
|
|
||||||
LOG.emph(diff)
|
|
||||||
# ask user
|
|
||||||
msg = 'Dotfile \"{}\" already exists, overwrite?'
|
|
||||||
overwrite = LOG.ask(msg.format(srcf))
|
|
||||||
|
|
||||||
if o.debug:
|
|
||||||
LOG.dbg('will overwrite: {}'.format(overwrite))
|
|
||||||
if overwrite:
|
|
||||||
cmd = 'mkdir -p {}'.format(os.path.dirname(srcf))
|
|
||||||
if o.dry:
|
|
||||||
LOG.dry('would run: {}'.format(cmd))
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
os.makedirs(os.path.dirname(srcf), exist_ok=True)
|
|
||||||
except Exception:
|
|
||||||
LOG.err('importing \"{}\" failed!'.format(path))
|
|
||||||
ret = False
|
|
||||||
continue
|
|
||||||
|
|
||||||
if o.dry:
|
|
||||||
LOG.dry('would copy {} to {}'.format(dst, srcf))
|
|
||||||
else:
|
|
||||||
# copy the file to the dotpath
|
|
||||||
if os.path.isdir(dst):
|
|
||||||
if os.path.exists(srcf):
|
|
||||||
shutil.rmtree(srcf)
|
|
||||||
shutil.copytree(dst, srcf)
|
|
||||||
else:
|
|
||||||
shutil.copy2(dst, srcf)
|
|
||||||
|
|
||||||
chmod = None
|
|
||||||
dflperm = get_default_file_perms(dst, o.umask)
|
|
||||||
|
|
||||||
if o.debug:
|
|
||||||
LOG.dbg('import mode: {}'.format(o.import_mode))
|
|
||||||
if o.import_mode or perm != dflperm:
|
|
||||||
if o.debug:
|
|
||||||
LOG.dbg('adopt mode {:o} (umask {:o})'.format(perm, dflperm))
|
|
||||||
# insert chmod
|
|
||||||
chmod = perm
|
|
||||||
retconf = o.conf.new_dotfile(src, dst, linktype, chmod=chmod)
|
|
||||||
if retconf:
|
|
||||||
LOG.sub('\"{}\" imported'.format(path))
|
|
||||||
cnt += 1
|
cnt += 1
|
||||||
else:
|
|
||||||
LOG.warn('\"{}\" ignored'.format(path))
|
|
||||||
if o.dry:
|
if o.dry:
|
||||||
LOG.dry('new config file would be:')
|
LOG.dry('new config file would be:')
|
||||||
LOG.raw(o.conf.dump())
|
LOG.raw(o.conf.dump())
|
||||||
else:
|
else:
|
||||||
o.conf.save()
|
o.conf.save()
|
||||||
LOG.log('\n{} file(s) imported.'.format(cnt))
|
LOG.log('\n{} file(s) imported.'.format(cnt))
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
203
dotdrop/importer.py
Normal file
203
dotdrop/importer.py
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
"""
|
||||||
|
author: deadc0de6 (https://github.com/deadc0de6)
|
||||||
|
Copyright (c) 2020, deadc0de6
|
||||||
|
|
||||||
|
handle import of dotfiles
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from dotdrop.logger import Logger
|
||||||
|
from dotdrop.utils import strip_home, get_default_file_perms, \
|
||||||
|
get_file_perm, get_umask
|
||||||
|
from dotdrop.linktypes import LinkTypes
|
||||||
|
from dotdrop.comparator import Comparator
|
||||||
|
|
||||||
|
|
||||||
|
class Importer:
|
||||||
|
|
||||||
|
def __init__(self, profile, conf, dotpath, diff_cmd,
|
||||||
|
dry=False, safe=True, debug=False,
|
||||||
|
keepdot=True):
|
||||||
|
"""constructor
|
||||||
|
@profile: the selected profile
|
||||||
|
@conf: configuration manager
|
||||||
|
@dotpath: dotfiles dotpath
|
||||||
|
@diff_cmd: diff command to use
|
||||||
|
@dry: simulate
|
||||||
|
@safe: ask for overwrite if True
|
||||||
|
@debug: enable debug
|
||||||
|
@keepdot: keep dot prefix
|
||||||
|
"""
|
||||||
|
self.profile = profile
|
||||||
|
self.conf = conf
|
||||||
|
self.dotpath = dotpath
|
||||||
|
self.diff_cmd = diff_cmd
|
||||||
|
self.dry = dry
|
||||||
|
self.safe = safe
|
||||||
|
self.debug = debug
|
||||||
|
self.keepdot = keepdot
|
||||||
|
|
||||||
|
self.umask = get_umask()
|
||||||
|
self.log = Logger()
|
||||||
|
|
||||||
|
def import_path(self, path, import_as=None,
|
||||||
|
import_link=LinkTypes.NOLINK, import_mode=False):
|
||||||
|
"""
|
||||||
|
import a dotfile pointed by path
|
||||||
|
returns:
|
||||||
|
1: 1 dotfile imported
|
||||||
|
0: ignored
|
||||||
|
-1: error
|
||||||
|
"""
|
||||||
|
if self.debug:
|
||||||
|
self.log.dbg('import {}'.format(path))
|
||||||
|
if not os.path.exists(path):
|
||||||
|
self.log.err('\"{}\" does not exist, ignored!'.format(path))
|
||||||
|
return -1
|
||||||
|
|
||||||
|
return self._import(path, import_as=import_as,
|
||||||
|
import_link=import_link, import_mode=import_mode)
|
||||||
|
|
||||||
|
def _import(self, path, import_as=None,
|
||||||
|
import_link=LinkTypes.NOLINK, import_mode=False):
|
||||||
|
"""
|
||||||
|
import path
|
||||||
|
returns:
|
||||||
|
1: 1 dotfile imported
|
||||||
|
0: ignored
|
||||||
|
-1: error
|
||||||
|
"""
|
||||||
|
|
||||||
|
# normalize path
|
||||||
|
dst = path.rstrip(os.sep)
|
||||||
|
dst = os.path.abspath(dst)
|
||||||
|
|
||||||
|
# ask confirmation for symlinks
|
||||||
|
if self.safe:
|
||||||
|
realdst = os.path.realpath(dst)
|
||||||
|
if dst != realdst:
|
||||||
|
msg = '\"{}\" is a symlink, dereference it and continue?'
|
||||||
|
if not self.log.ask(msg.format(dst)):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# create src path
|
||||||
|
src = strip_home(dst)
|
||||||
|
if import_as:
|
||||||
|
# handle import as
|
||||||
|
src = os.path.expanduser(import_as)
|
||||||
|
src = src.rstrip(os.sep)
|
||||||
|
src = os.path.abspath(src)
|
||||||
|
src = strip_home(src)
|
||||||
|
if self.debug:
|
||||||
|
self.log.dbg('import src for {} as {}'.format(dst, src))
|
||||||
|
# with or without dot prefix
|
||||||
|
strip = '.' + os.sep
|
||||||
|
if self.keepdot:
|
||||||
|
strip = os.sep
|
||||||
|
src = src.lstrip(strip)
|
||||||
|
|
||||||
|
# get the permission
|
||||||
|
perm = get_file_perm(dst)
|
||||||
|
|
||||||
|
# get the link attribute
|
||||||
|
linktype = import_link
|
||||||
|
if linktype == LinkTypes.LINK_CHILDREN and \
|
||||||
|
not os.path.isdir(path):
|
||||||
|
self.log.err('importing \"{}\" failed!'.format(path))
|
||||||
|
return -1
|
||||||
|
|
||||||
|
if self._already_exists(src, dst):
|
||||||
|
return -1
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
self.log.dbg('import dotfile: src:{} dst:{}'.format(src, dst))
|
||||||
|
|
||||||
|
if not self._prepare_hierarchy(src, dst):
|
||||||
|
return -1
|
||||||
|
|
||||||
|
# handle file mode
|
||||||
|
chmod = None
|
||||||
|
dflperm = get_default_file_perms(dst, self.umask)
|
||||||
|
if self.debug:
|
||||||
|
self.log.dbg('import mode: {}'.format(import_mode))
|
||||||
|
if import_mode or perm != dflperm:
|
||||||
|
if self.debug:
|
||||||
|
msg = 'adopt mode {:o} (umask {:o})'
|
||||||
|
self.log.dbg(msg.format(perm, dflperm))
|
||||||
|
chmod = perm
|
||||||
|
|
||||||
|
# add file to config file
|
||||||
|
retconf = self.conf.new_dotfile(src, dst, linktype, chmod=chmod)
|
||||||
|
if not retconf:
|
||||||
|
self.log.warn('\"{}\" ignored'.format(path))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
self.log.sub('\"{}\" imported'.format(path))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def _prepare_hierarchy(self, src, dst):
|
||||||
|
"""prepare hierarchy for dotfile"""
|
||||||
|
srcf = os.path.join(self.dotpath, src)
|
||||||
|
|
||||||
|
# a dotfile in dotpath already exists at that spot
|
||||||
|
if os.path.exists(srcf):
|
||||||
|
if self.safe:
|
||||||
|
c = Comparator(debug=self.debug,
|
||||||
|
diff_cmd=self.diff_cmd)
|
||||||
|
diff = c.compare(srcf, dst)
|
||||||
|
if diff != '':
|
||||||
|
# files are different, dunno what to do
|
||||||
|
self.log.log('diff \"{}\" VS \"{}\"'.format(dst, srcf))
|
||||||
|
self.log.emph(diff)
|
||||||
|
# ask user
|
||||||
|
msg = 'Dotfile \"{}\" already exists, overwrite?'
|
||||||
|
if not self.log.ask(msg.format(srcf)):
|
||||||
|
return False
|
||||||
|
if self.debug:
|
||||||
|
self.log.dbg('will overwrite existing file')
|
||||||
|
|
||||||
|
# create directory hierarchy
|
||||||
|
cmd = 'mkdir -p {}'.format(os.path.dirname(srcf))
|
||||||
|
if self.dry:
|
||||||
|
self.log.dry('would run: {}'.format(cmd))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
os.makedirs(os.path.dirname(srcf), exist_ok=True)
|
||||||
|
except Exception:
|
||||||
|
self.log.err('importing \"{}\" failed!'.format(dst))
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.dry:
|
||||||
|
self.log.dry('would copy {} to {}'.format(dst, srcf))
|
||||||
|
else:
|
||||||
|
# copy the file to the dotpath
|
||||||
|
if os.path.isdir(dst):
|
||||||
|
if os.path.exists(srcf):
|
||||||
|
shutil.rmtree(srcf)
|
||||||
|
shutil.copytree(dst, srcf)
|
||||||
|
else:
|
||||||
|
shutil.copy2(dst, srcf)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _already_exists(self, src, dst):
|
||||||
|
"""
|
||||||
|
test no other dotfile exists with same
|
||||||
|
dst for this profile but different src
|
||||||
|
"""
|
||||||
|
dfs = self.conf.get_dotfile_by_dst(dst)
|
||||||
|
if not dfs:
|
||||||
|
return False
|
||||||
|
for df in dfs:
|
||||||
|
profiles = self.conf.get_profiles_by_dotfile_key(df.key)
|
||||||
|
profiles = [x.key for x in profiles]
|
||||||
|
if self.profile in profiles and \
|
||||||
|
not self.conf.get_dotfile_by_src_dst(src, dst):
|
||||||
|
# same profile
|
||||||
|
# different src
|
||||||
|
self.log.err('duplicate dotfile for this profile')
|
||||||
|
return True
|
||||||
|
return False
|
||||||
@@ -16,7 +16,7 @@ from dotdrop.linktypes import LinkTypes
|
|||||||
from dotdrop.logger import Logger
|
from dotdrop.logger import Logger
|
||||||
from dotdrop.cfg_aggregator import CfgAggregator as Cfg
|
from dotdrop.cfg_aggregator import CfgAggregator as Cfg
|
||||||
from dotdrop.action import Action
|
from dotdrop.action import Action
|
||||||
from dotdrop.utils import uniq_list, get_umask
|
from dotdrop.utils import uniq_list
|
||||||
from dotdrop.exceptions import YamlException
|
from dotdrop.exceptions import YamlException
|
||||||
|
|
||||||
ENV_PROFILE = 'DOTDROP_PROFILE'
|
ENV_PROFILE = 'DOTDROP_PROFILE'
|
||||||
@@ -123,7 +123,6 @@ class Options(AttrMonitor):
|
|||||||
self.log = Logger()
|
self.log = Logger()
|
||||||
self.debug = self.args['--verbose'] or ENV_DEBUG in os.environ
|
self.debug = self.args['--verbose'] or ENV_DEBUG in os.environ
|
||||||
self.dry = self.args['--dry']
|
self.dry = self.args['--dry']
|
||||||
self.umask = get_umask()
|
|
||||||
if ENV_NODEBUG in os.environ:
|
if ENV_NODEBUG in os.environ:
|
||||||
# force disabling debugs
|
# force disabling debugs
|
||||||
self.debug = False
|
self.debug = False
|
||||||
|
|||||||
Reference in New Issue
Block a user