From c17eed5fdff8aea70bf37d955328bf640dd0980c Mon Sep 17 00:00:00 2001 From: Marco Esters Date: Wed, 8 May 2024 14:34:59 -0700 Subject: [PATCH] pkg installer: Fix ownership of files installed into $HOME (#784) * Change permissions for .conda directory * Fix owner of files created by conda init * Ensure that only files from HOME or PREFIX are looped over * Add news items * Test file ownership for pkg installers * Remove install target * Use getpass.getuser instead of os.getlogin * Apply suggestions from code review Co-authored-by: jaimergp * Use bash to update path * Update constructor/osx/update_path.sh Co-authored-by: jaimergp * Change ownership of .condarc * Add conda config files to test files --------- Co-authored-by: jaimergp --- constructor/osx/run_installation.sh | 4 ++- constructor/osx/update_path.sh | 34 ++++++++++++++++++++++-- news/784-pkg-installation-file-ownership | 19 +++++++++++++ tests/test_examples.py | 26 ++++++++++++++++++ 4 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 news/784-pkg-installation-file-ownership diff --git a/constructor/osx/run_installation.sh b/constructor/osx/run_installation.sh index acea8d943..f01e43c5d 100644 --- a/constructor/osx/run_installation.sh +++ b/constructor/osx/run_installation.sh @@ -110,9 +110,11 @@ if ! "$PREFIX/bin/python" -V; then exit 1 fi -# This is unneeded for the default install to ~, but if the user changes the +# This is not needed for the default install to ~, but if the user changes the # install location, the permissions will default to root unless this is done. chown -R "$USER" "$PREFIX" +chown -R "$USER" "${HOME}/.conda" +test -f "${HOME}/.condarc" && chown "${USER}" "${HOME}/.condarc" notify "Done! Installation is available in $PREFIX." diff --git a/constructor/osx/update_path.sh b/constructor/osx/update_path.sh index 0d0ef1378..ec61d8cea 100644 --- a/constructor/osx/update_path.sh +++ b/constructor/osx/update_path.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Copyright (c) 2012-2017 Anaconda, Inc. # All rights reserved. @@ -8,4 +8,34 @@ set -eux PREFIX="$2/__NAME_LOWER__" PREFIX=$(cd "$PREFIX"; pwd) -"$PREFIX/bin/python" -m conda init --all +INIT_FILES=$("$PREFIX/bin/python" -m conda init --all | tee) + +# Just like in run_install.sh, the files generated by the installer +# are owned by root when installed outside of $HOME. So, ownership of +# files modified by conda init must be changed to belong to $USER. +# For shells like fish or powershell, conda init creates subdirectories +# inside $HOME and/or $PREFIX, so ensure that parent directories also +# have the correct owner. +if [[ "${USER}" != "root" ]]; then + echo "Fixing permissions..." + read -r -a MODIFIED_FILES <<< "$(\ + echo "${INIT_FILES}" |\ + awk '/modified/{print $2}' |\ + # Only grab files inside $HOME or $PREFIX. + # All init files should be there, but that may change, and it + # is better to miss files than to have an infinite loop below. + grep -E "^(${HOME}|${PREFIX})"\ + )" + for file in "${MODIFIED_FILES[@]}"; do + while [[ "${file}" != "${HOME}" ]] && [[ "${file}" != "${PREFIX}" ]]; do + # Check just in case the file wasn't created due to flaky conda init + if [[ -f "${file}" ]] || [[ -d "${file}" ]]; then + OWNER=$(stat -f "%u" "${file}" | id -un) + if [[ "${OWNER}" == "root" ]]; then + chown "${USER}" "${file}" + fi + fi + file="${file%/*}" + done + done +fi diff --git a/news/784-pkg-installation-file-ownership b/news/784-pkg-installation-file-ownership new file mode 100644 index 000000000..dc378d05b --- /dev/null +++ b/news/784-pkg-installation-file-ownership @@ -0,0 +1,19 @@ +### Enhancements + +* + +### Bug fixes + +* Fix ownership of files created by the PKG installer outside of `$PREFIX`. (#784) + +### Deprecations + +* + +### Docs + +* + +### Other + +* diff --git a/tests/test_examples.py b/tests/test_examples.py index d06b9e39c..66edb7895 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1,3 +1,4 @@ +import getpass import os import shutil import subprocess @@ -413,8 +414,33 @@ def test_example_noconda(tmp_path, request): @pytest.mark.skipif(sys.platform != "darwin", reason="macOS only") def test_example_osxpkg(tmp_path, request): input_path = _example_path("osxpkg") + ownership_test_files_home = [ + ".bash_profile", + ".conda", + ".condarc", + ".config", + ".config/fish", + ".config/fish/fish.config", + ".config/powershell", + ".config/powershell/profile.ps1", + ".tcshrc", + ".xonshrc", + ".zshrc", + ] + ownership_test_files_home = [Path.home() / file for file in ownership_test_files_home] + # getpass.getuser is more reliable than os.getlogin: + # https://docs.python.org/3/library/os.html#os.getlogin + expected_owner = getpass.getuser() for installer, install_dir in create_installer(input_path, tmp_path): _run_installer(input_path, installer, install_dir, request=request) + expected = {} + found = {} + for file in ownership_test_files_home: + if not file.exists(): + continue + expected[file] = expected_owner + found[file] = file.owner() + assert expected == found def test_example_scripts(tmp_path, request):