2023-12-23 02:18:29 -08:00
#!/usr/bin/env bash
# @description Copies the `~/.config/shell/exports.sh` file to `/etc/zshenv` so that non-interactive ZSH sessions have all of the same
# environment PATH variables as interactive sessions. This was initially required to make Cakebrew work on macOS.
addZshEnv() {
### Ensure /etc/zshenv is populated
# No equivalent type of file for Bash
logg info "Copying ${ XDG_CONFIG_HOME : - $ HOME / . config } /shell/exports.sh to /etc/zshenv" && sudo cp -f " ${ XDG_CONFIG_HOME : - $ HOME / . config } /shell/exports.sh" /etc/zshenv
}
# @description Ensures fonts are available at the system level and, on Linux, it configures the system font settings.
applyFontsToSystem() {
### Sync user fonts with system fonts
if [ -d /Applications ] && [ -d /System ]; then
### macOS
logg info 'Copying fonts from ~/Library/Fonts and ~/.local/share/fonts to /Library/Fonts to make them available globally'
FONT_DIR='/Library/Fonts'
sudo rsync -av " $ HOME /Library/Fonts" " $ FONT_DIR "
sudo rsync -av " ${ XDG_DATA_HOME : - $ HOME / . local / share } /fonts" " $ FONT_DIR "
else
### Linux
logg info 'Copying fonts from ~/.local/share/fonts to /usr/local/share/fonts to make them available globally'
FONT_DIR='/usr/local/share/fonts'
sudo rsync -av " ${ XDG_DATA_HOME : - $ HOME / . local / share } /fonts" " $ FONT_DIR "
fi
### Configure system font properties
if [ -d /etc/fonts ]; then
logg info 'Copying ~/.config/fontconfig/fonts.conf to /etc/fonts/local.conf'
sudo cp -f " ${ XDG_CONFIG_HOME : - $ HOME / . config } /fontconfig/fonts.conf" /etc/fonts/local.conf
else
logg warn 'The /etc/fonts directory is missing'
fi
}
# @description Applies various `dconf`, `xconf`, etc. settings to Linux systems
applyLinuxConfSettings() {
if [ "{{ .host.distro.family }}" = "linux" ] && command -v apply-linux-conf-settings > /dev/null; then
apply-linux-conf-settings
fi
}
# @description
# This script applies various themes to Linux systems that use GNOME or KDE.
applyLinuxThemeFiles() {
if [ "{{ .host.distro.family }}" = "linux" ]; then
### Ensure /usr/local/bin/squash-symlink is present
if [ ! -f /usr/local/bin/squash-symlink ] && [ -f " $ HOME /.local/bin/squash-symlink" ]; then
logg info 'Copying ~/.local/bin/squash-symlink to /usr/local/bin/squash-symlink'
sudo cp -f " $ HOME /.local/bin/squash-symlink" /usr/local/bin/squash-symlink
sudo chmod +x /usr/local/bin/squash-symlink
fi
### Clean up system theme settings
for ITEM_TO_BE_REMOVED in "/usr/share/backgrounds/images" "/usr/share/backgrounds/f32" "/usr/share/backgrounds/qubes" "/usr/share/wallpapers"; do
if [ -d " $ ITEM_TO_BE_REMOVED " ] || [ -f " $ ITEM_TO_BE_REMOVED " ]; then
sudo rm -rf " $ ITEM_TO_BE_REMOVED "
logg success "Removed $ ITEM_TO_BE_REMOVED "
fi
done
### Ensure /usr/local/share exists
if [ ! -d /usr/local/share ]; then
sudo mkdir -p /usr/local/share
logg success 'Created /usr/local/share'
fi
### Copy theme files over to /usr/local/share
if [ -d " $ HOME /.local/src/{{ .theme | lower }}/share" ]; then
logg info 'Copying ~/.local/src/{{ .theme | lower }}/share to /usr/local/share'
sudo rsync --chown=root:root --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r -artvu --inplace " ${ XDG_DATA_HOME : - $ HOME / . local / share } /betelgeuse/share/" "/usr/local/share/" > /dev/null
else
logg warn '~/.local/share/betelgeuse/share is missing'
fi
### Flatten GRUB theme files (i.e. convert symlinks to regular files)
if command -v squash-symlink > /dev/null; then
logg info 'Converting /usr/local/share/grub symlinks to equivalent regular files'
sudo find /usr/local/share/grub -type l -exec squash-symlink {} +
else
logg warn 'squash-symlink is not a script in the PATH'
fi
### Ensure /usr/share/backgrounds/default.png is deleted
if [ -f /usr/share/backgrounds/default.png ]; then
sudo rm -f /usr/share/backgrounds/default.png
fi
### Add the default image symlink based on the OS
if [ '{{ .host.distro.id }}' == 'archlinux' ]; then
sudo ln -s /usr/local/share/wallpapers/Betelgeuse-Archlinux/contents/source.png /usr/share/backgrounds/default.png
elif [ '{{ .host.distro.id }}' == 'centos' ]; then
sudo ln -s /usr/local/share/wallpapers/Betelgeuse-CentOS/contents/source.png /usr/share/backgrounds/default.png
elif [ '{{ .host.distro.id }}' == 'darwin' ]; then
sudo ln -s /usr/local/share/wallpapers/Betelgeuse-macOS/contents/source.png /usr/share/backgrounds/default.png
elif [ '{{ .host.distro.id }}' == 'debian' ]; then
sudo ln -s /usr/local/share/wallpapers/Betelgeuse-Debian/contents/source.png /usr/share/backgrounds/default.png
elif [ '{{ .host.distro.id }}' == 'fedora' ]; then
sudo ln -s /usr/local/share/wallpapers/Betelgeuse-Fedora/contents/source.png /usr/share/backgrounds/default.png
elif [ '{{ .host.distro.id }}' == 'ubuntu' ]; then
sudo ln -s /usr/local/share/wallpapers/Betelgeuse-Ubuntu/contents/source.png /usr/share/backgrounds/default.png
elif [ '{{ .host.distro.id }}' == 'windows' ]; then
sudo ln -s /usr/local/share/wallpapers/Betelgeuse-Windows/contents/source.png /usr/share/backgrounds/default.png
else
sudo ln -s /usr/local/share/wallpapers/Betelgeuse/contents/source.png /usr/share/backgrounds/default.png
fi
### Note: ubuntu-default-greyscale-wallpaper.png symlink to whiteish gray background
### Set appropriate platform-specific icon in plymouth theme
if [ -f '/usr/local/share/plymouth/themes/{{ .theme }}/icons/{{ .host.distro.id }}.png' ]; then
sudo cp -f '/usr/local/share/plymouth/themes/{{ .theme }}/icons/{{ .host.distro.id }}.png' '/usr/local/share/plymouth/themes/{{ .theme }}/icon.png'
logg success 'Added platform-specific icon to {{ .theme }} Plymouth theme'
else
logg warn 'The {{ .host.distro.id }}.png icon is not available in the icons folder insider the {{ .theme }} Plymouth theme'
fi
fi
}
# @description
# This script configures the root user's folder so that scripts running as the root user can:
#
# 1. Access binaries installed by the provisioning user (by setting the appropriate `~/.bashrc` and `~/.zshrc` symlinks)
# 2. Use the same shell profile rules that the provisioning user uses (by symlinking the `~/.config/shell`, `~/.bashrc`, and `~/.zshrc` locations)
applyRootConfig() {
### Detect root folder
if [ -d /var/root ]; then
ROOT_FOLDER="/var/root"
elif [ -d /root ]; then
ROOT_FOLDER="/root"
else
logg warn 'Unable to find root user folder location'
fi
if [ -n " $ ROOT_FOLDER " ]; then
2023-12-24 14:36:54 -08:00
### Copy minimal set of profile configuration files
2023-12-23 02:18:29 -08:00
logg info "Copying ~/.bashrc to $ ROOT_FOLDER /.bashrc" && sudo cp -f " $ HOME /.bashrc" " $ ROOT_FOLDER /.bashrc"
logg info "Copying ~/.zshrc to $ ROOT_FOLDER /.zshrc" && sudo cp -f " $ HOME /.zshrc" " $ ROOT_FOLDER /.zshrc"
logg info "Ensuring ~/.config folder exists" && sudo mkdir -p " $ ROOT_FOLDER /.config"
logg info "Copying ~/.config/shell to $ ROOT_FOLDER /.config/shell" && sudo mkdir -p " $ ROOT_FOLDER /.config" && sudo rm -rf " $ ROOT_FOLDER /.config/shell" && sudo cp -rf " $ HOME /.config/shell" " $ ROOT_FOLDER /.config/shell"
2023-12-24 14:36:54 -08:00
### Copy Autorestic configurations
logg info "Copying ${ XDG_CONFIG_HOME : - $ HOME / . config } /autorestic/autorestic-system.yml file to $ ROOT_FOLDER /.autorestic.yml" && sudo cp -f " ${ XDG_CONFIG_HOME : - $ HOME / . config } /autorestic/autorestic-system.yml" " $ ROOT_FOLDER /.autorestic.yml"
logg info "Removing ${ XDG_CONFIG_HOME : - $ HOME / . config } /autorestic/autorestic-system.yml" && sudo rm -f " ${ XDG_CONFIG_HOME : - $ HOME / . config } /autorestic/autorestic-system.yml"
logg info "Applying proper permissions to $ ROOT_FOLDER /.autorestic.yml" && sudo chmod 600 " $ ROOT_FOLDER /.autorestic.yml"
2023-12-23 02:18:29 -08:00
fi
}
# @description
# This function ensures the macOS desktop wallpaper is set to the macOS Betelgeuse wallpaper. It uses the
# `m` CLI to apply the change.
#
# This function ensures the Qubes desktop wallpaper is set to the Qubes Betelgeuse wallpaper on KDE by
# using the `ksetwallpaper` script found in `~/.local/bin/ksetwallpaper`.
applyWallpaper() {
{{- if (eq .host.distro.id "qubes") -}}
logg info 'Setting wallpaper to /usr/local/share/wallpapers/Betelgeuse/contents/images/3440x1440.jpg'
ksetwallpaper --file /usr/local/share/wallpapers/Betelgeuse/contents/images/3440x1440.jpg
{{ else -}}
### Set macOS wallpaper
if command -v m > /dev/null && [ -f " ${ XDG_DATA_HOME : - $ HOME / . local / share } /betelgeuse/share/wallpapers/Betelgeuse-macOS/contents/source.png" ]; then
logg info 'Setting macOS wallpaper with m'
m wallpaper " ${ XDG_DATA_HOME : - $ HOME / . local / share } /betelgeuse/share/wallpapers/Betelgeuse-macOS/contents/source.png"
else
logg warn 'Either m or the macOS default wallpaper is missing.'
fi
{{ end -}}
}
# @description
# This script ensures ASDF is setup and then adds the plugins specified in the `~/.tool-versions` file. After that,
# it ensures the ASDF plugins are pre-installed.
asdfPluginInstall() {
### Re-source ~/.bashrc if necessary
if [ -z " $ ASDF_DIR " ]; then
logg info 'ASDF_DIR is not defined so ~/.bashrc will be sourced' && source ~/.bashrc
fi
if [ -f " $ ASDF_DIR /asdf.sh" ] && [ -f ~/.tool-versions ]; then
logg info 'Sourcing asdf.sh'
. ${ ASDF_DIR } /asdf.sh
cat .tool-versions | while read TOOL; do
logg info 'Installing ASDF plugin '"$(echo " $ TOOL " | sed 's/ .*//')"'' && asdf plugin add "$(echo " $ TOOL " | sed 's/ .*//')" > /dev/null && logg info "Successfully added $(echo " $ TOOL " | sed 's/ .*//') via ASDF"
done
# Only proceed with installation if either DEBUG_MODE is enabled or ~/.cache/megabyte-labs/asdf-install is missing
# Added to save time between tests because PHP takes awhile to install
if [ " $ DEBUG_MODE " == 'true' ] || [ ! -f " ${ XDG_CACHE_HOME : - $ HOME / . cache } /megabyte-labs/asdf-install" ]; then
logg info 'Installing ASDF dependencies derived from ~/.tool-versions via asdf install'
asdf install || EXIT_CODE=$?
if [ -n " $ EXIT_CODE " ]; then
logg error 'Error installing the ASDF plugins specified in ~/.tool-versions'
fi
mkdir -p " ${ XDG_CACHE_HOME : - $ HOME / . cache } /megabyte-labs"
touch " ${ XDG_CACHE_HOME : - $ HOME / . cache } /megabyte-labs/asdf-install"
fi
else
logg warn 'The $ ASDF_DIR /asdf.sh or ~/.tool-versions file is not present'
fi
}
### Helper function for configureNetworkManager
ensureNetworkConfigs() {
if [ ! -d /etc/network/if-up.d ]; then
logg info 'Creating /etc/network/if-up.d folder'
sudo mkdir -p /etc/network/if-up.d
fi
if [ ! -d /etc/network/if-post-down.d ]; then
logg info 'Creating /etc/network/if-post.d folder'
sudo mkdir -p /etc/network/if-post.d
fi
}
# @description
# This script installs OpenVPN and WireGuard VPN profiles. It does a few things to install the profiles and make sure
# they are usable by desktop users:
#
# 1. It ensures OpenVPN and `NetworkManager-*` plugins are installed (this allows you to see all the different VPN profile types available when you try to import a VPN profile on Linux devices)
# 2. Imports the OpenVPN profiles stored in ` ${ XDG_CONFIG_HOME : - $ HOME / . config } /vpn`
# 3. Applies the OpenVPN username and password to all the OpenVPN profiles (which can be passed in as `OVPN_USERNAME` and `OVPN_PASSWORD` if you use the environment variable method)
# 4. Bypasses the OpenVPN connection for all the networks defined in `.host.vpn.excludedSubnets` (in the `home/.chezmoi.yaml.tmpl` file)
# 5. Repeats the process for WireGuard by looping through all the `*.nmconnection` files stored in ` ${ XDG_CONFIG_HOME : - $ HOME / . config } /vpn` (username and password should already be stored in the encrypted files)
#
# ## Creating VPN Profiles
#
# More details on embedding your VPN profiles into your Install Doctor fork can be found by reading the [Secrets documentation](https://install.doctor/docs/customization/secrets #vpn - profiles ) .
#
# ## Links
#
# * [VPN profile folder](https://github.com/megabyte-labs/install.doctor/blob/master/home/dot_config/vpn)
# * [VPN profile documentation](https://install.doctor/docs/customization/secrets #vpn - profiles )
configureNetworkManagerVPNProfiles() {
if [ "{{ .host.distro.family }}" = "linux" ]; then
{{ $ ovpnUsername := (env "OVPN_USERNAME") }}
{{ if (stat (joinPath .chezmoi.sourceDir ".chezmoitemplates" "secrets" "OVPN_USERNAME")) }}
{{ $ ovpnUsername := (includeTemplate "secrets/OVPN_USERNAME" | decrypt | trim) }}
{{ end }}
{{ $ ovpnPassword := (env "OVPN_PASSWORD") }}
{{ if (stat (joinPath .chezmoi.sourceDir ".chezmoitemplates" "secrets" "OVPN_PASSWORD")) }}
{{ $ ovpnPassword := (includeTemplate "secrets/OVPN_PASSWORD" | decrypt | trim) }}
{{ end }}
RESTART_NM=false
### Ensure `NetworkManager` plugins are
# NOTE: By default, all the NetworkManager plugins are installed.
if command -v apt-get > /dev/null; then
sudo apt-get install -y network-manager*
elif command -v dnf > /dev/null; then
sudo dnf install -y openvpn NetworkManager*
elif command -v pacman > /dev/null; then
sudo pacman -Syu openvpn networkmanager*
else
logg warn 'Unknown package manager - install OpenVPN / WireGuard / NetworkManager plugins individually'
fi
### Ensures `nmcli` (the CLI for NetworkManager) is available in the `PATH`
if command -v nmcli > /dev/null; then
### Sets up OpenVPN profiles
if [ '{{ $ ovpnUsername }}' != '' ] && [ '{{ $ ovpnPassword }}' != '' ]; then
find " ${ XDG_CONFIG_HOME : - $ HOME / . config } /vpn" -type f -name "*.ovpn" | while read OVPN_FILE; do
### Adds the OpenVPN profiles by importing the `*.ovpn` files in `${XDG_CONFIG_HOME:-$HOME/.config}/vpn` and then applying the OpenVPN username and password
logg info "Adding $ OVPN_FILE to NetworkManager OpenVPN profiles"
OVPN_NAME="$(basename " $ OVPN_FILE " | sed 's/.ovpn$//')"
nmcli connection import type openvpn file " $ OVPN_FILE "
nmcli connection modify " $ OVPN_NAME " +vpn.data 'username={{- $ ovpnUsername }}'
nmcli connection modify " $ OVPN_NAME " vpn.secrets 'password={{- $ ovpnPassword }}'
nmcli connection modify " $ OVPN_NAME " +vpn.data password-flags=0
### Register the excluded subnets in the routeadd / routedel files
for EXCLUDED_SUBNET in '{{ $ removeShortcuts := join "' '" .host.vpn.excludedSubnets }}'; do
ensureNetworkConfigs
nmcli connection modify " $ OVPN_NAME " +ipv4.routes " $ EXCLUDED_SUBNET " | sudo tee -a /etc/network/if-up.d/routeadd
nmcli connection modify " $ OVPN_NAME " -ipv4.routes " $ EXCLUDED_SUBNET " | sudo tee -a /etc/network/if-post-down.d/routedel
fi
RESTART_NM=true
done
else
logg info 'Either the OpenVPN username or password is undefined.'
logg info 'See the docs/VARIABLES.md file for details.'
fi
{{ if (stat (joinPath .host.home ".config" "age" "chezmoi.txt")) }}
### Setup WireGuard profiles
if [ -d /etc/NetworkManager/system-connections ]; then
find " ${ XDG_CONFIG_HOME : - $ HOME / . config } /vpn" -type f -name "*.nmconnection" | while read WG_FILE; do
### Ensure the WireGuard NetworkManager plugin is available
if [ ! -d /usr/lib/NetworkManager/nm-wireguard-service ]; then
logg info 'The nm-wireguard-service is not present'
logg info 'Installing the nm-wireguard-service'
fi
### Add the WireGuard profiles
logg info "Adding $ WG_FILE to /etc/NetworkManager/system-connections"
WG_FILENAME="$(basename " $ WG_FILE ")"
chezmoi decrypt " $ WG_FILE " | sudo tee "/etc/NetworkManager/system-connections/ $ WG_FILENAME "
### Register the excluded subnets in the routeadd / routedel files
for EXCLUDED_SUBNET in '{{ $ removeShortcuts := join "' '" .host.vpn.excludedSubnets }}'; do
ensureNetworkConfigs
WG_PROFILE_NAME="$(echo " $ WG_FILENAME " | sed 's/.nmconnection$//')"
nmcli connection modify " $ WG_PROFILE_NAME " +ipv4.routes " $ EXCLUDED_SUBNET " | sudo tee -a /etc/network/if-up.d/routeadd
nmcli connection modify " $ WG_PROFILE_NAME " -ipv4.routes " $ EXCLUDED_SUBNET " | sudo tee -a /etc/network/if-post-down.d/routedel
fi
RESTART_NM=true
done
else
logg warn '/etc/NetworkManager/system-connections is not a directory!'
fi
{{ end -}}
### Restart NetworkManager if changes were made and environment is not WSL
if [ " $ RESTART_NM " == 'true' ] && [[ ! "$(test -d proc && grep Microsoft /proc/version > /dev/null)" ]]; then
logg info 'Restarting NetworkManager since VPN profiles were updated'
sudo service NetworkManager restart
fi
else
logg warn 'nmcli is unavailable'
fi
fi
}
# @description
# This script applies the SSH server MOTD banner and `sshd_config` (which are housed in the `home/private_dot_ssh/system` location)
# to the system by copying the files to the system location and then restarting / enabling the system SSH server.
#
# ## Links
#
# * [System SSHD configurations](https://github.com/megabyte-labs/install.doctor/tree/master/home/private_dot_ssh/system)
configureSSHD() {
### Update /etc/ssh/sshd_config if environment is not WSL
if [[ ! "$(test -d /proc && grep Microsoft /proc/version > /dev/null)" ]]; then
if [ -d /etc/ssh ]; then
logg info 'Copying ~/.ssh/system/banner to /etc/ssh/banner' && sudo cp -f " $ HOME /.ssh/system/banner" /etc/ssh/banner
logg info 'Copying ~/.ssh/system/sshd_config to /etc/ssh/sshd_config' && sudo cp -f " $ HOME /.ssh/system/sshd_config" /etc/ssh/sshd_config
if command -v semanage > /dev/null; then
logg info 'Apply SELinux configuration addressing custom SSH port' && sudo semanage port -a -t ssh_port_t -p tcp {{ .host.ssh.port }}
logg info 'Allow NIS SSHD' && sudo setsebool -P nis_enabled 1
fi
### Ensure keys are created
logg info 'Running sudo ssh-keygen -A' && sudo ssh-keygen -A
### Restart SSH server
if [ -d /Applications ] && [ -d /System ]; then
### macOS
logg info 'Running sudo systemsetup -setremotelogin on' && sudo systemsetup -setremotelogin on
logg info 'Running sudo launchctl load -w /System/Library/LaunchDaemons/ssh.plist' && sudo launchctl load -w /System/Library/LaunchDaemons/ssh.plist 2> /dev/null
logg info 'Running sudo launchctl stop com.openssh.sshd' && sudo launchctl stop com.openssh.sshd
logg info 'Running sudo launchctl start com.openssh.sshd' && sudo launchctl start com.openssh.sshd && logg info 'Successfully ran launchctl start com.openssh.sshd'
else
### Linux
logg info 'Enabling the sshd service'
sudo systemctl enable sshd
logg info 'Restarting the sshd service'
sudo systemctl restart sshd && logg info 'Successfully ran sudo systemctl restart sshd'
fi
else
logg warn 'The /etc/ssh folder does not exist'
fi
else
logg info 'Skipping sshd_config application since environment is WSL'
fi
}
# @description
# This script allows you to apply `dconf` settings that you can store in your fork of Install Doctor. By default,
# it makes a handful of `dconf` settings optimizations.
dconfSettings() {
if command -v dconf > /dev/null; then
### Update background to be OS-specific
if [ -f " ${ XDG_CONFIG_HOME : - $ HOME / . config } /dconf/settings/org.gnome.desktop.background" ]; then
logg info 'Checking for presence of /usr/local/share/wallpapers/Betelgeuse-{{ title .host.distro.id }}/contents/source.jpg'
if [ -f /usr/local/share/wallpapers/Betelgeuse-{{ title .host.distro.id }}/contents/source.jpg ]; then
logg info "Updating ${ XDG_CONFIG_HOME : - $ HOME / . config } /dconf/settings/org.gnome.desktop.background to point to OS-specific background"
TMP="$(mktemp)"
sed 's/Betelgeuse/Betelgeuse-{{ title .host.distro.id }}/g' < " ${ XDG_CONFIG_HOME : - $ HOME / . config } /dconf/settings/org.gnome.desktop.background" > " $ TMP "
mv " $ TMP " " ${ XDG_CONFIG_HOME : - $ HOME / . config } /dconf/settings/org.gnome.desktop.background"
else
logg info 'OS-specific background not found'
fi
fi
### Backup system settings
DCONF_TMP="$(mktemp)"
dconf dump / > " $ DCONF_TMP "
logg info 'Backed up system dconf settings to '" $ DCONF_TMP "
### Reset system settings / load saved configurations from ~/.config/dconf/settings
if [ -d " ${ XDG_CONFIG_HOME : - $ HOME / . config } /dconf/settings" ]; then
find " ${ XDG_CONFIG_HOME : - $ HOME / . config } /dconf/settings" -mindepth 1 -maxdepth 1 -type f | while read DCONF_CONFIG_FILE; do
if [ " $ DEBUG_MODE " == 'true' ]; then
logg info 'Dconf configuration file:'
echo " $ DCONF_CONFIG_FILE "
fi
DCONF_SETTINGS_ID="/$(basename " $ DCONF_CONFIG_FILE " | sed 's/\./\//g')/"
if [ " $ DEBUG_MODE " == 'true' ]; then
logg info 'Dconf settings ID:'
echo " $ DCONF_SETTINGS_ID "
fi
### Reset dconf settings if environment variable RESET_DCONF is set to true
if [ " $ RESET_DCONF " == 'true' ]; then
logg info 'Resetting dconf settings for '" $ DCONF_SETTINGS_ID "''
dconf reset -f " $ DCONF_SETTINGS_ID "
fi
logg info 'Loading versioned dconf settings for '" $ DCONF_SETTINGS_ID "''
dconf load " $ DCONF_SETTINGS_ID " < " $ DCONF_CONFIG_FILE "
logg success 'Finished applying dconf settings for '" $ DCONF_SETTINGS_ID "''
done
else
logg warn '~/.config/dconf/settings does not exist!'
fi
fi
}
# @description
# This script decrypts the SSH key files that are housed in the `home/.chezmoitemplates/ssh` section of the repository.
# It loops through all the files in `home/.chezmoitemplates/ssh` and stores them to the `~/.ssh` folder
# when they are successfully decrypted.
#
# This script generates a pair of default `id_rsa` and `id_rsa.pub` keys if one is not already present
# on the system after the Install Doctor provisioning process completes. It also ensures the private
# key is only readable and writable the provisioning user.
decryptSSHKeys() {
### Unpack existing encrypted keys
logg info 'Decrypting SSH keys stored in the home/.chezmoitemplates/ssh folder of the Install Doctor repo / fork.'
find "{{ .chezmoi.sourceDir }}/.chezmoitemplates/ssh" -type f | while read SSH_FILE; do
### Decrypt SSH file with Chezmoi
logg info "Decrypting the $(basename " $ SSH_FILE ") encrypted SSH file"
chezmoi decrypt " $ SSH_FILE " > " $ HOME /.ssh/$(basename " $ SSH_FILE ")" || EXIT_CODE=$?
### Handle failed decryption with warning log message
if [ -n " $ EXIT_CODE " ]; then
logg warn "Unable to decrypt the file stored in $ SSH_FILE "
fi
### Apply appropriate permission to decrypted ~/.ssh file
if [ -f " $ HOME /.ssh/$(basename " $ SSH_FILE ")" ]; then
logg info "Applying appropriate permissions on $ HOME /.ssh/$(basename " $ SSH_FILE ")"
chmod 600 " $ HOME /.ssh/$(basename " $ SSH_FILE ")"
fi
done
### Ensure id_rsa is present and create one if it does not exist
if [ ! -f " $ HOME /.ssh/id_rsa" ]; then
logg 'Generating missing default private key / public key (~/.ssh/id_rsa)'
ssh-keygen -b 4096 -t rsa -f " $ HOME /.ssh/id_rsa" -q -N ""
chmod 600 " $ HOME /.ssh/id_rsa"
fi
}
# @description
# This script installs and activates the latest version of Emscripten. This script
# implements the [instructions outlined on Emscripten's website](https://emscripten.org/docs/getting_started/downloads.html #installation - instructions - using - the - emsdk - recommended ) .
#
# This script will only run when ` ${ XDG_DATA_HOME : - $ HOME / . local / share } /emsdk` is present on the system. This folder
# is populated via the definition in `home/.chezmoiexternal.toml.tmpl`.
emscriptenInstall() {
if [ -d " ${ XDG_DATA_HOME : - $ HOME / . local / share } /emsdk" ]; then
logg info 'Pulling latest changes for Emscripten source code' && cd " ${ XDG_DATA_HOME : - $ HOME / . local / share } /emsdk" && git pull
logg info "Running emsdk install latest" && emsdk install latest > /dev/null
logg info "Running emsdk activate latest" && emsdk activate latest > /dev/null
logg info 'Profile source inclusions are already implemented in Bash / ZSH profile'
fi
}
# @description
# Adds auto-update feature to macOS that automatically downloads and installs updates. Also enables
# an auto-update feature for Homebrew on macOS.
enableAutoUpdateDarwin() {
if [ -d /Applications ] && [ -d /System ]; then
### Enable automated system updates on macOS
if [ -f " $ HOME /Library/LaunchDaemons/com.apple.automatedupdates.plist" ] && [ ! -f "/Library/LaunchDaemons/com.apple.automatedupdates.plist" ]; then
logg info 'Configuring macOS to automatically apply system updates'
sudo mkdir -p /Library/LaunchDaemons
sudo cp -f " $ HOME /Library/LaunchDaemons/com.apple.automatedupdates.plist" "/Library/LaunchDaemons/com.apple.automatedupdates.plist"
logg info 'Loading /Library/LaunchDaemons/com.apple.automatedupdates.plist'
sudo launchctl load "/Library/LaunchDaemons/com.apple.automatedupdates.plist" && logg success 'launchctl load successful'
fi
### Enable Homebrew auto-update service
if brew autoupdate status | grep 'Autoupdate is not configured.' > /dev/null; then
logg info 'Enabling Homebrew auto-update service (every 24 hours)'
brew autoupdate start --cleanup --greedy --upgrade
fi
fi
}
# @description
# This script modifies the `/etc/environment` file on Linux devices to include:
#
# * `export QT_STYLE_OVERRIDE=kvantum-dark` which is required for the Linux GNOME / KDE themeing that relies on Kvantum.
ensureQtStyleOverride() {
### Ensure QT_STYLE_OVERRIDE is set in /etc/environment
logg info 'Ensuring QT_STYLE_OVERRIDE is set in /etc/environment'
if cat /etc/environment | grep QT_STYLE_OVERRIDE > /dev/null; then
sudo sed -i 's/.*QT_STYLE_OVERRIDE.*/export QT_STYLE_OVERRIDE=kvantum-dark/' /etc/environment
logg info 'Updated QT_STYLE_OVERRIDE in /etc/environment'
else
echo 'export QT_STYLE_OVERRIDE=kvantum-dark' | sudo tee -a /etc/environment
logg info 'Added QT_STYLE_OVERRIDE to /etc/environment'
fi
}
# @description Run `gem update --system` if `gem` is available
ensureSystemGemUpdated() {
### Ensure gem is updated
if command -v gem > /dev/null; then
logg info 'Ensuring system gem is updated' && gem update --system
else
logg info 'Could not find gem in PATH so skipping gem system update'
fi
}
# @description
# This script ensures ZSH is used as the default shell by ensuring it is added to `/etc/shells`. The script
# also ensures ZSH is available at `/usr/local/bin/zsh` on ARM64 systems by symlinking the Homebrew ZSH shell
# to `/usr/local/bin/zsh` if it is missing.
ensureZshShell() {
logg 'Ensuring ZSH is set as the default shell'
if ! grep -qc "/usr/local/bin/zsh" /etc/shells; then
echo "/usr/local/bin/zsh" | sudo tee -a /etc/shells > /dev/null
fi
if [ ! -f /usr/local/bin/zsh ] && [ -f " ${ HOMEBREW_PREFIX : - / opt / homebrew } /bin/zsh" ]; then
sudo ln -sf " ${ HOMEBREW_PREFIX : - / opt / homebrew } /bin/zsh" /usr/local/bin/zsh
fi
}
# @description
# This script ensures your GNOME extensions come pre-configured to your liking. It provides the ability
# to automatically install a configurable list of GNOME extensions as well as apply your preferred settings.
gnomeExtensionSettings() {
if command -v gnome-shell > /dev/null; then
### Ensure /tmp/install-gnome-extensions.txt is not present on the system
if [ -f /tmp/install-gnome-extensions.txt ]; then
rm -f /tmp/install-gnome-extensions.txt
fi
### Register temporary file for gnome.yml JSON
if [ -f " ${ XDG_CONFIG_HOME : - $ HOME / . config } /desktop/gnome.yml" ]; then
TMP_YQ="$(mktemp)"
cat " ${ XDG_CONFIG_HOME : - $ HOME / . config } /desktop/gnome.yml" | yq e -o=j '.' > " $ TMP_YQ "
fi
### Populate /tmp/install-gnome-extensions.txt with GNOME extensions that need to be installed
if [ -f " ${ XDG_CONFIG_HOME : - $ HOME / . config } /desktop/gnome.yml" ]; then
cat " $ TMP_YQ " | jq -c '.default_gnome_extensions[] | tojson' | while read EXT; do
TMP="$(mktemp)"
echo " $ EXT " | sed 's/^.\(.*\).$/\1/' > " $ TMP "
EXT_URL="$(cat " $ TMP " | jq -r '.url')"
EXT_ID="$(cat " $ TMP " | jq -r '.regex')"
echo " $ EXT_URL " >> /tmp/install-gnome-extensions.txt
if [ ! -d " ${ XDG_DATA_HOME : - $ HOME / . local / share } /gnome-shell/extensions" ]; then
mkdir -p " ${ XDG_DATA_HOME : - $ HOME / . local / share } /gnome-shell/extensions"
fi
find " ${ XDG_DATA_HOME : - $ HOME / . local / share } /gnome-shell/extensions" -mindepth 1 -maxdepth 1 -type d | while read EXT_FOLDER; do
if [[ " $ EXT_FOLDER " == *" $ EXT_ID "* ]] && [ -f /tmp/install-gnome-extensions.txt ]; then
TMP_EXT="$(mktemp)"
head -n -1 /tmp/install-gnome-extensions.txt > " $ TMP_EXT "
mv -f " $ TMP_EXT " /tmp/install-gnome-extensions.txt > /dev/null
fi
done
done
else
logg warn 'The ~/.config/desktop/gnome.yml file is missing so GNOME extension install orders cannot be calculated'
fi
### Remove /tmp/install-gnome-extensions.txt if it is empty
if [ "$(cat /tmp/install-gnome-extensions.txt)" == "" ]; then
rm -f /tmp/install-gnome-extensions.txt > /dev/null
fi
### Install the GNOME extensions using the `install-gnome-extensions` script
if command -v install-gnome-extensions > /dev/null; then
if [ -f /tmp/install-gnome-extensions.txt ]; then
logg info 'Running the install-gnome-extensions script'
cd /tmp
install-gnome-extensions --enable --overwrite --file /tmp/install-gnome-extensions.txt
rm -f /tmp/install-gnome-extensions.txt
logg success 'Finished installing the GNOME extensions'
else
logg info 'No new GNOME extensions to install'
fi
else
logg warn 'Cannot install GNOME extensions because the install-gnome-extensions script is missing from ~/.local/bin'
fi
### Apply plugin gsettings
if [ -f " ${ XDG_CONFIG_HOME : - $ HOME / . config } /desktop/gnome.yml" ]; then
cat " $ TMP_YQ " | jq -c '.default_gnome_extensions[] | tojson' | while read EXT; do
if [ " $ DEBUG_MODE " == 'true' ]; then
logg info 'Extension data:'
echo " $ EXT "
fi
TMP="$(mktemp)"
echo " $ EXT " | sed 's/^.\(.*\).$/\1/' > " $ TMP "
EXT_URL="$(cat " $ TMP " | jq -r '.url')"
EXT_ID="$(cat " $ TMP " | jq -r '.regex')"
if [ " $ DEBUG_MODE " == 'true' ]; then
logg info 'Extension ID:'
echo " $ EXT_ID "
fi
EXT_SETTINGS_TYPE="$(cat " $ TMP " | jq -r '.settings | type')"
EXT_SETTINGS="$(cat " $ TMP " | jq -r '.settings')"
if [ " $ EXT_SETTINGS " != 'null' ]; then
logg info 'Evaluating extension settings for '" $ EXT_ID "''
if [ " $ EXT_SETTINGS_TYPE " == 'array' ]; then
cat " $ TMP " | jq -r '.settings[]' | while read EXT_SETTING; do
logg info 'Applying following extension setting:'
echo " $ EXT_SETTING "
eval " $ EXT_SETTING "
done
else
logg info 'Applying following extension setting:'
echo " $ EXT_SETTINGS "
eval " $ EXT_SETTINGS "
fi
logg success 'Applied gsettings configuration for the '" $ EXT_ID "' GNOME extension'
fi
done
fi
fi
}
# @description
# This script configures GRUB2 with a custom theme on Linux systems.
grubSettings() {
if [ "{{ .host.distro.family }}" = "linux" ]; then
### Fix Qubes issue
if command -v qubesctl > /dev/null && [ -f /boot/grub2/grubenv ] && [ -d /boot/efi/EFI/qubes ]; then
sudo cp -f /boot/grub2/grubenv /boot/efi/EFI/qubes/grubenv
logg success 'Copied /boot/grub2/grubenv to /boot/efi/EFI/qubes/grubenv'
fi
### Ensure /boot/grub2/themes is directory
if [ ! -d /boot/grub2/themes ]; then
sudo mkdir -p /boot/grub2/themes
logg success 'Created /boot/grub2/themes'
fi
### Copy GRUB theme to /boot/grub2/themes
if [ -d /usr/local/share/grub/themes ]; then
sudo cp -rf /usr/local/share/grub/themes /boot/grub2/
logg success 'Copied GRUB themes in /usr/local/share/grub/themes to /boot/grub2/themes'
else
logg warn '/usr/local/share/grub/themes is missing'
fi
### Set default GRUB screen resolution variables
SCREEN_RATIO_ULTRAWIDE="2100"
GRUB_RESOLUTION_TYPE="1080p"
### Determine screen size ratio (used for picking GRUB2 theme resolution)
if command -v xrandr > /dev/null && command -v uniq > /dev/null; then
SCREEN_WIDTH="$(xrandr --current | grep '*' | uniq | awk '{print $1}' | cut -d 'x' -f1)"
SCREEN_HEIGHT="$(xrandr --current | grep '*' | uniq | awk '{print $1}' | cut -d 'x' -f2)"
SCREEN_RATIO="$(awk -v height=" $ SCREEN_HEIGHT " -v width=" $ SCREEN_WIDTH " 'BEGIN { print ((height / width) * 1000) }')"
SCREEN_RATIO=" ${ SCREEN_RATIO % . * } "
logg success "Screen detected as $ SCREEN_WIDTH x $ SCREEN_HEIGHT (ratio of $ SCREEN_RATIO )"
if (( $(echo " $ SCREEN_RATIO $ SCREEN_RATIO_ULTRAWIDE " | awk '{print ($1 > $2)}') )); then
GRUB_RESOLUTION_TYPE="ultrawide"
logg info 'GRUB resolution registered as ultrawide'
fi
else
logg warn 'Missing either xrandr or uniq (required for calculating screen size ratio)'
fi
### Optimize the GRUB resolution
if [ -f /etc/default/grub ]; then
### GRUB_GFXMODE
logg info 'Setting GRUB_GFXMODE=auto in /etc/default/grub'
if cat /etc/default/grub | grep GRUB_GFX_MODE > /dev/null; then
sudo sed -i 's/.*GRUB_GFXMODE.*/GRUB_GFXMODE=auto/' /etc/default/grub
else
echo "GRUB_GFXMODE=auto" | sudo tee -a /etc/default/grub > /dev/null
fi
### GRUB_GFXPAYLOAD_LINUX
logg info 'Setting GRUB_GFXPAYLOAD_LINUX=keep in /etc/default/grub'
if cat /etc/default/grub | grep GRUB_GFXPAYLOAD_LINUX > /dev/null; then
sudo sed -i 's/.*GRUB_GFXPAYLOAD_LINUX.*/GRUB_GFXPAYLOAD_LINUX="keep"/' /etc/default/grub
else
echo 'GRUB_GFXPAYLOAD_LINUX="keep"' | sudo tee -a /etc/default/grub > /dev/null
fi
### GRUB_THEME
logg info 'Setting GRUB_THEME={{ .theme }} in /etc/default/grub'
if cat /etc/default/grub | grep GRUB_THEME > /dev/null; then
sudo sed -i 's/.*GRUB_THEME.*/GRUB_THEME="{{ .theme }}-'" $ GRUB_RESOLUTION_TYPE "'"/' /etc/default/grub
else
echo 'GRUB_THEME="{{ .theme }}-'" $ GRUB_RESOLUTION_TYPE "'"' | sudo tee -a /etc/default/grub > /dev/null
fi
### GRUB_BACKGROUND
# Removed since the background should be flat black which is configurable
# Leaving this code here in case we need to add a flat black image background for some reason
# logg info 'Setting GRUB_BACKGROUND=/usr/local/share/grub/{{ .theme }}-blue.png in /etc/default/grub'
# if cat /etc/default/grub | grep GRUB_BACKGROUND > /dev/null; then
# sudo sed -i 's/.*GRUB_BACKGROUND.*/GRUB_BACKGROUND="\/usr\/local\/share\/grub\/{{ .theme }}-blue.png"/' /etc/default/grub
# else
# echo 'GRUB_BACKGROUND="/usr/local/share/grub/{{ .theme }}-blue.png"' | sudo tee -a /etc/default/grub > /dev/null
# fi
### GRUB_TIMEOUT
logg info 'Setting GRUB_TIMEOUT={{ .grub.timeout }} in /etc/default/grub'
if cat /etc/default/grub | grep GRUB_TIMEOUT > /dev/null; then
sudo sed -i 's/.*GRUB_TIMEOUT.*/GRUB_TIMEOUT="{{ .grub.timeout }}"/' /etc/default/grub
else
echo 'GRUB_TIMEOUT="{{ .grub.timeout }}"' | sudo tee -a /etc/default/grub > /dev/null
fi
### GRUB_FORCE_HIDDEN_MENU
logg info 'Setting GRUB_FORCE_HIDDEN_MENU={{ .grub.shiftToSee }} in /etc/default/grub'
sudo sed -i '/GRUB_FORCE_HIDDEN_MENU/d' /etc/default/grub
echo "GRUB_FORCE_HIDDEN_MENU={{ .grub.shiftToSee }}" | sudo tee -a /etc/default/grub > /dev/null
### Remove duplicate lines in /etc/default/grub
logg info 'Ensuring there are no duplicate entries in /etc/default/grub'
cat /etc/default/grub | uniq | sudo tee /etc/default/grub > /dev/null
else
logg warn '/etc/default/grub is missing'
fi
### Determine platform-specific icon to use
if command -v qubesctl > /dev/null; then
GRUB_ICON='qubes'
elif [ -f "/usr/local/share/grub/themes/{{ .theme }}- $ GRUB_RESOLUTION_TYPE /icons/{{ .host.distro.id }}.png" ]; then
GRUB_ICON='{{ .host.distro.id }}'
elif [ -f "/usr/local/share/grub/themes/{{ .theme }}- $ GRUB_RESOLUTION_TYPE /icons/{{ .host.distro.family }}.png" ]; then
GRUB_ICON='{{ .host.distro.family }}'
else
GRUB_ICON='linux'
fi
### Copy icon to GRUB2 theme folder
# Check looks in /usr/local/share/grub because on some systems the /boot folder is behind permissions for non-root users
if [ -f "/usr/local/share/grub/themes/{{ .theme }}- $ GRUB_RESOLUTION_TYPE /icons/ $ GRUB_ICON . png " ]; then
sudo cp -f /boot/grub2/themes/{{ .theme }}- $ GRUB_RESOLUTION_TYPE /icons/ $ GRUB_ICON . png /boot/grub2/themes/{{ .theme }}- $ GRUB_RESOLUTION_TYPE /icon.png
logg success 'Copied platform-specific icon to GRUB2 theme folder'
else
logg warn "/boot/grub2/themes/{{ .theme }}- $ GRUB_RESOLUTION_TYPE /icons/ $ GRUB_ICON . png is missing"
fi
### Hide unnecessary Boot messages and Bliking cursor
GRUB_DEFAULT_CMDLINE=$(grep 'GRUB_CMDLINE_LINUX_DEFAULT' /etc/default/grub)
if [[ -n $ GRUB_DEFAULT_CMDLINE ]]; then
KERNEL_PARAMS_QUIET=$(echo " $ GRUB_DEFAULT_CMDLINE " | grep 'quiet')
logg info 'Updating GRUB_CMDLINE_LINUX_DEFAULT to hide log messages'
if [[ -z $ KERNEL_PARAMS_QUIET ]]; then
sudo sed -i 's/^GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="quiet loglevel=3 systemd.show_status=auto rd.udev.log_level=3 vt.global_cursor_default=0 /' /etc/default/grub
else
NEW_KERNEL_PARAMS=$(echo $ KERNEL_PARAMS_QUIET | sed -e "s/quiet/quiet loglevel=3 systemd.show_status=auto rd.udev.log_level=3 vt.global_cursor_default=0/")
sudo sed -i "s/^GRUB_CMDLINE_LINUX_DEFAULT.*/ ${ NEW_KERNEL_PARAMS } /" /etc/default/grub
fi
else
logg info 'GRUB_CMDLINE_LINUX_DEFAULT was not present, adding one with parameters to hide log messages'
echo 'GRUB_CMDLINE_LINUX_DEFAULT="quiet loglevel=3 systemd.show_status=auto rd.udev.log_level=3 vt.global_cursor_default=0"' | sudo tee -a /etc/default/grub > /dev/null
fi
### Ensure grub2-mkconfig is available
if ! command -v grub2-mkconfig > /dev/null; then
if command -v grub-mkconfig > /dev/null; then
sudo ln -s "$(which grub-mkconfig)" /usr/bin/grub2-mkconfig
elif sudo which grub-mkconfig > /dev/null; then
sudo ln -s "$(sudo which grub-mkconfig)" /usr/bin/grub2-mkconfig
else
logg warn 'Neither grub2-mkconfig or grub-mkconfig are available'
fi
fi
### Apply GRUB2 theme
# Set export DEBUG_MODE=true to bypass GRUB2 / Plymouth application
if [ " $ DEBUG_MODE " != 'true' ]; then
if command -v grub2-mkconfig > /dev/null; then
if [ -d /sys/firmware/efi ]; then
logg info 'Assuming system is UEFI-enabled since /sys/firmware/efi is present'
if [ -f /boot/efi/EFI/qubes/grub.cfg ]; then
logg info 'Running sudo grub2-mkconfig -o /boot/efi/EFI/qubes/grub.cfg'
sudo grub2-mkconfig -o /boot/efi/EFI/qubes/grub.cfg
logg success 'Applied GRUB2 theme'
elif [ -f /boot/efi/EFI/grub.cfg ]; then
logg info 'Running sudo grub2-mkconfig -o /boot/efi/EFI/grub.cfg'
sudo grub2-mkconfig -o /boot/efi/EFI/grub.cfg
logg success 'Applied GRUB2 theme'
else
logg warn 'Unknown GRUB2 configuration - not applying GRUB2 theme'
fi
else
logg info 'Assuming system is non-UEFI since /sys/firmware/efi is not present'
logg info 'Running sudo grub2-mkconfig -o /boot/grub2/grub.cfg'
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
logg success 'Applied GRUB2 theme'
fi
elif [ -f /usr/sbin/update-grub ]; then
logg info 'Running sudo update-grub'
sudo update-grub
else
logg warn 'Unable to find appropriate GRUB mkconfig command'
fi
else
logg info 'Skipping GRUB2 theme application because DEBUG_MODE is set to true'
fi
fi
}
# @description
# This script sets the [Docker Rclone plugin](https://rclone.org/docker/) which allows you to mount Rclone mounts as Docker volumes
#
# ## Docker Rclone
#
# The Docker Rclone installation ensures necessary system directories are initialized / created. It also copies the [Docker Rclone configuration](https://github.com/megabyte-labs/install.doctor/blob/master/home/dot_config/rclone/private_docker-rclone.conf.tmpl)
# to the proper system location.
installDockerRclonePlugin() {
### Docker Rclone plugin
# Source: https://rclone.org/docker/
# First, ensure Docker Rclone configuration exists (which only happens when the Chezmoi Age decryption key is present as well as keys for Rclone)
if [ -f " ${ XDG_CONFIG_HOME : - $ HOME / . config } /rclone/docker-rclone.conf" ]; then
### Ensure Docker Rclone plugin system folders exist
logg info 'Ensure Docker Rclone plugin system folders exist'
logg info 'Ensuring directory /var/lib/docker-plugins/rclone/config is created' && sudo mkdir -p /var/lib/docker-plugins/rclone/config
logg info 'Ensuring directory /var/lib/docker-plugins/rclone/cache is created' && sudo mkdir -p /var/lib/docker-plugins/rclone/cache
### Copy Rclone configuration
logg info "Copy the Rclone configuration from ${ XDG_CONFIG_HOME : - $ HOME / . config } /rclone/docker-rclone.conf to /var/lib/docker-plugins/rclone/config/rclone.conf"
sudo cp -f " ${ XDG_CONFIG_HOME : - $ HOME / . config } /rclone/docker-rclone.conf" /var/lib/docker-plugins/rclone/config/rclone.conf
### Install the Rclone Docker plugin (if not already installed)
if ! sudo su -c 'docker plugin ls' - " $ USER " | grep 'rclone:latest' > /dev/null; then
sudo su -c 'docker plugin install rclone/docker-volume-rclone:amd64 args="-v" --alias rclone --grant-all-permissions' - " $ USER "
fi
fi
}
# @description
# This script loads crontab jobs that are defined and housed in your Install Doctor fork.
loadCronjobs() {
2023-12-24 14:36:54 -08:00
logg info 'Installing user crontab jobs'
crontab < " ${ XDG_CONFIG_HOME : - $ HOME / . config } /crontab/config-user" || EXIT_CODE=$?
2023-12-23 02:18:29 -08:00
if [ -n " $ EXIT_CODE " ]; then
2023-12-24 14:36:54 -08:00
logg error 'Failed to load cronjobs for user'
fi
logg info 'Installing system crontab jobs'
sudo crontab < " ${ XDG_CONFIG_HOME : - $ HOME / . config } /crontab/config-system" || EXIT_CODE=$?
if [ -n " $ EXIT_CODE " ]; then
logg error 'Failed to load cronjobs for system'
2023-12-23 02:18:29 -08:00
fi
}
# @description
# This script houses a wide range of macOS system tweaks that are intended to improve the developer experience on macOS,
# as well as improve security. Some of the tweaks include modifying default settings for various applications.
macOSSettings() {
if [ -d /System ] && [ -d /Applications ] && [ -f " ${ XDG_CONFIG_HOME : - $ HOME / . config } /shell/macos.sh" ]; then
bash " ${ XDG_CONFIG_HOME : - $ HOME / . config } /shell/macos.sh"
fi
}
# @description Ensures all files in `~/.local/bin` are executable
makeLocalBinExecutable() {
logg info 'Ensuring all files in ~/.local/bin are executable'
find " $ HOME /.local/bin" -mindepth 1 -maxdepth 2 -type f | while read EXE_FILE; do
chmod +x " $ EXE_FILE "
done
}
# @description
# Install Doctor was previously called Gas Station. It was also Ansible based. Some of the features that Install Doctor
# provides are made available via Ansible roles that Gas Station provides. This script symlinks Gas Station's roles
# so that they can be leveraged by Install Doctor.
#
# Some of the roles that Gas Station provides are not available via Ansible Galaxy yet. This script symlinks Gas Station
# roles to an Ansible Galaxy / Ansible friendly location.
#
# ## Ansible Installation
#
# If Ansible is not already installed, this script will also install Ansible and all the necessary requirements using `pipx`.
# This script must run before the `install-packages` script because some of the Ansible roles might be leveraged by it.
#
# ## TODO
#
# * Move installation logic into the ZX installer so that Ansible and its dependencies are only installed when required
# * Remove Ansible dependency completely
symlinkAnsibleRoles() {
logg info 'Ensuring Gas Station roles are symlinked to ~/.local/share/ansible/roles'
mkdir -p " ${ XDG_DATA_HOME : - $ HOME / . local / share } /ansible/roles"
find " ${ XDG_DATA_HOME : - $ HOME / . local / share } /gas-station/roles" -mindepth 2 -maxdepth 2 -type d | while read ROLE_PATH; do
ROLE_FOLDER="professormanhattan.$(echo " $ ROLE_PATH " | sed 's/.*\/\([^\/]*\)$/\1/')"
ALT_ROLE_FOLDER="$(echo " $ ROLE_PATH " | sed 's/.*\/\([^\/]*\)$/\1/')"
if [ ! -d " ${ XDG_DATA_HOME : - $ HOME / . local / share } /ansible/roles/ $ ROLE_FOLDER " ] || [ "$(readlink -f " ${ XDG_DATA_HOME : - $ HOME / . local / share } /ansible/roles/ $ ROLE_FOLDER ")" != " $ ROLE_PATH " ]; then
logg info 'Symlinking '" $ ROLE_FOLDER "''
rm -f " ${ XDG_DATA_HOME : - $ HOME / . local / share } /ansible/roles/ $ ROLE_FOLDER "
ln -s " $ ROLE_PATH " " ${ XDG_DATA_HOME : - $ HOME / . local / share } /ansible/roles/ $ ROLE_FOLDER "
fi
if [ ! -d " ${ XDG_DATA_HOME : - $ HOME / . local / share } /ansible/roles/ $ ALT_ROLE_FOLDER " ] || [ "$(readlink -f " ${ XDG_DATA_HOME : - $ HOME / . local / share } /ansible/roles/ $ ALT_ROLE_FOLDER ")" != " $ ROLE_PATH " ]; then
rm -f " ${ XDG_DATA_HOME : - $ HOME / . local / share } /ansible/roles/ $ ALT_ROLE_FOLDER "
ln -s " $ ROLE_PATH " " ${ XDG_DATA_HOME : - $ HOME / . local / share } /ansible/roles/ $ ALT_ROLE_FOLDER "
fi
done
if [ -f " ${ XDG_DATA_HOME : - $ HOME / . local / share } /gas-station/requirements.yml" ]; then
### Install Ansible Galaxy and dependencies if missing
if ! command -v ansible-galaxy > /dev/null; then
if ! command -v pipx > /dev/null; then
logg info 'Installing pipx via Homebrew'
brew install --quiet pipx
logg info 'Running pipx ensurepath'
pipx ensurepath
fi
logg info 'Installing ansible-core via pipx'
pipx install ansible-core
if [ -d /Applications ] && [ -d /System ]; then
logg info 'Injecting macOS-specific pipx dependencies via pipx'
pipx inject ansible-core PyObjC PyObjC-core
fi
logg info 'Injecting Ansible dependencies via pipx'
pipx inject ansible-core docker lxml netaddr pexpect python-vagrant pywinrm requests-credssp watchdog
mkdir -p " ${ XDG_CACHE_HOME : - $ HOME / . cache } /megabyte-labs"
touch " ${ XDG_CACHE_HOME : - $ HOME / . cache } /megabyte-labs/ansible-installed"
fi
### Ensure Ansible Galaxy was successfully loaded and then install the Ansible Galaxy requirements
if command -v ansible-galaxy > /dev/null; then
logg info 'Ensuring Ansible Galaxy collections are installed'
export ANSIBLE_CONFIG=" ${ XDG_DATA_HOME : - $ HOME / . local / share } /ansible/ansible.cfg"
ansible-galaxy install -r " ${ XDG_DATA_HOME : - $ HOME / . local / share } /ansible/requirements.yml" > /dev/null || EXIT_CODE=$?
if [ -n " $ EXIT_CODE " ]; then
logg error 'Failed to install Ansible requirements from Ansible Galaxy'
if [ -d " ${ XDG_DATA_HOME : - $ HOME / . local / share } /gas-station/collections" ]; then
logg info 'Attempting to use locally stored Ansible requirements'
cd " ${ XDG_DATA_HOME : - $ HOME / . local / share } /gas-station/collections"
ansible-galaxy install -r requirements.yml || SECOND_EXIT_CODE=$?
if [ -n " $ SECOND_EXIT_CODE " ]; then
logg error 'Failed to install requirements from both the cloud and the local copy' && exit 1
fi
else
logg warn " ${ XDG_DATA_HOME : - $ HOME / . local / share } /gas-station/collections is missing"
fi
fi
else
logg warn 'Unable to install the Ansible Galaxy requirements.yml since the ansible-galaxy executable is missing from the PATH'
fi
else
logg warn '~/.local/share/ansible/requirements.yml is missing'
fi
}
# @description
# This script checks if `python3` is available and if `python` is not available. If both are true, then the script
# symlinks `python` to `python3` so that the `python` command uses `python3`.
#
# This is useful if you do not want to install Python 2.7 and would like Python 3 to be used in all scenarios where Python is
# invoked with the `python` command.
symlinkPython() {
### Symlink python3 to python if it is unavailable
if ! command -v python > /dev/null && command -v python3 > /dev/null; then
logg info 'Symlinking python3 to python since the latter is unavailable'
sudo ln -s "$(which python3)" /usr/local/bin/python
fi
}
# @description
# This script creates an empty directory with each user's name in `/var/log/user`. It initializes the folder in hopes
# that we can eventually store all user logs in a single directory alongside the system logs folder.
userLogFolders() {
find '{{ .host.homeParentFolder }}' -mindepth 1 -maxdepth 1 -type d | while read HOME_DIR; do
USER_FOLDER="$(echo " $ HOME_DIR " | sed 's/.*\/\([^\/]*\)$/\1/')"
if [ -d " $ HOME_DIR /.local" ]; then
if [ ! -d "/var/log/user/ $ USER_FOLDER " ]; then
logg info 'Creating /var/log/user/'" $ USER_FOLDER "'' && sudo mkdir -p "/var/log/user/ $ USER_FOLDER "
fi
logg info "Applying user permissions to /var/log/user/ $ USER_FOLDER " && sudo chown -Rf " $ USER_FOLDER " "/var/log/user/ $ USER_FOLDER "
fi
done
}
addZshEnv &
applyFontsToSystem &
applyLinuxConfSettings &
applyLinuxThemeFiles &
applyRootConfig &
applyWallpaper &
asdfPluginInstall &
configureNetworkManagerVPNProfiles &
configureSSHD &
dconfSettings &
decryptSSHKeys &
emscriptenInstall &
enableAutoUpdateDarwin &
ensureQtStyleOverride &
ensureSystemGemUpdated &
ensureZshShell &
gnomeExtensionSettings &
grubSettings &
installDockerRclonePlugin &
loadCronjobs &
macOSSettings &
makeLocalBinExecutable &
symlinkAnsibleRoles &
symlinkPython &
userLogFolders &
wait
logg info 'Completed asynchronous post-dotfile-application routine'