adding mac support mainly
This commit is contained in:
162
install.sh
162
install.sh
@@ -1,15 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
# streamdeck-go installer
|
||||
# streamdeck-go installer — Linux and macOS
|
||||
# Handles dotfiles-aware installation with interactive prompts.
|
||||
set -euo pipefail
|
||||
|
||||
BINARY="streamdeck-go"
|
||||
BIN_DIR="${HOME}/.local/bin"
|
||||
SYSTEMD_USER="${HOME}/.config/systemd/user"
|
||||
UDEV_RULE="/etc/udev/rules.d/99-streamdeck.rules"
|
||||
XDG_CONFIG="${HOME}/.config"
|
||||
DEFAULT_DOTFILES="${HOME}/dotfiles"
|
||||
|
||||
# ── OS detection ───────────────────────────────────────────────────────────────
|
||||
OS="$(uname -s)"
|
||||
IS_MAC=false
|
||||
[[ "$OS" == "Darwin" ]] && IS_MAC=true
|
||||
|
||||
if $IS_MAC; then
|
||||
BIN_DIR="${HOME}/go/bin"
|
||||
LAUNCHAGENTS_DIR="${HOME}/Library/LaunchAgents"
|
||||
PLIST_LABEL="com.woodarddigital.streamdeck-go"
|
||||
PLIST_DST="${LAUNCHAGENTS_DIR}/${PLIST_LABEL}.plist"
|
||||
else
|
||||
BIN_DIR="${HOME}/.local/bin"
|
||||
SYSTEMD_USER="${HOME}/.config/systemd/user"
|
||||
UDEV_RULE="/etc/udev/rules.d/99-streamdeck.rules"
|
||||
fi
|
||||
|
||||
# ── Colours ────────────────────────────────────────────────────────────────────
|
||||
if [[ -t 1 ]]; then
|
||||
BOLD='\033[1m'; DIM='\033[2m'
|
||||
@@ -46,7 +59,6 @@ prompt_yn() {
|
||||
}
|
||||
|
||||
prompt_input() {
|
||||
# echo-ne goes to /dev/tty so it isn't captured when called inside $()
|
||||
local msg="$1" default="$2"
|
||||
echo -ne " ${ARROW} ${msg} ${DIM}[${default}]${NC}: " >/dev/tty
|
||||
read -r _input </dev/tty
|
||||
@@ -73,7 +85,6 @@ pick_directory() {
|
||||
) || result=""
|
||||
fi
|
||||
|
||||
# fzf not available or ctrl-c pressed — fall back to plain text input
|
||||
if [[ -z "$result" ]]; then
|
||||
echo -ne " ${ARROW} Path to dotfiles repo ${DIM}[${default}]${NC}: " >/dev/tty
|
||||
read -r result </dev/tty
|
||||
@@ -84,33 +95,59 @@ pick_directory() {
|
||||
}
|
||||
|
||||
abspath() {
|
||||
# Expand ~, resolve to absolute path without requiring it to exist yet.
|
||||
local p="${1/#\~/$HOME}"
|
||||
echo "$p"
|
||||
echo "${1/#\~/$HOME}"
|
||||
}
|
||||
|
||||
# Portable realpath: macOS ships without readlink -f unless coreutils is installed.
|
||||
realpath_portable() {
|
||||
local path="$1"
|
||||
if command -v realpath &>/dev/null; then
|
||||
realpath "$path"
|
||||
elif command -v python3 &>/dev/null; then
|
||||
python3 -c "import os,sys; print(os.path.realpath(sys.argv[1]))" "$path"
|
||||
else
|
||||
echo "${path/#\~/$HOME}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Portable install with parent dir creation.
|
||||
# macOS install(1) lacks -D; use explicit mkdir -p instead.
|
||||
install_file() {
|
||||
local mode="$1" src="$2" dst="$3"
|
||||
mkdir -p "$(dirname "$dst")"
|
||||
install -m "$mode" "$src" "$dst"
|
||||
}
|
||||
|
||||
# ── Dependency detection ───────────────────────────────────────────────────────
|
||||
|
||||
detect_pkg_manager() {
|
||||
if command -v pacman &>/dev/null; then echo "pacman"
|
||||
elif command -v apt &>/dev/null; then echo "apt"
|
||||
elif command -v dnf &>/dev/null; then echo "dnf"
|
||||
elif command -v zypper &>/dev/null; then echo "zypper"
|
||||
if $IS_MAC; then echo "brew"
|
||||
elif command -v pacman &>/dev/null; then echo "pacman"
|
||||
elif command -v apt &>/dev/null; then echo "apt"
|
||||
elif command -v dnf &>/dev/null; then echo "dnf"
|
||||
elif command -v zypper &>/dev/null; then echo "zypper"
|
||||
else echo "unknown"
|
||||
fi
|
||||
}
|
||||
|
||||
# Returns 0 if hidapi dev headers are present (needed for cgo build).
|
||||
hidapi_present() {
|
||||
pkg-config --exists hidapi-hidraw 2>/dev/null ||
|
||||
pkg-config --exists hidapi 2>/dev/null ||
|
||||
ldconfig -p 2>/dev/null | grep -q libhidapi
|
||||
if $IS_MAC; then
|
||||
# On macOS hidapi is a formula; check pkg-config or the dylib directly.
|
||||
pkg-config --exists hidapi 2>/dev/null ||
|
||||
[ -f /usr/local/lib/libhidapi.dylib ] ||
|
||||
[ -f /opt/homebrew/lib/libhidapi.dylib ]
|
||||
else
|
||||
pkg-config --exists hidapi-hidraw 2>/dev/null ||
|
||||
pkg-config --exists hidapi 2>/dev/null ||
|
||||
ldconfig -p 2>/dev/null | grep -q libhidapi
|
||||
fi
|
||||
}
|
||||
|
||||
install_pkg() {
|
||||
local pm="$1"; shift
|
||||
local pkgs=("$@")
|
||||
case "$pm" in
|
||||
brew) brew install "${pkgs[@]}" ;;
|
||||
pacman) sudo pacman -S --needed --noconfirm "${pkgs[@]}" ;;
|
||||
apt) sudo apt-get install -y "${pkgs[@]}" ;;
|
||||
dnf) sudo dnf install -y "${pkgs[@]}" ;;
|
||||
@@ -137,6 +174,7 @@ if ! command -v go &>/dev/null; then
|
||||
fi
|
||||
if prompt_yn "Install Go now?" "y"; then
|
||||
case "$PM" in
|
||||
brew) install_pkg brew go ;;
|
||||
pacman) install_pkg pacman go ;;
|
||||
apt) install_pkg apt golang-go ;;
|
||||
dnf) install_pkg dnf golang ;;
|
||||
@@ -151,20 +189,25 @@ else
|
||||
ok "Go found: $(go version | awk '{print $3}')"
|
||||
fi
|
||||
|
||||
# hidapi (C library + dev headers required for cgo)
|
||||
# hidapi
|
||||
if hidapi_present; then
|
||||
ok "hidapi found"
|
||||
else
|
||||
warn "hidapi not found (required for USB HID communication)."
|
||||
if [[ "$PM" == "unknown" ]]; then
|
||||
warn "Could not detect a package manager — install hidapi manually, then re-run."
|
||||
warn " Arch: sudo pacman -S hidapi"
|
||||
warn " Debian: sudo apt install libhidapi-dev libhidapi-hidraw0"
|
||||
warn " Fedora: sudo dnf install hidapi-devel"
|
||||
if $IS_MAC; then
|
||||
warn " macOS: brew install hidapi"
|
||||
else
|
||||
warn " Arch: sudo pacman -S hidapi"
|
||||
warn " Debian: sudo apt install libhidapi-dev libhidapi-hidraw0"
|
||||
warn " Fedora: sudo dnf install hidapi hidapi-devel"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
if prompt_yn "Install hidapi now?" "y"; then
|
||||
case "$PM" in
|
||||
brew) install_pkg brew hidapi ;;
|
||||
pacman) install_pkg pacman hidapi ;;
|
||||
apt) install_pkg apt libhidapi-dev libhidapi-hidraw0 ;;
|
||||
dnf) install_pkg dnf hidapi hidapi-devel ;;
|
||||
@@ -188,26 +231,37 @@ else
|
||||
fi
|
||||
nl
|
||||
|
||||
# ── 3. udev rule ───────────────────────────────────────────────────────────────
|
||||
step "Checking udev rule..."
|
||||
if [[ -f "${UDEV_RULE}" ]]; then
|
||||
ok "udev rule already installed — skipping"
|
||||
# ── 3. Device permissions ──────────────────────────────────────────────────────
|
||||
if $IS_MAC; then
|
||||
step "Device permissions (macOS)..."
|
||||
info "macOS grants HID access via IOKit — no udev rule needed."
|
||||
info "On first run the OS may show an Input Monitoring prompt; grant access if asked."
|
||||
ok "No action required"
|
||||
else
|
||||
info "Installing udev rule (requires sudo):"
|
||||
dim " ${UDEV_RULE}"
|
||||
echo 'KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", MODE="0666"' \
|
||||
| sudo tee "${UDEV_RULE}" >/dev/null
|
||||
sudo udevadm control --reload
|
||||
sudo udevadm trigger
|
||||
ok "udev rule installed — device accessible without root"
|
||||
step "Checking udev rule..."
|
||||
if [[ -f "${UDEV_RULE}" ]]; then
|
||||
ok "udev rule already installed — skipping"
|
||||
else
|
||||
info "Installing udev rule (requires sudo):"
|
||||
dim " ${UDEV_RULE}"
|
||||
echo 'KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", MODE="0666"' \
|
||||
| sudo tee "${UDEV_RULE}" >/dev/null
|
||||
sudo udevadm control --reload
|
||||
sudo udevadm trigger
|
||||
ok "udev rule installed — device accessible without root"
|
||||
fi
|
||||
fi
|
||||
nl
|
||||
|
||||
# ── 4. Binary ──────────────────────────────────────────────────────────────────
|
||||
step "Installing binary to ${BOLD}${BIN_DIR}/${BINARY}${NC}..."
|
||||
mkdir -p "${BIN_DIR}"
|
||||
install -Dm755 "${BINARY}" "${BIN_DIR}/${BINARY}"
|
||||
install -m 755 "${BINARY}" "${BIN_DIR}/${BINARY}"
|
||||
ok "Binary installed"
|
||||
if $IS_MAC && [[ ":$PATH:" != *":${BIN_DIR}:"* ]]; then
|
||||
warn "${BIN_DIR} is not in your PATH — add it to your shell profile:"
|
||||
dim " export PATH=\"\$HOME/go/bin:\$PATH\""
|
||||
fi
|
||||
nl
|
||||
|
||||
# ── 5. Dotfiles ────────────────────────────────────────────────────────────────
|
||||
@@ -231,7 +285,7 @@ if prompt_yn "Use a dotfiles directory?" "y"; then
|
||||
warn "Directory ${DOTFILES} does not exist — it will be created."
|
||||
fi
|
||||
|
||||
CONFIG_DIR="${DOTFILES}/streamdeck-go/.config/streamdeck-go"
|
||||
CONFIG_DIR="${DOTFILES}/.config/streamdeck-go"
|
||||
SYMLINK_TARGET="${XDG_CONFIG}/streamdeck-go"
|
||||
|
||||
nl
|
||||
@@ -268,7 +322,7 @@ mkdir -p "${CONFIG_DIR}/icons"
|
||||
ok "Created ${CONFIG_DIR}/icons/"
|
||||
|
||||
if [[ ! -f "${CONFIG_DIR}/config.yaml" ]]; then
|
||||
install -Dm644 config.example.yaml "${CONFIG_DIR}/config.yaml"
|
||||
install -m 644 config.example.yaml "${CONFIG_DIR}/config.yaml"
|
||||
ok "Default config written to config.yaml"
|
||||
else
|
||||
ok "config.yaml already exists — not overwritten"
|
||||
@@ -299,7 +353,7 @@ if [[ "${USE_DOTFILES}" == "true" ]]; then
|
||||
SYMLINK_TARGET="${XDG_CONFIG}/streamdeck-go"
|
||||
|
||||
if [[ -L "${SYMLINK_TARGET}" ]]; then
|
||||
existing="$(readlink -f "${SYMLINK_TARGET}")"
|
||||
existing="$(realpath_portable "${SYMLINK_TARGET}")"
|
||||
if [[ "${existing}" == "${CONFIG_DIR}" ]]; then
|
||||
ok "Symlink already correct — skipping"
|
||||
else
|
||||
@@ -323,14 +377,30 @@ if [[ "${USE_DOTFILES}" == "true" ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── 8. Systemd user service ────────────────────────────────────────────────────
|
||||
# ── 8. Service ─────────────────────────────────────────────────────────────────
|
||||
nl
|
||||
step "Installing systemd user service..."
|
||||
mkdir -p "${SYSTEMD_USER}"
|
||||
install -Dm644 systemd/streamdeck-go.service "${SYSTEMD_USER}/streamdeck-go.service"
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable --now streamdeck-go.service
|
||||
ok "Service enabled and started"
|
||||
if $IS_MAC; then
|
||||
step "Installing launchd agent..."
|
||||
mkdir -p "${LAUNCHAGENTS_DIR}"
|
||||
LOG_PATH="${HOME}/Library/Logs/streamdeck-go.log"
|
||||
# Substitute binary path and log path into the plist template.
|
||||
sed \
|
||||
-e "s|STREAMDECK_BINARY_PATH|${BIN_DIR}/${BINARY}|g" \
|
||||
-e "s|STREAMDECK_LOG_PATH|${LOG_PATH}|g" \
|
||||
launchd/com.woodarddigital.streamdeck-go.plist \
|
||||
> "${PLIST_DST}"
|
||||
# Unload first in case an old version is already loaded.
|
||||
launchctl unload "${PLIST_DST}" 2>/dev/null || true
|
||||
launchctl load "${PLIST_DST}"
|
||||
ok "launchd agent loaded — will start at login"
|
||||
else
|
||||
step "Installing systemd user service..."
|
||||
mkdir -p "${SYSTEMD_USER}"
|
||||
install_file 644 systemd/streamdeck-go.service "${SYSTEMD_USER}/streamdeck-go.service"
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable --now streamdeck-go.service
|
||||
ok "Service enabled and started"
|
||||
fi
|
||||
|
||||
# ── Done ───────────────────────────────────────────────────────────────────────
|
||||
nl
|
||||
@@ -352,5 +422,9 @@ nl
|
||||
info "Edit and save config.yaml — the deck reloads automatically."
|
||||
info "For privileged commands (suspend, reboot, etc): ${BOLD}make install-helper${NC}"
|
||||
nl
|
||||
info "Logs: ${DIM}journalctl --user -u streamdeck-go -f${NC}"
|
||||
if $IS_MAC; then
|
||||
info "Logs: ${DIM}tail -f ~/Library/Logs/streamdeck-go.log${NC}"
|
||||
else
|
||||
info "Logs: ${DIM}journalctl --user -u streamdeck-go -f${NC}"
|
||||
fi
|
||||
nl
|
||||
|
||||
Reference in New Issue
Block a user