Notice the battery, and the machine it is or is not in
Panama had no idea whether it was running on a laptop. No upower, no battery, no lid, no AC: hypridle.conf says "This is a desktop" in its own header, and that was true of the code as well as the machine. panama-hw answers hardware questions one at a time, exits 0 or 1, and prints nothing, so scripts, services and contracts all ask the same way. The definition the rest of the laptop work hangs on is one line: clamshell is lid-closed AND an external monitor. A machine with no mains supply at all reports as being on wall power, because a desktop cannot run out of it. The battery service follows Vitals: sysfs through FileView, an availability flag, and no subprocess on the timer. Globbing is the one thing QML cannot do -- a battery is BAT0 or BAT1 or CMB0, mains is AC or ADP1 or ACAD -- so panama-battery resolves the names once and the shell reads the files directly after. Nothing falls back to a plausible zero: a desktop shows no indicator, no card, and no charge limit control where the firmware has no ceiling. Also repairs two contracts that were already failing and had not been noticed, because only the full suite runs them. The dependency scanner treated line-initial variable assignments, case labels, comments and heredoc bodies as commands, and `count`, `host`, `cancel` and `import` are all real binaries on Fedora, so `command -v` could not filter them out. It now drops comments and heredoc bodies and requires a command to be followed by whitespace. Verified it still catches a genuinely undeclared dependency rather than passing quietly. The launcher command contract had not been told about the fourteen commands added earlier today.
This commit is contained in:
@@ -72,6 +72,9 @@ package_for() {
|
||||
wl-copy|wl-paste) printf 'wl-clipboard' ;;
|
||||
ssh-keygen) printf 'openssh' ;;
|
||||
ssh|ssh-add) printf 'openssh-clients' ;;
|
||||
# The Wayland build is the one that can inject into this session; the
|
||||
# x11 one cannot. desktop-packages declares it under that name.
|
||||
espanso) printf 'espanso-wayland' ;;
|
||||
rg) printf 'ripgrep' ;;
|
||||
xdg-mime|xdg-settings|xdg-open) printf 'xdg-utils' ;;
|
||||
update-desktop-database|desktop-file-validate) printf 'desktop-file-utils' ;;
|
||||
@@ -114,13 +117,50 @@ while read -r script; do
|
||||
|
||||
missing+=("$cmd (from $(basename "$script"), package: $pkg)")
|
||||
done < <({
|
||||
# Heredoc bodies are not shell. A python or sql block embedded in a
|
||||
# script is scanned as though every line began a command, and its
|
||||
# keywords collide with real binaries often enough to matter: `import`
|
||||
# is ImageMagick, `time` is a package, `select` is shell syntax. The
|
||||
# scanner cannot parse those languages and should not try, so the
|
||||
# bodies are dropped before anything else looks at them.
|
||||
scanned="$(awk '
|
||||
# Whole-line comments. These files explain themselves at length,
|
||||
# and prose containing "; cancel it" or "| list" reads as a
|
||||
# statement to a line-based scanner. Dropped first so a comment
|
||||
# mentioning <<EOF cannot open a heredoc either.
|
||||
!inbody && /^[[:space:]]*#/ { next }
|
||||
|
||||
# <<MARKER, <<-MARKER, <<"MARKER", <<'"'"'MARKER'"'"' -- with or
|
||||
# without a command in front of it.
|
||||
!inbody && match($0, /<<-?[[:space:]]*["'"'"']?[A-Za-z_][A-Za-z0-9_]*["'"'"']?/) {
|
||||
marker = substr($0, RSTART, RLENGTH)
|
||||
gsub(/^<<-?[[:space:]]*["'"'"']?|["'"'"']?$/, "", marker)
|
||||
inbody = 1
|
||||
print
|
||||
next
|
||||
}
|
||||
inbody {
|
||||
line = $0
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", line)
|
||||
if (line == marker) inbody = 0
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$script")"
|
||||
|
||||
# Statement-initial or after a pipe.
|
||||
grep -oE '(^|[|;&]|\$\()[[:space:]]*[a-z][a-z0-9_-]+' "$script" \
|
||||
| grep -oE '[a-z][a-z0-9_-]+$'
|
||||
#
|
||||
# A word followed by `=` is a variable assignment and a word followed
|
||||
# by `)` is a case label; neither is a command, and both collide with
|
||||
# real binaries -- `count`, `host` and `cancel` are all installed on a
|
||||
# normal Fedora machine, so `command -v` below cannot filter them out.
|
||||
# Requiring whitespace or end-of-line after the word excludes both.
|
||||
grep -oE '(^|[|;&]|\$\()[[:space:]]*[a-z][a-z0-9_-]+([[:space:]]|$)' <<<"$scanned" \
|
||||
| grep -oE '[a-z][a-z0-9_-]+'
|
||||
|
||||
# Behind a wrapper. ddcutil is always invoked as `timeout 10 ddcutil`,
|
||||
# so it never appears statement-initial and was missed entirely.
|
||||
grep -oE '\b(timeout[[:space:]]+[0-9.]+|sudo|nohup|env)[[:space:]]+[a-z][a-z0-9_-]+' "$script" \
|
||||
grep -oE '\b(timeout[[:space:]]+[0-9.]+|sudo|nohup|env)[[:space:]]+[a-z][a-z0-9_-]+' <<<"$scanned" \
|
||||
| grep -oE '[a-z][a-z0-9_-]+$'
|
||||
|
||||
# `command -v X` is how these helpers probe for a tool before using it,
|
||||
|
||||
Reference in New Issue
Block a user