Krolyxon
Commit 3dfc07c

Branches

Refs →

budctlinstall.sh

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