This commit is contained in:
Brian Zalewski 2023-11-13 04:37:45 +00:00
parent c8ac018f64
commit d413ce15c7
5 changed files with 299 additions and 210 deletions

View file

@ -1,29 +1,41 @@
if ! command -v curl > /dev/null || ! command -v git > /dev/null || ! command -v expect > /dev/null || ! command -v rsync > /dev/null; then if ! command -v curl > /dev/null || ! command -v git > /dev/null || ! command -v expect > /dev/null || ! command -v rsync > /dev/null || ! command -v unbuffer; then
if command -v apt-get > /dev/null; then if command -v apt-get > /dev/null; then
# Debian / Ubuntu ### Debian / Ubuntu
sudo apt-get update sudo apt-get update
sudo apt-get install -y build-essential curl expect git rsync sudo apt-get install -y build-essential curl expect git rsync
elif command -v dnf > /dev/null; then elif command -v dnf > /dev/null; then
# Fedora ### Fedora
sudo dnf install -y curl expect git rsync sudo dnf install -y curl expect git rsync
elif command -v yum > /dev/null; then elif command -v yum > /dev/null; then
# CentOS ### CentOS
sudo yum install -y curl expect git rsync sudo yum install -y curl expect git rsync
elif command -v pacman > /dev/null; then elif command -v pacman > /dev/null; then
# Archlinux ### Archlinux
sudo pacman update sudo pacman update
sudo pacman -Syu base-devel curl expect git rsync procps-ng file sudo pacman -Syu base-devel curl expect git rsync procps-ng file
elif command -v zypper > /dev/null; then elif command -v zypper > /dev/null; then
# OpenSUSE ### OpenSUSE
sudo zypper install -y curl expect git rsync sudo zypper install -y curl expect git rsync
elif command -v apk > /dev/null; then elif command -v apk > /dev/null; then
# Alpine ### Alpine
apk add curl expect git rsync apk add curl expect git rsync
elif [ -d /Applications ] && [ -d /Library ]; then elif [ -d /Applications ] && [ -d /Library ]; then
# macOS ### macOS
sudo xcode-select -p >/dev/null 2>&1 || xcode-select --install sudo xcode-select -p >/dev/null 2>&1 || xcode-select --install
elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then
# Windows ### Windows
choco install -y curl expect git rsync choco install -y curl expect git rsync
elif command -v nix-env > /dev/null; then
### NixOS
echo "TODO - Add support for NixOS"
elif [[ "$OSTYPE" == 'freebsd'* ]]; then
### FreeBSD
echo "TODO - Add support for FreeBSD"
elif command -v pkg > /dev/null; then
### Termux
echo "TODO - Add support for Termux"
elif command -v xbps-install > /dev/null; then
### Void
echo "TODO - Add support for Void"
fi fi
fi fi

View file

@ -1,36 +1,45 @@
if ! command -v brew > /dev/null; then if ! command -v brew > /dev/null; then
if [ -d /home/linuxbrew/.linuxbrew/bin ]; then if [ -d /home/linuxbrew/.linuxbrew/bin ]; then
eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv) logg info "Sourcing from /home/linuxbrew/.linuxbrew/bin/brew" && eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
if ! command -v brew > /dev/null; then if ! command -v brew > /dev/null; then
echo "The /home/linuxbrew/.linuxbrew directory exists but something is not right. Try removing it and running the script again." && exit 1 logg error "The /home/linuxbrew/.linuxbrew directory exists but something is not right. Try removing it and running the script again." && exit 1
fi fi
else else
# @description Installs Homebrew and addresses a couple potential issues ### Installs Homebrew and addresses a couple potential issues
if command -v sudo > /dev/null && sudo -n true; then if command -v sudo > /dev/null && sudo -n true; then
logg info "Installing Homebrew"
echo | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" echo | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
else else
echo "Homebrew is not installed. The script will attempt to install Homebrew and you might be prompted for your password." logg info "Homebrew is not installed. The script will attempt to install Homebrew and you might be prompted for your password."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" || BREW_EXIT_CODE="$?" /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" || BREW_EXIT_CODE="$?"
if [ -n "$BREW_EXIT_CODE" ]; then if [ -n "$BREW_EXIT_CODE" ]; then
if command -v brew > /dev/null; then if command -v brew > /dev/null; then
echo "Homebrew was installed but part of the installation failed. Trying a few things to fix the installation.." logg warn "Homebrew was installed but part of the installation failed. Trying a few things to fix the installation.."
BREW_DIRS="share/man share/doc share/zsh/site-functions etc/bash_completion.d" BREW_DIRS="share/man share/doc share/zsh/site-functions etc/bash_completion.d"
for BREW_DIR in $BREW_DIRS; do for BREW_DIR in $BREW_DIRS; do
if [ -d "$(brew --prefix)/$BREW_DIR" ]; then if [ -d "$(brew --prefix)/$BREW_DIR" ]; then
sudo chown -R "$(whoami)" "$(brew --prefix)/$BREW_DIR" logg info "Chowning $(brew --prefix)/$BREW_DIR" && sudo chown -R "$(whoami)" "$(brew --prefix)/$BREW_DIR"
fi fi
done done
brew update --force --quiet logg info "Running brew update --force --quiet" && brew update --force --quiet && logg success "Successfully ran brew update --force --quiet"
fi fi
fi fi
fi fi
# @description Ensures the `brew` binary is available on Linux machines. macOS installs `brew` into the default `PATH` ### Ensures the `brew` binary is available on Linux machines. macOS installs `brew` into the default `PATH`
# so nothing needs to be done for macOS. # so nothing needs to be done for macOS.
if [ -d /home/linuxbrew/.linuxbrew/bin ]; then if [ -d /home/linuxbrew/.linuxbrew/bin ]; then
eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv) logg info "Sourcing shellenv from /home/linuxbrew/.linuxbrew/bin/brew" && eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
elif [ -f /opt/homebrew/bin/brew ]; then elif [ -f /opt/homebrew/bin/brew ]; then
eval "$(/opt/homebrew/bin/brew shellenv)" logg info "Sourcing shellenv from /opt/homebrew/bin/brew" && eval "$(/opt/homebrew/bin/brew shellenv)"
fi fi
fi fi
fi fi
if command -v brew > /dev/null; then
if ! brew list | grep gcc > /dev/null; then
logg info "Installing Homebrew gcc" && brew install gcc
fi
else
logg error "Failed to initialize Homebrew" && exit 2
fi

