Created
March 25, 2026 00:27
-
-
Save ara303/328252ad77cf4c945a9bd863c114c774 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # ============================================================================= | |
| # Arch Linux + KDE Plasma — Automated Setup Script | |
| # ============================================================================= | |
| # Run as your normal user (NOT root). sudo will be invoked as needed. | |
| # Usage: bash arch-setup.sh | |
| # ============================================================================= | |
| set -euo pipefail | |
| # ── Colours ────────────────────────────────────────────────────────────────── | |
| RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' | |
| CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m' | |
| info() { echo -e "${CYAN}[INFO]${RESET} $*"; } | |
| success() { echo -e "${GREEN}[OK]${RESET} $*"; } | |
| warn() { echo -e "${YELLOW}[WARN]${RESET} $*"; } | |
| section() { echo -e "\n${BOLD}${GREEN}══ $* ══${RESET}"; } | |
| manual() { echo -e "${YELLOW}[MANUAL]${RESET} $*"; } | |
| # ── Sanity checks ───────────────────────────────────────────────────────────── | |
| if [[ "$EUID" -eq 0 ]]; then | |
| echo -e "${RED}Do not run this script as root. Run it as your normal user.${RESET}" | |
| exit 1 | |
| fi | |
| if ! grep -q "Arch Linux" /etc/os-release 2>/dev/null; then | |
| warn "This script is designed for Arch Linux. Proceed anyway? (y/N)" | |
| read -r ans; [[ "$ans" =~ ^[Yy]$ ]] || exit 1 | |
| fi | |
| # ============================================================================= | |
| # SECTION 1 — System update & AUR helper (paru) | |
| # ============================================================================= | |
| section "System update & AUR helper" | |
| info "Updating system packages..." | |
| sudo pacman -Syu --noconfirm | |
| if ! command -v paru &>/dev/null; then | |
| info "Installing paru (AUR helper)..." | |
| sudo pacman -S --noconfirm --needed base-devel git | |
| TMPDIR=$(mktemp -d) | |
| git clone https://aur.archlinux.org/paru.git "$TMPDIR/paru" | |
| (cd "$TMPDIR/paru" && makepkg -si --noconfirm) | |
| rm -rf "$TMPDIR" | |
| success "paru installed." | |
| else | |
| success "paru already installed." | |
| fi | |
| # ============================================================================= | |
| # SECTION 2 — Install packages via pacman | |
| # ============================================================================= | |
| section "Installing pacman packages" | |
| PACMAN_PACKAGES=( | |
| # Terminal & shell | |
| ghostty | |
| # Code editor | |
| code # Visual Studio Code (OSS build; see note below) | |
| # Communication | |
| discord | |
| # Development — GitHub CLI | |
| github-cli | |
| # Development — PHP 8.5 + common extensions | |
| php | |
| php-fpm | |
| php-gd | |
| php-intl | |
| php-sqlite | |
| php-snmp | |
| php-embed | |
| php-redis | |
| php-cgi | |
| xdebug # Xdebug (packaged for current php) | |
| # Development — MariaDB | |
| mariadb | |
| # Development — composer (useful alongside PHP) | |
| composer | |
| # Fonts & utilities | |
| noto-fonts | |
| ttf-liberation | |
| wget | |
| curl | |
| unzip | |
| jq | |
| git | |
| base-devel | |
| ) | |
| info "Installing pacman packages (this may take a while)..." | |
| sudo pacman -S --noconfirm --needed "${PACMAN_PACKAGES[@]}" | |
| success "pacman packages installed." | |
| # NOTE: 'code' is the open-source VS Code build. If you want the official | |
| # Microsoft binary with the proprietary marketplace, replace 'code' with the | |
| # AUR package 'visual-studio-code-bin' installed in the next section. | |
| # ============================================================================= | |
| # SECTION 3 — Install AUR packages | |
| # ============================================================================= | |
| section "Installing AUR packages" | |
| AUR_PACKAGES=( | |
| google-chrome # Google Chrome (official binary) | |
| helium-browser-bin # Helium Browser | |
| visual-studio-code-bin # Official Microsoft VS Code binary | |
| # (remove if you kept 'code' from pacman above) | |
| ) | |
| info "Importing Helium's PGP signing key..." | |
| gpg --keyserver hkps://keyserver.ubuntu.com \ | |
| --recv-keys BE677C1989D35EAB2C5F26C9351601AD01D6378E || \ | |
| warn "Could not import Helium key from keyserver — install may prompt you." | |
| info "Installing AUR packages..." | |
| paru -S --noconfirm --needed "${AUR_PACKAGES[@]}" | |
| success "AUR packages installed." | |
| # ============================================================================= | |
| # SECTION 4 — nvm + Node + npm | |
| # ============================================================================= | |
| section "nvm → Node & npm" | |
| if [[ ! -d "$HOME/.nvm" ]]; then | |
| info "Installing nvm..." | |
| # Fetch latest nvm version tag from GitHub | |
| NVM_VERSION=$(curl -fsSL https://api.github.com/repos/nvm-sh/nvm/releases/latest \ | |
| | grep '"tag_name"' | sed 's/.*"tag_name": "\(.*\)".*/\1/') | |
| curl -fsSL "https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VERSION}/install.sh" | bash | |
| success "nvm ${NVM_VERSION} installed." | |
| else | |
| success "nvm already installed." | |
| fi | |
| # Source nvm for the rest of this script | |
| export NVM_DIR="$HOME/.nvm" | |
| # shellcheck disable=SC1091 | |
| [[ -s "$NVM_DIR/nvm.sh" ]] && source "$NVM_DIR/nvm.sh" | |
| info "Installing latest LTS Node via nvm..." | |
| nvm install --lts | |
| nvm use --lts | |
| nvm alias default 'lts/*' | |
| success "Node $(node -v) / npm $(npm -v) active." | |
| # Ensure nvm is sourced in future shells | |
| for RC in "$HOME/.bashrc" "$HOME/.zshrc"; do | |
| if [[ -f "$RC" ]] && ! grep -q 'NVM_DIR' "$RC"; then | |
| cat >> "$RC" << 'EOF' | |
| # nvm | |
| export NVM_DIR="$HOME/.nvm" | |
| [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" | |
| [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" | |
| EOF | |
| success "nvm bootstrap appended to $RC." | |
| fi | |
| done | |
| # ============================================================================= | |
| # SECTION 5 — MariaDB: initialise & enable passwordless root | |
| # ============================================================================= | |
| section "MariaDB setup" | |
| info "Initialising MariaDB data directory..." | |
| sudo mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql | |
| info "Enabling & starting MariaDB..." | |
| sudo systemctl enable --now mariadb | |
| info "Configuring passwordless root access (unix_socket auth)..." | |
| # unix_socket plugin means 'root on the local machine = no password needed' | |
| sudo mariadb -u root << 'SQL' | |
| ALTER USER 'root'@'localhost' IDENTIFIED VIA unix_socket; | |
| FLUSH PRIVILEGES; | |
| SQL | |
| success "MariaDB root login: just run 'mariadb' (or 'sudo mariadb') — no password." | |
| # ============================================================================= | |
| # SECTION 6 — PHP: enable extensions & Xdebug config | |
| # ============================================================================= | |
| section "PHP 8.5 configuration" | |
| PHP_INI="/etc/php/php.ini" | |
| PHP_CONFD="/etc/php/conf.d" | |
| # Enable common extensions in php.ini (uncomment their lines) | |
| EXTENSIONS=(bcmath curl gd intl mbstring pdo_mysql mysqli sqlite3 zip) | |
| for EXT in "${EXTENSIONS[@]}"; do | |
| sudo sed -i "s/^;extension=${EXT}/extension=${EXT}/" "$PHP_INI" || true | |
| sudo sed -i "s/^;extension=${EXT}.so/extension=${EXT}.so/" "$PHP_INI" || true | |
| done | |
| # OPcache is enabled by default in PHP 8.5 — just verify the ini block exists | |
| if ! grep -q "opcache.enable=1" "$PHP_INI"; then | |
| sudo tee -a "$PHP_INI" > /dev/null << 'EOF' | |
| [opcache] | |
| opcache.enable=1 | |
| opcache.memory_consumption=128 | |
| opcache.interned_strings_buffer=8 | |
| opcache.max_accelerated_files=10000 | |
| opcache.revalidate_freq=2 | |
| EOF | |
| fi | |
| # Xdebug config for VS Code (listen mode) | |
| XDEBUG_INI="${PHP_CONFD}/xdebug.ini" | |
| if [[ ! -f "$XDEBUG_INI" ]]; then | |
| sudo tee "$XDEBUG_INI" > /dev/null << 'EOF' | |
| zend_extension=xdebug.so | |
| [xdebug] | |
| xdebug.mode=debug | |
| xdebug.start_with_request=yes | |
| xdebug.client_host=127.0.0.1 | |
| xdebug.client_port=9003 | |
| xdebug.log=/tmp/xdebug.log | |
| EOF | |
| success "Xdebug configured at ${XDEBUG_INI}." | |
| else | |
| success "Xdebug ini already exists, skipping." | |
| fi | |
| success "PHP extensions enabled. Run 'php -m' to verify." | |
| # ============================================================================= | |
| # SECTION 7 — VS Code extensions (PHP + Xdebug) | |
| # ============================================================================= | |
| section "VS Code extensions" | |
| # Wait for 'code' binary to be available (handles both 'code' and 'code-oss') | |
| CODE_BIN="" | |
| for BIN in code code-oss; do | |
| if command -v "$BIN" &>/dev/null; then CODE_BIN="$BIN"; break; fi | |
| done | |
| if [[ -n "$CODE_BIN" ]]; then | |
| VSCODE_EXTENSIONS=( | |
| xdebug.php-debug # PHP Debug (Xdebug adapter) | |
| bmewburn.vscode-intelephense-client # PHP Intelephense | |
| github.vscode-pull-request-github # GitHub Pull Requests & Issues | |
| github.copilot # GitHub Copilot (optional — comment out if unwanted) | |
| ) | |
| for EXT in "${VSCODE_EXTENSIONS[@]}"; do | |
| info "Installing VS Code extension: $EXT" | |
| "$CODE_BIN" --install-extension "$EXT" --force 2>/dev/null || warn "Could not install $EXT" | |
| done | |
| success "VS Code extensions installed." | |
| else | |
| warn "VS Code binary not found — extensions skipped. Run manually after install." | |
| fi | |
| # ============================================================================= | |
| # SECTION 8 — Google Sans Code font | |
| # ============================================================================= | |
| section "Google Sans Code font" | |
| FONT_DIR="$HOME/.local/share/fonts/GoogleSansCode" | |
| if [[ ! -d "$FONT_DIR" ]]; then | |
| info "Cloning Google Sans Code font repo..." | |
| git clone --depth=1 https://github.com/google/google-sans-code.git \ | |
| "$HOME/.cache/google-sans-code-src" | |
| mkdir -p "$FONT_DIR" | |
| find "$HOME/.cache/google-sans-code-src" -name "*.ttf" -o -name "*.otf" \ | |
| | xargs -I{} cp {} "$FONT_DIR/" | |
| fc-cache -fv "$FONT_DIR" &>/dev/null | |
| success "Google Sans Code installed to ${FONT_DIR}." | |
| else | |
| success "Google Sans Code already installed." | |
| fi | |
| # ============================================================================= | |
| # SECTION 9 — Ghostty configuration | |
| # ============================================================================= | |
| section "Ghostty configuration" | |
| GHOSTTY_CFG_DIR="$HOME/.config/ghostty" | |
| GHOSTTY_CFG="$GHOSTTY_CFG_DIR/config" | |
| mkdir -p "$GHOSTTY_CFG_DIR" | |
| cat > "$GHOSTTY_CFG" << 'EOF' | |
| # ── Ghostty config ───────────────────────────────────────────────────────── | |
| # Font | |
| font-family = "Google Sans Code" | |
| font-size = 10 | |
| # Dracula colour theme | |
| # (Ghostty ships Dracula built-in as of v1.x) | |
| theme = dracula | |
| # ── Key bindings ──────────────────────────────────────────────────────────── | |
| # Ctrl+C → COPY (not SIGINT) | |
| keybind = ctrl+c=copy_to_clipboard | |
| # Ctrl+V → PASTE | |
| keybind = ctrl+v=paste_from_clipboard | |
| # Super+C (Windows key + C) → send SIGINT to the running process | |
| keybind = super+c=esc:c | |
| # Ctrl+Shift+C → send SIGINT (fallback if Super+C doesn't work on your WM) | |
| keybind = ctrl+shift+c=esc:c | |
| # Standard new tab / window shortcuts preserved | |
| keybind = ctrl+t=new_tab | |
| keybind = ctrl+n=new_window | |
| EOF | |
| success "Ghostty config written to ${GHOSTTY_CFG}." | |
| warn "IMPORTANT — Ctrl+C is now COPY; Super+C / Ctrl+Shift+C sends SIGINT." | |
| warn "If Ghostty's built-in 'dracula' theme name differs in your version," | |
| warn "check 'ghostty +list-themes' and update the theme= line accordingly." | |
| # ============================================================================= | |
| # SECTION 10 — KDE: system-wide Ctrl+C copy / Ctrl+V paste | |
| # ============================================================================= | |
| section "KDE system-wide Ctrl+C/V clipboard shortcuts" | |
| # KDE Plasma 6 stores global shortcuts in kglobalshortcutsrc. | |
| # We patch klipper (the KDE clipboard daemon) to map Ctrl+C → copy, Ctrl+V → paste. | |
| # These are ADDITIONAL to Ghostty's own rebinding above. | |
| KGLOBAL="$HOME/.config/kglobalshortcutsrc" | |
| patch_kde_shortcut() { | |
| local group="$1" key="$2" value="$3" | |
| # If the file / group / key doesn't exist, kwriteconfig6 will create them. | |
| kwriteconfig6 --file kglobalshortcutsrc --group "$group" --key "$key" "$value" 2>/dev/null || true | |
| } | |
| if command -v kwriteconfig6 &>/dev/null; then | |
| # Ensure Ctrl+C is the global "copy" shortcut in KDE | |
| patch_kde_shortcut "klipper" "copy_clipboard_history" "ctrl+c,ctrl+c,Copy" | |
| patch_kde_shortcut "klipper" "paste_clipboard_history" "ctrl+v,ctrl+v,Paste" | |
| # Reload KDE shortcuts | |
| qdbus6 org.kde.kglobalaccel /kglobalaccel org.kde.kglobalaccel.reloadConfig 2>/dev/null || true | |
| success "KDE Ctrl+C/V shortcuts patched." | |
| else | |
| warn "kwriteconfig6 not found — KDE shortcut patch skipped (run after login)." | |
| fi | |
| # ============================================================================= | |
| # SECTION 11 — Set default applications (xdg-mime) | |
| # ============================================================================= | |
| section "Default applications" | |
| if command -v xdg-mime &>/dev/null; then | |
| info "Setting Google Chrome as default browser..." | |
| xdg-settings set default-web-browser google-chrome.desktop 2>/dev/null \ | |
| || xdg-mime default google-chrome.desktop x-scheme-handler/http \ | |
| && xdg-mime default google-chrome.desktop x-scheme-handler/https | |
| info "Setting Ghostty as default terminal..." | |
| kwriteconfig6 --file kdeglobals --group General \ | |
| --key TerminalApplication ghostty 2>/dev/null || true | |
| success "Default apps set." | |
| fi | |
| # ============================================================================= | |
| # SECTION 12 — GitHub CLI authentication | |
| # ============================================================================= | |
| section "GitHub CLI (gh) authentication" | |
| if ! gh auth status &>/dev/null; then | |
| info "Launching GitHub CLI auth flow (browser will open)..." | |
| gh auth login --web --git-protocol https | |
| success "GitHub CLI authenticated." | |
| else | |
| success "GitHub CLI already authenticated." | |
| fi | |
| # Also authenticate VS Code's GitHub extension via the CLI token | |
| if [[ -n "$CODE_BIN" ]]; then | |
| info "VS Code GitHub sign-in: open VS Code → Accounts (bottom-left) → Sign in with GitHub." | |
| manual "VS Code GitHub auth cannot be scripted — you must click through the UI once." | |
| fi | |
| # ============================================================================= | |
| # SECTION 13 — Things that MUST be done manually | |
| # ============================================================================= | |
| section "Manual steps remaining" | |
| cat << 'MANUAL' | |
| The following steps require interactive UI and cannot be fully automated: | |
| 1. GOOGLE CHROME — Sign in to your Google account: | |
| Open Chrome → click the profile icon (top-right) → Sign in to Chrome. | |
| 2. VS CODE — Sign in with GitHub (for Settings Sync etc.): | |
| Open VS Code → click Accounts icon (bottom-left) → "Sign in with GitHub". | |
| (The gh CLI token is separate from VS Code's own GitHub session.) | |
| 3. GHOSTTY CTRL+C SIGINT — Verify the keybind works: | |
| Open Ghostty, run a long process (e.g. sleep 100), then press Super+C. | |
| If Super+C is intercepted by KDE before Ghostty, use Ctrl+Shift+C instead — | |
| both are configured in the Ghostty config. | |
| 4. DISCORD — Log in interactively when you first open Discord. | |
| 5. HELIUM BROWSER — First launch shows a "Helium Services" setup screen; | |
| choose your preferred privacy settings there. | |
| 6. XDEBUG IN VS CODE — Create a .vscode/launch.json in your project: | |
| { | |
| "version": "0.2.0", | |
| "configurations": [{ | |
| "name": "Listen for Xdebug", | |
| "type": "php", | |
| "request": "launch", | |
| "port": 9003 | |
| }] | |
| } | |
| MANUAL | |
| success "Setup complete! Reboot or re-login to ensure all services and env vars are active." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment