1 Commits
Author SHA1 Message Date
bryanpedini 33ebf89748 feat: resolve dependencies in batch trust commands 2026-10-30 21:07:05 +01:00
4 changed files with 91 additions and 25 deletions

No files matched your search

+6 -3
View File
@@ -130,8 +130,8 @@ package (pacman backend with paru or yay, see below) and a space on every other
One line, whitespace-separated tokens; `N` is the number in brackets. Numbers are
stable for the whole session (rows that leave the list leave a gap). Every token
of a line is validated first: a line with an unknown token or a number that is not
listed prints an error, runs nothing, and the prompt comes back.
of a line is checked for syntax first: an unknown token rejects the line. A number
that is not listed prints an error and the remaining commands still run.
| token | action |
|-------|--------|
@@ -147,7 +147,10 @@ listed prints an error, runs nothing, and the prompt comes back.
| `h`, `?` | help |
| end of input | abort without upgrading (exit 2) |
Several tokens may share a line (`1? 3- 4+`); they run left to right. With `--all`
Several tokens may share a line (`1? 3- 4+`); they run left to right. Consecutive
`N+` commands are processed together: selected packages required by another selected
package are recorded as dependencies, regardless of their order. `*+` works the same
way. With `--all`
trusted packages are listed too, marked with `=`. When purges leave nothing to upgrade the
session ends by itself (exit 0).
+5 -2
View File
@@ -125,6 +125,9 @@ Refreshing package lists... done.
```
Command grammar (one line, whitespace-separated tokens, each `N` is the number in brackets):
Unknown tokens reject the line; nonexistent numbers report an error and are skipped.
Consecutive `N+` commands and `*+` trust the selected dependency roots, recording
other selected packages in their closures as dependencies regardless of input order.
| token | action |
|--------|---------------------------------------------------------------------------|
@@ -153,8 +156,8 @@ On AUR rows `N?`, `N+` and `Nx` act through pacman like on any installed package
`pacman -Qi` with `<helper> -Sia PKG`; `N-` excludes the row through the helper's `--ignore`.
Numbers are stable for the whole session (removed rows leave a gap, never renumber).
All tokens of a line are validated first: an unknown token or a number that is not listed
prints an error, runs nothing of that line and re-prompts. EOF on stdin aborts without
Syntax is validated first; an unknown token rejects the line. Numbers that are not listed
print an error and are skipped while the remaining commands run. EOF on stdin aborts without
upgrading (exit 2) so a piped run can never upgrade by accident unless it sends an empty
line. When purges leave no row at all, the session ends ("Nothing left to upgrade.", exit 0).
+40 -18
View File
@@ -6,7 +6,7 @@
# and zypper: list what would be upgraded, interrogate every package, remember the
# ones you trust, then run the native upgrade (with per-run exclusions).
#
# Version: 0.1.0
# Version: 0.2.0
#
# Specification: docs/DESIGN.md. Single file, bash >= 4.3, no runtime dependency
# beyond the distro's package manager and coreutils/sed/grep (awk only on the apt
@@ -23,7 +23,7 @@ export LC_ALL=C DEBIAN_FRONTEND=noninteractive
# never read from or written to a file (no HISTFILE; `history -r`/`-w`/`-a` are never used).
unset HISTFILE
readonly SU_VERSION=0.1.0
readonly SU_VERSION=0.2.0
readonly SU_PROG=skeptical-updater
readonly SU_STATE_HEADER='# skeptical-updater trusted packages. Columns: backend package reason added(UTC ISO-8601)'
readonly SU_PROMPT_HELP='<enter> upgrade | N? what is it | N?? full info | N- exclude this run | Nx purge now | N+ trust forever'
@@ -2712,9 +2712,28 @@ session_purge() {
}
session_trust() { # session_trust IDX...
local i now
now=$(now_utc)
local i j d now covered
local -a selected=() roots=()
local -A seen=() reach=()
for i in "$@"; do
[[ -z ${seen[$i]+x} ]] || continue
seen[$i]=1
selected+=("$i")
done
for i in "${selected[@]}"; do
closure_of "${R_NAME[i]}"
for d in ${CLOSURE[@]+"${CLOSURE[@]}"}; do reach["$i,$d"]=1; done
done
for i in "${selected[@]}"; do
covered=0
for j in "${selected[@]}"; do
[[ $i != "$j" && -n ${reach[$j,${R_NAME[i]}]+x} ]] || continue
if [[ -z ${reach[$i,${R_NAME[j]}]+x} ]] || ((j < i)); then covered=1; break; fi
done
((covered)) || roots+=("$i")
done
now=$(now_utc)
for i in "${roots[@]}"; do
trust_pkg "${R_NAME[i]}" "$now"
R_EXCL[i]=0
done
@@ -2747,10 +2766,10 @@ session_toggle_all() {
SESSION_PROCEED=0
UPGRADE_FAILED_MSG= # set by <be>_upgrade when the failing step is not the package manager's own
# session_line LINE: validate every token first (a typo never executes half a line), then run them
session_line() {
local tok n op i
local -a toks=()
local -a toks=() batch=()
local -A initial=()
local re='^([0-9]{1,6})(\?\?|\?|-|x|\+)$'
read -r -a toks <<<"$1" || true
if ((${#toks[@]} == 0)); then
@@ -2758,13 +2777,7 @@ session_line() {
return 0
fi
for tok in "${toks[@]}"; do
if [[ $tok =~ $re ]]; then
n=$((10#${BASH_REMATCH[1]}))
if ! num_row "$n"; then
err "no package [$n] in the list (l to redraw)"
return 0
fi
else
if [[ ! $tok =~ $re ]]; then
case $tok in
'*+' | '*-' | l | q | h | '?') ;;
*)
@@ -2774,15 +2787,23 @@ session_line() {
esac
fi
done
for ((n = 1; n <= N_MAX; n++)); do
if num_row "$n"; then initial[$n]=$REPLY; fi
done
for tok in "${toks[@]}"; do
if [[ $tok =~ $re ]]; then
n=$((10#${BASH_REMATCH[1]}))
op=${BASH_REMATCH[2]}
if ! num_row "$n"; then
err "package [$n] has left the list; ignoring the rest of the line"
return 0
if [[ -z ${initial[$n]+x} ]] || ((R_GONE[${initial[$n]:-0}])); then
err "no package [$n] in the list (l to redraw)"
continue
fi
i=$REPLY
i=${initial[$n]}
if [[ $op == + ]]; then
batch+=("$i")
continue
fi
if ((${#batch[@]})); then session_trust "${batch[@]}"; batch=(); fi
case $op in
'?') print_info "${R_NAME[i]}" "${R_OLD[i]}" || true ;;
'??') print_full_info "${R_NAME[i]}" "${R_OLD[i]}" || true ;;
@@ -2802,9 +2823,9 @@ session_line() {
exit 0
fi
;;
+) session_trust "$i" ;;
esac
else
if ((${#batch[@]})); then session_trust "${batch[@]}"; batch=(); fi
case $tok in
'*+')
local -a idx=()
@@ -2823,6 +2844,7 @@ session_line() {
esac
fi
done
if ((${#batch[@]})); then session_trust "${batch[@]}"; fi
}
cmd_interactive() {
+40 -2
View File
@@ -292,11 +292,49 @@ UPGRADE='apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--forc
run skup --no-refresh <<<$'*+\nl\nq'
assert_success
assert_line 'trusted su-dummy-a (+1 dependency: su-dummy-b)'
assert_line 'trusted su-dummy-b'
assert_line 'trusted su-dummy-c'
assert_line '3 upgradable, 3 trusted (hidden, use --all to show), 0 to review:'
run cut -f2,3 "$STATE_FILE"
assert_line "su-dummy-b${T}user"
assert_line "su-dummy-b${T}dep:su-dummy-a"
}
@test "consecutive trust commands keep dependencies under their selected root in either order" {
for line in '1+ 2+ 3+' '2+ 3+ 1+'; do
rm -rf "$STATE"
run skup --no-refresh <<<"$line"$'\nq'
assert_success
assert_line 'trusted su-dummy-a (+1 dependency: su-dummy-b)'
assert_line 'trusted su-dummy-c'
refute_output --partial 'no package ['
run cut -f2,3 "$STATE_FILE"
assert_line "su-dummy-a${T}user"
assert_line "su-dummy-b${T}dep:su-dummy-a"
assert_line "su-dummy-c${T}user"
done
}
@test "invalid numbers do not stop other commands on the line" {
run skup --no-refresh <<<$'9+ 2- 0? 3+\nq'
assert_success
assert_output --partial 'no package [9] in the list'
assert_output --partial 'no package [0] in the list'
assert_line 'excluded from this run: su-dummy-b'
assert_line 'trusted su-dummy-c'
}
@test "a package removed by a purge does not stop later commands" {
FAKE_APT_AUTO_ORPHANS=su-dummy-a:su-dummy-b run skup --no-refresh <<<$'1x 2? 3-\ny\nq'
assert_success
assert_output --partial 'no package [2] in the list'
assert_line 'excluded from this run: su-dummy-c'
}
@test "trusting a dependency and its root with --all keeps the dependency implicit" {
run skup --no-refresh --all <<<$'2+ 1+\nq'
assert_success
run cut -f2,3 "$STATE_FILE"
assert_line "su-dummy-a${T}user"
assert_line "su-dummy-b${T}dep:su-dummy-a"
}
@test "*- excludes every listed package; *- again re-includes them" {