Krolyxon
Commit e479dcc

Branches

Refs →

budctlinstall.sh

199 lines · 6.6 KB · Bourne Again Shell (bash)

1#!/bin/sh
2# Install budctl and budctl-gui on Linux, or remove them.
3#
4# curl -fsSL https://github.com/krolyxon/budctl/releases/latest/download/install.sh | sh
5# ./install.sh in an unpacked release: installs the binaries next to it
6# ./install.sh --source in a checkout: builds with cargo, then installs
7# ./install.sh --uninstall
8#
9# Installs into ~/.local by default (bin/, and the launcher entry and its icon in share/), so no
10# root is needed.
11
12set -eu
13
14REPO=krolyxon/budctl
15PREFIX=$HOME/.local
16VERSION=latest
17ACTION=install
18FROM=auto
19
20usage() {
21 cat <<'EOF'
22Install budctl (command line) and budctl-gui (desktop app), or remove them.
23
24Usage:
25 install.sh [--prefix DIR] [--version X.Y.Z] [--source]
26 install.sh --uninstall [--prefix DIR]
27
28Options:
29 --prefix DIR Install into DIR/bin and DIR/share (default: ~/.local)
30 --version X.Y.Z Download this release instead of the latest
31 --source Build from this checkout with cargo instead of downloading
32 --uninstall Remove what install.sh installed. Your config (~/.config/budctl)
33 and log (~/.local/state/budctl) are left alone.
34 -h, --help Show this help
35EOF
36}
37
38fail() {
39 printf 'install.sh: %s\n' "$1" >&2
40 exit 1
41}
42
43say() {
44 printf '%s\n' "$1"
45}
46
47while [ "$#" -gt 0 ]; do
48 case "$1" in
49 --prefix)
50 [ "$#" -ge 2 ] && [ -n "$2" ] || fail "--prefix needs a directory"
51 PREFIX=$2
52 shift 2
53 ;;
54 --version)
55 [ "$#" -ge 2 ] && [ -n "$2" ] || fail "--version needs a version, like 0.1.1"
56 VERSION=${2#v}
57 shift 2
58 ;;
59 --source)
60 FROM=source
61 shift
62 ;;
63 --uninstall)
64 ACTION=uninstall
65 shift
66 ;;
67 -h | --help)
68 usage
69 exit 0
70 ;;
71 *)
72 fail "unknown option: $1 (see --help)"
73 ;;
74 esac
75done
76
77[ "$(uname -s)" = Linux ] || fail "budctl runs on Linux only"
78
79BIN_DIR=$PREFIX/bin
80APP_DIR=$PREFIX/share/applications
81ICON_THEME=$PREFIX/share/icons/hicolor
82ICON_DIR=$ICON_THEME/scalable/apps
83
84# Use sudo only when the prefix cannot be written as this user (such as /usr/local).
85SUDO=
86writable_dir=$PREFIX
87while [ ! -d "$writable_dir" ]; do
88 writable_dir=$(dirname "$writable_dir")
89done
90if [ ! -w "$writable_dir" ] && [ "$(id -u)" -ne 0 ]; then
91 command -v sudo >/dev/null 2>&1 || fail "$PREFIX is not writable and sudo was not found"
92 SUDO=sudo
93fi
94
95if [ "$ACTION" = uninstall ]; then
96 for file in "$BIN_DIR/budctl" "$BIN_DIR/budctl-gui" "$APP_DIR/budctl.desktop" "$ICON_DIR/budctl.svg"; do
97 if [ -e "$file" ]; then
98 $SUDO rm -f "$file"
99 say "Removed $file"
100 fi
101 done
102 say "Your config (~/.config/budctl) and log (~/.local/state/budctl) were left alone."
103 exit 0
104fi
105
106WORK=$(mktemp -d)
107trap 'rm -rf "$WORK"' EXIT INT TERM
108
109# Where the files to install come from: a release unpacked next to this script, this checkout
110# built with cargo, or a release downloaded from GitHub.
111SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)
112if [ "$FROM" = auto ]; then
113 if [ -f "$SCRIPT_DIR/install.sh" ] && [ -x "$SCRIPT_DIR/budctl" ] && [ -x "$SCRIPT_DIR/budctl-gui" ]; then
114 FROM=bundle
115 else
116 FROM=download
117 fi
118fi
119
120case "$FROM" in
121 bundle)
122 FILES=$SCRIPT_DIR
123 DESKTOP=$SCRIPT_DIR/budctl.desktop
124 ICON=$SCRIPT_DIR/budctl.svg
125 ;;
126 source)
127 [ -f "$SCRIPT_DIR/Cargo.toml" ] || fail "--source needs a budctl checkout next to install.sh"
128 command -v cargo >/dev/null 2>&1 || fail "cargo was not found; install Rust from https://rustup.rs/"
129 say "Building budctl in $SCRIPT_DIR"
130 cargo build --release --locked --manifest-path "$SCRIPT_DIR/Cargo.toml" -p budctl -p budctl-gui
131 FILES=$SCRIPT_DIR/target/release
132 DESKTOP=$SCRIPT_DIR/contrib/budctl.desktop
133 ICON=$SCRIPT_DIR/docs/icon.svg
134 ;;
135 download)
136 case "$(uname -m)" in
137 x86_64 | amd64) TARGET=x86_64-unknown-linux-gnu ;;
138 aarch64 | arm64) TARGET=aarch64-unknown-linux-gnu ;;
139 *) fail "no release build for $(uname -m); build from a checkout with ./install.sh --source" ;;
140 esac
141 ARCHIVE=budctl-$TARGET.tar.gz
142 if [ "$VERSION" = latest ]; then
143 URL=https://github.com/$REPO/releases/latest/download
144 else
145 URL=https://github.com/$REPO/releases/download/v$VERSION
146 fi
147
148 if command -v curl >/dev/null 2>&1; then
149 fetch() { curl -fsSL --proto '=https' --tlsv1.2 -o "$2" "$1"; }
150 elif command -v wget >/dev/null 2>&1; then
151 fetch() { wget -q -O "$2" "$1"; }
152 else
153 fail "curl or wget is needed to download budctl"
154 fi
155
156 say "Downloading $URL/$ARCHIVE"
157 fetch "$URL/$ARCHIVE" "$WORK/$ARCHIVE" || fail "download failed: $URL/$ARCHIVE"
158 fetch "$URL/$ARCHIVE.sha256" "$WORK/$ARCHIVE.sha256" || fail "download failed: $URL/$ARCHIVE.sha256"
159 (cd "$WORK" && sha256sum -c --quiet "$ARCHIVE.sha256") || fail "checksum mismatch for $ARCHIVE"
160 tar -xzf "$WORK/$ARCHIVE" -C "$WORK"
161 FILES=$WORK/budctl-$TARGET
162 DESKTOP=$FILES/budctl.desktop
163 ICON=$FILES/budctl.svg
164 ;;
165esac
166
167for name in budctl budctl-gui; do
168 [ -x "$FILES/$name" ] || fail "$name was not found in $FILES"
169done
170[ -f "$DESKTOP" ] || fail "budctl.desktop was not found at $DESKTOP"
171
172# The launcher entry runs the binary by its full path, so it works even when the launcher's PATH
173# lacks the install directory.
174sed "s|^Exec=budctl-gui|Exec=$BIN_DIR/budctl-gui|" "$DESKTOP" >"$WORK/budctl.desktop"
175
176$SUDO install -d "$BIN_DIR" "$APP_DIR"
177$SUDO install -m 0755 "$FILES/budctl" "$FILES/budctl-gui" "$BIN_DIR/"
178$SUDO install -m 0644 "$WORK/budctl.desktop" "$APP_DIR/budctl.desktop"
179# Releases from before the icon carry none; their launcher entry names a stock one instead.
180ICON_INSTALLED=
181if [ -f "$ICON" ]; then
182 $SUDO install -d "$ICON_DIR"
183 $SUDO install -m 0644 "$ICON" "$ICON_DIR/budctl.svg"
184 # A theme with an icon cache only sees a new icon once the cache is rebuilt.
185 if [ -f "$ICON_THEME/icon-theme.cache" ] && command -v gtk-update-icon-cache >/dev/null 2>&1; then
186 $SUDO gtk-update-icon-cache -q -t "$ICON_THEME" || true
187 fi
188 ICON_INSTALLED=yes
189fi
190
191say "Installed $("$BIN_DIR/budctl" --version) to $BIN_DIR (budctl, budctl-gui)"
192say "Installed the launcher entry to $APP_DIR/budctl.desktop"
193if [ -n "$ICON_INSTALLED" ]; then
194 say "Installed the icon to $ICON_DIR/budctl.svg"
195fi
196case ":$PATH:" in
197 *":$BIN_DIR:"*) ;;
198 *) say "Note: $BIN_DIR is not on your PATH. Add it to run budctl from a terminal." ;;
199esac