diff --git a/test/test_alt.py b/test/test_alt.py index ef421f7..138d609 100644 --- a/test/test_alt.py +++ b/test/test_alt.py @@ -217,6 +217,29 @@ def test_auto_alt(runner, yadm_cmd, paths, autoalt): assert str(paths.work.join(source_file)) not in linked +@pytest.mark.usefixtures("ds1_copy") +@pytest.mark.parametrize("autoexclude", [None, "true", "false"]) +def test_alt_exclude(runner, yadm_cmd, paths, autoexclude): + """Test alt exclude""" + + # set the value of auto-exclude + if autoexclude: + os.system(" ".join(yadm_cmd("config", "yadm.auto-exclude", autoexclude))) + + utils.create_alt_files(paths, "##default") + run = runner(yadm_cmd("alt", "-d")) + assert run.success + + run = runner(yadm_cmd("status", "-z", "-uall", "--ignored")) + assert run.success + assert run.err == "" + status = run.out.split("\0") + + for link_path in TEST_PATHS: + flags = "??" if autoexclude == "false" else "!!" + assert f"{flags} {link_path}" in status + + @pytest.mark.usefixtures("ds1_copy") def test_stale_link_removal(runner, yadm_cmd, paths): """Stale links to alternative files are removed diff --git a/test/test_unit_exclude_encrypted.py b/test/test_unit_exclude_encrypted.py index bbe29fd..1daa891 100644 --- a/test/test_unit_exclude_encrypted.py +++ b/test/test_unit_exclude_encrypted.py @@ -9,7 +9,12 @@ import pytest def test_exclude_encrypted(runner, tmpdir, yadm, encrypt_exists, auto_exclude, exclude): """Test exclude_encrypted()""" - header = "# yadm-auto-excludes\n# This section is managed by yadm.\n# Any edits below will be lost.\n" + header = """\ +# yadm-auto-excludes +# This section is managed by yadm. +# Any edits below will be lost. +# yadm encrypt +""" config_function = 'function config() { echo "false";}' if auto_exclude: diff --git a/yadm b/yadm index 2dbb540..2e8bea2 100755 --- a/yadm +++ b/yadm @@ -691,6 +691,11 @@ function set_local_alt_values() { } function alt_linking() { + local -a exclude=() + + local log="debug" + [ -n "$loud" ] && log="echo" + local -i index for ((index = 0; index < ${#alt_targets[@]}; ++index)); do local target="${alt_targets[$index]}" @@ -709,17 +714,17 @@ function alt_linking() { if [[ -n "$template_processor" ]]; then template "$template_processor" "$source" "$target" elif [[ "$do_copy" -eq 1 ]]; then - debug "Copying $source to $target" - [[ -n "$loud" ]] && echo "Copying $source to $target" - + $log "Copying $source to $target" cp -f "$source" "$target" else - debug "Linking $source to $target" - [[ -n "$loud" ]] && echo "Linking $source to $target" - + $log "Linking $source to $target" ln_relative "$source" "$target" fi + + exclude+=("${target#"$YADM_WORK"}") done + + update_exclude alt "${exclude[@]}" } function ln_relative() { @@ -1473,18 +1478,35 @@ function version() { # ****** Utility Functions ****** -function exclude_encrypted() { +function update_exclude() { local auto_exclude auto_exclude=$(config --bool yadm.auto-exclude) [ "$auto_exclude" == "false" ] && return 0 - # do nothing if there is no YADM_ENCRYPT - [ -e "$YADM_ENCRYPT" ] || return 0 + local exclude_path="${YADM_REPO}/info/exclude" + local newline=$'\n' - readonly exclude_path="${YADM_REPO}/info/exclude" - readonly newline=$'\n' - readonly exclude_flag="# yadm-auto-excludes" + local part_path="$exclude_path.yadm-$1" + local part_str + part_str=$(join_string "$newline" "${@:2}") + + if [ -e "$part_path" ]; then + if [ "$part_str" = "$(<"$part_path")" ]; then + return + fi + + rm -f "$part_path" + elif [ -z "$part_str" ]; then + return + fi + + if [ -n "$part_str" ]; then + assert_parent "$part_path" + cat >"$part_path" <<<"$part_str" + fi + + local exclude_flag="# yadm-auto-excludes" local exclude_header="${exclude_flag}${newline}" exclude_header="${exclude_header}# This section is managed by yadm." @@ -1492,30 +1514,6 @@ function exclude_encrypted() { exclude_header="${exclude_header}# Any edits below will be lost." exclude_header="${exclude_header}${newline}" - # read encrypt - local encrypt_data="" - local pattern - while IFS='' read -r pattern || [ -n "$pattern" ]; do - case ${pattern:0:1} in - \#) - pattern="" - ;; - !) - # Prepend / to the pattern so that it matches the same files as in - # parse_encrypt (i.e. only from the root) - pattern="!/${pattern:1}$newline" - ;; - *) - if ! [[ $pattern =~ ^[[:blank:]]*(#|$) ]]; then - pattern="/$pattern$newline" - else - pattern="" - fi - ;; - esac - encrypt_data="${encrypt_data}${pattern}" - done <"$YADM_ENCRYPT" - # read info/exclude local unmanaged="" local managed="" @@ -1532,14 +1530,39 @@ function exclude_encrypted() { done <"$exclude_path" fi - if [ "${exclude_header}${encrypt_data}" != "$managed" ]; then + local exclude_str="" + for suffix in alt encrypt; do + if [ -e "${exclude_path}.yadm-$suffix" ]; then + local header="# yadm $suffix$newline" + exclude_str="$exclude_str$header$(<"$exclude_path".yadm-"$suffix")" + fi + done + + if [ "${exclude_header}${exclude_str}${newline}" != "$managed" ]; then debug "Updating ${exclude_path}" - assert_parent "$exclude_path" - printf "%s" "${unmanaged}${exclude_header}${encrypt_data}" >"$exclude_path" + cat >"$exclude_path" <<<"${unmanaged}${exclude_header}${exclude_str}" fi return 0 +} +function exclude_encrypted() { + local -a exclude=() + + if [ -r "$YADM_ENCRYPT" ]; then + local pattern + while IFS='' read -r pattern || [ -n "$pattern" ]; do + # Prepend / to the pattern so that it matches the same files as in + # parse_encrypt (i.e. only from the root) + if [ "${pattern:0:1}" = "!" ]; then + exclude+=("!/${pattern:1}") + elif ! [[ $pattern =~ ^[[:blank:]]*(#|$) ]]; then + exclude+=("/$pattern") + fi + done <"$YADM_ENCRYPT" + fi + + update_exclude encrypt "${exclude[@]}" } function query_distro() { @@ -1956,19 +1979,11 @@ function parse_encrypt() { local pattern while IFS='' read -r pattern || [ -n "$pattern" ]; do - case ${pattern:0:1} in - \#) - # Ignore comments - ;; - !) - exclude+=("--exclude=/${pattern:1}") - ;; - *) - if ! [[ $pattern =~ ^[[:blank:]]*(#|$) ]]; then - include+=("$pattern") - fi - ;; - esac + if [ "${pattern:0:1}" = "!" ]; then + exclude+=("--exclude=/${pattern:1}") + elif ! [[ $pattern =~ ^[[:blank:]]*(#|$) ]]; then + include+=("$pattern") + fi done <"$YADM_ENCRYPT" if [ ${#include[@]} -gt 0 ]; then diff --git a/yadm.1 b/yadm.1 index caa37de..52ef994 100644 --- a/yadm.1 +++ b/yadm.1 @@ -363,7 +363,8 @@ you may still run "yadm alt" manually to create the alternate links. This feature is enabled by default. .TP .B yadm.auto-exclude -Disable the automatic exclusion of patterns defined in +Disable the automatic exclusion of created alternate links, template files and +patterns defined in .IR $HOME/.config/yadm/encrypt . This feature is enabled by default. .TP @@ -614,6 +615,12 @@ configuration. Even if disabled, links can be manually created by running .BR "yadm alt" . +Created links are automatically added to the repository's +.I info/exclude +file. This can be disabled using the +.I yadm.auto-exclude +configuration. + Class is a special value which is stored locally on each host (inside the local repository). To use alternate symlinks using class, you must set the value of class using the configuration @@ -748,6 +755,12 @@ would look like: <%+ whatever.extra %> <% fi -%> +Created files are automatically added to the repository's +.I info/exclude +file. This can be disabled using the +.I yadm.auto-exclude +configuration. + .SH ENCRYPTION It can be useful to manage confidential files, like SSH or GPG keys, across