2 Commits
5 changed files with 94 additions and 14 deletions

No files matched your search

+7 -2
View File
@@ -59,7 +59,7 @@ options:
none (default: paru, else yay, when installed; other backends ignore it)
--no-aur same as --aur-helper=none
-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),
@@ -131,7 +131,12 @@ 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 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.
or package name that is not listed prints an error and the remaining commands still run.
For `?` and `??`, a listed package name can replace the number, for example
`su-dummy-a?` or `su-dummy-a??`. A digits-only reference is interpreted as a number.
Names are restricted to these information commands to avoid unnecessary parsing
complexity: package names may contain `-` or end in `x`, which conflicts with the
other command suffixes.
| token | action |
|-------|--------|
+5
View File
@@ -126,6 +126,11 @@ 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.
For `?` and `??`, the exact name of a listed package may replace `N`; a digits-only
reference is treated as a number. Missing or no longer listed names are reported and
skipped. Other commands accept only numbers: package names can contain `-` or end in
`x`, and limiting name references to information commands avoids unnecessary parsing
complexity.
Consecutive `N+` commands and `*+` trust the selected dependency roots, recording
other selected packages in their closures as dependencies regardless of input order.
+28 -8
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.2.0
# Version: 0.3.1
#
# 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.2.0
readonly SU_VERSION=0.3.1
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'
@@ -105,7 +105,7 @@ options:
none (default: paru, else yay, when installed; other backends ignore it)
--no-aur same as --aur-helper=none
-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):
<empty line> proceed: run the native upgrade with the current exclusions
@@ -114,6 +114,7 @@ interactive commands (one line, whitespace-separated tokens, N = number in brack
that depend on it - installed packages only
N?? full native info: the installed record; lines starting with '*' come
from the upstream candidate (differing values, fields it adds)
For N? and N??, a listed package name may be used instead of N.
N- exclude from this run (toggle); shown with a '-' marker
Nx purge it now with its now-unneeded dependencies (shown, asks y/N); refused
when other installed packages depend on it, like pacman -Rns
@@ -138,6 +139,7 @@ session_help() {
<enter> upgrade now, skipping the rows marked '-'
N? what is it: description, install reason + installed packages that need it
N?? full native info: installed record, '*' lines from the upstream candidate
A listed package name may replace N for ? and ?? (e.g. okular?).
N- exclude N from this run (toggle)
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
@@ -197,7 +199,7 @@ parse_args() {
usage
exit 0
;;
-V | --version)
-v | --version)
printf '%s %s\n' "$SU_PROG" "$SU_VERSION"
exit 0
;;
@@ -2767,17 +2769,18 @@ SESSION_PROCEED=0
UPGRADE_FAILED_MSG= # set by <be>_upgrade when the failing step is not the package manager's own
session_line() {
local tok n op i
local tok n op i name
local -a toks=() batch=()
local -A initial=()
local -A initial=() named=()
local re='^([0-9]{1,6})(\?\?|\?|-|x|\+)$'
local name_re='^([A-Za-z0-9_+@][A-Za-z0-9_+.@:~-]*)(\?\?|\?)$'
read -r -a toks <<<"$1" || true
if ((${#toks[@]} == 0)); then
SESSION_PROCEED=1
return 0
fi
for tok in "${toks[@]}"; do
if [[ ! $tok =~ $re ]]; then
if [[ ! $tok =~ $re && ! $tok =~ $name_re ]]; then
case $tok in
'*+' | '*-' | l | q | h | '?') ;;
*)
@@ -2788,7 +2791,10 @@ session_line() {
fi
done
for ((n = 1; n <= N_MAX; n++)); do
if num_row "$n"; then initial[$n]=$REPLY; fi
if num_row "$n"; then
initial[$n]=$REPLY
named[${R_NAME[REPLY]}]=$REPLY
fi
done
for tok in "${toks[@]}"; do
if [[ $tok =~ $re ]]; then
@@ -2824,6 +2830,20 @@ session_line() {
fi
;;
esac
elif [[ $tok =~ $name_re ]]; then
name=${BASH_REMATCH[1]}
op=${BASH_REMATCH[2]}
if [[ -z ${named[$name]+x} ]] || ((R_GONE[${named[$name]:-0}])); then
err "no package '$name' in the list (l to redraw)"
continue
fi
i=${named[$name]}
if ((${#batch[@]})); then session_trust "${batch[@]}"; batch=(); fi
if [[ $op == '?' ]]; then
print_info "${R_NAME[i]}" "${R_OLD[i]}" || true
else
print_full_info "${R_NAME[i]}" "${R_OLD[i]}" || true
fi
else
if ((${#batch[@]})); then session_trust "${batch[@]}"; batch=(); fi
case $tok in
+6 -4
View File
@@ -24,12 +24,14 @@ setup() { common_setup; }
assert_line 'usage: skeptical-updater [options] [command]'
}
@test "--version and -V print the version" {
@test "--version and -v print the version" {
run skup --version
assert_success
assert_output 'skeptical-updater 0.1.0'
run skup -V
assert_output 'skeptical-updater 0.1.0'
assert_output --regexp '^skeptical-updater [0-9]+\.[0-9]+\.[0-9]+(\+(alpha|beta|rc)[.-]?[0-9]+)?$'
local version=$output
run skup -v
assert_success
assert_output "$version"
}
@test "an unknown option is a usage error (exit 1)" {
+48
View File
@@ -101,6 +101,54 @@ UPGRADE='apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--forc
log_has 'apt-cache show --no-all-versions su-dummy-a'
}
@test "listed package names support both information commands alongside numeric commands" {
run skup --no-refresh <<<$'su-dummy-a? 2? su-dummy-b?? 3-\nq'
assert_success
assert_line 'su-dummy-a: dummy package su-dummy-a for skeptical-updater tests'
assert_line 'su-dummy-b: dummy package su-dummy-b for skeptical-updater tests'
assert_line ' Package: su-dummy-b'
assert_line 'excluded from this run: su-dummy-c'
log_has 'dpkg-query -s -- su-dummy-b'
}
@test "unknown or hidden package names report an error and do not stop later commands" {
write_state "$HEADER" "apt${T}su-dummy-a${T}user${T}2026-01-01T00:00:00Z"
run skup --no-refresh <<<$'missing? su-dummy-a?? 2?\nq'
assert_success
assert_output --partial "no package 'missing' in the list"
assert_output --partial "no package 'su-dummy-a' in the list"
assert_line 'su-dummy-c: dummy package su-dummy-c for skeptical-updater tests'
}
@test "names work for trusted rows shown with --all and new dependencies" {
write_state "$HEADER" "apt${T}su-dummy-a${T}user${T}2026-01-01T00:00:00Z"
run skup --no-refresh --all <<<$'su-dummy-a?\nq'
assert_success
assert_line 'su-dummy-a: dummy package su-dummy-a for skeptical-updater tests'
FAKE_APT_SIM=real run skup --no-refresh <<<$'su-dummy-newdep? su-dummy-newdep??\nq'
assert_success
assert_line 'reason: not installed'
log_lacks '^dpkg-query -s -- su-dummy-newdep'
log_has 'apt-cache show --no-all-versions su-dummy-newdep'
}
@test "a package removed during a line cannot be queried by name" {
run skup --no-refresh <<<$'3x su-dummy-c? 2?\ny\nq'
assert_success
assert_output --partial "no package 'su-dummy-c' in the list"
assert_line 'su-dummy-b: dummy package su-dummy-b for skeptical-updater tests'
}
@test "package names are not accepted for trust, exclude or purge" {
for token in su-dummy-a+ su-dummy-a- su-dummy-ax; do
run skup --no-refresh <<<"$token"$'\nq'
assert_success
assert_output --partial "unknown command '$token'"
[[ ! -e $STATE_FILE ]]
log_lacks '^apt-get -y purge'
done
}
@test "N?? on a new dependency (old version -) never looks for an installed record" {
FAKE_APT_SIM=real run skup --no-refresh <<<$'9??\nq'
assert_success