1
0
mirror of https://github.com/TheLocehiliosan/yadm synced 2025-06-06 01:23:59 +00:00

Automatically exclude alt links and template files

unless yadm.auto-exclude is set to false (#234, #465).

Alt files exclude pattern will be written to $GIT_DIR/info/exclude.yadm-alt and
encrypt files exclude patthern to ...yadm-encrypt. Then these two files will be
merged together and added to $GIT_DIR/info/exclude whenever one of them has
changed.
This commit is contained in:
Erik Flodin 2025-02-27 21:18:07 +01:00
parent 6726730701
commit d4796108f4
No known key found for this signature in database
GPG Key ID: 420A7C865EE3F85F
4 changed files with 110 additions and 54 deletions

View File

@ -217,6 +217,29 @@ def test_auto_alt(runner, yadm_cmd, paths, autoalt):
assert str(paths.work.join(source_file)) not in linked 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") @pytest.mark.usefixtures("ds1_copy")
def test_stale_link_removal(runner, yadm_cmd, paths): def test_stale_link_removal(runner, yadm_cmd, paths):
"""Stale links to alternative files are removed """Stale links to alternative files are removed

View File

@ -9,7 +9,12 @@ import pytest
def test_exclude_encrypted(runner, tmpdir, yadm, encrypt_exists, auto_exclude, exclude): def test_exclude_encrypted(runner, tmpdir, yadm, encrypt_exists, auto_exclude, exclude):
"""Test exclude_encrypted()""" """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";}' config_function = 'function config() { echo "false";}'
if auto_exclude: if auto_exclude:

113
yadm
View File

@ -691,6 +691,11 @@ function set_local_alt_values() {
} }
function alt_linking() { function alt_linking() {
local -a exclude=()
local log="debug"
[ -n "$loud" ] && log="echo"
local -i index local -i index
for ((index = 0; index < ${#alt_targets[@]}; ++index)); do for ((index = 0; index < ${#alt_targets[@]}; ++index)); do
local target="${alt_targets[$index]}" local target="${alt_targets[$index]}"
@ -709,17 +714,17 @@ function alt_linking() {
if [[ -n "$template_processor" ]]; then if [[ -n "$template_processor" ]]; then
template "$template_processor" "$source" "$target" template "$template_processor" "$source" "$target"
elif [[ "$do_copy" -eq 1 ]]; then elif [[ "$do_copy" -eq 1 ]]; then
debug "Copying $source to $target" $log "Copying $source to $target"
[[ -n "$loud" ]] && echo "Copying $source to $target"
cp -f "$source" "$target" cp -f "$source" "$target"
else else
debug "Linking $source to $target" $log "Linking $source to $target"
[[ -n "$loud" ]] && echo "Linking $source to $target"
ln_relative "$source" "$target" ln_relative "$source" "$target"
fi fi
exclude+=("${target#"$YADM_WORK"}")
done done
update_exclude alt "${exclude[@]}"
} }
function ln_relative() { function ln_relative() {
@ -1473,18 +1478,35 @@ function version() {
# ****** Utility Functions ****** # ****** Utility Functions ******
function exclude_encrypted() { function update_exclude() {
local auto_exclude local auto_exclude
auto_exclude=$(config --bool yadm.auto-exclude) auto_exclude=$(config --bool yadm.auto-exclude)
[ "$auto_exclude" == "false" ] && return 0 [ "$auto_exclude" == "false" ] && return 0
# do nothing if there is no YADM_ENCRYPT local exclude_path="${YADM_REPO}/info/exclude"
[ -e "$YADM_ENCRYPT" ] || return 0 local newline=$'\n'
readonly exclude_path="${YADM_REPO}/info/exclude" local part_path="$exclude_path.yadm-$1"
readonly newline=$'\n' local part_str
readonly exclude_flag="# yadm-auto-excludes" 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}" local exclude_header="${exclude_flag}${newline}"
exclude_header="${exclude_header}# This section is managed by yadm." 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}# Any edits below will be lost."
exclude_header="${exclude_header}${newline}" 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 # read info/exclude
local unmanaged="" local unmanaged=""
local managed="" local managed=""
@ -1532,14 +1530,39 @@ function exclude_encrypted() {
done <"$exclude_path" done <"$exclude_path"
fi 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}" debug "Updating ${exclude_path}"
assert_parent "$exclude_path" cat >"$exclude_path" <<<"${unmanaged}${exclude_header}${exclude_str}"
printf "%s" "${unmanaged}${exclude_header}${encrypt_data}" >"$exclude_path"
fi fi
return 0 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() { function query_distro() {
@ -1956,19 +1979,11 @@ function parse_encrypt() {
local pattern local pattern
while IFS='' read -r pattern || [ -n "$pattern" ]; do while IFS='' read -r pattern || [ -n "$pattern" ]; do
case ${pattern:0:1} in if [ "${pattern:0:1}" = "!" ]; then
\#)
# Ignore comments
;;
!)
exclude+=("--exclude=/${pattern:1}") exclude+=("--exclude=/${pattern:1}")
;; elif ! [[ $pattern =~ ^[[:blank:]]*(#|$) ]]; then
*)
if ! [[ $pattern =~ ^[[:blank:]]*(#|$) ]]; then
include+=("$pattern") include+=("$pattern")
fi fi
;;
esac
done <"$YADM_ENCRYPT" done <"$YADM_ENCRYPT"
if [ ${#include[@]} -gt 0 ]; then if [ ${#include[@]} -gt 0 ]; then

15
yadm.1
View File

@ -363,7 +363,8 @@ you may still run "yadm alt" manually to create the alternate links. This
feature is enabled by default. feature is enabled by default.
.TP .TP
.B yadm.auto-exclude .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 . .IR $HOME/.config/yadm/encrypt .
This feature is enabled by default. This feature is enabled by default.
.TP .TP
@ -614,6 +615,12 @@ configuration.
Even if disabled, links can be manually created by running Even if disabled, links can be manually created by running
.BR "yadm alt" . .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 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 repository). To use alternate symlinks using class, you must set the value of
class using the configuration class using the configuration
@ -748,6 +755,12 @@ would look like:
<%+ whatever.extra %> <%+ whatever.extra %>
<% fi -%> <% 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 .SH ENCRYPTION
It can be useful to manage confidential files, like SSH or GPG keys, across It can be useful to manage confidential files, like SSH or GPG keys, across