diff --git a/docs/config/config-file.md b/docs/config/config-file.md index 763177f..2c93dde 100644 --- a/docs/config/config-file.md +++ b/docs/config/config-file.md @@ -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-` for directories and `666-` 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: diff --git a/dotdrop/installer.py b/dotdrop/installer.py index 775eb70..8fabee0 100644 --- a/dotdrop/installer.py +++ b/dotdrop/installer.py @@ -180,40 +180,52 @@ class Installer: 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, + 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" + """ 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 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, @@ -594,8 +606,9 @@ class Installer: 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, @@ -604,6 +617,8 @@ class Installer: # error occured return res, err + self._apply_chmod_after_install(fpath, fdst, ret, err) + if res: # something got installed ret = True, None diff --git a/tests-ng/chmod-install-dir.sh b/tests-ng/chmod-install-dir.sh new file mode 100755 index 0000000..6576952 --- /dev/null +++ b/tests-ng/chmod-install-dir.sh @@ -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