diff --git a/Makefile b/Makefile index e74376e..60e9bcb 100644 --- a/Makefile +++ b/Makefile @@ -1,100 +1,172 @@ -SHELL = /usr/bin/bash - -home: dirs preferences deps - @echo "All done, reboot is strongly recommended." - +SHELL := /bin/bash + +# ── home-maker ──────────────────────────────────────────────────────────────── +# One Makefile to install and upgrade every tool on your dev machine. +# Supports Arch Linux (pacman/paru) and Fedora (dnf). +# Inspired by https://github.com/santhoshtr/hm + +# ── detect OS ───────────────────────────────────────────────────────────────── +OS_ID := $(shell . /etc/os-release 2>/dev/null && echo $$ID || echo unknown) + +# ── package group includes ──────────────────────────────────────────────────── +include dev/cli.mk +include dev/langs.mk +include dev/rust.mk +include dev/fonts.mk + +include desktop/apps.mk +include desktop/gnome.mk + +# ── package manager commands ────────────────────────────────────────────────── +ifeq ($(OS_ID),arch) + PKG_INSTALL := sudo pacman -Syyuu --needed + AUR_INSTALL := paru -Syu --needed +else ifeq ($(OS_ID),fedora) + PKG_INSTALL := sudo dnf install -y + AUR_INSTALL := echo +else + $(warning Unknown OS '$(OS_ID)'. Defaulting to dnf.) + PKG_INSTALL := sudo dnf install -y + AUR_INSTALL := echo +endif + +CARGO_INSTALL := cargo install +GO_INSTALL := go install +NPM_INSTALL := sudo npm i -g + +# ── helper functions ────────────────────────────────────────────────────────── + +# Split "name@version" → name and version +pkg-name = $(firstword $(subst @, ,$(1))) +pkg-version = $(word 2,$(subst @, ,$(1))) + +# Use PKG_ override when the Make target name differs from the package +# name the manager expects (e.g. libreoffice → libreoffice-fresh on Arch). +pkg-pkgname = $(or $(PKG_$(call pkg-name,$(1))),$(call pkg-name,$(1))) + +# ── target generators ───────────────────────────────────────────────────────── + +define gen-pkg +.PHONY: $(call pkg-name,$(1)) +$(call pkg-name,$(1)): + @echo "installing/upgrading $$@..." + @$(PKG_INSTALL) $(call pkg-pkgname,$(1)) +endef + +ifeq ($(OS_ID),arch) +define gen-aur +.PHONY: $(call pkg-name,$(1)) +$(call pkg-name,$(1)): + @echo "installing/upgrading $$@..." + @$(AUR_INSTALL) $(call pkg-pkgname,$(1)) +endef +else +define gen-aur +.PHONY: $(call pkg-name,$(1)) +$(call pkg-name,$(1)): + @echo "Skipping AUR package $$@ (not available on $(OS_ID))" +endef +endif + +define gen-cargo +.PHONY: $(call pkg-name,$(1)) +$(call pkg-name,$(1)): + @echo "installing/upgrading $$@..." + @$(CARGO_INSTALL) $(call pkg-pkgname,$(1)) +endef + +define gen-go +.PHONY: $(call pkg-name,$(1)) +$(call pkg-name,$(1)): + @echo "installing/upgrading $$@..." + @$(GO_INSTALL) $(call pkg-pkgname,$(1))@$(or $(call pkg-version,$(1)),latest) +endef + +define gen-npm +.PHONY: $(call pkg-name,$(1)) +$(call pkg-name,$(1)): + @echo "installing/upgrading $$@..." + @$(NPM_INSTALL) $(call pkg-pkgname,$(1))$(if $(call pkg-version,$(1)),@$(call pkg-version,$(1))) +endef + +# ── generate targets from accumulated package lists ─────────────────────────── + +$(foreach p,$(PKG),$(eval $(call gen-pkg,$(p)))) +$(foreach p,$(AUR),$(eval $(call gen-aur,$(p)))) +$(foreach p,$(CARGO),$(eval $(call gen-cargo,$(p)))) +$(foreach p,$(GO),$(eval $(call gen-go,$(p)))) +$(foreach p,$(NPM),$(eval $(call gen-npm,$(p)))) + +# ── group targets ───────────────────────────────────────────────────────────── + +.PHONY: cli +cli: build-tools fzf git cmake zsh zsh-completions neovim bash-completion man-db man-pages eza zoxide + +.PHONY: langs +langs: nodejs npm go dotnet-sdk rustup dart + +.PHONY: rust +rust: sccache configman gprofile cargo-cache cargo-edit + +.PHONY: fonts +fonts: noto-fonts noto-fonts-cjk noto-fonts-emoji noto-fonts-extra ttf-ms-fonts + +.PHONY: apps +apps: firefox tilix libreoffice gnome-browser-connector vscode pycharm zoom + +# ── top-level targets ───────────────────────────────────────────────────────── + +.PHONY: dev +dev: cli langs rust + +.PHONY: desktop +desktop: fonts apps gnome + +.PHONY: dirs dirs: - @echo "Creating folders" - mkdir -p ~/Projects ~/Work ~/.npm/packages/bin ~/.pub-cache/bin - -preferences: - @echo "Applying gnome settings" - gsettings set org.gnome.desktop.datetime automatic-timezone true - gsettings set org.gnome.desktop.interface clock-format '12h' - gsettings set org.gnome.desktop.interface clock-show-date true - gsettings set org.gnome.desktop.interface clock-show-weekday true - gsettings set org.gnome.desktop.interface gtk-theme 'Adwaita-dark' - gsettings set org.gnome.desktop.interface show-battery-percentage true - gsettings set org.gnome.desktop.peripherals.touchpad natural-scroll false - gsettings set org.gnome.desktop.peripherals.touchpad tap-to-click true - gsettings set org.gnome.desktop.sound allow-volume-above-100-percent true - gsettings set org.gnome.desktop.wm.keybindings show-desktop "['d']" - gsettings set org.gnome.desktop.wm.keybindings switch-applications "['Tab']" - gsettings set org.gnome.desktop.wm.keybindings switch-applications-backward "['Tab']" - gsettings set org.gnome.desktop.wm.keybindings switch-windows "['Tab']" - gsettings set org.gnome.desktop.wm.keybindings switch-windows-backward "['Tab']" - gsettings set org.gnome.desktop.wm.preferences button-layout ":minimize,maximize,close" - -.ONESHELL: -deps: - @echo "Installing essential packages" - sudo pacman -Syyuu --needed \ - base-devel \ - fzf \ - git \ - cmake \ - firefox \ - tilix \ - nodejs \ - noto-fonts \ - noto-fonts-cjk \ - noto-fonts-emoji \ - noto-fonts-extra \ - npm \ - rustup \ - zsh \ - zsh-completions \ - neovim \ - libreoffice-fresh \ - bash-completion \ - dart \ - dotnet-sdk \ - go \ - man-db \ - man-pages - - # curl \ - # --fail \ - # --no-progress-meter \ - # --show-error \ - # --output-dir zsh \ - # --remote-name-all "https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/lib/{key-bindings,completion}.zsh" - - @echo "Set up rust compiler & its components" - rustup toolchain install stable - rustup set profile default - cargo install sccache - export PATH=$$PATH:$$HOME/.cargo/bin - export RUSTC_WRAPPER=$(which sccache) - cargo install --locked --jobs=3 configman - - @echo "Linking config files" - - configman --src=. --dest=~ - - ( - # installing AUR helper - cd /tmp - git clone https://aur.archlinux.org/paru.git - cd paru - makepkg -si - ) - - cargo install gprofile - - paru -Syu --needed snapd chrome-gnome-shell zoom shellcheck-bin ttf-ms-fonts - - # cargo install bat cargo-edit cargo-expand cargo-watch evcxr_repl exa - - - @echo "Installing snaps packages" - sudo systemctl enable --now snapd.socket - - if [[ ! -d /snap ]]; then - sudo ln -s /var/lib/snapd/snap /snap + @echo "Creating directories..." + @mkdir -p ~/Projects ~/Work ~/.npm/packages/bin ~/.pub-cache/bin + +.PHONY: link-dots +link-dots: + @echo "Linking config files..." + @configman --src=. --dest=~ + +.PHONY: aur-helper +aur-helper: + @if [ "$(OS_ID)" != "arch" ]; then \ + echo "paru is only for Arch Linux"; exit 1; \ + fi + @if ! command -v paru &>/dev/null; then \ + tmpdir=$$(mktemp -d) && \ + git clone https://aur.archlinux.org/paru.git "$$tmpdir/paru" && \ + cd "$$tmpdir/paru" && makepkg -si && \ + rm -rf "$$tmpdir"; \ + else \ + echo "paru is already installed"; \ fi - sleep 10s - - sudo snap install code --classic - sudo snap install pycharm-community --classic +.PHONY: all +all: dirs dev desktop + +.PHONY: hm +hm: + @bash "$(CURDIR)/hm.sh" + +.PHONY: clean +clean: + @echo "cleaning cargo..." + @cargo-cache -a 2>/dev/null || true + @. /etc/os-release 2>/dev/null; \ + if [ "$$ID" = "arch" ]; then \ + echo "cleaning pacman cache..."; \ + sudo pacman -Sc --noconfirm; \ + elif [ "$$ID" = "fedora" ]; then \ + echo "cleaning dnf cache..."; \ + sudo dnf clean all; \ + fi + @echo "cleaning npm..." + @npm cache clean --force 2>/dev/null || true + @echo "cleaning go..." + @go clean -cache 2>/dev/null || true + @echo "all caches cleaned" diff --git a/desktop/apps.mk b/desktop/apps.mk new file mode 100644 index 0000000..ba262cf --- /dev/null +++ b/desktop/apps.mk @@ -0,0 +1,61 @@ +# desktop applications + +PKG += firefox tilix + +# libreoffice: 'libreoffice-fresh' on Arch, 'libreoffice' on Fedora +PKG += libreoffice +ifeq ($(OS_ID),arch) +PKG_libreoffice := libreoffice-fresh +endif + +# gnome-browser-connector: AUR on Arch, official repo on Fedora +ifeq ($(OS_ID),arch) +AUR += gnome-browser-connector +else +PKG += gnome-browser-connector +endif + +# snapd: AUR on Arch, official repo on Fedora +ifeq ($(OS_ID),arch) +AUR += snapd +else +PKG += snapd +endif + +# VSCode — installed via snap on both distros +.PHONY: vscode +vscode: snapd-setup + @echo "installing/upgrading $@..." + @sudo snap install code --classic + +# PyCharm Community — installed via snap on both distros +.PHONY: pycharm +pycharm: snapd-setup + @echo "installing/upgrading $@..." + @sudo snap install pycharm-community --classic + +# zoom: AUR on Arch; direct RPM download on Fedora +ifeq ($(OS_ID),arch) +AUR += zoom +else +.PHONY: zoom +zoom: + @echo "installing/upgrading $@..." + @tmpdir=$$(mktemp -d) && \ + curl -fsSL "https://zoom.us/client/latest/zoom_x86_64.rpm" \ + -o "$$tmpdir/zoom.rpm" && \ + sudo dnf install -y "$$tmpdir/zoom.rpm" && \ + rm -rf "$$tmpdir" +endif + +# snapd post-install setup (enable service, create /snap symlink) +.PHONY: snapd-setup +snapd-setup: snapd + @echo "setting up snapd..." + @sudo systemctl enable --now snapd.socket + @if [ ! -e /snap ]; then \ + sudo ln -s /var/lib/snapd/snap /snap; \ + fi + @echo "waiting for snapd to initialise..." + @sleep 5 + @echo "snapd initialized" diff --git a/desktop/gnome.mk b/desktop/gnome.mk new file mode 100644 index 0000000..54b2225 --- /dev/null +++ b/desktop/gnome.mk @@ -0,0 +1,20 @@ +# GNOME desktop preferences + +.PHONY: gnome +gnome: + @echo "Applying GNOME settings..." + gsettings set org.gnome.desktop.datetime automatic-timezone true + gsettings set org.gnome.desktop.interface clock-format '12h' + gsettings set org.gnome.desktop.interface clock-show-date true + gsettings set org.gnome.desktop.interface clock-show-weekday true + gsettings set org.gnome.desktop.interface gtk-theme 'Adwaita-dark' + gsettings set org.gnome.desktop.interface show-battery-percentage true + gsettings set org.gnome.desktop.peripherals.touchpad natural-scroll false + gsettings set org.gnome.desktop.peripherals.touchpad tap-to-click true + gsettings set org.gnome.desktop.sound allow-volume-above-100-percent true + gsettings set org.gnome.desktop.wm.keybindings show-desktop "['d']" + gsettings set org.gnome.desktop.wm.keybindings switch-applications "['Tab']" + gsettings set org.gnome.desktop.wm.keybindings switch-applications-backward "['Tab']" + gsettings set org.gnome.desktop.wm.keybindings switch-windows "['Tab']" + gsettings set org.gnome.desktop.wm.keybindings switch-windows-backward "['Tab']" + gsettings set org.gnome.desktop.wm.preferences button-layout ":minimize,maximize,close" diff --git a/dev/cli.mk b/dev/cli.mk new file mode 100644 index 0000000..426ca0a --- /dev/null +++ b/dev/cli.mk @@ -0,0 +1,18 @@ +# core CLI tools every developer uses + +PKG += fzf git cmake zsh zsh-completions neovim bash-completion man-db man-pages + +CARGO += eza zoxide + +# base-devel on Arch, "Development Tools" group on Fedora +.PHONY: build-tools +build-tools: + @echo "installing/upgrading $@..." + @. /etc/os-release 2>/dev/null; \ + if [ "$$ID" = "arch" ]; then \ + sudo pacman -Syyuu --needed base-devel; \ + elif [ "$$ID" = "fedora" ]; then \ + sudo dnf groupinstall -y "Development Tools"; \ + else \ + echo "Unsupported OS: $$ID, skipping $@"; \ + fi diff --git a/dev/fonts.mk b/dev/fonts.mk new file mode 100644 index 0000000..b82eff0 --- /dev/null +++ b/dev/fonts.mk @@ -0,0 +1,20 @@ +# fonts + +# Noto font family — package names differ between distros +PKG += noto-fonts noto-fonts-cjk noto-fonts-emoji noto-fonts-extra + +ifeq ($(OS_ID),fedora) +PKG_noto-fonts := google-noto-fonts-common +PKG_noto-fonts-cjk := google-noto-cjk-fonts +PKG_noto-fonts-emoji := google-noto-emoji-fonts +PKG_noto-fonts-extra := google-noto-sans-fonts +endif + +# MS fonts: AUR on Arch; not available on Fedora +ifeq ($(OS_ID),arch) +AUR += ttf-ms-fonts +else +.PHONY: ttf-ms-fonts +ttf-ms-fonts: + @echo "ttf-ms-fonts not available on $(OS_ID), skipping" +endif diff --git a/dev/langs.mk b/dev/langs.mk new file mode 100644 index 0000000..3037a8e --- /dev/null +++ b/dev/langs.mk @@ -0,0 +1,19 @@ +# language toolchains + +PKG += nodejs npm go rustup + +# dotnet-sdk package has a version suffix on Fedora +ifeq ($(OS_ID),fedora) +PKG_dotnet-sdk := dotnet-sdk-9.0 +endif +PKG += dotnet-sdk + +# dart: in AUR on Arch; install via snap on Fedora +ifeq ($(OS_ID),arch) +AUR += dart +else +.PHONY: dart +dart: + @echo "installing/upgrading $@..." + @sudo snap install dart --classic +endif diff --git a/dev/rust.mk b/dev/rust.mk new file mode 100644 index 0000000..5610083 --- /dev/null +++ b/dev/rust.mk @@ -0,0 +1,3 @@ +# rust/cargo tools + +CARGO += sccache configman gprofile cargo-cache cargo-edit diff --git a/hm.sh b/hm.sh new file mode 100755 index 0000000..e54086b --- /dev/null +++ b/hm.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# hm — home maker: fuzzy install/upgrade packages and groups + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +GROUP_TARGETS="cli langs rust fonts apps gnome dev desktop dirs aur-helper link-dots all" +META_TARGETS="hm Makefile clean" + +exclude_patterns() { + printf '%s\n' "$META_TARGETS" + echo "$GROUP_TARGETS" | tr ' ' '\n' +} + +packages() { + make -pn -C "$SCRIPT_DIR" 2>/dev/null | + grep -E '^[a-zA-Z][a-zA-Z0-9_-]*:' | + sed 's/:.*//' | + grep -vxF -f <(exclude_patterns) | + sort -u +} + +list() { + printf '# ── groups ──────────────────────────────\n' + echo "$GROUP_TARGETS" | tr ' ' '\n' + printf '# ── packages ────────────────────────────\n' + packages +} + +while true; do + selected=$(list | fzf \ + --multi \ + --prompt "install > " \ + --reverse \ + --height 60% \ + --preview "make -n -C '$SCRIPT_DIR' {} 2>/dev/null" \ + --preview-window "right:50%:wrap" \ + --color "header:italic" \ + --ansi) || break + + [[ -z "$selected" ]] && break + + while IFS= read -r target; do + [[ "$target" == \#* ]] && continue + echo ">>> make $target" + make -C "$SCRIPT_DIR" "$target" + done <<<"$selected" +done