diff --git a/tests/test_init.bats b/tests/test_init.bats index a2b7be1..37a84e5 100755 --- a/tests/test_init.bats +++ b/tests/test_init.bats @@ -131,6 +131,51 @@ SETUP_SKIP_INIT_TRANSCRYPT=1 [ ! "$(git config --get transcrypt.openssl-path)" = 'openssl' ] } +@test "init: --uninstall works when core.attributesFile is a symlink" { + # transcrypt must edit the attributes file through the symlink. Editing with + # `sed -i` errors on BSD/macOS for symlinks ("in-place editing only works for + # regular files") and silently replaces the symlink with a regular file on + # GNU/Linux. + echo '"sensitive_file" filter=crypt diff=crypt merge=crypt' > attributes_target + ln -sf attributes_target attributes_link + git config core.attributesFile "$BATS_TEST_DIRNAME/attributes_link" + + init_transcrypt + [ -L attributes_link ] + + run ../transcrypt --uninstall --yes + [ "$status" -eq 0 ] + + # The symlink must be preserved, not replaced by a regular file... + [ -L attributes_link ] + # ...and the crypt pattern removed from its target + run cat attributes_link + [[ "${output}" != *"filter=crypt"* ]] + + rm -f attributes_link attributes_target +} + +@test "init: --upgrade works when core.attributesFile is a symlink" { + # The upgrade path rewrites "diff=crypt" lines to add "merge=crypt", and must + # likewise edit through the symlink rather than over it. + echo '"sensitive_file" filter=crypt diff=crypt' > attributes_target + ln -sf attributes_target attributes_link + git config core.attributesFile "$BATS_TEST_DIRNAME/attributes_link" + + init_transcrypt + + run ../transcrypt --upgrade --yes + [ "$status" -eq 0 ] + + # The symlink must be preserved, not replaced by a regular file... + [ -L attributes_link ] + # ...and the pattern upgraded in place to include "merge=crypt" + run cat attributes_link + [[ "${output}" = *"diff=crypt merge=crypt"* ]] + + rm -f attributes_link attributes_target +} + @test "init: transcrypt.crypt-dir config setting is applied during init" { # Clear tmp crypt/ directory, in case junk was left there from prior test runs rm -fR /tmp/crypt/ diff --git a/transcrypt b/transcrypt index 43b2aac..1237973 100755 --- a/transcrypt +++ b/transcrypt @@ -143,6 +143,19 @@ die() { exit "$st" } +# Apply a sed script ($1) in-place to a file ($2), following symlinks so a +# symlinked file is edited in place rather than being replaced. We avoid +# `sed -i` because BSD/macOS sed refuses to edit symlinks ("in-place editing +# only works for regular files") while GNU sed would replace the symlink with +# a regular file, breaking the link. Writing back through `cat >` follows the +# symlink and preserves it. +sed_inplace() { + local script="$1" file="$2" tmp + tmp=$(mktemp) + sed "$script" "$file" >"$tmp" && cat "$tmp" >"$file" + rm -f "$tmp" +} + # return context name if $1 has format 'context=name-of-context' else empty string extract_context_name_from_name_value_arg() { local before_last_equals=${1%=*} @@ -956,14 +969,7 @@ uninstall_transcrypt() { fi # remove any defined crypt patterns in gitattributes - case $OSTYPE in - darwin*) - /usr/bin/sed -i '' "/filter=crypt/d" "$GIT_ATTRIBUTES" - ;; - linux*) - sed -i "/filter=crypt/d" "$GIT_ATTRIBUTES" - ;; - esac + sed_inplace "/filter=crypt/d" "$GIT_ATTRIBUTES" if [[ ! $upgrade ]]; then printf 'The transcrypt configuration has been completely removed from the repository.\n' @@ -1037,14 +1043,7 @@ upgrade_transcrypt() { echo "$ORIG_GITATTRIBUTES" >"$GIT_ATTRIBUTES" # Update .gitattributes for transcrypt'ed files to include "merge=crypt" config - case $OSTYPE in - darwin*) - /usr/bin/sed -i '' 's/diff=crypt$/diff=crypt merge=crypt/' "$GIT_ATTRIBUTES" - ;; - linux*) - sed -i 's/diff=crypt$/diff=crypt merge=crypt/' "$GIT_ATTRIBUTES" - ;; - esac + sed_inplace 's/diff=crypt$/diff=crypt merge=crypt/' "$GIT_ATTRIBUTES" printf 'Upgrade is complete\n'