1
0
mirror of https://github.com/TheLocehiliosan/yadm synced 2025-06-02 15:43:59 +00:00

Add support for negative alt conditions (#522)

This commit is contained in:
AaronYoung5 2025-03-02 22:05:15 +01:00 committed by Erik Flodin
parent d4796108f4
commit 9ff5e09650
No known key found for this signature in database
GPG Key ID: 420A7C865EE3F85F
3 changed files with 109 additions and 15 deletions

View File

@ -321,3 +321,76 @@ def test_underscores_and_upper_case_in_distro_and_family(runner, yadm):
assert run.success
assert run.err == ""
assert run.out == expected
def test_negative_class_condition(runner, yadm):
"""Test negative class condition: returns 0 when matching and proper score when not matching."""
script = f"""
YADM_TEST=1 source {yadm}
local_class="testclass"
local_classes=("testclass")
# 0
score=0
score_file "filename##~class.testclass" "dest"
echo "score: $score"
# 16
score=0
score_file "filename##~class.badclass" "dest"
echo "score2: $score"
# 16
score=0
score_file "filename##~c.badclass" "dest"
echo "score3: $score"
"""
run = runner(command=["bash"], inp=script)
assert run.success
output = run.out.strip().splitlines()
assert output[0] == "score: 0"
assert output[1] == "score2: 16"
assert output[2] == "score3: 16"
def test_negative_combined_conditions(runner, yadm):
"""Test negative conditions for multiple alt types: returns 0 when matching and proper score when not matching."""
script = f"""
YADM_TEST=1 source {yadm}
local_class="testclass"
local_classes=("testclass")
local_distro="testdistro"
# (0) + (0) = 0
score=0
score_file "filename##~class.testclass,~distro.testdistro" "dest"
echo "score: $score"
# (1000 + 16) + (1000 + 4) = 2020
score=0
score_file "filename##class.testclass,distro.testdistro" "dest"
echo "score2: $score"
# 0 (negated class condition)
score=0
score_file "filename##~class.badclass,~distro.testdistro" "dest"
echo "score3: $score"
# (1000 + 16) + (4) = 1020
score=0
score_file "filename##class.testclass,~distro.baddistro" "dest"
echo "score4: $score"
# (1000 + 16) + (16) = 1032
score=0
score_file "filename##class.testclass,~class.badclass" "dest"
echo "score5: $score"
"""
run = runner(command=["bash"], inp=script)
assert run.success
output = run.out.strip().splitlines()
assert output[0] == "score: 0"
assert output[1] == "score2: 2020"
assert output[2] == "score3: 0"
assert output[3] == "score4: 1020"
assert output[4] == "score5: 1032"

27
yadm
View File

@ -180,32 +180,39 @@ function score_file() {
local value=${field#*.}
[ "$field" = "$label" ] && value="" # when .value is omitted
# Check for negative condition prefix (e.g., "~<label>")
local negate=0
if [ "${label:0:1}" = "~" ]; then
negate=1
label="${label:1}"
fi
shopt -s nocasematch
local -i delta=-1
local -i delta=$((negate ? 1 : -1))
case "$label" in
default)
delta=0
;;
a | arch)
[[ "$value" = "$local_arch" ]] && delta=1
[[ "$value" = "$local_arch" ]] && delta=1 || delta=-1
;;
o | os)
[[ "$value" = "$local_system" ]] && delta=2
[[ "$value" = "$local_system" ]] && delta=2 || delta=-2
;;
d | distro)
[[ "${value// /_}" = "${local_distro// /_}" ]] && delta=4
[[ "${value// /_}" = "${local_distro// /_}" ]] && delta=4 || delta=-4
;;
f | distro_family)
[[ "${value// /_}" = "${local_distro_family// /_}" ]] && delta=8
[[ "${value// /_}" = "${local_distro_family// /_}" ]] && delta=8 || delta=-8
;;
c | class)
in_list "$value" "${local_classes[@]}" && delta=16
in_list "$value" "${local_classes[@]}" && delta=16 || delta=-16
;;
h | hostname)
[[ "$value" = "$local_host" ]] && delta=32
[[ "$value" = "$local_host" ]] && delta=32 || delta=-32
;;
u | user)
[[ "$value" = "$local_user" ]] && delta=64
[[ "$value" = "$local_user" ]] && delta=64 || delta=-64
;;
e | extension)
# extension isn't a condition and doesn't affect the score
@ -231,11 +238,13 @@ function score_file() {
esac
shopt -u nocasematch
((negate)) && delta=$((-delta))
if ((delta < 0)); then
score=0
return
fi
score=$((score + 1000 + delta))
((negate)) || delta=$((delta + 1000))
score=$((score + delta))
done
record_score "$score" "$target" "$source" "$template_processor"

24
yadm.1
View File

@ -476,9 +476,11 @@ commas.
Each condition is an attribute/value pair, separated by a period. Some
conditions do not require a "value", and in that case, the period and value can
be omitted. Most attributes can be abbreviated as a single letter.
be omitted. Most attributes can be abbreviated as a single letter. Prefixing an
attribute with "~" negates the condition, meaning the condition is considered
only if the attribute/value pair evaluates to false.
<attribute>[.<value>]
[~]<attribute>[.<value>]
.BR NOTE :
Value is compared case-insensitive.
@ -555,9 +557,10 @@ symbolic links will be created for the most appropriate version.
The "most appropriate" version is determined by calculating a score for each
version of a file. A template is always scored higher than any symlink
condition. The number of conditions is the next largest factor in scoring.
Files with more conditions will always be favored. Any invalid condition will
disqualify that file completely.
condition. The number of conditions is the next largest factor in scoring;
files with more conditions will always be favored. Negative conditions (prefixed
with "~") are scored only relative to the number of non-negated conditions.
Any invalid condition will disqualify that file completely.
If you don't care to have all versions of alternates stored in the same
directory as the generated symlink, you can place them in the
@ -576,6 +579,7 @@ files are managed by yadm's repository:
- $HOME/path/example.txt##os.Linux
- $HOME/path/example.txt##os.Linux,hostname.host1
- $HOME/path/example.txt##os.Linux,hostname.host2
- $HOME/path/example.txt##class.Work,~os.Darwin
If running on a Macbook named "host2",
yadm will create a symbolic link which looks like this:
@ -598,10 +602,18 @@ If running on a Solaris server, the link will use the default version:
.IR $HOME/path/example.txt " -> " $HOME/path/example.txt##default
If running on a system, with class set to "Work", the link will be:
If running on a Macbook with class set to "Work", the link will be:
.IR $HOME/path/example.txt " -> " $HOME/path/example.txt##class.Work
Negative conditions are supported via the "~" prefix. If again running on a system
with class set to "Work", but instead within Windows Subsystem for Linux, where the
os is reported as WSL, the link will be:
.IR $HOME/path/example.txt " -> " $HOME/path/example.txt##class.Work,~os.Darwin
Negative conditions use the same weight which corresponds to the attached attribute.
If no "##default" version exists and no files have valid conditions, then no
link will be created.