Let applications be chosen a few at a time
The catalog held fourteen applications. This machine runs thirty-four flatpaks, so most of what is actually used had no way to be installed from here at all -- Zoom, Slack, Obsidian, Spotify, LibreOffice, OBS and its sixteen plugins. So the catalog is seeded from the machine, and `panama apps` opens it: pick a category, tick what you want, install just those. ./install still offers the same catalog as whole categories, because during a first install you want coarse and fast. Both read setup/lib/extras-catalog. Two parsers would eventually disagree about what a category contains, and the one that disagreed quietly would be the one that runs unattended. Two pieces of syntax earn their keep. A `| Name` suffix gives the menu something readable, since com.obsproject.Studio is not a name anybody wants to pick from a list. An indented line belongs to the entry above it, which is how OBS carries its plugins as one thing to tick rather than seventeen -- they are extensions of the flatpak, useless alone. That is also why creative moved from dnf to Flathub: the plugins attach only to the flatpak, so the dnf build cannot have them. The rest of the category followed rather than leave one machine with GIMP from dnf and its neighbour from Flathub. The contract now reads the catalog through the same parser instead of keeping a third idea of the format, and checks the two things this syntax can break silently: a label leaking into an install command, and a bundle that installs the application without its plugins. It caught a typo in the Pixelorama id on the first run. It also got slow enough to be worth fixing -- fifty-one names, each its own network call. One bulk query per manager took it from minutes to four seconds. That query needs `flatpak remote-ls --all`: without it, end-of-life applications are hidden and read as missing, which reported yuzu as gone from Flathub when it installs perfectly well. Claude-Session: https://claude.ai/code/session_01Q84axqUE5inJhf5Jz9CFy1
This commit is contained in:
Executable
+107
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Reading the optional-application catalog.
|
||||
#
|
||||
# Sourced by both front doors -- the interview's checklist during ./install, and
|
||||
# `panama apps` afterwards -- so the two can never disagree about what exists.
|
||||
# One catalog, parsed once, here.
|
||||
#
|
||||
# The format is one file per category under setup/packages/extras/, and a line
|
||||
# is one of:
|
||||
#
|
||||
# gimp a dnf package
|
||||
# flatpak:org.gimp.GIMP a Flathub id
|
||||
# flatpak:com.slack.Slack | Slack either, with a name for the menu
|
||||
# flatpak:com.obsproject.Studio.Plugin.SceneSwitcher
|
||||
#
|
||||
# An indented line belongs to the entry above it and is installed with it, which
|
||||
# is how OBS carries its sixteen plugin extensions as a single thing to tick
|
||||
# rather than seventeen. Comments and blank lines are ignored.
|
||||
#
|
||||
# Without an explicit name, the menu derives one from the last dotted component
|
||||
# of a Flathub id, or uses the package name as it stands.
|
||||
|
||||
# Every category name, one per line.
|
||||
catalog_categories() {
|
||||
local dir="$1" file
|
||||
for file in "$dir"/*; do
|
||||
[[ -f "$file" ]] && basename "$file"
|
||||
done
|
||||
}
|
||||
|
||||
# The selectable entries in one category, as `target<TAB>label`. Indented
|
||||
# continuation lines are folded into the entry above and never listed.
|
||||
catalog_entries() {
|
||||
local file="$1" line target label
|
||||
while IFS= read -r line; do
|
||||
line="${line%%#*}"
|
||||
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
|
||||
[[ "$line" =~ ^[[:space:]] ]] && continue
|
||||
|
||||
if [[ "$line" == *"|"* ]]; then
|
||||
target="${line%%|*}"
|
||||
label="${line#*|}"
|
||||
else
|
||||
target="$line"
|
||||
label=""
|
||||
fi
|
||||
target="${target#"${target%%[![:space:]]*}"}"; target="${target%"${target##*[![:space:]]}"}"
|
||||
label="${label#"${label%%[![:space:]]*}"}"; label="${label%"${label##*[![:space:]]}"}"
|
||||
|
||||
if [[ -z "$label" ]]; then
|
||||
label="${target#flatpak:}"
|
||||
[[ "$target" == flatpak:* ]] && label="${label##*.}"
|
||||
fi
|
||||
printf '%s\t%s\n' "$target" "$label"
|
||||
done <"$file"
|
||||
}
|
||||
|
||||
# Every install target for one entry: the entry itself, then the indented lines
|
||||
# beneath it. Called with the category file and the entry's target.
|
||||
catalog_targets() {
|
||||
local file="$1" want="$2" line target seen=0
|
||||
while IFS= read -r line; do
|
||||
line="${line%%#*}"
|
||||
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
|
||||
|
||||
if [[ "$line" =~ ^[[:space:]] ]]; then
|
||||
(( seen == 1 )) || continue
|
||||
line="${line#"${line%%[![:space:]]*}"}"
|
||||
printf '%s\n' "${line%%|*}" | sed 's/[[:space:]]*$//'
|
||||
continue
|
||||
fi
|
||||
|
||||
target="${line%%|*}"
|
||||
target="${target#"${target%%[![:space:]]*}"}"; target="${target%"${target##*[![:space:]]}"}"
|
||||
if [[ "$target" == "$want" ]]; then
|
||||
seen=1
|
||||
printf '%s\n' "$target"
|
||||
elif (( seen == 1 )); then
|
||||
break
|
||||
fi
|
||||
done <"$file"
|
||||
}
|
||||
|
||||
# Every install target in a whole category, in file order.
|
||||
catalog_all_targets() {
|
||||
local file="$1" line
|
||||
while IFS= read -r line; do
|
||||
line="${line%%#*}"
|
||||
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
|
||||
line="${line#"${line%%[![:space:]]*}"}"
|
||||
line="${line%%|*}"
|
||||
printf '%s\n' "${line%"${line##*[![:space:]]}"}"
|
||||
done <"$file"
|
||||
}
|
||||
|
||||
# Is this target already on the machine? Used to mark the menu, never to skip
|
||||
# silently -- a re-run that reinstalls what is present is harmless, but a menu
|
||||
# that hides it is confusing.
|
||||
catalog_installed() {
|
||||
local target="$1"
|
||||
if [[ "$target" == flatpak:* ]]; then
|
||||
flatpak info "${target#flatpak:}" >/dev/null 2>&1
|
||||
else
|
||||
rpm -q "$target" >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
Reference in New Issue
Block a user