From 58b6c10779c2b5434ffa96f00201b5797253c127 Mon Sep 17 00:00:00 2001 From: Prad Nukala Date: Fri, 16 Oct 2026 14:08:35 -0400 Subject: [PATCH] feat(scripts): automate tool and AUR package installation setup --- .../run_onchange_after_15-tools.sh.tmpl | 58 +++++++++++++++++++ ...un_onchange_after_20-gh-extensions.sh.tmpl | 29 +++++++--- .../run_onchange_after_40-post.sh.tmpl | 9 --- .../run_onchange_before_10-packages.sh.tmpl | 23 +++++++- 4 files changed, 99 insertions(+), 20 deletions(-) create mode 100644 .chezmoiscripts/run_onchange_after_15-tools.sh.tmpl diff --git a/.chezmoiscripts/run_onchange_after_15-tools.sh.tmpl b/.chezmoiscripts/run_onchange_after_15-tools.sh.tmpl new file mode 100644 index 0000000..26a4551 --- /dev/null +++ b/.chezmoiscripts/run_onchange_after_15-tools.sh.tmpl @@ -0,0 +1,58 @@ +#!/bin/sh +# Tools that come from their own installers rather than pacman/Homebrew: +# rustup (cargo) and the packages.shared.cargo crates, the +# packages.shared.mise tools (omp, opencode, …), and strata on Linux desktop +# roles. Runs before 20-gh-extensions, whose fish session sources +# ~/.cargo/env.fish (conf.d/rustup.fish) and activates mise. +# cargo hash: {{ .packages.shared.cargo | toJson | sha256sum }} +# mise hash: {{ .packages.shared.mise | toJson | sha256sum }} +set -eu + +# Where rustup puts cargo; chezmoi's environment can predate the shell config +# that adds it. +PATH="$HOME/.cargo/bin:$PATH" +export PATH + +# rustup: -y answers its prompt (no tty under chezmoi); --no-modify-path keeps +# it out of the shell rc files, which are symlinks into this repo and already +# source ~/.cargo/env (bash, zsh, fish conf.d/rustup.fish). +if ! command -v cargo >/dev/null 2>&1; then + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path +fi + +installed=$(cargo install --list | awk '/^[^ ]/{print $1}') +{{ range .packages.shared.cargo -}} +echo "$installed" | grep -qx {{ . | quote }} || cargo install --locked {{ . | quote }} +{{ end }} + +# Global mise tools. Only tools absent from the global config are added, so a +# local pin (node = "22") is never rewritten to latest; `mise install` then +# fetches any configured version that is not installed yet. +if command -v mise >/dev/null 2>&1; then + global=$(mise ls --global --json) + missing='' + for tool in{{ range .packages.shared.mise }} {{ . | quote }}{{ end }}; do + echo "$global" | jq -e --arg t "$tool" 'has($t)' >/dev/null || missing="$missing $tool" + done + # shellcheck disable=SC2086 # one word per tool + [ -z "$missing" ] || mise use -g $missing + mise install{{ range .packages.shared.mise }} {{ . | quote }}{{ end }} + # omp's own installer (omp.sh/install) put a binary in ~/.local/bin, which + # precedes mise's shims on PATH; drop it once mise provides omp. + if [ -f "$HOME/.local/bin/omp" ] && [ ! -L "$HOME/.local/bin/omp" ] && mise which omp >/dev/null 2>&1; then + rm -f "$HOME/.local/bin/omp" + fi +else + echo "warning: mise not installed — skipping mise tools" >&2 +fi +{{- if and (eq .chezmoi.os "linux") (or (eq .role "desktop") (eq .role "laptop")) }} + +# strata (GUI file manager): binary in ~/.local/bin, desktop entry, and the +# FileManager1 D-Bus service for "Open file location". The inode/directory +# default is dot_config/mimeapps.list; --with-folder-association is not used +# because xdg-mime would replace that symlink with a plain file. Runtime deps +# are in packages.arch.desktop.pacman. +if [ ! -e "$HOME/.local/bin/strata" ]; then + curl -fsSL https://raw.githubusercontent.com/lgse/strata/main/install.sh | bash -s -- --with-desktop-entry --with-file-manager +fi +{{- end }} diff --git a/.chezmoiscripts/run_onchange_after_20-gh-extensions.sh.tmpl b/.chezmoiscripts/run_onchange_after_20-gh-extensions.sh.tmpl index 4182694..fd9f650 100644 --- a/.chezmoiscripts/run_onchange_after_20-gh-extensions.sh.tmpl +++ b/.chezmoiscripts/run_onchange_after_20-gh-extensions.sh.tmpl @@ -1,12 +1,25 @@ -#!/bin/sh +#!/usr/bin/env fish # Install gh extensions declared in .chezmoidata/packages.yaml. +# A fish script, so fish's config (~/.config/fish, applied before after-scripts +# run) is sourced first: PATH (mise, ~/.local/bin, ~/.cargo/bin) and +# GITHUB_TOKEN (conf.d/10-env.fish) match a fish session. # gh_extensions hash: {{ .packages.shared.gh_extensions | toJson | sha256sum }} -set -eu -command -v gh >/dev/null 2>&1 || { echo "warning: gh not installed — skipping extensions" >&2; exit 0; } -gh auth status >/dev/null 2>&1 || { echo "warning: gh not authenticated — run 'gh auth login', then 'chezmoi apply' again" >&2; exit 0; } +if not command -q gh + echo "warning: gh not installed — skipping extensions" >&2 + exit 0 +end +if not gh auth status >/dev/null 2>&1 + echo "warning: gh not authenticated — run 'gh auth login', then 'chezmoi apply' again" >&2 + exit 0 +end -installed=$(gh extension list 2>/dev/null | cut -f2 || true) -{{ range .packages.shared.gh_extensions }} -echo "$installed" | grep -qxF "{{ . }}" || gh extension install "{{ . }}" -{{ end }} +set -l installed (gh extension list 2>/dev/null | string split -f2 \t) +set -l failed +for ext in{{ range .packages.shared.gh_extensions }} {{ . | quote }}{{ end }} + contains -- $ext $installed; or gh extension install $ext; or set -a failed $ext +end +if set -q failed[1] + echo "error: gh extension install failed for: $failed" >&2 + exit 1 +end diff --git a/.chezmoiscripts/run_onchange_after_40-post.sh.tmpl b/.chezmoiscripts/run_onchange_after_40-post.sh.tmpl index e1e3781..1842acb 100644 --- a/.chezmoiscripts/run_onchange_after_40-post.sh.tmpl +++ b/.chezmoiscripts/run_onchange_after_40-post.sh.tmpl @@ -7,12 +7,3 @@ if command -v bat >/dev/null 2>&1; then bat cache --build >/dev/null || true; fi {{ if eq .chezmoi.os "linux" -}} if command -v fc-cache >/dev/null 2>&1; then fc-cache -f >/dev/null || true; fi {{ end -}} - -# cargo installs from packages.shared.cargo; skipped when cargo is absent. -# cargo hash: {{ .packages.shared.cargo | toJson | sha256sum }} -if command -v cargo >/dev/null 2>&1; then - installed=$(cargo install --list | awk '/^[^ ]/{print $1}') -{{ range .packages.shared.cargo }} - echo "$installed" | grep -qx "{{ . }}" || cargo install --locked "{{ . }}" -{{ end }} -fi diff --git a/.chezmoiscripts/run_onchange_before_10-packages.sh.tmpl b/.chezmoiscripts/run_onchange_before_10-packages.sh.tmpl index 9246646..077179d 100644 --- a/.chezmoiscripts/run_onchange_before_10-packages.sh.tmpl +++ b/.chezmoiscripts/run_onchange_before_10-packages.sh.tmpl @@ -28,15 +28,32 @@ aur_pkgs="{{ range .packages.arch.desktop.aur }}{{ . }} {{ end }}{{ range .packa {{ else -}} aur_pkgs="{{ range .packages.arch.aur }}{{ . }} {{ end }}" {{ end -}} -if [ -n "$(echo "$aur_pkgs" | tr -d ' ')" ]; then +# AUR names no installed package provides yet; ones already installed from a +# sync repo (cachyos, [omarchy]) are neither rebuilt nor replaced. +# shellcheck disable=SC2086 +aur_missing=$(pacman -T $aur_pkgs || true) +if [ -n "$aur_missing" ]; then + # No AUR helper yet: take yay from a sync repo that ships it (cachyos, + # chaotic-aur), else build yay-bin from the AUR (makepkg refuses root). + if ! command -v yay >/dev/null 2>&1 && ! command -v paru >/dev/null 2>&1; then + if pacman -Si yay >/dev/null 2>&1; then + $PACMAN -S --needed --noconfirm yay + elif [ "$(id -u)" -ne 0 ]; then + install_missing git base-devel + yay_build=$(mktemp -d) + git clone --depth 1 https://aur.archlinux.org/yay-bin.git "$yay_build" + (cd "$yay_build" && makepkg -si --noconfirm) + rm -rf "$yay_build" + fi + fi if command -v yay >/dev/null 2>&1; then AUR="yay" elif command -v paru >/dev/null 2>&1; then AUR="paru" else AUR=""; fi if [ -n "$AUR" ]; then # shellcheck disable=SC2086 - $AUR -S --needed --noconfirm $aur_pkgs + $AUR -S --needed --noconfirm $aur_missing else - echo "warning: yay/paru not found — skipping AUR packages: $aur_pkgs" >&2 + echo "warning: no yay/paru and none could be bootstrapped — skipping AUR packages:" $aur_missing >&2 fi fi