Skip to content

Instantly share code, notes, and snippets.

@benfairless
Last active March 7, 2024 20:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save benfairless/a7aed788854e89c1459b to your computer and use it in GitHub Desktop.
Save benfairless/a7aed788854e89c1459b to your computer and use it in GitHub Desktop.
Fedora development environment setup
# yaml-language-server: $schema=https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json
final_space: true
version: 2
blocks:
- type: prompt
alignment: left
segments:
- type: os
style: diamond
leading_diamond: "\ue0b6"
foreground: '#ffffff'
background: '#6272a4'
- type: session
style: accordion
foreground: "#ffffff"
background: "#6272a4"
template: "{{ if .SSHSession }} {{ .HostName }} {{ end }}"
- type: path
style: powerline
powerline_symbol: "\ue0b0"
foreground: "#ffffff"
background: "#bd93f9"
template: " {{ .Path }} "
properties:
style: folder
mapped_locations:
'~': ''
~/Desktop: '󰇄'
~/Documents: '󰲂'
~/Downloads: '󰉍'
~/Pictures: '󰉏'
~/Public: '󰡰'
~/Music: '󱍙'
~/Videos: '󰚝'
~/Templates: '󱋣'
- type: git
style: powerline
powerline_symbol: "\ue0b0"
foreground: "#ffffff"
background: "#ffb86c"
template: " ➜ ({{ .UpstreamIcon }}{{ .HEAD }}{{ if gt .StashCount 0 }} \uf692 {{ .StashCount }}{{ end }}) "
properties:
branch_icon: ""
fetch_stash_count: true
fetch_status: false
fetch_upstream_icon: true
- type: node
style: powerline
powerline_symbol: "\ue0b0"
foreground: "#ffffff"
background: "#8be9fd"
template: " \ue718 {{ if .PackageManagerIcon }}{{ .PackageManagerIcon }} {{ end }}{{ .Full }} "
- type: time
style: diamond
foreground: "#ffffff"
background: "#ff79c6"
trailing_diamond: "\ue0b0"
template: " {{ .CurrentDate | date .Format }} "
properties:
time_format: "15:04"
#!/usr/bin/env bash
# Sets up a workstation environment with all the tools necessary to get crackin!
NAME='Fedora development setup'
VERSION='0.5.5'
AUTHOR='Ben Fairless'
################################################################################
#################################### CHECKS ####################################
################################################################################
# Check yo' privilege!
if [[ $(id -u) == 0 ]]; then
echo "${0} should be run by a standard user account (non-root)."
echo "When privilege escalation is necessary you will be prompted."
exit 1
fi
# Check that the system is actually a Fedora release
if [[ $(cut -f 1 -d ' ' < '/etc/redhat-release') != 'Fedora' ]]; then
echo "${0} is only available for Fedora distributions!"
exit 1
fi
################################################################################
#################################### SETUP #####################################
################################################################################
# Variables
FEDORA_VERSION=$(rpm -E %fedora)
WORKING_DIR="/tmp/setup"
DOWNLOADS_DIR="${WORKING_DIR}/downloads"
CONFIG_DIR="${HOME}/.config"
TICK="\033[32m✓\033[0m"
# File creation
[[ ! -d "${WORKING_DIR}" ]] && mkdir -p "${WORKING_DIR}"
[[ ! -d "${DOWNLOADS_DIR}" ]] && mkdir -p "${DOWNLOADS_DIR}"
[[ ! -d "${CONFIG_DIR}" ]] && mkdir -p "${CONFIG_DIR}"
# Cleanup
cleanup() {
rm -rf "${WORKING_DIR}"
}
################################################################################
############################## GENERAL FUNCTIONS ###############################
################################################################################
# Sets up DNF for unattended installs
dnf() { sudo /usr/bin/dnf -e 0 -y -q "$@" 2>/dev/null; }
# Allow escape characters in echo
echo() { /usr/bin/echo -e "$@"; }
# Provides pretty colourful output with timestamps
output() {
local TIMESTAMP=$(date +%H:%M)
local LABEL='SETUP'
local COLOUR='\033[34m' # Blue
local RESET='\033[0m' # Standard
case ${1} in
ERROR) local COLOUR='\033[31m' ;; # Red
SUCCESS) local COLOUR='\033[32m' ;; # Green
WARN) local COLOUR='\033[33m' ;; # Yellow
*) ;;
esac
while read -r LINE; do
echo "${COLOUR}${LABEL} [${TIMESTAMP}]${RESET} ${LINE}"
done
}
# Produces bold output
say() {
echo "$(tput bold)$@$(tput sgr0)"
}
# Checks that the previous command exited correctly and prints a pretty error
# message if so. Optionally a second string can be provided to print on success.
onfail() {
if [[ ${?} != 0 ]]; then
echo "${1}" | output ERROR
elif [[ -n ${2} ]]; then
echo "${2}" | output
fi
}
# Creates a copy of $1 at $1.old
backup() {
local FILE=${1}
[[ -z ${FILE} ]] && echo 'backup() - No file variable set' | output ERROR && kill -INT $$
if [[ -f ${FILE} ]]; then
echo "Backing up ${FILE}..." | output
RESULT=$(cp -f "${FILE}" "${FILE}.old" 2>&1)
if [[ ${?} != 0 ]]; then
echo "Failed to backup ${FILE}!" | output WARN
echo "${RESULT}" | output WARN
else
echo "${FILE} backup created successfully" | output
fi
fi
}
# Creates here documents, with error handling and backing up of the previous version.
createfile() {
local FILE=${1}
[[ -z ${FILE} ]] && echo 'createfile() - No file variable set' | output ERROR && kill -INT $$
sudo rm -rf "${FILE}" # Delete the file if at already exists
while read -r LINE; do
# Add each line of STDIN to $CONTENT, along with a line break
local CONTENT+="${LINE}\n"
done
RESULT=$(echo -e "${CONTENT}" > "${FILE}" 2>&1)
if [[ $? != 0 ]]; then
echo "Failed to create ${FILE}" | output ERROR
echo "${RESULT}" | output ERROR
exit 1
fi
}
# Download remote file
download() {
local FILE=${1}
local URI=${2}
[[ -z ${FILE} ]] && echo 'download() - No file variable set' | output ERROR && kill -INT $$
[[ -z ${URI} ]] && echo 'download() - No uri variable set' | output ERROR && kill -INT $$
if [[ ! -f "${FILE}" ]]; then
wget -q --user-agent=Mozilla --content-disposition -E -c -O "${FILE}" "${URI}" 2>/dev/null
onfail "Failed to download ${URI}" && kill -INT $$
fi
}
# Installs an RPM from a remote URL
remoteinstall() {
local PKG=${1}
local URI=${2}
local FILE="${DOWNLOADS_DIR}/${PKG}.rpm"
[[ -z ${PKG} ]] && echo 'remoteinstall() - No package variable set' | output ERROR && kill -INT $$
[[ -z ${URI} ]] && echo 'remoteinstall() - No uri variable set' | output ERROR && kill -INT $$
if [[ $(rpm -q "${PKG}" | wc -l) == '0' ]]; then
echo "Installing ${PKG}" | output
download "${FILE}" "${URI}"
dnf install "${FILE}"
rm -f "${FILE}"
fi
}
# Installs a repofile from a remote URL
repoinstall() {
local REPO=${1}
local URI=${2}
[[ -z ${REPO} ]] && echo 'repoinstall() - No repo variable set' | output ERROR && kill -INT $$
[[ -z ${URI} ]] && echo 'repoinstall() - No uri variable set' | output ERROR && kill -INT $$
local LOCATION="/etc/yum.repos.d/${REPO}.repo"
if [[ ! -f ${LOCATION} ]]; then
echo "Installing ${REPO} repository file..." | output
sudo curl -L -o "${LOCATION}" "${URI}" >/dev/null 2>&1
sudo chown root:root "${LOCATION}"
sudo restorecon "${LOCATION}"
fi
}
# Check if the system is a virtual machine
vm() {
case $(sudo dmidecode -s system-product-name) in
'VMware Virtual Platform') return 0;;
'VirtualBox') return 0;;
'KVM') return 0;;
*) return 1;;
esac
}
# Check hardware type
hardware() {
case $(sudo dmidecode -s system-product-name) in
'Surface Pro 4') echo 'surface';;
*) echo 'unknown';;
esac
}
################################################################################
############################## SPECIFIC INSTALLS ###############################
################################################################################
# Information about the install
setupStart() {
say "${NAME} v${VERSION} - ${AUTHOR}" | output
cat <<INTRO | output
Automatic configuration for your Fedora ${FEDORA_VERSION} workstation
The following will be installed and configured where appropriate:
- Sudo privledges
- RPM Fusion repositories
- Development tools
- Desktop tools
- Atom text editor
- Gnome customisations
- Firefox addons
- Docker & Kubernetes tools
- Cockpit managment interface
INTRO
# Surface-specific items
if [[ $(hardware) == 'surface' ]]; then
cat <<SURFACE | output
- Surface Pro tweaks
SURFACE
fi
if [[ vm == 1 ]]; then
cat <<HARDWARE | output
- Hashicorp Vagrant
HARDWARE
fi
}
# Set up passwordless sudo
setupSudo() {
local USER=$(whoami)
local FILE="/etc/sudoers.d/${USER}"
local TEMP="${WORKING_DIR}/sudo-${USER}.cfg"
if [[ $(sudo cat "${FILE}" 2>/dev/null) == '' ]]; then
echo "Reconfiguring sudo privledges for ${USER}..." | output
createfile "${TEMP}" <<SUDO
## Manage Sudo privledges for ${USER}
# Allow non-password sudo
${USER} ALL=(ALL) NOPASSWD: ALL
# Allow non-interactive sessions to use sudo privledges
Defaults:${USER} !requiretty
SUDO
sudo chown root:root "${TEMP}"
visudo -c -f "${TEMP}" >/dev/null 2>&1
onfail "Sudoers file contains errors. Falling back" && exit 1
echo "Installing sudoers file to ${FILE}..." | output
sudo mv "${TEMP}" "${FILE}"
sudo restorecon "${FILE}"
fi
echo "Sudo configuration ${TICK}" | output SUCCESS
}
# Install RPM Fusion
setupRPMfusion() {
remoteinstall "rpmfusion-free-release-${FEDORA_VERSION}" \
"http://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-${FEDORA_VERSION}.noarch.rpm"
remoteinstall "rpmfusion-nonfree-release-${FEDORA_VERSION}" \
"http://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-${FEDORA_VERSION}.noarch.rpm"
echo "RPM Fusion configuration ${TICK}" | output SUCCESS
}
# Update system packages
setupUpdate() {
dnf update
echo "System update ${TICK}" | output SUCCESS
}
# Install that stuff you always need
setupEssentials() {
dnf install \
kernel-headers kernel-devel dkms \
python3-dnf-plugins-extras-migrate \
python3-dnf-plugins-leaves \
python3-dnf-plugins-versionlock \
p7zip p7zip-plugins unzip unrar \
deltarpm kernel-tools
echo "Essential packages ${TICK}" | output SUCCESS
}
# Installs tools useful for development
setupDevelopmentTools() {
dnf groupinstall 'Development Tools'
dnf install \
gcc gcc-c++ openssl openssl-devel libxml-devel libcurl-devel \
libvirt libvirt-client libvirt-devel rpm-sign rpm-build \
ruby ruby-devel python3 python3-devel
echo "Development tools ${TICK}" | output SUCCESS
}
# Installs desktop GUI tools
setupDesktopTools() {
dnf install \
meld
echo "Desktop tools ${TICK}" | output SUCCESS
dnf remove \
simple-scan gnome-tour gnome-contacts gnome-calendar totem \
libreoffice-calc libreoffice-impress libreoffice-writer
}
# Gnome tweaks
setupGnome(){
# Necessary packages
dnf install gnome-tweaks gnome-extensions-app \
gtk2-engines gtk-murrine-engine gtk-unico-engine \
gnome-shell-extension-user-theme \
gnome-shell-extension-background-logo \
gnome-shell-extension-caffeine \
gnome-shell-extension-blur-my-shell \
gnome-shell-extension-dash-to-dock \
gnome-shell-extension-no-overview \
gnome-shell-extension-places-menu
# Simple tweaks
#gsettings set org.gnome.nautilus.icon-view captions "['size', 'none', 'none']"
#gsettings set org.gnome.nautilus.icon-view default-zoom-level 'small'
gsettings set org.gnome.Terminal.Legacy.Settings default-show-menubar false
gsettings set org.gnome.desktop.privacy send-software-usage-stats 'false'
# Enable extensions
gnome-extensions enable user-theme@gnome-shell-extensions.gcampax.github.com
gnome-extensions enable background-logo@fedorahosted.org
gnome-extensions enable background-logo@fedorahosted.org
gnome-extensions enable dash-to-dock@micxgx.gmail.com
gnome-extensions enable no-overview@fthx
# Create user themes directories
local USER_FONTS="${HOME}/.local/share/fonts"
local USER_THEMES="${HOME}/.local/share/themes"
[[ ! -d "${USER_FONTS}" ]] && mkdir -p "${USER_FONTS}"
[[ ! -d "${USER_THEMES}" ]] && mkdir -p "${USER_THEMES}"
# Themes - Numix
dnf copr enable numix/numix
dnf install numix-icon-theme numix-icon-theme-circle numix-gtk-theme
gsettings set org.gnome.desktop.interface icon-theme 'Numix-Circle'
# Themes - Dracula
# https://draculatheme.com/gtk
local GTK_THEME="${USER_THEMES}/Dracula"
local TMP_THEME="${DOWNLOADS_DIR}/dracula"
if [[ ! -d "${GTK_THEME}" ]]; then
download "${TMP_THEME}.zip" https://github.com/dracula/gtk/archive/master.zip
unzip -qou "${TMP_THEME}" -d "${TMP_THEME}"
mv -fu "${TMP_THEME}/gtk-master" "${GTK_THEME}"
fi
# Fix for GTK 4.0
mkdir -p "${CONFIG_DIR}/gtk-4.0"
ln -sf "${GTK_THEME}/gtk-4.0/gtk.css" "${CONFIG_DIR}/gtk-4.0/gtk.css"
ln -sf "${GTK_THEME}/gtk-4.0/gtk-dark.css" "${CONFIG_DIR}/gtk-4.0/gtk-dark.css"
ln -sf "${GTK_THEME}/gtk-4.0/assets" "${CONFIG_DIR}/gtk-4.0/assets"
ln -sf "${GTK_THEME}/assets" "${CONFIG_DIR}/assets"
# Apply theme
gsettings set org.gnome.desktop.interface gtk-theme "Dracula"
gsettings set org.gnome.desktop.wm.preferences theme "Dracula"
# Dracula - Terminal
# https://draculatheme.com/gnome-terminal
local TERM_THEME="${DOWNLOADS_DIR}/gnome-terminal"
local TERMINAL=$(dconf list /org/gnome/terminal/legacy/profiles:/ | grep ^: | sed 's/\///g' | head -n 1)
download "${TERM_THEME}.zip" 'https://github.com/dracula/gnome-terminal/archive/refs/heads/master.zip'
unzip -qou "${TERM_THEME}.zip" -d "${TERM_THEME}"
"${TERM_THEME}/gnome-terminal-master/install.sh" -s 'Dracula' --skip-dircolors -p "${TERMINAL}"
# Download Roboto font set (and NerdFont custom font)
dnf install google-roboto-fonts google-roboto-slab-fonts \
google-roboto-condensed-fonts google-roboto-mono-fonts
if [[ ! -d "${USER_FONTS}/NerdFonts" ]]; then
local FONT="${DOWNLOADS_DIR}/nerd-roboto.zip"
download "${FONT}" 'https://github.com/ryanoasis/nerd-fonts/releases/download/v3.1.1/RobotoMono.zip'
unzip -qu "${FONT}" -d "${USER_FONTS}/NerdFonts/"
rm -f "${FONT}"
fi
# Set fonts
gsettings set org.gnome.desktop.wm.preferences titlebar-font 'Roboto Bold 10'
gsettings set org.gnome.desktop.interface document-font-name 'Roboto Slab Regular 10'
gsettings set org.gnome.desktop.interface font-name 'Roboto Medium 10'
gsettings set org.gnome.desktop.interface monospace-font-name 'RobotoMono Nerd Font 9'
# TODO: Set Terminal font specifically
echo "Gnome customisations ${TICK}" | output SUCCESS
}
# Configure shell
setupShell() {
# Create local bin path
local LOCAL_BIN="${HOME}/.local/bin"
[[ ! -d ${LOCAL_BIN} ]] && mkdir -p "${LOCAL_BIN}"
# Install shell dependencies
dnf install vim-enhanced
# Install dircolors
download "${HOME}/.dircolors" \
https://raw.githubusercontent.com/dracula/dircolors/main/.dircolors
# Install OhMyPosh
OHMYPOSH_PATH="${LOCAL_BIN}/oh-my-posh"
if [[ ! -f "${OHMYPOSH_PATH}" ]]; then
download "${OHMYPOSH_PATH}" \
'https://github.com/JanDeDobbeleer/oh-my-posh/releases/latest/download/posh-linux-amd64'
chmod +x "${OHMYPOSH_PATH}"
fi
# TODO: Add custom config
download "${CONFIG_DIR}/ohmyposh.yml" \
https://gist.githubusercontent.com/benfairless/a7aed788854e89c1459b/raw/ohmyposh.yaml
# Create custom shell-agnostic config
cat <<SHELLCUSTOM > "${CONFIG_DIR}/sync_profilerc"
# Add user bin to PATH
[[ -d '${LOCAL_BIN}' ]] && export PATH="${LOCAL_BIN}:${PATH}"
# OhMyPosh config
[[ -f '${CONFIG_DIR}/ohmyposh.yml' ]] && eval "\$(oh-my-posh init bash --config ${CONFIG_DIR}/ohmyposh.yml)"
# Set manpage/less theme to Dracula
export MANPAGER='/usr/bin/less -s -M +Gg'
export LESS_TERMCAP_mb=$'\e[1;31m'
export LESS_TERMCAP_md=$'\e[1;34m'
export LESS_TERMCAP_so=$'\e[01;45;37m'
export LESS_TERMCAP_us=$'\e[01;36m'
export LESS_TERMCAP_me=$'\e[0m'
export LESS_TERMCAP_se=$'\e[0m'
export LESS_TERMCAP_ue=$'\e[0m'
export GROFF_NO_SGR=1
# Set Vi to ViM mode
alias vi=vim
SHELLCUSTOM
echo "Shell customisations ${TICK}" | output SUCCESS
}
setupVim() {
local THEME_PATH="${HOME}/.vim/pack/themes/start"
[[ ! -d "${THEME_PATH}" ]] && mkdir -p "${THEME_PATH}"
[[ ! -d "${THEME_PATH}/dracula" ]] && \
git clone https://github.com/dracula/vim.git "${THEME_PATH}/dracula"
cat <<VIMRC > "${HOME}/.vimrc"
syntax on
set number
set cursorline
" Code styling
set tabstop=2 softtabstop=2
set smartindent
set smartcase
" General
set mouse=a
set noswapfile
set encoding=utf-8
" Dracula themeing
packadd! dracula
colorscheme dracula
let g:dracula_colorterm = 0
if (has("termguicolors"))
set termguicolors
endif
VIMRC
}
# TODO:
# setupGit() {
# https://draculatheme.com/git
# }
# Configure Firefox
setupFirefox() {
# https://addons.mozilla.org/en-GB/firefox/addon/minimalist-dracula
# local PROFILE_DIR=$(cat ${HOME}/.mozilla/firefox/profiles.ini | sed -n -e 's/^.*Path=//p' | head -n 1)
# local EXTENSION_DIR="${HOME}/.mozilla/firefox/${PROFILE_DIR}/extensions"
# [[ -z ${PROFILE_DIR} ]] && echo "Could not find Firefox profile" | output WARN
# # Install Firefox addons
# local PLUGINS[3]='576580' # uBlock
# for PLUGIN in ${PLUGINS[@]}; do
# local XPI="${DOWNLOADS_DIR}/addon-${PLUGIN}-latest.xpi"
# local TMPDIR="${WORKING_DIR}/ff-${PLUGIN}"
# if [[ ! -f ${XPI} ]]; then
# echo "Downloading Firefox plugin ${PLUGIN}..." | output
# curl -L -o ${XPI} "https://addons.mozilla.org/firefox/downloads/latest/${PLUGIN}/platform:2/addon-${PLUGIN}-latest.xpi?" 2>/dev/null
# fi
# [[ ! -d ${TMPDIR} ]] && mkdir -p ${TMPDIR} && unzip -o ${XPI} -d ${TMPDIR} >/dev/null 2>&1
# # Get Extension ID from installation file
# local EXTENSION_ID=$(grep -m 1 -e em:id ${TMPDIR}/install.rdf | sed -n -e 's/^ *<em:id>\({.*}\)<\/em:id>/\1/p')
# if [[ ! -d "${EXTENSION_DIR}/${EXTENSION_ID}" ]]; then
# mv -f ${TMPDIR} "${EXTENSION_DIR}/${EXTENSION_ID}"
# fi
# done
echo "Firefox configuration ${TICK}" | output SUCCESS
}
# Install VSCode and essential plugins
setupVSCode() {
remoteinstall code \
"https://code.visualstudio.com/sha/download?build=stable&os=linux-rpm-x64"
# Install dependencies
dnf install shellcheck
# Install extensions
local EXTENSIONS=(
# 'mathcale.theme-dracula-refined'
'dracula-theme.theme-dracula'
'fnando.linter'
'timonwong.shellcheck'
'esbenp.prettier-vscode'
'kamikillerto.vscode-colorize'
'oderwat.indent-rainbow'
'wayou.vscode-todo-highlight'
'johnpapa.vscode-peacock' # https://draculatheme.com/peacock-extension
'equinusocio.vsc-material-theme-icons'
)
for EXTENSION in "${EXTENSIONS[@]}"; do
# TODO: Hide error output
code --install-extension "${EXTENSION}" >/dev/null
done
# TODO: Work out about config sync
echo "VSCode configuration ${TICK}" | output SUCCESS
}
# Install Cockpit
setupCockpit() {
dnf install cockpit-ws cockpit-system cockpit-selinux cockpit-packagekit \
cockpit-kdump cockpit-sosreport cockpit-pcp cockpit-navigator \
cockpit-networkmanager cockpit-storaged cockpit-composer cockpit-podman
if [[ vm == 1 ]]; then
dnf install cockpit-machines
fi
echo "Cockpit installed ${TICK}" | output SUCCESS
# sudo systemctl start cockpit.service pmlogger.service
# sudo systemctl enable cockpit.service pmlogger.service > /dev/null
# echo "Cockpit available at http://localhost:9090" | output INFO
}
# Install Vagrant
setupVagrant() {
[[ vm == 0 ]] && return
dnf install vagrant vagrant-lxc vagrant-libvirt vagrant-cachier
echo "Vagrant configuration ${TICK}" | output SUCCESS
}
setupSurface() {
[[ $(hardware) != 'surface' ]] && return 0
dnf config-manager --add-repo=https://pkg.surfacelinux.com/fedora/linux-surface.repo
dnf install --allowerasing kernel-surface kernel-surface-devel iptsd libwacom-surface \
kernel-surface-default-watchdog surface-secureboot surface-control
sudo systemctl enable --now linux-surface-default-watchdog.path
dnf install libcamera libcamera-tools libcamera-gstreamer libcamera-ipa \
pipewire-plugin-libcamera
echo "Surface Pro configuration ${TICK}" | output SUCCESS
}
################################################################################
################################### RUNTIME ####################################
################################################################################
setupStart
setupSudo
setupRPMfusion
setupUpdate
setupEssentials
setupDevelopmentTools
setupDesktopTools
setupGnome
setupShell
setupFirefox
setupVSCode
setupCockpit
setupSurface
# Skip on VM systems
#if [[ vm == 1 ]]; then
#setupVagrant
#fi
cleanup
# https://github.com/Frostbitten-jello/Skeuowaita/tree/main
@benfairless
Copy link
Author

benfairless commented Nov 9, 2015

gsettings set org.gnome.shell favorite-apps "['org.gnome.Nautilus.desktop', 'org.gnome.Terminal.desktop', 'firefox.desktop', 'gnome-system-monitor.desktop', 'atom.desktop']"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment