App-Ariza.git | resources/templates/ | launcher-posix.sh.j2


#!/bin/sh
# {{ app_display }} {{ app_version }} — self-contained bundle launcher.
#
# Generated by ariza for {{ platform }}. Everything this script needs is
# inside the directory above it; nothing outside the bundle is read, and
# nothing is installed. Move the bundle anywhere, including a path with
# spaces, and this still works.

set -eu

ARIZA_DISPLAY='{{ app_display }}'

# Resolve this script to its physical path, following symlinks.
#
# Not `readlink -f`: that is a GNU extension, absent from macOS before
# Monterey and from the BSDs, and a launcher that only resolves symlinks
# on Linux is a launcher that breaks the moment someone puts one in
# ~/bin. Plain `readlink` is POSIX-ish and everywhere.
ariza_resolve() {
    ariza_target=$1
    while [ -L "$ariza_target" ]; do
        ariza_dir=$(cd -P -- "$(dirname -- "$ariza_target")" && pwd)
        ariza_link=$(readlink -- "$ariza_target")
        case $ariza_link in
            /*) ariza_target=$ariza_link ;;
            *)  ariza_target=$ariza_dir/$ariza_link ;;
        esac
    done
    ariza_dir=$(cd -P -- "$(dirname -- "$ariza_target")" && pwd)
    printf '%s\n' "$ariza_dir/$(basename -- "$ariza_target")"
}

# $0 is a bare name when some shells find us on PATH; ask the shell where.
ariza_self=$0
case $ariza_self in
    */*) ;;
    *)   ariza_self=$(command -v -- "$ariza_self" 2>/dev/null || printf '%s\n' "$0") ;;
esac
ariza_self=$(ariza_resolve "$ariza_self")

BUNDLE_ROOT=$(cd -P -- "$(dirname -- "$ariza_self")/.." && pwd)

ARIZA_RAKU="$BUNDLE_ROOT/rakudo/bin/raku"
if [ ! -x "$ARIZA_RAKU" ]; then
    printf '%s: this bundle is incomplete — no interpreter at %s\n' \
        "$ARIZA_DISPLAY" "$ARIZA_RAKU" >&2
    printf '  Unpack the whole archive and run this script from inside it.\n' >&2
    exit 1
fi

# The bundle is the only module repository. A RAKULIB or PERL6LIB left in
# the user's environment would otherwise put their own Raku modules —
# compiled against a different Rakudo — ahead of ours.
#
# It is the bundled runtime's own vendor repository, which is what makes
# the shipped bytecode usable here: Rakudo records a precompiled module's
# dependencies relative to the repository only when it has a name for it,
# and a path of our own choosing would not have one.
RAKULIB="inst#$BUNDLE_ROOT/{{ site }}"
export RAKULIB
unset PERL6LIB || :

# Notcurses::Native looks for its prebuilt libraries here, and sets
# TERMINFO_DIRS from the same location. Never NOTCURSES_NATIVE_LIB_DIR,
# which skips that and leaves the terminal without terminfo.
NOTCURSES_NATIVE_DATA_DIR="$BUNDLE_ROOT/native"
export NOTCURSES_NATIVE_DATA_DIR
{%- if sqlcipher and os == 'linux' %}

# ELF resolves DT_NEEDED and dlopen("libsqlcipher.so") by name, so the
# bundle's library directory has to be on the search path; DBIish also
# pre-loads the library by absolute path when told where it is.
LD_LIBRARY_PATH="$BUNDLE_ROOT/{{ sqlcipher_dir }}${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
export LD_LIBRARY_PATH
DBIISH_SQLCIPHER_LIB="$BUNDLE_ROOT/{{ sqlcipher_lib }}"
export DBIISH_SQLCIPHER_LIB
{%- endif %}

# A first launch compiles nothing (the bundle ships warm bytecode) but
# does have to page a few hundred megabytes off disk, so say so once.
ARIZA_STATE_DIR="${XDG_STATE_HOME:-${HOME:-/tmp}/.local/state}/{{ app_exec }}"
if [ ! -e "$ARIZA_STATE_DIR/.first-run" ]; then
    printf '%s: first launch can take a few seconds. Later ones are instant.\n' \
        "$ARIZA_DISPLAY" >&2
    if mkdir -p "$ARIZA_STATE_DIR" 2>/dev/null; then
        : >"$ARIZA_STATE_DIR/.first-run" 2>/dev/null || :
    fi
fi

# A warning, never a refusal: TERM is frequently wrong in environments
# where the app still runs fine, and a launcher that second-guesses the
# user's terminal is worse than one that mentions it.
case "${TERM:-}" in
    ''|dumb|unknown)
        printf '%s: TERM is "%s" — this is a full-screen terminal application\n' \
            "$ARIZA_DISPLAY" "${TERM:-unset}" >&2
        printf '  and may not display correctly. Try TERM=xterm-256color.\n' >&2
        ;;
esac

{% if updates_enabled %}# The update coordinator returns 75 only after its private installer has
# committed a new managed `current` and written an authenticated handoff.
# A natural application exit 75 has no matching record and is returned unchanged.
unset ARIZA_UPDATE_HANDOFF ARIZA_UPDATE_NONCE || :
ARIZA_UPDATE_RECORD=
ARIZA_UPDATE_EXPECTED=

