Compare commits
4
Commits
a9e1b686d0
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aaecbd8472 | ||
|
|
d32a30f39e | ||
|
|
4e31a23470 | ||
|
|
7c8d0eff4b |
@@ -0,0 +1,9 @@
|
||||
# Keep image builds light: the runtime mounts the live repo via -v.
|
||||
# NOTE: do NOT exclude test/ - the dev stage COPYs the repo and then runs
|
||||
# test/lint.sh from it.
|
||||
.git
|
||||
**/node_modules
|
||||
**/lazy-lock.json
|
||||
**/fish_variables
|
||||
kreation/.vst3
|
||||
term/.config/tmux/plugins
|
||||
@@ -7,15 +7,15 @@ Only entries that would be missed without explicit help are included.
|
||||
## 1. Project Scope
|
||||
|
||||
- **Dotfiles** for a personal workstation (hyprland + Neovim + VST plugin).
|
||||
- No tests, CI or build scripts – the repo is purely configuration data.
|
||||
- The only executable content is the `install_*` shell scripts that install system packages.
|
||||
- No CI or build scripts – the repo is purely configuration data; the only executable content is the `install_*` scripts and the `test/` harness.
|
||||
- **Testing** lives in `test/`. Run `make test-lint` / `make test-stow` (offline, no Docker) or `make test-smoke` (Docker-based, see `test/Dockerfile` and `README.md → Testing`). The harness currently surfaces pre-existing Makefile bugs by design (missing `/` in `install_*` script paths, `stow nvim` with no `nvim/` package).
|
||||
|
||||
---
|
||||
## 2. System Setup
|
||||
|
||||
| Task | Command | Why it matters |
|
||||
|------|---------|----------------|
|
||||
| **Install required programs** | `bash .install_skripte/install_terminal_pcks.sh` <br>`bash .install_skripte/install_basic_pcks.sh` <br>`bash .install_skripte/install_hypr_pcks.sh` <br>`bash .install_skripte/install_kreation_pcks.sh` | These scripts install Neovim, hyprland, VST runtime, and other utilities. |
|
||||
| **Install required programs** | `bash term/setup.sh` <br>`bash _install_skripte/install_basic_pcks.sh` <br>`bash _install_skripte/install_hypr_pcks.sh` <br>`bash _install_skripte/install_kreation_pcks.sh` | These scripts install Neovim, hyprland, VST runtime, and other utilities. Edit `term/packages.txt` to add/remove terminal packages. |
|
||||
| **Link configuration directories** | `ln -s ~/.dotfiles/hypr/.config/hypr ~/.config/hypr` <br>`ln -s ~/.dotfiles/term/.config/nvim ~/.config/nvim` | The repository expects the configs to live in `~/.config`. |
|
||||
| **Add VST plugin** | Copy `kreation/.vst3/MT-PowerDrumKit.vst3` into your DAW’s plugins folder (usually `~/.vst`). | The repo contains a pre‑built VST for use in audio software. |
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# setup ist ein Kommando-Target (keine Datei) und soll immer ausgeführt werden.
|
||||
.PHONY: basic
|
||||
|
||||
termn:
|
||||
bash _install_skripte_install_terminal_pcks.sh
|
||||
term:
|
||||
bash term/setup.sh
|
||||
stow term
|
||||
basic: term
|
||||
bash _install_skripte_install_basic_pcks.sh
|
||||
@@ -19,3 +19,44 @@ kreation: basic
|
||||
stow kreation
|
||||
bunters-monster: basic hypr kreation
|
||||
_install_skripte/buntes-monster-links.sh
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Testing (see README.md "Testing" and test/ for details)
|
||||
# ---------------------------------------------------------------------------
|
||||
.PHONY: test test-lint test-smoke test-stow test-shellcheck test-lint-docker
|
||||
|
||||
# Full test suite: fast offline lint first, then the optional Docker smoke.
|
||||
test: test-lint test-smoke
|
||||
|
||||
# Fast, offline integrity checks. No Docker required.
|
||||
# shellcheck is used if installed, otherwise only syntax checks run.
|
||||
test-lint:
|
||||
bash test/lint.sh
|
||||
|
||||
# Check stow package resolution without creating symlinks.
|
||||
test-stow:
|
||||
bash test/probe_stow.sh
|
||||
|
||||
# Run shellcheck over all tracked repo scripts (needs shellcheck installed).
|
||||
test-shellcheck:
|
||||
shellcheck -S warning $$(git ls-files '*.sh' | grep -v '/tpm/')
|
||||
|
||||
# Test shellcheck: build the /dev stage with shellcheck and run lint.sh.
|
||||
# docker build context is the repo root; .dockerignore excludes heavy dirs.
|
||||
test-lint-docker:
|
||||
docker build --target dev -t dotfiles-lint -f test/Dockerfile .
|
||||
docker run --rm dotfiles-lint
|
||||
|
||||
# Docker smoke test: builds the Arch container and runs the dry probes.
|
||||
# RUN_SMOKE=1 also installs the real pacman packages inside the container.
|
||||
# RUN_SMOKE=1 RUN_FULL=1 additionally attempts AUR/npm + hypr/kreation.
|
||||
# SKIP_NET=1 skips anything that contacts the network.
|
||||
# Example: make test-smoke RUN_SMOKE=1
|
||||
test-smoke:
|
||||
docker build --target test -t dotfiles-test -f test/Dockerfile .
|
||||
docker run --rm \
|
||||
-e RUN_SMOKE="$${RUN_SMOKE:-0}" \
|
||||
-e RUN_FULL="$${RUN_FULL:-0}" \
|
||||
-e SKIP_NET=$${SKIP_NET:-0} \
|
||||
-v "$$PWD":/dotfiles \
|
||||
dotfiles-test
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
# Dotfiles
|
||||
|
||||
Personal configuration files for an Arch Linux workstation (Hyprland + Neovim + VST plugin), managed with [GNU Stow](https://www.gnu.org/software/stow/).
|
||||
|
||||
## Layout
|
||||
|
||||
| Directory | Contents |
|
||||
|-----------|----------|
|
||||
| `term/` | Fish + kitty + starship + nvim + tmux + yazi + fastfetch + opencode terminal environment |
|
||||
| `hypr/` | Hyprland compositor config, kitty theme, wpaperd wallpapers |
|
||||
| `kreation/`| VST plugin (`MT-PowerDrumKit.vst3`) |
|
||||
| `local_ai/`| Local AI / MCP tooling config |
|
||||
| `_install_skripte/` | Miscellaneous install helper scripts |
|
||||
| `test/` | Docker test harness (lint + smoke tests) |
|
||||
|
||||
Each top-level directory is a **stow package**. `stow` symlinks the files from the repository into your home directory (e.g. `term/.config/...` → `~/.config/...`).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Arch Linux (or a distro with `pacman`)
|
||||
- [`stow`](https://www.gnu.org/software/stow/)
|
||||
- Relies on `git`, `sudo`, `curl`
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
# 1. Clone the repo
|
||||
git clone https://github.com/<user>/dotfiles.git ~/.dotfiles
|
||||
cd ~/.dotfiles
|
||||
|
||||
# 2. Install a target and symlink its files
|
||||
make term # terminal + fish + kitty + nvim
|
||||
make hypr # Hyprland (also pulls in basic + term)
|
||||
make kreation # VST plugin (also pulls in basic)
|
||||
```
|
||||
|
||||
Run `make <target>` from the repository root. Each target first runs the relevant install script and then `stow`s the package.
|
||||
|
||||
> The install scripts require `sudo` and may prompt for your password.
|
||||
|
||||
## Targets
|
||||
|
||||
| Target | What it does |
|
||||
|--------|--------------|
|
||||
| `make term` | Runs `term/setup.sh` (installs terminal packages, sets fish as default shell, clones tmuxifier), then `stow term` |
|
||||
| `make basic` | Runs `install_basic_pcks.sh`, stows `nvim`, runs `nextcloud.sh` |
|
||||
| `make hypr` | `make basic`, installs Hyprland packages, stows `hypr`, runs the Hyprland setup scripts (`ensure-env.sh`, `portal-host-override.sh`, `fix_dolphin_file_associations.sh`) |
|
||||
| `make kreation` | `make basic`, installs VST runtime, stows `kreation` |
|
||||
| `make buntes-monster` | Everything (`basic` + `hypr` + `kreation`), then wires up symlinks |
|
||||
|
||||
### Managing terminal packages
|
||||
|
||||
The terminal package list lives in a single editable file:
|
||||
|
||||
```
|
||||
term/packages.txt
|
||||
```
|
||||
|
||||
Add/remove packages on separate lines. Lines starting with `#` are ignored, and you can switch between package sources with section markers:
|
||||
|
||||
```
|
||||
@pacman # install via pacman (default)
|
||||
@aur # install via yay (AUR)
|
||||
@npm # install via npm -g
|
||||
```
|
||||
|
||||
The default section is `@pacman`. Any AUR dependency (`yay`) is bootstrapped automatically by `term/setup.sh`.
|
||||
|
||||
## What the install scripts do
|
||||
|
||||
- **`term/setup.sh`** – enables multilib, updates the system, installs `yay` if missing, reads `term/packages.txt` and installs all packages with `--needed` (skips already-installed ones), sets `fish` as the default shell (`chsh -s /usr/bin/fish`), clones `tmuxifier`, adds the `git` yazi plugin, and runs `sudo updatedb`. It is idempotent and safe to re-run.
|
||||
|
||||
## Stow: resolving conflicts with existing paths
|
||||
|
||||
`stow` refuses to create a symlink where a real file or directory already exists on the target (your home directory). When you first set up a machine that already has configs in `~/.config`, you typically see an error like:
|
||||
|
||||
```
|
||||
WARNING: stowing term would cause conflicts:
|
||||
* existing target is neither a link nor a directory: .config/fish/config.fish
|
||||
```
|
||||
|
||||
The **recommended** workflow is to back up the affected paths and remove them from the target location so stow can take over, then restore anything you want to keep as an overlay on top of the symlink. There are a few common approaches:
|
||||
|
||||
### 1. Back up and remove (recommended for first-time setup)
|
||||
|
||||
Point stow elsewhere so it only *reports* conflicts without touching anything, then handle the listed paths yourself:
|
||||
|
||||
```bash
|
||||
# Dry-run to see exactly which paths conflict
|
||||
cd ~/.dotfiles
|
||||
stow --verbose --simulate term
|
||||
|
||||
# Inspect the conflicts, then back them up on the target side
|
||||
mkdir -p ~/.config-backup
|
||||
mv ~/.config/fish ~/.config-backup/fish # example — repeat for each conflicting path
|
||||
|
||||
# Now stow can create the links
|
||||
stow term
|
||||
```
|
||||
|
||||
Afterwards you can restore your old configs **on top of** the symlinks if you want a custom overlay:
|
||||
|
||||
```bash
|
||||
cp -r ~/.config-backup/fish/* ~/.config/fish/
|
||||
```
|
||||
|
||||
> The backup dir lives outside the stow packages, so stow never touches it.
|
||||
|
||||
### 2. Use `--adopt` with care
|
||||
|
||||
```bash
|
||||
git stash # only if you have uncommitted repo changes you want to keep
|
||||
cd ~/.dotfiles
|
||||
stow --adopt term
|
||||
git checkout . # discard any files that stow pulled into the repo
|
||||
```
|
||||
|
||||
`stow --adopt` moves conflicting files *from your home dir into the repository* (overwriting what's stored there), then creates the symlink. This is convenient but **destructive to the repo copy** of those files — always review with a simulate run first and reset via git afterwards if you didn't intend to change the tracked config. Prefer approach #1 unless you deliberately want to recapture your live config into the repo.
|
||||
|
||||
### 3. Skip conflicts you don't manage in the repo
|
||||
|
||||
If a path simply shouldn't be managed by stow, use `--ignore`:
|
||||
|
||||
```bash
|
||||
stow --ignore='\.config/(local|misc)' term
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
The `test/` directory contains a Docker-based test harness that verifies the
|
||||
install scripts, the `Makefile`, and the stow packages.
|
||||
|
||||
There are **two tiers**:
|
||||
|
||||
| Target | What it does | Docker required? |
|
||||
|--------|--------------|------------------|
|
||||
| `make test-lint` | Fast, offline integrity checks: `bash -n` syntax pass over every script, `shellcheck` (if installed), `packages.txt` marker validation, and Makefile ↔ script/stow-package name consistency. Catches typos like `_install_skripte_install_basic_pcks.sh` (missing `/`). | No |
|
||||
| `make test-stow` | `stow --simulate` for every stow-able package; fails if a package the Makefile stows doesn't exist (e.g. `stow nvim` — nvim lives under `term/`). | No |
|
||||
| `make test-shellcheck` | Runs shellcheck over all tracked scripts (shellcheck must be installed). | No |
|
||||
| `make test-lint-docker` | Builds the `/dev` stage (`archlinux:latest` + shellcheck) and runs `test/lint.sh` inside it. | Yes |
|
||||
| `make test-smoke` | Builds the `/test` stage and runs the container's dry probes: `make -n` over every target, `stow --simulate` over every Makefile-stowed package, plus `test/lint.sh`. A network-enabled `RUN_SMOKE=1` variant installs the real pacman packages. | Yes |
|
||||
| `make test` | `test-lint` + `test-smoke`. | Partially |
|
||||
|
||||
### Docker smoke details
|
||||
|
||||
```bash
|
||||
make test-smoke # dry probes only (fast, no installs)
|
||||
make test-smoke RUN_SMOKE=1 # + real pacman installs of the term packages
|
||||
make test-smoke RUN_SMOKE=1 RUN_FULL=1 # + AUR/npm + hypr/kreation/buntes-monster
|
||||
make test-smoke SKIP_NET=1 # skip anything that contacts the network
|
||||
```
|
||||
|
||||
The container:
|
||||
|
||||
- uses `archlinux:latest` (the scripts assume Arch + pacman),
|
||||
- creates a non-root user (`mathias`, uid 1000) with passwordless sudo so the
|
||||
scripts don't hang on a password prompt,
|
||||
- mounts the live repo at `/dotfiles` (`-v "$PWD":/dotfiles`), so the tests
|
||||
always run against your current checkout,
|
||||
- stubs host daemon commands (`systemctl`, `updatedb`, `kbuildsycoca6`,
|
||||
`dbus-update-activation-environment`) as no-ops so nothing talks to a real
|
||||
systemd/dbus inside the container.
|
||||
|
||||
In the `RUN_SMOKE=1` install path, the harness additionally **verifies the
|
||||
installed packages** (`test/verify_packages.sh`):
|
||||
- every `@pacman` entry is confirmed via `pacman -Q <pkg>` (covers fonts,
|
||||
libs and python modules that ship no binary),
|
||||
- a curated mapping probes each package's binary with a real
|
||||
`--version`-style call (e.g. `fish --version`, `nvim --version`,
|
||||
`ssh -V`), failing if the binary is missing or does not report a version.
|
||||
|
||||
> **Note for the current repo state:** `make test-lint` and `make test-smoke`
|
||||
> currently **fail** because the harness surfaces pre-existing Makefile bugs
|
||||
> (missing `/` in the `install_*` script paths and `stow nvim` for a
|
||||
> non-existent `nvim/` package). That is the intended behaviour — fix the
|
||||
> Makefile and the tests should go green.
|
||||
|
||||
---
|
||||
|
||||
**When in doubt:** run `stow --verbose --simulate <target>` first — it prints what would happen (linking) without changing anything. Never `rm -rf` the `~/.config` of something you still want to keep; move it to a backup directory instead.
|
||||
@@ -1,73 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# multilib aktivieren in /etc/pacman.conf
|
||||
sudo sed -i.bak '/^#\[multilib\]/,+1 s/^#//' /etc/pacman.conf
|
||||
# Für Noobs wie mich
|
||||
# - erstellt Sicherheitskopie pacman.conf.bak vor der Änderung
|
||||
# - sucht nach "[multilib]"
|
||||
# - bezieht die nächste Zeile mit ein
|
||||
# - Ersetzt ^# mit nüscht -> Kommentar entfernt :-)
|
||||
|
||||
sudo pacman -Syu
|
||||
|
||||
# Install yay
|
||||
if pacman -Q yay &>/dev/null; then
|
||||
echo "yay bereits installiert; fahre fort."
|
||||
elif pacman -Si yay &>/dev/null; then
|
||||
echo "Installiere yay mit pacman ..."
|
||||
sudo pacman -S --needed yay
|
||||
else
|
||||
sudo pacman -S --needed base-devel git
|
||||
echo "installiere yay via git ..."
|
||||
git clone https://aur.archlinux.org/yay.git
|
||||
(
|
||||
cd yay || {
|
||||
echo "Fehler: CD in yay-Verzeichnis fehlgeschlagen";
|
||||
exit 1;
|
||||
}
|
||||
makepkg -si
|
||||
)
|
||||
rm -rf yay
|
||||
fi
|
||||
|
||||
sudo pacman -S --needed \
|
||||
fish \
|
||||
vim \
|
||||
nvim \
|
||||
openssh \
|
||||
rclone \
|
||||
rsync \
|
||||
unison \
|
||||
stow \
|
||||
nodejs \
|
||||
npm \
|
||||
starship \
|
||||
fastfetch \
|
||||
tree-sitter-cli \
|
||||
python \
|
||||
python-pip \
|
||||
extra/ttf-jetbrains-mono-nerd \
|
||||
unzip \
|
||||
ruff \
|
||||
python-ruff \
|
||||
python-debugpy \
|
||||
chromium \
|
||||
selene \
|
||||
opencode \
|
||||
tmux \
|
||||
yazi \
|
||||
zoxide \
|
||||
fzf \
|
||||
locate \
|
||||
ttf-jetbrains-mono-nerd \
|
||||
ttf-font-awesome
|
||||
|
||||
# fish as standard terminal
|
||||
chsh -s /usr/bin/fish
|
||||
|
||||
git clone https://github.com/jimeh/tmuxifier.git ~/.tmuxifier
|
||||
|
||||
npm install -g vscode-js-debug
|
||||
npm install -g eslint_d
|
||||
ya pkg add yazi-rs/plugins:git
|
||||
sudo updatedb
|
||||
@@ -37,4 +37,3 @@ fi
|
||||
|
||||
export DATAFLEX_BACKEND_REPO="/c/_Master/202602 DataFleX/repos/flexibility_platform_backend/"
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
. "$HOME/.cargo/env"
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
function fish_prompt
|
||||
# This prompt shows:
|
||||
# - green lines if the last return command is OK, red otherwise
|
||||
# - your user name, in red if root or yellow otherwise
|
||||
# - your hostname, in cyan if ssh or blue otherwise
|
||||
# - the current path (with prompt_pwd)
|
||||
# - date +%X
|
||||
# - the current virtual environment, if any
|
||||
# - the current git status, if any, with fish_git_prompt
|
||||
# - the current battery state, if any, and if your power cable is unplugged, and if you have "acpi"
|
||||
# - current background jobs, if any
|
||||
|
||||
# It goes from:
|
||||
# ┬─[nim@Hattori:~]─[11:39:00]
|
||||
# ╰─>$ echo here
|
||||
|
||||
# To:
|
||||
# ┬─[nim@Hattori:~/w/dashboard]─[11:37:14]─[V:django20]─[G:master↑1|●1✚1…1]─[B:85%, 05:41:42 remaining]
|
||||
# │ 2 15054 0% arrêtée sleep 100000
|
||||
# │ 1 15048 0% arrêtée sleep 100000
|
||||
# ╰─>$ echo there
|
||||
|
||||
set -l retc red
|
||||
test $status = 0; and set retc green
|
||||
|
||||
set -q __fish_git_prompt_showupstream
|
||||
or set -g __fish_git_prompt_showupstream auto
|
||||
|
||||
function _nim_prompt_wrapper
|
||||
set retc $argv[1]
|
||||
set -l field_name $argv[2]
|
||||
set -l field_value $argv[3]
|
||||
|
||||
set_color normal
|
||||
set_color $retc
|
||||
echo -n '─'
|
||||
set_color -o green
|
||||
echo -n '['
|
||||
set_color normal
|
||||
test -n $field_name
|
||||
and echo -n $field_name:
|
||||
set_color $retc
|
||||
echo -n $field_value
|
||||
set_color -o green
|
||||
echo -n ']'
|
||||
end
|
||||
|
||||
set_color $retc
|
||||
echo -n '┬─'
|
||||
set_color -o green
|
||||
echo -n [
|
||||
|
||||
if functions -q fish_is_root_user; and fish_is_root_user
|
||||
set_color -o red
|
||||
else
|
||||
set_color -o yellow
|
||||
end
|
||||
|
||||
echo -n $USER
|
||||
set_color -o white
|
||||
echo -n @
|
||||
|
||||
if test -z "$SSH_CLIENT"
|
||||
set_color -o blue
|
||||
else
|
||||
set_color -o cyan
|
||||
end
|
||||
|
||||
echo -n (prompt_hostname)
|
||||
set_color -o white
|
||||
echo -n :(prompt_pwd)
|
||||
set_color -o green
|
||||
echo -n ']'
|
||||
|
||||
# Date
|
||||
_nim_prompt_wrapper $retc '' (date +%X)
|
||||
|
||||
# Vi-mode
|
||||
|
||||
if test "$fish_key_bindings" = fish_vi_key_bindings
|
||||
or test "$fish_key_bindings" = fish_hybrid_key_bindings
|
||||
set -l mode
|
||||
switch $fish_bind_mode
|
||||
case default
|
||||
set mode (set_color --bold red)N
|
||||
case operator f F t T
|
||||
set mode (set_color --bold cyan)N
|
||||
case insert
|
||||
set mode (set_color --bold green)I
|
||||
case replace_one
|
||||
set mode (set_color --bold green)R
|
||||
case replace
|
||||
set mode (set_color --bold cyan)R
|
||||
case visual
|
||||
set mode (set_color --bold magenta)V
|
||||
end
|
||||
set mode $mode(set_color normal)
|
||||
_nim_prompt_wrapper $retc '' $mode
|
||||
end
|
||||
|
||||
# Virtual Environment
|
||||
set -q VIRTUAL_ENV_DISABLE_PROMPT
|
||||
or set -g VIRTUAL_ENV_DISABLE_PROMPT true
|
||||
set -q VIRTUAL_ENV
|
||||
and _nim_prompt_wrapper $retc V (path basename "$VIRTUAL_ENV")
|
||||
|
||||
# git
|
||||
set -l prompt_git (fish_git_prompt '%s')
|
||||
test -n "$prompt_git"
|
||||
and _nim_prompt_wrapper $retc G $prompt_git
|
||||
|
||||
# Battery status
|
||||
type -q acpi
|
||||
and acpi -a 2>/dev/null | string match -rq off
|
||||
and _nim_prompt_wrapper $retc B (acpi -b | cut -d' ' -f 4-)
|
||||
|
||||
# New line
|
||||
echo
|
||||
|
||||
# Background jobs
|
||||
set_color normal
|
||||
|
||||
for job in (jobs)
|
||||
set_color $retc
|
||||
echo -n '│ '
|
||||
set_color brown
|
||||
echo $job
|
||||
end
|
||||
|
||||
set_color normal
|
||||
set_color $retc
|
||||
echo -n '╰─>'
|
||||
set_color -o red
|
||||
echo -n '$ '
|
||||
set_color normal
|
||||
end
|
||||
@@ -0,0 +1,46 @@
|
||||
# ---------------------------------------------------------------------------
|
||||
# Terminal package list for term/setup.sh
|
||||
#
|
||||
# Maintenance:
|
||||
# - Add / remove packages on separate lines.
|
||||
# - Lines starting with '#' and blank lines are ignored.
|
||||
# - Use "@pacman", "@aur" or "@npm" markers to switch section.
|
||||
# - The default section is @pacman.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@pacman
|
||||
fish
|
||||
extra/kitty
|
||||
neovim
|
||||
openssh
|
||||
rclone
|
||||
rsync
|
||||
unison
|
||||
stow
|
||||
nodejs
|
||||
npm
|
||||
starship
|
||||
fastfetch
|
||||
tree-sitter-cli
|
||||
python
|
||||
python-pip
|
||||
unzip
|
||||
ruff
|
||||
python-ruff
|
||||
python-debugpy
|
||||
chromium
|
||||
selene
|
||||
tmux
|
||||
yazi
|
||||
zoxide
|
||||
fzf
|
||||
locate
|
||||
ttf-jetbrains-mono-nerd
|
||||
ttf-font-awesome
|
||||
|
||||
@aur
|
||||
opencode
|
||||
|
||||
@npm
|
||||
eslint_d
|
||||
vscode-js-debug
|
||||
Executable
+154
@@ -0,0 +1,154 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PACKAGES_FILE="$SCRIPT_DIR/packages.txt"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Enable multilib in /etc/pacman.conf (with a safety backup)
|
||||
# ---------------------------------------------------------------------------
|
||||
enable_multilib() {
|
||||
sudo sed -i.bak '/^#\[multilib\]/,+1 s/^#//' /etc/pacman.conf
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Ensure yay (AUR helper) is installed.
|
||||
# ---------------------------------------------------------------------------
|
||||
install_yay() {
|
||||
if pacman -Q yay &>/dev/null; then
|
||||
echo "yay bereits installiert; fahre fort."
|
||||
return
|
||||
fi
|
||||
if pacman -Si yay &>/dev/null; then
|
||||
echo "Installiere yay mit pacman ..."
|
||||
sudo pacman -S --needed yay
|
||||
return
|
||||
fi
|
||||
echo "installiere yay via git ..."
|
||||
sudo pacman -S --needed base-devel git
|
||||
local tmp
|
||||
tmp="$(mktemp -d)"
|
||||
git clone https://aur.archlinux.org/yay.git "$tmp/yay"
|
||||
(
|
||||
cd "$tmp/yay"
|
||||
makepkg -si
|
||||
)
|
||||
rm -rf "$tmp"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Read packages.txt, split into pacman / aur / npm sections.
|
||||
# ---------------------------------------------------------------------------
|
||||
read_packages() {
|
||||
local section="pacman"
|
||||
local current
|
||||
current=""
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
current="${line%%#*}"
|
||||
current="${current##*( )}"
|
||||
current="${current%%*( )}"
|
||||
[[ -z "$current" ]] && continue
|
||||
case "$current" in
|
||||
@pacman) section="pacman" ;;
|
||||
@aur) section="aur" ;;
|
||||
@npm) section="npm" ;;
|
||||
*)
|
||||
case "$section" in
|
||||
pacman) pacman_pkgs+=("$current") ;;
|
||||
aur) aur_pkgs+=("$current") ;;
|
||||
npm) npm_pkgs+=("$current") ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
done < "$PACKAGES_FILE"
|
||||
}
|
||||
|
||||
declare -a pacman_pkgs=() aur_pkgs=() npm_pkgs=()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Install sections.
|
||||
# ---------------------------------------------------------------------------
|
||||
install_pacman() {
|
||||
if ((${#pacman_pkgs[@]})); then
|
||||
echo "==> Installiere Pacman-Pakete: ${pacman_pkgs[*]}"
|
||||
yay -S --needed "${pacman_pkgs[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
install_aur() {
|
||||
if ((${#aur_pkgs[@]})); then
|
||||
echo "==> Installiere AUR-Pakete: ${aur_pkgs[*]}"
|
||||
yay -S --needed "${aur_pkgs[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
install_npm() {
|
||||
if ((${#npm_pkgs[@]})); then
|
||||
echo "==> Installiere globale npm-Pakete: ${npm_pkgs[*]}"
|
||||
npm install -g "${npm_pkgs[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Post-install configuration.
|
||||
# ---------------------------------------------------------------------------
|
||||
set_fish_default_shell() {
|
||||
if command -v fish >/dev/null 2>&1; then
|
||||
if [[ "$SHELL" != "/usr/bin/fish" ]]; then
|
||||
echo "Setze fish als Standard-Shell ..."
|
||||
chsh -s /usr/bin/fish
|
||||
else
|
||||
echo "fish ist bereits die Standard-Shell."
|
||||
fi
|
||||
else
|
||||
echo "Warnung: fish wurde nicht installiert; ueberspringe chsh."
|
||||
fi
|
||||
}
|
||||
|
||||
install_tmuxifier() {
|
||||
if [[ -d "$HOME/.tmuxifier" ]]; then
|
||||
echo "tmuxifier bereits installiert; fahre fort."
|
||||
else
|
||||
echo "Klone tmuxifier ..."
|
||||
git clone https://github.com/jimeh/tmuxifier.git "$HOME/.tmuxifier"
|
||||
fi
|
||||
}
|
||||
|
||||
add_yazi_git_plugin() {
|
||||
if command -v yazi >/dev/null 2>&1 && command -v ya >/dev/null 2>&1; then
|
||||
echo "Fuege yazi git-Plugin hinzu ..."
|
||||
ya pkg add yazi-rs/plugins:git
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------------------------------------
|
||||
main() {
|
||||
echo "==> Aktiviere multilib ..."
|
||||
enable_multilib
|
||||
|
||||
echo "==> System aktualisieren ..."
|
||||
sudo pacman -Syu
|
||||
|
||||
echo "==> Sicherstelle yay ..."
|
||||
install_yay
|
||||
|
||||
echo "==> Lese Paketliste: $PACKAGES_FILE"
|
||||
read_packages
|
||||
|
||||
install_pacman
|
||||
install_aur
|
||||
install_npm
|
||||
|
||||
set_fish_default_shell
|
||||
install_tmuxifier
|
||||
add_yazi_git_plugin
|
||||
|
||||
echo "==> Aktualisiere locate-Datenbank ..."
|
||||
sudo updatedb
|
||||
|
||||
echo "Fertig. Starte ein neues Terminal, damit fish und die Umgebung greifen."
|
||||
}
|
||||
|
||||
main
|
||||
@@ -0,0 +1,73 @@
|
||||
# test/Dockerfile
|
||||
#
|
||||
# Docker test harness for the dotfiles repo.
|
||||
#
|
||||
# Two stages:
|
||||
# --target dev : fast shellcheck/lint environment (no heavy installs)
|
||||
# --target test : smoke-test environment that can run the install scripts
|
||||
#
|
||||
# Build with (repo root as context; .dockerignore trims heavy/binary dirs):
|
||||
# docker build --target test -t dotfiles-test -f test/Dockerfile .
|
||||
# Run with:
|
||||
# docker run --rm -v "$PWD":/dotfiles dotfiles-test
|
||||
#
|
||||
# Or simply use the Makefile targets:
|
||||
# make test-lint-docker # build dev stage, run shellcheck+lint
|
||||
# make test-smoke # dry probes; add RUN_SMOKE=1 for real installs
|
||||
#
|
||||
# See README.md ("Testing") and test/entrypoint-test.sh for details.
|
||||
|
||||
FROM archlinux:latest AS base
|
||||
|
||||
# Non-root test user that mirrors the workstation account.
|
||||
ARG UID_ARG=1000
|
||||
ARG USERNAME=mathias
|
||||
|
||||
RUN pacman -Syu --noconfirm \
|
||||
base-devel \
|
||||
git \
|
||||
stow \
|
||||
curl \
|
||||
which \
|
||||
sudo \
|
||||
&& pacman -Scc --noconfirm
|
||||
|
||||
RUN useradd -m -u "$UID_ARG" -s /bin/bash -G wheel "$USERNAME"
|
||||
|
||||
# Passwordless sudo so scripts that call `sudo pacman`, `sudo systemctl`, etc.
|
||||
# do not hang on a password prompt inside the container.
|
||||
RUN printf '%%wheel ALL=(ALL) NOPASSWD: ALL\n' > /etc/sudoers.d/wheel-nopasswd \
|
||||
&& chmod 440 /etc/sudoers.d/wheel-nopasswd
|
||||
|
||||
USER "$USERNAME"
|
||||
ENV HOME="/home/$USERNAME"
|
||||
WORKDIR /dotfiles
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# dev stage: shellcheck/lint only. Fast, no install heavy-lifting.
|
||||
# ---------------------------------------------------------------------------
|
||||
FROM base AS dev
|
||||
|
||||
USER root
|
||||
RUN pacman -S --noconfirm --needed shellcheck
|
||||
|
||||
USER "$USERNAME"
|
||||
WORKDIR /dotfiles
|
||||
|
||||
COPY --chown="$USERNAME" . /dotfiles
|
||||
|
||||
CMD ["bash", "test/lint.sh"]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# test stage: prepared to actually run the install scripts & probe stow.
|
||||
# Uses the mounted repo (see -v $PWD:/dotfiles in the Makefile) so scripts
|
||||
# see the live workspace checkout.
|
||||
# ---------------------------------------------------------------------------
|
||||
FROM base AS test
|
||||
|
||||
USER "$USERNAME"
|
||||
WORKDIR /dotfiles
|
||||
|
||||
# The entrypoint drives the smoke tests. The repo is mounted at /dotfiles
|
||||
# via the Makefile's `docker run -v`.
|
||||
ENTRYPOINT ["bash", "/dotfiles/test/entrypoint-test.sh"]
|
||||
Executable
+252
@@ -0,0 +1,252 @@
|
||||
#!/bin/bash
|
||||
# test/entrypoint-test.sh
|
||||
#
|
||||
# Runs inside the docker `test` stage. The repo is mounted read-write at
|
||||
# /dotfiles (see the Makefile's `docker run -v "$PWD":/dotfiles`).
|
||||
#
|
||||
# Two tiers:
|
||||
# 1. DRY PROBES (always, fast, offline)
|
||||
# - `make -n` dry-runs to catch Makefile typos / missing scripts
|
||||
# - `stow --simulate` to prove every stow target resolves
|
||||
# 2. REAL SMOKE (guarded by RUN_SMOKE=1)
|
||||
# - exercises term/setup.sh package parsing and (optionally) installs
|
||||
# - verifies stow actually creates symlinks into $HOME
|
||||
# Full AUR/npm + kreation/buntes-monster only run with RUN_FULL=1.
|
||||
#
|
||||
# Env flags (set via `docker run -e`):
|
||||
# RUN_SMOKE=1 run the real pacman-install smoke
|
||||
# RUN_FULL=1 also attempt AUR/npm + hypr/kreation/buntes-monster
|
||||
# SKIP_NET=1 pretend no network; skip anything that needs the mirror
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
# Resolve the repo root: prefer the container's /dotfiles mount, otherwise
|
||||
# fall back to the directory above this script (a local checkout).
|
||||
if [[ -d /dotfiles ]]; then
|
||||
cd /dotfiles || { echo "FATAL: repo not mounted at /dotfiles"; exit 1; }
|
||||
else
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")/.." || { echo "FATAL: cannot locate repo root"; exit 1; }
|
||||
fi
|
||||
REPO="$(pwd)"
|
||||
|
||||
# Guard: both resolution paths must find a real checkout.
|
||||
if [[ ! -f "$REPO/Makefile" || ! -d "$REPO/test" ]]; then
|
||||
echo "FATAL: '$REPO' does not look like the dotfiles repo root." >&2
|
||||
echo " In docker, run with -v \"\$PWD\":/dotfiles ." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
say_ok() { printf ' ok: %s\n' "$*"; PASS=$((PASS+1)); }
|
||||
say_fail(){ printf ' FAIL: %s\n' "$*"; FAIL=$((FAIL+1)); }
|
||||
header() { printf '\n==== %s ====\n' "$*"; }
|
||||
|
||||
if command -v git >/dev/null 2>&1 && [[ ! -d .git ]]; then
|
||||
# when mounted from a worktree, .git may be a file; fine.
|
||||
:
|
||||
fi
|
||||
|
||||
echo "entrypoint-test.sh: running as $(id -un) in $(pwd)"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 1. DRY PROBES - always run, cheap, offline.
|
||||
# ---------------------------------------------------------------------------
|
||||
header "Dry probes"
|
||||
|
||||
# 1a. Makefile dry runs for every target.
|
||||
# Discover targets dynamically so we never drift from the Makefile.
|
||||
targets=$(grep -oE '^[A-Za-z0-9_-]+:' Makefile | tr -d ':' | grep -v '^test' | sort -u)
|
||||
for target in $targets; do
|
||||
# `make -n` must not error out from missing scripts / dirs.
|
||||
if ( cd "$REPO" && make -n "$target" >/dev/null 2>err.txt ); then
|
||||
say_ok "make -n $target parses"
|
||||
else
|
||||
say_fail "make -n $target (typo? missing script?)"
|
||||
sed 's/^/ /' err.txt 2>/dev/null
|
||||
fi
|
||||
rm -f err.txt
|
||||
done
|
||||
|
||||
# 1b. stow --simulate for every package the Makefile stows.
|
||||
while IFS= read -r pkg; do
|
||||
[[ -z "$pkg" ]] && continue
|
||||
if stow --simulate --dir="$REPO" --target="$REPO" "$pkg" 2>err.txt; then
|
||||
say_ok "stow --simulate $pkg resolves"
|
||||
else
|
||||
say_fail "stow --simulate $pkg (non-existent package or conflict)"
|
||||
sed 's/^/ /' err.txt 2>/dev/null
|
||||
fi
|
||||
rm -f err.txt
|
||||
done < <(grep -P '^\t' Makefile | grep -oE 'stow [A-Za-z0-9_-]+' | awk '{print $2}' | sort -u)
|
||||
|
||||
# 1c. run the repo's own lint + probe scripts (they're mounted here).
|
||||
if [[ -x test/lint.sh ]] || [[ -f test/lint.sh ]]; then
|
||||
if bash test/lint.sh >/dev/null 2>&1; then
|
||||
say_ok "test/lint.sh"
|
||||
else
|
||||
say_fail "test/lint.sh (see logs above / rerun outside container)"
|
||||
fi
|
||||
else
|
||||
say_fail "test/lint.sh not present in mount"
|
||||
fi
|
||||
|
||||
header "Smoke gates"
|
||||
echo " RUN_SMOKE=${RUN_SMOKE:-0} RUN_FULL=${RUN_FULL:-0} SKIP_NET=${SKIP_NET:-0}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2. REAL SMOKE (RUN_SMOKE=1)
|
||||
# ---------------------------------------------------------------------------
|
||||
if [[ "${RUN_SMOKE:-0}" == "1" ]]; then
|
||||
header "Real smoke (RUN_SMOKE=1)"
|
||||
|
||||
# 2a. Allow scripts to use sudo without a password (already NOPASSWD via
|
||||
# the Dockerfile for the wheel group).
|
||||
#
|
||||
# 2b. Override host-daemon commands with no-ops so nothing tries to talk
|
||||
# to systemd/dbus/dbus in the container.
|
||||
mkdir -p "$HOME/bin"
|
||||
cat > "$HOME/bin/systemctl" <<'NOOP'
|
||||
#!/bin/bash
|
||||
echo "[stub] systemctl $*"
|
||||
exit 0
|
||||
NOOP
|
||||
cat > "$HOME/bin/dbus-update-activation-environment" <<'NOOP'
|
||||
#!/bin/bash
|
||||
echo "[stub] dbus-update-activation-environment $*"
|
||||
exit 0
|
||||
NOOP
|
||||
cat > "$HOME/bin/kbuildsycoca6" <<'NOOP'
|
||||
#!/bin/bash
|
||||
echo "[stub] kbuildsycoca6 $*"
|
||||
exit 0
|
||||
NOOP
|
||||
cat > "$HOME/bin/updatedb" <<'NOOP'
|
||||
#!/bin/bash
|
||||
echo "[stub] updatedb $*"
|
||||
exit 0
|
||||
NOOP
|
||||
chmod +x "$HOME/bin/"*
|
||||
export PATH="$HOME/bin:$PATH"
|
||||
|
||||
# 2c. term/setup.sh package parsing sanity (mirrors read_packages).
|
||||
if ( cd "$REPO" && bash -n term/setup.sh ); then
|
||||
say_ok "term/setup.sh syntax OK"
|
||||
else
|
||||
say_fail "term/setup.sh syntax error"
|
||||
fi
|
||||
pacman_count=$(awk '
|
||||
/^@pacman/{s="pacman"}
|
||||
/^@aur/{s="aur"}
|
||||
/^@npm/{s="npm"}
|
||||
/^[[:space:]]*#/{next}
|
||||
/^[[:space:]]*$/{next}
|
||||
s=="pacman"{pc++} s=="aur"{ac++} s=="npm"{nc++}
|
||||
END{printf "%d %d %d", pc,ac,nc}
|
||||
' $REPO/term/packages.txt)
|
||||
read -r PC AC NC <<< "$pacman_count"
|
||||
say_ok "packages.txt -> ${PC} pacman, ${AC} aur, ${NC} npm entries"
|
||||
|
||||
# 2d. If network allowed and not skipped, actually install the pacman
|
||||
# section of term/setup.sh (mirrors `make term` without AUR/npm).
|
||||
if [[ "${SKIP_NET:-0}" != "1" ]]; then
|
||||
header "make term (pacman packages, AUR/npm skipped)"
|
||||
# Patch packages.txt to drop @aur/@npm so we don't hit AUR/npm.
|
||||
cp "$REPO/term/packages.txt" "$HOME/packages.txt.full"
|
||||
awk '
|
||||
/^@aur$/{s="skip"}; /^@npm$/{s="skip"}; /^@pacman$/{s="pacman"}
|
||||
{ if (s!="skip" && NF>0 && $1 !~ /^#/) print }
|
||||
' "$REPO/term/packages.txt" > "$HOME/packages.txt.term-only"
|
||||
# set a scratch HOME for setup.sh so it doesn't touch the real one.
|
||||
mkdir -p "$HOME/term"
|
||||
cp "$REPO/term/setup.sh" "$HOME/term/setup.sh"
|
||||
cp "$HOME/packages.txt.term-only" "$HOME/term/packages.txt"
|
||||
# chsh is stubbed via $HOME/bin so even `chsh` works without a pty.
|
||||
setup_ok=0
|
||||
if ( cd "$HOME/term" && bash setup.sh ) ; then
|
||||
say_ok "term/setup.sh (term-only) completed"
|
||||
setup_ok=1
|
||||
else
|
||||
say_fail "term/setup.sh (term-only) failed (exit $?)"
|
||||
fi
|
||||
|
||||
# 2d2. Verify that every package is really installed and its binary
|
||||
# runs. Only meaningful if the install itself succeeded.
|
||||
if [[ "$setup_ok" == "1" ]]; then
|
||||
if [[ -x test/verify_packages.sh ]]; then
|
||||
header "verify installed packages"
|
||||
if bash test/verify_packages.sh "$HOME/packages.txt.term-only"; then
|
||||
say_ok "verify_packages.sh: all packages verified"
|
||||
else
|
||||
say_fail "verify_packages.sh: one or more packages failed verification (see above)"
|
||||
fi
|
||||
else
|
||||
say_fail "test/verify_packages.sh missing"
|
||||
fi
|
||||
else
|
||||
echo " skipping package verification (install did not complete)"
|
||||
fi
|
||||
else
|
||||
echo " SKIP_NET=1 -> skipping real package install and verification"
|
||||
fi
|
||||
|
||||
# 2e. stow --actual into $HOME to prove real symlinks are created.
|
||||
header "stow real symlinks into \$HOME"
|
||||
for pkg in term hypr kreation local_ai; do
|
||||
if [[ -d "$REPO/$pkg" ]]; then
|
||||
if stow --dir="$REPO" --target="$HOME" "$pkg" 2>err.txt; then
|
||||
say_ok "stow $pkg into \$HOME"
|
||||
else
|
||||
say_fail "stow $pkg into \$HOME"
|
||||
sed 's/^/ /' err.txt 2>/dev/null
|
||||
fi
|
||||
rm -f err.txt
|
||||
else
|
||||
say_fail "package dir missing: $pkg"
|
||||
fi
|
||||
done
|
||||
# sanity: representative symlinks must now exist in $HOME.
|
||||
missing=""
|
||||
for link in .config/fish .config/hypr .vst3/MT-PowerDrumKit.vst3; do
|
||||
[[ -e "$HOME/$link" ]] || missing="$missing $link"
|
||||
done
|
||||
if [[ -z "$missing" ]]; then
|
||||
say_ok "stow created representative \$HOME symlinks"
|
||||
else
|
||||
say_fail "missing expected stow symlinks:$missing"
|
||||
fi
|
||||
|
||||
# 2f. kreation package: verify the VST path is stow-able and present.
|
||||
header "kreation VST"
|
||||
if [[ -f "$REPO/kreation/.vst3/MT-PowerDrumKit.vst3/Contents/x86_64-linux/MT-PowerDrumKit.so" ]]; then
|
||||
say_ok "MT-PowerDrumKit.vst3 binary present"
|
||||
else
|
||||
say_fail "MT-PowerDrumKit.vst3 binary missing"
|
||||
fi
|
||||
else
|
||||
echo " RUN_SMOKE not set -> skipping real installs & symlink creation."
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 3. FULL (RUN_FULL=1) - opt-in heavy path (AUR/npm/hypr/kreation)
|
||||
# ---------------------------------------------------------------------------
|
||||
if [[ "${RUN_FULL:-0}" == "1" ]] && [[ "${SKIP_NET:-0}" != "1" ]]; then
|
||||
header "Full smoke (RUN_FULL=1)"
|
||||
if ( cd "$REPO" && make buntes-monster ); then
|
||||
say_ok "make buntes-monster"
|
||||
else
|
||||
say_fail "make buntes-monster (exit $?)"
|
||||
fi
|
||||
else
|
||||
echo " RUN_FULL not set (or SKIP_NET) -> skipping full/hypr/kreation install."
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Summary & exit code
|
||||
# ---------------------------------------------------------------------------
|
||||
echo
|
||||
echo "=============================================="
|
||||
echo "entrypoint-test.sh: $PASS passed, $FAIL failed"
|
||||
echo "=============================================="
|
||||
# Exit non-zero if anything failed.
|
||||
[[ $FAIL -eq 0 ]]
|
||||
Executable
+145
@@ -0,0 +1,145 @@
|
||||
#!/bin/bash
|
||||
# test/lint.sh
|
||||
#
|
||||
# Fast, offline integrity checks for the dotfiles repo.
|
||||
# Does NOT require Docker or any packages to be installed beyond:
|
||||
# - shellcheck (silently skipped if unavailable)
|
||||
# - bash (for `bash -n` syntax checks)
|
||||
#
|
||||
# Run with: make test-lint
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CD="$(pwd)"
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
|
||||
say_fail() { printf 'FAIL: %s\n' "$*"; FAIL=$((FAIL+1)); }
|
||||
say_pass() { printf 'ok: %s\n' "$*"; PASS=$((PASS+1)); }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 1. Collect scripts to check.
|
||||
# Only repo-owned scripts; skip vendored tpm/ plugin shell scripts.
|
||||
# Preferred source is git ls-files (the repo checkout); fall back to a
|
||||
# filesystem scan when .git is not present (e.g. inside the docker dev
|
||||
# image, where the repo is COPYied without .git).
|
||||
# ---------------------------------------------------------------------------
|
||||
mapfile -t SCRIPTS < <(git ls-files '*.sh' 2>/dev/null | grep -v '/tpm/' | grep -vE '/plugins/[^/]+/scripts/')
|
||||
if [[ ${#SCRIPTS[@]} -eq 0 ]]; then
|
||||
mapfile -t SCRIPTS < <(find . -name '*.sh' -not -path './.git/*' -not -path '*/tpm/*' -not -path '*/.git/*' | sort)
|
||||
fi
|
||||
|
||||
if [[ ${#SCRIPTS[@]} -eq 0 ]]; then
|
||||
say_fail "no *.sh files found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "== Checking $((${#SCRIPTS[@]})) tracked shell scripts =="
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2. Syntax check (bash -n) - always runs, no external deps.
|
||||
# ---------------------------------------------------------------------------
|
||||
for s in "${SCRIPTS[@]}"; do
|
||||
if bash -n "$s" 2>/dev/null; then
|
||||
:
|
||||
else
|
||||
say_fail "bash -n $s"
|
||||
fi
|
||||
done
|
||||
[[ $FAIL -eq 0 ]] && say_pass "bash -n syntax check passed"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 3. shellcheck (optional dependency).
|
||||
# ---------------------------------------------------------------------------
|
||||
if command -v shellcheck >/dev/null 2>&1; then
|
||||
echo "== Running shellcheck =="
|
||||
if shellcheck --version >/dev/null 2>&1; then
|
||||
if shellcheck -S warning "${SCRIPTS[@]}" ; then
|
||||
say_pass "shellcheck (warning severity)"
|
||||
else
|
||||
say_fail "shellcheck reported issues (warning severity)"
|
||||
fi
|
||||
else
|
||||
say_fail "shellcheck binary present but not functional"
|
||||
fi
|
||||
else
|
||||
echo "== shellcheck not installed; skipping (install with: pacman -S shellcheck) =="
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 4. packages.txt parsing sanity (mirrors term/setup.sh read_packages).
|
||||
# ---------------------------------------------------------------------------
|
||||
echo "== Checking term/packages.txt markers =="
|
||||
if grep -q '^@pacman' term/packages.txt; then
|
||||
say_pass "packages.txt declares @pacman section"
|
||||
else
|
||||
say_fail "packages.txt missing @pacman marker"
|
||||
fi
|
||||
# every @aur/@npm marker must be matched by a real consumer in setup.sh
|
||||
for marker in '@aur' '@npm'; do
|
||||
if grep -q "^$marker" term/packages.txt; then
|
||||
grep -q "$marker" term/setup.sh \
|
||||
&& say_pass "packages.txt $marker handled by term/setup.sh" \
|
||||
|| say_fail "packages.txt uses $marker but term/setup.sh does not handle it"
|
||||
else
|
||||
say_pass "packages.txt has no $marker section"
|
||||
fi
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 5. Makefile <-> install script name consistency.
|
||||
# Catches typos such as `_install_skripte_install_basic_pcks.sh`
|
||||
# (missing '/') in Makefile targets.
|
||||
# ---------------------------------------------------------------------------
|
||||
echo "== Checking Makefile references install scripts that exist =="
|
||||
# Pull out every `*_install_*_pcks.sh` token mentioned in the Makefile.
|
||||
while IFS= read -r tok; do
|
||||
[[ -z "$tok" ]] && continue
|
||||
if [[ -e "$REPO_ROOT/$tok" ]]; then
|
||||
say_pass "Makefile references existing script: $tok"
|
||||
else
|
||||
say_fail "Makefile references missing script: $tok"
|
||||
fi
|
||||
done < <(grep -P '^\t' Makefile | grep -oE '[_A-Za-z0-9/-]*install_[A-Za-z0-9_-]*\.sh' | sort -u)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 6. Makefile <-> stow package name consistency.
|
||||
# Catches bugs such as `stow nvim` when the actual package dir is `term/`.
|
||||
# ---------------------------------------------------------------------------
|
||||
echo "== Checking Makefile stow targets reference real package dirs =="
|
||||
# A 'package' is any tracked top-level directory (a .config/ subtree is stowable).
|
||||
have_pkg() {
|
||||
local name="$1"
|
||||
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
git ls-files 2>/dev/null | awk -F/ 'NF>1 {print $1}' | sort -u | grep -qx "$name"
|
||||
else
|
||||
# git-less checkout (e.g. docker dev image): fall back to filesystem.
|
||||
# Only verify the dir exists; "is it stowable" is validated by stow
|
||||
# itself during the smoke test.
|
||||
[[ -d "$name" ]]
|
||||
fi
|
||||
}
|
||||
|
||||
while IFS= read -r pkg; do
|
||||
[[ -z "$pkg" ]] && continue
|
||||
case "$pkg" in
|
||||
\#*|"") continue ;;
|
||||
esac
|
||||
if have_pkg "$pkg" && [[ -d "$REPO_ROOT/$pkg" ]]; then
|
||||
say_pass "Makefile stows existing package: $pkg"
|
||||
else
|
||||
say_fail "Makefile stows non-existent package: $pkg"
|
||||
fi
|
||||
done < <(grep -P '^\t' Makefile | grep -oE 'stow [A-Za-z0-9_-]+' | awk '{print $2}' | sort -u)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Summary
|
||||
# ---------------------------------------------------------------------------
|
||||
echo
|
||||
echo "=============================================="
|
||||
echo "lint.sh: $PASS passed, $FAIL failed"
|
||||
echo "=============================================="
|
||||
[[ $FAIL -eq 0 ]]
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
# test/probe_stow.sh
|
||||
#
|
||||
# Verifies that each package the Makefile stows actually resolves and that
|
||||
# stow can create all symlinks into the target without conflicts.
|
||||
#
|
||||
# This is a DRY RUN: it uses `stow --simulate` and creates no real symlinks.
|
||||
# It catches bugs like `stow nvim` for a non-existent `nvim/` package.
|
||||
#
|
||||
# Usage:
|
||||
# bash test/probe_stow.sh # probe every stow-able package
|
||||
# bash test/probe_stow.sh <pkg>... # probe specific packages
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
# A stow-able package is any tracked top-level directory that contains a
|
||||
# dot-prefixed config subtree (or a dot-prefixed top-level file/dir).
|
||||
tracked_basenames() {
|
||||
git ls-files | awk -F/ 'NF>1 {print $1}' | sort -u
|
||||
}
|
||||
|
||||
is_stowable() {
|
||||
local pkg="$1"
|
||||
[[ -d "$REPO_ROOT/$pkg" ]] || return 1
|
||||
# stow needs at least one dot-prefixed entry at the package root
|
||||
git ls-files "$pkg/" | grep -qE "^$pkg/\." || return 1
|
||||
}
|
||||
|
||||
# Determine which packages to probe.
|
||||
PACKAGES=()
|
||||
if [[ $# -eq 0 ]]; then
|
||||
while IFS= read -r b; do
|
||||
[[ -z "$b" ]] && continue
|
||||
is_stowable "$b" && PACKAGES+=("$b")
|
||||
done < <(tracked_basenames)
|
||||
else
|
||||
PACKAGES=("$@")
|
||||
fi
|
||||
|
||||
if [[ ${#PACKAGES[@]} -eq 0 ]]; then
|
||||
echo "probe_stow.sh: no stow-able packages found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "== Probing stow packages: ${PACKAGES[*]} =="
|
||||
|
||||
FAIL=0
|
||||
for pkg in "${PACKAGES[@]}"; do
|
||||
echo "--- stow --simulate: $pkg ---"
|
||||
if stow --simulate --verbose=1 --dir="$REPO_ROOT" --target="$REPO_ROOT" "$pkg" >/dev/null 2>probe_err.txt; then
|
||||
echo "ok: stow --simulate '$pkg' resolved without conflicts"
|
||||
else
|
||||
echo "FAIL: stow --simulate '$pkg'"
|
||||
sed 's/^/ /' probe_err.txt
|
||||
FAIL=$((FAIL+1))
|
||||
fi
|
||||
rm -f probe_err.txt
|
||||
done
|
||||
|
||||
# Cross-check: does the Makefile stow anything that is NOT in our package set?
|
||||
echo "== Checking Makefile stow targets against stow-able packages =="
|
||||
make_pkgs="$(grep -P '^\t' Makefile | grep -oE 'stow [A-Za-z0-9_-]+' | awk '{print $2}' | sort -u)"
|
||||
for mp in $make_pkgs; do
|
||||
if is_stowable "$mp"; then
|
||||
echo "ok: Makefile 'stow $mp' matches a stow-able package"
|
||||
else
|
||||
echo "FAIL: Makefile 'stow $mp' does not match a stow-able package"
|
||||
FAIL=$((FAIL+1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo
|
||||
echo "=============================================="
|
||||
echo "probe_stow.sh: $(( ${#PACKAGES[@]} + $(printf '%s\n' $make_pkgs | grep -c . || true) )) checks, $FAIL failed"
|
||||
echo "=============================================="
|
||||
[[ $FAIL -eq 0 ]]
|
||||
Executable
+121
@@ -0,0 +1,121 @@
|
||||
#!/bin/bash
|
||||
# test/verify_packages.sh
|
||||
#
|
||||
# Post-install verification of the packages the smoke test installs.
|
||||
# Given a package-list file (one package per line, `@section` markers
|
||||
# allowed - only the section content is verified), it checks that every
|
||||
# listed package is:
|
||||
# 1. installed in the pacman DB -> `pacman -Q <pkg>`
|
||||
# 2. its expected binary exists and reports a version (curated mapping)
|
||||
#
|
||||
# Layer 2 runs a real `--version`-style probe for packages that install a
|
||||
# binary, so a broken/renamed executable is caught even if pacman reports
|
||||
# the package as installed. Fonts/libs/modules without a binary are verified
|
||||
# by Layer 1 only.
|
||||
#
|
||||
# Usage: bash test/verify_packages.sh <package-list-file>
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
PKGS_FILE="${1:?usage: verify_packages.sh <package-list-file>}"
|
||||
[[ -f "$PKGS_FILE" ]] || { echo "verify_packages.sh: not a file: $PKGS_FILE" >&2; exit 1; }
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
say_ok() { printf ' ok: %s\n' "$*"; PASS=$((PASS+1)); }
|
||||
say_fail() { printf ' FAIL: %s\n' "$*"; FAIL=$((FAIL+1)); }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Curated mapping: package -> "binary version_flag"
|
||||
# - packages not listed here are checked with pacman -Q only
|
||||
# - flag missing (empty) => existence check only
|
||||
# ---------------------------------------------------------------------------
|
||||
declare -A BINS=(
|
||||
[fish]="fish --version"
|
||||
[kitty]="kitty --version"
|
||||
[neovim]="nvim --version"
|
||||
[openssh]="ssh -V"
|
||||
[rclone]="rclone --version"
|
||||
[rsync]="rsync --version"
|
||||
[unison]="unison -version"
|
||||
[stow]="stow --version"
|
||||
[nodejs]="node --version"
|
||||
[npm]="npm --version"
|
||||
[starship]="starship --version"
|
||||
[fastfetch]="fastfetch --version"
|
||||
[tree-sitter-cli]="tree-sitter --version"
|
||||
[python]="python --version"
|
||||
[python-pip]="pip3 --version"
|
||||
[unzip]="unzip --version"
|
||||
[selene]="selene --version"
|
||||
[tmux]="tmux -V"
|
||||
[yazi]="yazi --version"
|
||||
[zoxide]="zoxide --version"
|
||||
[fzf]="fzf --version"
|
||||
[chromium]="chromium --version"
|
||||
[locate]="locate --version"
|
||||
)
|
||||
|
||||
# python-pip may expose `pip` instead of `pip3` on some setups; treat them
|
||||
# as interchangeable so the probe does not spuriously fail.
|
||||
probe_pip() {
|
||||
for b in pip3 pip; do
|
||||
if command -v "$b" >/dev/null 2>&1; then
|
||||
out=$("$b" --version 2>&1) && [[ -n "$out" ]] && { say_ok "$b --version ($out)"; return 0; }
|
||||
fi
|
||||
done
|
||||
say_fail "python-pip: neither pip3 nor pip found/probe failed"
|
||||
return 1
|
||||
}
|
||||
|
||||
echo "== Verifying installed packages from: $PKGS_FILE =="
|
||||
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
line="${line%%#*}" # strip trailing comments
|
||||
line="${line// /}" # drop inline whitespace
|
||||
[[ -z "$line" ]] && continue
|
||||
[[ "$line" == @* ]] && continue # skip @pacman/@aur/@npm section markers
|
||||
|
||||
pkg="${line#*/}" # strip repo prefix (extra/kitty -> kitty)
|
||||
if [[ -z "$pkg" ]]; then
|
||||
say_fail "empty package name in '$line'"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Layer 1: installed in the pacman DB?
|
||||
if pacman -Q "$pkg" >/dev/null 2>&1; then
|
||||
say_ok "pacman -Q $pkg (installed)"
|
||||
else
|
||||
say_fail "pacman -Q $pkg (not installed)"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Layer 2: runtime binary probe (curated mapping)?
|
||||
spec="${BINS[$pkg]:-}"
|
||||
[[ -z "$spec" ]] && continue # no binary (font/lib/module) - Layer 1 only
|
||||
|
||||
# python-pip has special multi-candidate handling
|
||||
[[ "$pkg" == "python-pip" ]] && { probe_pip; continue; }
|
||||
|
||||
read -r bin flag <<< "$spec"
|
||||
if command -v "$bin" >/dev/null 2>&1; then
|
||||
if [[ -z "$flag" ]]; then
|
||||
say_ok "command -v $bin (present)"
|
||||
else
|
||||
out=$("$bin" $flag 2>&1) && [[ -n "$out" ]]
|
||||
if [[ $? -eq 0 ]]; then
|
||||
say_ok "$bin $flag ($out)"
|
||||
else
|
||||
say_fail "$bin $flag (binary present but probe failed)"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
say_fail "command -v $bin (expected from package '$pkg')"
|
||||
fi
|
||||
done < "$PKGS_FILE"
|
||||
|
||||
echo
|
||||
echo "----------------------------------------------"
|
||||
echo "verify_packages.sh: $PASS passed, $FAIL failed"
|
||||
echo "----------------------------------------------"
|
||||
[[ $FAIL -eq 0 ]]
|
||||
Reference in New Issue
Block a user