2026-10-02 09:53:35 +01:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# install.sh — download and install the latest YOUreka Fingerprint Agent
|
|
|
|
|
# (fpagent) release on this machine.
|
|
|
|
|
#
|
|
|
|
|
# Hosted at https://tools.yrka.app/fpagent/install.sh and meant to be piped
|
|
|
|
|
# straight into bash on a field laptop:
|
|
|
|
|
#
|
|
|
|
|
# curl -fsSL https://tools.yrka.app/fpagent/install.sh | bash
|
|
|
|
|
#
|
|
|
|
|
# It downloads the bundle, verifies its sha256, then hands off to the bundle's
|
|
|
|
|
# own installer, which handles libfprint, the udev rule, plugdev and the
|
|
|
|
|
# systemd service. Every flag is forwarded to that installer, e.g.:
|
|
|
|
|
#
|
|
|
|
|
# curl -fsSL https://tools.yrka.app/fpagent/install.sh | bash -s -- --enable-service
|
|
|
|
|
# curl -fsSL https://tools.yrka.app/fpagent/install.sh | bash -s -- --check
|
|
|
|
|
#
|
|
|
|
|
# The private release is reached through the tools app, so no credentials are
|
|
|
|
|
# needed here.
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
BASE="${FPAGENT_BASE_URL:-https://tools.yrka.app/fpagent}"
|
|
|
|
|
|
|
|
|
|
say() { printf '%s\n' "$*"; }
|
|
|
|
|
die() { printf '[FAIL] %s\n' "$*" >&2; exit 1; }
|
|
|
|
|
|
|
|
|
|
usage() {
|
|
|
|
|
cat <<EOF
|
|
|
|
|
Usage: curl -fsSL ${BASE}/install.sh | bash -s -- [installer options]
|
|
|
|
|
|
|
|
|
|
Downloads the latest fpagent release, verifies it, and runs its installer.
|
|
|
|
|
Options are forwarded to that installer:
|
|
|
|
|
--check preflight and report only; change nothing
|
|
|
|
|
--offline never touch the network; fail if dependencies are missing
|
|
|
|
|
--enable-service install and enable a systemd service (starts at boot)
|
|
|
|
|
--service-user NAME user for the service (default: the invoking user)
|
|
|
|
|
--install-path PATH binary destination (default: /usr/local/bin/fpagent)
|
|
|
|
|
--uninstall remove the binary, the udev rule and the service
|
|
|
|
|
-h, --help the installer's help
|
|
|
|
|
|
|
|
|
|
Environment:
|
|
|
|
|
FPAGENT_BASE_URL override the download base (default: ${BASE})
|
|
|
|
|
EOF
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for arg in "$@"; do
|
|
|
|
|
case "$arg" in
|
|
|
|
|
-h | --help)
|
|
|
|
|
usage
|
|
|
|
|
exit 0
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|
2026-10-02 10:49:07 +01:00
|
|
|
for cmd in curl tar sha256sum python3 find; do
|
2026-10-02 09:53:35 +01:00
|
|
|
command -v "$cmd" >/dev/null 2>&1 || die "$cmd is required but not installed"
|
|
|
|
|
done
|
|
|
|
|
|
2026-10-02 10:49:07 +01:00
|
|
|
tmpbase="${TMPDIR:-/tmp}"
|
|
|
|
|
# Sweep scratch left by an earlier run that was SIGKILLed: the EXIT trap cannot
|
|
|
|
|
# run then (that is the one signal it cannot catch). Scoped to our own prefix,
|
|
|
|
|
# this user, and dirs older than a day, so a concurrent install is never touched.
|
|
|
|
|
# Runs before any network work so cleanup is not skipped when the fetch fails.
|
|
|
|
|
find "$tmpbase" -maxdepth 1 -mindepth 1 -type d -uid "$(id -u)" \
|
|
|
|
|
-name 'fpagent-install.*' -mtime +0 -exec rm -rf {} + 2>/dev/null || true
|
|
|
|
|
|
2026-10-02 09:53:35 +01:00
|
|
|
# Offline bundles ship the libfprint .deb closure for a specific Ubuntu
|
|
|
|
|
# release; prefer the one matching this machine, else fall back to the generic
|
|
|
|
|
# online bundle (whose installer apt-get's the dependency).
|
|
|
|
|
codename="$(sed -n 's/^VERSION_CODENAME=//p' /etc/os-release 2>/dev/null | tr -d '"')"
|
|
|
|
|
|
|
|
|
|
say "Fetching the latest Fingerprint Agent release..."
|
|
|
|
|
meta="$(curl -fsSL "${BASE}/releases/latest")" || die "could not fetch the release list from ${BASE}"
|
|
|
|
|
|
|
|
|
|
readout="$(python3 -c '
|
|
|
|
|
import json, sys
|
|
|
|
|
codename = sys.argv[1]
|
|
|
|
|
release = json.loads(sys.argv[2])
|
|
|
|
|
assets = [a["name"] for a in release.get("assets", [])]
|
|
|
|
|
|
|
|
|
|
def pick(suffix):
|
|
|
|
|
return next((n for n in assets if n.endswith(suffix) and not n.endswith(".sha256")), "")
|
|
|
|
|
|
|
|
|
|
name = pick("-ubuntu-" + codename + "-x86_64.tar.gz") if codename else ""
|
|
|
|
|
if not name:
|
|
|
|
|
name = pick("-linux-x86_64.tar.gz")
|
|
|
|
|
print(release.get("tag", "") + "\t" + name)
|
|
|
|
|
' "$codename" "$meta")" || die "could not parse the release metadata"
|
|
|
|
|
|
|
|
|
|
tag="${readout%%$'\t'*}"
|
|
|
|
|
asset="${readout#*$'\t'}"
|
|
|
|
|
[ -n "$tag" ] && [ -n "$asset" ] || die "no compatible bundle in the latest release (x86_64 required)"
|
|
|
|
|
|
|
|
|
|
say " release ${tag}"
|
|
|
|
|
say " bundle ${asset}"
|
|
|
|
|
|
2026-10-02 10:49:07 +01:00
|
|
|
tmp="$(mktemp -d "$tmpbase/fpagent-install.XXXXXXXX")"
|
2026-10-02 09:53:35 +01:00
|
|
|
trap 'rm -rf "$tmp"' EXIT
|
|
|
|
|
|
|
|
|
|
get() { curl -fsSL "$1" -o "$2" || die "download failed: $1"; }
|
|
|
|
|
|
|
|
|
|
say "Downloading and verifying..."
|
|
|
|
|
get "${BASE}/releases/download/${tag}/${asset}" "${tmp}/${asset}"
|
|
|
|
|
get "${BASE}/releases/download/${tag}/${asset}.sha256" "${tmp}/${asset}.sha256"
|
|
|
|
|
(cd "$tmp" && sha256sum -c "${asset}.sha256") || die "checksum verification failed for ${asset}"
|
|
|
|
|
|
|
|
|
|
tar xzf "${tmp}/${asset}" -C "$tmp" || die "could not extract ${asset}"
|
|
|
|
|
[ -x "${tmp}/install.sh" ] || die "the bundle does not contain install.sh"
|
|
|
|
|
|
|
|
|
|
say "Running the installer (pass --enable-service to start at boot)..."
|
|
|
|
|
"${tmp}/install.sh" "$@"
|