mirror of
https://github.com/deadc0de6/dotdrop.git
synced 2026-02-15 19:05:06 +00:00
Merge branch 'master' of github.com:deadc0de6/dotdrop
This commit is contained in:
2
docs/config/config-dotfiles.md
vendored
2
docs/config/config-dotfiles.md
vendored
@@ -8,7 +8,7 @@ Entry | Description
|
||||
`src` | Dotfile path within the `dotpath` (dotfiles with empty `src` are ignored and considered installed, can use `variables`, make sure to quote)
|
||||
`link` | Defines how this dotfile is installed. Possible values: *nolink*, *absolute*, *relative*, *link_children* (See [Symlinking dotfiles](config-file.md#symlinking-dotfiles)) (defaults to value of `link_dotfile_default`)
|
||||
`actions` | List of action keys that need to be defined in the **actions** entry below (See [actions](config-actions.md))
|
||||
`chmod` | Defines the file permissions in octal notation to apply during installation (See [permissions](config-file.md#permissions))
|
||||
`chmod` | Defines the file permissions in octal notation to apply during installation or the special keyword `preserve` (See [permissions](config-file.md#permissions))
|
||||
`cmpignore` | List of patterns to ignore when comparing (enclose in quotes when using wildcards; see [ignore patterns](config-file.md#ignore-patterns))
|
||||
`ignore_missing_in_dotdrop` | Ignore missing files in dotdrop when comparing and importing (see [Ignore missing](config-file.md#ignore-missing))
|
||||
`ignoreempty` | If true, an empty template will not be deployed (defaults to the value of `ignoreempty`)
|
||||
|
||||
17
docs/config/config-file.md
vendored
17
docs/config/config-file.md
vendored
@@ -90,8 +90,20 @@ dotfiles:
|
||||
src: dir
|
||||
dst: ~/dir
|
||||
chmod: 744
|
||||
f_preserve:
|
||||
src: preserve
|
||||
dst: ~/preserve
|
||||
chmod: preserve
|
||||
```
|
||||
|
||||
The `chmod` value defines the file permissions in octal notation to apply on dotfiles. If undefined
|
||||
new files will get the system default permissions (see `umask`, `777-<umask>` for directories and
|
||||
`666-<umask>` for files).
|
||||
|
||||
The special keyword `preserve` allows to ensure that if the dotfiles already exists
|
||||
on the filesystem, it is not altered during `install` and the `chmod` value won't
|
||||
be changed during `update`.
|
||||
|
||||
On `import`, the following rules are applied:
|
||||
|
||||
* If the `-m`/`--preserve-mode` switch is provided or the config option
|
||||
@@ -107,12 +119,13 @@ On `install`, the following rules are applied:
|
||||
* Otherwise, the permissions of the dotfile in the `dotpath` are applied.
|
||||
* If the global setting `force_chmod` is set to true, dotdrop will not ask
|
||||
for confirmation to apply permissions.
|
||||
* If `chmod` is `preserve` and the destination exists with a different permission set
|
||||
than system default, then it is not altered
|
||||
|
||||
On `update`, the following rule is applied:
|
||||
|
||||
* If the permissions of the file in the filesystem differ from the dotfile in the `dotpath`,
|
||||
then the dotfile entry `chmod` is added/updated accordingly.
|
||||
|
||||
then the dotfile entry `chmod` is added/updated accordingly (unless `chmod` value is `preserve`)
|
||||
|
||||
## Symlinking dotfiles
|
||||
|
||||
|
||||
@@ -67,6 +67,9 @@ class CfgYaml:
|
||||
key_dotfile_template = 'template'
|
||||
key_dotfile_chmod = 'chmod'
|
||||
|
||||
# chmod value
|
||||
chmod_ignore = 'preserve'
|
||||
|
||||
# profile
|
||||
key_profile_dotfiles = 'dotfiles'
|
||||
key_profile_include = 'include'
|
||||
@@ -378,14 +381,17 @@ class CfgYaml:
|
||||
"""return all existing dotfile keys"""
|
||||
return self.dotfiles.keys()
|
||||
|
||||
def update_dotfile(self, key, chmod):
|
||||
"""update an existing dotfile"""
|
||||
if key not in self.dotfiles.keys():
|
||||
return False
|
||||
dotfile = self._yaml_dict[self.key_dotfiles][key]
|
||||
def _update_dotfile_chmod(self, key, dotfile, chmod):
|
||||
old = None
|
||||
if self.key_dotfile_chmod in dotfile:
|
||||
old = dotfile[self.key_dotfile_chmod]
|
||||
if old == self.chmod_ignore:
|
||||
msg = (
|
||||
'ignore chmod change since '
|
||||
f'{self.chmod_ignore}'
|
||||
)
|
||||
self._dbg(msg)
|
||||
return False
|
||||
if old == chmod:
|
||||
return False
|
||||
if self._debug:
|
||||
@@ -397,6 +403,18 @@ class CfgYaml:
|
||||
del dotfile[self.key_dotfile_chmod]
|
||||
else:
|
||||
dotfile[self.key_dotfile_chmod] = str(format(chmod, 'o'))
|
||||
return True
|
||||
|
||||
def update_dotfile(self, key, chmod):
|
||||
"""
|
||||
update an existing dotfile
|
||||
return true if updated
|
||||
"""
|
||||
if key not in self.dotfiles.keys():
|
||||
return False
|
||||
dotfile = self._yaml_dict[self.key_dotfiles][key]
|
||||
if not self._update_dotfile_chmod(key, dotfile, chmod):
|
||||
return False
|
||||
self._dirty = True
|
||||
return True
|
||||
|
||||
@@ -743,62 +761,77 @@ class CfgYaml:
|
||||
new[k] = val
|
||||
return new
|
||||
|
||||
def _norm_dotfile_chmod(self, entry):
|
||||
value = str(entry[self.key_dotfile_chmod])
|
||||
if value == self.chmod_ignore:
|
||||
# is preserve
|
||||
return
|
||||
if len(value) < 3:
|
||||
# bad format
|
||||
err = f'bad format for chmod: {value}'
|
||||
self._log.err(err)
|
||||
raise YamlException(f'config content error: {err}')
|
||||
|
||||
# check is valid value
|
||||
try:
|
||||
int(value)
|
||||
except Exception as exc:
|
||||
err = f'bad format for chmod: {value}'
|
||||
self._log.err(err)
|
||||
err = f'config content error: {err}'
|
||||
raise YamlException(err) from exc
|
||||
|
||||
# normalize chmod value
|
||||
for chmodv in list(value):
|
||||
chmodint = int(chmodv)
|
||||
if chmodint < 0 or chmodint > 7:
|
||||
err = f'bad format for chmod: {value}'
|
||||
self._log.err(err)
|
||||
raise YamlException(
|
||||
f'config content error: {err}'
|
||||
)
|
||||
# octal
|
||||
entry[self.key_dotfile_chmod] = int(value, 8)
|
||||
|
||||
def _norm_dotfiles(self, dotfiles):
|
||||
"""normalize and check dotfiles entries"""
|
||||
if not dotfiles:
|
||||
return dotfiles
|
||||
new = {}
|
||||
for k, val in dotfiles.items():
|
||||
# add 'src' as key' if not present
|
||||
if self.key_dotfile_src not in val:
|
||||
# add 'src' as key' if not present
|
||||
val[self.key_dotfile_src] = k
|
||||
new[k] = val
|
||||
else:
|
||||
new[k] = val
|
||||
# fix deprecated trans key
|
||||
|
||||
if self.old_key_trans_r in val:
|
||||
msg = '\"trans\" is deprecated, please use \"trans_read\"'
|
||||
# fix deprecated trans key
|
||||
msg = f'{k} \"trans\" is deprecated, please use \"trans_read\"'
|
||||
self._log.warn(msg)
|
||||
val[self.key_trans_r] = val[self.old_key_trans_r]
|
||||
del val[self.old_key_trans_r]
|
||||
new[k] = val
|
||||
|
||||
if self.key_dotfile_link not in val:
|
||||
# apply link value if undefined
|
||||
value = self.settings[self.key_settings_link_dotfile_default]
|
||||
val[self.key_dotfile_link] = value
|
||||
# apply noempty if undefined
|
||||
|
||||
if self.key_dotfile_noempty not in val:
|
||||
# apply noempty if undefined
|
||||
value = self.settings.get(self.key_settings_noempty, False)
|
||||
val[self.key_dotfile_noempty] = value
|
||||
# apply template if undefined
|
||||
|
||||
if self.key_dotfile_template not in val:
|
||||
# apply template if undefined
|
||||
value = self.settings.get(self.key_settings_template, True)
|
||||
val[self.key_dotfile_template] = value
|
||||
# validate value of chmod if defined
|
||||
if self.key_dotfile_chmod in val:
|
||||
value = str(val[self.key_dotfile_chmod])
|
||||
if len(value) < 3:
|
||||
err = f'bad format for chmod: {value}'
|
||||
self._log.err(err)
|
||||
raise YamlException(f'config content error: {err}')
|
||||
try:
|
||||
int(value)
|
||||
except Exception as exc:
|
||||
err = f'bad format for chmod: {value}'
|
||||
self._log.err(err)
|
||||
err = f'config content error: {err}'
|
||||
raise YamlException(err) from exc
|
||||
# normalize chmod value
|
||||
for chmodv in list(value):
|
||||
chmodint = int(chmodv)
|
||||
if chmodint < 0 or chmodint > 7:
|
||||
err = f'bad format for chmod: {value}'
|
||||
self._log.err(err)
|
||||
raise YamlException(
|
||||
f'config content error: {err}'
|
||||
)
|
||||
val[self.key_dotfile_chmod] = int(value, 8)
|
||||
|
||||
if self.key_dotfile_chmod in val:
|
||||
# validate value of chmod if defined
|
||||
self._norm_dotfile_chmod(val)
|
||||
return new
|
||||
|
||||
def _add_variables(self, new, shell=False, template=True, prio=False):
|
||||
|
||||
@@ -117,7 +117,10 @@ class Dotfile(DictParser):
|
||||
msg += f', link:\"{self.link}\"'
|
||||
msg += f', template:{self.template}'
|
||||
if self.chmod:
|
||||
msg += f', chmod:{self.chmod:o}'
|
||||
if isinstance(self.chmod, int) or len(self.chmod) == 3:
|
||||
msg += f', chmod:{self.chmod:o}'
|
||||
else:
|
||||
msg += f', chmod:\"{self.chmod}\"'
|
||||
return msg
|
||||
|
||||
def prt(self):
|
||||
@@ -129,7 +132,10 @@ class Dotfile(DictParser):
|
||||
out += f'\n{indent}link: \"{self.link}\"'
|
||||
out += f'\n{indent}template: \"{self.template}\"'
|
||||
if self.chmod:
|
||||
out += f'\n{indent}chmod: \"{self.chmod:o}\"'
|
||||
if isinstance(self.chmod, int) or len(self.chmod) == 3:
|
||||
out += f'\n{indent}chmod: \"{self.chmod:o}\"'
|
||||
else:
|
||||
out += f'\n{indent}chmod: \"{self.chmod}\"'
|
||||
|
||||
out += f'\n{indent}pre-action:'
|
||||
some = self.get_pre_actions()
|
||||
|
||||
@@ -14,6 +14,7 @@ from dotdrop.logger import Logger
|
||||
from dotdrop.linktypes import LinkTypes
|
||||
from dotdrop import utils
|
||||
from dotdrop.exceptions import UndefinedException
|
||||
from dotdrop.cfg_yaml import CfgYaml
|
||||
|
||||
|
||||
class Installer:
|
||||
@@ -138,13 +139,15 @@ class Installer:
|
||||
ret, err = self._link_absolute(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
is_template=is_template,
|
||||
ignore=ignore)
|
||||
ignore=ignore,
|
||||
chmod=chmod)
|
||||
elif linktype == LinkTypes.RELATIVE:
|
||||
# symlink
|
||||
ret, err = self._link_relative(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
is_template=is_template,
|
||||
ignore=ignore)
|
||||
ignore=ignore,
|
||||
chmod=chmod)
|
||||
elif linktype == LinkTypes.LINK_CHILDREN:
|
||||
# symlink direct children
|
||||
if not isdir:
|
||||
@@ -158,7 +161,16 @@ class Installer:
|
||||
is_template=is_template,
|
||||
ignore=ignore)
|
||||
|
||||
self.log.dbg(f'before chmod: {ret} err:{err}')
|
||||
if self.log.debug and chmod:
|
||||
cur = utils.get_file_perm(dst)
|
||||
if chmod == CfgYaml.chmod_ignore:
|
||||
chmodstr = CfgYaml.chmod_ignore
|
||||
else:
|
||||
chmodstr = f'{chmod:o}'
|
||||
self.log.dbg(
|
||||
f'before chmod (cur:{cur:o}, new:{chmodstr}): '
|
||||
f'installed:{ret} err:{err}'
|
||||
)
|
||||
|
||||
if self.dry:
|
||||
return self._log_install(ret, err)
|
||||
@@ -169,9 +181,15 @@ class Installer:
|
||||
# but not when
|
||||
# - error (not r, err)
|
||||
# - aborted (not r, err)
|
||||
if os.path.exists(dst) and (ret or (not ret and not err)):
|
||||
# - special keyword "preserve"
|
||||
apply_chmod = linktype in [LinkTypes.NOLINK, LinkTypes.LINK_CHILDREN]
|
||||
apply_chmod = apply_chmod and os.path.exists(dst)
|
||||
apply_chmod = apply_chmod and (ret or (not ret and not err))
|
||||
apply_chmod = apply_chmod and chmod != CfgYaml.chmod_ignore
|
||||
if apply_chmod:
|
||||
if not chmod:
|
||||
chmod = utils.get_file_perm(src)
|
||||
self.log.dbg(f'applying chmod {chmod:o} to {dst}')
|
||||
dstperms = utils.get_file_perm(dst)
|
||||
if dstperms != chmod:
|
||||
# apply mode
|
||||
@@ -187,6 +205,8 @@ class Installer:
|
||||
else:
|
||||
ret = False
|
||||
err = 'chmod failed'
|
||||
else:
|
||||
self.log.dbg('no chmod applied')
|
||||
|
||||
return self._log_install(ret, err)
|
||||
|
||||
@@ -255,7 +275,8 @@ class Installer:
|
||||
def _link_absolute(self, templater, src, dst,
|
||||
actionexec=None,
|
||||
is_template=True,
|
||||
ignore=None):
|
||||
ignore=None,
|
||||
chmod=None):
|
||||
"""
|
||||
install link:absolute|link
|
||||
|
||||
@@ -269,12 +290,14 @@ class Installer:
|
||||
actionexec=actionexec,
|
||||
is_template=is_template,
|
||||
ignore=ignore,
|
||||
absolute=True)
|
||||
absolute=True,
|
||||
chmod=chmod)
|
||||
|
||||
def _link_relative(self, templater, src, dst,
|
||||
actionexec=None,
|
||||
is_template=True,
|
||||
ignore=None):
|
||||
ignore=None,
|
||||
chmod=None):
|
||||
"""
|
||||
install link:relative
|
||||
|
||||
@@ -288,13 +311,18 @@ class Installer:
|
||||
actionexec=actionexec,
|
||||
is_template=is_template,
|
||||
ignore=ignore,
|
||||
absolute=False)
|
||||
absolute=False,
|
||||
chmod=chmod)
|
||||
|
||||
def _link_dotfile(self, templater, src, dst, actionexec=None,
|
||||
is_template=True, ignore=None, absolute=True):
|
||||
is_template=True, ignore=None, absolute=True,
|
||||
chmod=None):
|
||||
"""
|
||||
symlink
|
||||
|
||||
chmod is only used if the dotfile is a template
|
||||
and needs to be installed to the workdir first
|
||||
|
||||
return
|
||||
- True, None : success
|
||||
- False, error_msg : error
|
||||
@@ -302,15 +330,15 @@ class Installer:
|
||||
- False, 'aborted' : user aborted
|
||||
"""
|
||||
if is_template:
|
||||
self.log.dbg('is a template')
|
||||
self.log.dbg(f'install to {self.workdir}')
|
||||
self.log.dbg(f'is a template, installing to {self.workdir}')
|
||||
tmp = utils.pivot_path(dst, self.workdir,
|
||||
striphome=True, logger=self.log)
|
||||
ret, err = self.install(templater, src, tmp,
|
||||
LinkTypes.NOLINK,
|
||||
actionexec=actionexec,
|
||||
is_template=is_template,
|
||||
ignore=ignore)
|
||||
ignore=ignore,
|
||||
chmod=chmod)
|
||||
if not ret and not os.path.exists(tmp):
|
||||
return ret, err
|
||||
src = tmp
|
||||
@@ -467,6 +495,10 @@ class Installer:
|
||||
dstrel = os.path.dirname(dstrel)
|
||||
lnk_src = os.path.relpath(src, dstrel)
|
||||
os.symlink(lnk_src, dst)
|
||||
self.log.dbg(
|
||||
f'symlink {dst} to {lnk_src} '
|
||||
f'(mode:{utils.get_file_perm(dst):o})'
|
||||
)
|
||||
if not self.comparing:
|
||||
self.log.sub(f'linked {dst} to {lnk_src}')
|
||||
return True, None
|
||||
@@ -527,7 +559,10 @@ class Installer:
|
||||
ret, err = self._write(src, dst,
|
||||
content=content,
|
||||
actionexec=actionexec)
|
||||
|
||||
if ret and not err:
|
||||
rights = f'{utils.get_file_perm(src):o}'
|
||||
self.log.dbg(f'installed file {src} to {dst} ({rights})')
|
||||
if not self.dry and not self.comparing:
|
||||
self.log.sub(f'install {src} to {dst}')
|
||||
return ret, err
|
||||
@@ -587,13 +622,12 @@ class Installer:
|
||||
@classmethod
|
||||
def _write_content_to_file(cls, content, src, dst):
|
||||
"""write content to file"""
|
||||
|
||||
if content:
|
||||
# write content the file
|
||||
try:
|
||||
with open(dst, 'wb') as file:
|
||||
file.write(content)
|
||||
shutil.copymode(src, dst)
|
||||
# shutil.copymode(src, dst)
|
||||
except NotADirectoryError as exc:
|
||||
err = f'opening dest file: {exc}'
|
||||
return False, err
|
||||
@@ -605,7 +639,7 @@ class Installer:
|
||||
# copy file
|
||||
try:
|
||||
shutil.copyfile(src, dst)
|
||||
shutil.copymode(src, dst)
|
||||
# shutil.copymode(src, dst)
|
||||
except OSError as exc:
|
||||
return False, str(exc)
|
||||
return True, None
|
||||
@@ -665,7 +699,7 @@ class Installer:
|
||||
if not ret:
|
||||
return False, err
|
||||
|
||||
self.log.dbg(f'install file to \"{dst}\"')
|
||||
self.log.dbg(f'installing file to \"{dst}\"')
|
||||
# re-check in case action created the file
|
||||
if self.safe and not overwrite and \
|
||||
os.path.lexists(dst) and \
|
||||
@@ -674,7 +708,10 @@ class Installer:
|
||||
return False, 'aborted'
|
||||
|
||||
# writing to file
|
||||
return self._write_content_to_file(content, src, dst)
|
||||
self.log.dbg(f'before writing to {dst} ({utils.get_file_perm(src):o})')
|
||||
ret = self._write_content_to_file(content, src, dst)
|
||||
self.log.dbg(f'written to {dst} ({utils.get_file_perm(src):o})')
|
||||
return ret
|
||||
|
||||
########################################################
|
||||
# helpers
|
||||
@@ -749,7 +786,13 @@ class Installer:
|
||||
return
|
||||
dst = path.rstrip(os.sep) + self.backup_suffix
|
||||
self.log.log(f'backup {path} to {dst}')
|
||||
os.rename(path, dst)
|
||||
# os.rename(path, dst)
|
||||
# copy to preserve mode on chmod=preserve
|
||||
# since we expect dotfiles this shouldn't have
|
||||
# such a big impact but who knows.
|
||||
shutil.copy2(path, dst)
|
||||
stat = os.stat(path)
|
||||
os.chown(dst, stat.st_uid, stat.st_gid)
|
||||
|
||||
def _exec_pre_actions(self, actionexec):
|
||||
"""execute action executor"""
|
||||
|
||||
@@ -439,7 +439,9 @@ def get_default_file_perms(path, umask):
|
||||
|
||||
def get_file_perm(path):
|
||||
"""return file permission"""
|
||||
return os.stat(path).st_mode & 0o777
|
||||
if not os.path.exists(path):
|
||||
return 0o777
|
||||
return os.stat(path, follow_symlinks=True).st_mode & 0o777
|
||||
|
||||
|
||||
def chmod(path, mode, debug=False):
|
||||
|
||||
11
tests-ng/chmod-install.sh
vendored
11
tests-ng/chmod-install.sh
vendored
@@ -90,6 +90,7 @@ cfg="${tmps}/config.yaml"
|
||||
echo 'f777' > ${tmps}/dotfiles/f777
|
||||
chmod 700 ${tmps}/dotfiles/f777
|
||||
echo 'link' > ${tmps}/dotfiles/link
|
||||
chmod 777 ${tmps}/dotfiles/link
|
||||
mkdir -p ${tmps}/dotfiles/dir
|
||||
echo "f1" > ${tmps}/dotfiles/dir/f1
|
||||
|
||||
@@ -99,6 +100,7 @@ echo "exists" > ${tmpd}/exists
|
||||
chmod 644 ${tmpd}/exists
|
||||
|
||||
echo "existslink" > ${tmps}/dotfiles/existslink
|
||||
chmod 777 ${tmps}/dotfiles/existslink
|
||||
chmod 644 ${tmpd}/exists
|
||||
|
||||
mkdir -p ${tmps}/dotfiles/direxists
|
||||
@@ -187,7 +189,6 @@ profiles:
|
||||
p2:
|
||||
dotfiles:
|
||||
- f_exists
|
||||
- f_existslink
|
||||
- d_linkchildren
|
||||
- f_symlinktemplate
|
||||
- f_nomode
|
||||
@@ -196,8 +197,8 @@ _EOF
|
||||
|
||||
# install
|
||||
echo "first install round"
|
||||
#cd ${ddpath} | ${bin} install -c ${cfg} -f -p p1 -V
|
||||
cd ${ddpath} | ${bin} install -c ${cfg} -p p1 -V
|
||||
cd ${ddpath} | ${bin} install -c ${cfg} -f -p p1 -V
|
||||
echo "first install round"
|
||||
|
||||
has_rights "${tmpd}/f777" "777"
|
||||
has_rights "${tmpd}/link" "777"
|
||||
@@ -223,17 +224,15 @@ chmod 600 ${tmps}/dotfiles/exists
|
||||
echo "exists" > ${tmpd}/exists
|
||||
chmod 600 ${tmpd}/exists
|
||||
|
||||
chmod 600 ${tmpd}/existslink
|
||||
|
||||
chmod 700 ${tmpd}/linkchildren
|
||||
|
||||
chmod 600 ${tmpd}/symlinktemplate
|
||||
|
||||
echo "second install round"
|
||||
cd ${ddpath} | ${bin} install -c ${cfg} -p p2 -f -V
|
||||
echo "second install round"
|
||||
|
||||
has_rights "${tmpd}/exists" "777"
|
||||
has_rights "${tmpd}/existslink" "777"
|
||||
has_rights "${tmpd}/linkchildren/f1" "644"
|
||||
has_rights "${tmpd}/linkchildren/d1" "755"
|
||||
has_rights "${tmpd}/linkchildren/d1/f2" "644"
|
||||
|
||||
300
tests-ng/chmod-preserve-install.sh
vendored
Executable file
300
tests-ng/chmod-preserve-install.sh
vendored
Executable file
@@ -0,0 +1,300 @@
|
||||
#!/usr/bin/env bash
|
||||
# author: deadc0de6 (https://github.com/deadc0de6)
|
||||
# Copyright (c) 2022, deadc0de6
|
||||
#
|
||||
# test chmod preserve on install
|
||||
#
|
||||
|
||||
# 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"
|
||||
hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true
|
||||
|
||||
echo "dotdrop path: ${ddpath}"
|
||||
echo "pythonpath: ${PYTHONPATH}"
|
||||
|
||||
# get the helpers
|
||||
source ${cur}/helpers
|
||||
|
||||
echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)"
|
||||
|
||||
################################################################
|
||||
# this is the test
|
||||
################################################################
|
||||
|
||||
# $1 path
|
||||
# $2 rights
|
||||
has_rights()
|
||||
{
|
||||
echo "testing ${1} is ${2}"
|
||||
[ ! -e "$1" ] && echo "`basename $1` does not exist" && exit 1
|
||||
local mode=`stat -L -c '%a' "$1"`
|
||||
[ "${mode}" != "$2" ] && echo "bad mode for `basename $1` (${mode} VS expected ${2})" && exit 1
|
||||
true
|
||||
}
|
||||
|
||||
# test $1 path has same right than $2
|
||||
is_same_as()
|
||||
{
|
||||
echo "testing ${1} has same rights than ${2}"
|
||||
[ ! -e "$1" ] && echo "`basename $1` does not exist" && exit 1
|
||||
[ ! -e "$2" ] && echo "`basename $2` does not exist" && exit 1
|
||||
|
||||
local mode1=`stat -L -c '%a' "$1"`
|
||||
echo "$1: ${mode1}"
|
||||
local mode2=`stat -L -c '%a' "$2"`
|
||||
echo "$2: ${mode2}"
|
||||
|
||||
[ "${mode1}" != "${mode2}" ] && echo "`basename $1` (${mode1}) does not have same mode as `basename $2` (${mode2})" && exit 1
|
||||
true
|
||||
}
|
||||
|
||||
get_default_file_mode()
|
||||
{
|
||||
u=`umask`
|
||||
u=`echo ${u} | sed 's/^0*//'`
|
||||
v=$((666 - u))
|
||||
echo "${v}"
|
||||
}
|
||||
|
||||
get_default_dir_mode()
|
||||
{
|
||||
u=`umask`
|
||||
u=`echo ${u} | sed 's/^0*//'`
|
||||
v=$((777 - u))
|
||||
echo "${v}"
|
||||
}
|
||||
|
||||
# the dotfile source
|
||||
tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d`
|
||||
mkdir -p ${tmps}/dotfiles
|
||||
# the dotfile destination
|
||||
tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d`
|
||||
#echo "dotfile destination: ${tmpd}"
|
||||
|
||||
clear_on_exit "${tmps}"
|
||||
clear_on_exit "${tmpd}"
|
||||
|
||||
# create the config file
|
||||
cfg="${tmps}/config.yaml"
|
||||
|
||||
##
|
||||
# non existing files
|
||||
##
|
||||
|
||||
# file
|
||||
echo 'f777' > ${tmps}/dotfiles/f777
|
||||
chmod 700 ${tmps}/dotfiles/f777
|
||||
|
||||
# link
|
||||
echo 'link' > ${tmps}/dotfiles/link
|
||||
chmod 700 ${tmps}/dotfiles/link
|
||||
|
||||
# directory
|
||||
mkdir -p ${tmps}/dotfiles/dir
|
||||
echo "f1" > ${tmps}/dotfiles/dir/f1
|
||||
chmod 700 ${tmps}/dotfiles/dir
|
||||
chmod 700 ${tmps}/dotfiles/dir/f1
|
||||
|
||||
# template
|
||||
echo '{{@@ profile @@}}' > ${tmps}/dotfiles/template
|
||||
chmod 700 ${tmps}/dotfiles/template
|
||||
|
||||
# link template
|
||||
echo '{{@@ profile @@}}' > ${tmps}/dotfiles/link-template
|
||||
chmod 700 ${tmps}/dotfiles/link-template
|
||||
|
||||
##
|
||||
# existing files
|
||||
##
|
||||
|
||||
# file
|
||||
echo "exists-original" > ${tmps}/dotfiles/exists
|
||||
chmod 644 ${tmps}/dotfiles/exists
|
||||
echo "exists" > ${tmpd}/exists
|
||||
chmod 700 ${tmpd}/exists
|
||||
|
||||
# link
|
||||
echo "existslink" > ${tmps}/dotfiles/existslink
|
||||
chmod 700 ${tmps}/dotfiles/existslink
|
||||
ln -s ${tmps}/dotfiles/existslink ${tmpd}/existslink
|
||||
|
||||
# directory
|
||||
mkdir -p ${tmps}/dotfiles/direxists
|
||||
echo "f1-original" > ${tmps}/dotfiles/direxists/f1
|
||||
mkdir -p ${tmpd}/direxists
|
||||
echo "f1" > ${tmpd}/direxists/f1
|
||||
chmod 700 ${tmpd}/direxists/f1
|
||||
chmod 700 ${tmpd}/direxists
|
||||
|
||||
# link children
|
||||
mkdir -p ${tmps}/dotfiles/linkchildren
|
||||
echo "f1-original" > ${tmps}/dotfiles/linkchildren/f1
|
||||
chmod 700 ${tmps}/dotfiles/linkchildren/f1
|
||||
mkdir -p ${tmps}/dotfiles/linkchildren/d1
|
||||
chmod 700 ${tmps}/dotfiles/linkchildren/d1
|
||||
echo "f2-original" > ${tmps}/dotfiles/linkchildren/d1/f2
|
||||
chmod 700 ${tmps}/dotfiles/linkchildren/d1/f2
|
||||
|
||||
mkdir -p ${tmpd}/linkchildren
|
||||
chmod 700 ${tmpd}/linkchildren
|
||||
echo "f1" > ${tmpd}/linkchildren/f1
|
||||
mkdir -p ${tmpd}/linkchildren/d1
|
||||
echo "f2" > ${tmpd}/linkchildren/d1/f2
|
||||
|
||||
# no mode
|
||||
echo 'nomode-original' > ${tmps}/dotfiles/nomode
|
||||
echo 'nomode' > ${tmpd}/nomode
|
||||
|
||||
cat > ${cfg} << _EOF
|
||||
config:
|
||||
backup: true
|
||||
create: true
|
||||
dotpath: dotfiles
|
||||
force_chmod: true
|
||||
dotfiles:
|
||||
f_f777:
|
||||
src: f777
|
||||
dst: ${tmpd}/f777
|
||||
chmod: preserve
|
||||
f_link:
|
||||
src: link
|
||||
dst: ${tmpd}/link
|
||||
chmod: preserve
|
||||
link: absolute
|
||||
d_dir:
|
||||
src: dir
|
||||
dst: ${tmpd}/dir
|
||||
chmod: preserve
|
||||
f_template:
|
||||
src: template
|
||||
dst: ${tmpd}/template
|
||||
chmod: preserve
|
||||
f_link_template:
|
||||
src: link-template
|
||||
dst: ${tmpd}/link-template
|
||||
chmod: preserve
|
||||
f_exists:
|
||||
src: exists
|
||||
dst: ${tmpd}/exists
|
||||
chmod: preserve
|
||||
f_existslink:
|
||||
src: existslink
|
||||
dst: ${tmpd}/existslink
|
||||
chmod: preserve
|
||||
link: absolute
|
||||
d_direxists:
|
||||
src: direxists
|
||||
dst: ${tmpd}/direxists
|
||||
chmod: preserve
|
||||
d_linkchildren:
|
||||
src: linkchildren
|
||||
dst: ${tmpd}/linkchildren
|
||||
chmod: preserve
|
||||
link: link_children
|
||||
f_nomode:
|
||||
src: nomode
|
||||
dst: ${tmpd}/nomode
|
||||
chmod: preserve
|
||||
profiles:
|
||||
p1:
|
||||
dotfiles:
|
||||
- f_f777
|
||||
- f_link
|
||||
- d_dir
|
||||
- f_template
|
||||
- f_link_template
|
||||
- f_exists
|
||||
- f_existslink
|
||||
- d_direxists
|
||||
- d_linkchildren
|
||||
- f_nomode
|
||||
_EOF
|
||||
#cat ${cfg}
|
||||
|
||||
exists_before=`stat -L -c '%a' "${tmpd}/exists"`
|
||||
direxists_before=`stat -L -c '%a' "${tmpd}/direxists"`
|
||||
direxists_f1_before=`stat -L -c '%a' "${tmpd}/direxists/f1"`
|
||||
|
||||
# install
|
||||
echo "first round"
|
||||
cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V
|
||||
echo "first round"
|
||||
|
||||
# non-existing but will create with "default" rights on preserve
|
||||
# 644 for file
|
||||
# 755 for directory
|
||||
# link will get the rights of the file it points to
|
||||
has_rights "${tmpd}/f777" "`get_default_file_mode`"
|
||||
has_rights "${tmpd}/link" "700"
|
||||
has_rights "${tmpd}/dir" "`get_default_dir_mode`"
|
||||
has_rights "${tmpd}/template" "`get_default_file_mode`"
|
||||
# first install to workdir (def rights) and then symlink
|
||||
has_rights "${tmpd}/link-template" "644"
|
||||
[ -L "${tmpd}/link-template" ] && echo "link-template is not a symlink" && exit 1
|
||||
|
||||
# existing
|
||||
has_rights "${tmpd}/exists" "700"
|
||||
has_rights "${tmpd}/exists" "${exists_before}"
|
||||
|
||||
has_rights "${tmpd}/existslink" "700" # points back to dotpath
|
||||
is_same_as "${tmpd}/existslink" "${tmps}/dotfiles/existslink"
|
||||
|
||||
has_rights "${tmpd}/direxists" "700"
|
||||
has_rights "${tmpd}/direxists" "${direxists_before}"
|
||||
|
||||
has_rights "${tmpd}/direxists/f1" "700"
|
||||
has_rights "${tmpd}/direxists/f1" "${direxists_f1_before}"
|
||||
|
||||
has_rights "${tmpd}/linkchildren" "700" # default for new directory
|
||||
has_rights "${tmpd}/linkchildren/f1" "700" # points back to dotpath
|
||||
has_rights "${tmpd}/linkchildren/d1" "700" # points back to dotpath
|
||||
has_rights "${tmpd}/linkchildren/d1/f2" "700"
|
||||
|
||||
# modify
|
||||
echo 'f777-2' >> ${tmps}/dotfiles/f777
|
||||
chmod 701 ${tmps}/dotfiles/f777
|
||||
echo 'link-2' >> ${tmps}/dotfiles/link
|
||||
chmod 701 ${tmps}/dotfiles/link
|
||||
echo "f1-2" >> ${tmps}/dotfiles/dir/f1
|
||||
chmod 701 ${tmps}/dotfiles/dir
|
||||
chmod 701 ${tmps}/dotfiles/dir/f1
|
||||
|
||||
f777_before=`stat -L -c '%a' "${tmpd}/f777"`
|
||||
link_before=`stat -L -c '%a' "${tmpd}/link"`
|
||||
dir_before=`stat -L -c '%a' "${tmpd}/dir"`
|
||||
|
||||
echo "second round"
|
||||
cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V
|
||||
echo "second round"
|
||||
|
||||
# existing
|
||||
has_rights "${tmpd}/f777" "${f777_before}"
|
||||
has_rights "${tmpd}/link" "${link_before}"
|
||||
has_rights "${tmpd}/dir" "${dir_before}"
|
||||
|
||||
echo "OK"
|
||||
exit 0
|
||||
154
tests-ng/chmod-preserve-update.sh
vendored
Executable file
154
tests-ng/chmod-preserve-update.sh
vendored
Executable file
@@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env bash
|
||||
# author: deadc0de6 (https://github.com/deadc0de6)
|
||||
# Copyright (c) 2022, deadc0de6
|
||||
#
|
||||
# test chmod preserve on update
|
||||
#
|
||||
|
||||
# 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"
|
||||
hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true
|
||||
|
||||
echo "dotdrop path: ${ddpath}"
|
||||
echo "pythonpath: ${PYTHONPATH}"
|
||||
|
||||
# get the helpers
|
||||
source ${cur}/helpers
|
||||
|
||||
echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)"
|
||||
|
||||
################################################################
|
||||
# this is the test
|
||||
################################################################
|
||||
|
||||
# the dotfile source
|
||||
tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d`
|
||||
mkdir -p ${tmps}/dotfiles
|
||||
# the dotfile destination
|
||||
tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d`
|
||||
#echo "dotfile destination: ${tmpd}"
|
||||
|
||||
clear_on_exit "${tmps}"
|
||||
clear_on_exit "${tmpd}"
|
||||
|
||||
##
|
||||
# existing files
|
||||
##
|
||||
|
||||
# file
|
||||
echo "exists-original" > ${tmps}/dotfiles/exists
|
||||
chmod 644 ${tmps}/dotfiles/exists
|
||||
echo "exists" > ${tmpd}/exists
|
||||
chmod 700 ${tmpd}/exists
|
||||
|
||||
# link
|
||||
echo "existslink" > ${tmps}/dotfiles/existslink
|
||||
chmod 700 ${tmps}/dotfiles/existslink
|
||||
ln -s ${tmps}/dotfiles/existslink ${tmpd}/existslink
|
||||
|
||||
# directory
|
||||
mkdir -p ${tmps}/dotfiles/direxists
|
||||
echo "f1-original" > ${tmps}/dotfiles/direxists/f1
|
||||
mkdir -p ${tmpd}/direxists
|
||||
echo "f1" > ${tmpd}/direxists/f1
|
||||
chmod 700 ${tmpd}/direxists/f1
|
||||
chmod 700 ${tmpd}/direxists
|
||||
|
||||
# link children
|
||||
mkdir -p ${tmps}/dotfiles/linkchildren
|
||||
echo "f1-original" > ${tmps}/dotfiles/linkchildren/f1
|
||||
chmod 700 ${tmps}/dotfiles/linkchildren/f1
|
||||
mkdir -p ${tmps}/dotfiles/linkchildren/d1
|
||||
chmod 700 ${tmps}/dotfiles/linkchildren/d1
|
||||
echo "f2-original" > ${tmps}/dotfiles/linkchildren/d1/f2
|
||||
chmod 700 ${tmps}/dotfiles/linkchildren/d1/f2
|
||||
|
||||
mkdir -p ${tmpd}/linkchildren
|
||||
chmod 700 ${tmpd}/linkchildren
|
||||
echo "f1" > ${tmpd}/linkchildren/f1
|
||||
mkdir -p ${tmpd}/linkchildren/d1
|
||||
echo "f2" > ${tmpd}/linkchildren/d1/f2
|
||||
|
||||
# no mode
|
||||
echo 'nomode-original' > ${tmps}/dotfiles/nomode
|
||||
echo 'nomode' > ${tmpd}/nomode
|
||||
|
||||
# create the config file
|
||||
cfg="${tmps}/config.yaml"
|
||||
cat > ${cfg} << _EOF
|
||||
config:
|
||||
backup: true
|
||||
create: true
|
||||
dotpath: dotfiles
|
||||
force_chmod: true
|
||||
dotfiles:
|
||||
f_exists:
|
||||
src: exists
|
||||
dst: ${tmpd}/exists
|
||||
chmod: preserve
|
||||
f_existslink:
|
||||
src: existslink
|
||||
dst: ${tmpd}/existslink
|
||||
chmod: preserve
|
||||
link: absolute
|
||||
d_direxists:
|
||||
src: direxists
|
||||
dst: ${tmpd}/direxists
|
||||
chmod: preserve
|
||||
d_linkchildren:
|
||||
src: linkchildren
|
||||
dst: ${tmpd}/linkchildren
|
||||
chmod: preserve
|
||||
link: link_children
|
||||
f_nomode:
|
||||
src: nomode
|
||||
dst: ${tmpd}/nomode
|
||||
chmod: preserve
|
||||
profiles:
|
||||
p1:
|
||||
dotfiles:
|
||||
- f_exists
|
||||
- f_existslink
|
||||
- d_direxists
|
||||
- d_linkchildren
|
||||
- f_nomode
|
||||
_EOF
|
||||
#cat ${cfg}
|
||||
|
||||
echo "update"
|
||||
cd ${ddpath} | ${bin} update -f -c ${cfg} -p p1 -V ${tmpd}/exists
|
||||
cd ${ddpath} | ${bin} update -f -c ${cfg} -p p1 -V ${tmpd}/existslink
|
||||
cd ${ddpath} | ${bin} update -f -c ${cfg} -p p1 -V ${tmpd}/direxists
|
||||
cd ${ddpath} | ${bin} update -f -c ${cfg} -p p1 -V ${tmpd}/linkchildren
|
||||
cd ${ddpath} | ${bin} update -f -c ${cfg} -p p1 -V ${tmpd}/nomode
|
||||
|
||||
count=$(cat ${cfg} | grep chmod | grep -v 'chmod: preserve\|force_chmod' | wc -l)
|
||||
echo "${count}"
|
||||
[ "${count}" != "0" ] && echo "chmod altered" && exit 1
|
||||
|
||||
echo "OK"
|
||||
exit 0
|
||||
9
tests-ng/helpers
vendored
9
tests-ng/helpers
vendored
@@ -101,3 +101,12 @@ if [[ $OSTYPE == 'darwin'* ]]; then
|
||||
export -f readlink
|
||||
export -f realpath
|
||||
fi
|
||||
|
||||
# workdir tricks
|
||||
# when tests are called without using the
|
||||
# top level tests.sh script which sets the workdir
|
||||
if [ -z "${DOTDROP_WORKDIR}" ]; then
|
||||
_workdir="/tmp/dotdrop-test-workdir"
|
||||
export DOTDROP_WORKDIR="${_workdir}"
|
||||
clear_on_exit "${_workdir}"
|
||||
fi
|
||||
Reference in New Issue
Block a user