wren-linux / bin / wrenctl
#!/bin/bash
# wrenctl: written by Wren Linux parts/wren-menu.sh
# One command for Wren Linux. Without an option it opens the menu on a sway
# screen and prints the help anywhere else. man wrenctl has the details.
set -u
# Where Wren Linux is: WREN_DIR if you set it; else the root-owned copy the
# Wren Linux package installs, which no program running as you can change; else
# your own copy, ~/wren
if [ -n "${WREN_DIR:-}" ]; then
DIR=$WREN_DIR
elif [ -f "${WREN_SYSTEM_DIR:-/usr/share/wren}/lib.sh" ]; then
DIR=${WREN_SYSTEM_DIR:-/usr/share/wren}
else
DIR=$HOME/wren
fi
usage() {
cat <<'TEXT'
usage: wrenctl [OPTION]
(no option) open the menu: a window in sway, or in this terminal on a
text console or over ssh; this help when there is no terminal
--menu open the menu: browse, chat, files, More apps, Wi-Fi, Privacy, Settings, Help
--update bring Wren Linux up to date, then rerun the scripts whose files
changed (more: --update --since COMMIT, --update --all)
--apps pick optional programs to install (--apps --list lists them)
--install NAME install the extra NAME, or repair or update it (Tab
completes NAME); options after NAME go to its script
--remove NAME remove the extra NAME (your own data stays)
--manual NAME print the steps to set up the extra NAME by hand
--list the extras: what each is, and whether it is installed
--part NAME run one part of the base desktop again (repairs it)
--status show what Wren Linux has set up on this computer
--git [DIR] set git up for you: your name and email, and pushing to GitHub
over ssh with a key of your own, for DIR or the repository you
are in (git-helper.sh)
--test check that the scripts, packages, downloads and keys still work
--wifi [on|off] switch Wi-Fi on or off (no word: say whether it is on)
--bluetooth [on|off] the same for Bluetooth
--mic [on|off] the same for the microphone (muted at every login)
--camera [on|off] the same for the webcam (with the webcam extra; off at every boot)
--welcome show the key cheat sheet again (the first-login screen)
--learn learn bash by reading Wren Linux's scripts, smallest first, checking each off
--version which version of Wren Linux this is
--dir where the Wren Linux folder is
--help this help
Each works without its dashes too: wrenctl update, wrenctl install vim.
More: man wrenctl, and README.md in the Wren Linux folder.
TEXT
}
die() { printf 'wrenctl: %s\n' "$*" >&2; exit 1; }
# need_dir: the commands that run a script of Wren Linux's need its folder
need_dir() {
[ -f "$DIR/lib.sh" ] ||
die "can't find the Wren Linux folder ($DIR). Set WREN_DIR to where it is, or get it again: git clone <address> ~/wren"
}
# script_for extras|parts NAME: the path of that script, only if NAME is
# exactly the name of one (lowercase letters, digits and dashes): nothing
# but a script of Wren Linux's own is ever run
script_for() {
local kind=extra list="wrenctl --list shows the extras."
[ "$1" = extras ] || kind=part list="The parts are in $DIR/parts."
[ -n "${2:-}" ] || die "which $kind? $list"
if [[ ! $2 =~ ^[a-z0-9][a-z0-9-]*$ ]] || [ ! -f "$DIR/$1/$2.sh" ]; then
die "there is no $kind called \"$2\". $list"
fi
echo "$DIR/$1/$2.sh"
}
MENU=$HOME/.local/bin/wren-menu
# in_sway: there is a screen to open a window on
in_sway() { [ -n "${WAYLAND_DISPLAY:-}" ] && [ -x "$MENU" ]; }
# in_terminal: no sway, but a terminal to show the menu in (a text console, ssh)
in_terminal() { [ -t 0 ] && [ -t 1 ] && [ -x "$MENU" ]; }
cmd=${1:-}
[ $# -eq 0 ] || shift
# watched SCRIPT ARGS...: runs a Wren Linux script, then takes the startup files
# its run changed as Wren Linux's (parts/startup-watch.sh), so they don't show
# as "startup changed". A file that had changed before the run is left out:
# that change is for you to look at (wren-watch). Exits with the script's status.
WATCH=${WREN_WATCH_CMD:-/usr/local/bin/wren-watch}
watched() {
local rc=0 before after list pending f
if [ ! -x "$WATCH" ]; then bash "$@"; exit; fi
before=$(mktemp) after=$(mktemp)
"$WATCH" manifest >"$before" 2>/dev/null || true
pending=$(cat "${WREN_WATCH_RUN:-/run/wren-watch}/$(id -un)" 2>/dev/null || true)
bash "$@" || rc=$?
"$WATCH" manifest >"$after" 2>/dev/null || true
list=$({ diff <(sort -k5 "$before") <(sort -k5 "$after") || true; } | sed -n 's/^[<>] [^ ]* [^ ]* [^ ]* [^ ]* //p' | sort -u)
rm -f "$before" "$after"
local take=()
while IFS= read -r f; do
[ -n "$f" ] || continue
if grep -qxF -- "$f" <<<"$pending"; then
printf 'wrenctl: ~/%s had changed before this run: not taken as Wren Linux'"'"'s (look: wren-watch)\n' "$f" >&2
else
take+=("$f")
fi
done <<<"$list"
if [ ${#take[@]} -gt 0 ]; then
printf 'wrenctl: taking what this changed in your startup files as Wren Linux'"'"'s (sudo):\n' >&2
printf ' ~/%s\n' "${take[@]}" >&2
sudo "$WATCH" accept "${take[@]}" >/dev/null || printf 'wrenctl: not taken; they show as "startup changed" until: sudo wren-watch accept\n' >&2
fi
exit "$rc"
}
case $cmd in
"")
if in_sway; then exec "$MENU"; fi
if in_terminal; then exec "$MENU" --tui; fi
usage ;;
--menu | menu)
if in_sway; then exec "$MENU"; fi
in_terminal || die "the menu needs your sway session or a terminal (and the Wren menu installed: bash $DIR/parts/wren-menu.sh)."
exec "$MENU" --tui ;;
--update | update) need_dir; watched "$DIR/update.sh" "$@" ;;
--apps | apps) need_dir; watched "$DIR/install-extras.sh" "$@" ;;
--status | status) need_dir; exec bash "$DIR/status.sh" "$@" ;;
--install | install | --remove | remove | --manual | manual)
# one name; anything after it goes to the script (--account NAME...)
need_dir
script=$(script_for extras "${1:-}") || exit 1
shift
case $cmd in
*install)
! grep -q '^# retired: ' "$script" ||
die "Wren Linux retired $(basename "$script" .sh): $(sed -n 's/^# retired: //p' "$script" | head -1) (wrenctl remove $(basename "$script" .sh) takes it out.)"
watched "$script" "$@" ;;
*manual) exec bash "$script" --manual "$@" ;;
*) watched "$script" "--${cmd#--}" "$@" ;;
esac ;;
--list | list) need_dir; exec bash "$DIR/install-extras.sh" --list ;;
--part | part)
need_dir
[ $# -le 1 ] || die "one name at a time (wrenctl part NAME)."
script=$(script_for parts "${1:-}") || exit 1
watched "$script" ;;
--git | git)
need_dir
[ $# -le 1 ] || die "one folder at a time (wrenctl git [DIR])."
# no folder named: the repository you are in, if you are in one
if [ $# -eq 0 ] && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then set -- "$PWD"; fi
exec bash "$DIR/git-helper.sh" "$@" ;;
--test | test) need_dir; cd "$DIR" && exec bash test.sh "$@" ;;
--wifi | --bluetooth | --mic | --camera)
[ -x "$HOME/.local/bin/radios" ] || die "the radio switch isn't installed (bash $DIR/parts/wren-menu.sh)."
exec "$HOME/.local/bin/radios" "${cmd#--}" "$@" ;;
--learn | learn)
[ -x "$HOME/.local/bin/wren-learn" ] || die "wren-learn isn't installed (bash $DIR/parts/wren-menu.sh)."
exec "$HOME/.local/bin/wren-learn" "$@" ;;
--welcome | welcome)
# the same cheat sheet the first login opens, in this terminal
if [ -x "$HOME/.local/bin/keys" ]; then exec "$HOME/.local/bin/keys"; fi
die "the key cheat sheet isn't installed (bash $DIR/install-base.sh)." ;;
--version | version)
need_dir
git -C "$DIR" describe --always --dirty 2>/dev/null || echo unknown ;;
--dir) echo "$DIR" ;;
--help | -h | help) usage ;;
*)
printf 'wrenctl: unknown option "%s"\n\n' "$cmd" >&2
usage >&2
exit 2 ;;
esac