View file

@ -12,7 +12,7 @@ logg() {
fi fi
elif [ "$TYPE" == 'info' ]; then elif [ "$TYPE" == 'info' ]; then
if command -v gum > /dev/null; then if command -v gum > /dev/null; then
gum style " $(gum style --foreground="#00ffff" "○") $(gum style --faint --foreground="#ffffff" "$MSG")" gum style " $(gum style --foreground="#00ffff" "○") $(gum style --faint "$MSG")"
else else
echo "INFO: $MSG" echo "INFO: $MSG"
fi fi

View file

@ -67,28 +67,32 @@ if [ -n "$CI" ]; then
export WORK_ENVIRONMENT=false export WORK_ENVIRONMENT=false
fi fi
# @description Disconnect from WARP, if connected
if command -v warp-cli > /dev/null; then
if warp-cli status | grep 'Connected' > /dev/null; then
warp-cli disconnect && echo "Disconnected WARP to prevent conflicts"
fi
fi
# @description Detect `START_REPO` format and determine appropriate git address, otherwise use the master Install Doctor branch # @description Detect `START_REPO` format and determine appropriate git address, otherwise use the master Install Doctor branch
if [ -z "$START_REPO" ]; then setStartRepo() {
START_REPO="https://github.com/megabyte-labs/install.doctor.git" if [ -z "$START_REPO" ]; then
else START_REPO="https://github.com/megabyte-labs/install.doctor.git"
if [[ "$START_REPO" == *"/"* ]]; then
# Either full git address or GitHubUser/RepoName
if [[ "$START_REPO" == *":"* ]] || [[ "$START_REPO" == *"//"* ]]; then
START_REPO="$START_REPO"
else
START_REPO="https://github.com/${START_REPO}.git"
fi
else else
START_REPO="https://github.com/$START_REPO/install.doctor.git" if [[ "$START_REPO" == *"/"* ]]; then
# Either full git address or GitHubUser/RepoName
if [[ "$START_REPO" == *":"* ]] || [[ "$START_REPO" == *"//"* ]]; then
START_REPO="$START_REPO"
else
START_REPO="https://github.com/${START_REPO}.git"
fi
else
START_REPO="https://github.com/$START_REPO/install.doctor.git"
fi
fi fi
fi }
# @description Disconnect from WARP, if connected
warpDisconnect() {
if command -v warp-cli > /dev/null; then
if warp-cli status | grep 'Connected' > /dev/null; then
warp-cli disconnect && echo "Disconnected WARP to prevent conflicts"
fi
fi
}
# @description Logs with style using Gum if it is installed, otherwise it uses `echo`. It also leverages Glow to render markdown. # @description Logs with style using Gum if it is installed, otherwise it uses `echo`. It also leverages Glow to render markdown.
# When Glow is not installed, it uses `cat`. # When Glow is not installed, it uses `cat`.

View file

@ -51,72 +51,88 @@
# [Install Doctor homepage](https://install.doctor) # [Install Doctor homepage](https://install.doctor)
# [Install Doctor documentation portal](https://install.doctor/docs) (includes tips, tricks, and guides on how to customize the system to your liking) # [Install Doctor documentation portal](https://install.doctor/docs) (includes tips, tricks, and guides on how to customize the system to your liking)
# @description Ensure Ubuntu / Debian run in `noninteractive` mode
export DEBIAN_FRONTEND=noninteractive
# @description Load default settings if it is in a CI setting
if [ -n "$CI" ]; then
export HOST="$HOST"
export NO_RESTART=true
export HEADLESS_INSTALL=true
export SOFTWARE_GROUP="Full"
export FULL_NAME="Brian Zalewski"
export PRIMARY_EMAIL="help@megabyte.space"
export PUBLIC_SERVICES_DOMAIN="lab.megabyte.space"
export RESTRICTED_ENVIRONMENT=false
export WORK_ENVIRONMENT=false
fi
# @description Disconnect from WARP, if connected
if command -v warp-cli > /dev/null; then
if warp-cli status | grep 'Connected' > /dev/null; then
warp-cli disconnect && echo "Disconnected WARP to prevent conflicts"
fi
fi
# @description Detect `START_REPO` format and determine appropriate git address, otherwise use the master Install Doctor branch
if [ -z "$START_REPO" ] && [ -z "$REPO" ]; then
START_REPO="https://github.com/megabyte-labs/install.doctor.git"
else
if [ -n "$REPO" ] && [ -z "$START_REPO" ]; then
START_REPO="$REPO"
fi
if [[ "$START_REPO" == *"/"* ]]; then
# Either full git address or GitHubUser/RepoName
if [[ "$START_REPO" == *":"* ]] || [[ "$START_REPO" == *"//"* ]]; then
START_REPO="$START_REPO"
else
START_REPO="https://github.com/${START_REPO}.git"
fi
else
START_REPO="https://github.com/$START_REPO/install.doctor.git"
fi
fi
{{ include "partials" "logg" }} {{ include "partials" "logg" }}
# @description Notify user that they can press CTRL+C to prevent /etc/sudoers from being modified (which is currently required for headless installs on some systems) # @section Environment variables and system dependencies
sudo -n true || SUDO_EXIT_CODE=$? # @description Ensure Ubuntu / Debian run in `noninteractive` mode. Detect `START_REPO` format and determine appropriate git address,
logg info 'Your user will temporarily be granted passwordless sudo for the duration of the script' # otherwise use the master Install Doctor branch
if [ -n "$SUDO_EXIT_CODE" ]; then setEnvironmentVariables() {
logg info 'Press CTRL+C to bypass this prompt to either enter your password when needed or perform a non-privileged installation' export DEBIAN_FRONTEND=noninteractive
logg info 'Note: Non-privileged installations are not yet supported' if [ -z "$START_REPO" ] && [ -z "$REPO" ]; then
fi export START_REPO="https://github.com/megabyte-labs/install.doctor.git"
# @description Add current user to /etc/sudoers so that headless automation is possible
if ! sudo cat /etc/sudoers | grep '# TEMPORARY FOR INSTALL DOCTOR' > /dev/null; then
if [ -n "$SUDO_PASSWORD" ]; then
printf '%s\n' "$SUDO_PASSWORD" | sudo -p "" -S echo "$(whoami) ALL=(ALL:ALL) NOPASSWD: ALL # TEMPORARY FOR INSTALL DOCTOR" | sudo tee -a /etc/sudoers
else else
echo "$(whoami) ALL=(ALL:ALL) NOPASSWD: ALL # TEMPORARY FOR INSTALL DOCTOR" | sudo tee -a /etc/sudoers if [ -n "$REPO" ] && [ -z "$START_REPO" ]; then
export START_REPO="$REPO"
fi
if [[ "$START_REPO" == *"/"* ]]; then
# Either full git address or GitHubUser/RepoName
if [[ "$START_REPO" == *":"* ]] || [[ "$START_REPO" == *"//"* ]]; then
export START_REPO="$START_REPO"
else
export START_REPO="https://github.com/${START_REPO}.git"
fi
else
export START_REPO="https://github.com/$START_REPO/install.doctor.git"
fi
fi fi
fi }
# @section Qubes dom0 Bootstrap # @description Ensure dependencies like `git` and `curl` are installed (among a few other lightweight system packages)
# @description Perform Qubes dom0 specific logic like updating system packages, setting up the Tor VM, updating TemplateVMs, and ensureBasicDeps() {
# beginning the provisioning process using Ansible and an AppVM used to handle the provisioning process {{ include "partials" "basic-deps" }}
if command -v qubesctl > /dev/null; then }
# @description Ensure sys-whonix is configured (for Qubes dom0)
# @description Ensure Homebrew is installed and available in the `PATH`
ensureHomebrew() {
{{ include "partials" "homebrew" }}
}
# @description Load default settings if it is in a CI setting
setCIEnvironmentVariables() {
if [ -n "$CI" ]; then
logg info "Automatically setting environment variables since the CI environment variable is defined"
logg info "Setting HOST to $HOST" && export HOST="$HOST"
logg info "Setting NO_RESTART to true" && export NO_RESTART=true
logg info "Setting HEADLESS_INSTALL to true " && export HEADLESS_INSTALL=true
logg info "Setting SOFTWARE_GROUP to Full-Desktop" && export SOFTWARE_GROUP="Full-Desktop"
logg info "Setting FULL_NAME to Brian Zalewski" && export FULL_NAME="Brian Zalewski"
logg info "Setting PRIMARY_EMAIL to help@megabyte.space" && export PRIMARY_EMAIL="help@megabyte.space"
logg info "Setting PUBLIC_SERVICES_DOMAIN to lab.megabyte.space" && export PUBLIC_SERVICES_DOMAIN="lab.megabyte.space"
logg info "Setting RESTRICTED_ENVIRONMENT to false" && export RESTRICTED_ENVIRONMENT=false
logg info "Setting WORK_ENVIRONMENT to false" && export WORK_ENVIRONMENT=false
fi
}
# @description Disconnect from WARP, if connected
ensureWarpDisconnected() {
if command -v warp-cli > /dev/null; then
if warp-cli status | grep 'Connected' > /dev/null; then
logg info "Disconnecting from WARP" && warp-cli disconnect && logg success "Disconnected WARP to prevent conflicts"
fi
fi
}
# @description Notify user that they can press CTRL+C to prevent `/etc/sudoers` from being modified (which is currently required for headless installs on some systems).
# Additionally, this function will add the current user to `/etc/sudoers` so that headless automation is possible.
setupPasswordlessSudo() {
sudo -n true || SUDO_EXIT_CODE=$?
logg info 'Your user will temporarily be granted passwordless sudo for the duration of the script'
if [ -n "$SUDO_EXIT_CODE" ]; then
logg info 'Press CTRL+C to bypass this prompt to either enter your password when needed or perform a non-privileged installation'
logg info 'Note: Non-privileged installations are not yet supported'
fi
if ! sudo cat /etc/sudoers | grep '# TEMPORARY FOR INSTALL DOCTOR' > /dev/null; then
if [ -n "$SUDO_PASSWORD" ]; then
printf '%s\n' "$SUDO_PASSWORD" | sudo -p "" -S echo "$(whoami) ALL=(ALL:ALL) NOPASSWD: ALL # TEMPORARY FOR INSTALL DOCTOR" | sudo tee -a /etc/sudoers
else
echo "$(whoami) ALL=(ALL:ALL) NOPASSWD: ALL # TEMPORARY FOR INSTALL DOCTOR" | sudo tee -a /etc/sudoers
fi
fi
}
# @section Qubes dom0
# @description Ensure sys-whonix is configured (for Qubes dom0)
ensureSysWhonix() {
CONFIG_WIZARD_COUNT=0 CONFIG_WIZARD_COUNT=0
function configureWizard() { function configureWizard() {
if xwininfo -root -tree | grep "Anon Connection Wizard"; then if xwininfo -root -tree | grep "Anon Connection Wizard"; then
@ -143,21 +159,27 @@ if command -v qubesctl > /dev/null; then
fi fi
fi fi
} }
}
# @description Ensure dom0 is updated # @description Ensure dom0 is updated
ensureDom0Updated() {
if [ ! -f /root/dom0-updated ]; then if [ ! -f /root/dom0-updated ]; then
sudo qubesctl --show-output state.sls update.qubes-dom0 sudo qubesctl --show-output state.sls update.qubes-dom0
sudo qubes-dom0-update --clean -y sudo qubes-dom0-update --clean -y
touch /root/dom0-updated touch /root/dom0-updated
fi fi
}
# @description Ensure sys-whonix is running # @description Ensure sys-whonix is running
ensureSysWhonixRunning() {
if ! qvm-check --running sys-whonix; then if ! qvm-check --running sys-whonix; then
qvm-start sys-whonix --skip-if-running qvm-start sys-whonix --skip-if-running
configureWizard > /dev/null configureWizard > /dev/null
fi fi
}
# @description Ensure TemplateVMs are updated # @description Ensure TemplateVMs are updated
ensureTemplateVMsUpdated() {
if [ ! -f /root/templatevms-updated ]; then if [ ! -f /root/templatevms-updated ]; then
# timeout of 10 minutes is added here because the whonix-gw VM does not like to get updated # timeout of 10 minutes is added here because the whonix-gw VM does not like to get updated
# with this method. Anyone know how to fix this? # with this method. Anyone know how to fix this?
@ -167,17 +189,20 @@ if command -v qubesctl > /dev/null; then
done< <(qvm-ls --all --no-spinner --fields=name,state | grep Running | grep -v sys-net | grep -v sys-firewall | grep -v sys-whonix | grep -v dom0 | awk '{print $1}') done< <(qvm-ls --all --no-spinner --fields=name,state | grep Running | grep -v sys-net | grep -v sys-firewall | grep -v sys-whonix | grep -v dom0 | awk '{print $1}')
sudo touch /root/templatevms-updated sudo touch /root/templatevms-updated
fi fi
}
# @description Ensure provisioning VM can run commands on any VM # @description Ensure provisioning VM can run commands on any VM
ensureProvisioningVMPermissions() {
echo "/bin/bash" | sudo tee /etc/qubes-rpc/qubes.VMShell echo "/bin/bash" | sudo tee /etc/qubes-rpc/qubes.VMShell
sudo chmod 755 /etc/qubes-rpc/qubes.VMShell sudo chmod 755 /etc/qubes-rpc/qubes.VMShell
echo "${ANSIBLE_PROVISION_VM:=provision}"' dom0 allow' | sudo tee /etc/qubes-rpc/policy/qubes.VMShell echo "${ANSIBLE_PROVISION_VM:=provision}"' dom0 allow' | sudo tee /etc/qubes-rpc/policy/qubes.VMShell
echo "$ANSIBLE_PROVISION_VM"' $anyvm allow' | sudo tee -a /etc/qubes-rpc/policy/qubes.VMShell echo "$ANSIBLE_PROVISION_VM"' $anyvm allow' | sudo tee -a /etc/qubes-rpc/policy/qubes.VMShell
sudo chown "$(whoami):$(whoami)" /etc/qubes-rpc/policy/qubes.VMShell sudo chown "$(whoami):$(whoami)" /etc/qubes-rpc/policy/qubes.VMShell
sudo chmod 644 /etc/qubes-rpc/policy/qubes.VMShell sudo chmod 644 /etc/qubes-rpc/policy/qubes.VMShell
}
# @description Create provisioning VM and initialize the provisioning process from there
# @description Create provisioning VM and initialize the provisioning process from there createAndInitProvisionVM() {
qvm-create --label red --template debian-11 "$ANSIBLE_PROVISION_VM" &> /dev/null || true qvm-create --label red --template debian-11 "$ANSIBLE_PROVISION_VM" &> /dev/null || true
qvm-volume extend "$ANSIBLE_PROVISION_VM:private" "40G" qvm-volume extend "$ANSIBLE_PROVISION_VM:private" "40G"
if [ -f ~/.vaultpass ]; then if [ -f ~/.vaultpass ]; then
@ -185,68 +210,87 @@ if command -v qubesctl > /dev/null; then
qvm-copy-to-vm "$ANSIBLE_PROVISION_VM" ~/.vaultpass qvm-copy-to-vm "$ANSIBLE_PROVISION_VM" ~/.vaultpass
qvm-run "$ANSIBLE_PROVISION_VM" 'cp ~/QubesIncoming/dom0/.vaultpass ~/.vaultpass' qvm-run "$ANSIBLE_PROVISION_VM" 'cp ~/QubesIncoming/dom0/.vaultpass ~/.vaultpass'
fi fi
}
# @description Restart the provisioning process with the same script but from the provisioning VM # @description Restart the provisioning process with the same script but from the provisioning VM
runStartScriptInProvisionVM() {
qvm-run --pass-io "$ANSIBLE_PROVISION_VM" 'curl -sSL https://install.doctor/start > ~/start.sh && bash ~/start.sh' qvm-run --pass-io "$ANSIBLE_PROVISION_VM" 'curl -sSL https://install.doctor/start > ~/start.sh && bash ~/start.sh'
exit 0 }
fi
# @description Ensure basic system packages are available on the device # @description Perform Qubes dom0 specific logic like updating system packages, setting up the Tor VM, updating TemplateVMs, and
{{ include "partials" "basic-deps" }} # beginning the provisioning process using Ansible and an AppVM used to handle the provisioning process
handleQubesDom0() {
if command -v qubesctl > /dev/null; then
ensureSysWhonix
ensureDom0Updated
ensureSysWhonixRunning
ensureTemplateVMsUpdated
ensureProvisioningVMPermissions
createAndInitProvisionVM
runStartScriptInProvisionVM
exit 0
fi
}
# @description Ensure Homebrew is installed and available # @section Homebrew dependencies
{{ include "partials" "homebrew" }} # @description Helper function used by [[ensureHomebrewDeps]] to ensure a Homebrew package is installed after
# first checking if it is already available on the system.
installBrewPackage() {
if ! command -v "$1" > /dev/null; then
logg 'Installing '"$1"''
brew install "$1"
fi
}
# @description Ensure Chezmoi is installed # @description Installs various dependencies using Homebrew.
if ! command -v chezmoi > /dev/null; then #
brew install chezmoi # 1. Ensures Chezmoi, Node.js, and ZX are installed.
fi # 2. Installs Glow and Gum if the `HEADLESS_INSTALL` environment variable is not set.
# 3. If the system is macOS, then also install `gsed`.
ensureHomebrewDeps() {
### TUI experience
if [ -z "$HEADLESS_INSTALL" ]; then
installBrewPackage "gum"
installBrewPackage "glow"
fi
# @description Ensure Node.js is installed ### Base dependencies
if ! command -v node > /dev/null; then installBrewPackage "chezmoi"
brew install node installBrewPackage "node"
fi installBrewPackage "zx"
# @description Ensure ZX is installed ### macOS
if ! command -v zx > /dev/null; then if [ -d /Applications ] && [ -d /System ]; then
brew install zx installBrewPackage "gsed"
fi fi
}
# @description Install Glow / Gum if the `HEADLESS_INSTALL` variable is not set to true # @section Chezmoi
if [ "$HEADLESS_INSTALL" != 'true' ]; then # @description Ensure the `${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi` directory is cloned and up-to-date using the previously
# @description Ensure Gum is installed # set `START_REPO` as the source repository.
if ! command -v gum > /dev/null; then cloneChezmoiSourceRepo() {
brew install gum logg info 'Setting git http.postBuffer value high for large source repository' && git config --global http.postBuffer 524288000
fi if [ -d "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/.git" ]; then
logg info "Changing directory to ${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi" && cd "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi"
logg info "Pulling the latest changes in ${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi" && git pull origin master
else
logg info "Ensuring ${XDG_DATA_HOME:-$HOME/.local/share} is a folder" && mkdir -p "${XDG_DATA_HOME:-$HOME/.local/share}"
logg info "Cloning ${START_REPO} to ${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi" && git clone "${START_REPO}" "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi"
fi
}
# @description Ensure Glow is installed # @description Guide the user through the initial setup by showing TUI introduction and accepting input through various prompts.
if ! command -v glow > /dev/null; then #
brew install glow # 1. Show `chezmoi-intro.md` with `glow`
fi # 2. Prompt for the software group if the `SOFTWARE_GROUP` variable is not defined
fi # 3. Run `chezmoi init` when the Chezmoi configuration is missing (i.e. `${XDG_CONFIG_HOME:-$HOME/.config}/chezmoi/chezmoi.yaml`)
initChezmoiAndPrompt() {
# @description Ensure the ${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi directory is cloned and up-to-date ### Show `chezmoi-intro.md` with `glow`
logg info 'Setting git http.postBuffer value high for large source repository'
git config --global http.postBuffer 524288000
if [ -d "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/.git" ]; then
cd "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi"
logg info "Pulling the latest changes from ${START_REPO:-https://github.com/megabyte-labs/install.doctor.git}"
git pull origin master
else
logg info "Ensuring ${XDG_DATA_HOME:-$HOME/.local/share} is a folder"
mkdir -p "${XDG_DATA_HOME:-$HOME/.local/share}"
logg info "Cloning ${START_REPO} to ${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi"
git clone ${START_REPO} "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi"
fi
# @description If the `${XDG_CONFIG_HOME:-$HOME/.config}/chezmoi/chezmoi.yaml` file is missing, then guide the user through the initial setup
if [ ! -f "${XDG_CONFIG_HOME:-$HOME/.config}/chezmoi/chezmoi.yaml" ]; then
# @description Show introduction message if Glow is installed
if command -v glow > /dev/null; then if command -v glow > /dev/null; then
glow "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/docs/terminal/chezmoi-intro.md" glow "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/docs/terminal/chezmoi-intro.md"
fi fi
# @description Prompt for the software group if the `SOFTWARE_GROUP` variable is not defined ### Prompt for the software group if the `SOFTWARE_GROUP` variable is not defined
if command -v gum > /dev/null; then if command -v gum > /dev/null; then
if [ -z "$SOFTWARE_GROUP" ]; then if [ -z "$SOFTWARE_GROUP" ]; then
logg prompt 'Select the software group you would like to install. If your environment is a macOS, Windows, or environment with the DISPLAY environment variable then desktop software will be installed too. The software groups are in the '"${XDG_CONFIG_HOME:-$HOME/.config}/chezmoi/chezmoi.yaml"' file.' logg prompt 'Select the software group you would like to install. If your environment is a macOS, Windows, or environment with the DISPLAY environment variable then desktop software will be installed too. The software groups are in the '"${XDG_CONFIG_HOME:-$HOME/.config}/chezmoi/chezmoi.yaml"' file.'
@ -257,74 +301,94 @@ if [ ! -f "${XDG_CONFIG_HOME:-$HOME/.config}/chezmoi/chezmoi.yaml" ]; then
logg error 'Woops! Gum needs to be installed for the guided installation. Try running brew install gum' && exit 1 logg error 'Woops! Gum needs to be installed for the guided installation. Try running brew install gum' && exit 1
fi fi
# @description Run `chezmoi init` when the Chezmoi configuration is missing if [ ! -f "${XDG_CONFIG_HOME:-$HOME/.config}/chezmoi/chezmoi.yaml" ]; then
logg info 'Running chezmoi init since the '"${XDG_CONFIG_HOME:-$HOME/.config}/chezmoi/chezmoi.yaml"' is not present' ### Run `chezmoi init` when the Chezmoi configuration is missing
chezmoi init logg info 'Running chezmoi init since the '"${XDG_CONFIG_HOME:-$HOME/.config}/chezmoi/chezmoi.yaml"' is not present'
fi chezmoi init
fi
}
# @description Run `chezmoi apply` and enable verbose mode if the `DEBUG_MODE` or `DEBUG` environment variable is set to true # @description Run `chezmoi apply` and enable verbose mode if the `DEBUG_MODE` or `DEBUG` environment variable is set to true
if [ "$DEBUG_MODE" = 'true' ]; then configureDebugMode() {
DEBUG_MODIFIER="-vvvvv" if [ -n "$DEBUG_MODE" ] || [ -n "$DEBUG" ]; then
elif [ "$DEBUG" = 'true' ]; then logg info "Either DEBUG_MODE or DEBUG environment variables were set so Chezmoi will be run in debug mode"
DEBUG_MODIFIER="-vvvvv" export DEBUG_MODIFIER="-vvvvv"
fi fi
}
# @description Save the log of the provision process to `$HOME/.local/var/log/install.doctor/install.doctor.$(date +%s).log` and add the Chezmoi # @description Save the log of the provision process to `$HOME/.local/var/log/install.doctor/install.doctor.$(date +%s).log` and add the Chezmoi
# `--force` flag if the `HEADLESS_INSTALL` variable is set to true. # `--force` flag if the `HEADLESS_INSTALL` variable is set to `true``.
mkdir -p "$HOME/.local/var/log/install.doctor" runChezmoi() {
LOG_FILE="$HOME/.local/var/log/install.doctor/install.doctor.$(date +%s).log" mkdir -p "$HOME/.local/var/log/install.doctor"
if [ "$HEADLESS_INSTALL" = 'true' ]; then LOG_FILE="$HOME/.local/var/log/install.doctor/install.doctor.$(date +%s).log"
logg info 'Running chezmoi apply forcefully' if [ "$HEADLESS_INSTALL" = 'true' ]; then
if command -v unbuffer > /dev/null; then logg info 'Running chezmoi apply forcefully'
if command -v caffeinate > /dev/null; then if command -v unbuffer > /dev/null; then
caffeinate unbuffer -p chezmoi apply $DEBUG_MODIFIER -k --force 2>&1 | tee "$LOG_FILE" if command -v caffeinate > /dev/null; then
caffeinate unbuffer -p chezmoi apply $DEBUG_MODIFIER -k --force 2>&1 | tee "$LOG_FILE"
else
unbuffer -p chezmoi apply $DEBUG_MODIFIER -k --force 2>&1 | tee "$LOG_FILE"
fi
else else
unbuffer -p chezmoi apply $DEBUG_MODIFIER -k --force 2>&1 | tee "$LOG_FILE" if command -v caffeinate > /dev/null; then
caffeinate chezmoi apply $DEBUG_MODIFIER -k --force 2>&1 | tee "$LOG_FILE"
else
chezmoi apply $DEBUG_MODIFIER -k --force 2>&1 | tee "$LOG_FILE"
fi
fi fi
else else
if command -v caffeinate > /dev/null; then logg info 'Running chezmoi apply'
caffeinate chezmoi apply $DEBUG_MODIFIER -k --force 2>&1 | tee "$LOG_FILE" if command -v unbuffer > /dev/null; then
if command -v caffeinate > /dev/null; then
caffeinate unbuffer -p chezmoi apply $DEBUG_MODIFIER -k 2>&1 | tee "$LOG_FILE"
else
unbuffer -p chezmoi apply $DEBUG_MODIFIER -k 2>&1 | tee "$LOG_FILE"
fi
else else
chezmoi apply $DEBUG_MODIFIER -k --force 2>&1 | tee "$LOG_FILE" if command -v caffeinate > /dev/null; then
caffeinate chezmoi apply $DEBUG_MODIFIER -k 2>&1 | tee "$LOG_FILE"
else
chezmoi apply $DEBUG_MODIFIER -k 2>&1 | tee "$LOG_FILE"
fi
fi fi
fi fi
else }
logg info 'Running chezmoi apply'
if command -v unbuffer > /dev/null; then
if command -v caffeinate > /dev/null; then
caffeinate unbuffer -p chezmoi apply $DEBUG_MODIFIER -k 2>&1 | tee "$LOG_FILE"
else
unbuffer -p chezmoi apply $DEBUG_MODIFIER -k 2>&1 | tee "$LOG_FILE"
fi
else
if command -v caffeinate > /dev/null; then
caffeinate chezmoi apply $DEBUG_MODIFIER -k 2>&1 | tee "$LOG_FILE"
else
chezmoi apply $DEBUG_MODIFIER -k 2>&1 | tee "$LOG_FILE"
fi
fi
fi
# @description Ensure gsed is available on macOS (for modifying `/etc/sudoers` to remove passwordless sudo)
if [ -d /Applications ] && [ -d /System ]; then
if ! command -v gsed > /dev/null; then
if command -v brew > /dev/null; then
brew install gsed
else
logg warn 'Homebrew is not available and passwordless sudo might still be enabled in /etc/sudoers. Modify the file manually if you wish to disable passwordless sudo.'
fi
fi
fi
# @section Post-provision logic
# @description Ensure temporary passwordless sudo privileges are removed from `/etc/sudoers` # @description Ensure temporary passwordless sudo privileges are removed from `/etc/sudoers`
if command -v gsed > /dev/null; then removePasswordlessSudo() {
if command -v gsed > /dev/null; then
sudo gsed -i '/# TEMPORARY FOR INSTALL DOCTOR/d' /etc/sudoers || logg warn 'Failed to remove passwordless sudo from the /etc/sudoers file' sudo gsed -i '/# TEMPORARY FOR INSTALL DOCTOR/d' /etc/sudoers || logg warn 'Failed to remove passwordless sudo from the /etc/sudoers file'
else else
sudo sed -i '/# TEMPORARY FOR INSTALL DOCTOR/d' /etc/sudoers || logg warn 'Failed to remove passwordless sudo from the /etc/sudoers file' sudo sed -i '/# TEMPORARY FOR INSTALL DOCTOR/d' /etc/sudoers || logg warn 'Failed to remove passwordless sudo from the /etc/sudoers file'
fi fi
}
# @description Render the `docs/terminal/post-install.md` file to the terminal at the end of the provisioning process # @description Render the `docs/terminal/post-install.md` file to the terminal at the end of the provisioning process
logg success 'Provisioning complete!' postProvision() {
if command -v glow > /dev/null && [ -f "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/docs/terminal/post-install.md" ]; then logg success 'Provisioning complete!'
glow "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/docs/terminal/post-install.md" if command -v glow > /dev/null && [ -f "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/docs/terminal/post-install.md" ]; then
fi glow "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/docs/terminal/post-install.md"
fi
}
# @section Execution order
# @description The `provisionLogic` function is used to define the order of the script. All of the functions it relies on are defined
# above.
provisionLogic() {
logg info "Setting environment variables" && setEnvironmentVariables
logg info "Handling CI variables" && setCIEnvironmentVariables
logg info "Ensuring WARP is disconnected" && ensureWarpDisconnected
logg info "Ensuring system Homebrew dependencies are installed" && ensureBasicDeps
logg info "Ensuring Homebrew is available" && ensureHomebrew
logg info "Applying passwordless sudo" && setupPasswordlessSudo
logg info "Handling Qubes dom0 logic (if applicable)" && handleQubesDom0
logg info "Cloning / updating source repository" && cloneChezmoiSourceRepo
logg info "Handling pre-provision logic" && initChezmoiAndPrompt
logg info "Handling debug mode if DEBUG or DEBUG_MODE are defined" && configureDebugMode
logg info "Running the Chezmoi provisioning" && runChezmoi
logg info "Ensuring temporary passwordless sudo is removed" && removePasswordlessSudo
logg info "Handling post-provision logic" && postProvision
}
provisionLogic