Compare commits

...
16 Commits
Author SHA1 Message Date
edemilov 8a6f0fe413 test: cover install_terminal_prereqs apt branch and Hyprland-skip/TerminalLook-completes composition
- install_terminal_prereqs's apt branch was untested even though the
  existing test already stubbed apt-get; added a dedicated test asserting
  the exact apt-get update / apt-get install -y zsh git curl fzf calls with
  no pacman on PATH.
- Added an end-to-end test (both components picked, Hyprland's prerequisite
  missing) exercising the exact composition Task 2's refactor exists for:
  Hyprland skips cleanly via check_arch failing, and TerminalLook (Kali
  look) still runs to completion in the same main() invocation. Uses a
  clean-room PATH (real coreutils/git/bash symlinked in, no-op stand-ins for
  apt-get/sudo/zsh/chsh, pacman never added) so the assertion holds
  regardless of whether the host running the tests actually has pacman.

Addresses findings 5 and 6 from the final whole-branch review.
2026-08-30 17:46:56 +03:00
edemilov cc9282dfd4 docs: fix Terminal-look README defect, warn about wholesale .zshrc replacement
- The chsh-to-zsh behavior is real, consequential shell-changing behavior
  and doesn't belong in the "What it does NOT do" list; moved it into the
  module's prose description and noted that chsh prompts for the account
  password via PAM.
- Added a plain warning that ~/.zshrc is replaced wholesale (not merged) by
  either look, and that any existing personal customizations end up in the
  dotevo-backup directory rather than being preserved -- important ahead of
  the upcoming live test on this machine.

Addresses findings 3 and 4 from the final whole-branch review.
2026-08-30 17:46:51 +03:00
edemilov 6183c7859b fix: persist zsh history on Kali look, skip TerminalLook module cleanly on missing prereqs
- write_zshrc_kali now sets HISTFILE/HISTSIZE/SAVEHIST and share_history/
  hist_ignore_dups so command history survives across sessions (previously
  unset, so history was in-memory only despite shipping
  zsh-history-substring-search and fzf history search).
- apply_terminal_look now guards install_terminal_prereqs and returns 0 with
  a skip message on failure, matching apply_hyprland's existing pattern, so
  an unrecognized package manager no longer aborts the whole script under
  set -euo pipefail and blocks a Hyprland module that would otherwise
  succeed.

Addresses findings 1 and 2 from the final whole-branch review.
2026-08-30 17:46:47 +03:00
edemilovandClaude Haiku 4.5 2c5fe486fe docs: fix Terminal look README — clarify automatic shell change
README.md line 95 incorrectly claimed shell change ("chsh") only happens
"unless you confirm the interactive prompt." In reality, chsh_to_zsh()
unconditionally switches to zsh when invoked, with no separate whiptail
confirmation for this step. The only confirmation is at component and
look-picker level; once Terminal look is selected, the shell change
is automatic.

Rewrote the bullet to accurately reflect this: "Automatically changes
your login shell to zsh via chsh (if not already zsh) — once Terminal
look is selected and a prompt style is chosen, the shell change
happens automatically as part of applying the configuration."

Co-Authored-By: Claude Haiku 4.5 <[email protected]>
2026-08-30 17:32:53 +03:00
edemilov efd3a0e754 docs: document terminal-look module in README 2026-08-30 17:30:18 +03:00
edemilov 5a0629468b feat: wire terminal-look module into main(), add chsh, extend ensure_repo_files 2026-08-30 17:25:49 +03:00
edemilov 73edf11a23 feat: add Kali terminal-look module 2026-08-30 17:19:21 +03:00
edemilov f6b2d929f2 feat: add Powerlevel10k terminal-look module 2026-08-30 17:15:43 +03:00
edemilov 56016bda3b feat: add clone_or_update_repo helper for terminal-look plugin repos 2026-08-30 17:12:04 +03:00
edemilovandClaude Sonnet 5 5693fd1680 feat: add terminal-look picker (Powerlevel10k / Kali)
Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-30 17:09:13 +03:00
edemilovandClaude Sonnet 5 153e64d785 feat: detect package manager and install terminal-look prerequisites
Add two new functions:
- detect_pkg_manager(): checks for pacman, apt-get, dnf, zypper in order
- install_terminal_prereqs(): installs zsh git curl fzf via detected manager

