Files
Triple-C/scripts/install-appimage.sh
T
shadowdaoandClaude Opus 5 7a5c0c1f13
Secret Scan / scan (push) Successful in 8s
Secret Scan / scan (pull_request) Successful in 8s
Retire the Arch package, document AppImage desktop integration
The `triple-c-bin` package was never on the AUR, so installing it meant
downloading a file and running `pacman -U` — the same gesture as making an
AppImage executable, for a second artifact to keep building. And being
`workflow_dispatch`-only it reached 1 release in 28 (only v0.4.16 has a
`.pkg.tar.zst`), while HOW-TO-USE.md told Arch and CachyOS users to download
it from every release. A distribution channel that is absent 27 times out of
28 is worse than not promising one.

`packaging/arch/` and `.gitea/workflows/publish-arch-package.yml` are
preserved whole on `hold/arch-packaging`, the same way the disk panel and
drag-out work were held rather than deleted. What would make an Arch package
worth having is an AUR account and its SSH key as a repo secret — both
one-time manual steps that never happened; the workflow's own header already
said as much about its AUR push step.

This also closes the gap that prompted the review: nothing validated the
PKGBUILD until someone manually dispatched the workflow, making it the only
packaging path with no CI coverage. Removing it removes the untested surface
rather than adding a job to test something nobody installs.

In its place, `scripts/install-appimage.sh` does what a package manager's
install hooks would. An AppImage carries a `.desktop` entry and icons inside
itself, but nothing on the host reads them, so it never appears in the app
launcher. The script extracts the bundled icons into the user's icon theme
and writes a launcher entry — no sudo, nothing outside `~/.local/share`, and
the AppImage itself is never copied or moved.

Two details it gets right on purpose:

  * The `Exec` line is rewritten, not copied. The bundled entry says
    `Exec=triple-c`, which resolves only inside the running AppImage's own
    mount — a verbatim copy gives a launcher entry that starts nothing.
  * Extraction uses `--appimage-extract`, which needs no FUSE, so the script
    works on a machine where *running* the AppImage would first need
    `fuse2` installed. That requirement is now documented too: Arch and
    CachyOS do not ship FUSE 2 by default.

Verified against the real artifact — the AppImage from this repo's own
preview-3a49a67 release: 4 icon sizes install, `desktop-file-validate` passes
with no warnings, `--uninstall` leaves nothing behind, and shellcheck is
clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ApLYH6ybHwQFkMCtKuHrrV
2026-08-28 13:11:26 -07:00

128 lines
4.5 KiB
Bash
Executable File

#!/bin/sh
# Register a Triple-C AppImage with the desktop, so it appears in the app
# launcher with its icon instead of only being runnable from a file manager.
#
# An AppImage is a single executable file and nothing more: it ships a
# `.desktop` entry and icons *inside* itself, but nothing on the host ever
# reads them, because nothing installed it. This script does what a package
# manager's install hooks would — copies the icons into the user's icon theme
# and writes a `.desktop` entry pointing at wherever the AppImage actually
# lives.
#
# ./scripts/install-appimage.sh ~/Apps/Triple-C_0.4.17_amd64.AppImage
# ./scripts/install-appimage.sh --uninstall
#
# Everything goes under ~/.local/share, so there is no sudo and no root-owned
# file to clean up later. The AppImage itself is never copied or moved — the
# launcher entry points at the path you give here, so keep the file somewhere
# stable (`~/Apps` or `~/.local/bin`, not `~/Downloads`) or re-run this after
# moving it.
#
# Extraction uses `--appimage-extract`, which unpacks the payload directly and
# needs no FUSE. So this script works even on a system where *running* the
# AppImage would need `fuse2` installed first.
set -eu
APP_ID="triple-c"
DESKTOP_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/applications"
ICON_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor"
DESKTOP_FILE="${DESKTOP_DIR}/${APP_ID}.desktop"
refresh_caches() {
# Both are best-effort: a minimal desktop may ship neither, and neither
# failing means the install did not work.
if command -v update-desktop-database >/dev/null 2>&1; then
update-desktop-database "${DESKTOP_DIR}" 2>/dev/null || true
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
gtk-update-icon-cache -f -t "${ICON_DIR}" 2>/dev/null || true
fi
}
uninstall() {
rm -f "${DESKTOP_FILE}"
find "${ICON_DIR}" -name "${APP_ID}.png" -delete 2>/dev/null || true
refresh_caches
echo "Removed the Triple-C launcher entry and icons."
echo "The AppImage itself was not touched."
}
if [ "${1:-}" = "--uninstall" ]; then
uninstall
exit 0
fi
APPIMAGE="${1:-}"
if [ -z "${APPIMAGE}" ]; then
echo "usage: $0 [--uninstall] /path/to/Triple-C_<version>_amd64.AppImage" >&2
exit 2
fi
if [ ! -f "${APPIMAGE}" ]; then
echo "No such file: ${APPIMAGE}" >&2
exit 1
fi
# An absolute path, because the .desktop Exec line is read from anywhere.
APPIMAGE=$(cd "$(dirname "${APPIMAGE}")" && printf '%s/%s' "$(pwd)" "$(basename "${APPIMAGE}")")
if [ ! -x "${APPIMAGE}" ]; then
echo "Making ${APPIMAGE} executable"
chmod +x "${APPIMAGE}"
fi
WORK=$(mktemp -d)
# shellcheck disable=SC2064 # WORK is expanded now on purpose.
trap "rm -rf '${WORK}'" EXIT INT TERM
echo "Extracting bundled icons from $(basename "${APPIMAGE}")..."
( cd "${WORK}" && "${APPIMAGE}" --appimage-extract >/dev/null )
SRC="${WORK}/squashfs-root"
if [ ! -d "${SRC}/usr/share/icons/hicolor" ]; then
echo "That AppImage has no bundled icons — is it really Triple-C?" >&2
exit 1
fi
# Copy every size the bundle ships, keeping the theme's directory layout.
COUNT=0
while IFS= read -r icon; do
[ -n "${icon}" ] || continue
rel=${icon#"${SRC}/usr/share/icons/hicolor/"}
install -Dm644 "${icon}" "${ICON_DIR}/${rel}"
COUNT=$((COUNT + 1))
done <<EOF
$(find "${SRC}/usr/share/icons/hicolor" -name "${APP_ID}.png")
EOF
echo "Installed ${COUNT} icon size(s) into ${ICON_DIR}"
# Written rather than copied from the bundle. The bundled entry has
# `Exec=triple-c`, which resolves only inside the running AppImage's own mount
# — from the host it names a binary that is not on PATH, so the launcher entry
# would appear and then fail to start anything. `Categories` is empty in the
# bundle too, which leaves the entry to fall into "Other" in most menus.
# `StartupWMClass` is kept exactly as the bundle sets it: it is what lets the
# shell match the running window to this entry, so the taskbar shows the real
# icon instead of a generic placeholder.
mkdir -p "${DESKTOP_DIR}"
cat > "${DESKTOP_FILE}" <<DESKTOP
[Desktop Entry]
Type=Application
Name=Triple-C
Comment=Run Claude Code sandboxed in Docker containers
Exec=${APPIMAGE} %U
Icon=${APP_ID}
Terminal=false
Categories=Development;
StartupWMClass=${APP_ID}
DESKTOP
chmod 644 "${DESKTOP_FILE}"
echo "Wrote ${DESKTOP_FILE}"
refresh_caches
echo
echo "Done. Triple-C should now be in your app launcher."
echo "If the icon is generic or the entry is missing, log out and back in —"
echo "see \"App Icon Missing After Installing (Linux)\" in HOW-TO-USE.md."