#!/bin/sh set -eu BASE_URL="https://pois.zyrntopo.com/downloads" VERSION_URL="https://zyrntopo.com/version.json" PREFIX="${ZYRN_PREFIX:-$HOME/.local}" RED=''; GRN=''; YLW=''; DIM=''; RST='' if [ -t 1 ] && [ "${TERM:-dumb}" != "dumb" ]; then RED=$(printf '\033[31m'); GRN=$(printf '\033[32m'); YLW=$(printf '\033[33m') DIM=$(printf '\033[2m'); RST=$(printf '\033[0m') fi say() { printf '%s\n' "$*"; } info() { printf '%s==>%s %s\n' "$GRN" "$RST" "$*"; } warn() { printf '%s warning:%s %s\n' "$YLW" "$RST" "$*" >&2; } die() { printf '%s error:%s %s\n' "$RED" "$RST" "$*" >&2; exit 1; } need() { command -v "$1" >/dev/null 2>&1; } ACTION="${ZYRN_ACTION:-${1:-install}}" usage() { cat <<'USAGE' ZYRNTOPO installer First install: curl -fsSL https://zyrntopo.com/install.sh | sh After that, manage it with the zyrntopo command: zyrntopo launch the app zyrntopo update upgrade to the current release zyrntopo version show installed and current versions zyrntopo remove uninstall (keeps your saved maps) zyrntopo help this text Architectures: x86_64 (amd64) and arm64 (aarch64), detected automatically. There is no 32-bit build — Electron dropped 32-bit Linux in 2019. Environment: ZYRN_VERSION=1.16.8 pin a version (only the current release is hosted) ZYRN_METHOD=appimage force the no-root AppImage even on deb/rpm/Arch ZYRN_PREFIX=~/.local where the AppImage install lives (default ~/.local) Native packages are installed directly rather than from a repository, so `apt upgrade` and friends will not see new ZYRNTOPO releases — use `zyrntopo update`. USAGE } case "$ACTION" in help|-h|--help) usage; exit 0 ;; update|upgrade) ACTION=install ;; remove) ACTION=uninstall ;; esac write_cli() { _target="$1"; _dest="$2"; _sudo="${3:-}" $_sudo sh -c "cat > '$_dest'" </dev/null | grep -q .; then printf 'deb %s\n' "$(dpkg-query -W -f='${Version}' zyrntopo-desktop 2>/dev/null)" elif need rpm && rpm -q zyrntopo-desktop >/dev/null 2>&1; then printf 'rpm %s\n' "$(rpm -q --qf '%{VERSION}' zyrntopo-desktop 2>/dev/null)" elif need pacman && pacman -Q zyrntopo-desktop >/dev/null 2>&1; then printf 'pacman %s\n' "$(pacman -Q zyrntopo-desktop 2>/dev/null | awk '{print $2}')" else printf 'not installed\n' fi } if [ "$ACTION" = "uninstall" ] || [ "$ACTION" = "remove" ]; then REMOVED=0 FAILED=0 if need dpkg-query && dpkg-query -W zyrntopo-desktop >/dev/null 2>&1; then info "Removing the .deb package"; sudo_for if $SUDO apt-get remove -y zyrntopo-desktop 2>/dev/null || $SUDO dpkg -r zyrntopo-desktop then REMOVED=1; else warn "could not remove the .deb package"; FAILED=1; fi fi if need rpm && rpm -q zyrntopo-desktop >/dev/null 2>&1; then info "Removing the .rpm package"; sudo_for if need dnf; then $SUDO dnf remove -y zyrntopo-desktop elif need zypper; then $SUDO zypper --non-interactive remove zyrntopo-desktop else $SUDO rpm -e zyrntopo-desktop fi && REMOVED=1 || { warn "could not remove the .rpm package"; FAILED=1; } fi if need pacman && pacman -Q zyrntopo-desktop >/dev/null 2>&1; then info "Removing the pacman package"; sudo_for if $SUDO pacman -R --noconfirm zyrntopo-desktop then REMOVED=1; else warn "could not remove the pacman package"; FAILED=1; fi fi if [ -e /usr/local/bin/zyrntopo ]; then sudo_for; $SUDO rm -f /usr/local/bin/zyrntopo; REMOVED=1 fi if [ -e "$PREFIX/lib/zyrntopo/zyrntopo.AppImage" ] || [ -e "$PREFIX/bin/zyrntopo" ]; then info "Removing the AppImage install from $PREFIX" rm -f "$PREFIX/bin/zyrntopo" "$PREFIX/share/applications/zyrntopo.desktop" rm -rf "$PREFIX/lib/zyrntopo" rm -f "$PREFIX"/share/icons/hicolor/*/apps/zyrntopo.png need update-desktop-database && update-desktop-database "$PREFIX/share/applications" 2>/dev/null || true need gtk-update-icon-cache && gtk-update-icon-cache -qtf "$PREFIX/share/icons/hicolor" 2>/dev/null || true REMOVED=1 fi if [ "$REMOVED" -eq 0 ] && [ "$FAILED" -eq 0 ]; then warn "ZYRNTOPO does not appear to be installed." exit 0 fi if [ "$FAILED" -ne 0 ]; then say "" die "some parts could not be removed (see the warnings above). If sudo could not prompt, re-run it from a terminal: zyrntopo remove" fi say "" info "ZYRNTOPO removed." say "${DIM} Your saved maps and offline tiles were kept, in ~/.config/ZYRNTOPO.${RST}" say "${DIM} Delete them too with: rm -rf ~/.config/ZYRNTOPO${RST}" exit 0 fi VERSION="${ZYRN_VERSION:-}" if [ -z "$VERSION" ]; then info "Resolving the current release…" VERSION_JSON=$(fetch_stdout "$VERSION_URL" 2>/dev/null | tr -d ' \t\r\n') || true VERSION=$(printf '%s' "$VERSION_JSON" \ | sed -n 's/.*"platforms":{[^}]*"linux":"\([^"]*\)".*/\1/p') [ -n "$VERSION" ] || VERSION=$(printf '%s' "$VERSION_JSON" \ | sed -n 's/.*"version":"\([^"]*\)".*/\1/p') [ -n "$VERSION" ] || die "could not read the current version from $VERSION_URL. Pin one explicitly: ZYRN_VERSION=1.16.8 sh install.sh" fi if [ "$ACTION" = "version" ]; then say "installed: $(installed_version)" say "published: $VERSION" exit 0 fi info "ZYRNTOPO $VERSION ($ARCH)" METHOD="${ZYRN_METHOD:-}" if [ -z "$METHOD" ]; then if need apt-get || need dpkg; then METHOD=deb elif need pacman; then METHOD=pacman elif need dnf || need yum || need zypper || need rpm; then METHOD=rpm else METHOD=appimage fi fi if [ "$ZARCH" = "arm64" ]; then DEB_ARCH=arm64; RPM_ARCH=aarch64; PAC_ARCH=aarch64; APP_ARCH=arm64 else DEB_ARCH=amd64; RPM_ARCH=x86_64; PAC_ARCH=x64; APP_ARCH=x86_64 fi case "$METHOD" in deb) FILE="zyrntopo-$VERSION-linux-$DEB_ARCH.deb" ;; rpm) FILE="zyrntopo-$VERSION-linux-$RPM_ARCH.rpm" ;; pacman) FILE="zyrntopo-$VERSION-linux-$PAC_ARCH.pacman" ;; appimage) FILE="zyrntopo-$VERSION-linux-$APP_ARCH.AppImage" ;; *) die "unknown ZYRN_METHOD '$METHOD' (expected deb, rpm, pacman or appimage)." ;; esac TMP=$(mktemp -d "${TMPDIR:-/tmp}/zyrntopo.XXXXXXXX") || die "could not create a temp directory." cleanup() { rm -rf "$TMP"; } trap cleanup EXIT INT TERM info "Downloading $FILE" say "${DIM} $BASE_URL/$FILE${RST}" fetch "$BASE_URL/$FILE" "$TMP/$FILE" \ || die "download failed. Is $VERSION a published release? Browse what is available: https://zyrntopo.com/#download" if fetch "$BASE_URL/SHA256SUMS-$VERSION.txt" "$TMP/SHA256SUMS" 2>/dev/null; then WANT=$(sed -n "s/^\([0-9a-f]\{64\}\) *$FILE\$/\1/p" "$TMP/SHA256SUMS" | head -n1) if [ -n "$WANT" ]; then if need sha256sum; then GOT=$(sha256sum "$TMP/$FILE" | cut -d' ' -f1) elif need shasum; then GOT=$(shasum -a 256 "$TMP/$FILE" | cut -d' ' -f1) else GOT=""; warn "no sha256sum/shasum on this system — skipping verification." fi if [ -n "$GOT" ]; then [ "$GOT" = "$WANT" ] || die "CHECKSUM MISMATCH for $FILE. expected $WANT got $GOT The download was corrupted or tampered with. Nothing was installed." info "Checksum verified." fi else warn "no checksum listed for $FILE — continuing unverified." fi else warn "checksum file unavailable — continuing unverified." fi SUDO="" [ "$METHOD" = "appimage" ] || sudo_for case "$METHOD" in deb) info "Installing with apt" if need apt-get; then $SUDO apt-get install -y "$TMP/$FILE" else $SUDO dpkg -i "$TMP/$FILE" || { $SUDO apt-get -f install -y && $SUDO dpkg -i "$TMP/$FILE"; } fi ;; rpm) if need dnf; then info "Installing with dnf"; $SUDO dnf install -y "$TMP/$FILE" elif need zypper; then info "Installing with zypper"; $SUDO zypper --non-interactive install --allow-unsigned-rpm "$TMP/$FILE" elif need yum; then info "Installing with yum"; $SUDO yum install -y "$TMP/$FILE" else info "Installing with rpm"; $SUDO rpm -Uvh --replacepkgs "$TMP/$FILE" fi ;; pacman) info "Installing with pacman" $SUDO pacman -U --noconfirm "$TMP/$FILE" ;; appimage) info "Installing the AppImage into $PREFIX" mkdir -p "$PREFIX/bin" "$PREFIX/lib/zyrntopo" "$PREFIX/share/applications" "$PREFIX/share/icons/hicolor/512x512/apps" install -m755 "$TMP/$FILE" "$PREFIX/lib/zyrntopo/zyrntopo.AppImage" chmod +x "$TMP/$FILE" ( cd "$TMP" && "./$FILE" --appimage-extract 'usr/share/icons/*' >/dev/null 2>&1 ) || true ICONS_FOUND=0 for src in "$TMP"/squashfs-root/usr/share/icons/hicolor/*/apps/zyrntopo.png; do [ -f "$src" ] || continue dim=$(basename "$(dirname "$(dirname "$src")")") mkdir -p "$PREFIX/share/icons/hicolor/$dim/apps" install -m644 "$src" "$PREFIX/share/icons/hicolor/$dim/apps/zyrntopo.png" ICONS_FOUND=$((ICONS_FOUND + 1)) done if [ "$ICONS_FOUND" -gt 0 ]; then need gtk-update-icon-cache && \ gtk-update-icon-cache -qtf "$PREFIX/share/icons/hicolor" 2>/dev/null || true else warn "could not extract the application icon — the launcher will use a generic one." fi cat > "$PREFIX/lib/zyrntopo/launch" <<'LAUNCHER' #!/bin/sh APPIMAGE="$(dirname "$(readlink -f "$0")")/zyrntopo.AppImage" if (ldconfig -p 2>/dev/null | grep -q 'libfuse\.so\.2') \ || [ -e /usr/lib/libfuse.so.2 ] || [ -e /usr/lib64/libfuse.so.2 ] \ || [ -e /usr/lib/x86_64-linux-gnu/libfuse.so.2 ] \ || [ -e /usr/lib/aarch64-linux-gnu/libfuse.so.2 ]; then exec "$APPIMAGE" "$@" fi exec "$APPIMAGE" --appimage-extract-and-run "$@" LAUNCHER chmod 755 "$PREFIX/lib/zyrntopo/launch" write_cli "$PREFIX/lib/zyrntopo/launch" "$PREFIX/bin/zyrntopo" cat > "$PREFIX/share/applications/zyrntopo.desktop" </dev/null || true case ":$PATH:" in *":$PREFIX/bin:"*) ;; *) warn "$PREFIX/bin is not on your PATH — add it to run 'zyrntopo' from a shell: echo 'export PATH=\"$PREFIX/bin:\$PATH\"' >> ~/.profile" ;; esac ;; esac if [ "$METHOD" != "appimage" ]; then for real in /opt/ZYRNTOPO/zyrntopo /usr/lib/zyrntopo-desktop/zyrntopo; do [ -x "$real" ] || continue $SUDO mkdir -p /usr/local/bin write_cli "$real" /usr/local/bin/zyrntopo "$SUDO" break done fi say "" info "ZYRNTOPO $VERSION installed." say "${DIM} zyrntopo update / version / remove — manage it from the terminal${RST}" say "${DIM} Launch it from your application menu, or run: zyrntopo${RST}" say "${DIM} Docs: https://zyrntopo.com/documentation${RST}"