#!/bin/sh # Installs Tayang Studio, the latest release, on macOS or Linux: # # curl -fsSL https://studio.tayang.app/install.sh | sh # # On Windows, run install.ps1 in PowerShell instead; from Git Bash this script hands over to it. # # Options (environment variables): # TAYANG_VERSION=0.0.2 install this version instead of the latest # TAYANG_INSTALL_DIR= macOS: where the app goes (default /Applications, else ~/Applications) # TAYANG_LINUX_PACKAGE=deb|rpm|appimage # Linux: the package to use (default: deb with apt, rpm with dnf, # yum or zypper, else the AppImage, which needs no sudo) # TAYANG_NO_LAUNCH=1 don't open Tayang when done # # Installed copies update themselves, so this is only needed once. set -eu REPO="Maru-Yasa/tayang-studio" BASE="${TAYANG_DOWNLOAD_BASE:-https://github.com/$REPO/releases}" # studio.tayang.app serves install.sh and install.ps1 (copies of the ones in this repo). SITE="${TAYANG_SITE:-https://studio.tayang.app}" RAW="${TAYANG_RAW_BASE:-https://raw.githubusercontent.com/$REPO/main}" APP="Tayang Studio" # GitHub swaps the space in "Tayang Studio" for a dot in asset names. ASSET="Tayang.Studio" say() { printf '%s\n' "$*"; } fail() { printf 'Tayang install failed: %s\n' "$*" >&2 exit 1 } has() { command -v "$1" >/dev/null 2>&1; } download() { # url file if has curl; then curl -fL --progress-bar --retry 3 -o "$2" "$1" || fail "couldn't download $1" elif has wget; then wget -q --show-progress -O "$2" "$1" || fail "couldn't download $1" else fail "this needs curl or wget" fi } fetch() { # url -> stdout if has curl; then curl -fsSL --retry 3 "$1" elif has wget; then wget -qO- "$1" else fail "this needs curl or wget" fi } # Root needs no sudo; otherwise sudo, or nothing when there is none. as_root() { if [ "$(id -u)" = 0 ]; then "$@" else sudo "$@" fi } can_root() { [ "$(id -u)" = 0 ] || has sudo; } latest_version() { json=$(fetch "$BASE/latest/download/latest.json") || fail "couldn't find the latest release at $BASE" v=$(printf '%s' "$json" | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1) [ -n "$v" ] || fail "the latest release has no version" printf '%s' "${v#v}" } TMP="" cleanup() { [ -n "$TMP" ] && rm -rf "$TMP"; } trap cleanup EXIT INT TERM install_macos() { case "$(uname -m)" in arm64) arch=aarch64 ;; x86_64) # An Intel shell on an Apple chip (Rosetta) still gets the Apple chip build. if [ "$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)" = 1 ]; then arch=aarch64; else arch=x64; fi ;; *) fail "unsupported Mac processor: $(uname -m)" ;; esac file="${ASSET}_${VERSION}_${arch}.app.tar.gz" say "Downloading $APP $VERSION for macOS ($arch)..." download "$BASE/download/v$VERSION/$file" "$TMP/app.tar.gz" mkdir "$TMP/app" tar -xzf "$TMP/app.tar.gz" -C "$TMP/app" || fail "the download is damaged; try again" [ -d "$TMP/app/$APP.app" ] || fail "the download has no $APP.app" dir="${TAYANG_INSTALL_DIR:-}" if [ -z "$dir" ]; then if [ -w /Applications ]; then dir=/Applications; else dir="$HOME/Applications"; fi fi mkdir -p "$dir" # This copy only: a dev build (Tayang Studio Dev) has the same process name. if pgrep -qf "$dir/$APP.app/Contents/MacOS/" 2>/dev/null; then say "Tayang is open; the new version starts the next time you open it." fi rm -rf "$dir/$APP.app" mv "$TMP/app/$APP.app" "$dir/" || fail "couldn't write to $dir (set TAYANG_INSTALL_DIR to another folder)" # Only browsers mark downloads for Gatekeeper, but clear the mark in case something added it. xattr -dr com.apple.quarantine "$dir/$APP.app" 2>/dev/null || true say "Installed $APP $VERSION in $dir." if [ -z "${TAYANG_NO_LAUNCH:-}" ]; then open "$dir/$APP.app" 2>/dev/null || true; fi } linux_package() { if [ -n "${TAYANG_LINUX_PACKAGE:-}" ]; then case "$TAYANG_LINUX_PACKAGE" in deb | rpm | appimage) printf '%s' "$TAYANG_LINUX_PACKAGE" ;; *) fail "TAYANG_LINUX_PACKAGE must be deb, rpm or appimage" ;; esac return fi if can_root && has apt-get && has dpkg; then printf deb elif can_root && { has dnf || has yum || has zypper; }; then printf rpm else printf appimage fi } install_linux() { # Release file names: .deb says amd64/arm64, .rpm and .AppImage say x86_64/aarch64 (amd64 for x86_64 AppImages). case "$(uname -m)" in x86_64 | amd64) deb_arch=amd64 rpm_arch=x86_64 appimage_arch=amd64 ;; aarch64 | arm64) deb_arch=arm64 rpm_arch=aarch64 appimage_arch=aarch64 ;; *) fail "Tayang for Linux is built for x86_64 and ARM64 (aarch64) only, not $(uname -m)" ;; esac kind=$(linux_package) case "$kind" in deb) file="${ASSET}_${VERSION}_${deb_arch}.deb" say "Downloading $APP $VERSION (.deb)..." download "$BASE/download/v$VERSION/$file" "$TMP/$file" chmod 644 "$TMP/$file" chmod 755 "$TMP" say "Installing (this may ask for your password)..." # apt-get pulls in the WebKit and tray libraries the app needs. as_root env DEBIAN_FRONTEND=noninteractive apt-get install -y "$TMP/$file" || fail "apt-get couldn't install $file" bin=tayang-studio ;; rpm) file="${ASSET}-${VERSION}-1.${rpm_arch}.rpm" say "Downloading $APP $VERSION (.rpm)..." download "$BASE/download/v$VERSION/$file" "$TMP/$file" say "Installing (this may ask for your password)..." if has dnf; then as_root dnf install -y "$TMP/$file" elif has yum; then as_root yum install -y "$TMP/$file" else as_root zypper --non-interactive install --allow-unsigned-rpm "$TMP/$file" fi || fail "couldn't install $file" bin=tayang-studio ;; appimage) file="${ASSET}_${VERSION}_${appimage_arch}.AppImage" say "Downloading $APP $VERSION (AppImage)..." download "$BASE/download/v$VERSION/$file" "$TMP/$file" data="${XDG_DATA_HOME:-$HOME/.local/share}" dir="$data/tayang-studio" mkdir -p "$dir" "$HOME/.local/bin" "$data/applications" mv "$TMP/$file" "$dir/Tayang-Studio.AppImage" chmod 755 "$dir/Tayang-Studio.AppImage" ln -sf "$dir/Tayang-Studio.AppImage" "$HOME/.local/bin/tayang-studio" # The app menu entry; the AppImage updates itself in place, so the path stays the same. cat >"$data/applications/tayang-studio.desktop" <"$TMP/icon.png" 2>/dev/null && mv "$TMP/icon.png" "$data/icons/hicolor/128x128/apps/tayang-studio.png" || true has update-desktop-database && update-desktop-database "$data/applications" >/dev/null 2>&1 || true if ! ldconfig -p 2>/dev/null | grep -q 'libfuse\.so\.2'; then say "Note: AppImages need FUSE 2 (libfuse2 on Ubuntu/Debian, fuse-libs on Fedora) to run." fi case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) say "Add ~/.local/bin to your PATH to start it with 'tayang-studio'." ;; esac bin="$dir/Tayang-Studio.AppImage" ;; esac say "Installed $APP $VERSION. Find it in your app menu, or run: tayang-studio" if [ -z "${TAYANG_NO_LAUNCH:-}" ] && { [ -n "${DISPLAY:-}" ] || [ -n "${WAYLAND_DISPLAY:-}" ]; }; then (nohup "$bin" >/dev/null 2>&1 &) || true fi } install_windows() { has powershell.exe || fail "on Windows, run install.ps1 in PowerShell" say "Handing over to PowerShell..." exec powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "irm '$SITE/install.ps1' | iex" } main() { os=$(uname -s) case "$os" in MINGW* | MSYS* | CYGWIN*) install_windows ;; esac VERSION="${TAYANG_VERSION:-}" VERSION="${VERSION#v}" [ -n "$VERSION" ] || VERSION=$(latest_version) TMP=$(mktemp -d 2>/dev/null || mktemp -d -t tayang) case "$os" in Darwin) install_macos ;; Linux) install_linux ;; *) fail "unsupported system: $os (Tayang runs on macOS, Windows and Linux)" ;; esac } main "$@"