Build the two applications nobody packages, on purpose rather than in passing
Claude Desktop and ChatGPT Desktop ship for macOS and Windows. The Linux path for both is a community wrapper that converts the official build into an RPM -- so what lands is still a package dnf owns and can remove, which is the part of the dnf/flatpak rule that actually matters. What they need an exception for is the build itself, and there is no packaged form to prefer over it. `panama app` builds one by name, and is deliberately not part of ./install. A source build is slow, wants the network throughout, and depends on an upstream that moves -- twenty minutes in, an error, with nobody at the keyboard, which is the exact failure the interview exists to prevent. Asking for one is something you do on purpose, and it is also the rebuild path when a new version ships. Nothing is pinned. Each build takes the current default branch and the current upstream release, and reports a failure rather than working around it, leaving the tree where the error can be read. sunhat pinned versions and every pin was a 404 within a release cycle. Adding one is adding a file to setup/apps/, and the file has to say why the exception exists -- the contract fails a definition that does not, because the guard against this list growing by habit is having to write the reason down. sunhat had seventy-odd installers and a reason recorded for none of them. The contract had a bug worth recording: `while read` on the right of a pipe runs in a subshell, so two of its three per-definition checks recorded findings into an array that went out of scope at the end of the loop. It reported PASS on a definition with no description and no build function. Found by standing one in deliberately and noticing only the third check spoke up. Also: nautilus-open-any-terminal is now declared, and Panama's copy of the extension is gone. Fedora packages that extension AND its gsettings schema, and Panama shipped its own fork of the .py over the same path while declaring neither -- so a fresh machine got an extension whose schema did not exist. It worked here only because the RPM has been installed since sunhat. The fork was also 63 lines behind the packaged version, missing its newer Nautilus and Caja handling. Auditing the rest of config/copy for the same shape found nothing else: dnf.conf is a config file its package expects to be replaced, and the GPU udev rules are Panama's own. 125 contracts pass. Claude-Session: https://claude.ai/code/session_01NvgBuSWB5sE43yWmg21ozj
This commit is contained in:
+83
@@ -10,6 +10,7 @@
|
||||
# doctor Report what is actually running on this machine
|
||||
# test Run every contract under tests/
|
||||
# upgrade Re-run the installer from anywhere
|
||||
# app Build and install an application that no repository packages
|
||||
# help Show this help
|
||||
#
|
||||
# Designed to grow: add new subcommands as cmd_<name> functions and
|
||||
@@ -71,6 +72,8 @@ ${BOLD}Commands:${RESET}
|
||||
a subset: 'panama test dock' runs the ones matching 'dock'.
|
||||
${GREEN}upgrade${RESET} Re-run ./install from anywhere. Safe: every stage is
|
||||
idempotent and this is the documented upgrade path.
|
||||
${GREEN}app${RESET} Build and install an application that neither dnf nor
|
||||
Flathub carries. With no name, lists what is available.
|
||||
${GREEN}help${RESET} Show this help (also -h, --help).
|
||||
|
||||
${BOLD}Options:${RESET}
|
||||
@@ -83,6 +86,8 @@ ${BOLD}Examples:${RESET}
|
||||
$PROGRAM doctor --summary
|
||||
$PROGRAM test dock
|
||||
$PROGRAM upgrade
|
||||
$PROGRAM app
|
||||
$PROGRAM app claude-desktop
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -311,6 +316,83 @@ cmd_upgrade() {
|
||||
exec "$installer" "$@"
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Command: app
|
||||
# ----------------------------------------------------------------------------
|
||||
#
|
||||
# The applications that neither dnf nor Flathub carries, built from source into
|
||||
# a package dnf can still own and remove.
|
||||
#
|
||||
# Deliberately NOT part of ./install. A source build is slow, wants the network
|
||||
# for the whole of it, and depends on an upstream that moves -- which is exactly
|
||||
# the failure the interview exists to prevent: twenty minutes in, a prompt or an
|
||||
# error, with nobody at the keyboard. Asking for one of these is a thing you do
|
||||
# on purpose, and it is also the rebuild path when a new version ships.
|
||||
#
|
||||
# Nothing is pinned. Each build takes the current default branch and the current
|
||||
# upstream release, and says so when it fails. A recorded version is a 404
|
||||
# waiting to happen -- sunhat proved that three times over.
|
||||
APPS_DIR="$PANAMA_DIR/setup/apps"
|
||||
APPS_WORK="${XDG_CACHE_HOME:-$HOME/.cache}/panama/apps"
|
||||
|
||||
cmd_app() {
|
||||
local name="${1:-}"
|
||||
|
||||
if [[ -z "$name" ]]; then
|
||||
header "Applications"
|
||||
printf 'Built from source, because no repository carries them.\n\n'
|
||||
local file
|
||||
for file in "$APPS_DIR"/*; do
|
||||
[[ -f "$file" ]] || continue
|
||||
local description=""
|
||||
# shellcheck source=/dev/null
|
||||
source "$file"
|
||||
printf ' %s%-18s%s %s\n' "$GREEN" "$(basename "$file")" "$RESET" "$description"
|
||||
done
|
||||
printf '\nBuild one with: %s%s app <name>%s\n' "$BOLD" "$PROGRAM" "$RESET"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local definition="$APPS_DIR/$name"
|
||||
if [[ ! -f "$definition" ]]; then
|
||||
err "No such application: '$name'"
|
||||
printf "Run '%s app' to see what is available.\n" "$PROGRAM" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local description="" repo=""
|
||||
# shellcheck source=/dev/null
|
||||
source "$definition"
|
||||
[[ -n "$repo" ]] || { err "$name declares no repository"; exit 1; }
|
||||
|
||||
# The checkout lives in the cache because it is entirely rebuildable and
|
||||
# should never be mistaken for something to keep. Existing checkouts are
|
||||
# reset to upstream rather than merged: a local edit in a build tree is not
|
||||
# something to preserve silently.
|
||||
local tree="$APPS_WORK/$name"
|
||||
if [[ -d "$tree/.git" ]]; then
|
||||
info "Updating $name"
|
||||
git -C "$tree" fetch --depth 1 origin HEAD || { err "Could not reach $repo"; exit 1; }
|
||||
git -C "$tree" reset --hard FETCH_HEAD >/dev/null
|
||||
else
|
||||
info "Cloning $name"
|
||||
mkdir -p "$APPS_WORK"
|
||||
rm -rf "$tree"
|
||||
git clone --depth 1 "$repo" "$tree" || { err "Could not clone $repo"; exit 1; }
|
||||
fi
|
||||
|
||||
info "Building ${BOLD}${name}${RESET} — this takes a while and needs the network"
|
||||
if ( cd "$tree" && build ); then
|
||||
ok "$name installed"
|
||||
printf 'Built from %s\n' "$(git -C "$tree" rev-parse --short HEAD)"
|
||||
else
|
||||
err "$name failed to build"
|
||||
printf 'The tree is left at %s so the failure can be read.\n' "$tree" >&2
|
||||
printf 'This builds against upstream HEAD, so a break there breaks this.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Dispatcher
|
||||
# ----------------------------------------------------------------------------
|
||||
@@ -322,6 +404,7 @@ main() {
|
||||
doctor) shift; cmd_doctor "$@" ;;
|
||||
test) shift; cmd_test "$@" ;;
|
||||
upgrade) shift; cmd_upgrade "$@" ;;
|
||||
app) shift; cmd_app "$@" ;;
|
||||
help|-h|--help|"") usage ;;
|
||||
--version) printf '%s %s\n' "$PROGRAM" "$VERSION" ;;
|
||||
*)
|
||||
|
||||
Reference in New Issue
Block a user