macbook-setup/setup-base.sh

495 lines
20 KiB
Bash
Executable file

#!/bin/bash
# macbook-setup/setup-base.sh
# System-Setup: Pakete, Konfiguration, Locale, Services
# Verwendung: curl ... | sudo bash -s -- 16
# Kann mehrfach ausgeführt werden (idempotent)
FORGEJO="https://git.motocamp.de"
SETUP_RAW="$FORGEJO/rene/macbook-setup/raw/branch/main"
# ── Farben ──────────────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
ok() { echo -e "${GREEN}$*${NC}"; }
warn() { echo -e "${YELLOW}$*${NC}"; }
fail() { echo -e "${RED}$*${NC}"; }
# ── Lokales Repo erkennen (falls nicht via curl|bash) ─────────────────
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)"
if [[ -n "$SCRIPT_DIR" && -f "$SCRIPT_DIR/mbpfan-13.conf" ]]; then
REPO_DIR="$SCRIPT_DIR"
ok "Lokales Repo erkannt: $REPO_DIR"
else
REPO_DIR=""
fi
# ── Modell ermitteln ─────────────────────────────────────────────────────
if [[ "$1" == "13" || "$1" == "16" ]]; then
MODEL="$1"
else
echo ""
echo "Welches MacBook Pro?"
echo " 13 = MBP 13\" Late 2013"
echo " 16 = MBP 16\" Mid 2014 (Intel + AMD Radeon)"
read -rp "Modell [13/16]: " MODEL < /dev/tty
[[ "$MODEL" != "13" && "$MODEL" != "16" ]] && { fail "Ungültiges Modell: $MODEL"; exit 1; }
fi
ok "Modell: MacBook Pro $MODEL\""
echo ""
echo "════════════════════════════════════════════"
echo " setup-base.sh für MBP $MODEL\" startet"
echo "════════════════════════════════════════════"
# ── 0. sudoers reparieren (macOS-Installer hinterlässt macOS-sudoers) ──────
echo -e "\n=== 0/12 sudoers ==="
cat > /etc/sudoers <<'SUDOEOF'
Defaults env_reset
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
root ALL=(ALL:ALL) ALL
%sudo ALL=(ALL:ALL) ALL
@includedir /etc/sudoers.d
SUDOEOF
chmod 440 /etc/sudoers
# Passwordless sudo fuer rene
echo "rene ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/rene
chmod 440 /etc/sudoers.d/rene
ok "sudoers: Linux-Standard + NOPASSWD fuer rene"
# ── 1. Kritische Hardware-Fixes ZUERST (vor allem anderen) ───────────────
echo -e "\n=== 1/12 Hardware-Fixes ==="
# GPU: AMD Radeon blacklisten (MBP 16")
if [[ "$MODEL" == "16" ]]; then
cat > /etc/modprobe.d/blacklist-radeon.conf <<EOF
blacklist radeon
blacklist amdgpu
EOF
ok "AMD Radeon/amdgpu geblockt — nur Intel i915"
else
ok "MBP 13\": nur Intel-GPU, kein Blacklisting nötig"
fi
# Broadcom BCM4360: Open-Source-Treiber blockieren (b43 kann BCM4360 nicht,
# belegt aber den Chip und verhindert das Laden des proprietaeren wl-Treibers)
cat > /etc/modprobe.d/blacklist-bcm.conf <<EOF
blacklist b43
blacklist bcma
blacklist ssb
EOF
ok "b43/bcma/ssb geblockt — wl-Treiber wird verwendet"
# Fn-Tasten + ISO-Layout-Fix (iso_layout=1 korrigiert ^/< auf Apple ISO-Keyboards)
echo 'options hid_apple fnmode=2 iso_layout=1' > /etc/modprobe.d/hid_apple.conf
ok "hid_apple: fnmode=2, iso_layout=1"
# Tastaturbelegung (deadkeys + lv3:lalt_switch: @=Alt+Q, |=Alt+<)
cat > /etc/default/keyboard <<EOF
XKBMODEL="macbook79"
XKBLAYOUT="de"
XKBVARIANT=""
XKBOPTIONS="lv3:lalt_switch,terminate:ctrl_alt_bksp,ctrl:swap_lwin_lctl"
EOF
dpkg-reconfigure -f noninteractive keyboard-configuration 2>/dev/null || true
ok "Tastatur: mac_deadkeys"
# cloud-init deaktivieren (blockiert Boot ohne Cloud-Provider)
if command -v cloud-init &>/dev/null; then
touch /etc/cloud/cloud-init.disabled
ok "cloud-init deaktiviert"
fi
# initramfs aktualisieren (GPU-Blacklist + hid_apple wirksam machen)
update-initramfs -u 2>/dev/null || true
ok "initramfs aktualisiert"
# ── 2. Sleep/Suspend verhindern während Installation ─────────────────────
echo -e "\n=== 2/12 Sleep verhindern ==="
systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target 2>/dev/null || true
ok "Sleep/Suspend deaktiviert (für Installation)"
# ── 3. Eventuelle unterbrochene Installationen reparieren ────────────────
echo -e "\n=== 3/12 dpkg reparieren ==="
dpkg --configure -a 2>/dev/null || true
apt install -f -y 2>/dev/null || true
ok "dpkg/apt repariert"
# ── 4. System aktualisieren ──────────────────────────────────────────────
echo -e "\n=== 4/12 System aktualisieren ==="
apt update && apt upgrade -y || warn "apt upgrade hatte Probleme"
ok "System aktuell"
# ── 5. Pakete installieren ───────────────────────────────────────────────
echo -e "\n=== 5/12 Pakete installieren ==="
apt install -y \
xubuntu-core \
bcmwl-kernel-source \
mbpfan thermald \
lm-sensors xfce4-sensors-plugin \
tlp tlp-rdw \
git curl wget stow \
zsh neovim \
build-essential dkms \
python3 python3-pip python3-venv \
nodejs npm \
wireguard wireguard-tools \
openssh-server \
libheif-examples imagemagick \
pipx \
zoxide \
micro \
flatpak \
keepassxc \
kitty \
htop btop cmatrix libcurses-perl cowsay fortune-mod fortunes-de \
bat eza fd-find fzf ripgrep tldr ncdu duf xclip \
timeshift \
vlc \
language-pack-de \
wngerman \
bc \
xfce4-terminal \
|| { fail "apt install fehlgeschlagen"; dpkg --configure -a; apt install -f -y; }
ok "Pakete installiert (apt)"
# asciiquarium-ng (Python-Port, von git.motocamp.de)
if ! command -v asciiquarium &>/dev/null; then
if command -v python3 &>/dev/null; then
curl -fsSL "https://git.motocamp.de/rene/asciiquarium/raw/branch/main/asciiquarium_ng.py" \
-o /usr/local/bin/asciiquarium \
&& chmod +x /usr/local/bin/asciiquarium \
&& ok "asciiquarium-ng installiert" \
|| warn "asciiquarium-ng uebersprungen"
else
warn "asciiquarium: python3 nicht gefunden, uebersprungen"
fi
fi
# fastfetch (nicht in apt verfuegbar, .deb von GitHub)
if ! command -v fastfetch &>/dev/null; then
curl -fsSL https://github.com/fastfetch-cli/fastfetch/releases/latest/download/fastfetch-linux-amd64.deb -o /tmp/fastfetch.deb \
&& dpkg -i /tmp/fastfetch.deb \
&& ok "fastfetch installiert" \
|| warn "fastfetch uebersprungen"
rm -f /tmp/fastfetch.deb
fi
# WezTerm (offizielles apt-Repository)
if ! command -v wezterm &>/dev/null; then
curl -fsSL https://apt.fury.io/wez/gpg.key \
| gpg --yes --dearmor -o /usr/share/keyrings/wezterm-fury.gpg
echo 'deb [signed-by=/usr/share/keyrings/wezterm-fury.gpg] https://apt.fury.io/wez/ * *' \
> /etc/apt/sources.list.d/wezterm.list
apt update -qq
apt install -y wezterm && ok "WezTerm installiert" || warn "WezTerm uebersprungen"
fi
if command -v wezterm &>/dev/null; then
update-alternatives --install /usr/bin/x-terminal-emulator x-terminal-emulator /usr/bin/wezterm 50
update-alternatives --set x-terminal-emulator /usr/bin/wezterm
ok "WezTerm: als x-terminal-emulator gesetzt"
fi
# FreeCAD + LibreOffice + Bitwarden via Snap
snap install freecad 2>/dev/null || warn "FreeCAD Snap uebersprungen"
snap install libreoffice 2>/dev/null || warn "LibreOffice Snap uebersprungen"
snap install bitwarden 2>/dev/null || warn "Bitwarden Snap uebersprungen"
snap install thunderbird 2>/dev/null || warn "Thunderbird Snap uebersprungen"
# ── 6. FaceTime HD Webcam (Broadcom 1570, PCIe) ─────────────────────────
echo -e "\n=== 6/12 FaceTime HD Webcam ==="
if lspci | grep -qi "facetime"; then
if ! dkms status 2>/dev/null | grep -q "facetimehd"; then
# Firmware extrahieren
FTMP=$(mktemp -d)
git clone --depth 1 https://github.com/patjak/facetimehd-firmware.git "$FTMP/fw" \
&& make -C "$FTMP/fw" \
&& make -C "$FTMP/fw" install \
&& ok "facetimehd Firmware installiert" \
|| warn "facetimehd Firmware fehlgeschlagen"
# Treiber bauen und via DKMS installieren
git clone --depth 1 https://github.com/patjak/bcwc_pcie.git /usr/src/facetimehd-1.0 \
&& cat > /usr/src/facetimehd-1.0/dkms.conf <<'DKMSEOF'
PACKAGE_NAME="facetimehd"
PACKAGE_VERSION="1.0"
MAKE[0]="make -C /lib/modules/${kernelver}/build M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build modules"
CLEAN="make -C /lib/modules/${kernelver}/build M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build clean"
BUILT_MODULE_NAME[0]="facetimehd"
DEST_MODULE_LOCATION[0]="/updates"
AUTOINSTALL="yes"
DKMSEOF
dkms add facetimehd/1.0 \
&& dkms build facetimehd/1.0 \
&& dkms install facetimehd/1.0 \
&& ok "facetimehd DKMS-Treiber installiert" \
|| warn "facetimehd DKMS fehlgeschlagen"
rm -rf "$FTMP"
else
ok "facetimehd bereits via DKMS installiert"
fi
# Modul beim Boot laden
echo "facetimehd" > /etc/modules-load.d/facetimehd.conf
# Modul jetzt laden (falls nicht in chroot)
if [[ "$(stat -c %d:%i /)" == "$(stat -c %d:%i /proc/1/root/.)" ]] 2>/dev/null; then
modprobe facetimehd 2>/dev/null || true
fi
else
ok "Keine FaceTime-Kamera erkannt — uebersprungen"
fi
# ── 7. Brave Browser (apt-Repo) ──────────────────────────────────────────
echo -e "\n=== 7/12 Brave Browser ==="
if ! command -v brave-browser &>/dev/null; then
curl -fsSLo /tmp/brave-browser-archive-keyring.gpg https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg
install -o root -g root -m 644 /tmp/brave-browser-archive-keyring.gpg /usr/share/keyrings/brave-browser-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg] https://brave-browser-apt-release.s3.brave.com/ stable main" > /etc/apt/sources.list.d/brave-browser-release.list
apt update -qq && apt install -y brave-browser
rm -f /tmp/brave-browser-archive-keyring.gpg
fi
ok "Brave Browser installiert (apt)"
# ── 8. Systemkonfigurationen ─────────────────────────────────────────────
echo -e "\n=== 8/12 Systemkonfigurationen ==="
# mbpfan (modellabhängig)
if [[ -n "$REPO_DIR" && -f "$REPO_DIR/mbpfan-${MODEL}.conf" ]]; then
cp "$REPO_DIR/mbpfan-${MODEL}.conf" /etc/mbpfan.conf
ok "mbpfan.conf (lokal)"
else
wget -q -O /etc/mbpfan.conf "$SETUP_RAW/mbpfan-${MODEL}.conf" || warn "mbpfan.conf Download fehlgeschlagen"
fi
# Temperatur-Watch-Skript
if [[ -n "$REPO_DIR" && -f "$REPO_DIR/temp-watch.sh" ]]; then
cp "$REPO_DIR/temp-watch.sh" /usr/local/bin/temp-watch.sh
chmod +x /usr/local/bin/temp-watch.sh
ok "temp-watch.sh (lokal)"
else
wget -q -O /usr/local/bin/temp-watch.sh "$SETUP_RAW/temp-watch.sh" && \
chmod +x /usr/local/bin/temp-watch.sh || warn "temp-watch.sh Download fehlgeschlagen"
fi
# WireGuard via wg-quick (einfacher als NetworkManager, Config 1:1 verwendbar)
WG_CONF="$REPO_DIR/wireguard/m${MODEL}.conf"
if [[ -n "$REPO_DIR" && -f "$WG_CONF" ]]; then
# Alte NM-WireGuard-Verbindung entfernen falls vorhanden
nmcli connection delete wg0 2>/dev/null || true
rm -f /etc/NetworkManager/system-connections/wg0.nmconnection
# wg-quick Config installieren (Dateiname = Interface-Name)
WG_NAME="wg-vps"
cp "$WG_CONF" "/etc/wireguard/${WG_NAME}.conf"
chmod 600 "/etc/wireguard/${WG_NAME}.conf"
# Service aktivieren (startet automatisch beim Boot)
systemctl enable wg-quick@${WG_NAME} 2>/dev/null || true
# Starten falls nicht in chroot
if [[ "$(stat -c %d:%i /)" == "$(stat -c %d:%i /proc/1/root/.)" ]] 2>/dev/null; then
wg-quick up "$WG_NAME" 2>/dev/null || true
fi
ok "WireGuard ${WG_NAME} installiert (wg-quick, DNS: Pihole, autostart)"
else
warn "WireGuard: keine lokale Config gefunden — manuell einrichten"
fi
ok "Systemkonfigurationen gesetzt"
# ── 8. XFCE-Konfiguration (beide MBPs haben Retina-Displays) ─────────────
echo -e "\n=== 8/12 XFCE-Konfiguration (HiDPI) ==="
XFCE_XML_DIR="/home/rene/.config/xfce4/xfconf/xfce-perchannel-xml"
mkdir -p "$XFCE_XML_DIR"
# Compositor deaktivieren + HiDPI-Theme + Titelschrift
cat > "$XFCE_XML_DIR/xfwm4.xml" <<XFEOF
<?xml version="1.0" encoding="UTF-8"?>
<channel name="xfwm4" version="1.0">
<property name="general" type="empty">
<property name="use_compositing" type="bool" value="false"/>
<property name="theme" type="string" value="Default-xhdpi"/>
<property name="title_font" type="string" value="Noto Sans Bold 10"/>
</property>
</channel>
XFEOF
# Display-Skalierung für Retina (2x)
cat > "$XFCE_XML_DIR/xsettings.xml" <<XSEOF
<?xml version="1.0" encoding="UTF-8"?>
<channel name="xsettings" version="1.0">
<property name="Gdk" type="empty">
<property name="WindowScalingFactor" type="int" value="2"/>
</property>
<property name="Xft" type="empty">
<property name="DPI" type="int" value="96"/>
</property>
<property name="Gtk" type="empty">
<property name="CursorThemeSize" type="int" value="48"/>
<property name="FontName" type="string" value="Noto Sans 10"/>
</property>
</channel>
XSEOF
# xfce4-display-settings deaktivieren (Endlosschleife)
dpkg-divert --local --rename --divert /usr/bin/xfce4-display-settings.real /usr/bin/xfce4-display-settings 2>/dev/null || true
ln -sf /usr/bin/true /usr/bin/xfce4-display-settings
# Panel-Konfiguration (Höhe passend zur Schriftgröße)
PANEL_XML="$XFCE_XML_DIR/xfce4-panel.xml"
if [[ ! -f "$PANEL_XML" ]]; then
cat > "$PANEL_XML" <<PEOF
<?xml version="1.0" encoding="UTF-8"?>
<channel name="xfce4-panel" version="1.0">
<property name="panels" type="array">
<value type="int" value="1"/>
<property name="panel-1" type="empty">
<property name="size" type="uint" value="28"/>
</property>
</property>
</channel>
PEOF
fi
chown -R 1000:1000 /home/rene/.config/xfce4
ok "XFCE: Compositor aus, Retina-Skalierung, Schriften 10pt, Panel 28px, Display-Settings deaktiviert"
# ── 10. Netzwerk: NetworkManager statt netplan ────────────────────────────
echo -e "\n=== 10/12 Netzwerk ==="
# WLAN-Zugangsdaten aus bestehender netplan-Config übernehmen (falls vorhanden)
WLAN_SSID=""
WLAN_PASS=""
for npfile in /etc/netplan/*.yaml; do
if [[ -f "$npfile" ]] && grep -q "wifis:" "$npfile" 2>/dev/null; then
WLAN_SSID=$(grep -A5 'wifis:' "$npfile" | grep -oP '"\K[^"]+' | head -1)
WLAN_PASS=$(grep -oP 'password:\s*"\K[^"]+' "$npfile" | head -1)
[[ -n "$WLAN_SSID" ]] && ok "WLAN-Config gefunden: $WLAN_SSID"
fi
done
cat > /etc/netplan/01-network-manager.yaml <<NMEOF
network:
version: 2
renderer: NetworkManager
NMEOF
# Cloud-init netplan-Config entfernen (blockiert NetworkManager)
rm -f /etc/netplan/50-cloud-init.yaml
# Boot nicht auf Netzwerk warten lassen
systemctl disable systemd-networkd-wait-online.service 2>/dev/null || true
systemctl enable NetworkManager-wait-online.service 2>/dev/null || true
ok "NetworkManager als Netzwerk-Renderer"
# /etc/hosts: Synology-Dienste im LAN (intern nicht per DNS erreichbar)
# dsm -> Synology direkt, git -> Nginx Proxy Manager (macvlan)
for entry in "10.47.11.10 dsm.motocamp.de" "10.47.11.23 git.motocamp.de"; do
host="${entry##* }"
if ! grep -q "$host" /etc/hosts; then
echo "$entry" >> /etc/hosts
ok "/etc/hosts: $host"
else
ok "/etc/hosts: $host (bereits vorhanden)"
fi
done
# Auto-Boot beim Anstecken des Netzteils deaktivieren
EFI_VAR="/sys/firmware/efi/efivars/auto-boot-7c436110-ab2a-4bbb-a880-fe41995c9f82"
if [ -f "$EFI_VAR" ]; then
chattr -i "$EFI_VAR" 2>/dev/null
printf '\x07\x00\x00\x00\x66\x61\x6c\x73\x65' > "$EFI_VAR"
ok "auto-boot deaktiviert (kein Start beim Netzteil-Anstecken)"
fi
# ── 11. Energieeinstellungen & Lokalisierung ─────────────────────────────
echo -e "\n=== 11/12 Energie & Lokalisierung ==="
tee /etc/systemd/sleep.conf > /dev/null <<EOF
[Sleep]
HibernateDelaySec=1800
EOF
tee -a /etc/systemd/logind.conf > /dev/null <<EOF
HandleLidSwitch=suspend
HandleLidSwitchExternalPower=ignore
EOF
locale-gen de_DE.UTF-8
update-locale LANG=de_DE.UTF-8 LC_ALL=de_DE.UTF-8
timedatectl set-timezone Europe/Berlin 2>/dev/null || ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime
ok "Energie & Lokalisierung gesetzt"
# ── 12. Services aktivieren ──────────────────────────────────────────────
echo -e "\n=== 12/12 Services & Shell ==="
systemctl enable mbpfan 2>/dev/null || true
systemctl enable thermald 2>/dev/null || true
systemctl enable tlp 2>/dev/null || true
systemctl enable ssh 2>/dev/null || true
# Services starten (nur wenn nicht in chroot)
if [[ "$(stat -c %d:%i /)" == "$(stat -c %d:%i /proc/1/root/.)" ]] 2>/dev/null; then
systemctl start mbpfan 2>/dev/null || true
systemctl start thermald 2>/dev/null || true
systemctl start ssh 2>/dev/null || true
sensors-detect --auto 2>/dev/null || true
fi
# WLAN-Verbindung als NetworkManager-Profil anlegen (überlebt Reboot)
if [[ -n "$WLAN_SSID" && -n "$WLAN_PASS" ]]; then
NM_CONN_DIR="/etc/NetworkManager/system-connections"
mkdir -p "$NM_CONN_DIR"
cat > "$NM_CONN_DIR/$WLAN_SSID.nmconnection" <<WIFIEOF
[connection]
id=$WLAN_SSID
type=wifi
autoconnect=true
[wifi]
ssid=$WLAN_SSID
mode=infrastructure
[wifi-security]
key-mgmt=wpa-psk
psk=$WLAN_PASS
[ipv4]
method=auto
[ipv6]
method=auto
WIFIEOF
chmod 600 "$NM_CONN_DIR/$WLAN_SSID.nmconnection"
ok "WLAN-Profil angelegt: $WLAN_SSID (autoconnect)"
fi
# zsh als Standard-Shell
chsh -s /bin/zsh rene 2>/dev/null || true
# Autostart für setup-desktop.sh anlegen (als root, da setup.sh-Eintrag
# beim ersten XFCE-Start verloren gehen kann)
REPO_DIR_USER="/home/rene/git-projekte/macbook-setup"
if [[ -f "$REPO_DIR_USER/setup-desktop.sh" ]]; then
AUTOSTART_DIR="/home/rene/.config/autostart"
mkdir -p "$AUTOSTART_DIR"
cat > "$AUTOSTART_DIR/macbook-setup-desktop.desktop" <<ASEOF
[Desktop Entry]
Type=Application
Name=MacBook Setup Desktop
Exec=xfce4-terminal -e "bash -c 'bash $REPO_DIR_USER/setup-desktop.sh 2>&1 | tee /tmp/setup-desktop.log; echo; echo Setup abgeschlossen - Enter zum Schliessen; read'"
Hidden=false
X-GNOME-Autostart-enabled=true
ASEOF
chown -R 1000:1000 "$AUTOSTART_DIR"
ok "Autostart für setup-desktop.sh eingerichtet"
fi
# Sleep wieder erlauben
systemctl unmask sleep.target suspend.target hibernate.target hybrid-sleep.target 2>/dev/null || true
ok "Services aktiviert, zsh als Standard-Shell"
# ── Zusammenfassung ──────────────────────────────────────────────────────
echo ""
echo "════════════════════════════════════════════"
echo -e " ${GREEN}setup-base.sh abgeschlossen!${NC}"
echo "════════════════════════════════════════════"
echo ""
echo "Nächste Schritte:"
echo " 1. sudo reboot"
echo " 2. In XFCE einloggen"
echo " 3. Terminal öffnen und setup-desktop.sh starten:"
echo " curl -fsSL $SETUP_RAW/setup-desktop.sh | bash"