provisioning tool for building opinionated architecture
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

712 lines
22 KiB

9 months ago
9 months ago
7 months ago
9 months ago
7 months ago
9 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
6 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
  1. #!/bin/bash
  2. RED='\e[0;41m\e[1;37m'
  3. GREEN='\033[0;32m'
  4. YELLOW='\033[0;33m'
  5. PURPLE='\033[0;35m'
  6. DARK='\e[100m'
  7. NC='\033[0m' # No Color
  8. TO_BE_DEFINED="TO BE DEFINED"
  9. # BOLD='\033[1m'
  10. # DIM='\e[2m\e[0;90m'
  11. function echo() {
  12. [[ -n ${PREFIX:-} ]] && printf "${DARK}%25.25s${NC} " "${PREFIX}"
  13. builtin echo "$@"
  14. }
  15. function check_normal_user() {
  16. [[ $(id -u) -lt 1000 ]] && echoerr "normal user (>1000) expected, please connect as a normal user then call again!" && exit 100
  17. return 0
  18. }
  19. function sudo_required() {
  20. check_normal_user
  21. command -v sudo &>/dev/null &&
  22. id -G | grep -q sudo && echoerr "command <sudo> not found, please install as so: \`apt install -y sudo\`" && exit 1
  23. if ! sudo -n true &>/dev/null; then
  24. if [[ -n "${1:-}" ]]; then
  25. echowarnn "[sudo] requiring authorized access for: [ $1 ]"
  26. else
  27. echowarnn "[sudo] requiring authorized access for further processing"
  28. fi
  29. fi
  30. sudo -vp ' : '
  31. }
  32. # idempotent cargo install <package1 package2 ...>
  33. function idem_cargo_install() {
  34. for i in "$@"; do
  35. if [ ! -f ~/.cargo/bin/"$i" ]; then
  36. cargo install "$i"
  37. fi
  38. done
  39. }
  40. # display error in red
  41. function echoerr() {
  42. echo -e "${RED}$*${NC}" >&2
  43. }
  44. function echoerrn() {
  45. echo -en "${RED}$*${NC}" >&2
  46. }
  47. # display warn in yellow
  48. function echowarn() {
  49. echo -e "${YELLOW}$*${NC}" >&2
  50. }
  51. function echowarnn() {
  52. echo -en "${YELLOW}$*${NC}" >&2
  53. }
  54. # display error in green
  55. function echoinfo() {
  56. echo -e "${GREEN}$*${NC}" >&2
  57. }
  58. function echoinfon() {
  59. echo -en "${GREEN}$*${NC}" >&2
  60. }
  61. # test whether <ip> is a valid ipv4 address?
  62. function valid_ipv4() {
  63. local ip="$1"
  64. if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
  65. IFS='.' read -ra ADDR <<<"$ip"
  66. [[ ${ADDR[0]} -le 255 && ${ADDR[1]} -le 255 && ${ADDR[2]} -le 255 && ${ADDR[3]} -le 255 ]]
  67. return $?
  68. fi
  69. return 1
  70. }
  71. function enable_trace() {
  72. trap 'trap_error $? ${LINENO:-0} ${BASH_LINENO:-0} ${BASH_COMMAND:-empty} $(printf "::%s" ${FUNCNAME[@]})' ERR
  73. }
  74. function disable_trace() {
  75. trap - ERR
  76. }
  77. function disable_all_signals {
  78. trap - ERR
  79. trap - HUP
  80. trap - INT
  81. trap - TERM
  82. }
  83. function prepare_nftables() {
  84. local PREFIX="miaou:nftables"
  85. if ! [[ -f /etc/nftables.rules.d/firewall.table ]]; then
  86. echo "installing nftables ..."
  87. sudo apt install -y nftables
  88. sudo cp -f "$MIAOU_BASEDIR/templates/hardened/nftables.conf" /etc/
  89. sudo mkdir -p /etc/nftables.rules.d
  90. sudo systemctl enable nftables
  91. else
  92. echo "nftables already installed!"
  93. fi
  94. current_target="${TARGET:-not_defined_yet}"
  95. desktop=false
  96. samba=false
  97. transmission_daemon=false
  98. if [[ $current_target == not_defined_yet ]]; then
  99. echo -n "generating new firewall table first time... "
  100. else
  101. if [[ $current_target == 'dev' ]]; then
  102. if exist_command 'xprop'; then
  103. desktop=true
  104. fi
  105. if exist_command 'smbstatus'; then
  106. samba=true
  107. fi
  108. if exist_command 'transmission-daemon'; then
  109. transmission_daemon=true
  110. fi
  111. echo -n "generating new firewall table according to target=<${current_target}>, desktop=<$desktop>, samba=<$samba>, transmission_daemon=<$transmission_daemon> ..."
  112. else
  113. echo -n "generating new firewall table according to target=<${current_target}>..."
  114. fi
  115. fi
  116. sudo env target="$current_target" desktop="$desktop" samba="$samba" transmission_daemon="$transmission_daemon" tera -e --env-only --env-key env -t "$MIAOU_BASEDIR/templates/nftables/firewall.table.j2" -o /etc/nftables.rules.d/firewall.table &>/dev/null
  117. sudo systemctl reload nftables
  118. PREFIX="" echoinfo "DONE"
  119. }
  120. function miaou_init() {
  121. # shellcheck source=/dev/null
  122. [[ -f /opt/miaou-bash/lib/functions.sh ]] && source /opt/miaou-bash/lib/functions.sh
  123. # shellcheck source=/dev/null
  124. . "$MIAOU_BASEDIR/lib/functions.sh"
  125. export MIAOU_CONFIGDIR="$HOME/.config/miaou"
  126. set -Eeuo pipefail
  127. enable_trace
  128. trap 'ctrl_c $? ${LINENO:-0} ${BASH_LINENO:-0} ${BASH_COMMAND:-empty} $(printf "::%s" ${FUNCNAME[@]})' INT
  129. }
  130. function ctrl_c() {
  131. PREFIX="miaou:trap" echoerr "Ctrl + C happened, exiting!!! $*"
  132. exit 125
  133. }
  134. # extract source code error triggered on trap error <error_code> <error_line>
  135. function trap_error() {
  136. ERRORS_COUNT=0
  137. if [[ -f "$MIAOU_CONFIGDIR"/error_count ]]; then
  138. ERRORS_COUNT=$(cat "$MIAOU_CONFIGDIR"/error_count)
  139. else
  140. mkdir -p "$MIAOU_CONFIGDIR"
  141. printf 0 >"$MIAOU_CONFIGDIR"/error_count
  142. fi
  143. ERRORS_COUNT=$((ERRORS_COUNT + 1))
  144. printf '%s' $ERRORS_COUNT >"$MIAOU_CONFIGDIR"/error_count
  145. local PREFIX=""
  146. # local file="${0:-}"
  147. local err=$1 # error status
  148. local line=$2 # LINENO
  149. # local linecallfunc=${3:-}
  150. local command="${4:-}"
  151. local funcstack="${5:-}"
  152. local caller
  153. caller=$(caller | cut -d' ' -f2)
  154. # echo >&2
  155. # if [ "$funcstack" != "::" ]; then
  156. # echo -e "${RED}ERROR <$err>, due to command <$command> at line $line from <$caller>, stack=${funcstack}${NC}" >&2
  157. # else
  158. # echo >&2 "ERROR DETECTED"
  159. # fi
  160. # echo
  161. # echo -e "${PURPLE}$caller:$line ${NC}EXIT ${RED}<$err>${NC}" >&2
  162. # echo -e "${PURPLE}------------------------------------------ ${NC}" >&2
  163. if [[ $ERRORS_COUNT == 1 ]]; then
  164. echo
  165. echo -e "${RED}ERROR <$err>, due to command <$command $funcstack>${NC}" >&2
  166. fi
  167. echo -e "${PURPLE}$ERRORS_COUNT: $caller:$line ${RED}$command $funcstack${NC}" >&2
  168. # echo -e "${PURPLE}----------------------------- ${PURPLE}EXIT CODE ${PURPLE}--------------${PURPLE} $err ${NC}" >&2
  169. # if [[ $line -gt 2 ]]; then
  170. # sed "$((line - 2))q;d" "$caller" >&2
  171. # sed "$((line - 1))q;d" "$caller" >&2
  172. # fi
  173. # echo -ne "${BOLD}" >&2
  174. # sed "${line}q;d" "$caller" >&2
  175. # echo -e "${PURPLE}------------------------------------------ ${NC}" >&2
  176. }
  177. # exist_command(cmd1, ...)
  178. # test all commands exist, else fail
  179. function exist_command() {
  180. for i in "$@"; do
  181. command -v "$i" >/dev/null || return 50
  182. done
  183. }
  184. # test whether container <ct> is up and running?
  185. function container_running() {
  186. arg1_required "$@"
  187. container_exists "$1" && lxc list "$1" -c ns -f csv | head -n1 | grep -q "$1,RUNNING"
  188. lxc exec "$1" -- bash <<EOF
  189. set -Eeuo pipefail
  190. if [[ ! -f /root/cloud-status.json ]]; then
  191. cloud-init status --wait >/dev/null
  192. fi
  193. EOF
  194. }
  195. # test arg1 required
  196. function arg1_required() {
  197. [[ -z "${1:-}" ]] && echoerr "ERROR: arg#1 expected!" && return 125
  198. return 0
  199. }
  200. # test arg2 required
  201. function arg2_required() {
  202. [[ -z "${2:-}" ]] && echoerr "ERROR: arg#2 expected!" && return 125
  203. return 0
  204. }
  205. # test arg3 required
  206. function arg3_required() {
  207. [[ -z "${3:-}" ]] && echoerr "ERROR: arg#3 expected!" && return 125 || return 0
  208. }
  209. # test arg4 required
  210. function arg4_required() {
  211. [[ -z "${4:-}" ]] && echoerr "ERROR: arg#4 expected!" && return 125 || return 0
  212. }
  213. # test whether container <ct> exists yet?
  214. function container_exists() {
  215. arg1_required "$@"
  216. lxc list "$1" -c n -f csv | grep -q "^$1\$"
  217. }
  218. # 3 args expected: <commmand> <delay in s, example: 0.2> <max_attempts>
  219. function wait_for_command {
  220. arg3_required "$@"
  221. command=$1
  222. delay=$2
  223. max_attempt=$3
  224. attempt=0
  225. while ! eval "$command"; do
  226. attempt=$((attempt + 1))
  227. if [[ $attempt -gt $max_attempt ]]; then
  228. echoerr "command <$command> failed after a delay of $(bc <<<"$max_attempt * $delay")s and $max_attempt attempts"
  229. return 1
  230. else
  231. sleep "$delay"
  232. fi
  233. done
  234. echo SUCCESS
  235. }
  236. function wait_for_container_full_initialization {
  237. arg1_required "$@"
  238. wait_for_command "lxc exec $1 -- test -f /root/cloud-status.json" 0.2 40
  239. }
  240. # build debian image with prebuild miaou-bash and various useful settings
  241. # ARG1=release [bullseye, buster]
  242. function build_miaou_image() {
  243. local RELEASE="$1"
  244. local IMAGE_LABEL="$RELEASE-miaou"
  245. local PREFIX="miaou:image"
  246. local DEB_REPOSITORY
  247. DEB_REPOSITORY=$(grep ^deb /etc/apt/sources.list | head -n1 | cut -d ' ' -f2 | cut -d '/' -f3)
  248. if ! lxc image -cl list -f csv | grep -q "$IMAGE_LABEL"; then
  249. echo "building lxc image <$IMAGE_LABEL> ... "
  250. echo "image will reuse same local repository <$DEB_REPOSITORY>"
  251. creation_date=$(date +%s)
  252. sudo /opt/miaou-bash/tools/idem_apt_install debootstrap
  253. cat <<EOF1 | sudo bash
  254. set -euo pipefail
  255. rm -rf /tmp/$IMAGE_LABEL{,-image}
  256. mkdir -p /tmp/$IMAGE_LABEL{,-image}
  257. debootstrap $RELEASE /tmp/$IMAGE_LABEL http://$DEB_REPOSITORY/debian
  258. echo
  259. echo "DEBOOTSTRAP ... OK"
  260. echo
  261. cat <<EOF2 | chroot /tmp/$IMAGE_LABEL
  262. set -euo pipefail
  263. echo "image prepare source.list from $DEB_REPOSITORY"
  264. if [[ "$RELEASE" == "buster" ]]; then
  265. cat <<EOF3 >/etc/apt/sources.list
  266. deb http://$DEB_REPOSITORY/debian $RELEASE main contrib
  267. deb http://$DEB_REPOSITORY/debian $RELEASE-updates main contrib
  268. deb http://$DEB_REPOSITORY/debian-security/ $RELEASE/updates main contrib
  269. EOF3
  270. else
  271. cat <<EOF3 >/etc/apt/sources.list
  272. deb http://$DEB_REPOSITORY/debian $RELEASE main contrib
  273. deb http://$DEB_REPOSITORY/debian $RELEASE-updates main contrib
  274. deb http://$DEB_REPOSITORY/debian-security/ $RELEASE-security main contrib
  275. EOF3
  276. fi
  277. echo APT UPDATE
  278. apt update && apt dist-upgrade -y
  279. apt install -y curl wget file git sudo bash-completion
  280. curl https://git.artcode.re/miaou/miaou-bash/raw/branch/main/install.sh | sudo bash -s -- --host
  281. # TODO: remove line below
  282. # ln -sf /usr/share/zoneinfo/Indian/Reunion /etc/localtime
  283. cat <<EOF3 >/etc/network/interfaces
  284. # This file describes the network interfaces available on your system
  285. # and how to activate them. For more information, see interfaces(5).
  286. # The loopback network interface
  287. auto lo
  288. iface lo inet loopback
  289. auto eth0
  290. iface eth0 inet dhcp
  291. source /etc/network/interfaces.d/*
  292. EOF3
  293. echo "deboostrap ready!"
  294. EOF2
  295. cd /tmp/$IMAGE_LABEL-image
  296. tar -czf rootfs.tar.gz -C /tmp/$IMAGE_LABEL .
  297. cat <<EOF2 >metadata.yaml
  298. architecture: "x86_64"
  299. creation_date: $creation_date
  300. properties:
  301. architecture: "x86_64"
  302. description: "Debian $RELEASE for miaou instances"
  303. os: "debian"
  304. release: "$RELEASE"
  305. EOF2
  306. tar -czf metadata.tar.gz metadata.yaml
  307. EOF1
  308. lxc image import "/tmp/$IMAGE_LABEL-image/metadata.tar.gz" "/tmp/$IMAGE_LABEL-image/rootfs.tar.gz" --alias "$IMAGE_LABEL"
  309. echo "image <$IMAGE_LABEL> successfully built!"
  310. echo DONE
  311. else
  312. echo "image <$IMAGE_LABEL> already built!"
  313. fi
  314. }
  315. # convert array to string according to IFS arg1
  316. # example: join "," "${MY_ARRAY[@]}" => one,two,three
  317. function join() {
  318. local IFS="$1"
  319. shift
  320. builtin echo "$*"
  321. }
  322. # execute remote scripting onto one LXC container <CONTAINER> [COMMANDS, ...]
  323. # may use one command like: `lxc_exec ct1 uname -a`
  324. # or pipe like so: `
  325. # cat <<EOF | lxc_exec ct1
  326. # ls -l
  327. # uname -a
  328. # echo [\$0] [\$1] [\$2] # toto titi tata
  329. # EOF
  330. # `
  331. function lxc_exec() {
  332. arg1_required "$@"
  333. container="$1"
  334. shift
  335. declare -a ARGUMENTS
  336. ARGUMENTS=(toto titi tata) # might be overriden with interesting stuff!
  337. if ((${#} == 0)); then
  338. multiline=""
  339. while read -r line; do
  340. if [[ ! "$line" =~ ^\# ]] && [[ ! "$line" =~ ^[[:space:]]*$ ]]; then
  341. if [[ "$line" =~ .*\;$ ]] || [[ "$line" =~ do$ ]] || [[ "$line" =~ then$ ]] || [[ "$line" =~ else$ ]]; then
  342. multiline+="${line} " # append space in case of ending with either '; do then else'
  343. else
  344. multiline+="${line};" # append ; for multiple commands
  345. fi
  346. fi
  347. done
  348. # echo "DEBUG: multiline = [$multiline]"
  349. # echo DEBUG: lxc exec "$container" -- bash -lc "$multiline" "${ARGUMENTS[@]}"
  350. lxc exec "$container" -- bash -lc "$multiline" "${ARGUMENTS[@]}"
  351. else
  352. lxc exec "$container" -- bash -lc "$*" "${ARGUMENTS[@]}"
  353. fi
  354. }
  355. # check container exist and running
  356. function check_container() {
  357. arg1_required "$@"
  358. local CT="$1"
  359. container_exists "$CT"
  360. container_running "$CT"
  361. }
  362. function launch_container() {
  363. arg1_required "$@"
  364. local ct="$1"
  365. if ! container_exists "$ct"; then
  366. echo "container <$ct> about to be created ..."
  367. local extra_release="${2:-}"
  368. if [[ -n "$extra_release" ]] && ! lxc image info "${extra_release}-miaou" >/dev/null; then
  369. echoerrn "unknown extra_release <${extra_release}-miaou>!\nHINT : please add it into /etc/miaou/defaults.yaml, then re-install miaou!"
  370. exit 128
  371. fi
  372. if [[ -n "$extra_release" ]]; then
  373. echoerrn "FIXME: lxc-miaou-create -o release=bookworm should be implemented ...."
  374. lxc-miaou-create "$ct" "$extra_release"
  375. else
  376. lxc-miaou-create "$ct"
  377. fi
  378. echo "DONE"
  379. fi
  380. if ! container_running "$ct"; then
  381. echowarn "container <$ct> seems to be asleep, starting ..."
  382. lxc start "$ct"
  383. echowarn DONE
  384. fi
  385. }
  386. function load_yaml_from_expanded {
  387. arg1_required "$@"
  388. yaml_key="$1"
  389. yaml_file="$MIAOU_CONFIGDIR/miaou.expanded.yaml"
  390. yaml_value=$(yq ".$yaml_key" "$yaml_file")
  391. if [[ -n "$yaml_value" ]] && [[ "$yaml_value" != "null" ]] && [[ "$yaml_value" != "$TO_BE_DEFINED" ]]; then
  392. PREFIX="" echo "$yaml_value"
  393. else
  394. echoerr "undefined value for key: <$yaml_key> from file: <$yaml_file>"
  395. return 98
  396. fi
  397. }
  398. function check_yaml_defined_value {
  399. yaml_file="$1"
  400. yaml_key="$2"
  401. yaml_value=$(yq ".$yaml_key" "$yaml_file")
  402. if [[ -n "$yaml_value" ]] && [[ "$yaml_value" != "null" ]] && [[ "$yaml_value" != "$TO_BE_DEFINED" ]]; then
  403. return 0
  404. else
  405. echoerr "undefined value for key: <$yaml_key> from file: <$yaml_file>"
  406. return 99
  407. fi
  408. }
  409. # halt unless current user is root
  410. function root_required() {
  411. [[ $(id -u) == 0 ]] || (echoerr "root required" && return 1)
  412. }
  413. # arg#1: environment variable
  414. # read from environment or ask entry before exporting new variable
  415. function env_or_ask {
  416. if [[ -n ${1+x} ]]; then
  417. if printenv "$1" >/dev/null; then
  418. echo "value defined as $(printenv "$1")"
  419. else
  420. printf "Please define %20s: " "$1"
  421. read -r
  422. export "$1=\"$REPLY\"" >/dev/null
  423. fi
  424. else
  425. echoerr "env_or_ask requires one argument: <VARIABLE_NAME>" && exit 5
  426. fi
  427. }
  428. # grab and install related project
  429. function install_miaou_bash() {
  430. local PREFIX="miaou-bash:install"
  431. if [[ ! -d /opt/miaou-bash ]]; then
  432. echo "installing curl wget commands ..."
  433. sudo apt install -y curl wget
  434. echo "installing miaou-bash..."
  435. curl https://git.artcode.re/miaou/miaou-bash/raw/branch/main/install.sh | sudo bash -s -- --host
  436. export PATH=$PATH:/opt/miaou-bash/tools/
  437. echo "OK"
  438. else
  439. echo "addon <miaou-bash> already installed!"
  440. fi
  441. # shellcheck source=/dev/null
  442. source /etc/bash.bashrc
  443. sudo /opt/miaou-bash/tools/idem_apt_install bash-completion
  444. }
  445. function add_toolbox_sudoers {
  446. local PREFIX="toolbox:sudoers"
  447. echo -n "creating sudoers file to allow sudo as command from /TOOLBOX... "
  448. sudo mkdir -p /etc/sudoers.d
  449. if [[ ! -f /etc/sudoers.d/add_TOOLBOX_to_PATH ]]; then
  450. sudo tee /etc/sudoers.d/add_TOOLBOX_to_PATH &>/dev/null <<EOF
  451. Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/TOOLBOX"
  452. EOF
  453. PREFIX="" echo "updated!"
  454. else
  455. PREFIX="" echo "already done!"
  456. fi
  457. }
  458. function prepare_toolbox() {
  459. local PREFIX="toolbox:prepare"
  460. sudo mkdir -p /TOOLBOX
  461. if ! command -v cargo &>/dev/null; then
  462. echo -n "installing <cargo> ... "
  463. curl -sSf https://sh.rustup.rs | sh -s -- -y
  464. # shellcheck source=/dev/null
  465. source "$HOME/.cargo/env"
  466. /opt/miaou-bash/tools/append_or_replace "^PATH=\$PATH:\$HOME/\\.cargo/bin" "PATH=\$PATH:\$HOME/.cargo/bin" ~/.bashrc
  467. PREFIX="" echo "OK"
  468. else
  469. echo "command <cargo> already installed!"
  470. fi
  471. echo -n "installing <fd> ... "
  472. if [ ! -f "/TOOLBOX/fd" ]; then
  473. idem_cargo_install fd-find
  474. sudo cp "$HOME"/.cargo/bin/fd /TOOLBOX/fd
  475. PREFIX="" echo "successfully installed!"
  476. else
  477. PREFIX="" echo "already done!"
  478. fi
  479. echo -n "installing <viu> ... "
  480. if [ ! -f "/TOOLBOX/viu" ]; then
  481. idem_cargo_install viu
  482. sudo cp "$HOME"/.cargo/bin/viu /TOOLBOX/
  483. PREFIX="" echo "successfully installed!"
  484. else
  485. PREFIX="" echo "already done!"
  486. fi
  487. echo -n "installing <rg> alias <ripgrep> ... "
  488. if [ ! -f "/TOOLBOX/rg" ]; then
  489. sudo /opt/miaou-bash/tools/idem_apt_install ripgrep
  490. sudo ln /usr/bin/rg /TOOLBOX/
  491. PREFIX="" echo "successfully installed"
  492. else
  493. PREFIX="" echo "already done!"
  494. fi
  495. echo -n "installing <ag> alias <silversearcher-ag> ... "
  496. if [ ! -f "/TOOLBOX/ag" ]; then
  497. sudo /opt/miaou-bash/tools/idem_apt_install silversearcher-ag
  498. sudo ln /usr/bin/ag /TOOLBOX/
  499. PREFIX="" echo "successfully installed"
  500. else
  501. PREFIX="" echo "already done!"
  502. fi
  503. echo -n "installing <bandwhich> ... "
  504. if [ ! -f "/TOOLBOX/bandwhich" ]; then
  505. idem_cargo_install bandwhich
  506. sudo cp "$HOME"/.cargo/bin/bandwhich /TOOLBOX/bandwhich
  507. PREFIX="" echo "successfully installed"
  508. else
  509. PREFIX="" echo "already done!"
  510. fi
  511. echo -n "installing <btm> alias <bottom> ... "
  512. if [ ! -f "/TOOLBOX/btm" ]; then
  513. VERSION=$(wget_semver github ClementTsang/bottom)
  514. cd /tmp
  515. wget "https://github.com/ClementTsang/bottom/releases/download/$VERSION/bottom_x86_64-unknown-linux-musl.tar.gz"
  516. tar -xzvf bottom_x86_64-unknown-linux-musl.tar.gz
  517. sudo cp btm /usr/local/bin/
  518. sudo ln /usr/local/bin/btm /TOOLBOX/
  519. PREFIX="" echo "successfully installed"
  520. else
  521. PREFIX="" echo "already done!"
  522. fi
  523. echo -n "installing <micro> ... "
  524. if [ ! -f "/TOOLBOX/micro" ]; then
  525. cd /tmp || (echoerr "/tmp wrong permission" && exit 101)
  526. curl -q https://getmic.ro | GETMICRO_REGISTER=n sh
  527. sudo mv micro /TOOLBOX/micro
  528. sudo chown root:root /TOOLBOX/micro
  529. PREFIX="" echo "successfully installed"
  530. else
  531. PREFIX="" echo "already done!"
  532. fi
  533. echo -n "installing <ncdu> ... "
  534. if [ ! -f "/TOOLBOX/ncdu" ]; then
  535. sudo /opt/miaou-bash/tools/idem_apt_install ncdu
  536. sudo cp /usr/bin/ncdu /TOOLBOX/ncdu
  537. PREFIX="" echo "successfully installed"
  538. else
  539. PREFIX="" echo "already done!"
  540. fi
  541. echo -n "installing <unzip> ... "
  542. if [ ! -f "/TOOLBOX/unzip" ]; then
  543. sudo /opt/miaou-bash/tools/idem_apt_install unzip
  544. sudo cp /usr/bin/unzip /TOOLBOX/unzip
  545. PREFIX="" echo "successfully installed"
  546. else
  547. PREFIX="" echo "already done!"
  548. fi
  549. echo -n "installing <tree> ... "
  550. if [ ! -f "/TOOLBOX/tree" ]; then
  551. sudo /opt/miaou-bash/tools/idem_apt_install tree
  552. sudo cp /bin/tree /TOOLBOX/tree
  553. PREFIX="" echo "successfully installed"
  554. else
  555. PREFIX="" echo "already done!"
  556. fi
  557. echo -n "installing <duf> ... "
  558. if [ ! -f "/TOOLBOX/duf" ]; then
  559. VERSION=$(/opt/miaou-bash/tools/wget_semver github muesli/duf)
  560. VERSION_WITHOUT_V=${VERSION#v}
  561. wget -O /tmp/duf.deb "https://github.com/muesli/duf/releases/download/${VERSION}/duf_${VERSION_WITHOUT_V}_linux_amd64.deb"
  562. sudo dpkg -i /tmp/duf.deb
  563. sudo cp /bin/duf /TOOLBOX/duf
  564. PREFIX="" echo "successfully installed"
  565. else
  566. PREFIX="" echo "already done!"
  567. fi
  568. echo -n "installing <curl> ... "
  569. if [ ! -f "/TOOLBOX/curl" ]; then
  570. sudo wget -O /TOOLBOX/curl "https://github.com/moparisthebest/static-curl/releases/latest/download/curl-amd64"
  571. sudo chmod +x /TOOLBOX/curl
  572. PREFIX="" echo "successfully installed"
  573. else
  574. PREFIX="" echo "already done!"
  575. fi
  576. echo -n "installing <wget> ... "
  577. if [ ! -f "/TOOLBOX/wget" ]; then
  578. sudo ln -f /usr/bin/wget /TOOLBOX/wget
  579. PREFIX="" echo "successfully installed"
  580. else
  581. PREFIX="" echo "already done!"
  582. fi
  583. }
  584. # install_mandatory_commands
  585. function install_mandatory_commands() {
  586. local PREFIX="mandatory:commands"
  587. echo "installing various mandatory commands"
  588. sudo /opt/miaou-bash/tools/idem_apt_install dnsutils build-essential curl mariadb-client postgresql-client
  589. if ! exist_command tera; then
  590. echo "installing <tera> ..."
  591. local version=v0.2.4
  592. wget -q "https://github.com/chevdor/tera-cli/releases/download/${version}/tera-cli_linux_amd64.deb" -O /tmp/tera-cli_linux_amd64.deb
  593. sudo dpkg -i /tmp/tera-cli_linux_amd64.deb
  594. else
  595. echo "command <tera> already installed!"
  596. fi
  597. if ! exist_command yq; then
  598. local version binary
  599. version='v4.35.2'
  600. binary='yq_linux_amd64'
  601. sudo sh -c "wget https://github.com/mikefarah/yq/releases/download/${version}/${binary}.tar.gz -O - |\
  602. tar -xz ./${binary} && sudo mv ${binary} /usr/bin/yq"
  603. else
  604. echo "command <yq> already installed!"
  605. fi
  606. }
  607. # flatten array, aka remove duplicated elements in array
  608. # return: `mapfile -t OUTPUT_ARRAY < <(sort_array "${INPUT_ARRAY[@]}")`
  609. function flatten_array {
  610. declare -a array=("$@")
  611. IFS=" " read -r -a array <<<"$(tr ' ' '\n' <<<"${array[@]}" | sort -u | tr '\n' ' ')"
  612. printf '%s\n' "${array[@]}"
  613. }