install.fairie/home/.chezmoiscripts/universal/run_before_05-system.sh.tmpl
Brian Zalewski db8c2dbcb2 Linux tweak
2023-12-25 02:25:59 +00:00

646 lines
28 KiB
Bash

#!/usr/bin/env bash
# @file System Tweaks
# @brief Applies a set of system tweaks such as ensuring the hostname is set, setting the timezone, and more
# @description
# This script applies system tweaks that should be made before the rest of the provisioning process.
{{ includeTemplate "universal/profile-before" }}
{{ includeTemplate "universal/logg-before" }}
export VOLTA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/volta"
export PATH="$VOLTA_HOME/bin:$PATH"
# @description
# This script determines the ideal `/swapfile` size by checking how much RAM is available on the system.
# It then creates the appropriate `/swapfile` by considering factors such as the file system type. It
# currently supports BTRFS and regular file systems.
#
# After the `/swapfile` is created, it is enabled and assigned the appropriate permissions.
#
# ## TODO
#
# * Add logic that creates a swapfile for ZFS-based systems
# * Integrate logic from https://gitlab.com/megabyte-labs/gas-station/-/blob/master/roles/system/common/tasks/linux/swap.yml
allocateSwap() {
if [ "{{ .host.distro.family }}" = "linux" ]; then
if [ ! -f /swapfile ]; then
### Determine ideal size of /swapfile
MEMORY_IN_KB="$(grep MemTotal /proc/meminfo | sed 's/.* \(.*\) kB/\1/')"
MEMORY_IN_GB="$((MEMORY_IN_KB / 1024 / 1024))"
if [ "$MEMORY_IN_GB" -gt 64 ]; then
SWAP_SPACE="$((MEMORY_IN_GB / 10))"
elif [ "$MEMORY_IN_GB" -gt 32 ]; then
SWAP_SPACE="$((MEMORY_IN_GB / 8))"
elif [ "$MEMORY_IN_GB" -gt 8 ]; then
SWAP_SPACE="$((MEMORY_IN_GB / 4))"
else
SWAP_SPACE="$MEMORY_IN_GB"
fi
### Create /swapfile
FS_TYPE="$(df -Th | grep ' /$' | sed 's/[^ ]*\s*\([^ ]*\).*/\1/')"
if [ "$FS_TYPE" == 'btrfs' ]; then
logg info 'Creating BTRFS /swapfile'
sudo btrfs filesystem mkswapfile /swapfile
elif [ "$FS_TYPE" == 'zfs' ]; then
logg warn 'ZFS system detected - add logic here to add /swapfile'
else
logg info "Creating a $SWAP_SPACE GB /swapfile"
sudo fallocate -l "${SWAP_SPACE}G" /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
fi
### Enable the /swapfile
if [ -f /swapfile ]; then
logg info 'Running sudo swapon /swapfile'
sudo swapon /swapfile
if cat /etc/fstab | grep "/swapfile"; then
sudo sed -i '/\/swapfile/\/swapfile none swap defaults 0 0/' /etc/fstab
else
echo "/swapfile none swap defaults 0 0" | sudo tee -a /etc/fstab > /dev/null
fi
fi
fi
fi
}
# @description
# This script imports your publicly hosted GPG key using `pgp.mit.edu` as the key host. It then assigns it
# the ultimate trust level. It also downloads and configures GPG to use the configuration defined in `.config.gpg`
# in the `home/.chezmoidata.yaml` file.
configureGPG() {
export KEYID="{{ .user.gpg.id }}"
if [ -n "$KEYID" ] && command -v gpg > /dev/null; then
if [ ! -d "$HOME/.gnupg" ]; then
mkdir "$HOME/.gnupg"
fi
chown "$(whoami)" "$HOME/.gnupg"
chmod 700 "$HOME/.gnupg"
chown -Rf "$(whoami)" "$HOME/.gnupg/"
find "$HOME/.gnupg" -type f -exec chmod 600 {} \;
find "$HOME/.gnupg" -type d -exec chmod 700 {} \;
if [ ! -f "$HOME/.gnupg/gpg.conf" ]; then
logg 'Downloading hardened gpg.conf file to ~/.gpnupg/gpg.conf'
curl -sSL --compressed "{{ .config.gpg }}" > "$HOME/.gnupg/gpg.conf"
chmod 600 "$HOME/.gnupg/gpg.conf"
fi
logg info 'Killing dirmngr instance and reloading daemon with standard-resolver' && sudo pkill dirmngr
dirmngr --daemon --standard-resolver
KEYID_TRIMMED="$(echo "$KEYID" | sed 's/^0x//')"
if ! gpg --list-secret-keys --keyid-format=long | grep "$KEYID_TRIMMED" > /dev/null; then
if [ -f "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/home/private_dot_gnupg/public/${KEYID}.asc" ]; then
logg info "Importing GPG key stored in ${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/home/private_dot_gnupg/public/${KEYID}.asc since its name matches the GPG key ID in .chezmoi.yaml.tmpl"
gpg --import "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/home/private_dot_gnupg/public/${KEYID}.asc" && logg success 'Successfully imported master GPG key'
else
logg info 'Attempting to download the specified public GPG key ({{ .user.gpg.id }}) from public keyservers'
gpg --keyserver https://pgp.mit.edu --recv "$KEYID" || EXIT_CODE=$?
if [ -n "$EXIT_CODE" ]; then
logg info 'Non-zero exit code received when downloading public GPG key'
gpg --keyserver hkps://pgp.mit.edu --recv "$KEYID" || EXIT_CODE=$?
if [ -n "$EXIT_CODE" ]; then
logg info 'Non-zero exit code received when trying to retrieve public user GPG key on hkps://pgp.mit.edu'
else
logg success 'Successfully imported configured public user GPG key'
fi
fi
fi
else
logg info 'Key is already in keyring'
fi
logg info 'Stopping dirmngr'
gpgconf --kill dirmngr && logg info 'Stopped dirmngr' || info warn 'Failed to stop dirmngr'
logg 'Ensuring the trust of the provided public GPG key is set to maximum'
echo -e "trust\n5\ny" | gpg --command-fd 0 --edit-key "$KEYID"
else
logg warn 'gpg appears to be unavailable. Is it installed and on the PATH?'
fi
}
# @description Disable the creation of `.DS_Store` files on macOS.
disableDStoreFileCreation() {
if command -v m > /dev/null; then
logg info 'Disabling creation of .DS_Store files'
echo y | m dir dsfiles off
fi
}
# @description Enables transparent dark-mode on macOS
enableDarkTransparentMode() {
if command -v m > /dev/null; then
logg info 'Enabling dark mode' && m appearance darkmode YES
logg info 'Enabling theme transparency' && m appearance transparency YES
fi
}
# @description Helper function for installing Homebrew packages that have a matching package name and binary executable name.
ensureBrewPackageInstalled() {
if ! command -v "$1" > /dev/null; then
if command -v brew; then
logg info "Installing $1 via Homebrew"
brew install --quiet "$1" || EXIT_CODE=$?
if [ -n "$EXIT_CODE" ]; then
logg error "$1 was not successfully installed via Homebrew"
unset EXIT_CODE
fi
else
logg error "brew is unavailable. Cannot use it to perform installation of $1"
fi
else
logg info "$1 is already installed"
fi
}
# @description Ensure delta is installed via Homebrew
ensureDeltaInstalled() {
if ! command -v delta > /dev/null; then
if command -v brew; then
logg 'Installing delta via Homebrew'
brew install --quiet git-delta || DELTA_EXIT_CODE=$?
if [ -n "$DELTA_EXIT_CODE" ]; then
logg error 'git-delta was not successfully installed via Homebrew'
fi
else
logg 'brew is unavailable. Cannot use it to perform a system installation of node.'
fi
else
logg 'delta is available'
fi
}
# @description Ensure Node is installed via Homebrew
ensureNodeInstalled() {
### Ensure node is installed
if ! command -v node > /dev/null; then
if command -v brew; then
logg 'Installing node via Homebrew'
brew install --quiet node || NODE_EXIT_CODE=$?
if [ -n "$NODE_EXIT_CODE" ]; then
logg warn 'Calling brew link --overwrite node because the Node.js installation seems to be misconfigured'
brew link --overwrite node
fi
else
logg 'brew is unavailable. Cannot use it to perform a system installation of node.'
fi
else
logg 'node is available'
fi
}
# @description
# This script ensures that there is a group with the same name of the provisioning user available on the system.
ensureUserGroup() {
if [ "{{ .host.distro.family }}" = "darwin" ]; then
if [ -n "$USER" ]; then
logg info "Adding the $USER user to the $USER group"
### Ensure user has group of same name (required for Macports)
logg info "Ensuring user ($USER) has a group with the same name ($USER) and that it is a member. Sudo privileges may be required"
GROUP="$USER"
USERNAME="$USER"
### Add group
sudo dscl . create /Groups/$GROUP
### Add GroupID to group
if [[ "$(sudo dscl . read /Groups/$GROUP gid 2>&1)" == *'No such key'* ]]; then
MAX_ID_GROUP="$(dscl . -list /Groups gid | awk '{print $2}' | sort -ug | tail -1)"
GROUP_ID="$((MAX_ID_GROUP+1))"
sudo dscl . create /Groups/$GROUP gid "$GROUP_ID"
fi
### Add user
sudo dscl . create /Users/$USERNAME
### Add PrimaryGroupID to user
if [[ "$(sudo dscl . read /Users/$USERNAME PrimaryGroupID 2>&1)" == *'No such key'* ]]; then
sudo dscl . create /Users/$USERNAME PrimaryGroupID 20
fi
### Add UniqueID to user
if [[ "$(sudo dscl . read /Users/$USERNAME UniqueID 2>&1)" == *'No such key'* ]]; then
MAX_ID_USER="$(dscl . -list /Users UniqueID | awk '{print $2}' | sort -ug | tail -1)"
USER_ID="$((MAX_ID_USER+1))"
sudo dscl . create /Users/$USERNAME UniqueID "$USERID"
fi
### Add user to group
sudo dseditgroup -o edit -t user -a $USERNAME $GROUP
else
logg warn 'The USER environment variable is unavailable'
fi
fi
}
# @description Increases the amount of memory a process can consume on Linux. In the case of `netdata` and other programs, many systems will suggest
# increasing the `vm.max_map_count`. According to a [RedHat article](https://access.redhat.com/solutions/99913), the default value is `65530`.
# This function increases that value to `262144` if `sysctl` is available on the system.
increaseMapCount() {
if [ ! -d /Applications ] && [ ! -d /System ]; then
### Linux
if command -v sysctl > /dev/null; then
logg info 'Increasing vm.max_map_count size to 262144'
sudo sysctl -w vm.max_map_count=262144 > /dev/null
fi
fi
}
# @description Helper function for installDocker that installs pre-built gVisor using method recommended on official website
function gVisorPreBuilt() {
logg info 'Installing gVisor using method recommended on official website'
set -e
mkdir /tmp/gvisor && cd /tmp/gvisor
ARCH=$(uname -m)
URL="https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}"
logg info 'Downloading gVisor runsc and containerd-shim-runsc-v1 SHA signatures'
wget "${URL}/runsc ${URL}/runsc.sha512" "${URL}/containerd-shim-runsc-v1 ${URL}/containerd-shim-runsc-v1.sha512"
sha512sum -c runsc.sha512 -c containerd-shim-runsc-v1.sha512
rm -f *.sha512
chmod a+rx runsc containerd-shim-runsc-v1
sudo mv runsc containerd-shim-runsc-v1 /usr/local/bin
}
# @description Helper function for installDocker that installs gVisor using alternate Go method described on the GitHub page
function gVisorGo() {
# Official build timed out - use Go method
logg info 'Installing gVisor using the Go fallback method'
sudo chown -Rf "$(whoami)" /usr/local/src/gvisor
cd /usr/local/src/gvisor
echo "module runsc" > go.mod
GO111MODULE=on go get gvisor.dev/gvisor/runsc@go
CGO_ENABLED=0 GO111MODULE=on sudo -E go build -o /usr/local/bin/runsc gvisor.dev/gvisor/runsc
GO111MODULE=on sudo -E go build -o /usr/local/bin/containerd-shim-runsc-v1 gvisor.dev/gvisor/shim
}
# @description Helper function for installDocker that installs gVisor using the [GitHub developer page method](https://github.com/google/gvisor#installing-from-source). This method requires Docker to be installed
function gVisorSource() {
### Ensure sources are cloned / up-to-date
logg info 'Building gVisor from source'
if [ -d /usr/local/src/gvisor ]; then
cd /usr/local/src/gvisor
sudo git reset --hard HEAD
sudo git clean -fxd
sudo git pull origin master
else
sudo git clone https://github.com/google/gvisor.git /usr/local/src/gvisor
fi
### Build gVisor
cd /usr/local/src/gvisor
sudo mkdir -p bin
# Wait 5 minutes for build to finish, and if it does not use Go
# TODO - Generate container-shim-runsc-v1 as well (low priority since this method is not used and is only recommended for development)
sudo timeout 600 make copy TARGETS=runsc DESTINATION=bin/
if [ -f ./bin/runsc ]; then
sudo cp ./bin/runsc /usr/local/bin
else
logg error 'Timed out while building runsc from source (10 minutes)' && exit 6
fi
}
# @description Helper function for installDocker that Installs systemsecret credential helper for Linux
function installCredentialSecretService() {
curl -sSL https://github.com/docker/docker-credential-helpers/releases/download/v0.7.0/docker-credential-secretservice-v0.7.0.linux-amd64 > /tmp/docker-credential-secretservice
sudo mv /tmp/docker-credential-secretservice /usr/local/bin/docker-credential-secretservice
}
# @description
# This script ensures Docker is installed and then adds the provisioning user to the `docker` group so that they can
# access Docker without `sudo`. It also installs and configures gVisor for use with Docker.
#
# ## gVisor
#
# gVisor is included with our Docker setup because it improves the security of Docker. gVisor is an application kernel, written in Go,
# that implements a substantial portion of the Linux system call interface. It provides an additional layer of isolation between running
# applications and the host operating system. It has gained a lot of attention, perhaps partly, because it is maintained by Google.
installDocker() {
### Ensures `~/.config/docker` is symlinked to `~/.docker` which is required for Docker Desktop compatibility since it currently does not honor XDG spec. This will
### remove the current configuration at `~/.docker` if it is present and not symlinked to `~/.config/docker`.
if [ "$(readlink -f "$HOME/.docker")" != "${XDG_CONFIG_HOME:-$HOME/.config}/docker" ]; then
logg info 'Removing ~/.docker if present' && rm -rf "$HOME/.docker"
logg info 'Ensuring ~/.config/docker exists' && mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/docker"
logg info 'Symlinking ~/.config/docker to ~/.docker for Docker Desktop compatibility' && ln -s "${XDG_CONFIG_HOME:-$HOME/.config}/docker" "$HOME/.docker"
else
logg info 'Symlink from ~/.config/docker to ~/.docker is already present'
fi
### Install Docker
if [ -d /Applications ] && [ -d /System ]; then
### macOS
if [ ! -d /Applications/Docker.app ]; then
logg info 'Installing Docker on macOS via Homebrew cask'
brew install --cask --quiet --no-quarantine docker
else
logg info 'Docker appears to be installed already'
fi
logg info 'Opening the Docker for Desktop app so that the Docker engine starts running'
# TODO - --install-privileged-components may be necessary for `docker extension` command but it causes the command to no longer work
# open --background -a Docker --args --accept-license --unattended --install-privileged-components
open --background -a Docker --args --accept-license --unattended
elif command -v apt-get > /dev/null; then
. /etc/os-release
if [ "$ID" == 'ubuntu' ]; then
logg info 'Installing Docker on Ubuntu'
else
logg info 'Installing Docker on Debian'
fi
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release
sudo mkdir -p /etc/apt/keyrings
curl -fsSL "https://download.docker.com/linux/$ID/gpg" | sudo gpg --dearmor --yes -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$ID $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
elif command -v dnf > /dev/null; then
. /etc/os-release
if [ "$ID" == 'centos' ]; then
logg info 'Installing Docker on CentOS'
elif [ "$ID" == 'fedora' ]; then
logg info 'Installing Docker on Fedora'
else
logg error 'Unknown OS - cannot install Docker' && exit 1
fi
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo "https://download.docker.com/linux/$ID/docker-ce.repo"
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
elif command -v yum > /dev/null; then
# CentOS
logg info 'Installing Docker on CentOS'
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
elif command -v apk > /dev/null; then
# Alpine
logg info 'Installing Docker on Alpine'
sudo apk add --update docker
elif command -v pacman > /dev/null; then
# Archlinux
logg info 'Installing Docker on Archlinux'
sudo pacman -Syu
sudo pacman -S docker
elif command -v zypper > /dev/null; then
# OpenSUSE
logg info 'Installing Docker on OpenSUSE'
sudo zypper addrepo https://download.docker.com/linux/sles/docker-ce.repo
sudo zypper install docker-ce docker-ce-cli containerd.io docker-compose-plugin
fi
### Add Docker group on Linux
if command -v groupadd > /dev/null; then
# Linux
if ! cat /etc/group | grep docker > /dev/null; then
logg info 'Creating Docker group'
sudo groupadd docker
fi
logg info 'Adding user to Docker group'
sudo usermod -aG docker "$USER"
fi
### Boot Docker on start with systemd on Linux machines
if command -v systemctl > /dev/null; then
# Systemd Linux
sudo systemctl start docker.service
sudo systemctl enable docker.service
sudo systemctl enable containerd.service
fi
### Add gVisor
if [ ! -d /Applications ] || [ ! -d /System ]; then
### Linux
if ! command -v docker-credential-secretservice > /dev/null; then
installCredentialSecretService
fi
if ! command -v runsc > /dev/null; then
### Install gVisor
gVisorPreBuilt || PRE_BUILT_EXIT_CODE=$?
if [ -n "$PRE_BUILT_EXIT_CODE" ]; then
logg warn 'gVisor failed to install using the pre-built method'
gVisorGo || GO_METHOD_EXIT_CODE=$?
if [ -n "$GO_METHOD_EXIT_CODE" ]; then
logg warn 'gVisor failed to install using the Go fallback method'
gVisorSource || SOURCE_EXIT_CODE=$?
if [ -n "$SOURCE_EXIT_CODE" ]; then
logg error 'All gVisor installation methods failed' && exit 1
else
logg success 'gVisor installed via source'
fi
else
logg success 'gVisor installed via Go fallback method'
fi
else
logg success 'gVisor installed from pre-built Google-provided binaries'
fi
else
logg info 'runsc is installed'
fi
### Ensure Docker is configured to use runsc
if [ ! -f /etc/docker/daemon.json ]; then
### Configure Docker to use gVisor
### Create /etc/docker/daemon.json
logg info 'Creating /etc/docker'
sudo mkdir -p /etc/docker
if [ -f "${XDG_DATA_HOME:-$HOME/.local/share}/home/dot_config/docker/daemon.json" ]; then
logg info 'Creating /etc/docker/daemon.json'
sudo cp -f "${XDG_DATA_HOME:-$HOME/.local/share}/home/dot_config/docker/daemon.json" /etc/docker/daemon.json
else
logg warn "${XDG_DATA_HOME:-$HOME/.local/share}/home/dot_config/docker/daemon.json is not available so the /etc/docker/daemon.json file cannot be populated"
fi
### Restart / enable Docker
if [[ ! "$(test -d /proc && grep Microsoft /proc/version > /dev/null)" ]] && command -v systemctl > /dev/null; then
logg info 'Restarting Docker service'
sudo systemctl restart docker.service
sudo systemctl restart containerd.service
fi
### Test Docker /w runsc
logg info 'Testing that Docker can load application with runsc'
docker run --rm --runtime=runsc hello-world || RUNSC_EXIT_CODE=$?
if [ -n "$RUNSC_EXIT_CODE" ]; then
logg error 'Failed to run the Docker hello-world container with runsc' && exit 5
else
logg info 'Docker successfully ran the hello-world container with runsc'
fi
fi
fi
}
# @description
# This script enrolls the device as a JumpCloud managed asset. The `JUMPCLOUD_CONNECT_KEY` secret should
# be populated using one of the methods described in the [Secrets documentation](https://install.doctor/docs/customization/secrets).
#
# *Note: You should check out the supported systems before trying to enroll devices.*
#
# ## JumpCloud on macOS
#
# macOS offers a native device management feature offered through Apple Business. It is the preferred
# method since it offers most of the desirable features (like remote wipe). The [JumpCloud MDM documentation](https://support.jumpcloud.com/support/s/article/Getting-Started-MDM)
# details the steps required to register macOS MDM profiles with JumpCloud.
#
# ## Links
#
# * [JumpCloud device management requirements](https://support.jumpcloud.com/support/s/article/jumpcloud-agent-compatibility-system-requirements-and-impacts1)
installJumpCloud() {
if [ "{{ .host.distro.family }}" = "linux" ]; then
if [ "{{ if (stat (joinPath .chezmoi.sourceDir ".chezmoitemplates" "secrets" "JUMPCLOUD_CONNECT_KEY")) }}{{- includeTemplate "secrets/JUMPCLOUD_CONNECT_KEY" | decrypt | trim -}}{{ else }}{{- env "JUMPCLOUD_CONNECT_KEY" -}}{{ end }}" != "" ]; then
logg info 'Enrolling device with JumpCloud by running the kickstart script'
curl --tlsv1.2 --silent --show-error --header 'x-connect-key: {{ if (stat (joinPath .chezmoi.sourceDir ".chezmoitemplates" "secrets" "JUMPCLOUD_CONNECT_KEY")) }}{{- includeTemplate "secrets/JUMPCLOUD_CONNECT_KEY" | decrypt | trim -}}{{ else }}{{- env "JUMPCLOUD_CONNECT_KEY" -}}{{ end }}' https://kickstart.jumpcloud.com/Kickstart | sudo bash
fi
fi
}
# @description
# This script removes some of the software deemed to be "bloatware" by cycling through the values defined in
# `.removeLinuxPackages` of the `home/.chezmoidata.yaml` file.
removeLinuxBloatware() {
if [ "{{ .host.distro.family }}" = "linux" ]; then
{{- $removePackages := join " " .removeLinuxPackages }}
### Remove bloatware packages defined in .chezmoidata.yaml
for PKG in {{ $removePackages }}; do
if command -v apk > /dev/null; then
if apk list "$PKG" | grep "$PKG" > /dev/null; then
sudo apk delete "$PKG"
fi
elif command -v apt-get > /dev/null; then
if dpkg -l "$PKG" | grep -E '^ii' > /dev/null; then
sudo apt-get remove -y "$PKG"
logg success 'Removed '"$PKG"' via apt-get'
fi
elif command -v dnf > /dev/null; then
if rpm -qa | grep "$PKG" > /dev/null; then
sudo dnf remove -y "$PKG"
logg success 'Removed '"$PKG"' via dnf'
fi
elif command -v yum > /dev/null; then
if rpm -qa | grep "$PKG" > /dev/null; then
sudo yum remove -y "$PKG"
logg success 'Removed '"$PKG"' via yum'
fi
elif command -v pacman > /dev/null; then
if pacman -Qs "$PKG" > /dev/null; then
sudo pacman -R "$PKG"
logg success 'Removed '"$PKG"' via pacman'
fi
elif command -v zypper > /dev/null; then
if rpm -qa | grep "$PKG" > /dev/null; then
sudo zypper remove -y "$PKG"
logg success 'Removed '"$PKG"' via zypper'
fi
fi
done
fi
}
# @description Sets the hostname using `scutil` on macOS and using `hostname` and `hostnamectl` on Linux. On macOS, the HostName, LocalHostName, and ComputerName
# are set equal to the value stored in `.host.hostname` (in `.chezmoi.yaml.tmpl`) but with the `.host.domain` stripped off. On Linux, the same is done
# but only the hostname is set. On Linux, the hostname is set with the `hostname` command and then also with the `hostnamectl` command if it is available.
#
# ## Sources
#
# * [Changing Linux hostname permanently](https://www.tecmint.com/set-hostname-permanently-in-linux/)
setHostname() {
if [ -d /Applications ] && [ -d /System ]; then
# Source: https://apple.stackexchange.com/questions/287760/set-the-hostname-computer-name-for-macos
logg info 'Setting macOS hostname / local hostname / computer name'
sudo scutil --set HostName '{{ .host.hostname | replace .host.domain "" | replace "." "" }}.{{ .host.domain }}' && logg success 'Changed HostName to {{ .host.hostname | replace .host.domain "" | replace "." "" }}.{{ .host.domain }}'
sudo scutil --set LocalHostName '{{ .host.hostname | replace .host.domain "" | replace "." "" }}' && logg success 'Changed LocalHostName to {{ .host.hostname | replace .host.domain "" | replace "." "" }}'
sudo scutil --set ComputerName '{{ .host.hostname | replace .host.domain "" | replace "." "" }}' && logg success 'Changed ComputerName to {{ .host.hostname | replace .host.domain "" | replace "." "" }}'
logg info 'Flushing DNS cache'
dscacheutil -flushcache
elif [ -f /etc/passwd ]; then
logg info 'Setting Linux hostname'
hostname '{{ .host.hostname | replace .host.domain "" | replace "." "" }}.{{ .host.domain }}' && logg success 'Changed hostname to {{ .host.hostname | replace .host.domain "" | replace "." "" }}.{{ .host.domain }}'
if command -v hostnamectl > /dev/null; then
logg info 'Ensuring hostname persists after reboot'
sudo hostnamectl set-hostname '{{ .host.hostname | replace .host.domain "" | replace "." "" }}.{{ .host.domain }}' && logg success 'Permanently changed hostname to {{ .host.hostname | replace .host.domain "" | replace "." "" }}.{{ .host.domain }}'
else
logg warn 'hostnamectl was not available in the PATH - this operating system type might be unsupported'
fi
else
logg warn 'Could not configure hostname because system type was not detectable'
fi
}
# @description Sets the NTP server using `m` on macOS
setNtpServer() {
if command -v m > /dev/null; then
### macOS
m ntp set {{ .user.ntpServer }}
else
logg warn 'Skipped setting the NTP server'
fi
}
# @description Sets the system timezone using `timedatectl` on Linux and `m` on macOS. If neither commands are available
# then a warning message is printed.
setTimezone() {
if command -v timedatectl > /dev/null; then
### Linux
logg info 'Setting timezone to {{ .user.timezone }}'
sudo timedatectl set-timezone {{ .user.timezone }}
elif command -v m > /dev/null; then
### macOS
logg info 'Setting timezone to {{ .user.timezone }}' && m timezone set {{ .user.timezone }}
else
logg warn 'Neither timedatectl (Linux) or m (macOS) were found on the system'
fi
}
# @description Configures macOS to enable the notification center
showNotificationCenter() {
if command -v m > /dev/null; then
logg info 'Configuring macOS to show notification center' && m notification showcenter YES
fi
}
installBrewPackages() {
ensureNodeInstalled
ensureDeltaInstalled
ensureBrewPackageInstalled "gh"
ensureBrewPackageInstalled "go"
ensureBrewPackageInstalled "zx"
ensureBrewPackageInstalled "volta"
volta install node@latest &
npm install -g npm@latest &
wait
logg success 'Finished installing auxilary Homebrew packages'
}
if [ -n "$DEBUG" ] || [ -n "$DEBUG_MODE" ]; then
logg info 'The DEBUG or DEBUG_MODE environment variable is set so preliminary system tweaks will be run synchronously'
allocateSwap
configureGPG
disableDStoreFileCreation
enableDarkTransparentMode
ensureUserGroup
increaseMapCount
installBrewPackages
installDocker
installJumpCloud
removeLinuxBloatware
setHostname
setNtpServer
setTimezone
showNotificationCenter
else
allocateSwap &
configureGPG &
disableDStoreFileCreation &
enableDarkTransparentMode &
ensureUserGroup &
increaseMapCount &
installBrewPackages &
installDocker &
installJumpCloud &
removeLinuxBloatware &
setHostname &
setNtpServer &
setTimezone &
showNotificationCenter &
wait
fi
logg success 'Successfully applied preliminary system tweaks'