Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Added support for negative alt condition #522

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions test/test_unit_score_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,3 +321,75 @@ 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}
score=0
local_class="testclass"
local_classes=("testclass")

# 0
score_file "filename##~class.testclass" "dest"
echo "score: $score"

# 1000 + 16
score=0
score_file "filename##~class.badclass" "dest"
echo "score2: $score"

# 0
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: 1016"
assert output[2] == "score3: 1016"

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}
score=0
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 * 2 + 16 + 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 + 1000 + 4 = 2020
score=0
score_file "filename##class.testclass,~distro.baddistro" "dest"
echo "score4: $score"

# 1000 + 16 + 1000 + 16 = 2032
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: 2020"
assert output[4] == "score5: 2032"
22 changes: 15 additions & 7 deletions yadm
Original file line number Diff line number Diff line change
Expand Up @@ -179,32 +179,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" == ~* ]]; then
negate=1
label="${label:1}"
fi

shopt -s nocasematch
local -i delta=-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
Expand All @@ -230,6 +237,7 @@ function score_file() {
esac
shopt -u nocasematch

(( negate )) && delta=$((-delta))
if ((delta < 0)); then
score=0
return
Expand Down
62 changes: 61 additions & 1 deletion yadm.1
Original file line number Diff line number Diff line change
Expand Up @@ -489,16 +489,18 @@ These are the supported attributes, in the order of the weighted precedence:
Valid when the value matches a supported template processor.
See the TEMPLATES section for more details.
.TP
.BR user ,\ u
.BR user ,\ u\
Valid if the value matches the current user.
Current user is calculated by running
.BR "id \-u \-n" .
.BR [ weight\ = \ 64]
.TP
.BR hostname ,\ h
Valid if the value matches the short hostname.
Hostname is calculated by running
.BR "uname \-n" ,
and trimming off any domain.
.BR [ weight\ = \ 32]
.TP
.BR class ,\ c
Valid if the value matches the
Expand All @@ -508,29 +510,34 @@ Class must be manually set using
.BR "yadm config local.class <class>" .
See the CONFIGURATION section for more details about setting
.BR local.class .
.BR [ weight\ = \ 16]
.TP
.BR distro ,\ d
Valid if the value matches the distro.
Distro is calculated by running
.B "lsb_release \-si"
or by inspecting the ID from
.BR "/etc/os-release" .
.BR [ weight\ = \ 8]
.TP
.BR distro_family ,\ f
Valid if the value matches the distro family.
Distro family is calculated by inspecting the ID_LIKE line from
.B "/etc/os-release"
(or ID if no ID_LIKE line is found).
.BR [ weight\ = \ 4]
.TP
.BR os ,\ o
Valid if the value matches the OS.
OS is calculated by running
.BR "uname \-s" .
.BR [ weight\ = \ 2]
.TP
.BR arch ,\ a
Valid if the value matches the architecture.
Architecture is calculated by running
.BR "uname \-m" .
.BR [ weight\ = \ 1]
.TP
.B default
Valid when no other alternate is valid.
Expand Down Expand Up @@ -575,6 +582,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##~os.Linux,~os.Darwin,~os.SunOS

If running on a Macbook named "host2",
yadm will create a symbolic link which looks like this:
Expand All @@ -601,9 +609,61 @@ If running on a system, 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 running 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##~os.Linux,~os.Darwin,~os.SunOS

Negative conditions use the weight which corresponds to the attached attribute.

If no "##default" version exists and no files have valid conditions, then no
link will be created.

Weighting for multiple valid alternatives is calculated using the weights specified
above. For instance, on a Linux server named "host1" with class set to "Work", the
alternatives will have the following weights:

- $HOME/path/example.txt##default

Used if there are no other valid alternatives.

- $HOME/path/example.txt##class.Work

1064 = (1000 + 64 (class)) * 1 (valid)

- $HOME/path/example.txt##os.Darwin

0 = (1000 + 2 (os)) * 0 (invalid)

- $HOME/path/example.txt##os.Darwin,hostname.host1

0 = (1000 + 2 (os) + 32 (hostname)) * 0 (invalid)

- $HOME/path/example.txt##os.Darwin,hostname.host2

0 = (1000 + 2 (os) + 32 (hostname)) * 0 (invalid)

- $HOME/path/example.txt##os.Linux

1002 = (1000 + 2 (os)) * 1 (valid)

- $HOME/path/example.txt##os.Linux,hostname.host1

1034 = (1000 + 2 (os) + 32 (hostname)) * 1 (valid)

- $HOME/path/example.txt##os.Linux,hostname.host2

1034 = (1000 + 2 (os) + 32 (hostname)) * 0 (invalid)

- $HOME/path/example.txt##~os.Linux,~os.Darwin,~os.SunOS

1006 = (1000 + 2 (~os) + 2 (~os) + 2 (~os)) * 0 (invalid)

In this case, with a Linux server named "host1" with class set to "Work", the link will
be:

.IR $HOME/path/example.txt " -> " $HOME/path/example.txt##class.Work

Links are also created for directories named this way, as long as they have at
least one yadm managed file within them.

Expand Down