Compare commits
4
Commits
5f90c24b8d
...
a059e37564
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a059e37564 | ||
|
|
6da4312f5c | ||
|
|
57a3dc97d6 | ||
|
|
2aa0a21056 |
No files matched your search
@@ -59,7 +59,7 @@ options:
|
|||||||
none (default: paru, else yay, when installed; other backends ignore it)
|
none (default: paru, else yay, when installed; other backends ignore it)
|
||||||
--no-aur same as --aur-helper=none
|
--no-aur same as --aur-helper=none
|
||||||
-h, --help this help
|
-h, --help this help
|
||||||
-V, --version print the version
|
-v, --version print the version
|
||||||
```
|
```
|
||||||
|
|
||||||
Exit status: `0` ok, `1` usage or backend error, `2` aborted (end of input),
|
Exit status: `0` ok, `1` usage or backend error, `2` aborted (end of input),
|
||||||
@@ -146,13 +146,17 @@ other command suffixes.
|
|||||||
| `N-` | exclude from this run (toggle; `N-` again re-includes); shown with a `-` marker |
|
| `N-` | exclude from this run (toggle; `N-` again re-includes); shown with a `-` marker |
|
||||||
| `Nx` | purge now, with its now-unneeded dependencies: the purge is simulated first and the prompt lists everything it removes (`Purge su-dummy-a now? This also removes: su-dummy-b [y/N]`); the answer is read from the same stdin; the rows of every removed package leave the list. `x` refuses when other installed packages depend on the target, like `pacman -Rns`; purge the dependents first (`not purged: su-dummy-b is required by: su-dummy-a - purge the dependents first`). Rows with old version `-` (new dependencies, not installed) cannot be purged |
|
| `Nx` | purge now, with its now-unneeded dependencies: the purge is simulated first and the prompt lists everything it removes (`Purge su-dummy-a now? This also removes: su-dummy-b [y/N]`); the answer is read from the same stdin; the rows of every removed package leave the list. `x` refuses when other installed packages depend on the target, like `pacman -Rns`; purge the dependents first (`not purged: su-dummy-b is required by: su-dummy-a - purge the dependents first`). Rows with old version `-` (new dependencies, not installed) cannot be purged |
|
||||||
| `N+` | trust forever: write it and its installed dependency closure to the statefile; the row leaves the list. Rows this hides are re-included if they were excluded (a trusted package is upgraded) |
|
| `N+` | trust forever: write it and its installed dependency closure to the statefile; the row leaves the list. Rows this hides are re-included if they were excluded (a trusted package is upgraded) |
|
||||||
|
| `N++` | follow installed dependents recursively and trust each topmost package and its installed dependencies, even when a topmost package is not in the update list |
|
||||||
| `*+` / `*-` | `+` / `-` for every listed package (`*-` is an all-or-nothing toggle: when all are excluded it re-includes them) |
|
| `*+` / `*-` | `+` / `-` for every listed package (`*-` is an all-or-nothing toggle: when all are excluded it re-includes them) |
|
||||||
|
| `a` | show packages trusted during this session again, marked with `=`; packages already trusted before the session remain hidden |
|
||||||
| `l` | redraw the list |
|
| `l` | redraw the list |
|
||||||
| `q` | quit without upgrading (exit 0) |
|
| `q` | quit without upgrading (exit 0) |
|
||||||
| `h`, `?` | help |
|
| `h`, `?` | help |
|
||||||
| end of input | abort without upgrading (exit 2) |
|
| end of input | abort without upgrading (exit 2) |
|
||||||
|
|
||||||
Several tokens may share a line (`1? 3- 4+`); they run left to right. Consecutive
|
Several tokens may share a line (`1? 3- 4+`); they run left to right. Consecutive
|
||||||
|
`Nx` commands such as `5x 30x 41x` are simulated and confirmed together. Another
|
||||||
|
command ends the group; a dependent can be included with its dependency. Consecutive
|
||||||
`N+` commands are processed together: selected packages required by another selected
|
`N+` commands are processed together: selected packages required by another selected
|
||||||
package are recorded as dependencies, regardless of their order. `*+` works the same
|
package are recorded as dependencies, regardless of their order. `*+` works the same
|
||||||
way. With `--all`
|
way. With `--all`
|
||||||
|
|||||||
+202
-57
@@ -6,7 +6,7 @@
|
|||||||
# and zypper: list what would be upgraded, interrogate every package, remember the
|
# and zypper: list what would be upgraded, interrogate every package, remember the
|
||||||
# ones you trust, then run the native upgrade (with per-run exclusions).
|
# ones you trust, then run the native upgrade (with per-run exclusions).
|
||||||
#
|
#
|
||||||
# Version: 0.3.0
|
# Version: 0.6.0
|
||||||
#
|
#
|
||||||
# Specification: docs/DESIGN.md. Single file, bash >= 4.3, no runtime dependency
|
# 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
|
# 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).
|
# never read from or written to a file (no HISTFILE; `history -r`/`-w`/`-a` are never used).
|
||||||
unset HISTFILE
|
unset HISTFILE
|
||||||
|
|
||||||
readonly SU_VERSION=0.3.0
|
readonly SU_VERSION=0.6.0
|
||||||
readonly SU_PROG=skeptical-updater
|
readonly SU_PROG=skeptical-updater
|
||||||
readonly SU_STATE_HEADER='# skeptical-updater trusted packages. Columns: backend package reason added(UTC ISO-8601)'
|
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'
|
readonly SU_PROMPT_HELP='<enter> upgrade | N? what is it | N?? full info | N- exclude this run | Nx purge now | N+ trust forever'
|
||||||
@@ -105,7 +105,7 @@ options:
|
|||||||
none (default: paru, else yay, when installed; other backends ignore it)
|
none (default: paru, else yay, when installed; other backends ignore it)
|
||||||
--no-aur same as --aur-helper=none
|
--no-aur same as --aur-helper=none
|
||||||
-h, --help this help
|
-h, --help this help
|
||||||
-V, --version print the version
|
-v, --version print the version
|
||||||
|
|
||||||
interactive commands (one line, whitespace-separated tokens, N = number in brackets):
|
interactive commands (one line, whitespace-separated tokens, N = number in brackets):
|
||||||
<empty line> proceed: run the native upgrade with the current exclusions
|
<empty line> proceed: run the native upgrade with the current exclusions
|
||||||
@@ -118,8 +118,11 @@ interactive commands (one line, whitespace-separated tokens, N = number in brack
|
|||||||
N- exclude from this run (toggle); shown with a '-' marker
|
N- exclude from this run (toggle); shown with a '-' marker
|
||||||
Nx purge it now with its now-unneeded dependencies (shown, asks y/N); refused
|
Nx purge it now with its now-unneeded dependencies (shown, asks y/N); refused
|
||||||
when other installed packages depend on it, like pacman -Rns
|
when other installed packages depend on it, like pacman -Rns
|
||||||
|
consecutive Nx commands are purged together after one confirmation
|
||||||
N+ trust it forever (with its installed dependency closure)
|
N+ trust it forever (with its installed dependency closure)
|
||||||
|
N++ trust the topmost installed dependents and their dependency closures
|
||||||
*+ / *- apply + / - to every listed package
|
*+ / *- apply + / - to every listed package
|
||||||
|
a show packages trusted during this session again
|
||||||
l redraw the list
|
l redraw the list
|
||||||
q quit without upgrading (exit 0)
|
q quit without upgrading (exit 0)
|
||||||
h, ? this help
|
h, ? this help
|
||||||
@@ -144,8 +147,11 @@ session_help() {
|
|||||||
Nx purge N now: lists every package the purge removes, then asks y/N.
|
Nx purge N now: lists every package the purge removes, then asks y/N.
|
||||||
x refuses when other installed packages depend on the target, like
|
x refuses when other installed packages depend on the target, like
|
||||||
pacman -Rns; purge the dependents first
|
pacman -Rns; purge the dependents first
|
||||||
|
consecutive Nx commands are purged together after one confirmation
|
||||||
N+ trust N (and its installed dependencies) forever
|
N+ trust N (and its installed dependencies) forever
|
||||||
|
N++ trust the topmost installed dependents of N and their dependencies
|
||||||
*+ *- trust / exclude every listed package (*- again re-includes them)
|
*+ *- trust / exclude every listed package (*- again re-includes them)
|
||||||
|
a show packages trusted during this session again
|
||||||
l redraw the list (a '*' after the number marks an AUR package)
|
l redraw the list (a '*' after the number marks an AUR package)
|
||||||
q quit without upgrading
|
q quit without upgrading
|
||||||
h ? this help
|
h ? this help
|
||||||
@@ -199,7 +205,7 @@ parse_args() {
|
|||||||
usage
|
usage
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
-V | --version)
|
-v | --version)
|
||||||
printf '%s %s\n' "$SU_PROG" "$SU_VERSION"
|
printf '%s %s\n' "$SU_PROG" "$SU_VERSION"
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
@@ -972,20 +978,21 @@ apt_closure() {
|
|||||||
# is system-wide (it also takes pre-existing orphans, e.g. old kernels: verified), so the
|
# is system-wide (it also takes pre-existing orphans, e.g. old kernels: verified), so the
|
||||||
# orphans that were already autoremovable before are subtracted and the rest named explicitly.
|
# orphans that were already autoremovable before are subtracted and the rest named explicitly.
|
||||||
apt_purge_plan() {
|
apt_purge_plan() {
|
||||||
local pkg=$1 pre sim w name rest
|
local pre sim w name rest
|
||||||
local -A before=()
|
local -A before=() requested=()
|
||||||
local -a args=("$pkg")
|
local -a args=("$@")
|
||||||
|
for name in "$@"; do requested[$name]=1; done
|
||||||
pre=$(apt-get -s autoremove </dev/null 2>/dev/null) || pre=''
|
pre=$(apt-get -s autoremove </dev/null 2>/dev/null) || pre=''
|
||||||
while read -r w name rest; do
|
while read -r w name rest; do
|
||||||
[[ $w == Remv || $w == Purg ]] && before[$name]=1
|
[[ $w == Remv || $w == Purg ]] && before[$name]=1
|
||||||
done <<<"$pre"
|
done <<<"$pre"
|
||||||
sim=$(apt-get -s purge --auto-remove "$pkg" </dev/null 2>&1) || {
|
sim=$(apt-get -s purge --auto-remove "$@" </dev/null 2>&1) || {
|
||||||
printf '%s\n' "$sim" >&2
|
printf '%s\n' "$sim" >&2
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
while read -r w name rest; do
|
while read -r w name rest; do
|
||||||
[[ $w == Remv || $w == Purg ]] || continue
|
[[ $w == Remv || $w == Purg ]] || continue
|
||||||
[[ -z ${before[$name]+x} && $name != "$pkg" ]] && args+=("$name")
|
[[ -z ${before[$name]+x} && -z ${requested[$name]+x} ]] && args+=("$name")
|
||||||
done <<<"$sim"
|
done <<<"$sim"
|
||||||
sim=$(apt-get -s purge "${args[@]}" </dev/null 2>&1) || {
|
sim=$(apt-get -s purge "${args[@]}" </dev/null 2>&1) || {
|
||||||
printf '%s\n' "$sim" >&2
|
printf '%s\n' "$sim" >&2
|
||||||
@@ -1507,7 +1514,7 @@ pacman_closure() {
|
|||||||
# On refusal pacman explains on stdout (":: removing X breaks dependency ..."): show all of it.
|
# On refusal pacman explains on stdout (":: removing X breaks dependency ..."): show all of it.
|
||||||
pacman_purge_plan() {
|
pacman_purge_plan() {
|
||||||
local out rc=0 l
|
local out rc=0 l
|
||||||
out=$(COLUMNS=0 pacman -Rs --print --print-format '%n' -- "$1" </dev/null 2>&1) || rc=$?
|
out=$(COLUMNS=0 pacman -Rs --print --print-format '%n' -- "$@" </dev/null 2>&1) || rc=$?
|
||||||
if ((rc)); then
|
if ((rc)); then
|
||||||
printf '%s\n' "$out" >&2
|
printf '%s\n' "$out" >&2
|
||||||
return "$rc"
|
return "$rc"
|
||||||
@@ -1908,7 +1915,7 @@ dnf_purge_plan() {
|
|||||||
local out line in=0 wrapped=0
|
local out line in=0 wrapped=0
|
||||||
local -a f=()
|
local -a f=()
|
||||||
local arch_re='^(noarch|x86_64|x86_64_v[0-9]|aarch64|i[3-6]86|ppc64le|ppc64|s390x|armv7hl|armv7l|riscv64|loongarch64|src)$'
|
local arch_re='^(noarch|x86_64|x86_64_v[0-9]|aarch64|i[3-6]86|ppc64le|ppc64|s390x|armv7hl|armv7l|riscv64|loongarch64|src)$'
|
||||||
out=$("$DNF" --assumeno remove "$1" </dev/null 2>&1) || true
|
out=$("$DNF" --assumeno remove "$@" </dev/null 2>&1) || true
|
||||||
if [[ $out != *'Transaction Summary'* ]]; then
|
if [[ $out != *'Transaction Summary'* ]]; then
|
||||||
if [[ $out == *'Nothing to do'* ]]; then return 0; fi
|
if [[ $out == *'Nothing to do'* ]]; then return 0; fi
|
||||||
printf '%s\n' "$out" >&2
|
printf '%s\n' "$out" >&2
|
||||||
@@ -2091,7 +2098,7 @@ zypper_reason() {
|
|||||||
}
|
}
|
||||||
zypper_purge_plan() {
|
zypper_purge_plan() {
|
||||||
local xml rc=0 sec name rest
|
local xml rc=0 sec name rest
|
||||||
xml=$(zypper --non-interactive --no-refresh --xmlout remove --dry-run --clean-deps "$1" </dev/null 2>&1) || rc=$?
|
xml=$(zypper --non-interactive --no-refresh --xmlout remove --dry-run --clean-deps "$@" </dev/null 2>&1) || rc=$?
|
||||||
if ((rc)); then
|
if ((rc)); then
|
||||||
[[ $rc != 104 ]] || say "zypper: no installed package provides '$1' (exit 104)" >&2
|
[[ $rc != 104 ]] || say "zypper: no installed package provides '$1' (exit 104)" >&2
|
||||||
((rc == 104)) || printf '%s\n' "$xml" >&2
|
((rc == 104)) || printf '%s\n' "$xml" >&2
|
||||||
@@ -2229,6 +2236,8 @@ maybe_refresh() { if ((opt_refresh)); then be refresh || true; fi; }
|
|||||||
R_NAME=() R_OLD=() R_NEW=() R_REPO=() R_FLAGS=() R_AUR=() R_NUM=() R_GONE=() R_EXCL=() R_NOTES=()
|
R_NAME=() R_OLD=() R_NEW=() R_REPO=() R_FLAGS=() R_AUR=() R_NUM=() R_GONE=() R_EXCL=() R_NOTES=()
|
||||||
N_IDX=() # number -> row index
|
N_IDX=() # number -> row index
|
||||||
N_MAX=0
|
N_MAX=0
|
||||||
|
SESSION_SHOW_TRUSTED=0
|
||||||
|
declare -A SESSION_TRUSTED=()
|
||||||
|
|
||||||
load_rows() {
|
load_rows() {
|
||||||
local out rc=0 line name old new repo flags i n=0 w aur shown
|
local out rc=0 line name old new repo flags i n=0 w aur shown
|
||||||
@@ -2276,7 +2285,7 @@ rows_left() { # number of rows not purged
|
|||||||
row_visible() { # row_visible IDX
|
row_visible() { # row_visible IDX
|
||||||
local i=$1
|
local i=$1
|
||||||
((R_GONE[i] == 0 && R_NUM[i] > 0)) || return 1
|
((R_GONE[i] == 0 && R_NUM[i] > 0)) || return 1
|
||||||
((opt_all)) || ! is_trusted "${R_NAME[i]}"
|
((opt_all)) || ! is_trusted "${R_NAME[i]}" || { ((SESSION_SHOW_TRUSTED)) && [[ -n ${SESSION_TRUSTED[${R_NAME[i]}]+x} ]]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
print_list() {
|
print_list() {
|
||||||
@@ -2293,6 +2302,8 @@ print_list() {
|
|||||||
done
|
done
|
||||||
if ((opt_all)); then
|
if ((opt_all)); then
|
||||||
say "$total upgradable, $trusted trusted (marked with =), $review to review:"
|
say "$total upgradable, $trusted trusted (marked with =), $review to review:"
|
||||||
|
elif ((SESSION_SHOW_TRUSTED)); then
|
||||||
|
say "$total upgradable, $trusted trusted (session trusted marked with =), $review to review:"
|
||||||
else
|
else
|
||||||
say "$total upgradable, $trusted trusted (hidden, use --all to show), $review to review:"
|
say "$total upgradable, $trusted trusted (hidden, use --all to show), $review to review:"
|
||||||
fi
|
fi
|
||||||
@@ -2631,66 +2642,103 @@ num_row() { # num_row N -> $REPLY = row index of a listed row, rc 1 if N is not
|
|||||||
REPLY=${N_IDX[n]}
|
REPLY=${N_IDX[n]}
|
||||||
}
|
}
|
||||||
|
|
||||||
# session_purge IDX: pacman's -Rns principle on every backend - a package that other installed
|
# session_purge IDX...: pacman's -Rns principle on every backend - a package that other installed
|
||||||
# packages still depend on is never purged (they are listed, as N? does; nothing cascades);
|
# packages still depend on is never purged (they are listed, as N? does; nothing cascades);
|
||||||
# otherwise the package goes with the dependencies it alone needed, as the y/N prompt lists.
|
# otherwise the package goes with the dependencies it alone needed, as the y/N prompt lists.
|
||||||
session_purge() {
|
session_purge() {
|
||||||
local i=$1 name=${R_NAME[$1]} rc=0 out line p prompt rd d
|
local i name rd out rc=0 p prompt idx dep changed
|
||||||
local -a plan=() others=() extra=()
|
local -a targets=() target_names=() total_plan=() total_others=() extra=() rdeps=()
|
||||||
local -A still=() gone=() mine=()
|
local -A purge_selected=() rejected=() reverse=() gone=() allowed=() still=()
|
||||||
|
local line
|
||||||
|
|
||||||
|
for idx in "$@"; do purge_selected[${R_NAME[idx]}]=$idx; done
|
||||||
|
mapfile -t targets < <(printf '%s\n' "${purge_selected[@]}" | sort -n)
|
||||||
|
for i in "${targets[@]}"; do
|
||||||
|
name=${R_NAME[i]}
|
||||||
if [[ ${R_OLD[i]} == - ]]; then
|
if [[ ${R_OLD[i]} == - ]]; then
|
||||||
say "$name is not installed (the upgrade would install it as a new dependency): nothing to purge; use $((R_NUM[i]))- to exclude it from this run."
|
say "$name is not installed (the upgrade would install it as a new dependency): nothing to purge; use $((R_NUM[i]))- to exclude it from this run."
|
||||||
return 0
|
rejected[$name]=1
|
||||||
|
continue
|
||||||
fi
|
fi
|
||||||
rd=$(be rdepends "$name") || rd=''
|
rd=$(be rdepends "$name") || rd=''
|
||||||
rd=$(printf '%s\n' "$rd" | sed '/^$/d' | tr '\n' ' ')
|
reverse[$name]=$(printf '%s\n' "$rd" | sed '/^$/d' | tr '\n' ' ')
|
||||||
rd=${rd% }
|
done
|
||||||
if [[ -n $rd ]]; then
|
# A refused dependent also blocks its dependencies, including through a chain.
|
||||||
|
changed=1
|
||||||
|
while ((changed)); do
|
||||||
|
changed=0
|
||||||
|
for i in "${targets[@]}"; do
|
||||||
|
name=${R_NAME[i]}
|
||||||
|
[[ -z ${rejected[$name]+x} ]] || continue
|
||||||
|
read -r -a rdeps <<<"${reverse[$name]}"
|
||||||
|
for dep in "${rdeps[@]}"; do
|
||||||
|
if [[ -z ${purge_selected[$dep]+x} || -n ${rejected[$dep]+x} ]]; then
|
||||||
|
rd=${reverse[$name]% }
|
||||||
say "not purged: $name is required by: ${rd// /, } - purge the dependents first"
|
say "not purged: $name is required by: ${rd// /, } - purge the dependents first"
|
||||||
return 0
|
rejected[$name]=1
|
||||||
|
changed=1
|
||||||
|
break
|
||||||
fi
|
fi
|
||||||
out=$(be purge_plan "$name" | sort -u) || rc=$?
|
done
|
||||||
|
done
|
||||||
|
done
|
||||||
|
for i in "${targets[@]}"; do
|
||||||
|
name=${R_NAME[i]}
|
||||||
|
[[ -z ${rejected[$name]+x} ]] || continue
|
||||||
|
target_names+=("$name")
|
||||||
|
allowed[$name]=1
|
||||||
|
closure_of "$name"
|
||||||
|
for dep in ${CLOSURE[@]+"${CLOSURE[@]}"}; do allowed[$dep]=1; done
|
||||||
|
done
|
||||||
|
((${#target_names[@]})) || return 0
|
||||||
|
out=$(be purge_plan "${target_names[@]}" | sort -u) || rc=$?
|
||||||
if ((rc)); then
|
if ((rc)); then
|
||||||
err "cannot purge $name: $(be label) refuses (see above); nothing was removed"
|
err "cannot purge ${target_names[*]}: $(be label) refuses (see above); nothing was removed"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
while IFS= read -r p; do
|
while IFS= read -r p; do
|
||||||
[[ -n $p && -z ${gone[$p]+x} ]] || continue
|
[[ -n $p ]] || continue
|
||||||
|
total_plan+=("$p")
|
||||||
gone[$p]=1
|
gone[$p]=1
|
||||||
plan+=("$p")
|
[[ -n ${purge_selected[$p]+x} && -z ${rejected[$p]+x} ]] || total_others+=("$p")
|
||||||
[[ $p == "$name" ]] || others+=("$p")
|
[[ -n ${allowed[$p]+x} && -z ${rejected[$p]+x} ]] || extra+=("$p")
|
||||||
done <<<"$out"
|
done <<<"$out"
|
||||||
if ((${#plan[@]} == 0)); then
|
if ((${#total_plan[@]} == 0)); then
|
||||||
say "$name is not installed: nothing to purge."
|
say "${target_names[*]} is not installed: nothing to purge."
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
# belt and braces: the plan may only hold the target and its own (installed) dependencies;
|
for name in "${target_names[@]}"; do
|
||||||
# anything else means a dependent the reverse-dependency query missed (e.g. through a virtual)
|
if [[ -z ${gone[$name]+x} ]]; then
|
||||||
if ((${#others[@]})); then
|
err "cannot purge $name: $(be label) did not include it in the plan; nothing was removed"
|
||||||
closure_of "$name"
|
return 0
|
||||||
for d in ${CLOSURE[@]+"${CLOSURE[@]}"}; do mine[$d]=1; done
|
fi
|
||||||
for p in "${others[@]}"; do [[ -n ${mine[$p]+x} ]] || extra+=("$p"); done
|
done
|
||||||
|
# belt and braces: no package outside the targets and their dependencies may be removed.
|
||||||
if ((${#extra[@]})); then
|
if ((${#extra[@]})); then
|
||||||
err "cannot purge $name: $(be label) would also remove $(join_by ', ' "${extra[@]}"), which $name does not depend on (dependents of it?); nothing was removed"
|
err "cannot purge ${target_names[*]}: $(be label) would also remove $(join_by ', ' "${extra[@]}"), which ${target_names[*]} does not depend on (dependents of it?); nothing was removed"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
prompt="Purge ${target_names[*]} now?"
|
||||||
prompt="Purge $name now?"
|
((${#total_others[@]} == 0)) || prompt+=" This also removes: $(join_by ', ' "${total_others[@]}")"
|
||||||
((${#others[@]} == 0)) || prompt+=" This also removes: $(join_by ', ' "${others[@]}")"
|
|
||||||
read_line "$prompt [y/N] "
|
read_line "$prompt [y/N] "
|
||||||
case $REPLY in
|
case $REPLY in
|
||||||
y | Y | yes | YES | Yes) ;;
|
y | Y | yes | YES | Yes) ;;
|
||||||
*)
|
*)
|
||||||
say "not purged: $name"
|
say "not purged: ${target_names[*]}"
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
be purge "${plan[@]}" || rc=$?
|
rc=0
|
||||||
|
be purge "${total_plan[@]}" || rc=$?
|
||||||
if ((rc)); then
|
if ((rc)); then
|
||||||
err "purge of $name failed ($(be label) exited $rc); the row stays in the list"
|
if ((${#target_names[@]} == 1)); then
|
||||||
|
err "purge of ${target_names[0]} failed ($(be label) exited $rc); the row stays in the list"
|
||||||
|
else
|
||||||
|
err "purge of ${target_names[*]} failed ($(be label) exited $rc); the rows stay in the list"
|
||||||
|
fi
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
say "purged: ${plan[*]}"
|
say "purged: ${total_plan[*]}"
|
||||||
for ((i = 0; i < ${#R_NAME[@]}; i++)); do
|
for ((i = 0; i < ${#R_NAME[@]}; i++)); do
|
||||||
if [[ -n ${gone[${R_NAME[i]}]+x} ]]; then
|
if [[ -n ${gone[${R_NAME[i]}]+x} ]]; then
|
||||||
R_GONE[i]=1
|
R_GONE[i]=1
|
||||||
@@ -2739,9 +2787,17 @@ session_trust() { # session_trust IDX...
|
|||||||
trust_pkg "${R_NAME[i]}" "$now"
|
trust_pkg "${R_NAME[i]}" "$now"
|
||||||
R_EXCL[i]=0
|
R_EXCL[i]=0
|
||||||
done
|
done
|
||||||
|
session_finish_trust
|
||||||
|
}
|
||||||
|
|
||||||
|
session_finish_trust() {
|
||||||
|
local i
|
||||||
|
for ((i = 0; i < ${#R_NAME[@]}; i++)); do
|
||||||
|
if ((R_NUM[i] > 0 && !R_GONE[i])) && is_trusted "${R_NAME[i]}"; then SESSION_TRUSTED[${R_NAME[i]}]=1; fi
|
||||||
|
done
|
||||||
# a row this trust hides must not stay excluded behind the user's back
|
# a row this trust hides must not stay excluded behind the user's back
|
||||||
for ((i = 0; i < ${#R_NAME[@]}; i++)); do
|
for ((i = 0; i < ${#R_NAME[@]}; i++)); do
|
||||||
if ((R_EXCL[i] && !R_GONE[i] && !opt_all)) && is_trusted "${R_NAME[i]}"; then
|
if ((R_EXCL[i] && !R_GONE[i])) && is_trusted "${R_NAME[i]}" && ! row_visible "$i"; then
|
||||||
R_EXCL[i]=0
|
R_EXCL[i]=0
|
||||||
say " (${R_NAME[i]} is trusted now: no longer excluded from this run)"
|
say " (${R_NAME[i]} is trusted now: no longer excluded from this run)"
|
||||||
fi
|
fi
|
||||||
@@ -2749,6 +2805,62 @@ session_trust() { # session_trust IDX...
|
|||||||
state_save
|
state_save
|
||||||
}
|
}
|
||||||
|
|
||||||
|
session_trust_dependents() {
|
||||||
|
local i=$1 node dep out root other candidate now head blocked
|
||||||
|
local -a queue=() nodes=() roots=() walk=() deps=()
|
||||||
|
local -A seen=() edges=() reach=() visited=()
|
||||||
|
if [[ ${R_OLD[i]} == - ]]; then
|
||||||
|
say "${R_NAME[i]} is not installed: cannot find installed dependents."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
queue=("${R_NAME[i]}")
|
||||||
|
seen[${R_NAME[i]}]=1
|
||||||
|
for ((head = 0; head < ${#queue[@]}; head++)); do
|
||||||
|
node=${queue[head]}
|
||||||
|
nodes+=("$node")
|
||||||
|
if ! out=$(be rdepends "$node"); then
|
||||||
|
err "cannot find installed dependents of $node; nothing was trusted"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
while IFS= read -r dep; do
|
||||||
|
[[ -n $dep ]] || continue
|
||||||
|
edges[$node]+=" $dep"
|
||||||
|
if [[ -z ${seen[$dep]+x} ]]; then seen[$dep]=1; queue+=("$dep"); fi
|
||||||
|
done <<<"$out"
|
||||||
|
done
|
||||||
|
for root in "${nodes[@]}"; do
|
||||||
|
walk=("$root")
|
||||||
|
visited=(["$root"]=1)
|
||||||
|
for ((head = 0; head < ${#walk[@]}; head++)); do
|
||||||
|
node=${walk[head]}
|
||||||
|
read -r -a deps <<<"${edges[$node]-}"
|
||||||
|
for dep in "${deps[@]}"; do
|
||||||
|
[[ -z ${visited[$dep]+x} ]] || continue
|
||||||
|
visited[$dep]=1
|
||||||
|
walk+=("$dep")
|
||||||
|
done
|
||||||
|
done
|
||||||
|
for node in "${walk[@]}"; do reach["$root,$node"]=1; done
|
||||||
|
done
|
||||||
|
mapfile -t nodes < <(printf '%s\n' "${nodes[@]}" | sort -u)
|
||||||
|
for root in "${nodes[@]}"; do
|
||||||
|
blocked=0
|
||||||
|
for other in "${nodes[@]}"; do
|
||||||
|
[[ $root != "$other" && -n ${reach[$root,$other]+x} ]] || continue
|
||||||
|
if [[ -z ${reach[$other,$root]+x} || $other < $root ]]; then blocked=1; break; fi
|
||||||
|
done
|
||||||
|
((blocked)) || roots+=("$root")
|
||||||
|
done
|
||||||
|
now=$(now_utc)
|
||||||
|
for candidate in "${roots[@]}"; do
|
||||||
|
trust_pkg "$candidate" "$now"
|
||||||
|
for ((i = 0; i < ${#R_NAME[@]}; i++)); do
|
||||||
|
[[ ${R_NAME[i]} != "$candidate" ]] || R_EXCL[i]=0
|
||||||
|
done
|
||||||
|
done
|
||||||
|
session_finish_trust
|
||||||
|
}
|
||||||
|
|
||||||
session_toggle_all() {
|
session_toggle_all() {
|
||||||
local i all=1
|
local i all=1
|
||||||
local -a idx=()
|
local -a idx=()
|
||||||
@@ -2768,11 +2880,23 @@ session_toggle_all() {
|
|||||||
SESSION_PROCEED=0
|
SESSION_PROCEED=0
|
||||||
UPGRADE_FAILED_MSG= # set by <be>_upgrade when the failing step is not the package manager's own
|
UPGRADE_FAILED_MSG= # set by <be>_upgrade when the failing step is not the package manager's own
|
||||||
|
|
||||||
|
session_flush_purges() {
|
||||||
|
local -n pb=$1
|
||||||
|
if ((${#pb[@]})); then
|
||||||
|
session_purge "${pb[@]}"
|
||||||
|
pb=()
|
||||||
|
if (($(rows_left) == 0 && ${#R_NOTES[@]} == 0)); then
|
||||||
|
say "Nothing left to upgrade."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
session_line() {
|
session_line() {
|
||||||
local tok n op i name
|
local tok n op i name
|
||||||
local -a toks=() batch=()
|
local -a toks=() batch=() purge_batch=()
|
||||||
local -A initial=() named=()
|
local -A initial=() named=()
|
||||||
local re='^([0-9]{1,6})(\?\?|\?|-|x|\+)$'
|
local re='^([0-9]{1,6})(\?\?|\?|-|x|\+\+|\+)$'
|
||||||
local name_re='^([A-Za-z0-9_+@][A-Za-z0-9_+.@:~-]*)(\?\?|\?)$'
|
local name_re='^([A-Za-z0-9_+@][A-Za-z0-9_+.@:~-]*)(\?\?|\?)$'
|
||||||
read -r -a toks <<<"$1" || true
|
read -r -a toks <<<"$1" || true
|
||||||
if ((${#toks[@]} == 0)); then
|
if ((${#toks[@]} == 0)); then
|
||||||
@@ -2782,7 +2906,7 @@ session_line() {
|
|||||||
for tok in "${toks[@]}"; do
|
for tok in "${toks[@]}"; do
|
||||||
if [[ ! $tok =~ $re && ! $tok =~ $name_re ]]; then
|
if [[ ! $tok =~ $re && ! $tok =~ $name_re ]]; then
|
||||||
case $tok in
|
case $tok in
|
||||||
'*+' | '*-' | l | q | h | '?') ;;
|
'*+' | '*-' | a | l | q | h | '?') ;;
|
||||||
*)
|
*)
|
||||||
err "unknown command '$tok' (h for help)"
|
err "unknown command '$tok' (h for help)"
|
||||||
return 0
|
return 0
|
||||||
@@ -2797,6 +2921,24 @@ session_line() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
for tok in "${toks[@]}"; do
|
for tok in "${toks[@]}"; do
|
||||||
|
if [[ $tok =~ $re ]]; then
|
||||||
|
op=${BASH_REMATCH[2]}
|
||||||
|
if [[ $op == x ]]; then
|
||||||
|
if ((${#batch[@]})); then session_trust "${batch[@]}"; batch=(); fi
|
||||||
|
elif [[ $op == + ]]; then
|
||||||
|
session_flush_purges purge_batch
|
||||||
|
else
|
||||||
|
if ((${#batch[@]})); then session_trust "${batch[@]}"; batch=(); fi
|
||||||
|
session_flush_purges purge_batch
|
||||||
|
fi
|
||||||
|
elif [[ $tok =~ $name_re ]]; then
|
||||||
|
if ((${#batch[@]})); then session_trust "${batch[@]}"; batch=(); fi
|
||||||
|
session_flush_purges purge_batch
|
||||||
|
else
|
||||||
|
if ((${#batch[@]})); then session_trust "${batch[@]}"; batch=(); fi
|
||||||
|
session_flush_purges purge_batch
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ $tok =~ $re ]]; then
|
if [[ $tok =~ $re ]]; then
|
||||||
n=$((10#${BASH_REMATCH[1]}))
|
n=$((10#${BASH_REMATCH[1]}))
|
||||||
op=${BASH_REMATCH[2]}
|
op=${BASH_REMATCH[2]}
|
||||||
@@ -2809,8 +2951,12 @@ session_line() {
|
|||||||
batch+=("$i")
|
batch+=("$i")
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
if ((${#batch[@]})); then session_trust "${batch[@]}"; batch=(); fi
|
if [[ $op == x ]]; then
|
||||||
|
purge_batch+=("$i")
|
||||||
|
continue
|
||||||
|
fi
|
||||||
case $op in
|
case $op in
|
||||||
|
'++') session_trust_dependents "$i" ;;
|
||||||
'?') print_info "${R_NAME[i]}" "${R_OLD[i]}" || true ;;
|
'?') print_info "${R_NAME[i]}" "${R_OLD[i]}" || true ;;
|
||||||
'??') print_full_info "${R_NAME[i]}" "${R_OLD[i]}" || true ;;
|
'??') print_full_info "${R_NAME[i]}" "${R_OLD[i]}" || true ;;
|
||||||
-)
|
-)
|
||||||
@@ -2822,13 +2968,6 @@ session_line() {
|
|||||||
say "excluded from this run: ${R_NAME[i]}"
|
say "excluded from this run: ${R_NAME[i]}"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
x)
|
|
||||||
session_purge "$i"
|
|
||||||
if (($(rows_left) == 0 && ${#R_NOTES[@]} == 0)); then
|
|
||||||
say "Nothing left to upgrade."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
elif [[ $tok =~ $name_re ]]; then
|
elif [[ $tok =~ $name_re ]]; then
|
||||||
name=${BASH_REMATCH[1]}
|
name=${BASH_REMATCH[1]}
|
||||||
@@ -2838,14 +2977,12 @@ session_line() {
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
i=${named[$name]}
|
i=${named[$name]}
|
||||||
if ((${#batch[@]})); then session_trust "${batch[@]}"; batch=(); fi
|
|
||||||
if [[ $op == '?' ]]; then
|
if [[ $op == '?' ]]; then
|
||||||
print_info "${R_NAME[i]}" "${R_OLD[i]}" || true
|
print_info "${R_NAME[i]}" "${R_OLD[i]}" || true
|
||||||
else
|
else
|
||||||
print_full_info "${R_NAME[i]}" "${R_OLD[i]}" || true
|
print_full_info "${R_NAME[i]}" "${R_OLD[i]}" || true
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if ((${#batch[@]})); then session_trust "${batch[@]}"; batch=(); fi
|
|
||||||
case $tok in
|
case $tok in
|
||||||
'*+')
|
'*+')
|
||||||
local -a idx=()
|
local -a idx=()
|
||||||
@@ -2855,6 +2992,13 @@ session_line() {
|
|||||||
if ((${#idx[@]})); then session_trust "${idx[@]}"; else say "nothing to trust"; fi
|
if ((${#idx[@]})); then session_trust "${idx[@]}"; else say "nothing to trust"; fi
|
||||||
;;
|
;;
|
||||||
'*-') session_toggle_all ;;
|
'*-') session_toggle_all ;;
|
||||||
|
a)
|
||||||
|
SESSION_SHOW_TRUSTED=1
|
||||||
|
for ((n = 1; n <= N_MAX; n++)); do
|
||||||
|
if num_row "$n"; then initial[$n]=$REPLY; named[${R_NAME[REPLY]}]=$REPLY; fi
|
||||||
|
done
|
||||||
|
print_list
|
||||||
|
;;
|
||||||
l) print_list ;;
|
l) print_list ;;
|
||||||
q)
|
q)
|
||||||
say "Quit: nothing was upgraded."
|
say "Quit: nothing was upgraded."
|
||||||
@@ -2865,6 +3009,7 @@ session_line() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if ((${#batch[@]})); then session_trust "${batch[@]}"; fi
|
if ((${#batch[@]})); then session_trust "${batch[@]}"; fi
|
||||||
|
session_flush_purges purge_batch
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_interactive() {
|
cmd_interactive() {
|
||||||
|
|||||||
@@ -785,3 +785,13 @@ zypp_conf() { # a "real" zypp.conf + locks file with one user lock, in the test
|
|||||||
refute_output --partial 'skeptical-updater.'
|
refute_output --partial 'skeptical-updater.'
|
||||||
[[ ! -e $STATE/zypper-private ]]
|
[[ ! -e $STATE/zypper-private ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "pacman: dependent and dependency are simulated and purged together" {
|
||||||
|
use_fakes pacman
|
||||||
|
run skup --no-refresh <<<$'2x 1x\ny\nq'
|
||||||
|
assert_success
|
||||||
|
assert_line 'Purge su-dummy-a su-dummy-b now? [y/N] y'
|
||||||
|
assert_line 'purged: su-dummy-a su-dummy-b'
|
||||||
|
log_has 'pacman -Rs --print --print-format %n -- su-dummy-a su-dummy-b'
|
||||||
|
log_has 'pacman -Rns --noconfirm -- su-dummy-a su-dummy-b'
|
||||||
|
}
|
||||||
+6
-4
@@ -24,12 +24,14 @@ setup() { common_setup; }
|
|||||||
assert_line 'usage: skeptical-updater [options] [command]'
|
assert_line 'usage: skeptical-updater [options] [command]'
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "--version and -V print the version" {
|
@test "--version and -v print the version" {
|
||||||
run skup --version
|
run skup --version
|
||||||
assert_success
|
assert_success
|
||||||
assert_output 'skeptical-updater 0.1.0'
|
assert_output --regexp '^skeptical-updater [0-9]+\.[0-9]+\.[0-9]+(\+(alpha|beta|rc)[.-]?[0-9]+)?$'
|
||||||
run skup -V
|
local version=$output
|
||||||
assert_output 'skeptical-updater 0.1.0'
|
run skup -v
|
||||||
|
assert_success
|
||||||
|
assert_output "$version"
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "an unknown option is a usage error (exit 1)" {
|
@test "an unknown option is a usage error (exit 1)" {
|
||||||
|
|||||||
@@ -137,21 +137,20 @@ case $op in
|
|||||||
-Rs)
|
-Rs)
|
||||||
((print)) || exit 98
|
((print)) || exit 98
|
||||||
tg=${targets[0]}
|
tg=${targets[0]}
|
||||||
if [[ -n ${FAKE_PACMAN_RS_REFUSE:-} || $tg == su-dummy-b ]]; then
|
if [[ -n ${FAKE_PACMAN_RS_REFUSE:-} || " ${targets[*]} " == *' su-dummy-b '* && " ${targets[*]} " != *' su-dummy-a '* ]]; then
|
||||||
echo "checking dependencies..."
|
echo "checking dependencies..."
|
||||||
echo ":: removing $tg breaks dependency '$tg' required by su-dummy-a"
|
echo ":: removing $tg breaks dependency '$tg' required by su-dummy-a"
|
||||||
echo "error: failed to prepare transaction (could not satisfy dependencies)" >&2
|
echo "error: failed to prepare transaction (could not satisfy dependencies)" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
for tg in "${targets[@]}"; do
|
||||||
case $tg in
|
case $tg in
|
||||||
su-dummy-a) printf '%s\n' su-dummy-a su-dummy-b ;; # b was installed as a dependency of a
|
su-dummy-a) printf '%s\n' su-dummy-a su-dummy-b ;;
|
||||||
su-dummy-c | glib2 | expat | ca-certificates-mozilla | pfetch | su-dummy-aur) echo "$tg" ;; # (the last two: foreign, AUR)
|
su-dummy-b | su-dummy-c | glib2 | expat | ca-certificates-mozilla | pfetch | su-dummy-aur) echo "$tg" ;;
|
||||||
*)
|
*) echo "error: target not found: $tg" >&2; exit 1 ;;
|
||||||
echo "error: target not found: $tg" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
for r in ${FAKE_PACMAN_RS_EXTRA:-}; do [[ ${r%%:*} == "$tg" ]] && echo "${r#*:}"; done
|
for r in ${FAKE_PACMAN_RS_EXTRA:-}; do [[ ${r%%:*} == "$tg" ]] && echo "${r#*:}"; done
|
||||||
|
done
|
||||||
;;
|
;;
|
||||||
-Rns)
|
-Rns)
|
||||||
rc=$(fake_rc FAKE_PACMAN_RNS_RC 0)
|
rc=$(fake_rc FAKE_PACMAN_RNS_RC 0)
|
||||||
|
|||||||
@@ -457,3 +457,145 @@ UPGRADE='apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--forc
|
|||||||
assert_line ' [3] su-dummy-c 1.0 -> 2.0 file:/srv/su-repo ./ [held]'
|
assert_line ' [3] su-dummy-c 1.0 -> 2.0 file:/srv/su-repo ./ [held]'
|
||||||
assert_line 'su-dummy-c: dummy package su-dummy-c for skeptical-updater tests'
|
assert_line 'su-dummy-c: dummy package su-dummy-c for skeptical-updater tests'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "Nx: consecutive purges use one transaction including orphans" {
|
||||||
|
FAKE_APT_AUTO_ORPHANS=su-dummy-a:su-dummy-b run skup --no-refresh <<<$'1x 3x\ny\nq'
|
||||||
|
assert_success
|
||||||
|
assert_line 'Purge su-dummy-a su-dummy-c now? This also removes: su-dummy-b [y/N] y'
|
||||||
|
assert_line 'purged: su-dummy-a su-dummy-b su-dummy-c'
|
||||||
|
assert_line 'Nothing left to upgrade.'
|
||||||
|
log_has 'apt-get -y purge su-dummy-a su-dummy-b su-dummy-c'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Nx: other commands flush the batch before resolving removed rows" {
|
||||||
|
FAKE_APT_AUTO_ORPHANS=su-dummy-a:su-dummy-b run skup --no-refresh <<<$'1x 2? 3x\ny\ny\nq'
|
||||||
|
assert_success
|
||||||
|
assert_line 'purged: su-dummy-a su-dummy-b'
|
||||||
|
assert_output --partial 'no package [2] in the list'
|
||||||
|
assert_line 'Purge su-dummy-c now? [y/N] y'
|
||||||
|
assert_line 'purged: su-dummy-c'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Nx: a trust command cannot act on a removed row" {
|
||||||
|
FAKE_APT_AUTO_ORPHANS=su-dummy-a:su-dummy-b run skup --no-refresh <<<$'1x 2+\ny\nq'
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'no package [2] in the list'
|
||||||
|
assert_line 'purged: su-dummy-a su-dummy-b'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Nx: duplicate targets are purged once" {
|
||||||
|
run skup --no-refresh <<<$'3x 3x\ny\nq'
|
||||||
|
assert_success
|
||||||
|
assert_line 'Purge su-dummy-c now? [y/N] y'
|
||||||
|
assert_line 'purged: su-dummy-c'
|
||||||
|
log_has 'apt-get -y purge su-dummy-c'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Nx: a refused target does not prevent other purges" {
|
||||||
|
run skup --no-refresh <<<$'2x 3x\ny\nl\nq'
|
||||||
|
assert_success
|
||||||
|
assert_line 'not purged: su-dummy-b is required by: su-dummy-a - purge the dependents first'
|
||||||
|
assert_line 'purged: su-dummy-c'
|
||||||
|
log_has 'apt-get -y purge su-dummy-c'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Nx: invalid numbers do not interrupt a batch" {
|
||||||
|
FAKE_APT_AUTO_ORPHANS=su-dummy-a:su-dummy-b run skup --no-refresh <<<$'1x 9x 3x\ny\nq'
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'no package [9] in the list'
|
||||||
|
assert_line 'purged: su-dummy-a su-dummy-b su-dummy-c'
|
||||||
|
log_has 'apt-get -y purge su-dummy-a su-dummy-b su-dummy-c'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Nx: rejecting the prompt purges nothing" {
|
||||||
|
run skup --no-refresh <<<$'1x 3x\nn\nl\nq'
|
||||||
|
assert_success
|
||||||
|
assert_line 'not purged: su-dummy-a su-dummy-c'
|
||||||
|
assert_line '3 upgradable, 0 trusted (hidden, use --all to show), 3 to review:'
|
||||||
|
log_lacks '^apt-get -y purge'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Nx: failed transaction keeps all rows" {
|
||||||
|
FAKE_APT_PURGE_RC=100 run skup --no-refresh <<<$'1x 3x\nyes\nl\nq'
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'purge of su-dummy-a su-dummy-c failed (apt exited 100); the rows stay in the list'
|
||||||
|
assert_line '3 upgradable, 0 trusted (hidden, use --all to show), 3 to review:'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Nx: a dependent and dependency are planned together" {
|
||||||
|
FAKE_APT_AUTO_ORPHANS=su-dummy-a:su-dummy-b run skup --no-refresh <<<$'2x 1x\ny\nq'
|
||||||
|
assert_success
|
||||||
|
assert_line 'Purge su-dummy-a su-dummy-b now? [y/N] y'
|
||||||
|
assert_line 'purged: su-dummy-a su-dummy-b'
|
||||||
|
log_has 'apt-get -y purge su-dummy-a su-dummy-b'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Nx: unexpected removals never leak into another target's purge" {
|
||||||
|
FAKE_APT_SIM_EXTRA=su-dummy-a:su-dummy-c run skup --no-refresh <<<$'1x 2x\ny\nq'
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'would also remove su-dummy-c'
|
||||||
|
log_lacks '^apt-get -y purge'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "a shows session trusted rows without exposing previously trusted rows" {
|
||||||
|
write_state "$HEADER" "apt${T}su-dummy-c${T}user${T}2026-01-01T00:00:00Z"
|
||||||
|
run skup --no-refresh <<<$'1+\na\n2?\nq'
|
||||||
|
assert_success
|
||||||
|
assert_line '=[1] su-dummy-a 1.0 -> 2.0 su-fixture:su-repo'
|
||||||
|
assert_line '=[2] su-dummy-b 1.0 -> 2.0 su-fixture:su-repo'
|
||||||
|
refute_output --partial '=[3] su-dummy-c'
|
||||||
|
assert_line 'su-dummy-b: dummy package su-dummy-b for skeptical-updater tests'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "a works on the same line as trust and keeps removed rows hidden" {
|
||||||
|
run skup --no-refresh <<<$'3x\ny\n1+ a 2?\nq'
|
||||||
|
assert_success
|
||||||
|
assert_line 'purged: su-dummy-c'
|
||||||
|
assert_line '=[1] su-dummy-a 1.0 -> 2.0 su-fixture:su-repo'
|
||||||
|
assert_line 'su-dummy-b: dummy package su-dummy-b for skeptical-updater tests'
|
||||||
|
refute_output --partial '=[3] su-dummy-c'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "N++ trusts the topmost installed dependent and its dependencies" {
|
||||||
|
run skup --no-refresh <<<$'2++\nq'
|
||||||
|
assert_success
|
||||||
|
assert_line 'trusted su-dummy-a (+1 dependency: su-dummy-b)'
|
||||||
|
run cut -f2,3 "$STATE_FILE"
|
||||||
|
assert_line "su-dummy-a${T}user"
|
||||||
|
assert_line "su-dummy-b${T}dep:su-dummy-a"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "N++ trusts all topmost branches including packages outside the list" {
|
||||||
|
local fixture_data="$BATS_TEST_TMPDIR/data"
|
||||||
|
cp -R "$FAKE_DATA" "$fixture_data"
|
||||||
|
printf 'su-dummy-b\nReverse Depends:\n su-dummy-a\n su-dummy-c\n' >"$fixture_data/apt/rdepends/su-dummy-b"
|
||||||
|
printf 'su-dummy-a\nReverse Depends:\n su-dummy-w\n' >"$fixture_data/apt/rdepends/su-dummy-a"
|
||||||
|
sed -i 's/su-virtual | su-dummy-alt/su-dummy-a/; s/^ii \(.*su-dummy-c\)\t\t\t\t/ii \1\t\tsu-dummy-b\t/' "$fixture_data/apt/dpkg-status.tsv"
|
||||||
|
FAKE_DATA="$fixture_data" run skup --no-refresh <<<$'2++\nq'
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'trusted su-dummy-c'
|
||||||
|
assert_output --partial 'trusted su-dummy-w'
|
||||||
|
run cut -f2,3 "$STATE_FILE"
|
||||||
|
assert_line "su-dummy-c${T}user"
|
||||||
|
assert_line "su-dummy-w${T}user"
|
||||||
|
assert_line "su-dummy-a${T}dep:su-dummy-w"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "N++ terminates on a dependency cycle and chooses one root" {
|
||||||
|
local fixture_data="$BATS_TEST_TMPDIR/data"
|
||||||
|
cp -R "$FAKE_DATA" "$fixture_data"
|
||||||
|
printf 'su-dummy-a\nReverse Depends:\n su-dummy-b\n' >"$fixture_data/apt/rdepends/su-dummy-a"
|
||||||
|
sed -i 's/^ii \(.*su-dummy-b\)\t\t\t\t/ii \1\t\tsu-dummy-a\t/' "$fixture_data/apt/dpkg-status.tsv"
|
||||||
|
FAKE_DATA="$fixture_data" run skup --no-refresh <<<$'2++\nq'
|
||||||
|
assert_success
|
||||||
|
assert_output --partial 'trusted su-dummy-a'
|
||||||
|
run cut -f2,3 "$STATE_FILE"
|
||||||
|
assert_line "su-dummy-a${T}user"
|
||||||
|
assert_line "su-dummy-b${T}dep:su-dummy-a"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "N++ trusts the package itself when it has no installed dependents" {
|
||||||
|
run skup --no-refresh <<<$'3++\nq'
|
||||||
|
assert_success
|
||||||
|
assert_line 'trusted su-dummy-c'
|
||||||
|
}
|
||||||
Reference in new issue
Block a user