mirror of
https://github.com/deadc0de6/dotdrop.git
synced 2026-02-10 19:39:15 +00:00
Merge upstream
This commit is contained in:
50
README.md
50
README.md
@@ -212,7 +212,7 @@ that don't need to appear in the output of compare.
|
|||||||
|
|
||||||
Either use the command line switch `-i --ignore` or add an entry in the dotfile
|
Either use the command line switch `-i --ignore` or add an entry in the dotfile
|
||||||
directly in the `cmpignore` entry (see [Config](#config)).
|
directly in the `cmpignore` entry (see [Config](#config)).
|
||||||
The pattern follows Unix shell-style wildcards like for example `*/path/file`.
|
The ignore pattern must follow Unix shell-style wildcards like for example `*/path/file`.
|
||||||
Make sure to quote those when using wildcards in the config file.
|
Make sure to quote those when using wildcards in the config file.
|
||||||
|
|
||||||
It is also possible to install all dotfiles for a specific profile
|
It is also possible to install all dotfiles for a specific profile
|
||||||
@@ -474,6 +474,21 @@ $ dotdrop update ~/.vimrc
|
|||||||
$ dotdrop update --key f_vimrc
|
$ dotdrop update --key f_vimrc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
It is possible to ignore files to update using unix pattern by providing those
|
||||||
|
either through the switch `-i --ignore` or as part of the dotfile under the
|
||||||
|
key `upignore` (see [Config](#config)).
|
||||||
|
The ignore pattern must follow Unix shell-style wildcards like for example `*/path/file`.
|
||||||
|
Make sure to quote those when using wildcards in the config file.
|
||||||
|
```yaml
|
||||||
|
dotfiles:
|
||||||
|
d_vim
|
||||||
|
dst: ~/.vim
|
||||||
|
src: vim
|
||||||
|
upignore:
|
||||||
|
- "*/undo-dir"
|
||||||
|
- "*/plugged"
|
||||||
|
```
|
||||||
|
|
||||||
There are two cases when updating a dotfile:
|
There are two cases when updating a dotfile:
|
||||||
|
|
||||||
**The dotfile doesn't use [templating](#template)**
|
**The dotfile doesn't use [templating](#template)**
|
||||||
@@ -592,6 +607,7 @@ the following entries:
|
|||||||
* `src`: dotfile path within the `dotpath` (can use `variables` and `dynvariables`, make sure to quote).
|
* `src`: dotfile path within the `dotpath` (can use `variables` and `dynvariables`, make sure to quote).
|
||||||
* `link`: if true dotdrop will create a symlink instead of copying (default *false*).
|
* `link`: if true dotdrop will create a symlink instead of copying (default *false*).
|
||||||
* `cmpignore`: list of pattern to ignore when comparing (enclose in quotes when using wildcards).
|
* `cmpignore`: list of pattern to ignore when comparing (enclose in quotes when using wildcards).
|
||||||
|
* `upignore`: list of pattern to ignore when updating (enclose in quotes when using wildcards).
|
||||||
* `actions`: list of action keys that need to be defined in the **actions** entry below.
|
* `actions`: list of action keys that need to be defined in the **actions** entry below.
|
||||||
* `trans`: transformation key to apply when installing this dotfile (must be defined in the **trans** entry below).
|
* `trans`: transformation key to apply when installing this dotfile (must be defined in the **trans** entry below).
|
||||||
* `trans_write`: transformation key to apply when updating this dotfile (must be defined in the **trans_write** entry below).
|
* `trans_write`: transformation key to apply when updating this dotfile (must be defined in the **trans_write** entry below).
|
||||||
@@ -606,6 +622,8 @@ the following entries:
|
|||||||
ignoreempty: <true|false>
|
ignoreempty: <true|false>
|
||||||
cmpignore:
|
cmpignore:
|
||||||
- "<ignore-pattern>"
|
- "<ignore-pattern>"
|
||||||
|
upignore:
|
||||||
|
- "<ignore-pattern>"
|
||||||
actions:
|
actions:
|
||||||
- <action-key>
|
- <action-key>
|
||||||
trans: <transformation-key>
|
trans: <transformation-key>
|
||||||
@@ -616,6 +634,8 @@ the following entries:
|
|||||||
need to be managed
|
need to be managed
|
||||||
* `dotfiles`: the dotfiles associated to this profile
|
* `dotfiles`: the dotfiles associated to this profile
|
||||||
* `include`: include all dotfiles from another profile (optional)
|
* `include`: include all dotfiles from another profile (optional)
|
||||||
|
* `variables`: profile specific variables (see [Variables](#variables))
|
||||||
|
* `dynvariables`: profile specific interpreted variables (see [Interpreted variables](#interpreted-variables))
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
<some-name-usually-the-hostname>:
|
<some-name-usually-the-hostname>:
|
||||||
@@ -627,6 +647,10 @@ the following entries:
|
|||||||
include:
|
include:
|
||||||
- <some-other-profile>
|
- <some-other-profile>
|
||||||
- ...
|
- ...
|
||||||
|
variables:
|
||||||
|
<name>: <value>
|
||||||
|
dynvariables:
|
||||||
|
<name>: <value>
|
||||||
```
|
```
|
||||||
|
|
||||||
* **actions** entry (optional): a list of action (see [Use actions](#use-actions))
|
* **actions** entry (optional): a list of action (see [Use actions](#use-actions))
|
||||||
@@ -743,6 +767,7 @@ For example in the config file:
|
|||||||
```yaml
|
```yaml
|
||||||
variables:
|
variables:
|
||||||
var1: some variable content
|
var1: some variable content
|
||||||
|
var2: some other content
|
||||||
```
|
```
|
||||||
|
|
||||||
These can then be used in any template with
|
These can then be used in any template with
|
||||||
@@ -750,6 +775,26 @@ These can then be used in any template with
|
|||||||
{{@@ var1 @@}}
|
{{@@ var1 @@}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Profile variables will take precedence over globally defined variables what
|
||||||
|
means that you could do something like this:
|
||||||
|
```yaml
|
||||||
|
variables:
|
||||||
|
git_email: home@email.com
|
||||||
|
dotfiles:
|
||||||
|
f_gitconfig:
|
||||||
|
dst: ~/.gitconfig
|
||||||
|
src: gitconfig
|
||||||
|
profiles:
|
||||||
|
work:
|
||||||
|
dotfiles:
|
||||||
|
- f_gitconfig
|
||||||
|
variables:
|
||||||
|
git_email: work@email.com
|
||||||
|
private:
|
||||||
|
dotfiles:
|
||||||
|
- f_gitconfig
|
||||||
|
```
|
||||||
|
|
||||||
## Interpreted variables
|
## Interpreted variables
|
||||||
|
|
||||||
It is also possible to have *dynamic* variables in the sense that their
|
It is also possible to have *dynamic* variables in the sense that their
|
||||||
@@ -768,6 +813,9 @@ These can be used as any variables in the templates
|
|||||||
{{@@ dvar1 @@}}
|
{{@@ dvar1 @@}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
As for variables (see [Variables](#variables)) profile dynvariables will take
|
||||||
|
precedence over globally defined dynvariables.
|
||||||
|
|
||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
It's possible to access environment variables inside the templates.
|
It's possible to access environment variables inside the templates.
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ handle the comparison of dotfiles and local deployment
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import filecmp
|
import filecmp
|
||||||
import fnmatch
|
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
from dotdrop.logger import Logger
|
from dotdrop.logger import Logger
|
||||||
@@ -34,7 +33,7 @@ class Comparator:
|
|||||||
|
|
||||||
def _comp_file(self, left, right, ignore):
|
def _comp_file(self, left, right, ignore):
|
||||||
"""compare a file"""
|
"""compare a file"""
|
||||||
if self._ignore([left, right], ignore):
|
if utils.must_ignore([left, right], ignore, debug=self.debug):
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('ignoring diff {} and {}'.format(left, right))
|
self.log.dbg('ignoring diff {} and {}'.format(left, right))
|
||||||
return ''
|
return ''
|
||||||
@@ -44,7 +43,7 @@ class Comparator:
|
|||||||
"""compare a directory"""
|
"""compare a directory"""
|
||||||
if not os.path.exists(right):
|
if not os.path.exists(right):
|
||||||
return ''
|
return ''
|
||||||
if self._ignore([left, right], ignore):
|
if utils.must_ignore([left, right], ignore, debug=self.debug):
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('ignoring diff {} and {}'.format(left, right))
|
self.log.dbg('ignoring diff {} and {}'.format(left, right))
|
||||||
return ''
|
return ''
|
||||||
@@ -54,13 +53,15 @@ class Comparator:
|
|||||||
comp = filecmp.dircmp(left, right)
|
comp = filecmp.dircmp(left, right)
|
||||||
# handle files only in deployed file
|
# handle files only in deployed file
|
||||||
for i in comp.left_only:
|
for i in comp.left_only:
|
||||||
if self._ignore([os.path.join(left, i)], ignore):
|
if utils.must_ignore([os.path.join(left, i)],
|
||||||
|
ignore, debug=self.debug):
|
||||||
continue
|
continue
|
||||||
ret.append('only in left: \"{}\"\n'.format(i))
|
ret.append('=> \"{}\" does not exist on local\n'.format(i))
|
||||||
for i in comp.right_only:
|
for i in comp.right_only:
|
||||||
if self._ignore([os.path.join(right, i)], ignore):
|
if utils.must_ignore([os.path.join(right, i)],
|
||||||
|
ignore, debug=self.debug):
|
||||||
continue
|
continue
|
||||||
ret.append('only in right: \"{}\"\n'.format(i))
|
ret.append('=> \"{}\" does not exist in dotdrop\n'.format(i))
|
||||||
|
|
||||||
# same left and right but different type
|
# same left and right but different type
|
||||||
funny = comp.common_funny
|
funny = comp.common_funny
|
||||||
@@ -90,17 +91,5 @@ class Comparator:
|
|||||||
if header:
|
if header:
|
||||||
lshort = os.path.basename(left)
|
lshort = os.path.basename(left)
|
||||||
rshort = os.path.basename(right)
|
rshort = os.path.basename(right)
|
||||||
diff = 'diff \"{}\":\n{}'.format(lshort, diff)
|
diff = '=> diff \"{}\":\n{}'.format(lshort, diff)
|
||||||
return diff
|
return diff
|
||||||
|
|
||||||
def _ignore(self, paths, ignore):
|
|
||||||
'''return True if any paths is ignored - not very efficient'''
|
|
||||||
if not ignore:
|
|
||||||
return False
|
|
||||||
for p in paths:
|
|
||||||
for i in ignore:
|
|
||||||
if fnmatch.fnmatch(p, i):
|
|
||||||
if self.debug:
|
|
||||||
self.log.dbg('ignore match {}'.format(p))
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class Cfg:
|
|||||||
|
|
||||||
# template variables
|
# template variables
|
||||||
key_variables = 'variables'
|
key_variables = 'variables'
|
||||||
# shell variable
|
# shell variables
|
||||||
key_dynvariables = 'dynvariables'
|
key_dynvariables = 'dynvariables'
|
||||||
|
|
||||||
# dotfiles keys
|
# dotfiles keys
|
||||||
@@ -58,6 +58,7 @@ class Cfg:
|
|||||||
key_dotfiles_actions = 'actions'
|
key_dotfiles_actions = 'actions'
|
||||||
key_dotfiles_trans_r = 'trans'
|
key_dotfiles_trans_r = 'trans'
|
||||||
key_dotfiles_trans_w = 'trans_write'
|
key_dotfiles_trans_w = 'trans_write'
|
||||||
|
key_dotfiles_upignore = 'upignore'
|
||||||
|
|
||||||
# profiles keys
|
# profiles keys
|
||||||
key_profiles = 'profiles'
|
key_profiles = 'profiles'
|
||||||
@@ -112,13 +113,14 @@ class Cfg:
|
|||||||
# represents all dotfiles per profile by profile key
|
# represents all dotfiles per profile by profile key
|
||||||
# NOT linked inside the yaml dict (self.content)
|
# NOT linked inside the yaml dict (self.content)
|
||||||
self.prodots = {}
|
self.prodots = {}
|
||||||
|
|
||||||
if not self._load_file():
|
if not self._load_file():
|
||||||
raise ValueError('config is not valid')
|
raise ValueError('config is not valid')
|
||||||
|
|
||||||
def eval_dotfiles(self, profile, debug=False):
|
def eval_dotfiles(self, profile, debug=False):
|
||||||
"""resolve dotfiles src/dst templates"""
|
"""resolve dotfiles src/dst templating"""
|
||||||
t = Templategen(profile=profile,
|
t = Templategen(profile=profile,
|
||||||
variables=self.get_variables(),
|
variables=self.get_variables(profile),
|
||||||
debug=debug)
|
debug=debug)
|
||||||
for d in self.get_dotfiles(profile):
|
for d in self.get_dotfiles(profile):
|
||||||
d.src = t.generate_string(d.src)
|
d.src = t.generate_string(d.src)
|
||||||
@@ -275,15 +277,20 @@ class Cfg:
|
|||||||
trans_r = None
|
trans_r = None
|
||||||
trans_w = None
|
trans_w = None
|
||||||
|
|
||||||
# parse ignore pattern
|
# parse cmpignore pattern
|
||||||
ignores = v[self.key_dotfiles_cmpignore] if \
|
cmpignores = v[self.key_dotfiles_cmpignore] if \
|
||||||
self.key_dotfiles_cmpignore in v else []
|
self.key_dotfiles_cmpignore in v else []
|
||||||
|
|
||||||
|
# parse upignore pattern
|
||||||
|
upignores = v[self.key_dotfiles_upignore] if \
|
||||||
|
self.key_dotfiles_upignore in v else []
|
||||||
|
|
||||||
# create new dotfile
|
# create new dotfile
|
||||||
self.dotfiles[k] = Dotfile(k, dst, src,
|
self.dotfiles[k] = Dotfile(k, dst, src,
|
||||||
link=link, actions=actions,
|
link=link, actions=actions,
|
||||||
trans_r=trans_r, trans_w=trans_w,
|
trans_r=trans_r, trans_w=trans_w,
|
||||||
cmpignore=ignores, noempty=noempty)
|
cmpignore=cmpignores, noempty=noempty,
|
||||||
|
upignore=upignores)
|
||||||
|
|
||||||
# assign dotfiles to each profile
|
# assign dotfiles to each profile
|
||||||
for k, v in self.lnk_profiles.items():
|
for k, v in self.lnk_profiles.items():
|
||||||
@@ -604,15 +611,35 @@ class Cfg:
|
|||||||
"""return all defined settings"""
|
"""return all defined settings"""
|
||||||
return self.lnk_settings.copy()
|
return self.lnk_settings.copy()
|
||||||
|
|
||||||
def get_variables(self):
|
def get_variables(self, profile):
|
||||||
|
"""return the variables for this profile"""
|
||||||
variables = {}
|
variables = {}
|
||||||
|
|
||||||
|
# global variables
|
||||||
if self.key_variables in self.content:
|
if self.key_variables in self.content:
|
||||||
variables.update(self.content[self.key_variables])
|
variables.update(self.content[self.key_variables])
|
||||||
|
|
||||||
|
# global dynvariables
|
||||||
if self.key_dynvariables in self.content:
|
if self.key_dynvariables in self.content:
|
||||||
# interpret dynamic variables
|
# interpret dynamic variables
|
||||||
dynvars = self.content[self.key_dynvariables]
|
dynvars = self.content[self.key_dynvariables]
|
||||||
for key, cmd in dynvars.items():
|
for k, v in dynvars.items():
|
||||||
variables[key] = shell(cmd)
|
variables[k] = shell(v)
|
||||||
|
|
||||||
|
if profile not in self.lnk_profiles:
|
||||||
|
return variables
|
||||||
|
|
||||||
|
# profile variables
|
||||||
|
var = self.lnk_profiles[profile]
|
||||||
|
if self.key_variables in var.keys():
|
||||||
|
for k, v in var[self.key_variables].items():
|
||||||
|
variables[k] = v
|
||||||
|
|
||||||
|
# profile dynvariables
|
||||||
|
if self.key_dynvariables in var.keys():
|
||||||
|
for k, v in var[self.key_dynvariables].items():
|
||||||
|
variables[k] = shell(v)
|
||||||
|
|
||||||
return variables
|
return variables
|
||||||
|
|
||||||
def dump(self):
|
def dump(self):
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ Usage:
|
|||||||
dotdrop import [-ldVb] [-c <path>] [-p <profile>] <path>...
|
dotdrop import [-ldVb] [-c <path>] [-p <profile>] <path>...
|
||||||
dotdrop compare [-Vb] [-c <path>] [-p <profile>]
|
dotdrop compare [-Vb] [-c <path>] [-p <profile>]
|
||||||
[-o <opts>] [-C <file>...] [-i <pattern>...]
|
[-o <opts>] [-C <file>...] [-i <pattern>...]
|
||||||
dotdrop update [-fdVbk] [-c <path>] [-p <profile>] [<path>...]
|
dotdrop update [-fdVbk] [-c <path>] [-p <profile>]
|
||||||
|
[-i <pattern>...] [<path>...]
|
||||||
dotdrop listfiles [-VTb] [-c <path>] [-p <profile>]
|
dotdrop listfiles [-VTb] [-c <path>] [-p <profile>]
|
||||||
dotdrop detail [-Vb] [-c <path>] [-p <profile>] [<key>...]
|
dotdrop detail [-Vb] [-c <path>] [-p <profile>] [<key>...]
|
||||||
dotdrop list [-Vb] [-c <path>]
|
dotdrop list [-Vb] [-c <path>]
|
||||||
@@ -55,13 +56,13 @@ Options:
|
|||||||
-p --profile=<profile> Specify the profile to use [default: {}].
|
-p --profile=<profile> Specify the profile to use [default: {}].
|
||||||
-c --cfg=<path> Path to the config [default: config.yaml].
|
-c --cfg=<path> Path to the config [default: config.yaml].
|
||||||
-C --file=<path> Path of dotfile to compare.
|
-C --file=<path> Path of dotfile to compare.
|
||||||
-i --ignore=<pattern> Pattern to ignore when diffing.
|
-i --ignore=<pattern> Pattern to ignore.
|
||||||
-o --dopts=<opts> Diff options [default: ].
|
-o --dopts=<opts> Diff options [default: ].
|
||||||
-n --nodiff Do not diff when installing.
|
-n --nodiff Do not diff when installing.
|
||||||
-t --temp Install to a temporary directory for review.
|
-t --temp Install to a temporary directory for review.
|
||||||
-T --template Only template dotfiles.
|
-T --template Only template dotfiles.
|
||||||
-D --showdiff Show a diff before overwriting.
|
-D --showdiff Show a diff before overwriting.
|
||||||
-l --link Import and link.
|
-l --inv-link Invert the value of "link_by_default" when importing.
|
||||||
-f --force Do not warn if exists.
|
-f --force Do not warn if exists.
|
||||||
-k --key Treat <path> as a dotfile key.
|
-k --key Treat <path> as a dotfile key.
|
||||||
-V --verbose Be verbose.
|
-V --verbose Be verbose.
|
||||||
@@ -170,7 +171,10 @@ def cmd_compare(opts, conf, tmp, focus=[], ignore=[]):
|
|||||||
LOG.dbg('comparing {}'.format(dotfile))
|
LOG.dbg('comparing {}'.format(dotfile))
|
||||||
src = dotfile.src
|
src = dotfile.src
|
||||||
if not os.path.lexists(os.path.expanduser(dotfile.dst)):
|
if not os.path.lexists(os.path.expanduser(dotfile.dst)):
|
||||||
LOG.emph('\"{}\" does not exist on local\n'.format(dotfile.dst))
|
line = '=> compare {}: \"{}\" does not exist on local'
|
||||||
|
LOG.log(line.format(dotfile.key, dotfile.dst))
|
||||||
|
same = False
|
||||||
|
continue
|
||||||
|
|
||||||
tmpsrc = None
|
tmpsrc = None
|
||||||
if dotfile.trans_r:
|
if dotfile.trans_r:
|
||||||
@@ -178,12 +182,14 @@ def cmd_compare(opts, conf, tmp, focus=[], ignore=[]):
|
|||||||
tmpsrc = apply_trans(opts, dotfile)
|
tmpsrc = apply_trans(opts, dotfile)
|
||||||
if not tmpsrc:
|
if not tmpsrc:
|
||||||
# could not apply trans
|
# could not apply trans
|
||||||
|
same = False
|
||||||
continue
|
continue
|
||||||
src = tmpsrc
|
src = tmpsrc
|
||||||
# install dotfile to temporary dir
|
# install dotfile to temporary dir
|
||||||
ret, insttmp = inst.install_to_temp(t, tmp, src, dotfile.dst)
|
ret, insttmp = inst.install_to_temp(t, tmp, src, dotfile.dst)
|
||||||
if not ret:
|
if not ret:
|
||||||
# failed to install to tmp
|
# failed to install to tmp
|
||||||
|
same = False
|
||||||
continue
|
continue
|
||||||
ignores = list(set(ignore + dotfile.cmpignore))
|
ignores = list(set(ignore + dotfile.cmpignore))
|
||||||
diff = comp.compare(insttmp, dotfile.dst, ignore=ignores)
|
diff = comp.compare(insttmp, dotfile.dst, ignore=ignores)
|
||||||
@@ -194,23 +200,24 @@ def cmd_compare(opts, conf, tmp, focus=[], ignore=[]):
|
|||||||
remove(tmpsrc)
|
remove(tmpsrc)
|
||||||
if diff == '':
|
if diff == '':
|
||||||
if opts['debug']:
|
if opts['debug']:
|
||||||
LOG.dbg('diffing \"{}\" VS \"{}\"'.format(dotfile.key,
|
line = '=> compare {}: diffing with \"{}\"'
|
||||||
dotfile.dst))
|
LOG.dbg(line.format(dotfile.key, dotfile.dst))
|
||||||
LOG.dbg('same file')
|
LOG.dbg('same file')
|
||||||
else:
|
else:
|
||||||
LOG.log('diffing \"{}\" VS \"{}\"'.format(dotfile.key,
|
line = '=> compare {}: diffing with \"{}\"'
|
||||||
dotfile.dst))
|
LOG.log(line.format(dotfile.key, dotfile.dst))
|
||||||
LOG.emph(diff)
|
LOG.emph(diff)
|
||||||
same = False
|
same = False
|
||||||
|
|
||||||
return same
|
return same
|
||||||
|
|
||||||
|
|
||||||
def cmd_update(opts, conf, paths, iskey=False):
|
def cmd_update(opts, conf, paths, iskey=False, ignore=[]):
|
||||||
"""update the dotfile(s) from path(s) or key(s)"""
|
"""update the dotfile(s) from path(s) or key(s)"""
|
||||||
ret = True
|
ret = True
|
||||||
updater = Updater(conf, opts['dotpath'], opts['dry'],
|
updater = Updater(conf, opts['dotpath'], opts['dry'],
|
||||||
opts['safe'], iskey=iskey, debug=opts['debug'])
|
opts['safe'], iskey=iskey,
|
||||||
|
debug=opts['debug'], ignore=[])
|
||||||
if not iskey:
|
if not iskey:
|
||||||
# update paths
|
# update paths
|
||||||
if opts['debug']:
|
if opts['debug']:
|
||||||
@@ -434,9 +441,9 @@ def main():
|
|||||||
opts['profile'] = args['--profile']
|
opts['profile'] = args['--profile']
|
||||||
opts['safe'] = not args['--force']
|
opts['safe'] = not args['--force']
|
||||||
opts['installdiff'] = not args['--nodiff']
|
opts['installdiff'] = not args['--nodiff']
|
||||||
opts['link'] = args['--link']
|
opts['link'] = args['--inv-link']
|
||||||
opts['debug'] = args['--verbose']
|
opts['debug'] = args['--verbose']
|
||||||
opts['variables'] = conf.get_variables()
|
opts['variables'] = conf.get_variables(opts['profile'])
|
||||||
opts['showdiff'] = opts['showdiff'] or args['--showdiff']
|
opts['showdiff'] = opts['showdiff'] or args['--showdiff']
|
||||||
|
|
||||||
if opts['debug']:
|
if opts['debug']:
|
||||||
@@ -495,7 +502,8 @@ def main():
|
|||||||
if opts['debug']:
|
if opts['debug']:
|
||||||
LOG.dbg('running cmd: update')
|
LOG.dbg('running cmd: update')
|
||||||
iskey = args['--key']
|
iskey = args['--key']
|
||||||
ret = cmd_update(opts, conf, args['<path>'], iskey=iskey)
|
ret = cmd_update(opts, conf, args['<path>'], iskey=iskey,
|
||||||
|
ignore=args['--ignore'])
|
||||||
|
|
||||||
elif args['detail']:
|
elif args['detail']:
|
||||||
# detail files
|
# detail files
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ class Dotfile:
|
|||||||
|
|
||||||
def __init__(self, key, dst, src,
|
def __init__(self, key, dst, src,
|
||||||
actions={}, trans_r=None, trans_w=None,
|
actions={}, trans_r=None, trans_w=None,
|
||||||
link=LinkTypes.NOLINK, cmpignore=[], noempty=False):
|
link=LinkTypes.NOLINK, cmpignore=[], noempty=False,
|
||||||
|
upignore=[]):
|
||||||
# key of dotfile in the config
|
# key of dotfile in the config
|
||||||
self.key = key
|
self.key = key
|
||||||
# path where to install this dotfile
|
# path where to install this dotfile
|
||||||
@@ -31,6 +32,8 @@ class Dotfile:
|
|||||||
self.cmpignore = cmpignore
|
self.cmpignore = cmpignore
|
||||||
# do not deploy empty file
|
# do not deploy empty file
|
||||||
self.noempty = noempty
|
self.noempty = noempty
|
||||||
|
# pattern to ignore when updating
|
||||||
|
self.upignore = upignore
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = 'key:\"{}\", src:\"{}\", dst:\"{}\", link:\"{}\"'
|
msg = 'key:\"{}\", src:\"{}\", dst:\"{}\", link:\"{}\"'
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class Templategen:
|
|||||||
filetype = filetype.strip()
|
filetype = filetype.strip()
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('\"{}\" filetype: {}'.format(src, filetype))
|
self.log.dbg('\"{}\" filetype: {}'.format(src, filetype))
|
||||||
istext = 'text' in filetype
|
istext = 'text' in filetype or 'empty' in filetype
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('\"{}\" is text: {}'.format(src, istext))
|
self.log.dbg('\"{}\" is text: {}'.format(src, istext))
|
||||||
if not istext:
|
if not istext:
|
||||||
|
|||||||
@@ -21,13 +21,14 @@ TILD = '~'
|
|||||||
class Updater:
|
class Updater:
|
||||||
|
|
||||||
def __init__(self, conf, dotpath, dry, safe,
|
def __init__(self, conf, dotpath, dry, safe,
|
||||||
iskey=False, debug=False):
|
iskey=False, debug=False, ignore=[]):
|
||||||
self.conf = conf
|
self.conf = conf
|
||||||
self.dotpath = dotpath
|
self.dotpath = dotpath
|
||||||
self.dry = dry
|
self.dry = dry
|
||||||
self.safe = safe
|
self.safe = safe
|
||||||
self.iskey = iskey
|
self.iskey = iskey
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
|
self.ignore = ignore
|
||||||
self.log = Logger()
|
self.log = Logger()
|
||||||
|
|
||||||
def update_path(self, path, profile):
|
def update_path(self, path, profile):
|
||||||
@@ -58,9 +59,16 @@ class Updater:
|
|||||||
"""update dotfile from file pointed by path"""
|
"""update dotfile from file pointed by path"""
|
||||||
ret = False
|
ret = False
|
||||||
new_path = None
|
new_path = None
|
||||||
|
self.ignores = list(set(self.ignore + dotfile.upignore))
|
||||||
|
if self.debug:
|
||||||
|
self.log.dbg('ignore pattern(s): {}'.format(self.ignores))
|
||||||
|
|
||||||
left = os.path.expanduser(path)
|
left = os.path.expanduser(path)
|
||||||
right = os.path.join(self.conf.abs_or_rel(self.dotpath), dotfile.src)
|
right = os.path.join(self.conf.abs_or_rel(self.dotpath), dotfile.src)
|
||||||
right = os.path.expanduser(right)
|
right = os.path.expanduser(right)
|
||||||
|
|
||||||
|
if self._ignore([left, right]):
|
||||||
|
return True
|
||||||
if dotfile.trans_w:
|
if dotfile.trans_w:
|
||||||
# apply write transformation if any
|
# apply write transformation if any
|
||||||
new_path = self._apply_trans_w(path, dotfile)
|
new_path = self._apply_trans_w(path, dotfile)
|
||||||
@@ -137,6 +145,8 @@ class Updater:
|
|||||||
|
|
||||||
def _handle_file(self, left, right, compare=True):
|
def _handle_file(self, left, right, compare=True):
|
||||||
"""sync left (deployed file) and right (dotdrop dotfile)"""
|
"""sync left (deployed file) and right (dotdrop dotfile)"""
|
||||||
|
if self._ignore([left, right]):
|
||||||
|
return True
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('update for file {} and {}'.format(left, right))
|
self.log.dbg('update for file {} and {}'.format(left, right))
|
||||||
if self._is_template(right):
|
if self._is_template(right):
|
||||||
@@ -169,6 +179,8 @@ class Updater:
|
|||||||
# paths must be absolute (no tildes)
|
# paths must be absolute (no tildes)
|
||||||
left = os.path.expanduser(left)
|
left = os.path.expanduser(left)
|
||||||
right = os.path.expanduser(right)
|
right = os.path.expanduser(right)
|
||||||
|
if self._ignore([left, right]):
|
||||||
|
return True
|
||||||
# find the differences
|
# find the differences
|
||||||
diff = filecmp.dircmp(left, right, ignore=None)
|
diff = filecmp.dircmp(left, right, ignore=None)
|
||||||
# handle directories diff
|
# handle directories diff
|
||||||
@@ -179,6 +191,8 @@ class Updater:
|
|||||||
left, right = diff.left, diff.right
|
left, right = diff.left, diff.right
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.dbg('sync dir {} to {}'.format(left, right))
|
self.log.dbg('sync dir {} to {}'.format(left, right))
|
||||||
|
if self._ignore([left, right]):
|
||||||
|
return True
|
||||||
|
|
||||||
# create dirs that don't exist in dotdrop
|
# create dirs that don't exist in dotdrop
|
||||||
for toadd in diff.left_only:
|
for toadd in diff.left_only:
|
||||||
@@ -188,6 +202,8 @@ class Updater:
|
|||||||
continue
|
continue
|
||||||
# match to dotdrop dotpath
|
# match to dotdrop dotpath
|
||||||
new = os.path.join(right, toadd)
|
new = os.path.join(right, toadd)
|
||||||
|
if self._ignore([exist, new]):
|
||||||
|
continue
|
||||||
if self.dry:
|
if self.dry:
|
||||||
self.log.dry('would cp -r {} {}'.format(exist, new))
|
self.log.dry('would cp -r {} {}'.format(exist, new))
|
||||||
continue
|
continue
|
||||||
@@ -202,6 +218,8 @@ class Updater:
|
|||||||
if not os.path.isdir(old):
|
if not os.path.isdir(old):
|
||||||
# ignore files for now
|
# ignore files for now
|
||||||
continue
|
continue
|
||||||
|
if self._ignore([old]):
|
||||||
|
continue
|
||||||
if self.dry:
|
if self.dry:
|
||||||
self.log.dry('would rm -r {}'.format(old))
|
self.log.dry('would rm -r {}'.format(old))
|
||||||
continue
|
continue
|
||||||
@@ -219,6 +237,8 @@ class Updater:
|
|||||||
for f in fdiff:
|
for f in fdiff:
|
||||||
fleft = os.path.join(left, f)
|
fleft = os.path.join(left, f)
|
||||||
fright = os.path.join(right, f)
|
fright = os.path.join(right, f)
|
||||||
|
if self._ignore([fleft, fright]):
|
||||||
|
continue
|
||||||
if self.dry:
|
if self.dry:
|
||||||
self.log.dry('would cp {} {}'.format(fleft, fright))
|
self.log.dry('would cp {} {}'.format(fleft, fright))
|
||||||
continue
|
continue
|
||||||
@@ -233,6 +253,8 @@ class Updater:
|
|||||||
# ignore dirs, done above
|
# ignore dirs, done above
|
||||||
continue
|
continue
|
||||||
new = os.path.join(right, toadd)
|
new = os.path.join(right, toadd)
|
||||||
|
if self._ignore([exist, new]):
|
||||||
|
continue
|
||||||
if self.dry:
|
if self.dry:
|
||||||
self.log.dry('would cp {} {}'.format(exist, new))
|
self.log.dry('would cp {} {}'.format(exist, new))
|
||||||
continue
|
continue
|
||||||
@@ -248,6 +270,8 @@ class Updater:
|
|||||||
if os.path.isdir(new):
|
if os.path.isdir(new):
|
||||||
# ignore dirs, done above
|
# ignore dirs, done above
|
||||||
continue
|
continue
|
||||||
|
if self._ignore([new]):
|
||||||
|
continue
|
||||||
if self.dry:
|
if self.dry:
|
||||||
self.log.dry('would rm {}'.format(new))
|
self.log.dry('would rm {}'.format(new))
|
||||||
continue
|
continue
|
||||||
@@ -275,3 +299,10 @@ class Updater:
|
|||||||
if self.safe and not self.log.ask(msg):
|
if self.safe and not self.log.ask(msg):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _ignore(self, paths):
|
||||||
|
if utils.must_ignore(paths, self.ignores, debug=self.debug):
|
||||||
|
if self.debug:
|
||||||
|
self.log.dbg('ignoring update for {}'.format(paths))
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import uuid
|
|||||||
import shlex
|
import shlex
|
||||||
import functools
|
import functools
|
||||||
import operator
|
import operator
|
||||||
|
import fnmatch
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
|
|
||||||
# local import
|
# local import
|
||||||
@@ -115,3 +116,16 @@ def strip_home(path):
|
|||||||
def flatten(a):
|
def flatten(a):
|
||||||
"""flatten list"""
|
"""flatten list"""
|
||||||
return functools.reduce(operator.iconcat, a, [])
|
return functools.reduce(operator.iconcat, a, [])
|
||||||
|
|
||||||
|
|
||||||
|
def must_ignore(paths, ignores, debug=False):
|
||||||
|
"""return true if any paths in list matches any ignore patterns"""
|
||||||
|
if not ignores:
|
||||||
|
return False
|
||||||
|
for p in paths:
|
||||||
|
for i in ignores:
|
||||||
|
if fnmatch.fnmatch(p, i):
|
||||||
|
if debug:
|
||||||
|
LOG.dbg('ignore \"{}\" match: {}'.format(i, p))
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|||||||
130
tests-ng/profile-dynvariables.sh
Executable file
130
tests-ng/profile-dynvariables.sh
Executable file
@@ -0,0 +1,130 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# author: deadc0de6 (https://github.com/deadc0de6)
|
||||||
|
# Copyright (c) 2017, deadc0de6
|
||||||
|
#
|
||||||
|
# test variables per profile
|
||||||
|
# returns 1 in case of error
|
||||||
|
#
|
||||||
|
|
||||||
|
# exit on first error
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# all this crap to get current path
|
||||||
|
rl="readlink -f"
|
||||||
|
if ! ${rl} "${0}" >/dev/null 2>&1; then
|
||||||
|
rl="realpath"
|
||||||
|
|
||||||
|
if ! hash ${rl}; then
|
||||||
|
echo "\"${rl}\" not found !" && exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
cur=$(dirname "$(${rl} "${0}")")
|
||||||
|
|
||||||
|
#hash dotdrop >/dev/null 2>&1
|
||||||
|
#[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1
|
||||||
|
|
||||||
|
#echo "called with ${1}"
|
||||||
|
|
||||||
|
# dotdrop path can be pass as argument
|
||||||
|
ddpath="${cur}/../"
|
||||||
|
[ "${1}" != "" ] && ddpath="${1}"
|
||||||
|
[ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1
|
||||||
|
|
||||||
|
export PYTHONPATH="${ddpath}:${PYTHONPATH}"
|
||||||
|
bin="python3 -m dotdrop.dotdrop"
|
||||||
|
|
||||||
|
echo "dotdrop path: ${ddpath}"
|
||||||
|
echo "pythonpath: ${PYTHONPATH}"
|
||||||
|
|
||||||
|
# get the helpers
|
||||||
|
source ${cur}/helpers
|
||||||
|
|
||||||
|
echo -e "\e[96m\e[1m==> RUNNING $(basename $BASH_SOURCE) <==\e[0m"
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# this is the test
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# the dotfile source
|
||||||
|
tmps=`mktemp -d`
|
||||||
|
mkdir -p ${tmps}/dotfiles
|
||||||
|
# the dotfile destination
|
||||||
|
tmpd=`mktemp -d`
|
||||||
|
#echo "dotfile destination: ${tmpd}"
|
||||||
|
|
||||||
|
# create a shell script
|
||||||
|
export TESTENV="this is my global testenv"
|
||||||
|
scr=`mktemp`
|
||||||
|
chmod +x ${scr}
|
||||||
|
echo -e "#!/bin/bash\necho $TESTENV\n" >> ${scr}
|
||||||
|
|
||||||
|
export TESTENV2="this is my profile testenv"
|
||||||
|
scr2=`mktemp`
|
||||||
|
chmod +x ${scr2}
|
||||||
|
echo -e "#!/bin/bash\necho $TESTENV2\n" >> ${scr2}
|
||||||
|
|
||||||
|
# create the config file
|
||||||
|
cfg="${tmps}/config.yaml"
|
||||||
|
|
||||||
|
cat > ${cfg} << _EOF
|
||||||
|
config:
|
||||||
|
backup: true
|
||||||
|
create: true
|
||||||
|
dotpath: dotfiles
|
||||||
|
variables:
|
||||||
|
gvar1: "global1"
|
||||||
|
gvar2: "global2"
|
||||||
|
dynvariables:
|
||||||
|
gdvar1: head -1 /proc/meminfo
|
||||||
|
gdvar2: "echo 'this is some test' | rev | tr ' ' ','"
|
||||||
|
gdvar3: ${scr}
|
||||||
|
dotfiles:
|
||||||
|
f_abc:
|
||||||
|
dst: ${tmpd}/abc
|
||||||
|
src: abc
|
||||||
|
profiles:
|
||||||
|
p1:
|
||||||
|
variables:
|
||||||
|
gvar1: "local1"
|
||||||
|
lvar1: "local2"
|
||||||
|
dynvariables:
|
||||||
|
gdvar3: ${scr2}
|
||||||
|
pdvar1: "echo 'abc' | rev"
|
||||||
|
dotfiles:
|
||||||
|
- f_abc
|
||||||
|
_EOF
|
||||||
|
cat ${cfg}
|
||||||
|
|
||||||
|
# create the dotfile
|
||||||
|
echo "===================" > ${tmps}/dotfiles/abc
|
||||||
|
echo "{{@@ gvar1 @@}}" >> ${tmps}/dotfiles/abc
|
||||||
|
echo "{{@@ gvar2 @@}}" >> ${tmps}/dotfiles/abc
|
||||||
|
echo "{{@@ gdvar1 @@}}" >> ${tmps}/dotfiles/abc
|
||||||
|
echo "{{@@ gdvar2 @@}}" >> ${tmps}/dotfiles/abc
|
||||||
|
echo "{{@@ gdvar3 @@}}" >> ${tmps}/dotfiles/abc
|
||||||
|
echo "{{@@ lvar1 @@}}" >> ${tmps}/dotfiles/abc
|
||||||
|
echo "{{@@ pdvar1 @@}}" >> ${tmps}/dotfiles/abc
|
||||||
|
echo "===================" >> ${tmps}/dotfiles/abc
|
||||||
|
|
||||||
|
# install
|
||||||
|
cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V
|
||||||
|
|
||||||
|
#cat ${tmpd}/abc
|
||||||
|
|
||||||
|
# test variables
|
||||||
|
grep '^local1' ${tmpd}/abc >/dev/null
|
||||||
|
grep '^global2' ${tmpd}/abc >/dev/null
|
||||||
|
grep '^local2' ${tmpd}/abc >/dev/null
|
||||||
|
# test dynvariables
|
||||||
|
grep "^MemTotal" ${tmpd}/abc >/dev/null
|
||||||
|
grep '^tset,emos,si,siht' ${tmpd}/abc >/dev/null
|
||||||
|
grep "^${TESTENV2}" ${tmpd}/abc > /dev/null
|
||||||
|
grep "^cba" ${tmpd}/abc >/dev/null
|
||||||
|
|
||||||
|
#cat ${tmpd}/abc
|
||||||
|
|
||||||
|
## CLEANING
|
||||||
|
rm -rf ${tmps} ${tmpd} ${scr} ${scr2}
|
||||||
|
|
||||||
|
echo "OK"
|
||||||
|
exit 0
|
||||||
107
tests-ng/update-ignore.sh
Executable file
107
tests-ng/update-ignore.sh
Executable file
@@ -0,0 +1,107 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# author: deadc0de6 (https://github.com/deadc0de6)
|
||||||
|
# Copyright (c) 2017, deadc0de6
|
||||||
|
#
|
||||||
|
# test ignore update
|
||||||
|
# returns 1 in case of error
|
||||||
|
#
|
||||||
|
|
||||||
|
# exit on first error
|
||||||
|
#set -e
|
||||||
|
|
||||||
|
# all this crap to get current path
|
||||||
|
rl="readlink -f"
|
||||||
|
if ! ${rl} "${0}" >/dev/null 2>&1; then
|
||||||
|
rl="realpath"
|
||||||
|
|
||||||
|
if ! hash ${rl}; then
|
||||||
|
echo "\"${rl}\" not found !" && exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
cur=$(dirname "$(${rl} "${0}")")
|
||||||
|
|
||||||
|
#hash dotdrop >/dev/null 2>&1
|
||||||
|
#[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1
|
||||||
|
|
||||||
|
#echo "called with ${1}"
|
||||||
|
|
||||||
|
# dotdrop path can be pass as argument
|
||||||
|
ddpath="${cur}/../"
|
||||||
|
[ "${1}" != "" ] && ddpath="${1}"
|
||||||
|
[ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1
|
||||||
|
|
||||||
|
export PYTHONPATH="${ddpath}:${PYTHONPATH}"
|
||||||
|
bin="python3 -m dotdrop.dotdrop"
|
||||||
|
|
||||||
|
echo "dotdrop path: ${ddpath}"
|
||||||
|
echo "pythonpath: ${PYTHONPATH}"
|
||||||
|
|
||||||
|
# get the helpers
|
||||||
|
source ${cur}/helpers
|
||||||
|
|
||||||
|
echo -e "\e[96m\e[1m==> RUNNING $(basename $BASH_SOURCE) <==\e[0m"
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# this is the test
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# dotdrop directory
|
||||||
|
tmps=`mktemp -d`
|
||||||
|
dt="${tmps}/dotfiles"
|
||||||
|
mkdir -p ${dt}
|
||||||
|
mkdir -p ${dt}/a/{b,c}
|
||||||
|
echo 'a' > ${dt}/a/b/abfile
|
||||||
|
echo 'a' > ${dt}/a/c/acfile
|
||||||
|
|
||||||
|
# fs dotfiles
|
||||||
|
tmpd=`mktemp -d`
|
||||||
|
cp -r ${dt}/a ${tmpd}/
|
||||||
|
|
||||||
|
# create the config file
|
||||||
|
cfg="${tmps}/config.yaml"
|
||||||
|
cat > ${cfg} << _EOF
|
||||||
|
config:
|
||||||
|
backup: false
|
||||||
|
create: true
|
||||||
|
dotpath: dotfiles
|
||||||
|
dotfiles:
|
||||||
|
f_abc:
|
||||||
|
dst: ${tmpd}/a
|
||||||
|
src: a
|
||||||
|
upignore:
|
||||||
|
- "*/cfile"
|
||||||
|
- "*/newfile"
|
||||||
|
- "*/newdir"
|
||||||
|
profiles:
|
||||||
|
p1:
|
||||||
|
dotfiles:
|
||||||
|
- f_abc
|
||||||
|
_EOF
|
||||||
|
cat ${cfg}
|
||||||
|
|
||||||
|
#tree ${dt}
|
||||||
|
|
||||||
|
# edit/add files
|
||||||
|
echo "[+] edit/add files"
|
||||||
|
touch ${tmpd}/a/newfile
|
||||||
|
echo 'b' > ${tmpd}/a/c/acfile
|
||||||
|
mkdir -p ${tmpd}/a/newdir/b
|
||||||
|
touch ${tmpd}/a/newdir/b/c
|
||||||
|
|
||||||
|
#tree ${tmpd}/a
|
||||||
|
|
||||||
|
# update
|
||||||
|
echo "[+] update"
|
||||||
|
cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=p1 --key f_abc
|
||||||
|
|
||||||
|
#tree ${dt}
|
||||||
|
|
||||||
|
# check files haven't been updated
|
||||||
|
grep 'b' ${dt}/a/c/acfile >/dev/null
|
||||||
|
[ -e ${dt}/a/newfile ] && exit 1
|
||||||
|
|
||||||
|
## CLEANING
|
||||||
|
rm -rf ${tmps} ${tmpd}
|
||||||
|
|
||||||
|
echo "OK"
|
||||||
|
exit 0
|
||||||
2
tests.sh
2
tests.sh
@@ -6,6 +6,8 @@
|
|||||||
set -ev
|
set -ev
|
||||||
|
|
||||||
# PEP8 tests
|
# PEP8 tests
|
||||||
|
which pycodestyle 2>/dev/null
|
||||||
|
[ "$?" != "0" ] && echo "Install pycodestyle" && exit 1
|
||||||
pycodestyle --ignore=W605 dotdrop/
|
pycodestyle --ignore=W605 dotdrop/
|
||||||
pycodestyle tests/
|
pycodestyle tests/
|
||||||
pycodestyle scripts/
|
pycodestyle scripts/
|
||||||
|
|||||||
@@ -277,11 +277,12 @@ exec bspwm
|
|||||||
src = create_random_file(src_dir)[0]
|
src = create_random_file(src_dir)[0]
|
||||||
|
|
||||||
logger = MagicMock()
|
logger = MagicMock()
|
||||||
|
templater = MagicMock()
|
||||||
installer = Installer()
|
installer = Installer()
|
||||||
installer.log.err = logger
|
installer.log.err = logger
|
||||||
|
|
||||||
# pass src file not src dir
|
# pass src file not src dir
|
||||||
res = installer.linkall(templater=MagicMock(), src=src, dst='/dev/null',
|
res = installer.linkall(templater=templater, src=src, dst='/dev/null',
|
||||||
actions=[])
|
actions=[])
|
||||||
|
|
||||||
# ensure nothing performed
|
# ensure nothing performed
|
||||||
|
|||||||
Reference in New Issue
Block a user