dotfiles/run_onchange_after_install_main_deps.sh.tmpl
Anton Volnuhin e54f9d6dfb move welcome banner from chezmoi script to env.lat bootstrap
The banner with "Press Enter to continue" now shows early in env.lat,
right after installing prerequisites and terminfo but before cloning
chezmoi. Removes unused ASSUME_YES flag from the chezmoi script.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 20:50:28 +03:00

474 lines
16 KiB
Bash

#!/usr/bin/env bash
## Anton Volnuhin's env
## anton@volnuhin.com
##
## Supported OSes:
## * MacOS
## * Linux
## * FreeBSD
## * OpenBSD
##
## Supported Linux distros:
## * ubuntu
## * debian
## * fedora
## * almalinux
## * rocky
## * arch
#Config
{{ if eq .chezmoi.os "darwin" -}}
STEPS=9
{{ else if eq .chezmoi.os "freebsd" -}}
STEPS=9
{{ else if eq .chezmoi.os "openbsd" -}}
STEPS=8
{{ else if eq .chezmoi.osRelease.id "ubuntu" -}}
STEPS=14
{{ else if eq .chezmoi.osRelease.id "debian" -}}
STEPS=13
{{ else -}}
STEPS=12
{{ end -}}
PAD_LEN=4
BASE_PACKAGES="gnupg curl wget git"
ACT_PACKAGES="ripgrep unzip htop fzf bat gawk jq nnn tmux mc nethogs ngrep mtr gcc"
# Prefer doas over sudo (FreeBSD/OpenBSD), skip if already root
if [ "$(id -u)" -eq 0 ]; then
SUDO=""
elif command -v doas >/dev/null 2>&1 && [ -f /etc/doas.conf ]; then
SUDO="doas"
elif command -v sudo >/dev/null 2>&1; then
SUDO="sudo"
else
SUDO=""
fi
# Non-interactive mode flags
NON_INTERACTIVE=0
SKIP_TIMEZONE=0
SKIP_AUTH_KEYS=0
SKIP_SHELL_CHANGE=0
SKIP_NVIM_BOOTSTRAP=0
while [ $# -gt 0 ]; do
case "$1" in
--non-interactive) NON_INTERACTIVE=1 ;;
--skip-timezone) SKIP_TIMEZONE=1 ;;
--skip-authorized-keys) SKIP_AUTH_KEYS=1 ;;
--skip-shell-change) SKIP_SHELL_CHANGE=1 ;;
--skip-neovim-bootstrap) SKIP_NVIM_BOOTSTRAP=1 ;;
*) echo "Unknown option: $1"; exit 2 ;;
esac
shift
done
# Auto-detect non-interactive mode when no real terminal is present
if [ ! -t 0 ] || [ ! -t 1 ]; then
NON_INTERACTIVE=1
SKIP_NVIM_BOOTSTRAP=1
fi
# Timeout portability (macOS has gtimeout via coreutils, not timeout)
TIMEOUT_BIN="$(command -v timeout || command -v gtimeout || true)"
#Colors
Green='\033[0;32m' # Green
Blue='\033[0;34m' # Blue
Yello='\033[0;33m' # Blue
Gray='\033[38;5;7m' # Light Gray
NC='\033[0m' # No Color
# Fall back to xterm-256color if the terminal's terminfo isn't installed yet
# (ghostty/kitty terminfo gets installed later in this script)
if ! infocmp "$TERM" >/dev/null 2>&1; then
export TERM="xterm-256color"
fi
cd
#Setup
USER=$(whoami)
STEP=1
if [ "$NON_INTERACTIVE" -eq 1 ]; then
# Simple output for non-interactive mode (Vagrant, CI, etc.)
new_line () {
echo -e "\n--> $Blue$1$NC"
STEP=$(($STEP + 1))
}
pad() {
cat
}
print_block () { :; }
else
TERM_WIDTH_BASE=$(stty size 2>/dev/null | awk '{print $2}')
TERM_WIDTH_BASE=${TERM_WIDTH_BASE:-80}
PADDING=$(printf "%${PAD_LEN}s") # 4 spaces of padding
TERM_WIDTH=$(($TERM_WIDTH_BASE - $PAD_LEN))
get_line ()
{
IFS='[;' read -p $'\e[6n' -d R -a pos -rs || echo "failed with error: $? ; ${pos[*]}"
echo ${pos[1]}
}
print_block ()
{
local ln=$(get_line)
if [[ "$ln" -lt "$(($LINES - $STEPS))" ]];then
tput cup $(($LINES - $STEPS - 2)) 0
else
ln=$(($LINES - $STEPS - 1))
fi
echo "..........................................................."
for i in $(seq 1 $STEPS)
do
echo -n " >"
tput el
echo
done
tput cup $(($ln - 3)) 0
}
new_line () {
# move cursor to current step's line
local ln=$(get_line)
tput csr 0 $LINES
tput cup $(($LINES - $STEPS - 2 + $STEP)) 0
echo -e "==> "$Green$1$NC
STEP=$(($STEP + 1))
tput csr 0 $(($LINES - $STEPS - 3))
tput cup $(($ln - 1)) 0
echo -e "\n--> "$Blue$1$NC
tput el
}
pad() {
while IFS= read -r line; do
echo -e $Gray$line$NC | fold -s -w $TERM_WIDTH | sed "s/^/$PADDING/"
done
}
fi
{{ if eq .chezmoi.os "linux" -}}
{{ if eq .chezmoi.osRelease.id "almalinux" "rocky" "fedora" -}}
echo -e "--> "$Blue$1$Blue"Installing prerequestative ncurses for this script's interface"$NC
$SUDO dnf install ncurses -y 2>&1|pad
{{ end -}}
{{ end -}}
if [ "$NON_INTERACTIVE" -eq 0 ]; then
LINES=$(tput lines)
print_block
fi
new_line "Update caches and upgrade packages"
{{ if eq .chezmoi.os "freebsd" -}}
$SUDO pkg update 2>&1|pad
{{ else if eq .chezmoi.os "openbsd" -}}
$SUDO pkg_add -uv 2>&1|pad
{{ else if eq .chezmoi.os "darwin" -}}
if ! command -v brew >/dev/null 2>&1; then
bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 2>&1|pad
fi
if [ -x /opt/homebrew/bin/brew ]; then
eval "$(/opt/homebrew/bin/brew shellenv)"
elif [ -x /usr/local/bin/brew ]; then
eval "$(/usr/local/bin/brew shellenv)"
fi
brew update 2>&1|pad
{{ else if eq .chezmoi.osRelease.id "fedora" "almalinux" "rocky" -}}
$SUDO dnf update -y 2>&1|pad
{{ else if eq .chezmoi.osRelease.id "debian" "ubuntu" -}}
$SUDO apt-get update 2>&1|pad
DEBIAN_FRONTEND=noninteractive $SUDO apt-get -o "Dpkg::Options::=--force-confold" -o "Dpkg::Options::=--force-confdef" upgrade -y --allow-downgrades --allow-remove-essential --allow-change-held-packages 2>&1|pad
{{ else if eq .chezmoi.osRelease.id "arch" "archarm" -}}
$SUDO pacman -Syu --noconfirm 2>&1|pad
{{ end -}}
new_line "Install base packages"
{{ if eq .chezmoi.os "freebsd" -}}
$SUDO pkg install --yes $BASE_PACKAGES py39-pipx direnv 2>&1|pad
{{ else if eq .chezmoi.os "openbsd" -}}
$SUDO pkg_add -v $BASE_PACKAGES py3-pipx direnv 2>&1|pad
{{ else if eq .chezmoi.os "darwin" -}}
brew install -q $BASE_PACKAGES python pipx direnv goku 2>&1|pad
{{ else if eq .chezmoi.osRelease.id "fedora" -}}
$SUDO dnf install $BASE_PACKAGES direnv kitty-terminfo util-linux-user pipx -y 2>&1|pad
{{ else if eq .chezmoi.osRelease.id "almalinux" "rocky" -}}
$SUDO dnf install epel-release -y 2>&1|pad
$SUDO dnf install $BASE_PACKAGES kitty-terminfo which util-linux-user xz pipx -y 2>&1|pad
{{ else if eq .chezmoi.osRelease.id "debian" "ubuntu" -}}
$SUDO apt-get install $BASE_PACKAGES kitty-terminfo direnv pipx python3-venv -y 2>&1|pad
$SUDO apt-get install software-properties-common -y 2>&1|pad
{{ else if eq .chezmoi.osRelease.id "arch" "archarm" -}}
$SUDO pacman -Sy --noconfirm $BASE_PACKAGES kitty-terminfo ghostty-terminfo python-pipx 2>&1|pad
{{ end -}}
## Install ghostty terminfo if not already present
if ! infocmp xterm-ghostty >/dev/null 2>&1; then
cat <<'GHOSTTY_TERMINFO' | $SUDO tic -x - 2>&1|pad
xterm-ghostty|ghostty|Ghostty,
am, bce, ccc, hs, km, mc5i, mir, msgr, npc, xenl, AX, Su, Tc, XT, fullkbd,
colors#256, cols#80, it#8, lines#24, pairs#32767,
acsc=++\,\,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{ "{{" }}||{{ "}}" }}~~,
bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l,
clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=^M,
csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H,
cud=\E[%p1%dB, cud1=^J, cuf=\E[%p1%dC, cuf1=\E[C,
cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A,
cvvis=\E[?12;25h, dch=\E[%p1%dP, dch1=\E[P, dim=\E[2m,
dl=\E[%p1%dM, dl1=\E[M, dsl=\E]2;\007, ech=\E[%p1%dX,
ed=\E[J, el=\E[K, el1=\E[1K, flash=\E[?5h$<100/>\E[?5l,
fsl=^G, home=\E[H, hpa=\E[%i%p1%dG, ht=^I, hts=\EH,
ich=\E[%p1%d@, ich1=\E[@, il=\E[%p1%dL, il1=\E[L, ind=^J,
indn=\E[%p1%dS,
initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\,
invis=\E[8m, kDC=\E[3;2~, kEND=\E[1;2F, kHOM=\E[1;2H,
kIC=\E[2;2~, kLFT=\E[1;2D, kNXT=\E[6;2~, kPRV=\E[5;2~,
kRIT=\E[1;2C, kbs=\177, kcbt=\E[Z, kcub1=\EOD, kcud1=\EOB,
kcuf1=\EOC, kcuu1=\EOA, kdch1=\E[3~, kend=\EOF, kent=\EOM,
kf1=\EOP, kf10=\E[21~, kf11=\E[23~, kf12=\E[24~,
kf13=\E[1;2P, kf14=\E[1;2Q, kf15=\E[1;2R, kf16=\E[1;2S,
kf17=\E[15;2~, kf18=\E[17;2~, kf19=\E[18;2~, kf2=\EOQ,
kf20=\E[19;2~, kf21=\E[20;2~, kf22=\E[21;2~,
kf23=\E[23;2~, kf24=\E[24;2~, kf25=\E[1;5P, kf26=\E[1;5Q,
kf27=\E[1;5R, kf28=\E[1;5S, kf29=\E[15;5~, kf3=\EOR,
kf30=\E[17;5~, kf31=\E[18;5~, kf32=\E[19;5~,
kf33=\E[20;5~, kf34=\E[21;5~, kf35=\E[23;5~,
kf36=\E[24;5~, kf37=\E[1;6P, kf38=\E[1;6Q, kf39=\E[1;6R,
kf4=\EOS, kf40=\E[1;6S, kf41=\E[15;6~, kf42=\E[17;6~,
kf43=\E[18;6~, kf44=\E[19;6~, kf45=\E[20;6~,
kf46=\E[21;6~, kf47=\E[23;6~, kf48=\E[24;6~,
kf49=\E[1;3P, kf5=\E[15~, kf50=\E[1;3Q, kf51=\E[1;3R,
kf52=\E[1;3S, kf53=\E[15;3~, kf54=\E[17;3~,
kf55=\E[18;3~, kf56=\E[19;3~, kf57=\E[20;3~,
kf58=\E[21;3~, kf59=\E[23;3~, kf6=\E[17~, kf60=\E[24;3~,
kf61=\E[1;4P, kf62=\E[1;4Q, kf63=\E[1;4R, kf7=\E[18~,
kf8=\E[19~, kf9=\E[20~, khome=\EOH, kich1=\E[2~,
kind=\E[1;2B, kmous=\E[<, knp=\E[6~, kpp=\E[5~,
kri=\E[1;2A, oc=\E]104\007, op=\E[39;49m, rc=\E8,
rep=%p1%c\E[%p2%{1}%-%db, rev=\E[7m, ri=\EM,
rin=\E[%p1%dT, ritm=\E[23m, rmacs=\E(B, rmam=\E[?7l,
rmcup=\E[?1049l, rmir=\E[4l, rmkx=\E[?1l\E>, rmso=\E[27m,
rmul=\E[24m, rs1=\E]\E\\\Ec, sc=\E7,
setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m,
setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m,
sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m,
sgr0=\E(B\E[m, sitm=\E[3m, smacs=\E(0, smam=\E[?7h,
smcup=\E[?1049h, smir=\E[4h, smkx=\E[?1h\E=, smso=\E[7m,
smul=\E[4m, tbc=\E[3g, tsl=\E]2;, u6=\E[%i%d;%dR, u7=\E[6n,
u8=\E[?%[;0123456789]c, u9=\E[c, vpa=\E[%i%p1%dd,
BD=\E[?2004l, BE=\E[?2004h, Clmg=\E[s,
Cmg=\E[%i%p1%d;%p2%ds, Dsmg=\E[?69l, E3=\E[3J,
Enmg=\E[?69h, Ms=\E]52;%p1%s;%p2%s\007, PE=\E[201~,
PS=\E[200~, RV=\E[>c, Se=\E[2 q,
Setulc=\E[58\:2\:\:%p1%{65536}%/%d\:%p1%{256}%/%{255}%&%d\:%p1%{255}%&%d%;m,
Smulx=\E[4\:%p1%dm, Ss=\E[%p1%d q,
Sync=\E[?2026%?%p1%{1}%-%tl%eh%;,
XM=\E[?1006;1000%?%p1%{1}%=%th%el%;, XR=\E[>0q,
fd=\E[?1004l, fe=\E[?1004h, kDC3=\E[3;3~, kDC4=\E[3;4~,
kDC5=\E[3;5~, kDC6=\E[3;6~, kDC7=\E[3;7~, kDN=\E[1;2B,
kDN3=\E[1;3B, kDN4=\E[1;4B, kDN5=\E[1;5B, kDN6=\E[1;6B,
kDN7=\E[1;7B, kEND3=\E[1;3F, kEND4=\E[1;4F,
kEND5=\E[1;5F, kEND6=\E[1;6F, kEND7=\E[1;7F,
kHOM3=\E[1;3H, kHOM4=\E[1;4H, kHOM5=\E[1;5H,
kHOM6=\E[1;6H, kHOM7=\E[1;7H, kIC3=\E[2;3~, kIC4=\E[2;4~,
kIC5=\E[2;5~, kIC6=\E[2;6~, kIC7=\E[2;7~, kLFT3=\E[1;3D,
kLFT4=\E[1;4D, kLFT5=\E[1;5D, kLFT6=\E[1;6D,
kLFT7=\E[1;7D, kNXT3=\E[6;3~, kNXT4=\E[6;4~,
kNXT5=\E[6;5~, kNXT6=\E[6;6~, kNXT7=\E[6;7~,
kPRV3=\E[5;3~, kPRV4=\E[5;4~, kPRV5=\E[5;5~,
kPRV6=\E[5;6~, kPRV7=\E[5;7~, kRIT3=\E[1;3C,
kRIT4=\E[1;4C, kRIT5=\E[1;5C, kRIT6=\E[1;6C,
kRIT7=\E[1;7C, kUP=\E[1;2A, kUP3=\E[1;3A, kUP4=\E[1;4A,
kUP5=\E[1;5A, kUP6=\E[1;6A, kUP7=\E[1;7A, kxIN=\E[I,
kxOUT=\E[O, rmxx=\E[29m, rv=\E\\[[0-9]+;[0-9]+;[0-9]+c,
setrgbb=\E[48\:2\:%p1%d\:%p2%d\:%p3%dm,
setrgbf=\E[38\:2\:%p1%d\:%p2%d\:%p3%dm, smxx=\E[9m,
xm=\E[<%i%p3%d;%p1%d;%p2%d;%?%p4%tM%em%;,
xr=\EP>\\|[ -~]+a\E\\,
GHOSTTY_TERMINFO
fi
################################################################################
################################################################################
{{ if eq .chezmoi.os "linux" -}}
if ! type nix &> /dev/null
then
new_line "Install nix packet manager (single user)"
bash <(curl --silent -L https://nixos.org/nix/install) --no-daemon 2>&1|pad
source ~/.nix-profile/etc/profile.d/nix.sh
fi
################################################################################
################################################################################
{{ if eq .chezmoi.osRelease.id "debian" "ubuntu" -}}
{{ if eq .chezmoi.osRelease.id "debian" -}}
new_line "Subscribe to PPA for fish"
## Setup fish repo in debian
VERS={{ .chezmoi.osRelease.versionID }}
echo "deb http://download.opensuse.org/repositories/shells:/fish:/release:/3/Debian_$VERS/ /" | $SUDO tee /etc/apt/sources.list.d/shells:fish:release:3.list 2>/dev/null
curl -fsSL "https://download.opensuse.org/repositories/shells:fish:release:3/Debian_$VERS/Release.key" | gpg --dearmor | $SUDO tee /etc/apt/trusted.gpg.d/shells_fish_release_3.gpg > /dev/null
{{ else -}}
new_line "Subscribe to PPA for fish"
## Setup fish repo in ubuntu
$SUDO apt-add-repository ppa:fish-shell/release-3 -y 2>&1|pad
new_line "Subscribe to PPA for neovim"
## Neovim ppa
$SUDO add-apt-repository ppa:neovim-ppa/unstable -y 2>&1|pad
{{ end -}}
$SUDO apt-get update 2>&1|pad
{{ end -}}
{{ end -}}
new_line "Install neovim, fish, atuin, jump and the rest"
{{ if eq .chezmoi.os "freebsd" -}}
$SUDO pkg install --yes $ACT_PACKAGES neovim fish atuin lazygit fd-find pam_ssh_agent_auth fd 2>&1|pad
{{ else if eq .chezmoi.os "openbsd" -}}
$SUDO pkg_add -v neovim fish%main fd ripgrep unzip-- htop fzf bat gawk jq nnn-- mc ngrep mtr-- gcc%11 2>&1|pad
{{ else if eq .chezmoi.os "darwin" -}}
brew install -q fish neovim $ACT_PACKAGES atuin jump fd sk dust lazygit 2>&1|pad
{{ else if eq .chezmoi.osRelease.id "fedora" -}}
PAM_SSH_PKG=""
if dnf list --quiet pam_ssh_agent_auth >/dev/null 2>&1; then
PAM_SSH_PKG="pam_ssh_agent_auth"
else
echo -e "${Yello}WARNING: pam_ssh_agent_auth not available in repos. sudo via SSH agent won't work.${NC}"
fi
$SUDO dnf install neovim fish $ACT_PACKAGES fd-find ${PAM_SSH_PKG:+$PAM_SSH_PKG} -y 2>&1|pad
new_line "via NIX"
nix-env -iA nixpkgs.atuin nixpkgs.jump nixpkgs.dust nixpkgs.lazygit nixpkgs.skim 2>&1|pad
{{ else if eq .chezmoi.osRelease.id "almalinux" "rocky" -}}
PAM_SSH_PKG=""
if dnf list --quiet pam_ssh_agent_auth >/dev/null 2>&1; then
PAM_SSH_PKG="pam_ssh_agent_auth"
else
echo -e "${Yello}WARNING: pam_ssh_agent_auth not available in repos. $SUDO via SSH agent won't work.${NC}"
fi
$SUDO dnf install neovim fish $ACT_PACKAGES fd-find ${PAM_SSH_PKG:+$PAM_SSH_PKG} -y 2>&1|pad
new_line "via NIX"
nix-env -iA nixpkgs.fish nixpkgs.neovim nixpkgs.direnv nixpkgs.atuin nixpkgs.jump nixpkgs.dust nixpkgs.lazygit 2>&1|pad
{{ else if eq .chezmoi.osRelease.id "arch" "archarm" -}}
$SUDO pacman -Sy --noconfirm neovim fish atuin $ACT_PACKAGES fd skim lazygit 2>&1|pad
new_line "via NIX"
nix-env -iA nixpkgs.jump nixpkgs.dust 2>&1|pad
{{ else if eq .chezmoi.osRelease.id "debian" "ubuntu" -}}
$SUDO apt-get install fish -y 2>&1|pad
$SUDO apt-get install neovim -y 2>&1|pad
$SUDO apt-get install $ACT_PACKAGES fd-find libpam-ssh-agent-auth -y 2>&1|pad
new_line "via NIX"
nix-env -iA nixpkgs.neovim nixpkgs.lazygit nixpkgs.jump nixpkgs.skim nixpkgs.dust nixpkgs.atuin 2>&1|pad
{{ end -}}
new_line "Installing my authorized_keys"
mkdir ~/.ssh 2>/dev/null || true
cp ~/.ssh/authorized_keys ~/.ssh/authorized_keys.old 2>/dev/null || true
##print only unique lines
cat ~/.ssh/authorized_keys.old <(echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPdqIDeRKDOh7NQDcqnmLH/6M0ys7Wt/SEnF6ZYqroiH") 2>/dev/null | awk 'NF && !seen[$0]++' > ~/.ssh/authorized_keys
if [ "$SKIP_NVIM_BOOTSTRAP" -eq 0 ]; then
new_line "Waiting for neovim plugins and lsm compilations..."
nvim_run() {
local wait=${1:-5}; shift
TERM=dumb ${TIMEOUT_BIN:+$TIMEOUT_BIN 120s} nvim --headless \
-c "sleep 1" "$@" -c "sleep $wait" -c "qa" 2>&1 \
| sed -e "s/$(printf '\033')\[[0-9;]*[a-zA-Z]//g" -e "s/$(printf '\r')//g" \
| pad
}
nvim_run 10 -c ":Lazy! sync"
nvim_run 5 -c ":lua pcall(vim.cmd, 'MasonUpdate')"
nvim_run 5 -c ":lua pcall(vim.cmd, 'MasonInstall lua-language-server')"
nvim_run 5 -c ":lua pcall(vim.cmd, 'MasonInstall rust-analyzer')"
nvim_run 5 -c ":TSUpdate"
fi
{{ if eq .chezmoi.os "linux" -}}
if [ "$SKIP_TIMEZONE" -eq 0 ]; then
new_line "Setting timezone to Moscow"
$SUDO timedatectl set-timezone Europe/Moscow
fi
{{ end -}}
{{ if ne .chezmoi.os "openbsd" -}}
new_line "Installing shell-gpt"
pipx install shell-gpt 2>&1|pad
{{ end -}}
new_line "Install fisher plugin manager for fish"
## Install fisher plugin manager for fish, so that we may update in after plugin list sync by chezmoi
if ! fish -c "type -q fisher" &>/dev/null
then
fish -c "curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source && fisher update" 2>&1|pad
fi
new_line "Change default shell to fish for user $USER"
## Change default shell to fish
if [ "$(basename $SHELL)" != "fish" ]
then
$SUDO chsh -s $(command -v fish) $USER
fi
if [ "$NON_INTERACTIVE" -eq 0 ]; then
tput csr 0 $LINES
tput cup $LINES 0
fi
echo