Both functions follow the expected interfaces: detect_pkg_manager echoes one of
the manager names or "unknown", and install_terminal_prereqs returns 1 with a
dotevo:-prefixed message on unrecognized managers.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-30 17:06:03 +03:00
edemilov 3a795b290b feat: add component picker, split apply() into apply_hyprland() + apply_terminal_look() stub 2026-08-30 16:58:48 +03:00
edemilovandClaude Sonnet 5 540046d3ed rename: hyprevo -> dotevo
Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-30 16:54:38 +03:00
edemilov 715f52192d chore: ignore .worktrees/ for isolated plan execution 2026-08-30 16:52:45 +03:00
edemilovandClaude Sonnet 5 eb1a79352f Add implementation plan for dotevo rename + terminal-look module
9 TDD tasks: rename hyprevo->dotevo, component picker, package-manager
detection, terminal-look picker, shared clone helper, Powerlevel10k
module, Kali module, main() wiring + chsh, README + regression pass.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-30 16:50:57 +03:00
edemilovandClaude Sonnet 5 0dcd754e1e spec: dotevo rename + terminal-look module design
Extends hyprevo into a broader personal-environment installer. This round:
rename to dotevo, add a component picker, and a terminal-look module
(Powerlevel10k repro / Kali fresh) that works on any distro, independent
of the Arch-only Hyprland-rice module. KDE-rice deferred to a follow-up
design round.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-30 16:42:57 +03:00
8 changed files with 2415 additions and 99 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
.worktrees/
+61 -20
View File
@@ -1,31 +1,30 @@
# hyprevo
# dotevo
A one-command installer for Hyprland + DankMaterialShell (DMS) on any
Arch-based Linux (plain Arch, CachyOS, Manjaro, EndeavourOS, or anything else
with `pacman`). Installs the desktop shell itself unconditionally, then lets
you pick which extra apps you want via an interactive menu -- no fixed bundle
of KDE tools you didn't ask for.
A one-command installer for popular Linux desktop and terminal configurations.
Currently includes **Hyprland rice** (Hyprland + DankMaterialShell) and
**Terminal look** (zsh with Powerlevel10k or Kali-style prompt) — more modules
coming later.
## Install
```
bash -c "$(curl -fsSL https://git.edemilov.ru/edemilov/hyprevo/raw/branch/master/install.sh)"
bash -c "$(curl -fsSL https://git.edemilov.ru/edemilov/dotevo/raw/branch/master/install.sh)"
```
(Works from any login shell, including fish/zsh -- it's plain command
substitution, not bash's `<(...)` process substitution, which fish doesn't
support.)
## What's always installed
Hyprland, DankMaterialShell (panel bar + dock + app launcher), the scrolling
(niri-style) window layout, workspace/window keybinds, and a fix for a
common secret-service hang (`ksecretd`).
## What you pick
A `whiptail` menu walks you through:
A `whiptail` menu walks you through component selection first, then
module-specific options:
**Top-level component checklist** (space to toggle, enter to confirm):
- **Hyprland** -- Desktop rice (Hyprland + DankMaterialShell + keybinds) *(on by default)*
- **TerminalLook** -- zsh + prompt style (Powerlevel10k or Kali) *(off by default)*
If you pick **Hyprland**, you'll then choose:
- **Terminal** -- Konsole / Terminator / Kitty / Alacritty (pick one, becomes your `$terminal` default)
- **File manager** -- Dolphin / Nautilus / Thunar (pick one)
- **Mail client** -- Thunderbird / KMail (pick one)
@@ -34,18 +33,28 @@ A `whiptail` menu walks you through:
- **Screen capture** -- grim/slurp/satty/wf-recorder behind one Print-key menu (yes/no)
- **Bitwarden** (yes/no)
If you pick **TerminalLook**, you'll choose:
- **Powerlevel10k** -- Recreates your current zsh setup
- **Kali-style** -- Fresh box-drawing prompt
Want a second terminal or a different mail client later? Install it manually
afterward -- the picker is single-choice per slot on purpose.
## What it does NOT do
## Hyprland rice
The Hyprland-rice module installs Hyprland, DankMaterialShell (panel bar + dock + app launcher), the scrolling
(niri-style) window layout, workspace/window keybinds, and a fix for a
common secret-service hang (`ksecretd`).
### What it does NOT do
- Doesn't touch an existing desktop environment -- Hyprland installs as an
additional login session, nothing else is removed or reconfigured.
- Never needs the AUR -- everything it installs is in Arch's official repos.
- Doesn't overwrite your existing configs without backing them up first
(`~/.config/old2026_1/hyprevo-backup-<timestamp>/`).
(`~/.config/old2026_1/dotevo-backup-<timestamp>/`).
## Keybind cheat-sheet
### Keybind cheat-sheet
| Keys | Action |
|---|---|
@@ -67,8 +76,40 @@ afterward -- the picker is single-choice per slot on purpose.
| `Super + mouse drag` (left/right button) | Move/resize window |
| `Print` | Capture menu (screenshot/record) -- only if Screen capture was picked |
## Terminal look
The Terminal-look module sets up zsh with your chosen prompt style:
- **Powerlevel10k** -- Recreates your current setup: Oh My Zsh + Powerlevel10k theme + standard plugins (git, fzf, zsh-autosuggestions, zsh-syntax-highlighting, zsh-history-substring-search) + `.p10k.zsh` config + fzf integration. The theme renders your prompt with git branch/status icons and a right-side return code.
- **Kali-style** -- A fresh alternative: zsh with Kali Linux's box-drawing prompt style + core plugins (zsh-autosuggestions, zsh-syntax-highlighting, zsh-history-substring-search) + fzf integration. No Oh My Zsh or complex theme framework, just a simple box prompt and essentials.
Both looks install the terminal prerequisites (zsh, git, curl, fzf) across Arch, Debian, Fedora, and openSUSE via their native package managers.
Once a prompt style is chosen and applied, the module automatically changes
your login shell to zsh via `chsh` (if it isn't already zsh). `chsh` will
prompt you for your Unix account password (via PAM) when it runs.
`~/.zshrc` is replaced wholesale, not merged: any existing personal
customizations (PATH exports, environment variables, custom functions,
aliases, etc.) in your current `.zshrc` are moved into the backup directory
rather than preserved in the new file. Check
`~/.config/old2026_1/dotevo-backup-<timestamp>/` afterward if you had custom
additions you want to bring back.
### What it does NOT do
- Doesn't touch an existing shell config without backing it up first
(`~/.config/old2026_1/dotevo-backup-<timestamp>/`).
- Doesn't add any CLI tools beyond the prompt framework and plugins
(zsh, git, curl, fzf, and the theme/prompt files themselves).
## Customize
Re-run the installer to change your picks (it rebuilds `~/.config/hypr/extras.conf`
fresh each time). For anything more specific, edit `~/.config/hypr/hyprland.conf`
or `~/.config/hypr/extras.conf` directly afterward.
Re-run the installer to change your picks. For the Hyprland-rice module,
it rebuilds `~/.config/hypr/extras.conf` fresh each time. For the Terminal-look
module, it re-runs the setup and updates your `.zshrc` and prompt config.
For anything more specific:
- Hyprland: edit `~/.config/hypr/hyprland.conf` or `~/.config/hypr/extras.conf` directly
- Terminal: modify your `.zshrc` or `~/.p10k.zsh` (Powerlevel10k) / `~/.dotevo/kali-prompt.zsh` (Kali)
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,202 @@
# dotevo: Rename + Terminal-Look Module — Design
Extends `hyprevo` (public one-command Arch-based Hyprland+DMS installer,
see [`2026-08-29-hyprevo-design-v2.md`](2026-08-29-hyprevo-design-v2.md))
into a broader personal-environment installer covering more than one
desktop piece. This round: rename the repo and add a **Terminal-look**
module. A future round adds a **KDE-rice** module (deferred — needs its own
design pass, see Non-goals).
## Goal
One public repo, renamed `dotevo`, where each environment piece is an
independent, separately-picked module — not a single fixed bundle. This
round ships two modules:
1. **Hyprland + DMS rice** — the existing `hyprevo` flow, unchanged.
2. **Terminal look** — new. Sets up zsh with a choice of look:
- **Powerlevel10k** — reproduces the user's actual current setup
(oh-my-zsh + p10k + zsh-autosuggestions + zsh-syntax-highlighting +
zsh-history-substring-search).
- **Kali** — a fresh, hand-rolled prompt matching Kali Linux's real
default (`┌──(user㉿host)-[~]` box style), no oh-my-zsh at all, same
three plugin scripts layered on top for the same fish-like typing
feel.
Critically, **Terminal look must work on any Linux distro** (the motivating
case is bare Debian/Proxmox hosts — old bash, no DE, `apt` not `pacman`),
while Hyprland rice stays Arch/pacman-only as before. This is why the two
are picked independently rather than forced together.
## Non-goals (this round)
- **KDE-rice module**: deferred. The current `dotfiles` repo's KDE configs
are a raw personal dump (this machine's `kdeglobals`/`kwinrc`/etc.), not
a curated public "rice" — building that is its own design/build round,
same treatment Hyprland's `hyprland.conf` already got. The component
picker below leaves a slot for it, but it isn't built yet.
- **GUI/app wrapper**: mentioned as a future direction but out of scope.
Keeping the shell-script core clean and callable on its own (no assumed
terminal UI beyond `whiptail`) means a future GUI can shell out to the
same `install.sh` without a rewrite.
- Same non-goals `hyprevo` v2 already had for the Hyprland module: no AUR,
no hardware-specific tuning, doesn't touch an existing DE, Hyprland's own
app-picker slots stay single-choice.
- Terminal-look doesn't add config toggles/extras beyond the two looks
(no zoxide/eza/bat wiring, etc.) — matches "just the exact setup, no
toggles" from the design discussion. Anyone who wants more installs it
themselves afterward.
## Component picker (new top-level step)
Before any of the existing Hyprland-specific screens, one `whiptail
--checklist` (multi-select, unlike the single-choice slots inside Hyprland
setup):
```
[x] Hyprland + DMS desktop rice
[ ] Terminal look (zsh + prompt)
```
(KDE rice joins this list as a third checkbox once it's built — no
structural change needed then, just a new case in the dispatch below.)
At least one box must be checked — re-show the screen if the user confirms
with nothing selected. Selected modules run in order: Hyprland rice (if
checked) → Terminal look (if checked). Each module owns its own
prerequisite check, backup, and idempotency — picking one doesn't require
or block the other.
**Hyprland rice module**: runs its `pacman` check *inside* the module, not
at the top of the script. If `pacman` is missing, that module alone prints
a one-line skip message and continues to whatever else was picked, instead
of aborting the whole run — this is what lets someone select both boxes
inadvertently on a non-Arch box without losing Terminal-look.
## Terminal-look module
### Look A — Powerlevel10k (reproduce current setup)
- `ZSH="$HOME/.oh-my-zsh"`, `ZSH_CUSTOM="$HOME/.dotevo-zsh-custom"` — both
user-owned. (This directly fixes the real bug found while scoping this
project: CachyOS installs oh-my-zsh system-wide at
`/usr/share/oh-my-zsh`, root-owned, so anything dropped in the
conventional `~/.oh-my-zsh/custom/` path silently never loads. dotevo
never touches that system path — always its own user-owned tree.)
- git-clone (or `git -C <dir> pull` if already present — idempotent) into
that tree:
- `ohmyzsh/ohmyzsh` → `~/.oh-my-zsh`
- `romkatv/powerlevel10k` → `$ZSH_CUSTOM/themes/powerlevel10k`
- `zsh-users/zsh-autosuggestions` → `$ZSH_CUSTOM/plugins/zsh-autosuggestions`
- `zsh-users/zsh-syntax-highlighting` → `$ZSH_CUSTOM/plugins/zsh-syntax-highlighting`
- `zsh-users/zsh-history-substring-search` → `$ZSH_CUSTOM/plugins/zsh-history-substring-search`
- Ships `terminal/p10k/.p10k.zsh` (the user's actual current config) →
`~/.p10k.zsh`.
- Writes `~/.zshrc`: p10k instant-prompt block, `ZSH_THEME="powerlevel10k/powerlevel10k"`,
`plugins=(git fzf zsh-autosuggestions zsh-syntax-highlighting zsh-history-substring-search)`,
`source $ZSH/oh-my-zsh.sh`, `source ~/.p10k.zsh`, `fzf --zsh` (modern fzf's
built-in shell-integration flag — no hardcoded distro-specific
`key-bindings.zsh`/`completion.zsh` paths needed).
### Look B — Kali (fresh)
- No oh-my-zsh clone at all. Only the three standalone plugin repos,
git-cloned into `$ZSH_CUSTOM/plugins/...` (same paths as above, so
switching looks later doesn't leave orphaned clones).
- Ships `terminal/kali/prompt.zsh` — the hand-rolled prompt: `autoload -Uz
colors && colors`, the actual Kali `PROMPT`/`RPROMPT` (box-drawing
characters, red/blue segments, root shown in red), `autoload -Uz compinit
&& compinit`.
- Writes `~/.zshrc`: sources `prompt.zsh`, then the three plugin scripts
directly by their cloned path (`source
$ZSH_CUSTOM/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh`, etc. —
no framework loading them), `fzf --zsh`.
### Shared steps (both looks)
1. **Package-manager detection** for exactly four binaries needed either
way — `zsh git curl fzf`:
```
pacman -> pacman -S --needed --noconfirm zsh git curl fzf
apt -> apt-get install -y zsh git curl fzf
dnf -> dnf install -y zsh git curl fzf
zypper -> zypper install -y zsh git curl fzf
```
Anything else: print "package manager not recognized, install `zsh git
curl fzf` manually" and stop *this module only* (Hyprland module, if
also picked, is unaffected).
2. **Backup**: any existing `~/.zshrc` / `~/.p10k.zsh` moves to
`~/old2026_1/dotevo-backup-<timestamp>/` first (same never-overwrite
rule as `hyprevo`'s `~/.config/old2026_1/...`, just rooted at `$HOME`
since these files live there, not under `.config`).
3. **Look picker**: one `whiptail --radiolist` — Powerlevel10k / Kali.
4. **Shell change**: `chsh -s "$(command -v zsh)"` only if `$SHELL` isn't
already zsh.
5. Re-running the module re-picks the look and rewrites `~/.zshrc` fresh
each time (same "rebuilt, not diffed" idempotency pattern as
`extras.conf`) — the backup step still fires first, so nothing from a
previous run is silently lost.
## Repo layout (additions)
```
dotevo/ # renamed from hyprevo
├── install.sh # + component checklist, + terminal-look module, dispatch logic
├── terminal/
│ ├── p10k/
│ │ └── .p10k.zsh # shipped, the user's real current config
│ └── kali/
│ └── prompt.zsh # hand-rolled PROMPT/RPROMPT
├── hypr/... # unchanged
├── local-bin/... # unchanged
├── packages.txt # unchanged (Hyprland-only; terminal packages mapped inline like existing app-picker packages)
└── README.md # updated — see below
```
## README changes
- New name/description at the top: personal environment installer, pick
your pieces (Hyprland rice today, more later).
- Install one-liner updated to the new repo path once renamed.
- New section: **Terminal look** — what each look includes, the same
"what it does NOT do" style callout (doesn't touch an existing shell
config without backing it up, doesn't add extra CLI tools beyond the
picked look).
- Existing Hyprland sections (keybind cheat-sheet, app menu table, etc.)
carried over unchanged, just re-scoped as "the Hyprland-rice module"
rather than the whole repo.
## Rename mechanics
- Gitea repo rename `hyprevo` → `dotevo` via the web UI (Settings →
repository name) on `git.edemilov.ru`.
- Update the local remote if the URL changes, update the install one-liner
everywhere it's referenced (README, any vault notes, memory).
- Note for the plan: confirm whether this Gitea version leaves an
auto-redirect from the old `hyprevo` path — if not, the old
curl-one-liner in any existing bookmarks/notes breaks silently, worth a
one-line README note either way.
## Testing plan
Same discipline as `hyprevo` v2 (TDD via `bats` + `shellcheck`, existing
`tests/install.bats` extended rather than replaced):
- New bats tests for: package-manager detection (mock each of the four),
backup-before-write for `.zshrc`/`.p10k.zsh`, idempotent re-run (second
run with a different look picked leaves no leftover config from the
first), the "Hyprland module skips cleanly when `pacman` missing, other
picked modules still run" behavior.
- **Live test 1 (this desktop, Cachyos7800xt)**: already has the real
p10k setup — running Terminal-look → Powerlevel10k here is the
regression check (does it faithfully reproduce what's already dialed
in, backup working correctly on a machine with real data to lose).
Also run it a second time picking Kali on the same box to visually
confirm the fresh prompt renders correctly.
- **Live test 2 (a Proxmox host, e.g. `pile` or `vanga`)**: bash-only
Debian box, no zsh installed — the actual motivating cross-distro case.
Confirms `apt` detection path, git-clone fallback (no CachyOS-style
packages available there), and `chsh` working correctly outside Arch.
- **Regression**: re-run the existing Hyprland-only flow (component
picker with only that box checked) to confirm it behaves exactly as
`hyprevo` v2 did — no behavior change for that module.
+220 -42
View File
@@ -8,16 +8,48 @@ set -euo pipefail
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-}")" && pwd)"
CONFIG_HOME="${CONFIG_HOME:-${XDG_CONFIG_HOME:-$HOME/.config}}"
LOCAL_BIN="${LOCAL_BIN:-$HOME/.local/bin}"
HYPREVO_RAW_BASE="${HYPREVO_RAW_BASE:-https://git.edemilov.ru/edemilov/hyprevo/raw/branch/master}"
DOTEVO_RAW_BASE="${DOTEVO_RAW_BASE:-https://git.edemilov.ru/edemilov/dotevo/raw/branch/master}"
OMZ_DIR="${OMZ_DIR:-$HOME/.oh-my-zsh}"
ZSH_CUSTOM_DIR="${ZSH_CUSTOM_DIR:-$HOME/.dotevo-zsh-custom}"
ASSETS_DIR="${ASSETS_DIR:-$HOME/.dotevo}"
check_arch() {
command -v pacman >/dev/null 2>&1
}
detect_pkg_manager() {
if command -v pacman >/dev/null 2>&1; then
echo pacman
elif command -v apt-get >/dev/null 2>&1; then
echo apt
elif command -v dnf >/dev/null 2>&1; then
echo dnf
elif command -v zypper >/dev/null 2>&1; then
echo zypper
else
echo unknown
fi
}
install_terminal_prereqs() {
local mgr
mgr=$(detect_pkg_manager)
case "$mgr" in
pacman) sudo pacman -S --needed --noconfirm zsh git curl fzf ;;
apt) sudo apt-get update && sudo apt-get install -y zsh git curl fzf ;;
dnf) sudo dnf install -y zsh git curl fzf ;;
zypper) sudo zypper install -y zsh git curl fzf ;;
*)
echo "dotevo: package manager not recognized -- install zsh git curl fzf manually" >&2
return 1
;;
esac
}
# When install.sh is curl-piped straight into bash (the normal way to run it),
# $BASH_SOURCE resolves to a transient fd path (e.g. /dev/fd/63) with no
# sibling files at all -- there's no local checkout of the rest of the repo.
# Detect that case and fetch the handful of files apply() needs into a fresh
# Detect that case and fetch the handful of files apply_hyprland() needs into a fresh
# temp dir instead, then repoint REPO_DIR there. Running from a real git
# checkout (packages.txt already sits next to this script) is left untouched.
ensure_repo_files() {
@@ -27,13 +59,15 @@ ensure_repo_files() {
local tmp_dir
tmp_dir="$(mktemp -d)"
mkdir -p "$tmp_dir/hypr" "$tmp_dir/local-bin"
mkdir -p "$tmp_dir/hypr" "$tmp_dir/local-bin" "$tmp_dir/terminal/p10k" "$tmp_dir/terminal/kali"
curl -fsSL "$HYPREVO_RAW_BASE/packages.txt" -o "$tmp_dir/packages.txt"
curl -fsSL "$HYPREVO_RAW_BASE/hypr/hyprland.conf" -o "$tmp_dir/hypr/hyprland.conf"
curl -fsSL "$HYPREVO_RAW_BASE/local-bin/capture-menu.sh" -o "$tmp_dir/local-bin/capture-menu.sh"
curl -fsSL "$HYPREVO_RAW_BASE/local-bin/screenshot.sh" -o "$tmp_dir/local-bin/screenshot.sh"
curl -fsSL "$HYPREVO_RAW_BASE/local-bin/toggle-recording.sh" -o "$tmp_dir/local-bin/toggle-recording.sh"
curl -fsSL "$DOTEVO_RAW_BASE/packages.txt" -o "$tmp_dir/packages.txt"
curl -fsSL "$DOTEVO_RAW_BASE/hypr/hyprland.conf" -o "$tmp_dir/hypr/hyprland.conf"
curl -fsSL "$DOTEVO_RAW_BASE/local-bin/capture-menu.sh" -o "$tmp_dir/local-bin/capture-menu.sh"
curl -fsSL "$DOTEVO_RAW_BASE/local-bin/screenshot.sh" -o "$tmp_dir/local-bin/screenshot.sh"
curl -fsSL "$DOTEVO_RAW_BASE/local-bin/toggle-recording.sh" -o "$tmp_dir/local-bin/toggle-recording.sh"
curl -fsSL "$DOTEVO_RAW_BASE/terminal/p10k/.p10k.zsh" -o "$tmp_dir/terminal/p10k/.p10k.zsh"
curl -fsSL "$DOTEVO_RAW_BASE/terminal/kali/prompt.zsh" -o "$tmp_dir/terminal/kali/prompt.zsh"
REPO_DIR="$tmp_dir"
}
@@ -52,7 +86,7 @@ slot_package() {
Mail:KMail) echo "kmail" ;;
CloudSync:Nextcloud) echo "nextcloud-client" ;;
CloudSync:Syncthing) echo "syncthing" ;;
*) echo "hyprevo: unknown slot/choice pair: $slot/$choice" >&2; return 1 ;;
*) echo "dotevo: unknown slot/choice pair: $slot/$choice" >&2; return 1 ;;
esac
}
@@ -69,7 +103,7 @@ slot_binary() {
Mail:Thunderbird) echo "thunderbird" ;;
Mail:KMail) echo "kmail" ;;
CloudSync:Nextcloud) echo "nextcloud --background" ;;
*) echo "hyprevo: unknown slot/choice pair: $slot/$choice" >&2; return 1 ;;
*) echo "dotevo: unknown slot/choice pair: $slot/$choice" >&2; return 1 ;;
esac
}
@@ -80,7 +114,7 @@ terminal_desktop_id() {
Terminator) echo "terminator.desktop" ;;
Kitty) echo "kitty.desktop" ;;
Alacritty) echo "Alacritty.desktop" ;;
*) echo "hyprevo: unknown terminal choice: $choice" >&2; return 1 ;;
*) echo "dotevo: unknown terminal choice: $choice" >&2; return 1 ;;
esac
}
@@ -90,42 +124,42 @@ toggle_packages() {
Telegram) echo "telegram-desktop" ;;
Bitwarden) echo "bitwarden" ;;
ScreenCapture) echo "grim slurp satty wl-clipboard wf-recorder fuzzel libnotify" ;;
*) echo "hyprevo: unknown toggle: $toggle" >&2; return 1 ;;
*) echo "dotevo: unknown toggle: $toggle" >&2; return 1 ;;
esac
}
run_picker() {
if [[ "${HYPREVO_NONINTERACTIVE:-0}" == "1" ]]; then
PICK_TERMINAL="${HYPREVO_TERMINAL:?HYPREVO_TERMINAL required in non-interactive mode}"
PICK_FILEMANAGER="${HYPREVO_FILEMANAGER:?HYPREVO_FILEMANAGER required in non-interactive mode}"
PICK_MAIL="${HYPREVO_MAIL:?HYPREVO_MAIL required in non-interactive mode}"
PICK_CLOUDSYNC="${HYPREVO_CLOUDSYNC:?HYPREVO_CLOUDSYNC required in non-interactive mode}"
read -ra PICK_TOGGLES <<< "${HYPREVO_TOGGLES:-}"
if [[ "${DOTEVO_NONINTERACTIVE:-0}" == "1" ]]; then
PICK_TERMINAL="${DOTEVO_TERMINAL:?DOTEVO_TERMINAL required in non-interactive mode}"
PICK_FILEMANAGER="${DOTEVO_FILEMANAGER:?DOTEVO_FILEMANAGER required in non-interactive mode}"
PICK_MAIL="${DOTEVO_MAIL:?DOTEVO_MAIL required in non-interactive mode}"
PICK_CLOUDSYNC="${DOTEVO_CLOUDSYNC:?DOTEVO_CLOUDSYNC required in non-interactive mode}"
read -ra PICK_TOGGLES <<< "${DOTEVO_TOGGLES:-}"
return 0
fi
PICK_TERMINAL=$(whiptail --title "hyprevo" --radiolist \
PICK_TERMINAL=$(whiptail --title "dotevo" --radiolist \
"Choose your terminal emulator" 15 60 4 \
Konsole "" ON Terminator "" OFF Kitty "" OFF Alacritty "" OFF \
3>&1 1>&2 2>&3)
PICK_FILEMANAGER=$(whiptail --title "hyprevo" --radiolist \
PICK_FILEMANAGER=$(whiptail --title "dotevo" --radiolist \
"Choose your file manager" 15 60 3 \
Dolphin "" ON Nautilus "" OFF Thunar "" OFF \
3>&1 1>&2 2>&3)
PICK_MAIL=$(whiptail --title "hyprevo" --radiolist \
PICK_MAIL=$(whiptail --title "dotevo" --radiolist \
"Choose your mail client" 15 60 2 \
Thunderbird "" ON KMail "" OFF \
3>&1 1>&2 2>&3)
PICK_CLOUDSYNC=$(whiptail --title "hyprevo" --radiolist \
PICK_CLOUDSYNC=$(whiptail --title "dotevo" --radiolist \
"Choose your cloud sync client" 15 60 2 \
Nextcloud "" ON Syncthing "" OFF \
3>&1 1>&2 2>&3)
local toggle_raw
toggle_raw=$(whiptail --title "hyprevo" --checklist \
toggle_raw=$(whiptail --title "dotevo" --checklist \
"Choose optional apps (space to toggle, enter to confirm)" 15 70 3 \
Telegram "" OFF ScreenCapture "Screenshot/record via Print key" OFF Bitwarden "" OFF \
3>&1 1>&2 2>&3)
@@ -143,18 +177,18 @@ Telegram: $([[ " ${PICK_TOGGLES[*]} " == *" Telegram "* ]] && echo yes || echo n
Screen capture: $([[ " ${PICK_TOGGLES[*]} " == *" ScreenCapture "* ]] && echo yes || echo no)
Bitwarden: $([[ " ${PICK_TOGGLES[*]} " == *" Bitwarden "* ]] && echo yes || echo no)"
if [[ "${HYPREVO_NONINTERACTIVE:-0}" == "1" ]]; then
if [[ "${DOTEVO_NONINTERACTIVE:-0}" == "1" ]]; then
echo "$summary"
return 0
fi
whiptail --title "hyprevo -- confirm" --yesno "$summary
whiptail --title "dotevo -- confirm" --yesno "$summary
Install with these choices?" 18 60
}
build_extras_conf() {
echo "# Generated by hyprevo -- rebuilt fresh on every install run, do not hand-edit"
echo "# Generated by dotevo -- rebuilt fresh on every install run, do not hand-edit"
if [[ " ${PICK_TOGGLES[*]} " == *" ScreenCapture "* ]]; then
echo "bind = , Print, exec, ~/.local/bin/capture-menu.sh"
@@ -222,12 +256,12 @@ EOF
configure_dms_dock() {
if ! command -v dms >/dev/null 2>&1; then
echo "hyprevo: dms not on PATH yet, skipping dock settings -- log into Hyprland once, then re-run hyprevo (or run manually: dms ipc call settings set showDock true)"
echo "dotevo: dms not on PATH yet, skipping dock settings -- log into Hyprland once, then re-run dotevo (or run manually: dms ipc call settings set showDock true)"
return 0
fi
if ! dms ipc call settings get showDock >/dev/null 2>&1; then
echo "hyprevo: DMS not running yet, skipping dock settings -- log into Hyprland once, then re-run hyprevo (or run manually: dms ipc call settings set showDock true / dockAutoHide true / dockUseOverlayLayer true)"
echo "dotevo: DMS not running yet, skipping dock settings -- log into Hyprland once, then re-run dotevo (or run manually: dms ipc call settings set showDock true / dockAutoHide true / dockUseOverlayLayer true)"
return 0
fi
@@ -240,13 +274,54 @@ backup_if_exists() {
local target="$1"
[[ -e "$target" ]] || return 0
local backup_root
backup_root="$CONFIG_HOME/old2026_1/hyprevo-backup-$(date +%Y%m%d-%H%M%S)"
backup_root="$CONFIG_HOME/old2026_1/dotevo-backup-$(date +%Y%m%d-%H%M%S)"
local rel="${target#"$HOME"/}"
mkdir -p "$(dirname "$backup_root/$rel")"
mv "$target" "$backup_root/$rel"
}
apply() {
clone_or_update_repo() {
local url="$1" dest="$2"
if [[ -d "$dest/.git" ]]; then
git -C "$dest" pull --ff-only
else
mkdir -p "$(dirname "$dest")"
git clone --depth 1 "$url" "$dest"
fi
}
run_component_picker() {
if [[ "${DOTEVO_NONINTERACTIVE:-0}" == "1" ]]; then
read -ra PICK_COMPONENTS <<< "${DOTEVO_COMPONENTS:?DOTEVO_COMPONENTS required in non-interactive mode}"
return 0
fi
local raw
while true; do
raw=$(whiptail --title "dotevo" --checklist \
"Choose what to set up (space to toggle, enter to confirm)" 15 60 2 \
Hyprland "Hyprland + DMS desktop rice" ON \
TerminalLook "zsh + prompt (Powerlevel10k or Kali)" OFF \
3>&1 1>&2 2>&3)
read -ra PICK_COMPONENTS <<< "$(echo "$raw" | tr -d '"')"
[[ "${#PICK_COMPONENTS[@]}" -gt 0 ]] && break
whiptail --title "dotevo" --msgbox "Pick at least one." 8 40
done
}
apply_hyprland() {
if ! check_arch; then
echo "dotevo: skipping Hyprland rice -- pacman not found"
return 0
fi
run_picker
if ! confirm_summary; then
echo "dotevo: Hyprland rice cancelled, nothing was changed."
return 0
fi
backup_if_exists "$CONFIG_HOME/hypr/hyprland.conf"
backup_if_exists "$CONFIG_HOME/hypr/extras.conf"
backup_if_exists "$CONFIG_HOME/xdg-terminals.list"
@@ -290,26 +365,129 @@ apply() {
if [[ -n "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then
hyprctl reload
else
echo "hyprevo: log into Hyprland to see changes."
echo "dotevo: log into Hyprland to see changes."
fi
}
run_terminal_look_picker() {
if [[ "${DOTEVO_NONINTERACTIVE:-0}" == "1" ]]; then
PICK_TERMINAL_LOOK="${DOTEVO_TERMINAL_LOOK:?DOTEVO_TERMINAL_LOOK required in non-interactive mode}"
return 0
fi
# shellcheck disable=SC2034
PICK_TERMINAL_LOOK=$(whiptail --title "dotevo" --radiolist \
"Choose your terminal look" 12 60 2 \
P10k "Powerlevel10k (your current setup)" ON \
Kali "Kali-style box prompt" OFF \
3>&1 1>&2 2>&3)
}
write_zshrc_p10k() {
local dest="$1"
cat > "$dest" <<'EOF'
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
fi
export ZSH="$HOME/.oh-my-zsh"
export ZSH_CUSTOM="$HOME/.dotevo-zsh-custom"
ZSH_THEME="powerlevel10k/powerlevel10k"
plugins=(git fzf zsh-autosuggestions zsh-syntax-highlighting zsh-history-substring-search)
source $ZSH/oh-my-zsh.sh
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
if command -v fzf >/dev/null 2>&1; then
eval "$(fzf --zsh)"
fi
EOF
}
apply_terminal_look_p10k() {
clone_or_update_repo "https://github.com/ohmyzsh/ohmyzsh.git" "$OMZ_DIR"
clone_or_update_repo "https://github.com/romkatv/powerlevel10k.git" "$ZSH_CUSTOM_DIR/themes/powerlevel10k"
clone_or_update_repo "https://github.com/zsh-users/zsh-autosuggestions.git" "$ZSH_CUSTOM_DIR/plugins/zsh-autosuggestions"
clone_or_update_repo "https://github.com/zsh-users/zsh-syntax-highlighting.git" "$ZSH_CUSTOM_DIR/plugins/zsh-syntax-highlighting"
clone_or_update_repo "https://github.com/zsh-users/zsh-history-substring-search.git" "$ZSH_CUSTOM_DIR/plugins/zsh-history-substring-search"
backup_if_exists "$HOME/.p10k.zsh"
cp "$REPO_DIR/terminal/p10k/.p10k.zsh" "$HOME/.p10k.zsh"
backup_if_exists "$HOME/.zshrc"
write_zshrc_p10k "$HOME/.zshrc"
}
write_zshrc_kali() {
local dest="$1"
cat > "$dest" <<'EOF'
HISTFILE=~/.zsh_history
HISTSIZE=10000
SAVEHIST=10000
setopt share_history hist_ignore_dups
source "$HOME/.dotevo/kali-prompt.zsh"
source "$HOME/.dotevo-zsh-custom/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh"
source "$HOME/.dotevo-zsh-custom/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
source "$HOME/.dotevo-zsh-custom/plugins/zsh-history-substring-search/zsh-history-substring-search.zsh"
if command -v fzf >/dev/null 2>&1; then
eval "$(fzf --zsh)"
fi
EOF
}
apply_terminal_look_kali() {
clone_or_update_repo "https://github.com/zsh-users/zsh-autosuggestions.git" "$ZSH_CUSTOM_DIR/plugins/zsh-autosuggestions"
clone_or_update_repo "https://github.com/zsh-users/zsh-syntax-highlighting.git" "$ZSH_CUSTOM_DIR/plugins/zsh-syntax-highlighting"
clone_or_update_repo "https://github.com/zsh-users/zsh-history-substring-search.git" "$ZSH_CUSTOM_DIR/plugins/zsh-history-substring-search"
mkdir -p "$ASSETS_DIR"
backup_if_exists "$ASSETS_DIR/kali-prompt.zsh"
cp "$REPO_DIR/terminal/kali/prompt.zsh" "$ASSETS_DIR/kali-prompt.zsh"
backup_if_exists "$HOME/.zshrc"
write_zshrc_kali "$HOME/.zshrc"
}
chsh_to_zsh() {
local zsh_path
zsh_path="$(command -v zsh)"
if [[ "${SHELL:-}" != "$zsh_path" ]]; then
chsh -s "$zsh_path"
fi
}
apply_terminal_look() {
if ! install_terminal_prereqs; then
echo "dotevo: skipping Terminal look -- prerequisites unavailable"
return 0
fi
run_terminal_look_picker
case "$PICK_TERMINAL_LOOK" in
P10k) apply_terminal_look_p10k ;;
Kali) apply_terminal_look_kali ;;
*) echo "dotevo: unknown terminal look: $PICK_TERMINAL_LOOK" >&2; return 1 ;;
esac
chsh_to_zsh
}
main() {
if ! check_arch; then
echo "hyprevo is Arch-based only -- pacman not found"
exit 0
fi
ensure_repo_files
run_picker
run_component_picker
if ! confirm_summary; then
echo "hyprevo: cancelled, nothing was changed."
exit 0
if [[ " ${PICK_COMPONENTS[*]} " == *" Hyprland "* ]]; then
apply_hyprland
fi
apply
echo "hyprevo: done."
if [[ " ${PICK_COMPONENTS[*]} " == *" TerminalLook "* ]]; then
apply_terminal_look
fi
echo "dotevo: done."
}
# `return` only succeeds at top level when the script is being sourced (as
+11
View File
@@ -0,0 +1,11 @@
# Kali-style prompt -- matches Kali Linux's real default box prompt from
# /etc/zsh/zshrc (the kali-defaults package). No framework, no theme
# file, just zsh's own prompt escapes and the `colors` module.
autoload -Uz colors && colors
setopt prompt_subst
PROMPT='%F{%(#.blue.green)}┌──(%B%F{%(#.red.blue)}%n@%m%b%F{%(#.blue.green)})-[%B%F{reset}%(6~.%-1~/…/%4~.%5~)%b%F{%(#.blue.green)}]
└─%B%(#.%F{red}#.%F{blue}$)%b%F{reset} '
RPROMPT='%(?..%F{red}[%?]%f)'
autoload -Uz compinit && compinit
+199
View File
@@ -0,0 +1,199 @@
# Generated by Powerlevel10k configuration wizard on 2026-05-09 at 14:00 EEST.
# Based on romkatv/powerlevel10k/config/p10k-pure.zsh, checksum 07533.
# Wizard options: nerdfont-v3 + powerline, large icons, pure, snazzy, 24h time, 2 lines,
# compact, transient_prompt, instant_prompt=verbose.
# Type `p10k configure` to generate another config.
#
# Config file for Powerlevel10k with the style of Pure (https://github.com/sindresorhus/pure).
#
# Differences from Pure:
#
# - Git:
# - `@c4d3ec2c` instead of something like `v1.4.0~11` when in detached HEAD state.
# - No automatic `git fetch` (the same as in Pure with `PURE_GIT_PULL=0`).
#
# Apart from the differences listed above, the replication of Pure prompt is exact. This includes
# even the questionable parts. For example, just like in Pure, there is no indication of Git status
# being stale; prompt symbol is the same in command, visual and overwrite vi modes; when prompt
# doesn't fit on one line, it wraps around with no attempt to shorten it.
#
# If you like the general style of Pure but not particularly attached to all its quirks, type
# `p10k configure` and pick "Lean" style. This will give you slick minimalist prompt while taking
# advantage of Powerlevel10k features that aren't present in Pure.
# Temporarily change options.
'builtin' 'local' '-a' 'p10k_config_opts'
[[ ! -o 'aliases' ]] || p10k_config_opts+=('aliases')
[[ ! -o 'sh_glob' ]] || p10k_config_opts+=('sh_glob')
[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand')
'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand'
() {
emulate -L zsh -o extended_glob
# Unset all configuration options.
unset -m '(POWERLEVEL9K_*|DEFAULT_USER)~POWERLEVEL9K_GITSTATUS_DIR'
# Zsh >= 5.1 is required.
[[ $ZSH_VERSION == (5.<1->*|<6->.*) ]] || return
# Prompt colors.
local grey='242'
local red='#FF5C57'
local yellow='#F3F99D'
local blue='#57C7FF'
local magenta='#FF6AC1'
local cyan='#9AEDFE'
local white='#F1F1F0'
# Left prompt segments.
typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(
# =========================[ Line #1 ]=========================
context # user@host
dir # current directory
vcs # git status
command_execution_time # previous command duration
# =========================[ Line #2 ]=========================
newline # \n
virtualenv # python virtual environment
prompt_char # prompt symbol
)
# Right prompt segments.
typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(
# =========================[ Line #1 ]=========================
# command_execution_time # previous command duration
# virtualenv # python virtual environment
# context # user@host
time # current time
# =========================[ Line #2 ]=========================
newline # \n
)
# Basic style options that define the overall prompt look.
typeset -g POWERLEVEL9K_BACKGROUND= # transparent background
typeset -g POWERLEVEL9K_{LEFT,RIGHT}_{LEFT,RIGHT}_WHITESPACE= # no surrounding whitespace
typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SUBSEGMENT_SEPARATOR=' ' # separate segments with a space
typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SEGMENT_SEPARATOR= # no end-of-line symbol
typeset -g POWERLEVEL9K_VISUAL_IDENTIFIER_EXPANSION= # no segment icons
# Add an empty line before each prompt except the first. This doesn't emulate the bug
# in Pure that makes prompt drift down whenever you use the Alt-C binding from fzf or similar.
typeset -g POWERLEVEL9K_PROMPT_ADD_NEWLINE=false
# Magenta prompt symbol if the last command succeeded.
typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS}_FOREGROUND=$magenta
# Red prompt symbol if the last command failed.
typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS}_FOREGROUND=$red
# Default prompt symbol.
typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIINS_CONTENT_EXPANSION='❯'
# Prompt symbol in command vi mode.
typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VICMD_CONTENT_EXPANSION='❮'
# Prompt symbol in visual vi mode is the same as in command mode.
typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIVIS_CONTENT_EXPANSION='❮'
# Prompt symbol in overwrite vi mode is the same as in command mode.
typeset -g POWERLEVEL9K_PROMPT_CHAR_OVERWRITE_STATE=false
# Grey Python Virtual Environment.
typeset -g POWERLEVEL9K_VIRTUALENV_FOREGROUND=$grey
# Don't show Python version.
typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_PYTHON_VERSION=false
typeset -g POWERLEVEL9K_VIRTUALENV_{LEFT,RIGHT}_DELIMITER=
# Blue current directory.
typeset -g POWERLEVEL9K_DIR_FOREGROUND=$blue
# Context format when root: user@host. The first part white, the rest grey.
typeset -g POWERLEVEL9K_CONTEXT_ROOT_TEMPLATE="%F{$white}%n%f%F{$grey}@%m%f"
# Context format when not root: user@host. The whole thing grey.
typeset -g POWERLEVEL9K_CONTEXT_TEMPLATE="%F{$grey}%n@%m%f"
# Don't show context unless root or in SSH.
typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_CONTENT_EXPANSION=
# Show previous command duration only if it's >= 5s.
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD=5
# Don't show fractional seconds. Thus, 7s rather than 7.3s.
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION=0
# Duration format: 1d 2h 3m 4s.
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FORMAT='d h m s'
# Yellow previous command duration.
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND=$yellow
# Grey Git prompt. This makes stale prompts indistinguishable from up-to-date ones.
typeset -g POWERLEVEL9K_VCS_FOREGROUND=$grey
# Disable async loading indicator to make directories that aren't Git repositories
# indistinguishable from large Git repositories without known state.
typeset -g POWERLEVEL9K_VCS_LOADING_TEXT=
# Don't wait for Git status even for a millisecond, so that prompt always updates
# asynchronously when Git state changes.
typeset -g POWERLEVEL9K_VCS_MAX_SYNC_LATENCY_SECONDS=0
# Cyan ahead/behind arrows.
typeset -g POWERLEVEL9K_VCS_{INCOMING,OUTGOING}_CHANGESFORMAT_FOREGROUND=$cyan
# Don't show remote branch, current tag or stashes.
typeset -g POWERLEVEL9K_VCS_GIT_HOOKS=(vcs-detect-changes git-untracked git-aheadbehind)
# Don't show the branch icon.
typeset -g POWERLEVEL9K_VCS_BRANCH_ICON=
# When in detached HEAD state, show @commit where branch normally goes.
typeset -g POWERLEVEL9K_VCS_COMMIT_ICON='@'
# Don't show staged, unstaged, untracked indicators.
typeset -g POWERLEVEL9K_VCS_{STAGED,UNSTAGED,UNTRACKED}_ICON=
# Show '*' when there are staged, unstaged or untracked files.
typeset -g POWERLEVEL9K_VCS_DIRTY_ICON='*'
# Show '⇣' if local branch is behind remote.
typeset -g POWERLEVEL9K_VCS_INCOMING_CHANGES_ICON=':⇣'
# Show '⇡' if local branch is ahead of remote.
typeset -g POWERLEVEL9K_VCS_OUTGOING_CHANGES_ICON=':⇡'
# Don't show the number of commits next to the ahead/behind arrows.
typeset -g POWERLEVEL9K_VCS_{COMMITS_AHEAD,COMMITS_BEHIND}_MAX_NUM=1
# Remove space between '⇣' and '⇡' and all trailing spaces.
typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${${${P9K_CONTENT/⇣* :⇡/⇣⇡}// }//:/ }'
# Grey current time.
typeset -g POWERLEVEL9K_TIME_FOREGROUND=$grey
# Format for the current time: 09:51:02. See `man 3 strftime`.
typeset -g POWERLEVEL9K_TIME_FORMAT='%D{%H:%M:%S}'
# If set to true, time will update when you hit enter. This way prompts for the past
# commands will contain the start times of their commands rather than the end times of
# their preceding commands.
typeset -g POWERLEVEL9K_TIME_UPDATE_ON_COMMAND=false
# Transient prompt works similarly to the builtin transient_rprompt option. It trims down prompt
# when accepting a command line. Supported values:
#
# - off: Don't change prompt when accepting a command line.
# - always: Trim down prompt when accepting a command line.
# - same-dir: Trim down prompt when accepting a command line unless this is the first command
# typed after changing current working directory.
typeset -g POWERLEVEL9K_TRANSIENT_PROMPT=always
# Instant prompt mode.
#
# - off: Disable instant prompt. Choose this if you've tried instant prompt and found
# it incompatible with your zsh configuration files.
# - quiet: Enable instant prompt and don't print warnings when detecting console output
# during zsh initialization. Choose this if you've read and understood
# https://github.com/romkatv/powerlevel10k#instant-prompt.
# - verbose: Enable instant prompt and print a warning when detecting console output during
# zsh initialization. Choose this if you've never tried instant prompt, haven't
# seen the warning, or if you are unsure what this all means.
typeset -g POWERLEVEL9K_INSTANT_PROMPT=verbose
# Hot reload allows you to change POWERLEVEL9K options after Powerlevel10k has been initialized.
# For example, you can type POWERLEVEL9K_BACKGROUND=red and see your prompt turn red. Hot reload
# can slow down prompt by 1-2 milliseconds, so it's better to keep it turned off unless you
# really need it.
typeset -g POWERLEVEL9K_DISABLE_HOT_RELOAD=true
# If p10k is already loaded, reload configuration.
# This works even with POWERLEVEL9K_DISABLE_HOT_RELOAD=true.
(( ! $+functions[p10k] )) || p10k reload
}
# Tell `p10k configure` which file it should overwrite.
typeset -g POWERLEVEL9K_CONFIG_FILE=${${(%):-%x}:a}
(( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]}
'builtin' 'unset' 'p10k_config_opts'
+516 -37
View File
@@ -34,6 +34,8 @@ case "$url" in
*/local-bin/capture-menu.sh) echo "stub-capture-menu" > "$out" ;;
*/local-bin/screenshot.sh) echo "stub-screenshot" > "$out" ;;
*/local-bin/toggle-recording.sh) echo "stub-toggle-recording" > "$out" ;;
*/terminal/p10k/.p10k.zsh) echo "stub-p10k" > "$out" ;;
*/terminal/kali/prompt.zsh) echo "stub-kali-prompt" > "$out" ;;
*) echo "unexpected url: $url" >&2; exit 1 ;;
esac
EOF
@@ -50,6 +52,8 @@ EOF
[ "$(cat "$REPO_DIR/local-bin/capture-menu.sh")" = "stub-capture-menu" ]
[ "$(cat "$REPO_DIR/local-bin/screenshot.sh")" = "stub-screenshot" ]
[ "$(cat "$REPO_DIR/local-bin/toggle-recording.sh")" = "stub-toggle-recording" ]
[ "$(cat "$REPO_DIR/terminal/p10k/.p10k.zsh")" = "stub-p10k" ]
[ "$(cat "$REPO_DIR/terminal/kali/prompt.zsh")" = "stub-kali-prompt" ]
rm -rf "$fake_repo_dir" "$stub_bin"
}
@@ -137,12 +141,12 @@ EOF
@test "run_picker reads picks from env vars in non-interactive mode" {
source "${BATS_TEST_DIRNAME}/../install.sh"
HYPREVO_NONINTERACTIVE=1 \
HYPREVO_TERMINAL=Kitty \
HYPREVO_FILEMANAGER=Thunar \
HYPREVO_MAIL=Thunderbird \
HYPREVO_CLOUDSYNC=Syncthing \
HYPREVO_TOGGLES="Telegram Bitwarden" \
DOTEVO_NONINTERACTIVE=1 \
DOTEVO_TERMINAL=Kitty \
DOTEVO_FILEMANAGER=Thunar \
DOTEVO_MAIL=Thunderbird \
DOTEVO_CLOUDSYNC=Syncthing \
DOTEVO_TOGGLES="Telegram Bitwarden" \
run_picker
[ "$PICK_TERMINAL" = "Kitty" ]
@@ -156,11 +160,11 @@ EOF
@test "run_picker defaults toggles to empty when unset in non-interactive mode" {
source "${BATS_TEST_DIRNAME}/../install.sh"
HYPREVO_NONINTERACTIVE=1 \
HYPREVO_TERMINAL=Konsole \
HYPREVO_FILEMANAGER=Dolphin \
HYPREVO_MAIL=Thunderbird \
HYPREVO_CLOUDSYNC=Nextcloud \
DOTEVO_NONINTERACTIVE=1 \
DOTEVO_TERMINAL=Konsole \
DOTEVO_FILEMANAGER=Dolphin \
DOTEVO_MAIL=Thunderbird \
DOTEVO_CLOUDSYNC=Nextcloud \
run_picker
[ "${#PICK_TOGGLES[@]}" -eq 0 ]
@@ -170,7 +174,7 @@ EOF
source "${BATS_TEST_DIRNAME}/../install.sh"
PICK_TERMINAL=Konsole PICK_FILEMANAGER=Dolphin PICK_MAIL=Thunderbird PICK_CLOUDSYNC=Nextcloud
PICK_TOGGLES=()
HYPREVO_NONINTERACTIVE=1 run confirm_summary
DOTEVO_NONINTERACTIVE=1 run confirm_summary
[ "$status" -eq 0 ]
}
@@ -230,12 +234,12 @@ EOF
cat > "$stub_bin/systemctl" <<'EOF'
#!/usr/bin/env bash
echo "systemctl $*" >> "$HYPREVO_TEST_LOG"
echo "systemctl $*" >> "$DOTEVO_TEST_LOG"
exit 0
EOF
chmod +x "$stub_bin/systemctl"
export HYPREVO_TEST_LOG="$tmp_home/log"
export DOTEVO_TEST_LOG="$tmp_home/log"
export PATH="$stub_bin:$PATH"
export CONFIG_HOME="$tmp_home/.config"
@@ -246,8 +250,8 @@ EOF
grep -q '^Requires=graphical-session.target$' "$unit_file"
grep -q '^Wants=xdg-desktop-autostart.target$' "$unit_file"
grep -q 'daemon-reload' "$HYPREVO_TEST_LOG"
grep -q 'add-wants hyprland-session.target dms.service' "$HYPREVO_TEST_LOG"
grep -q 'daemon-reload' "$DOTEVO_TEST_LOG"
grep -q 'add-wants hyprland-session.target dms.service' "$DOTEVO_TEST_LOG"
rm -rf "$tmp_home" "$stub_bin"
}
@@ -285,12 +289,12 @@ EOF
stub_bin="$(mktemp -d)"
cat > "$stub_bin/dms" <<'EOF'
#!/usr/bin/env bash
echo "dms $*" >> "$HYPREVO_TEST_LOG"
echo "dms $*" >> "$DOTEVO_TEST_LOG"
exit 0
EOF
chmod +x "$stub_bin/dms"
HYPREVO_TEST_LOG="$tmp_home/log" PATH="$stub_bin:$PATH" configure_dms_dock
DOTEVO_TEST_LOG="$tmp_home/log" PATH="$stub_bin:$PATH" configure_dms_dock
grep -q 'ipc call settings set showDock true' "$tmp_home/log"
grep -q 'ipc call settings set dockAutoHide true' "$tmp_home/log"
@@ -312,7 +316,7 @@ EOF
[ "$count" -eq 1 ]
}
@test "apply backs up an existing hyprland.conf, writes new config and extras.conf" {
@test "apply_hyprland backs up an existing hyprland.conf, writes new config and extras.conf" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local tmp_home
@@ -320,20 +324,20 @@ EOF
local stub_bin
stub_bin="$(mktemp -d)"
# Stub out every command apply() calls that would touch the real system.
# Stub out every command apply_hyprland() calls that would touch the real system.
cat > "$stub_bin/pacman" <<'EOF'
#!/usr/bin/env bash
echo "pacman $*" >> "$HYPREVO_TEST_LOG"
echo "pacman $*" >> "$DOTEVO_TEST_LOG"
exit 0
EOF
cat > "$stub_bin/systemctl" <<'EOF'
#!/usr/bin/env bash
echo "systemctl $*" >> "$HYPREVO_TEST_LOG"
echo "systemctl $*" >> "$DOTEVO_TEST_LOG"
exit 0
EOF
cat > "$stub_bin/hyprctl" <<'EOF'
#!/usr/bin/env bash
echo "hyprctl $*" >> "$HYPREVO_TEST_LOG"
echo "hyprctl $*" >> "$DOTEVO_TEST_LOG"
exit 0
EOF
cat > "$stub_bin/sudo" <<'EOF'
@@ -342,7 +346,7 @@ exec "$@"
EOF
chmod +x "$stub_bin"/*
export HYPREVO_TEST_LOG="$tmp_home/log"
export DOTEVO_TEST_LOG="$tmp_home/log"
export PATH="$stub_bin:$PATH"
export HOME="$tmp_home"
export CONFIG_HOME="$tmp_home/.config"
@@ -351,10 +355,14 @@ EOF
mkdir -p "$CONFIG_HOME/hypr"
echo "old config" > "$CONFIG_HOME/hypr/hyprland.conf"
PICK_TERMINAL=Kitty PICK_FILEMANAGER=Thunar PICK_MAIL=Thunderbird PICK_CLOUDSYNC=Syncthing
PICK_TOGGLES=(ScreenCapture)
export DOTEVO_NONINTERACTIVE=1
export DOTEVO_TERMINAL=Kitty
export DOTEVO_FILEMANAGER=Thunar
export DOTEVO_MAIL=Thunderbird
export DOTEVO_CLOUDSYNC=Syncthing
export DOTEVO_TOGGLES="ScreenCapture"
apply
apply_hyprland
# Old config was backed up, not deleted.
backup_count=$(find "$CONFIG_HOME/old2026_1" -name "hyprland.conf" 2>/dev/null | wc -l)
@@ -372,16 +380,16 @@ EOF
# pacman was synced/upgraded first (avoids installing against a stale
# local package database that no longer matches the mirrors), then the
# actual package list was installed with --needed.
grep -q '\-Syu' "$HYPREVO_TEST_LOG"
grep -q '\-\-needed' "$HYPREVO_TEST_LOG"
grep -q '\-Syu' "$DOTEVO_TEST_LOG"
grep -q '\-\-needed' "$DOTEVO_TEST_LOG"
# Syncthing's systemd user service was enabled, since CloudSync=Syncthing.
grep -q 'enable --now --user syncthing.service' "$HYPREVO_TEST_LOG"
grep -q 'enable --now --user syncthing.service' "$DOTEVO_TEST_LOG"
# hyprland-session.target was written and dms.service wired to it, so
# DMS actually launches on login (this was previously missing entirely).
[ -f "$CONFIG_HOME/systemd/user/hyprland-session.target" ]
grep -q 'add-wants hyprland-session.target dms.service' "$HYPREVO_TEST_LOG"
grep -q 'add-wants hyprland-session.target dms.service' "$DOTEVO_TEST_LOG"
rm -rf "$tmp_home" "$stub_bin"
}
@@ -408,16 +416,487 @@ EOF
PATH="$stub_bin:$PATH" \
HOME="$tmp_home" \
HYPREVO_NONINTERACTIVE=1 \
HYPREVO_TERMINAL=Konsole \
HYPREVO_FILEMANAGER=Dolphin \
HYPREVO_MAIL=Thunderbird \
HYPREVO_CLOUDSYNC=Nextcloud \
DOTEVO_NONINTERACTIVE=1 \
DOTEVO_COMPONENTS="Hyprland" \
DOTEVO_TERMINAL=Konsole \
DOTEVO_FILEMANAGER=Dolphin \
DOTEVO_MAIL=Thunderbird \
DOTEVO_CLOUDSYNC=Nextcloud \
run bash -c "$(cat "${BATS_TEST_DIRNAME}/../install.sh")"
[ "$status" -eq 0 ]
[[ "$output" == *"hyprevo: done."* ]]
[[ "$output" == *"dotevo: done."* ]]
[ -f "$tmp_home/.config/hypr/hyprland.conf" ]
rm -rf "$tmp_home" "$stub_bin"
}
@test "run_component_picker reads picks from env var in non-interactive mode" {
source "${BATS_TEST_DIRNAME}/../install.sh"
DOTEVO_NONINTERACTIVE=1 DOTEVO_COMPONENTS="Hyprland TerminalLook" run_component_picker
[ "${#PICK_COMPONENTS[@]}" -eq 2 ]
[[ " ${PICK_COMPONENTS[*]} " == *" Hyprland "* ]]
[[ " ${PICK_COMPONENTS[*]} " == *" TerminalLook "* ]]
}
@test "run_component_picker requires DOTEVO_COMPONENTS in non-interactive mode" {
source "${BATS_TEST_DIRNAME}/../install.sh"
DOTEVO_NONINTERACTIVE=1 run run_component_picker
[ "$status" -ne 0 ]
}
@test "apply_hyprland skips cleanly when pacman is missing, without touching TerminalLook" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local stub_bin
stub_bin="$(mktemp -d)"
PATH="$stub_bin" run apply_hyprland
[ "$status" -eq 0 ]
[[ "$output" == *"skipping Hyprland rice"* ]]
rm -rf "$stub_bin"
}
@test "main dispatches only picked components (Hyprland skipped, TerminalLook runs) via bash -c" {
local tmp_home
tmp_home="$(mktemp -d)"
local stub_bin
stub_bin="$(mktemp -d)"
# No pacman/sudo stub calling through to a Hyprland-specific command on
# purpose -- proves the Hyprland module is skipped, not just that its
# commands happen to no-op. apply_terminal_look() is real (Task 8), so it
# still needs its own prereqs (pacman, sudo, git, zsh, chsh) stubbed to
# run end-to-end without touching the real system.
for cmd in curl pacman sudo git zsh chsh; do
cat > "$stub_bin/$cmd" <<EOF
#!/usr/bin/env bash
exit 0
EOF
chmod +x "$stub_bin/$cmd"
done
PATH="$stub_bin:/usr/bin:/bin" \
HOME="$tmp_home" \
DOTEVO_NONINTERACTIVE=1 \
DOTEVO_COMPONENTS="TerminalLook" \
DOTEVO_TERMINAL_LOOK="Kali" \
run bash -c "$(cat "${BATS_TEST_DIRNAME}/../install.sh")"
[ "$status" -eq 0 ]
[[ "$output" != *"skipping Hyprland rice"* ]]
[[ "$output" == *"dotevo: done."* ]]
rm -rf "$tmp_home" "$stub_bin"
}
@test "main runs both components: Hyprland skips cleanly on missing prereq, TerminalLook still completes" {
# Regression test for the Global Constraint that one module's failed
# prerequisite check must never block another module from running.
# Both components are picked; PATH is a clean room containing only the
# exact binaries this path needs, with pacman deliberately never added
# (not stubbed out -- simply absent), so apply_hyprland's check_arch
# fails regardless of whether the host machine actually has pacman, and
# it must skip via `return 0`, not abort the script under
# `set -euo pipefail`. apply_terminal_look (Kali look, no oh-my-zsh clone
# needed) must still run to completion afterward.
local tmp_home
tmp_home="$(mktemp -d)"
local stub_bin
stub_bin="$(mktemp -d)"
# Real passthrough binaries the Hyprland-skip + Kali terminal-look path
# actually calls (bash for the outer `run bash -c`, plus coreutils/git
# used by ensure_repo_files/apply_terminal_look_kali/write_zshrc_kali).
for real in bash dirname mkdir cp cat git; do
ln -s "$(command -v "$real")" "$stub_bin/$real"
done
# No-op stand-ins for everything else the script shells out to on this
# path -- deliberately no pacman here.
for cmd in curl apt-get sudo zsh chsh; do
cat > "$stub_bin/$cmd" <<'EOF'
#!/bin/bash
exit 0
EOF
chmod +x "$stub_bin/$cmd"
done
PATH="$stub_bin" \
HOME="$tmp_home" \
DOTEVO_NONINTERACTIVE=1 \
DOTEVO_COMPONENTS="Hyprland TerminalLook" \
DOTEVO_TERMINAL_LOOK="Kali" \
run bash -c "$(cat "${BATS_TEST_DIRNAME}/../install.sh")"
[ "$status" -eq 0 ]
[[ "$output" == *"skipping Hyprland rice"* ]]
[[ "$output" != *"unbound variable"* ]]
[[ "$output" != *"install.sh: line"* ]]
[[ "$output" == *"dotevo: done."* ]]
[ -f "$tmp_home/.zshrc" ]
rm -rf "$tmp_home" "$stub_bin"
}
@test "detect_pkg_manager finds pacman first when present" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local stub_bin
stub_bin="$(mktemp -d)"
touch "$stub_bin/pacman" && chmod +x "$stub_bin/pacman"
PATH="$stub_bin" run detect_pkg_manager
[ "$output" = "pacman" ]
rm -rf "$stub_bin"
}
@test "detect_pkg_manager finds apt-get, dnf, zypper when pacman absent" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local stub_bin
stub_bin="$(mktemp -d)"
touch "$stub_bin/apt-get" && chmod +x "$stub_bin/apt-get"
PATH="$stub_bin" run detect_pkg_manager
[ "$output" = "apt" ]
rm -f "$stub_bin"/*
touch "$stub_bin/dnf" && chmod +x "$stub_bin/dnf"
PATH="$stub_bin" run detect_pkg_manager
[ "$output" = "dnf" ]
rm -f "$stub_bin"/*
touch "$stub_bin/zypper" && chmod +x "$stub_bin/zypper"
PATH="$stub_bin" run detect_pkg_manager
[ "$output" = "zypper" ]
rm -rf "$stub_bin"
}
@test "detect_pkg_manager returns unknown when nothing recognized is on PATH" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local stub_bin
stub_bin="$(mktemp -d)"
PATH="$stub_bin" run detect_pkg_manager
[ "$output" = "unknown" ]
rm -rf "$stub_bin"
}
@test "install_terminal_prereqs calls the right install command per package manager" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local tmp_home stub_bin
tmp_home="$(mktemp -d)"
stub_bin="$(mktemp -d)"
for cmd in pacman apt-get sudo; do
cat > "$stub_bin/$cmd" <<EOF
#!/usr/bin/env bash
echo "$cmd \$*" >> "$tmp_home/log"
EOF
chmod +x "$stub_bin/$cmd"
done
PATH="$stub_bin:$PATH" DOTEVO_TEST_LOG="$tmp_home/log" install_terminal_prereqs
grep -q 'pacman -S --needed --noconfirm zsh git curl fzf' "$tmp_home/log"
rm -rf "$tmp_home" "$stub_bin"
}
@test "install_terminal_prereqs calls the right install command on apt (no pacman on PATH)" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local tmp_home stub_bin
tmp_home="$(mktemp -d)"
stub_bin="$(mktemp -d)"
for cmd in apt-get sudo; do
cat > "$stub_bin/$cmd" <<EOF
#!/bin/bash
echo "$cmd \$*" >> "$tmp_home/log"
EOF
chmod +x "$stub_bin/$cmd"
done
PATH="$stub_bin" install_terminal_prereqs
grep -q 'apt-get update' "$tmp_home/log"
grep -q 'apt-get install -y zsh git curl fzf' "$tmp_home/log"
rm -rf "$tmp_home" "$stub_bin"
}
@test "install_terminal_prereqs fails clearly on an unrecognized package manager" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local stub_bin
stub_bin="$(mktemp -d)"
PATH="$stub_bin" run install_terminal_prereqs
[ "$status" -ne 0 ]
[[ "$output" == *"dotevo:"*"package manager not recognized"* ]]
rm -rf "$stub_bin"
}
@test "run_terminal_look_picker reads pick from env var in non-interactive mode" {
source "${BATS_TEST_DIRNAME}/../install.sh"
DOTEVO_NONINTERACTIVE=1 DOTEVO_TERMINAL_LOOK=Kali run_terminal_look_picker
[ "$PICK_TERMINAL_LOOK" = "Kali" ]
}
@test "run_terminal_look_picker requires DOTEVO_TERMINAL_LOOK in non-interactive mode" {
source "${BATS_TEST_DIRNAME}/../install.sh"
DOTEVO_NONINTERACTIVE=1 run run_terminal_look_picker
[ "$status" -ne 0 ]
}
@test "clone_or_update_repo clones on first call, pulls on second" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local remote_dir work_dir dest
remote_dir="$(mktemp -d)"
work_dir="$(mktemp -d)"
dest="$work_dir/dest"
git init --bare -q "$remote_dir/repo.git"
git clone -q "$remote_dir/repo.git" "$work_dir/seed"
(cd "$work_dir/seed" && git config user.email [email protected] && git config user.name t \
&& echo v1 > file.txt && git add file.txt && git commit -q -m v1 && git push -q)
clone_or_update_repo "$remote_dir/repo.git" "$dest"
[ -f "$dest/file.txt" ]
[ "$(cat "$dest/file.txt")" = "v1" ]
(cd "$work_dir/seed" && echo v2 > file.txt && git commit -q -am v2 && git push -q)
clone_or_update_repo "$remote_dir/repo.git" "$dest"
[ "$(cat "$dest/file.txt")" = "v2" ]
rm -rf "$remote_dir" "$work_dir"
}
@test "terminal/p10k/.p10k.zsh is the real shipped config" {
[ -f "${BATS_TEST_DIRNAME}/../terminal/p10k/.p10k.zsh" ]
grep -q 'Powerlevel10k configuration wizard' "${BATS_TEST_DIRNAME}/../terminal/p10k/.p10k.zsh"
}
@test "write_zshrc_p10k writes a syntactically valid zsh file sourcing oh-my-zsh and p10k" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local tmp_home
tmp_home="$(mktemp -d)"
OMZ_DIR="$tmp_home/.oh-my-zsh" ZSH_CUSTOM_DIR="$tmp_home/.dotevo-zsh-custom" \
write_zshrc_p10k "$tmp_home/.zshrc"
grep -q 'ZSH_THEME="powerlevel10k/powerlevel10k"' "$tmp_home/.zshrc"
grep -q 'plugins=(git fzf zsh-autosuggestions zsh-syntax-highlighting zsh-history-substring-search)' "$tmp_home/.zshrc"
grep -q 'source \$ZSH/oh-my-zsh.sh' "$tmp_home/.zshrc"
grep -q 'source ~/.p10k.zsh' "$tmp_home/.zshrc"
grep -q 'fzf --zsh' "$tmp_home/.zshrc"
if command -v zsh >/dev/null 2>&1; then
zsh -n "$tmp_home/.zshrc"
fi
rm -rf "$tmp_home"
}
@test "apply_terminal_look_p10k clones the right repos, backs up, and writes config" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local tmp_home stub_bin
tmp_home="$(mktemp -d)"
stub_bin="$(mktemp -d)"
cat > "$stub_bin/git" <<EOF
#!/usr/bin/env bash
echo "git \$*" >> "$tmp_home/log"
if [[ "\$1" == "clone" ]]; then mkdir -p "\${@: -1}/.git"; fi
exit 0
EOF
chmod +x "$stub_bin/git"
echo "old zshrc" > "$tmp_home/.zshrc"
HOME="$tmp_home" \
CONFIG_HOME="$tmp_home/.config" \
REPO_DIR="${BATS_TEST_DIRNAME}/.." \
OMZ_DIR="$tmp_home/.oh-my-zsh" \
ZSH_CUSTOM_DIR="$tmp_home/.dotevo-zsh-custom" \
PATH="$stub_bin:$PATH" \
apply_terminal_look_p10k
grep -q "clone.*ohmyzsh/ohmyzsh.*$tmp_home/.oh-my-zsh" "$tmp_home/log"
grep -q "clone.*romkatv/powerlevel10k.*themes/powerlevel10k" "$tmp_home/log"
grep -q "clone.*zsh-users/zsh-autosuggestions" "$tmp_home/log"
grep -q "clone.*zsh-users/zsh-syntax-highlighting" "$tmp_home/log"
grep -q "clone.*zsh-users/zsh-history-substring-search" "$tmp_home/log"
[ -f "$tmp_home/.p10k.zsh" ]
grep -q "old zshrc" "$(find "$tmp_home/.config/old2026_1" -name .zshrc)"
grep -q 'ZSH_THEME="powerlevel10k/powerlevel10k"' "$tmp_home/.zshrc"
rm -rf "$tmp_home" "$stub_bin"
}
@test "terminal/kali/prompt.zsh is syntactically valid zsh" {
[ -f "${BATS_TEST_DIRNAME}/../terminal/kali/prompt.zsh" ]
if command -v zsh >/dev/null 2>&1; then
zsh -n "${BATS_TEST_DIRNAME}/../terminal/kali/prompt.zsh"
fi
}
@test "write_zshrc_kali sources prompt.zsh and the plugin scripts directly, no oh-my-zsh" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local tmp_home
tmp_home="$(mktemp -d)"
ASSETS_DIR="$tmp_home/.dotevo" ZSH_CUSTOM_DIR="$tmp_home/.dotevo-zsh-custom" \
write_zshrc_kali "$tmp_home/.zshrc"
grep -q 'source .*\.dotevo/kali-prompt.zsh' "$tmp_home/.zshrc"
grep -q 'source .*zsh-autosuggestions/zsh-autosuggestions.zsh' "$tmp_home/.zshrc"
grep -q 'source .*zsh-syntax-highlighting/zsh-syntax-highlighting.zsh' "$tmp_home/.zshrc"
grep -q 'source .*zsh-history-substring-search/zsh-history-substring-search.zsh' "$tmp_home/.zshrc"
grep -qv 'oh-my-zsh' "$tmp_home/.zshrc"
grep -q 'fzf --zsh' "$tmp_home/.zshrc"
if command -v zsh >/dev/null 2>&1; then
zsh -n "$tmp_home/.zshrc"
fi
rm -rf "$tmp_home"
}
@test "apply_terminal_look_kali clones only the three plugin repos, not oh-my-zsh" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local tmp_home stub_bin
tmp_home="$(mktemp -d)"
stub_bin="$(mktemp -d)"
cat > "$stub_bin/git" <<EOF
#!/usr/bin/env bash
echo "git \$*" >> "$tmp_home/log"
if [[ "\$1" == "clone" ]]; then mkdir -p "\${@: -1}/.git"; fi
exit 0
EOF
chmod +x "$stub_bin/git"
HOME="$tmp_home" \
CONFIG_HOME="$tmp_home/.config" \
REPO_DIR="${BATS_TEST_DIRNAME}/.." \
ASSETS_DIR="$tmp_home/.dotevo" \
ZSH_CUSTOM_DIR="$tmp_home/.dotevo-zsh-custom" \
PATH="$stub_bin:$PATH" \
apply_terminal_look_kali
grep -q "zsh-users/zsh-autosuggestions" "$tmp_home/log"
grep -q "zsh-users/zsh-syntax-highlighting" "$tmp_home/log"
grep -q "zsh-users/zsh-history-substring-search" "$tmp_home/log"
! grep -q "ohmyzsh" "$tmp_home/log"
! grep -q "powerlevel10k" "$tmp_home/log"
[ -f "$tmp_home/.dotevo/kali-prompt.zsh" ]
grep -q 'Kali-style prompt' "$tmp_home/.dotevo/kali-prompt.zsh"
[[ "$(cat "$tmp_home/.zshrc")" == *"kali-prompt.zsh"* ]]
rm -rf "$tmp_home" "$stub_bin"
}
@test "chsh_to_zsh switches shell only when current SHELL isn't zsh" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local tmp_home stub_bin
tmp_home="$(mktemp -d)"
stub_bin="$(mktemp -d)"
cat > "$stub_bin/zsh" <<EOF
#!/usr/bin/env bash
exit 0
EOF
cat > "$stub_bin/chsh" <<EOF
#!/usr/bin/env bash
echo "chsh \$*" >> "$tmp_home/log"
EOF
chmod +x "$stub_bin/zsh" "$stub_bin/chsh"
SHELL="/bin/bash" PATH="$stub_bin:$PATH" chsh_to_zsh
grep -q "chsh -s $stub_bin/zsh" "$tmp_home/log"
: > "$tmp_home/log"
SHELL="$stub_bin/zsh" PATH="$stub_bin:$PATH" chsh_to_zsh
[ ! -s "$tmp_home/log" ]
rm -rf "$tmp_home" "$stub_bin"
}
@test "apply_terminal_look installs prereqs, picks a look, applies it, and switches shell" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local tmp_home stub_bin
tmp_home="$(mktemp -d)"
stub_bin="$(mktemp -d)"
cat > "$stub_bin/pacman" <<EOF
#!/usr/bin/env bash
echo "pacman \$*" >> "$tmp_home/log"
EOF
# Stubbed directly (not just pacman) so this never reaches the real
# sudo binary -- real sudo enforces its own secure_path and would
# bypass our pacman stub, running the system's actual pacman instead.
cat > "$stub_bin/sudo" <<EOF
#!/usr/bin/env bash
echo "sudo \$*" >> "$tmp_home/log"
exec "\$@"
EOF
cat > "$stub_bin/git" <<EOF
#!/usr/bin/env bash
echo "git \$*" >> "$tmp_home/log"
if [[ "\$1" == "clone" ]]; then mkdir -p "\${@: -1}/.git"; fi
EOF
cat > "$stub_bin/zsh" <<EOF
#!/usr/bin/env bash
exit 0
EOF
cat > "$stub_bin/chsh" <<EOF
#!/usr/bin/env bash
echo "chsh \$*" >> "$tmp_home/log"
EOF
chmod +x "$stub_bin"/*
HOME="$tmp_home" \
CONFIG_HOME="$tmp_home/.config" \
REPO_DIR="${BATS_TEST_DIRNAME}/.." \
OMZ_DIR="$tmp_home/.oh-my-zsh" \
ZSH_CUSTOM_DIR="$tmp_home/.dotevo-zsh-custom" \
ASSETS_DIR="$tmp_home/.dotevo" \
SHELL="/bin/bash" \
PATH="$stub_bin:$PATH" \
DOTEVO_NONINTERACTIVE=1 \
DOTEVO_TERMINAL_LOOK=Kali \
apply_terminal_look
grep -q 'pacman -S --needed --noconfirm zsh git curl fzf' "$tmp_home/log"
grep -q "chsh -s $stub_bin/zsh" "$tmp_home/log"
[ -f "$tmp_home/.zshrc" ]
[[ "$(cat "$tmp_home/.zshrc")" == *"kali-prompt.zsh"* ]]
rm -rf "$tmp_home" "$stub_bin"
}
@test "ensure_repo_files fetches terminal/ assets too when curl-piped" {
source "${BATS_TEST_DIRNAME}/../install.sh"
local fake_repo_dir stub_bin
fake_repo_dir="$(mktemp -d)"
stub_bin="$(mktemp -d)"
cat > "$stub_bin/curl" <<'EOF'
#!/usr/bin/env bash
url="${@: -1}"
out=""
for ((i=1; i<=$#; i++)); do
if [[ "${!i}" == "-o" ]]; then j=$((i+1)); out="${!j}"; fi
done
case "$url" in
*/packages.txt) echo stub > "$out" ;;
*/hypr/hyprland.conf) echo stub > "$out" ;;
*/local-bin/*.sh) echo stub > "$out" ;;
*/terminal/p10k/.p10k.zsh) echo stub-p10k > "$out" ;;
*/terminal/kali/prompt.zsh) echo stub-kali > "$out" ;;
*) echo "unexpected url: $url" >&2; exit 1 ;;
esac
EOF
chmod +x "$stub_bin/curl"
REPO_DIR="$fake_repo_dir"
PATH="$stub_bin:$PATH"
ensure_repo_files
[ "$(cat "$REPO_DIR/terminal/p10k/.p10k.zsh")" = "stub-p10k" ]
[ "$(cat "$REPO_DIR/terminal/kali/prompt.zsh")" = "stub-kali" ]
rm -rf "$fake_repo_dir" "$stub_bin"
}