#!/bin/sh # Kiku installer. # # curl -fsSL kiku.sh | sh # # Downloads the latest Kiku.app from files.kiku.sh, checks that it is signed # by its developer, puts it in /Applications (or ~/Applications) and opens it. # Run it again to update. # # Options, as environment variables: # KIKU_VERSION=0.1.0 install that release instead of the latest # KIKU_ZIP=./Kiku.zip install from a local zip (for development) set -eu FILES="https://files.kiku.sh" TEAM_ID="NQCA86NEA3" # the Developer ID that signs every release if [ -t 1 ]; then bold=$(printf '\033[1m'); lavender=$(printf '\033[38;5;141m'); dim=$(printf '\033[2m'); reset=$(printf '\033[0m') else bold=""; lavender=""; dim=""; reset="" fi say() { printf '%s\n' "$*"; } fail() { printf '%s\n' "${bold}kiku:${reset} $*" >&2; exit 1; } [ "$(uname -s)" = "Darwin" ] || fail "Kiku is a macOS app." [ "$(uname -m)" = "arm64" ] || fail "Kiku needs a Mac with Apple silicon (M1 or later)." [ "$(id -u)" != 0 ] || fail "run this as yourself, without sudo: curl -fsSL kiku.sh | sh" os=$(sw_vers -productVersion) major=$(printf '%s' "$os" | cut -d. -f1) minor=$(printf '%s' "$os" | cut -d. -f2) if [ "$major" -lt 14 ] || { [ "$major" -eq 14 ] && [ "${minor:-0}" -lt 4 ]; }; then fail "Kiku needs macOS 14.4 or later (this Mac runs $os)." fi tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT if [ -n "${KIKU_ZIP:-}" ]; then cp "$KIKU_ZIP" "$tmp/Kiku.zip" else if [ -n "${KIKU_VERSION:-}" ]; then version=${KIKU_VERSION#v} else version=$(curl -fsS "$FILES/latest") || fail "could not reach $FILES" fi url="$FILES/v$version/Kiku.zip" say "${lavender}${bold}kiku${reset} ${dim}downloading $url${reset}" curl -fsSL "$url" -o "$tmp/Kiku.zip" || fail "could not download $url" fi ditto -x -k "$tmp/Kiku.zip" "$tmp" || fail "the download is not a valid zip" app="$tmp/Kiku.app" [ -d "$app" ] || fail "the zip holds no Kiku.app" codesign --verify --deep --strict "$app" 2>/dev/null || fail "Kiku.app's signature does not verify, not installing it" team=$(codesign -dv "$app" 2>&1 | sed -n 's/^TeamIdentifier=//p') [ "$team" = "$TEAM_ID" ] || fail "Kiku.app is signed by '$team', not by Kiku's developer ($TEAM_ID), not installing it" if [ -w /Applications ]; then dest=/Applications; else dest="$HOME/Applications"; mkdir -p "$dest"; fi # A running copy quits on its own terms: it finishes writing a call's notes first. if pgrep -xq Kiku; then osascript -e 'tell application id "fm.kiku.mac" to quit' >/dev/null 2>&1 || true i=0 while pgrep -xq Kiku && [ $i -lt 30 ]; do sleep 1; i=$((i + 1)); done pgrep -xq Kiku && fail "Kiku is still busy (writing a call's notes?). Try again in a minute." fi rm -rf "$dest/Kiku.app" ditto "$app" "$dest/Kiku.app" open "$dest/Kiku.app" version=$(defaults read "$dest/Kiku.app/Contents/Info" CFBundleShortVersionString 2>/dev/null || echo "?") say "" say " ${lavender}${bold}KIKU ${version}${reset} is in your menu bar, at $dest/Kiku.app" say " The next time you're on a call, it asks whether to take notes." say "" say " Uninstall: curl -fsSL kiku.sh/uninstall | sh" say ""