Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix chmod on dir dotfile #413

Merged
merged 4 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/config/config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,17 @@ dotfiles:
dst: ~/dir
chmod: 744
f_preserve:
src: preserve
dst: ~/preserve
src: pfile
dst: ~/pfile
chmod: preserve
```

The `chmod` value defines the file permissions in octal notation to apply on dotfiles. If undefined
The `chmod` value defines the file permissions in octal notation to apply to the dotfile. 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
on the filesystem, its permission is not altered during `install` and the `chmod` config value won't
be changed during `update`.

On `import`, the following rules are applied:
Expand Down
87 changes: 55 additions & 32 deletions dotdrop/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ def install(self, templater, src, dst, linktype,
ret, err = self._copy_dir(templater, src, dst,
actionexec=actionexec,
noempty=noempty, ignore=ignore,
is_template=is_template)
is_template=is_template,
chmod=chmod)
else:
ret, err = self._copy_file(templater, src, dst,
actionexec=actionexec,
Expand Down Expand Up @@ -180,40 +181,57 @@ def install(self, templater, src, dst, linktype,
if self.dry:
return self._log_install(ret, err)

# handle chmod
# - on success (r, not err)
# - no change (not r, not err)
# but not when
# - error (not r, err)
# - aborted (not r, err)
# - special keyword "preserve"
self._apply_chmod_after_install(src, dst, ret, err,
chmod=chmod,
force_chmod=force_chmod,
linktype=linktype)

return self._log_install(ret, err)

def _apply_chmod_after_install(self, src, dst, ret, err,
chmod=None,
is_sub=False,
force_chmod=False,
linktype=LinkTypes.NOLINK):
"""
handle chmod after install
- on success (r, not err)
- no change (not r, not err)
but not when
- error (not r, err)
- aborted (not r, err)
- special keyword "preserve"
is_sub is used to specify if the file/dir is
part of a dotfile directory
"""
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 = get_file_perm(src)
self.log.dbg(f'applying chmod {chmod:o} to {dst}')
dstperms = get_file_perm(dst)
if dstperms != chmod:
# apply mode
msg = f'chmod {dst} to {chmod:o}'
if not force_chmod and self.safe and not self.log.ask(msg):
ret = False
err = 'aborted'
else:
if not self.comparing:
self.log.sub(f'chmod {dst} to {chmod:o}')
if chmodit(dst, chmod, debug=self.debug):
ret = True
else:
ret = False
err = 'chmod failed'
else:
if is_sub:
chmod = None
if not apply_chmod:
self.log.dbg('no chmod applied')

return self._log_install(ret, err)
return
if not chmod:
chmod = get_file_perm(src)
self.log.dbg(f'dotfile in dotpath perm: {chmod:o}')
self.log.dbg(f'applying chmod {chmod:o} to {dst}')
dstperms = get_file_perm(dst)
if dstperms != chmod:
# apply mode
msg = f'chmod {dst} to {chmod:o}'
if not force_chmod and self.safe and not self.log.ask(msg):
ret = False
err = 'aborted'
else:
if not self.comparing:
self.log.sub(f'chmod {dst} to {chmod:o}')
if chmodit(dst, chmod, debug=self.debug):
ret = True
else:
ret = False
err = 'chmod failed'

def install_to_temp(self, templater, tmpdir, src, dst,
is_template=True, chmod=None, ignore=None,
Expand Down Expand Up @@ -574,7 +592,8 @@ def _copy_file(self, templater, src, dst,

def _copy_dir(self, templater, src, dst,
actionexec=None, noempty=False,
ignore=None, is_template=True):
ignore=None, is_template=True,
chmod=None):
"""
install src to dst when is a directory

Expand All @@ -594,8 +613,9 @@ def _copy_dir(self, templater, src, dst,
self.log.dbg(f'deploy sub from {dst}: {entry}')
if not os.path.isdir(fpath):
# is file
fdst = os.path.join(dst, entry)
res, err = self._copy_file(templater, fpath,
os.path.join(dst, entry),
fdst,
actionexec=actionexec,
noempty=noempty,
ignore=ignore,
Expand All @@ -604,6 +624,9 @@ def _copy_dir(self, templater, src, dst,
# error occured
return res, err

self._apply_chmod_after_install(fpath, fdst, ret, err,
chmod=chmod, is_sub=True)

if res:
# something got installed
ret = True, None
Expand Down
103 changes: 103 additions & 0 deletions tests-ng/chmod-install-dir.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/usr/bin/env bash
# author: deadc0de6 (https://github.com/deadc0de6)
# Copyright (c) 2023, deadc0de6
#
# test chmod dir sub file on install
#

## start-cookie
set -euo errtrace pipefail
cur=$(cd "$(dirname "${0}")" && pwd)
ddpath="${cur}/../"
PPATH="{PYTHONPATH:-}"
export PYTHONPATH="${ddpath}:${PPATH}"
altbin="python3 -m dotdrop.dotdrop"
if hash coverage 2>/dev/null; then
mkdir -p coverages/
altbin="coverage run -p --data-file coverages/coverage --source=dotdrop -m dotdrop.dotdrop"
fi
bin="${DT_BIN:-${altbin}}"
# shellcheck source=tests-ng/helpers
source "${cur}"/helpers
echo -e "$(tput setaf 6)==> RUNNING $(basename "${BASH_SOURCE[0]}") <==$(tput sgr0)"
## end-cookie

################################################################
# 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
mode=$(stat -L -c '%a' "$1")
[ "${mode}" != "$2" ] && echo "bad mode for $(basename "$1") (${mode} VS expected ${2})" && exit 1
true
}

# 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"

cat > "${cfg}" << _EOF
config:
backup: true
create: true
dotpath: dotfiles
force_chmod: true
dotfiles:
d_dir:
src: dir
dst: ${tmpd}/dir
profiles:
p1:
dotfiles:
- d_dir
_EOF
#cat ${cfg}

mkdir -p "${tmps}"/dotfiles/dir
echo 'file1' > "${tmps}"/dotfiles/dir/file1
chmod 700 "${tmps}"/dotfiles/dir/file1
echo 'file2' > "${tmps}"/dotfiles/dir/file2
chmod 777 "${tmps}"/dotfiles/dir/file2
echo 'file3' > "${tmps}"/dotfiles/dir/file3
chmod 644 "${tmps}"/dotfiles/dir/file3

ls -l "${tmps}"/dotfiles/dir/

# install
echo "install (1)"
cd "${ddpath}" | ${bin} install -c "${cfg}" -f -p p1 -V

has_rights "${tmpd}/dir/file1" "700"
has_rights "${tmpd}/dir/file2" "777"
has_rights "${tmpd}/dir/file3" "644"

# modify
chmod 666 "${tmpd}/dir/file1"
chmod 666 "${tmpd}/dir/file2"
chmod 666 "${tmpd}/dir/file3"

# install
echo "install (2)"
cd "${ddpath}" | ${bin} install -c "${cfg}" -f -p p1 -V

has_rights "${tmpd}/dir/file1" "700"
has_rights "${tmpd}/dir/file2" "777"
has_rights "${tmpd}/dir/file3" "644"

echo "OK"
exit 0
72 changes: 36 additions & 36 deletions tests-ng/chmod-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -61,42 +61,6 @@ clear_on_exit "${tmpd}"

# create the config file
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

echo "exists" > "${tmps}"/dotfiles/exists
chmod 644 "${tmps}"/dotfiles/exists
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
echo "f1" > "${tmps}"/dotfiles/direxists/f1
mkdir -p "${tmpd}"/direxists
echo "f1" > "${tmpd}"/direxists/f1
chmod 644 "${tmpd}"/direxists/f1
chmod 744 "${tmpd}"/direxists

mkdir -p "${tmps}"/dotfiles/linkchildren
echo "f1" > "${tmps}"/dotfiles/linkchildren/f1
mkdir -p "${tmps}"/dotfiles/linkchildren/d1
echo "f2" > "${tmps}"/dotfiles/linkchildren/d1/f2

echo '{{@@ profile @@}}' > "${tmps}"/dotfiles/symlinktemplate

mkdir -p "${tmps}"/dotfiles/symlinktemplatedir
echo "{{@@ profile @@}}" > "${tmps}"/dotfiles/symlinktemplatedir/t

echo 'nomode' > "${tmps}"/dotfiles/nomode

cat > "${cfg}" << _EOF
config:
backup: true
Expand Down Expand Up @@ -170,6 +134,42 @@ profiles:
_EOF
#cat ${cfg}

# create the dotfiles
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

echo "exists" > "${tmps}"/dotfiles/exists
chmod 644 "${tmps}"/dotfiles/exists
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
echo "f1" > "${tmps}"/dotfiles/direxists/f1
mkdir -p "${tmpd}"/direxists
echo "f1" > "${tmpd}"/direxists/f1
chmod 644 "${tmpd}"/direxists/f1
chmod 744 "${tmpd}"/direxists

mkdir -p "${tmps}"/dotfiles/linkchildren
echo "f1" > "${tmps}"/dotfiles/linkchildren/f1
mkdir -p "${tmps}"/dotfiles/linkchildren/d1
echo "f2" > "${tmps}"/dotfiles/linkchildren/d1/f2

echo '{{@@ profile @@}}' > "${tmps}"/dotfiles/symlinktemplate

mkdir -p "${tmps}"/dotfiles/symlinktemplatedir
echo "{{@@ profile @@}}" > "${tmps}"/dotfiles/symlinktemplatedir/t

echo 'nomode' > "${tmps}"/dotfiles/nomode

# install
echo "first install round"
cd "${ddpath}" | ${bin} install -c "${cfg}" -f -p p1 -V
Expand Down