ariza_update_cleanup() {
    [ -z "$ARIZA_UPDATE_RECORD" ] || rm -f -- "$ARIZA_UPDATE_RECORD" 2>/dev/null || :
    [ -z "$ARIZA_UPDATE_EXPECTED" ] || rm -f -- "$ARIZA_UPDATE_EXPECTED" 2>/dev/null || :
}
trap ariza_update_cleanup EXIT

# Only an installer-managed physical version gets a challenge. Portable
# bundles still run the coordinator (which dispatches the app) but never
# create update state beside themselves.
if [ "${ARIZA_UPDATE_RELAUNCHED:-}" != 1 ]; then
    ARIZA_INSTALL_ROOT=$(dirname -- "$(dirname -- "$BUNDLE_ROOT")")
    ARIZA_CURRENT="$ARIZA_INSTALL_ROOT/current"
    ARIZA_CURRENT_ROOT=
    if [ -L "$ARIZA_CURRENT" ]; then
        ARIZA_CURRENT_ROOT=$(ariza_resolve "$ARIZA_CURRENT" 2>/dev/null || :)
    fi
    if [ "$ARIZA_CURRENT_ROOT" = "$BUNDLE_ROOT" ]; then
        ARIZA_NONCE=$(od -An -N32 -tx1 /dev/urandom 2>/dev/null \
            | tr -d ' \n' 2>/dev/null || :)
        case $ARIZA_NONCE in
            *[!0-9a-f]*|'') ARIZA_NONCE= ;;
        esac
        if [ "$(printf '%s' "$ARIZA_NONCE" | wc -c)" -eq 64 ]; then
            ARIZA_STATE="$ARIZA_INSTALL_ROOT/.ariza/update-v1"
            ARIZA_OLD_UMASK=$(umask)
            umask 077
            if [ ! -L "$ARIZA_INSTALL_ROOT/.ariza" ] \
                && [ ! -L "$ARIZA_STATE" ] \
                && mkdir -p -- "$ARIZA_STATE" 2>/dev/null; then
                ARIZA_UPDATE_RECORD="$ARIZA_STATE/handoff.$$.$ARIZA_NONCE"
                ARIZA_UPDATE_EXPECTED="$ARIZA_UPDATE_RECORD.expected"
                if [ ! -e "$ARIZA_UPDATE_RECORD" ] && [ ! -L "$ARIZA_UPDATE_RECORD" ]; then
                    ARIZA_UPDATE_HANDOFF=$ARIZA_UPDATE_RECORD
                    ARIZA_UPDATE_NONCE=$ARIZA_NONCE
                    export ARIZA_UPDATE_HANDOFF ARIZA_UPDATE_NONCE
                fi
            fi
            umask "$ARIZA_OLD_UMASK"
        fi
    fi
fi

set +e
"$ARIZA_RAKU" "$BUNDLE_ROOT/{{ target }}" "$@"
ARIZA_STATUS=$?
set -e

if [ "$ARIZA_STATUS" -eq 75 ] \
    && [ -n "$ARIZA_UPDATE_RECORD" ] \
    && [ -f "$ARIZA_UPDATE_RECORD" ] \
    && [ ! -L "$ARIZA_UPDATE_RECORD" ] \
    && [ "$(wc -c <"$ARIZA_UPDATE_RECORD" 2>/dev/null || printf 99999)" -le 4096 ]; then
    ARIZA_CANDIDATE=$(sed -n '3s/^candidate=//p' "$ARIZA_UPDATE_RECORD")
    case $ARIZA_CANDIDATE in
        *[!0-9.]*|.*|*..*|*.) ARIZA_CANDIDATE= ;;
    esac
    if [ -n "$ARIZA_CANDIDATE" ] \
        && [ "$(printf '%s' "$ARIZA_CANDIDATE" | awk -F. '{ print NF }')" -eq 3 ]; then
        printf 'protocol=1\nnonce=%s\ncandidate=%s\n' \
            "$ARIZA_NONCE" "$ARIZA_CANDIDATE" >"$ARIZA_UPDATE_EXPECTED"
        if cmp -s -- "$ARIZA_UPDATE_EXPECTED" "$ARIZA_UPDATE_RECORD"; then
            ARIZA_CANDIDATE_ROOT="$ARIZA_INSTALL_ROOT/versions/$ARIZA_CANDIDATE"
            ARIZA_CURRENT_ROOT=$(ariza_resolve "$ARIZA_INSTALL_ROOT/current" 2>/dev/null || :)
            ARIZA_NEXT_ROOT=$(ariza_resolve "$ARIZA_CANDIDATE_ROOT" 2>/dev/null || :)
            if [ -n "$ARIZA_NEXT_ROOT" ] \
                && [ "$ARIZA_CURRENT_ROOT" = "$ARIZA_NEXT_ROOT" ] \
                && [ -x "$ARIZA_NEXT_ROOT/bin/{{ app_exec }}" ]; then
                ariza_update_cleanup
                ARIZA_UPDATE_RECORD=
                ARIZA_UPDATE_EXPECTED=
                unset ARIZA_UPDATE_HANDOFF ARIZA_UPDATE_NONCE || :
                ARIZA_UPDATE_RELAUNCHED=1
                export ARIZA_UPDATE_RELAUNCHED
                trap - EXIT
                exec "$ARIZA_NEXT_ROOT/bin/{{ app_exec }}" "$@"
            fi
        fi
    fi
fi

exit "$ARIZA_STATUS"
{% endif %}exec "$ARIZA_RAKU" "$BUNDLE_ROOT/{{ target }}" "$@"