mirror of
https://github.com/deadc0de6/dotdrop.git
synced 2026-02-07 20:54:22 +00:00
first commit
This commit is contained in:
0
dotdrop/__init__.py
Normal file
0
dotdrop/__init__.py
Normal file
133
dotdrop/config.py
Normal file
133
dotdrop/config.py
Normal file
@@ -0,0 +1,133 @@
|
||||
"""
|
||||
author: deadc0de6 (https://github.com/deadc0de6)
|
||||
config file manager
|
||||
"""
|
||||
|
||||
import yaml
|
||||
import os
|
||||
from dotfile import Dotfile
|
||||
from logger import Logger
|
||||
|
||||
|
||||
class Cfg:
|
||||
key_all = 'ALL'
|
||||
key_config = 'config'
|
||||
key_profiles = 'profiles'
|
||||
key_dotfiles = 'dotfiles'
|
||||
key_dotfiles_src = 'src'
|
||||
key_dotfiles_dst = 'dst'
|
||||
|
||||
def __init__(self, cfgpath, dotpath):
|
||||
if not os.path.exists(cfgpath):
|
||||
raise ValueError('config file does not exist')
|
||||
self.cfgpath = cfgpath
|
||||
self.log = Logger()
|
||||
relconf = dotpath
|
||||
if not relconf.startswith(os.sep):
|
||||
relconf = os.path.join(os.path.dirname(cfgpath), dotpath)
|
||||
self.configs = {'dotpath': relconf}
|
||||
self.dotfiles = {}
|
||||
self.profiles = {}
|
||||
self.prodots = {}
|
||||
if not self._load_file():
|
||||
raise ValueError('config is not valid')
|
||||
|
||||
def _load_file(self):
|
||||
with open(self.cfgpath, 'r') as f:
|
||||
self.content = yaml.load(f)
|
||||
if not self._is_valid():
|
||||
return False
|
||||
return self._parse()
|
||||
|
||||
def _is_valid(self):
|
||||
if self.key_profiles not in self.content:
|
||||
self.log.err('missing \"%s\" in config' % (self.key_profiles))
|
||||
return False
|
||||
if self.key_config not in self.content:
|
||||
self.log.err('missing \"%s\" in config' % (self.key_config))
|
||||
return False
|
||||
if self.key_dotfiles not in self.content:
|
||||
self.log.err('missing \"%s\" in config' % (self.key_dotfiles))
|
||||
return False
|
||||
return True
|
||||
|
||||
def _parse(self):
|
||||
""" parse config file """
|
||||
self.profiles = self.content[self.key_profiles]
|
||||
if self.profiles is None:
|
||||
self.profiles = {}
|
||||
self.configs = self.content[self.key_config]
|
||||
# contains all defined dotfiles
|
||||
if self.content[self.key_dotfiles] is not None:
|
||||
for k, v in self.content[self.key_dotfiles].items():
|
||||
src = v[self.key_dotfiles_src]
|
||||
dst = v[self.key_dotfiles_dst]
|
||||
self.dotfiles[k] = Dotfile(k, dst, src)
|
||||
# contains a list of dotfiles defined for each profile
|
||||
for k, v in self.profiles.items():
|
||||
self.prodots[k] = []
|
||||
if v is None:
|
||||
continue
|
||||
if len(v) == 1 and v == [self.key_all]:
|
||||
self.prodots[k] = self.dotfiles.values()
|
||||
else:
|
||||
self.prodots[k].extend([self.dotfiles[dot] for dot in v])
|
||||
# make sure we have a correct dotpath
|
||||
if not self.configs['dotpath'].startswith(os.sep):
|
||||
relconf = os.path.join(os.path.dirname(
|
||||
self.cfgpath), self.configs['dotpath'])
|
||||
self.configs['dotpath'] = relconf
|
||||
return True
|
||||
|
||||
def new(self, dotfile, profile):
|
||||
""" import new dotfile """
|
||||
dots = self.content[self.key_dotfiles]
|
||||
if dots is None:
|
||||
self.content[self.key_dotfiles] = {}
|
||||
dots = self.content[self.key_dotfiles]
|
||||
if self.content[self.key_dotfiles] and dotfile.key in dots:
|
||||
self.log.err('\"%s\" entry already exists in dotfiles' %
|
||||
(dotfile.key))
|
||||
return False
|
||||
home = os.path.expanduser('~')
|
||||
dotfile.dst = dotfile.dst.replace(home, '~')
|
||||
dots[dotfile.key] = {
|
||||
self.key_dotfiles_dst: dotfile.dst,
|
||||
self.key_dotfiles_src: dotfile.src
|
||||
}
|
||||
profiles = self.profiles
|
||||
if profile in profiles and profiles[profile] != [self.key_all]:
|
||||
if self.content[self.key_profiles][profile] is None:
|
||||
self.content[self.key_profiles][profile] = []
|
||||
self.content[self.key_profiles][profile].append(dotfile.key)
|
||||
elif profile not in profiles:
|
||||
if self.content[self.key_profiles] is None:
|
||||
self.content[self.key_profiles] = {}
|
||||
self.content[self.key_profiles][profile] = [dotfile.key]
|
||||
self.profiles = self.content[self.key_profiles]
|
||||
|
||||
def get_dotfiles(self, profile):
|
||||
""" returns a list of dotfiles for a specific profile """
|
||||
if profile not in self.prodots:
|
||||
return []
|
||||
tmp = sorted(self.prodots[profile], key=lambda x: x.key, reverse=True)
|
||||
return tmp
|
||||
|
||||
def get_profiles(self):
|
||||
""" returns all defined profiles """
|
||||
return self.profiles.keys()
|
||||
|
||||
def get_configs(self):
|
||||
""" returns all defined configs """
|
||||
return self.configs.copy()
|
||||
|
||||
def dump(self):
|
||||
""" dump config file """
|
||||
return yaml.dump(self.content, default_flow_style=False, indent=2)
|
||||
|
||||
def save(self):
|
||||
""" save config file to path """
|
||||
with open(self.cfgpath, 'w') as f:
|
||||
ret = yaml.dump(self.content, f,
|
||||
default_flow_style=False, indent=2)
|
||||
return ret
|
||||
178
dotdrop/dotdrop.py
Executable file
178
dotdrop/dotdrop.py
Executable file
@@ -0,0 +1,178 @@
|
||||
"""
|
||||
author: deadc0de6 (https://github.com/deadc0de6)
|
||||
entry point
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import utils
|
||||
from docopt import docopt
|
||||
from logger import Logger
|
||||
from templategen import Templategen
|
||||
from installer import Installer
|
||||
from dotfile import Dotfile
|
||||
from config import Cfg
|
||||
|
||||
VERSION = '0.1'
|
||||
DEF_DOTFILES = 'dotfiles'
|
||||
CUR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
LOG = Logger()
|
||||
HOSTNAME = os.uname()[1]
|
||||
|
||||
BANNER = """ _ _ _
|
||||
__| | ___ | |_ __| |_ __ ___ _ __
|
||||
/ _` |/ _ \| __/ _` | '__/ _ \| '_ |
|
||||
\__,_|\___/ \__\__,_|_| \___/| .__/ v%s
|
||||
|_|""" % (VERSION)
|
||||
|
||||
USAGE = """
|
||||
%s
|
||||
|
||||
Usage:
|
||||
dotdrop.py install [--profile=<profile>] [--cfg=<path>]
|
||||
[(-f | --force)] [--nodiff] [--dry]
|
||||
dotdrop.py compare [--profile=<profile>] [--cfg=<path>]
|
||||
dotdrop.py list [--cfg=<path>]
|
||||
dotdrop.py import [--cfg=<path>] [--profile=<profile>] [--dry] <paths>...
|
||||
dotdrop.py (-h | --help)
|
||||
dotdrop.py (-v | --version)
|
||||
|
||||
Options:
|
||||
--profile=<profiles> Specify the profile to use [default: %s].
|
||||
--cfg=<path> Path to the config [default: %s/config.yaml].
|
||||
--dry Dry run.
|
||||
--nodiff Do not diff when installing [default: False].
|
||||
-f --force Do not warn if exists [default: False].
|
||||
-v --version Show version.
|
||||
-h --help Show this screen.
|
||||
|
||||
""" % (BANNER, HOSTNAME, CUR)
|
||||
|
||||
###########################################################
|
||||
# entry point
|
||||
###########################################################
|
||||
|
||||
|
||||
def install(opts, conf):
|
||||
dotfiles = conf.get_dotfiles(opts['profile'])
|
||||
if dotfiles == []:
|
||||
LOG.err('no dotfiles defined for this profile (\"%s\")' %
|
||||
(str(opts['profile'])))
|
||||
return False
|
||||
t = Templategen(base=opts['dotpath'])
|
||||
inst = Installer(create=opts['create'], backup=opts['backup'],
|
||||
dry=opts['dry'], safe=opts['safe'], base=opts['dotpath'],
|
||||
diff=opts['installdiff'])
|
||||
installed = []
|
||||
for dotfile in dotfiles:
|
||||
r = inst.install(t, opts['profile'], dotfile.src, dotfile.dst)
|
||||
installed.extend(r)
|
||||
LOG.log('\n%u dotfile(s) installed.' % (len(installed)))
|
||||
return True
|
||||
|
||||
|
||||
def compare(opts, conf, tmp):
|
||||
dotfiles = conf.get_dotfiles(opts['profile'])
|
||||
if dotfiles == []:
|
||||
LOG.err('no dotfiles defined for this profile (\"%s\")' %
|
||||
(str(opts['profile'])))
|
||||
return False
|
||||
t = Templategen(base=opts['dotpath'])
|
||||
inst = Installer(create=opts['create'], backup=opts['backup'],
|
||||
dry=opts['dry'], base=opts['dotpath'], quiet=True)
|
||||
for dotfile in dotfiles:
|
||||
LOG.log('diffing \"%s\" VS \"%s\"' % (dotfile.key, dotfile.dst))
|
||||
inst.compare(t, tmp, opts['profile'], dotfile.src, dotfile.dst)
|
||||
return len(dotfiles) > 0
|
||||
|
||||
|
||||
def importer(opts, conf, paths):
|
||||
home = os.path.expanduser('~')
|
||||
cnt = 0
|
||||
for path in paths:
|
||||
dst = path.rstrip(os.sep)
|
||||
key = dst.split(os.sep)[-1]
|
||||
if key == 'config':
|
||||
key = '_'.join(dst.split(os.sep)[-2:])
|
||||
key = key.lstrip('.')
|
||||
key = key.lower()
|
||||
if os.path.isdir(dst):
|
||||
key = 'd_%s' % (key)
|
||||
else:
|
||||
key = 'f_%s' % (key)
|
||||
src = dst.lstrip(home).lstrip('.')
|
||||
dotfile = Dotfile(key, dst, src)
|
||||
srcf = os.path.join(CUR, opts['dotpath'], src)
|
||||
if os.path.exists(srcf):
|
||||
LOG.err('\"%s\" already exists !' % (srcf))
|
||||
continue
|
||||
conf.new(dotfile, opts['profile'])
|
||||
cmd = ['mkdir', '-p', '%s' % (os.path.dirname(srcf))]
|
||||
if opts['dry']:
|
||||
LOG.dry('would run: %s' % (' '.join(cmd)))
|
||||
else:
|
||||
utils.run(cmd, raw=False, log=False)
|
||||
cmd = ['cp', '-r', '%s' % (dst), '%s' % (srcf)]
|
||||
if opts['dry']:
|
||||
LOG.dry('would run: %s' % (' '.join(cmd)))
|
||||
else:
|
||||
utils.run(cmd, raw=False, log=False)
|
||||
LOG.sub('\"%s\" imported' % (path))
|
||||
cnt += 1
|
||||
if opts['dry']:
|
||||
LOG.dry('new config file would be:')
|
||||
LOG.raw(conf.dump())
|
||||
else:
|
||||
conf.save()
|
||||
LOG.log('\n%u file(s) imported.' % (cnt))
|
||||
|
||||
|
||||
def list_profiles(conf):
|
||||
LOG.log('Available profile(s):')
|
||||
for p in conf.get_profiles():
|
||||
LOG.sub(p)
|
||||
LOG.log('')
|
||||
|
||||
|
||||
def header():
|
||||
LOG.log(BANNER)
|
||||
LOG.log("")
|
||||
|
||||
if __name__ == '__main__':
|
||||
ret = True
|
||||
args = docopt(USAGE, version=VERSION)
|
||||
conf = Cfg(args['--cfg'], DEF_DOTFILES)
|
||||
|
||||
opts = conf.get_configs()
|
||||
opts['dry'] = args['--dry']
|
||||
opts['profile'] = args['--profile']
|
||||
opts['safe'] = not args['--force']
|
||||
opts['installdiff'] = not args['--nodiff']
|
||||
|
||||
header()
|
||||
|
||||
try:
|
||||
|
||||
if args['list']:
|
||||
list_profiles(conf)
|
||||
|
||||
elif args['install']:
|
||||
ret = install(opts, conf)
|
||||
|
||||
elif args['compare']:
|
||||
tmp = utils.get_tmpdir()
|
||||
if compare(opts, conf, tmp):
|
||||
LOG.log('generated temporary files available under %s' % (tmp))
|
||||
else:
|
||||
os.rmdir(tmp)
|
||||
|
||||
elif args['import']:
|
||||
importer(opts, conf, args['<paths>'])
|
||||
|
||||
except KeyboardInterrupt:
|
||||
LOG.err('interrupted')
|
||||
|
||||
if ret:
|
||||
sys.exit(0)
|
||||
sys.exit(1)
|
||||
17
dotdrop/dotfile.py
Normal file
17
dotdrop/dotfile.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""
|
||||
author: deadc0de6 (https://github.com/deadc0de6)
|
||||
represents a dotfile in dotdrop
|
||||
"""
|
||||
|
||||
|
||||
class Dotfile:
|
||||
|
||||
def __init__(self, key, dst, src):
|
||||
self.key = key
|
||||
self.dst = dst
|
||||
self.src = src
|
||||
|
||||
def __str__(self):
|
||||
string = 'key:%s, src: %s, dst: %s' % (self.key,
|
||||
self.src, self.dst)
|
||||
return string
|
||||
139
dotdrop/installer.py
Normal file
139
dotdrop/installer.py
Normal file
@@ -0,0 +1,139 @@
|
||||
"""
|
||||
author: deadc0de6 (https://github.com/deadc0de6)
|
||||
handle the installation of dotfiles
|
||||
"""
|
||||
|
||||
import os
|
||||
import utils
|
||||
from logger import Logger
|
||||
|
||||
|
||||
class Installer:
|
||||
|
||||
BACKUP_SUFFIX = '.dotdropbak'
|
||||
|
||||
def __init__(self, base='.', create=True, backup=True,
|
||||
dry=False, safe=False, quiet=False, diff=True):
|
||||
self.create = create
|
||||
self.backup = backup
|
||||
self.dry = dry
|
||||
self.safe = safe
|
||||
self.base = base
|
||||
self.quiet = quiet
|
||||
self.diff = diff
|
||||
self.log = Logger()
|
||||
|
||||
def install(self, templater, profile, src, dst):
|
||||
src = os.path.join(self.base, os.path.expanduser(src))
|
||||
dst = os.path.join(self.base, os.path.expanduser(dst))
|
||||
if os.path.isdir(src):
|
||||
return self._handle_dir(templater, profile, src, dst)
|
||||
return self._handle_file(templater, profile, src, dst)
|
||||
|
||||
def _preparesub(self):
|
||||
if not os.path.exists(self.sub):
|
||||
os.makedirs(self.sub)
|
||||
|
||||
def _handle_file(self, templater, profile, src, dst):
|
||||
content = templater.generate(src, profile)
|
||||
if content is None:
|
||||
self.log.err('generate from template \"%s\"' % (src))
|
||||
return []
|
||||
st = os.stat(src)
|
||||
ret = self._write(dst, content, st.st_mode)
|
||||
if ret < 0:
|
||||
self.log.err('installing %s to %s' % (src, dst))
|
||||
return []
|
||||
if ret > 0:
|
||||
self.log.sub('ignoring \"%s\", same content' % (dst))
|
||||
return []
|
||||
if ret == 0:
|
||||
if not self.quiet:
|
||||
self.log.sub('copied %s to %s' % (src, dst))
|
||||
return [(src, dst)]
|
||||
return []
|
||||
|
||||
def _handle_dir(self, templater, profile, src, dst):
|
||||
ret = []
|
||||
for entry in os.listdir(src):
|
||||
f = os.path.join(src, entry)
|
||||
if not os.path.isdir(f):
|
||||
res = self._handle_file(
|
||||
templater, profile, f, os.path.join(dst, entry))
|
||||
ret.extend(res)
|
||||
else:
|
||||
res = self._handle_dir(
|
||||
templater, profile, f, os.path.join(dst, entry))
|
||||
ret.extend(res)
|
||||
return ret
|
||||
|
||||
def _fake_diff(self, dst, content):
|
||||
cur = ''
|
||||
with open(dst, 'br') as f:
|
||||
cur = f.read()
|
||||
return cur == content
|
||||
|
||||
def _write(self, dst, content, rights):
|
||||
""" write file """
|
||||
if self.dry:
|
||||
self.log.dry('would install %s' % (dst))
|
||||
return 0
|
||||
if os.path.exists(dst) and self.safe:
|
||||
if self.diff and self._fake_diff(dst, content):
|
||||
return 1
|
||||
if not self.log.ask('Overwrite \"%s\"' % (dst)):
|
||||
self.log.warn('ignoring \"%s\", already present' % (dst))
|
||||
return 1
|
||||
if self.backup and os.path.exists(dst):
|
||||
self._backup(dst)
|
||||
base = os.path.dirname(dst)
|
||||
if not self._create_dirs(base):
|
||||
self.log.err('creating directory for %s' % (dst))
|
||||
return -1
|
||||
with open(dst, 'wb') as f:
|
||||
f.write(content)
|
||||
os.chmod(dst, rights)
|
||||
return 0
|
||||
|
||||
def _create_dirs(self, folder):
|
||||
if not self.create and not os.path.exists(folder):
|
||||
return False
|
||||
if os.path.exists(folder):
|
||||
return True
|
||||
os.makedirs(folder)
|
||||
return os.path.exists(folder)
|
||||
|
||||
def _backup(self, path):
|
||||
if self.dry:
|
||||
return
|
||||
dst = path.rstrip(os.sep) + self.BACKUP_SUFFIX
|
||||
self.log.log('backup %s to %s' % (path, dst))
|
||||
os.rename(path, dst)
|
||||
|
||||
def _install_to_temp(self, templater, profile, src, dst, tmpfolder):
|
||||
sub = dst
|
||||
if dst[0] == os.sep:
|
||||
sub = dst[1:]
|
||||
tmpdst = os.path.join(tmpfolder, sub)
|
||||
return self.install(templater, profile, src, tmpdst), tmpdst
|
||||
|
||||
def compare(self, templater, tmpfolder, profile, src, dst):
|
||||
drysaved = self.dry
|
||||
self.dry = False
|
||||
diffsaved = self.diff
|
||||
self.diff = False
|
||||
src = os.path.expanduser(src)
|
||||
dst = os.path.expanduser(dst)
|
||||
if not os.path.exists(dst):
|
||||
self.log.warn('\"%s\" does not exist on local' % (dst))
|
||||
else:
|
||||
ret, tmpdst = self._install_to_temp(
|
||||
templater, profile, src, dst, tmpfolder)
|
||||
if ret:
|
||||
diff = utils.diff(tmpdst, dst, log=False, raw=False)
|
||||
if diff == '':
|
||||
self.log.raw('same file')
|
||||
else:
|
||||
self.log.emph(diff)
|
||||
self.dry = drysaved
|
||||
self.diff = diffsaved
|
||||
65
dotdrop/logger.py
Normal file
65
dotdrop/logger.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""
|
||||
author: deadc0de6 (https://github.com/deadc0de6)
|
||||
handle logging to stdout/stderr
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
class Logger:
|
||||
|
||||
RED = '\033[91m'
|
||||
GREEN = '\033[92m'
|
||||
YELLOW = '\033[93m'
|
||||
BLUE = '\033[94m'
|
||||
MAGENTA = '\033[95m'
|
||||
RESET = '\033[0m'
|
||||
EMPH = '\033[33m'
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def log(self, string, end='\n', pre=''):
|
||||
cs = self._color(self.BLUE)
|
||||
ce = self._color(self.RESET)
|
||||
sys.stdout.write('%s%s%s%s%s' % (pre, cs, string, end, ce))
|
||||
|
||||
def sub(self, string):
|
||||
cs = self._color(self.BLUE)
|
||||
ce = self._color(self.RESET)
|
||||
sys.stdout.write('\t%s->%s %s\n' % (cs, ce, string))
|
||||
|
||||
def emph(self, string):
|
||||
cs = self._color(self.EMPH)
|
||||
ce = self._color(self.RESET)
|
||||
sys.stderr.write('%s%s%s' % (cs, string, ce))
|
||||
|
||||
def err(self, string, end='\n'):
|
||||
cs = self._color(self.RED)
|
||||
ce = self._color(self.RESET)
|
||||
sys.stderr.write('%s[ERR]%s %s%s' % (cs, string, end, ce))
|
||||
|
||||
def warn(self, string, end='\n'):
|
||||
cs = self._color(self.YELLOW)
|
||||
ce = self._color(self.RESET)
|
||||
sys.stderr.write('%s[WARN]%s %s%s' % (cs, string, end, ce))
|
||||
|
||||
def dry(self, string, end='\n'):
|
||||
cs = self._color(self.GREEN)
|
||||
ce = self._color(self.RESET)
|
||||
sys.stdout.write('%s[DRY]%s %s%s' % (cs, string, end, ce))
|
||||
|
||||
def raw(self, string, end='\n'):
|
||||
sys.stdout.write('%s%s' % (string, end))
|
||||
|
||||
def ask(self, query):
|
||||
cs = self._color(self.BLUE)
|
||||
ce = self._color(self.RESET)
|
||||
q = '%s%s%s' % (cs, query + ' [y/N] ? ', ce)
|
||||
r = input(q)
|
||||
return r == 'y'
|
||||
|
||||
def _color(self, col):
|
||||
if not sys.stdout.isatty():
|
||||
return ''
|
||||
return col
|
||||
56
dotdrop/templategen.py
Normal file
56
dotdrop/templategen.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""
|
||||
author: deadc0de6 (https://github.com/deadc0de6)
|
||||
jinja2 template generator
|
||||
"""
|
||||
|
||||
import os
|
||||
import utils
|
||||
from jinja2 import Environment, Template, FileSystemLoader
|
||||
|
||||
BLOCK_START = '{%@@'
|
||||
BLOCK_END = '@@%}'
|
||||
VAR_START = '{{@@'
|
||||
VAR_END = '@@}}'
|
||||
COMMENT_START = '{#@@'
|
||||
COMMENT_END = '@@#}'
|
||||
|
||||
|
||||
class Templategen:
|
||||
|
||||
def __init__(self, base='.'):
|
||||
self.base = base
|
||||
loader = FileSystemLoader(self.base)
|
||||
self.env = Environment(loader=loader,
|
||||
trim_blocks=True, lstrip_blocks=True,
|
||||
keep_trailing_newline=True,
|
||||
block_start_string=BLOCK_START,
|
||||
block_end_string=BLOCK_END,
|
||||
variable_start_string=VAR_START,
|
||||
variable_end_string=VAR_END,
|
||||
comment_start_string=COMMENT_START,
|
||||
comment_end_string=COMMENT_END)
|
||||
|
||||
def generate(self, src, profile):
|
||||
return self._handle_file(src, profile)
|
||||
|
||||
def _handle_file(self, src, profile):
|
||||
""" generate the file content from template """
|
||||
filetype = utils.run(['file', '-b', src], raw=False)
|
||||
istext = 'text' in filetype
|
||||
if not istext:
|
||||
return self._handle_bin_file(src, profile)
|
||||
return self._handle_text_file(src, profile)
|
||||
|
||||
def _handle_text_file(self, src, profile):
|
||||
l = len(self.base) + 1
|
||||
template = self.env.get_template(src[l:])
|
||||
content = template.render(profile=profile)
|
||||
content = content.encode('UTF-8')
|
||||
return content
|
||||
|
||||
def _handle_bin_file(self, src, profile):
|
||||
# this is dirty
|
||||
if not src.startswith(self.base):
|
||||
src = os.path.join(self.base, src)
|
||||
with open(src, 'rb') as f:
|
||||
return f.read()
|
||||
29
dotdrop/utils.py
Normal file
29
dotdrop/utils.py
Normal file
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
author: deadc0de6 (https://github.com/deadc0de6)
|
||||
utilities
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import tempfile
|
||||
from logger import Logger
|
||||
|
||||
LOG = Logger()
|
||||
|
||||
|
||||
def run(cmd, log=False, raw=True):
|
||||
""" expects a list """
|
||||
if log:
|
||||
LOG.log('cmd: \"%s\"' % (' '.join(cmd)))
|
||||
p = subprocess.Popen(cmd, shell=False,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if raw:
|
||||
return p.stdout.readlines()
|
||||
return ''.join([x.decode("utf-8") for x in p.stdout.readlines()])
|
||||
|
||||
|
||||
def diff(src, dst, log=False, raw=True):
|
||||
return run(['diff', '-r', src, dst], log=log, raw=raw)
|
||||
|
||||
|
||||
def get_tmpdir():
|
||||
return tempfile.mkdtemp(prefix='dotdrop-')
|
||||
Reference in New Issue
Block a user