Compare commits
5 Commits
19b31859f6
...
6c5c1f8589
| Author | SHA1 | Date |
|---|---|---|
|
|
6c5c1f8589 | |
|
|
8128ae84e4 | |
|
|
f1ea6dcb54 | |
|
|
b5a3b46c79 | |
|
|
e25dd231cb |
|
|
@ -22,6 +22,7 @@ yazi/*.toml-*
|
|||
|
||||
# Generated files
|
||||
readme.html
|
||||
docs/html/
|
||||
|
||||
# Build/image output artifacts
|
||||
*.iso
|
||||
|
|
|
|||
|
|
@ -0,0 +1,407 @@
|
|||
#!/usr/bin/env bash
|
||||
# md-to-html.sh — Convert Markdown docs to styled HTML with the CyberQueer theme.
|
||||
#
|
||||
# Usage:
|
||||
# bash docs/md-to-html.sh # convert all docs/md/*.md
|
||||
# bash docs/md-to-html.sh docs/md/foo.md # convert one file
|
||||
#
|
||||
# Output lands in docs/html/ with the same base name.
|
||||
# Requires: python3 with the 'markdown' package (python-markdown on Arch).
|
||||
# sudo pacman -S python-markdown
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
MD_DIR="$SCRIPT_DIR/md"
|
||||
HTML_DIR="$SCRIPT_DIR/html"
|
||||
|
||||
# ── Preflight ─────────────────────────────────────────────────────────────────
|
||||
if ! python3 -c "import markdown" 2>/dev/null; then
|
||||
echo "python-markdown not found. Installing..."
|
||||
sudo pacman -S --noconfirm python-markdown \
|
||||
|| { echo "Error: please install python-markdown manually."; exit 1; }
|
||||
fi
|
||||
|
||||
mkdir -p "$HTML_DIR"
|
||||
|
||||
# ── CyberQueer inline CSS ─────────────────────────────────────────────────────
|
||||
# Read live palette from colors.conf if available, otherwise use defaults.
|
||||
_hex() {
|
||||
local key="$1" default="$2"
|
||||
if [[ -f "$SCRIPT_DIR/../colors.conf" ]]; then
|
||||
local v
|
||||
v=$(grep -m1 "^${key}=" "$SCRIPT_DIR/../colors.conf" 2>/dev/null | cut -d= -f2 | tr -d '[:space:]' | sed 's/#.*//')
|
||||
[[ -n "$v" ]] && { printf '#%s' "${v^^}"; return; }
|
||||
fi
|
||||
printf '#%s' "$default"
|
||||
}
|
||||
|
||||
C_BG=$(_hex COLOR_BG 1A1A1A)
|
||||
C_TEXT=$(_hex COLOR_TEXT D6ABAB)
|
||||
C_HI=$(_hex COLOR_HIGHLIGHT E40046)
|
||||
C_VIO=$(_hex COLOR_DARK 5018DD)
|
||||
C_RED=$(_hex COLOR_RED F50505)
|
||||
|
||||
# Derived
|
||||
C_BG2="#242424"
|
||||
C_BG3="#2e2e2e"
|
||||
C_BORDER="${C_VIO}"
|
||||
|
||||
CSS="
|
||||
/* ── CyberQueer Theme ──────────────────────────────────────────────── */
|
||||
:root {
|
||||
--bg: ${C_BG};
|
||||
--bg2: ${C_BG2};
|
||||
--bg3: ${C_BG3};
|
||||
--text: ${C_TEXT};
|
||||
--accent: ${C_HI};
|
||||
--violet: ${C_VIO};
|
||||
--danger: ${C_RED};
|
||||
--border: ${C_BORDER};
|
||||
--radius: 10px;
|
||||
--radius-sm: 5px;
|
||||
}
|
||||
|
||||
/* ── Reset & Base ──────────────────────────────────────────────────── */
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
html { scroll-behavior: smooth; }
|
||||
|
||||
body {
|
||||
background-color: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: 'Agave Nerd Font Mono', 'JetBrains Mono', 'Fira Code',
|
||||
'Cascadia Code', 'Source Code Pro', monospace;
|
||||
font-size: 15px;
|
||||
line-height: 1.75;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 2.5rem 2rem 5rem;
|
||||
}
|
||||
|
||||
/* ── Typography ────────────────────────────────────────────────────── */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: 'Agave Nerd Font Mono', monospace;
|
||||
font-weight: 700;
|
||||
line-height: 1.25;
|
||||
margin-top: 2.2rem;
|
||||
margin-bottom: 0.6rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: var(--accent);
|
||||
font-size: 2.2rem;
|
||||
border-bottom: 3px solid var(--accent);
|
||||
padding-bottom: 0.4rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: var(--accent);
|
||||
font-size: 1.55rem;
|
||||
border-bottom: 2px solid var(--violet);
|
||||
padding-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: var(--violet);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
h4, h5, h6 {
|
||||
color: var(--text);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0.9rem 0;
|
||||
}
|
||||
|
||||
strong { color: var(--accent); font-weight: 700; }
|
||||
em { color: var(--violet); font-style: italic; }
|
||||
|
||||
/* ── Links ─────────────────────────────────────────────────────────── */
|
||||
a {
|
||||
color: var(--violet);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid transparent;
|
||||
transition: color 0.15s, border-color 0.15s;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--accent);
|
||||
border-bottom-color: var(--accent);
|
||||
}
|
||||
|
||||
/* ── Code ──────────────────────────────────────────────────────────── */
|
||||
code {
|
||||
font-family: inherit;
|
||||
background: var(--bg2);
|
||||
color: var(--violet);
|
||||
border: 1px solid var(--violet);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 0.1em 0.42em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: var(--bg2);
|
||||
border: 2px solid var(--violet);
|
||||
border-radius: var(--radius);
|
||||
padding: 1.2rem 1.4rem;
|
||||
overflow-x: auto;
|
||||
margin: 1.2rem 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
pre code {
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
color: var(--text);
|
||||
font-size: 0.875em;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Syntax-like token colouring (no JS required — structural only) */
|
||||
pre code .kw { color: var(--accent); }
|
||||
pre code .str { color: var(--violet); }
|
||||
pre code .cm { color: #666; font-style: italic; }
|
||||
|
||||
/* ── Horizontal Rule ───────────────────────────────────────────────── */
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 2px solid var(--violet);
|
||||
margin: 2rem 0;
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
/* ── Blockquote ────────────────────────────────────────────────────── */
|
||||
blockquote {
|
||||
border-left: 4px solid var(--accent);
|
||||
background: var(--bg2);
|
||||
margin: 1.2rem 0;
|
||||
padding: 0.8rem 1.2rem;
|
||||
border-radius: 0 var(--radius) var(--radius) 0;
|
||||
color: var(--text);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
blockquote p { margin: 0; }
|
||||
|
||||
/* ── Tables ────────────────────────────────────────────────────────── */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border: 2px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
margin: 1.2rem 0;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
th {
|
||||
background: var(--violet);
|
||||
color: var(--bg);
|
||||
text-align: left;
|
||||
padding: 0.55rem 0.9rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.5rem 0.9rem;
|
||||
border-top: 1px solid var(--bg3);
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
tr:nth-child(even) td { background: var(--bg2); }
|
||||
tr:hover td { background: var(--bg3); }
|
||||
|
||||
/* ── Lists ─────────────────────────────────────────────────────────── */
|
||||
ul, ol {
|
||||
padding-left: 1.6rem;
|
||||
margin: 0.7rem 0;
|
||||
}
|
||||
|
||||
li { margin: 0.25rem 0; }
|
||||
|
||||
ul li::marker { color: var(--accent); }
|
||||
ol li::marker { color: var(--violet); font-weight: 700; }
|
||||
|
||||
/* ── Nav sidebar (index page) ──────────────────────────────────────── */
|
||||
nav ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav li { margin: 0.3rem 0; }
|
||||
|
||||
/* ── Page Header Bar ───────────────────────────────────────────────── */
|
||||
.page-header {
|
||||
border-bottom: 3px solid var(--accent);
|
||||
padding-bottom: 0.6rem;
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.page-header .site-title {
|
||||
color: var(--accent);
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.75;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ── Back link ─────────────────────────────────────────────────────── */
|
||||
.back-link {
|
||||
display: inline-block;
|
||||
color: var(--violet);
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border: 1px solid var(--violet);
|
||||
border-radius: 30px;
|
||||
transition: background 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.back-link:hover {
|
||||
background: var(--violet);
|
||||
color: var(--bg);
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
/* ── Footer ────────────────────────────────────────────────────────── */
|
||||
footer {
|
||||
margin-top: 4rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--bg3);
|
||||
color: var(--text);
|
||||
opacity: 0.4;
|
||||
font-size: 0.8rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ── Responsive ────────────────────────────────────────────────────── */
|
||||
@media (max-width: 680px) {
|
||||
body { padding: 1.2rem 1rem 3rem; font-size: 14px; }
|
||||
h1 { font-size: 1.7rem; }
|
||||
h2 { font-size: 1.3rem; }
|
||||
table { display: block; overflow-x: auto; }
|
||||
}
|
||||
|
||||
/* ── Selection ─────────────────────────────────────────────────────── */
|
||||
::selection {
|
||||
background: var(--accent);
|
||||
color: var(--bg);
|
||||
}
|
||||
|
||||
/* ── Scrollbar ─────────────────────────────────────────────────────── */
|
||||
::-webkit-scrollbar { width: 8px; height: 8px; }
|
||||
::-webkit-scrollbar-track { background: var(--bg2); }
|
||||
::-webkit-scrollbar-thumb { background: var(--violet); border-radius: 4px; }
|
||||
::-webkit-scrollbar-thumb:hover { background: var(--accent); }
|
||||
"
|
||||
|
||||
# ── HTML template ─────────────────────────────────────────────────────────────
|
||||
render_html() {
|
||||
local title="$1"
|
||||
local body_html="$2"
|
||||
local is_index="$3" # "true" or "false"
|
||||
|
||||
local back_link=""
|
||||
if [[ "$is_index" == "false" ]]; then
|
||||
back_link='<a class="back-link" href="index.html">← Index</a>'
|
||||
fi
|
||||
|
||||
cat <<HTML
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>${title} — M-Archy Dotfiles</title>
|
||||
<style>${CSS}</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="page-header">
|
||||
<span class="site-title">M-Archy Dotfiles</span>
|
||||
</header>
|
||||
${back_link}
|
||||
<main>
|
||||
${body_html}
|
||||
</main>
|
||||
<footer>
|
||||
Generated by md-to-html.sh · CyberQueer theme · $(date '+%Y-%m-%d')
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
HTML
|
||||
}
|
||||
|
||||
# ── Python converter ──────────────────────────────────────────────────────────
|
||||
convert_file() {
|
||||
local src="$1"
|
||||
local base; base="$(basename "$src" .md)"
|
||||
local dest="$HTML_DIR/${base}.html"
|
||||
local is_index="false"
|
||||
[[ "$base" == "index" ]] && is_index="true"
|
||||
|
||||
# Extract title from first H1 in the markdown
|
||||
local title
|
||||
title=$(grep -m1 '^# ' "$src" | sed 's/^# //' || echo "$base")
|
||||
|
||||
# Convert markdown → HTML body via python-markdown
|
||||
local body_html
|
||||
body_html=$(python3 - "$src" <<'PYEOF'
|
||||
import sys, markdown
|
||||
|
||||
with open(sys.argv[1], encoding="utf-8") as fh:
|
||||
text = fh.read()
|
||||
|
||||
extensions = [
|
||||
"tables",
|
||||
"fenced_code",
|
||||
"toc",
|
||||
"attr_list",
|
||||
"def_list",
|
||||
"abbr",
|
||||
"meta",
|
||||
]
|
||||
|
||||
md = markdown.Markdown(extensions=extensions, extension_configs={
|
||||
"toc": {"permalink": True, "permalink_class": "toc-anchor"},
|
||||
})
|
||||
|
||||
print(md.convert(text))
|
||||
PYEOF
|
||||
)
|
||||
|
||||
render_html "$title" "$body_html" "$is_index" > "$dest"
|
||||
printf " %-40s → %s\n" "$(basename "$src")" "$(basename "$dest")"
|
||||
}
|
||||
|
||||
# ── Main ──────────────────────────────────────────────────────────────────────
|
||||
if [[ $# -gt 0 ]]; then
|
||||
# Explicit file list
|
||||
for f in "$@"; do
|
||||
[[ -f "$f" ]] || { echo "Not found: $f" >&2; continue; }
|
||||
convert_file "$f"
|
||||
done
|
||||
else
|
||||
# All markdown files in docs/md/
|
||||
shopt -s nullglob
|
||||
files=("$MD_DIR"/*.md)
|
||||
if [[ ${#files[@]} -eq 0 ]]; then
|
||||
echo "No .md files found in $MD_DIR"
|
||||
exit 0
|
||||
fi
|
||||
echo "Converting ${#files[@]} files → $HTML_DIR/"
|
||||
for f in "${files[@]}"; do
|
||||
convert_file "$f"
|
||||
done
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Done. Open docs/html/index.html in a browser."
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
# Archiso — Custom Live Installer
|
||||
|
||||
The archiso build system produces a bootable Arch Linux ISO pre-loaded with the M-Archy installer scripts. Optionally, an answerfile can be embedded so the entire install — base OS + dotfiles — runs with zero user interaction.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
sudo pacman -S archiso jq
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Building the ISO
|
||||
|
||||
```bash
|
||||
# Basic build — interactive installer, no answerfile
|
||||
bash setup/archiso/build.sh
|
||||
|
||||
# Specify output directory
|
||||
bash setup/archiso/build.sh /path/to/output
|
||||
|
||||
# Embed an answerfile for automated deployment
|
||||
bash setup/archiso/build.sh --preconf
|
||||
|
||||
# Embed a specific answerfile
|
||||
bash setup/archiso/build.sh --preconf ~/my-server.json
|
||||
|
||||
# Both flags together
|
||||
bash setup/archiso/build.sh --preconf ~/my-server.json /media/usb/output
|
||||
```
|
||||
|
||||
| Flag | Effect |
|
||||
|------|--------|
|
||||
| _(none)_ | Clean ISO, no answerfile |
|
||||
| `--preconf` | Embed `~/answerfile.json` at `/answerfile.json` in the ISO |
|
||||
| `--preconf FILE` | Embed the specified file instead |
|
||||
|
||||
Build artefacts land in `~/m-archy-out/` by default. Override with the `OUT_DIR` environment variable or by passing a path argument.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Default | Purpose |
|
||||
|----------|---------|---------|
|
||||
| `WORK_DIR` | `~/m-archy-build` | Scratch space for mkarchiso |
|
||||
| `OUT_DIR` | `~/m-archy-out` | ISO output directory |
|
||||
|
||||
---
|
||||
|
||||
## What the Build Does
|
||||
|
||||
1. Copies the upstream `releng` Arch base profile
|
||||
2. Applies the M-Archy overlay (`setup/archiso/overlay/`)
|
||||
3. Replaces `profiledef.sh` with the M-Archy version
|
||||
4. Adds extra packages from `packages.extra`
|
||||
5. Embeds both installer scripts (`arch-autoinstall.sh`, `archbaseos-guided-install.sh`) into `/root/installer/`
|
||||
6. If `--preconf`: copies the answerfile to `/answerfile.json` in the ISO's airootfs
|
||||
7. Runs `mkarchiso` to produce the final `.iso`
|
||||
|
||||
---
|
||||
|
||||
## Extra Packages on the Live System
|
||||
|
||||
Defined in `setup/archiso/overlay/packages.extra`:
|
||||
|
||||
```
|
||||
git
|
||||
jq
|
||||
pam-u2f
|
||||
btop
|
||||
fastfetch
|
||||
openssh
|
||||
```
|
||||
|
||||
These are added on top of the standard Arch `releng` package set.
|
||||
|
||||
---
|
||||
|
||||
## Live System Entry Points
|
||||
|
||||
Once booted from the ISO, the following are available:
|
||||
|
||||
### `install-arch`
|
||||
|
||||
A command placed in `/usr/local/bin/`:
|
||||
|
||||
```bash
|
||||
install-arch # guided mode (default)
|
||||
install-arch guided # guided interactive install
|
||||
install-arch auto # automated mode (reads /answerfile.json)
|
||||
```
|
||||
|
||||
### `/root/launch.sh`
|
||||
|
||||
Internal dispatcher used by `install-arch`.
|
||||
|
||||
### `/answerfile.json`
|
||||
|
||||
Only present when built with `--preconf`. Both installer scripts check for this file on startup. If found, all prompts are answered from it — the only interaction required is the disk-encryption password (passwords are never stored in answerfiles).
|
||||
|
||||
---
|
||||
|
||||
## Automated Deployment Workflow
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Developer machine │
|
||||
│ │
|
||||
│ 1. generate-answerfile.sh │
|
||||
│ → ~/answerfile.json │
|
||||
│ │
|
||||
│ 2. build.sh --preconf │
|
||||
│ → ~/m-archy-out/m-archy.iso │
|
||||
│ │
|
||||
│ 3. dd if=m-archy.iso of=/dev/sdX │
|
||||
└──────────────┬──────────────────────┘
|
||||
│ USB
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Target machine (boots from USB) │
|
||||
│ │
|
||||
│ 4. install-arch auto │
|
||||
│ reads /answerfile.json │
|
||||
│ installs base OS │
|
||||
│ runs tui-install.sh in chroot │
|
||||
│ installs dotfiles & apps │
|
||||
│ │
|
||||
│ 5. Reboot → ready system │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
For multi-machine deployments, the `hostname` field in the answerfile is combined with the machine's MAC address, so each system gets a unique hostname even though they share the same answerfile.
|
||||
|
||||
---
|
||||
|
||||
## Overlay Structure
|
||||
|
||||
```
|
||||
setup/archiso/overlay/
|
||||
├── airootfs/
|
||||
│ ├── etc/motd # Welcome message
|
||||
│ ├── root/
|
||||
│ │ └── launch.sh # Installer entry point
|
||||
│ └── usr/local/bin/
|
||||
│ └── install-arch # User-facing CLI command
|
||||
├── packages.extra # Additional live-system packages
|
||||
└── profiledef.sh # M-Archy ISO profile definition
|
||||
```
|
||||
|
||||
The `build.sh` script also adds at build time:
|
||||
```
|
||||
airootfs/root/installer/
|
||||
├── arch-autoinstall.sh
|
||||
└── archbaseos-guided-install.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Writing the ISO to USB
|
||||
|
||||
```bash
|
||||
# Find the USB drive
|
||||
lsblk
|
||||
|
||||
# Write (replace /dev/sdX with your drive — ALL DATA WILL BE ERASED)
|
||||
sudo dd if=~/m-archy-out/m-archy-*.iso of=/dev/sdX bs=4M status=progress oflag=sync
|
||||
```
|
||||
|
||||
Or use `ventoy` / `balenaEtcher` as alternatives.
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
# Editors
|
||||
|
||||
Three editors are configured and deployed by the `shell` component: Neovim (primary), Micro (lightweight), and Yazi (terminal file manager).
|
||||
|
||||
---
|
||||
|
||||
## Neovim
|
||||
|
||||
Config lives in `nvim/`. Deployed to `~/.config/nvim/` during `shell` module install.
|
||||
|
||||
### Plugin Manager
|
||||
|
||||
[vim-plug](https://github.com/junegunn/vim-plug) is auto-installed on first launch. Run `:PlugInstall` after the first start to fetch all plugins.
|
||||
|
||||
### Plugins
|
||||
|
||||
#### Language & Completion
|
||||
|
||||
| Plugin | Purpose |
|
||||
|--------|---------|
|
||||
| `neoclide/coc.nvim` | LSP client, auto-completion, diagnostics |
|
||||
| `rust-lang/rust.vim` | Rust filetype support |
|
||||
| `nvim-telescope/telescope.nvim` | Fuzzy finder (files, grep, LSP symbols) |
|
||||
|
||||
#### UI
|
||||
|
||||
| Plugin | Purpose |
|
||||
|--------|---------|
|
||||
| `vim-airline/vim-airline` | Status bar |
|
||||
| `vim-airline/vim-airline-themes` | Airline theme collection |
|
||||
| `junegunn/goyo.vim` | Distraction-free writing mode |
|
||||
| `voldikss/vim-floaterm` | Floating terminal windows |
|
||||
| `norcalli/nvim-colorizer.lua` | Inline colour preview |
|
||||
|
||||
#### Navigation
|
||||
|
||||
| Plugin | Purpose |
|
||||
|--------|---------|
|
||||
| `preservim/nerdtree` | File tree sidebar |
|
||||
| `junegunn/fzf` + `fzf.vim` | Fuzzy file/buffer search |
|
||||
| `elihunter173/dirbuf.nvim` | Editable directory buffer |
|
||||
|
||||
#### Snippets
|
||||
|
||||
| Plugin | Purpose |
|
||||
|--------|---------|
|
||||
| `SirVer/ultisnips` | Snippet engine |
|
||||
| `honza/vim-snippets` | Snippet collection |
|
||||
|
||||
#### Database
|
||||
|
||||
| Plugin | Purpose |
|
||||
|--------|---------|
|
||||
| `tpope/vim-dadbod` | Database query runner |
|
||||
| `kristijanhusak/vim-dadbod-ui` | GUI for vim-dadbod |
|
||||
|
||||
#### Markdown
|
||||
|
||||
| Plugin | Purpose |
|
||||
|--------|---------|
|
||||
| `tadmccorkle/markdown.nvim` | Enhanced Markdown support |
|
||||
| `ellisonleao/glow.nvim` | Markdown preview in terminal |
|
||||
|
||||
#### Theme
|
||||
|
||||
| Plugin | Purpose |
|
||||
|--------|---------|
|
||||
| `the_miro/cyberqueer.nvim` | CyberQueer colour scheme (local) |
|
||||
|
||||
### CoC LSP Settings
|
||||
|
||||
`nvim/coc-settings.json` configures language servers for:
|
||||
- Python (pyright)
|
||||
- Lua (lua-language-server)
|
||||
- Rust (rust-analyzer via rust.vim)
|
||||
|
||||
### CyberQueer Airline Theme
|
||||
|
||||
`nvim/cyberqueer-airline.vim` — a custom vim-airline theme using the CyberQueer palette, providing hot-pink/violet segments in the status bar.
|
||||
|
||||
---
|
||||
|
||||
## Micro
|
||||
|
||||
A modern, keyboard-friendly terminal editor. Config in `micro/`. Deployed to `~/.config/micro/`.
|
||||
|
||||
### Settings (`settings.json`)
|
||||
|
||||
| Setting | Value | Effect |
|
||||
|---------|-------|--------|
|
||||
| `colorscheme` | `miro-darcula` | Custom dark colour scheme |
|
||||
| Various | — | File-type-specific settings |
|
||||
|
||||
### Keybindings (`bindings.json`)
|
||||
|
||||
Custom keybindings extending Micro's defaults. See `micro/bindings.json` for the full list.
|
||||
|
||||
### Colour Schemes
|
||||
|
||||
`micro/colorschemes/` contains additional colour scheme definitions including the custom `miro-darcula` scheme.
|
||||
|
||||
### Plugins (`micro/plug/`)
|
||||
|
||||
| Plugin | Purpose |
|
||||
|--------|---------|
|
||||
| `filemanager` | Sidebar file browser |
|
||||
| `mlsp` | Language Server Protocol integration |
|
||||
|
||||
---
|
||||
|
||||
## Yazi
|
||||
|
||||
A fast, feature-rich terminal file manager written in Rust.
|
||||
Config in `yazi/`. Deployed to `~/.config/yazi/`.
|
||||
|
||||
### Configuration Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `yazi.toml` | Core settings (openers, file rules, display) |
|
||||
| `theme.toml` | CyberQueer colour palette |
|
||||
| `keymap.toml` | Keybindings (17 KB, extensively customised) |
|
||||
|
||||
### Theme
|
||||
|
||||
`yazi/theme.toml` maps the CyberQueer colours to Yazi's theme variables. It is a colour-substitution target in `apply-theme.sh`, so it updates automatically when you change the palette.
|
||||
|
||||
### File Openers
|
||||
|
||||
| File type | Opens with |
|
||||
|-----------|-----------|
|
||||
| `*.svg` | Inkscape |
|
||||
| Text files | Neovim |
|
||||
| Everything else | `xdg-open` (system default) |
|
||||
|
||||
### Display Options
|
||||
|
||||
- Hidden files visible by default
|
||||
- Symlinks shown and followed
|
||||
- Three-pane layout (parent, current, preview)
|
||||
|
||||
---
|
||||
|
||||
## Launching Editors
|
||||
|
||||
From the Hyprland desktop:
|
||||
|
||||
| Shortcut | Action |
|
||||
|----------|--------|
|
||||
| `Super + M` | Open Micro in Kitty |
|
||||
| `Super + E` | Open Yazi in Kitty |
|
||||
| `Super + T` then `nvim` | Neovim in terminal |
|
||||
|
||||
From the shell:
|
||||
```bash
|
||||
nvim file.rs # Neovim
|
||||
micro config.yaml # Micro
|
||||
yazi # Yazi file manager
|
||||
```
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
# FreeIPA & Ansible
|
||||
|
||||
The FreeIPA/Ansible system provides centralised identity management for a fleet of Arch Linux machines: single sign-on, host-group-driven package and module deployment, LUKS backup key collection, and automatic Keycloak configuration.
|
||||
|
||||
All relevant files live under `setup/modules/FreeipaAnsible/`.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌────────────────────────────────────┐
|
||||
│ FreeIPA Server │
|
||||
│ (can run in Docker / LXC) │
|
||||
│ │
|
||||
│ • User/host directory │
|
||||
│ • Kerberos KDC │
|
||||
│ • DNS (optional) │
|
||||
│ • Host group management │
|
||||
└──────────┬─────────────────────────┘
|
||||
│ SSSD / Kerberos
|
||||
▼
|
||||
┌────────────────────────────────────┐
|
||||
│ Enrolled client machine │
|
||||
│ │
|
||||
│ • sssd — authentication │
|
||||
│ • ipa CLI — host group queries │
|
||||
│ • Ansible-deployed timers │
|
||||
│ ├── package installer │
|
||||
│ ├── module installer │
|
||||
│ ├── Flatpak installer │
|
||||
│ └── baseuser group sync │
|
||||
└────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## FreeIPA Server
|
||||
|
||||
### Docker / OCI Image
|
||||
|
||||
A pre-built Docker image is available via `setup/modules/FreeipaAnsible/image/`:
|
||||
|
||||
```bash
|
||||
cd setup/modules/FreeipaAnsible/image
|
||||
cp .env.example .env
|
||||
# Edit .env with your domain, admin password, realm, etc.
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
The container runs `ipa-first-boot.sh` on first start to initialise the IPA instance, then optionally `keycloak-configure.sh` to wire up Keycloak as an OIDC provider.
|
||||
|
||||
### Interactive Server Setup
|
||||
|
||||
```bash
|
||||
bash setup/modules/optional-Modules/apps/freeipa-server.sh
|
||||
```
|
||||
|
||||
Prompts for realm, domain, admin password, and whether to generate client-install scripts.
|
||||
|
||||
---
|
||||
|
||||
## Client Enrollment
|
||||
|
||||
### Via Installer Module
|
||||
|
||||
Select `freeipa-client` during `tui-install.sh` or `install-modules.sh`.
|
||||
|
||||
### Manual Enrollment
|
||||
|
||||
Three modes:
|
||||
|
||||
```bash
|
||||
# Answerfile mode (unattended)
|
||||
bash setup/modules/FreeipaAnsible/freeipa-client.sh \
|
||||
--answerfile setup/modules/FreeipaAnsible/freeipa-client-answerfile.json
|
||||
|
||||
# Interactive prompts
|
||||
bash setup/modules/FreeipaAnsible/freeipa-client.sh --interactive
|
||||
|
||||
# Direct flag passthrough to freeipa-enroll.sh
|
||||
bash setup/modules/FreeipaAnsible/freeipa-client.sh \
|
||||
--domain freeipa.example.com \
|
||||
--server ipa.example.com \
|
||||
--principal admin
|
||||
```
|
||||
|
||||
### Client Answerfile Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"domain": "freeipa.abdelbaki.eu",
|
||||
"realm": "FREEIPA.ABDELBAKI.EU",
|
||||
"server": "freeipa.abdelbaki.eu",
|
||||
"hostname": "",
|
||||
"principal": "admin",
|
||||
"password": "",
|
||||
"mkhomedir": true,
|
||||
"sudo": true,
|
||||
"dns_update": true,
|
||||
"ntp_server": "",
|
||||
"fido2": false,
|
||||
"fido2_users": []
|
||||
}
|
||||
```
|
||||
|
||||
Leave `hostname` blank to use the current machine hostname. Leave `password` blank to be prompted at enrollment time.
|
||||
|
||||
---
|
||||
|
||||
## Ansible Playbooks
|
||||
|
||||
All playbooks live in `setup/modules/FreeipaAnsible/ansible/` and require an inventory of enrolled IPA clients.
|
||||
|
||||
### Deploy Package Auto-Installer
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventory deploy-ansipa-install.yml
|
||||
```
|
||||
|
||||
Deploys `ansipa-install-packages.sh` + a systemd timer that runs every 30 minutes. The script queries IPA for host groups named `ansipa-install-<package>` and installs/removes packages to match.
|
||||
|
||||
**Group naming convention:** `ansipa-install-firefox` → installs the `firefox` package.
|
||||
|
||||
### Deploy Module Auto-Installer
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventory deploy-ansipa-modules.yml \
|
||||
[-e ansipa_user=amir]
|
||||
```
|
||||
|
||||
Deploys `ansipa-install-modules.sh` + timer. Queries for groups named `ansipa-module-<name>` and runs the matching script from `/usr/local/lib/ansipa-modules/<name>.sh`.
|
||||
|
||||
Module scripts are the same ones used by `install-modules.sh` — copied from `setup/modules/optional-Modules/apps/*.sh`.
|
||||
|
||||
**Group naming convention:** `ansipa-module-docker` → runs `docker.sh` on the host.
|
||||
|
||||
Each module is applied once and stamped in `/var/lib/ansipa-modules/<name>.done`. Re-running the timer skips already-applied modules.
|
||||
|
||||
### Deploy BaseUser Sync
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventory deploy-baseuser-sync.yml
|
||||
```
|
||||
|
||||
Deploys a `systemd.path` unit that triggers whenever a user logs in. If the user is a member of the IPA `BaseUser` group, they are automatically added to the local `baseusers` group — useful for desktop permission grants.
|
||||
|
||||
### Collect LUKS Backup Keys
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventory collect-luks-keys.yml \
|
||||
[-e luks_keys_store=/secure/location]
|
||||
```
|
||||
|
||||
For each enrolled host, checks for `/_LUKS_BACKUP_KEY` (placed there by the M-Archy installer when disk encryption is enabled) and fetches it to the controller as:
|
||||
|
||||
```
|
||||
<luks_keys_store>/<HOSTNAME>_LUKS_BACKUP_KEY
|
||||
```
|
||||
|
||||
Keys are stored with mode `0400`. The store directory is created with mode `0700`.
|
||||
|
||||
**Schedule for automatic collection:**
|
||||
```bash
|
||||
# Add to crontab on the Ansible controller
|
||||
0 3 * * * cd /path/to/playbooks && ansible-playbook -i inventory collect-luks-keys.yml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Host Group Reference
|
||||
|
||||
| Group prefix | Handled by | Effect |
|
||||
|--------------|-----------|--------|
|
||||
| `ansipa-install-<pkg>` | `ansipa-install-packages.sh` | Install/remove native package |
|
||||
| `ansipa-module-<name>` | `ansipa-install-modules.sh` | Run module script once |
|
||||
| `fp_install-<app>` | `ansipa-install-flatpaks.sh` | Install Flatpak app |
|
||||
| `BaseUser` | `auto-add-baseuser.sh` | Add user to local `baseusers` group |
|
||||
|
||||
---
|
||||
|
||||
## LUKS Key Flow
|
||||
|
||||
```
|
||||
Install time (arch-autoinstall.sh or archbaseos-guided-install.sh)
|
||||
─────────────────────────────────────────────────────────────────
|
||||
1. User sets primary LUKS passphrase interactively
|
||||
2. 64-byte random key generated from /dev/urandom
|
||||
3. Key enrolled in second LUKS slot
|
||||
4. Key written to /_LUKS_BACKUP_KEY (mode 0400, root-only)
|
||||
inside the encrypted Btrfs volume
|
||||
|
||||
Post-install (Ansible)
|
||||
──────────────────────
|
||||
5. collect-luks-keys.yml runs from the controller
|
||||
6. Fetches /_LUKS_BACKUP_KEY from each client
|
||||
7. Stores as luks-keys/<HOSTNAME>_LUKS_BACKUP_KEY (mode 0400)
|
||||
on the controller
|
||||
```
|
||||
|
||||
The backup key lives inside the encrypted partition, so it is only accessible when the disk is already unlocked. Its purpose is to allow an admin to unlock the disk for recovery without knowing the user's passphrase.
|
||||
|
||||
---
|
||||
|
||||
## Auto Enrollment + Ansible
|
||||
|
||||
```bash
|
||||
bash setup/modules/FreeipaAnsible/auto-enroll-ansible.sh
|
||||
```
|
||||
|
||||
Combines FreeIPA client enrollment and Ansible deployment in one shot. Useful for provisioning scripts that run on first boot.
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
# Hyprland Desktop Environment
|
||||
|
||||
The Hyprland setup is the primary desktop environment — a Wayland compositor with a full ecosystem of bars, launchers, notification daemons, and theming tools.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
| Component | Role |
|
||||
|-----------|------|
|
||||
| **Hyprland** | Wayland tiling compositor (dwindle layout) |
|
||||
| **Kitty** | Primary terminal emulator |
|
||||
| **EWW** | Status bar (three device variants) |
|
||||
| **Waybar** | Alternative status bar |
|
||||
| **Wofi** | App launcher (keyboard-driven) |
|
||||
| **Walker** | Fast CLI launcher |
|
||||
| **uLauncher** | GUI app launcher |
|
||||
| **Dunst** | Notification daemon |
|
||||
| **Hyprlock** | Screen locker |
|
||||
| **Hyprpaper** | Wallpaper daemon |
|
||||
| **Hypridle** | Idle management (sleep, lock) |
|
||||
| **nwg-dock** | Application dock |
|
||||
| **nwg-drawer** | Application drawer |
|
||||
| **nwg-panel** | Desktop menu |
|
||||
| **Vicinae** | Gesture-triggered context launcher |
|
||||
| **ly** | TUI login manager |
|
||||
|
||||
---
|
||||
|
||||
## Config File Map
|
||||
|
||||
```
|
||||
desktopenvs/hyprland/
|
||||
├── hypr/
|
||||
│ ├── hyprland.conf # Root config — imports all below
|
||||
│ ├── hyprtoolkit.conf # Toolkit utilities
|
||||
│ ├── hyprlock.conf # Lock screen
|
||||
│ ├── hypridle.conf # Idle timeouts
|
||||
│ └── hyprpaper.conf # Wallpaper
|
||||
├── hypr-usr/ # Per-user override layer
|
||||
│ ├── binds.conf # All keybindings
|
||||
│ ├── input.conf # Keyboard/mouse settings
|
||||
│ ├── monitors.conf # Display layout
|
||||
│ ├── windowrules.conf # Per-app behaviour rules
|
||||
│ └── autostart.conf # Startup programs
|
||||
├── kitty/
|
||||
│ ├── kitty.conf # Terminal config
|
||||
│ ├── current-theme.conf # Active palette (CyberQueer)
|
||||
│ └── themes/cyberqueer.conf # Theme definition
|
||||
├── waybar/
|
||||
│ ├── config # Module layout and data sources
|
||||
│ └── style.css # CyberQueer styling
|
||||
├── wofi/style.css # Launcher styling
|
||||
├── walker/themes/ # Walker theme (cyberqueer.css)
|
||||
├── dunst/ # Notification styling
|
||||
├── eww/ # EWW bar (PC — no battery)
|
||||
├── eww-nobattery/ # EWW bar alias
|
||||
├── eww-touch/ # EWW bar (tablet / touchscreen)
|
||||
├── spicetify/ # Spotify CyberQueer themes
|
||||
├── Vencord/ # Discord CyberQueer themes
|
||||
├── btop/ # System monitor + cyberqueer.theme
|
||||
├── scripts/ # 20+ utility scripts
|
||||
└── config-updater/ # Config synchronisation tool
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Keybindings
|
||||
|
||||
All bindings live in `hypr-usr/binds.conf`.
|
||||
|
||||
### Applications
|
||||
|
||||
| Binding | Action |
|
||||
|---------|--------|
|
||||
| `Super + T` | Kitty terminal |
|
||||
| `Super + Shift + T` | Cool Retro Term (CRT profile) |
|
||||
| `Super + M` | Micro editor |
|
||||
| `Super + E` | Thunar file manager |
|
||||
| `Super + X` | Wofi app launcher |
|
||||
| `Super + F` | File search (wofi) |
|
||||
| `Super + Shift + F` | Folder search (wofi) |
|
||||
|
||||
### Window Management
|
||||
|
||||
| Binding | Action |
|
||||
|---------|--------|
|
||||
| `Super + Q` | Close focused window |
|
||||
| `Super + V` | Toggle floating |
|
||||
| `Super + Shift + V` | Centre floating window |
|
||||
| `Super + P` | Toggle pseudo-tiling |
|
||||
| `Super + J` | Toggle split direction |
|
||||
| `Super + Arrow / hjkl` | Focus window by direction |
|
||||
| `Super + Shift + Arrow / hjkl` | Move window by direction |
|
||||
| `Super + [0-9]` | Switch workspace |
|
||||
| `Super + Shift + [0-9]` | Move window to workspace |
|
||||
| `Super + mouse drag` | Move/resize floating window |
|
||||
|
||||
### System
|
||||
|
||||
| Binding | Action |
|
||||
|---------|--------|
|
||||
| `Super + O` | Lock screen (hyprlock) |
|
||||
| `Super + Alt + O` | Power menu |
|
||||
| `Super + Ctrl + O` | Shutdown immediately |
|
||||
| `Super + Z` | Toggle EWW bar |
|
||||
| `Super + Ctrl + P` | Start screen recording |
|
||||
|
||||
### Touchpad Gestures
|
||||
|
||||
| Gesture | Action |
|
||||
|---------|--------|
|
||||
| 3-finger swipe left/right | Switch workspace |
|
||||
| 3-finger swipe up/down | Move window to workspace |
|
||||
| 3-finger pinch | Toggle overview |
|
||||
| 4-finger tap | Open launcher |
|
||||
|
||||
---
|
||||
|
||||
## Status Bar (EWW)
|
||||
|
||||
EWW (Elkowar's Wayland Window Manager) is compiled from source during Hyprland install and comes in three variants:
|
||||
|
||||
| Variant | Device | Battery Widget |
|
||||
|---------|--------|---------------|
|
||||
| `eww/` | Desktop PC | No |
|
||||
| `eww-nobattery/` | Desktop PC | No |
|
||||
| `eww-touch/` | Laptop / tablet | Yes |
|
||||
|
||||
During install the EWW installer asks which variant to use.
|
||||
|
||||
### Waybar
|
||||
|
||||
An alternative to EWW — always installed. The bar layout:
|
||||
|
||||
- **Left**: clock, disk usage, RAM, CPU, temperature
|
||||
- **Centre**: workspace switcher, active window title
|
||||
- **Right**: network speed, IP address, system tray, audio volume, battery (if present)
|
||||
|
||||
---
|
||||
|
||||
## Launchers
|
||||
|
||||
### Wofi
|
||||
|
||||
Keyboard-driven app/file launcher. Activated with `Super + X`.
|
||||
Config: `wofi/style.css` — themed with CyberQueer colours.
|
||||
|
||||
### Walker
|
||||
|
||||
Fast CLI launcher (`walker`). Pre-configured with `walker/config.toml` and the `cyberqueer.css` / `cyberqueer.toml` theme files.
|
||||
|
||||
### uLauncher
|
||||
|
||||
GUI launcher with plugin support. Theme: `ulauncher/user-themes/cyberqueer/`.
|
||||
Colours in the theme are colour-substitution targets (updated by `apply-theme.sh`).
|
||||
|
||||
---
|
||||
|
||||
## Notification Daemon (Dunst)
|
||||
|
||||
Lightweight notification daemon. Config in `dunst/dunstrc`.
|
||||
Notifications appear top-right with CyberQueer styling.
|
||||
|
||||
---
|
||||
|
||||
## Lock Screen (Hyprlock)
|
||||
|
||||
`hyprlock` is the Wayland screen locker. Config at `hypr/hyprlock.conf`.
|
||||
Activated by `Super + O` or automatically via `hypridle` after idle timeout.
|
||||
|
||||
---
|
||||
|
||||
## Scripts Reference
|
||||
|
||||
All scripts live in `desktopenvs/hyprland/scripts/` and are deployed to `~/.config/scripts/`.
|
||||
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| `caffeine.sh` | Toggle hypridle (prevent sleep) |
|
||||
| `ewwstart.sh` | Launch EWW bar on startup |
|
||||
| `ewwstart-niri.sh` | EWW for niri compositor variant |
|
||||
| `togglebar.sh` | Show/hide EWW bar (monitor-aware) |
|
||||
| `helpmenu.sh` | Display keybindings from binds.conf |
|
||||
| `screenshot.sh` | Region/full screenshot via grim + slurp |
|
||||
| `screenrec.sh` | Screen recording |
|
||||
| `screenrotationacw.sh` | Rotate screen anti-clockwise (tablet) |
|
||||
| `screenrotationwcw.sh` | Rotate screen clockwise (tablet) |
|
||||
| `unified-rotate.sh` | Unified rotation handler |
|
||||
| `hyprland-toggle-touchpad.sh` | Enable/disable touchpad |
|
||||
| `wofi-file-search.sh` | File search via wofi |
|
||||
| `foldersearch.sh` | Folder search via wofi |
|
||||
| `pwr-dmenu.sh` | Power menu (shutdown/reboot/suspend) |
|
||||
| `caffeine.sh` | Toggle idle inhibitor |
|
||||
| `getispeed.sh` | Internet speed test display |
|
||||
| `journal.sh` | Quick journal entry |
|
||||
| `date.sh` / `time.sh` | Date/time waybar helpers |
|
||||
| `uptime.sh` | Uptime display |
|
||||
| `dysk-phydisks.sh` | Physical disk info (dysk) |
|
||||
| `drawer.sh` | Open nwg-drawer |
|
||||
| `menu.sh` | Application menu |
|
||||
| `onscreenkb.sh` | On-screen keyboard (wvkbd) |
|
||||
| `bluetooth-applet.sh` | Bluetooth UI wrapper |
|
||||
| `togglewinbars.sh` | Toggle window title bars |
|
||||
| `toggle-layout.sh` | Switch tiling layout |
|
||||
| `playpause.sh` | Media play/pause |
|
||||
| `calender-fix.sh` | Calendar waybar widget fix |
|
||||
|
||||
---
|
||||
|
||||
## Application Theming
|
||||
|
||||
### Spotify (Spicetify)
|
||||
|
||||
Two CyberQueer Spicetify themes are available:
|
||||
|
||||
| Theme | Style |
|
||||
|-------|-------|
|
||||
| `cli-cyberqueer` | CLI-inspired, minimal |
|
||||
| `matte-cyberqueer` | Matte finish variant |
|
||||
|
||||
Applied automatically when the `spotify` module is installed.
|
||||
|
||||
### Discord (Vencord)
|
||||
|
||||
Two Discord themes:
|
||||
|
||||
| Theme | Location |
|
||||
|-------|----------|
|
||||
| `cyberqueer.theme.css` | Standalone CyberQueer theme |
|
||||
| `system24/…/cyberqueer.theme.css` | system24 framework with CyberQueer colours |
|
||||
|
||||
### Terminal (Kitty)
|
||||
|
||||
The `kitty/themes/cyberqueer.conf` file defines the full 16-colour palette mapped to CyberQueer values. It is sourced by `current-theme.conf` which is imported in `kitty.conf`.
|
||||
|
||||
---
|
||||
|
||||
## Login Manager (ly)
|
||||
|
||||
`ly` is a TUI display manager configured via `etc-ly-config.ini` (deployed to `/etc/ly/config.ini`). Its colours are tracked by `apply-theme.sh` (system file, applied via `sudo`).
|
||||
|
||||
---
|
||||
|
||||
## Installing Hyprland
|
||||
|
||||
```bash
|
||||
# Via the TUI installer
|
||||
bash ~/Dotfiles/setup/tui-install.sh
|
||||
# → Select "shell" and "Hyprland" in the dialogs
|
||||
|
||||
# On an existing system
|
||||
bash ~/Dotfiles/setup/install-modules.sh
|
||||
# → Not available; Hyprland is a base DE, use the full installer
|
||||
|
||||
# Direct script
|
||||
bash ~/Dotfiles/setup/modules/Desktop-Environments/hyprland.sh
|
||||
```
|
||||
|
||||
The install script compiles EWW from source (requires Rust), copies all configs, installs the GTK and cursor themes, enables `ly@tty1`, and configures `greetd`.
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
# M-Archy Dotfiles
|
||||
|
||||
**Arch Linux · Hyprland · Wayland · CyberQueer**
|
||||
|
||||
A production-grade Arch Linux configuration for network administration, development, and gaming — built around the Hyprland Wayland compositor and the CyberQueer colour theme.
|
||||
|
||||
---
|
||||
|
||||
## What's Inside
|
||||
|
||||
| Area | Description |
|
||||
|------|-------------|
|
||||
| [Installation](installation.md) | Interactive TUI installer, answerfile automation, ISO building |
|
||||
| [Theming](theming.md) | CyberQueer colour system and `apply-theme.sh` |
|
||||
| [Hyprland](hyprland.md) | Desktop environment, keybindings, bars, launchers |
|
||||
| [Modules](modules.md) | Core modules and full optional-app catalogue |
|
||||
| [Archiso](archiso.md) | Building the custom live installer ISO |
|
||||
| [FreeIPA & Ansible](freeipa-ansible.md) | Identity management and automated config deployment |
|
||||
| [Editors](editors.md) | Neovim, Micro, Yazi |
|
||||
| [Utilities](utilities.md) | Encryption helpers, ClamAV, credentials, update scripts |
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# 1 — clone
|
||||
git clone https://git.abdelbaki.eu/The_miro/Dotfiles.git ~/Dotfiles
|
||||
|
||||
# 2 — run the interactive installer
|
||||
bash ~/Dotfiles/setup/tui-install.sh
|
||||
|
||||
# 3 — optionally apply a custom colour palette
|
||||
nano ~/Dotfiles/colors.conf
|
||||
bash ~/Dotfiles/apply-theme.sh
|
||||
```
|
||||
|
||||
For a fully automated install from a live USB, see [Archiso](archiso.md).
|
||||
|
||||
---
|
||||
|
||||
## CyberQueer Colour Palette
|
||||
|
||||
| Role | Name | Hex |
|
||||
|------|------|-----|
|
||||
| Background | Dark grey | `#1A1A1A` |
|
||||
| Text | Rose white | `#D6ABAB` |
|
||||
| Primary accent | Hot pink | `#E40046` |
|
||||
| Secondary accent | Electric violet | `#5018DD` |
|
||||
| Danger / alerts | Red | `#F50505` |
|
||||
|
||||
---
|
||||
|
||||
## Repository Layout
|
||||
|
||||
```
|
||||
Dotfiles/
|
||||
├── apply-theme.sh # Propagate colours across all configs
|
||||
├── colors.conf # Single source of truth for the palette
|
||||
├── update.sh # pacman + yay full system update
|
||||
├── setup/
|
||||
│ ├── tui-install.sh # Main interactive / answerfile installer
|
||||
│ ├── generate-answerfile.sh # Dry-run to produce answerfile.json
|
||||
│ ├── arch-autoinstall.sh # Automated base OS installer
|
||||
│ ├── archbaseos-guided-install.sh # Guided base OS installer
|
||||
│ ├── install-modules.sh # Add optional modules to existing system
|
||||
│ ├── archiso/ # Custom Arch live ISO builder
|
||||
│ └── modules/ # Modular install scripts
|
||||
├── desktopenvs/hyprland/ # All Hyprland / Wayland configs
|
||||
├── gtk-themes/cyberqueer/ # GTK 3 & 4 theme
|
||||
├── qt-themes/cyberqueer/ # Qt platform theme
|
||||
├── nvim/ # Neovim config
|
||||
├── micro/ # Micro editor config
|
||||
├── yazi/ # Yazi file manager config
|
||||
├── clamav/ # ClamAV on-access scan setup
|
||||
└── docs/ # This documentation
|
||||
```
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
# Installation
|
||||
|
||||
Three paths are available depending on how much you want to automate:
|
||||
|
||||
| Path | When to use |
|
||||
|------|-------------|
|
||||
| [Interactive TUI](#interactive-tui) | Fresh Arch system, guided dialogs |
|
||||
| [Answerfile (automated)](#answerfile-automated) | Unattended or repeatable installs |
|
||||
| [Custom ISO](#custom-live-iso) | Deploy from USB to multiple machines |
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Arch Linux (base install completed, user created)
|
||||
- Internet connection
|
||||
- `git` available (`sudo pacman -S git`)
|
||||
|
||||
---
|
||||
|
||||
## Interactive TUI
|
||||
|
||||
Clone the repo and run the installer:
|
||||
|
||||
```bash
|
||||
git clone https://git.abdelbaki.eu/The_miro/Dotfiles.git ~/Dotfiles
|
||||
bash ~/Dotfiles/setup/tui-install.sh
|
||||
```
|
||||
|
||||
The TUI walks you through:
|
||||
|
||||
1. **Hostname** — optional; sets `/etc/hostname` immediately
|
||||
2. **Components** — pick any combination:
|
||||
- `pkg` — package managers (yay, nvm, Rust)
|
||||
- `core` — 100+ base system packages
|
||||
- `svc` — core services (NetworkManager, cronie, fail2ban, greetd)
|
||||
- `shell` — zsh, Neovim, Yazi, Micro, Starship
|
||||
3. **Desktop Environment** — Hyprland, Sway, KDE Plasma, GNOME, COSMIC, XFCE, LXQt, or none
|
||||
4. **Applications** — checklist of ~50 optional apps (see [Modules](modules.md))
|
||||
5. **Colorway** — optional; enter hex values to customise the CyberQueer palette
|
||||
|
||||
All activity is logged to `~/dotfiles-install.log`.
|
||||
|
||||
### Adding Modules Later
|
||||
|
||||
To install additional optional apps on an already-configured system:
|
||||
|
||||
```bash
|
||||
bash ~/Dotfiles/setup/install-modules.sh
|
||||
```
|
||||
|
||||
This presents the same app checklist without re-running core setup.
|
||||
|
||||
---
|
||||
|
||||
## Answerfile (Automated)
|
||||
|
||||
An **answerfile** lets the entire install — base OS _and_ dotfiles — run without any user input.
|
||||
|
||||
### Generating an Answerfile
|
||||
|
||||
```bash
|
||||
bash ~/Dotfiles/setup/generate-answerfile.sh [OUTPUT_PATH]
|
||||
# Default output: ~/answerfile.json
|
||||
```
|
||||
|
||||
This dry-runs every installer dialog and saves your choices. **No software is installed.** Passwords are intentionally excluded — you will be prompted at install time.
|
||||
|
||||
### Answerfile Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"_generated": "2026-05-18T12:00:00+00:00",
|
||||
"drive": "/dev/sda",
|
||||
"kernel": "linux",
|
||||
"hostname": "myhost",
|
||||
"username": "amir",
|
||||
"encrypt": true,
|
||||
"fido2_root": false,
|
||||
"fido2_user": false,
|
||||
"run_tui": true,
|
||||
"components": ["pkg", "core", "svc", "shell"],
|
||||
"desktop_environment": "hyprland",
|
||||
"apps": ["firefox-browser", "vscodium", "docker"],
|
||||
"colors": {
|
||||
"COLOR_TEXT": "D6ABAB",
|
||||
"COLOR_BG": "1A1A1A",
|
||||
"COLOR_HIGHLIGHT": "E40046",
|
||||
"COLOR_DARK": "5018DD",
|
||||
"COLOR_RED": "F50505"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `drive` | string | Install target (`/dev/sda`, `/dev/nvme0n1`, …) |
|
||||
| `kernel` | string | `linux`, `linux-lts`, or `linux-zen` |
|
||||
| `hostname` | string | Base hostname — a MAC-address suffix is appended automatically |
|
||||
| `username` | string | Primary user account name |
|
||||
| `encrypt` | bool | Enable LUKS2 root encryption |
|
||||
| `fido2_root` | bool | Enroll FIDO2 key for LUKS unlock |
|
||||
| `fido2_user` | bool | Enroll FIDO2 key for PAM login |
|
||||
| `run_tui` | bool | Run dotfiles setup automatically after base install |
|
||||
| `components` | array | Dotfiles components to install |
|
||||
| `desktop_environment` | string | DE name or `"none"` |
|
||||
| `apps` | array | Optional app IDs (see [Modules](modules.md)) |
|
||||
| `colors` | object | Optional colour overrides (omit to keep defaults) |
|
||||
|
||||
### Hostname Uniqueness
|
||||
|
||||
When `hostname` is set in the answerfile, the MAC address of the primary network interface is automatically appended:
|
||||
|
||||
```
|
||||
myhost → myhost-aabbccddee11
|
||||
```
|
||||
|
||||
This prevents hostname conflicts when the same answerfile is used across multiple machines.
|
||||
|
||||
### Running with an Answerfile
|
||||
|
||||
Place the file at `/answerfile.json` (or set the `ANSWERFILE` environment variable):
|
||||
|
||||
```bash
|
||||
# Use default location
|
||||
sudo cp ~/answerfile.json /answerfile.json
|
||||
bash ~/Dotfiles/setup/tui-install.sh
|
||||
|
||||
# Or override the path
|
||||
ANSWERFILE=~/my-setup.json bash ~/Dotfiles/setup/tui-install.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Base OS Installers
|
||||
|
||||
Two scripts install Arch Linux itself (before the dotfiles step):
|
||||
|
||||
### Guided Installer (`archbaseos-guided-install.sh`)
|
||||
|
||||
Interactive, dialog-based. Prompts for each setting with sensible defaults. Good for hands-on installs where you want to review each option.
|
||||
|
||||
```bash
|
||||
bash ~/installer/archbaseos-guided-install.sh
|
||||
```
|
||||
|
||||
### Auto Installer (`arch-autoinstall.sh`)
|
||||
|
||||
Reads all settings from `/answerfile.json` if present; falls back to prompts for anything missing.
|
||||
|
||||
```bash
|
||||
bash ~/installer/arch-autoinstall.sh
|
||||
```
|
||||
|
||||
Both installers perform the same steps:
|
||||
|
||||
1. Partition disk (EFI 15 GiB · Root · Swap = RAM size)
|
||||
2. Optionally encrypt root with LUKS2
|
||||
3. Format root as Btrfs with `@` and `@home` subvolumes
|
||||
4. `pacstrap` base system
|
||||
5. Configure locale, timezone, hostname, user, sudo
|
||||
6. Set up mkinitcpio hooks and GRUB
|
||||
7. Optionally run `tui-install.sh` inside the chroot
|
||||
|
||||
### Disk Encryption
|
||||
|
||||
When encryption is enabled:
|
||||
|
||||
- **Primary key**: entered interactively at install time
|
||||
- **Backup key**: generated automatically from `/dev/urandom`, enrolled into a second LUKS slot, and written to `/_LUKS_BACKUP_KEY` inside the new system (mode `0400`, root-readable only, inside the encrypted container)
|
||||
- **FIDO2** (optional): enroll a hardware key for passwordless unlock
|
||||
|
||||
The backup key can be collected by Ansible — see [FreeIPA & Ansible](freeipa-ansible.md).
|
||||
|
||||
### mkinitcpio Hook Sets
|
||||
|
||||
| Scenario | Hooks |
|
||||
|----------|-------|
|
||||
| No encryption | `base udev autodetect microcode modconf kms consolefont block btrfs filesystems keyboard fsck` |
|
||||
| LUKS + password | `base udev autodetect microcode modconf kms consolefont block encrypt lvm2 btrfs filesystems keyboard keymap fsck` |
|
||||
| LUKS + FIDO2 | `base udev systemd autodetect microcode modconf kms consolefont block sd-encrypt lvm2 btrfs filesystems keyboard keymap fsck` |
|
||||
|
||||
---
|
||||
|
||||
## Custom Live ISO
|
||||
|
||||
See [Archiso](archiso.md) for building a bootable USB that embeds the installer and, optionally, a pre-baked answerfile for zero-touch deployment.
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
# Modules Reference
|
||||
|
||||
The setup system is modular — core components are installed first, then any combination of optional apps can be added. All module scripts are idempotent (safe to re-run).
|
||||
|
||||
---
|
||||
|
||||
## Core Modules
|
||||
|
||||
These are selected during the initial `tui-install.sh` run.
|
||||
|
||||
### `pkg` — Package Managers
|
||||
|
||||
Installs the AUR helper, language runtimes, and build toolchains:
|
||||
|
||||
- **yay** — AUR helper (built from source via `makepkg`)
|
||||
- **Rust / Cargo** — via `rustup` with the stable toolchain
|
||||
- **nvm** — Node Version Manager; installs Node.js v22 LTS by default
|
||||
|
||||
### `core` — Core Packages
|
||||
|
||||
~100 packages including:
|
||||
|
||||
`7zip` · `base-devel` · `bluez` · `bluez-utils` · `btop` · `fastfetch` · `fdupes` · `ffmpeg` · `git` · `greetd-tuigreet` · `htop` · `jq` · `less` · `lynx` · `neovim` · `networkmanager` · `openssh` · `pipewire` · `pipewire-alsa` · `pipewire-pulse` · `ripgrep` · `rsync` · `tmux` · `udiskie` · `yazi` · `zram-generator`
|
||||
|
||||
Also installs `pamtester` from the AUR.
|
||||
|
||||
### `svc` — Core Services
|
||||
|
||||
Enables and starts these systemd units:
|
||||
|
||||
| Service | Purpose |
|
||||
|---------|---------|
|
||||
| `NetworkManager` | Network connectivity |
|
||||
| `cronie` | Cron daemon |
|
||||
| `fail2ban` | Brute-force protection |
|
||||
| `greetd` | Login session manager |
|
||||
| `udisks2` | Removable media |
|
||||
|
||||
Also deploys `greetd-tuigreet` config from the dotfiles.
|
||||
|
||||
### `shell` — Shell Setup
|
||||
|
||||
- **zsh** with **Oh My Zsh** and plugins (zsh-syntax-highlighting, zsh-autosuggestions)
|
||||
- **Starship** shell prompt
|
||||
- **Neovim** with Vim-Plug (see [Editors](editors.md))
|
||||
- **Micro** editor
|
||||
- **Yazi** file manager
|
||||
- Deploys `.bashrc`, `.zshrc`, `starship.toml`, Micro config, Neovim config
|
||||
|
||||
---
|
||||
|
||||
## Desktop Environments
|
||||
|
||||
| ID | Name | Notes |
|
||||
|----|------|-------|
|
||||
| `hyprland` | Hyprland | Primary DE — see [Hyprland](hyprland.md) |
|
||||
| `sway` | Sway | Wayland tiling WM, lighter |
|
||||
| `kde-plasma` | KDE Plasma | Full-featured with sddm |
|
||||
| `gnome` | GNOME | Modern Wayland DE with gdm |
|
||||
| `cosmic` | COSMIC | Rust-based DE from System76 |
|
||||
| `xfce` | XFCE | Lightweight X11 with lightdm |
|
||||
| `lxqt` | LXQt | Lightweight Qt X11 with sddm |
|
||||
|
||||
---
|
||||
|
||||
## Optional Applications
|
||||
|
||||
Install via `tui-install.sh` at first install, or add later:
|
||||
|
||||
```bash
|
||||
bash ~/Dotfiles/setup/install-modules.sh
|
||||
```
|
||||
|
||||
### AI & Machine Learning
|
||||
|
||||
| ID | Package | Description |
|
||||
|----|---------|-------------|
|
||||
| `ollama` | ollama | Local LLM runner with REST API server |
|
||||
| `llama-cpp` | llama.cpp | Standalone inference CLI + server |
|
||||
| `open-webui` | open-webui | Browser UI for Ollama / OpenAI-compatible backends |
|
||||
| `claude` | claude (npm) | Anthropic Claude Code CLI |
|
||||
|
||||
### Networking & Security
|
||||
|
||||
| ID | Packages | Description |
|
||||
|----|---------|-------------|
|
||||
| `networking-cli` | nmap · nethogs · mitmproxy · httpie | Network analysis and HTTP tooling |
|
||||
| `disk-recovery` | ddrescue · f3 | Disk imaging and flash drive testing |
|
||||
| `ssh-server` | openssh | SSH daemon with key-auth enforcement |
|
||||
| `wireshark` | wireshark-qt | Packet capture and analysis GUI |
|
||||
|
||||
### Development
|
||||
|
||||
| ID | Packages | Description |
|
||||
|----|---------|-------------|
|
||||
| `python` | pyright · pipx · pynvim | Python LSP, isolated tool runner, Neovim integration |
|
||||
| `docker` | docker · docker-compose | Container runtime |
|
||||
| `podman` | podman · buildah · podman-compose | Rootless containers |
|
||||
| `cockpit` | cockpit · machines · podman | Web-based system management UI |
|
||||
| `k8s` | kubectl · podman-desktop | Kubernetes CLI and desktop client |
|
||||
| `db-clients` | pgcli · mycli | Enhanced interactive database CLIs |
|
||||
| `mysql` | mariadb | MariaDB server with initial setup |
|
||||
|
||||
### IDEs & Editors
|
||||
|
||||
| ID | Package | Description |
|
||||
|----|---------|-------------|
|
||||
| `vscodium` | vscodium-bin (AUR) | VS Code without telemetry |
|
||||
| `zed-ide` | zed | High-performance Rust IDE |
|
||||
| `geany` | geany · geany-plugins | Lightweight IDE |
|
||||
| `codeblocks` | codeblocks | C/C++ IDE |
|
||||
| `kate` | kate | KDE advanced text editor |
|
||||
|
||||
### Browsers
|
||||
|
||||
| ID | Package | Description |
|
||||
|----|---------|-------------|
|
||||
| `chromium` | chromium | Open-source Chromium |
|
||||
| `firefox-browser` | firefox | Mozilla Firefox |
|
||||
| `zen-browser` | zen-browser-bin (AUR) | Privacy-focused Firefox fork |
|
||||
| `nyxt` | nyxt (AUR) | Keyboard-driven, hackable browser |
|
||||
| `librewolf` | librewolf-bin (AUR) | Hardened Firefox fork |
|
||||
| `min-browser` | min (AUR) | Minimal Electron browser |
|
||||
|
||||
### Gaming
|
||||
|
||||
| ID | Package | Description |
|
||||
|----|---------|-------------|
|
||||
| `steam` | steam | Steam gaming platform |
|
||||
| `vesktop` | vesktop (AUR) | Discord client with Vencord built-in |
|
||||
| `spotify` | spotify (AUR) + spicetify | Music player with CyberQueer theme |
|
||||
| `prism` | prismlauncher (Flatpak) | Minecraft launcher |
|
||||
| `vintagestory` | vintagestory (AUR) | Survival / voxel game |
|
||||
|
||||
### Media & Creative
|
||||
|
||||
| ID | Packages | Description |
|
||||
|----|---------|-------------|
|
||||
| `ffmpeg` | gst-plugin-pipewire · gst-plugins-good · ffmpegthumbnailer | GStreamer codecs + thumbnailer |
|
||||
| `sox` | sox | Command-line audio processing |
|
||||
| `imagemagick` | imagemagick | Image manipulation suite |
|
||||
| `yt-dlp` | yt-dlp | YouTube / media downloader |
|
||||
| `blender` | blender | 3D creation suite |
|
||||
| `gnuplot` | gnuplot | Scientific plotting |
|
||||
| `povray` | povray | Ray-tracing renderer |
|
||||
|
||||
### Productivity
|
||||
|
||||
| ID | Packages | Description |
|
||||
|----|---------|-------------|
|
||||
| `productivity` | taskwarrior · watson · jrnl | Task management, time tracking, journaling |
|
||||
| `himalaya` | himalaya (AUR) | Terminal email client |
|
||||
| `toot` | toot (AUR) | Mastodon CLI client |
|
||||
|
||||
### System Utilities
|
||||
|
||||
| ID | Packages | Description |
|
||||
|----|---------|-------------|
|
||||
| `tlp` | tlp · tlp-rdw | Laptop battery optimisation |
|
||||
| `zfs` | zfs-dkms | ZFS kernel module |
|
||||
| `wprs` | wprs-git (AUR) | Wayland proxy for remote sessions |
|
||||
| `butter` | butter (AUR) | Btrfs snapshot backup manager |
|
||||
| `localsend` | localsend (AUR) | LAN file transfer (AirDrop-like) |
|
||||
| `croc` | croc | Cross-platform encrypted file transfer |
|
||||
| `localtunnel` | localtunnel (npm) | Expose localhost over a public URL |
|
||||
| `onlyoffice` | onlyoffice-bin (AUR) | Office suite (Docs, Sheets, Slides) |
|
||||
|
||||
### Identity & Infrastructure
|
||||
|
||||
| ID | Description |
|
||||
|----|-------------|
|
||||
| `freeipa-client` | sssd + ipa-client-install + auto-enrollment (see [FreeIPA](freeipa-ansible.md)) |
|
||||
| `freeipa-server` | Interactive FreeIPA server setup + client generator |
|
||||
| `freeipa-image` | OCI / LXC / Proxmox LXC image builder + Keycloak |
|
||||
|
||||
---
|
||||
|
||||
## Container Shell Setups
|
||||
|
||||
Scripts in `setup/Setup-shell-4-containers/` configure a minimal shell environment inside containers or chroots for each major distribution:
|
||||
|
||||
| Script | Target |
|
||||
|--------|--------|
|
||||
| `alpine.sh` | Alpine Linux |
|
||||
| `arch.sh` | Arch Linux |
|
||||
| `debian.sh` | Debian |
|
||||
| `fedora.sh` | Fedora |
|
||||
| `suse.sh` | openSUSE |
|
||||
| `ubuntu.sh` | Ubuntu |
|
||||
| `void.sh` | Void Linux |
|
||||
| `other.sh` | Generic fallback |
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
# CyberQueer Theme System
|
||||
|
||||
The CyberQueer theme is a single-source colour system: every config file that needs colours references a small set of hex values that can be changed in one place and propagated everywhere with a single command.
|
||||
|
||||
---
|
||||
|
||||
## The Palette
|
||||
|
||||
Defined in `~/Dotfiles/colors.conf` (bare 6-digit hex, no `#` prefix):
|
||||
|
||||
```ini
|
||||
COLOR_TEXT=D6ABAB # Rose-white — foreground text, labels
|
||||
COLOR_BG=1A1A1A # Near-black — base surface, backgrounds
|
||||
COLOR_HIGHLIGHT=E40046 # Hot pink — primary accent, active borders
|
||||
COLOR_DARK=5018DD # Violet — secondary accent, inactive borders
|
||||
COLOR_RED=F50505 # Red — danger indicators, alerts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Applying the Theme
|
||||
|
||||
```bash
|
||||
# Apply using the default colors.conf
|
||||
bash ~/Dotfiles/apply-theme.sh
|
||||
|
||||
# Apply from a custom palette file
|
||||
bash ~/Dotfiles/apply-theme.sh /path/to/custom-colors.conf
|
||||
```
|
||||
|
||||
`apply-theme.sh` will:
|
||||
|
||||
1. Read `colors.conf` (or the file you pass)
|
||||
2. Compare against the last-applied state in `~/.config/colors.state`
|
||||
3. Replace only **changed** colour values across all tracked files
|
||||
4. Save the new state to `colors.state`
|
||||
|
||||
If nothing changed it exits immediately — safe to call repeatedly.
|
||||
|
||||
### First-Run Bootstrap
|
||||
|
||||
On a fresh install where configs have been copied but no state file exists yet, `apply-theme.sh` bootstraps `~/.config/colors.state` with the repository defaults so the diff works correctly from the start.
|
||||
|
||||
---
|
||||
|
||||
## What Gets Themed
|
||||
|
||||
### User Configs (`~/.config/…`)
|
||||
|
||||
| File | What it styles |
|
||||
|------|---------------|
|
||||
| `starship.toml` | Shell prompt segment colours |
|
||||
| `yazi/theme.toml` | File manager UI colours |
|
||||
| `hypr/hyprland.conf` | Active/inactive window border gradients |
|
||||
| `hypr/hyprtoolkit.conf` | Additional Hyprland colours |
|
||||
| `hypr/hyprlock.conf` | Lock screen colours |
|
||||
| `kitty/current-theme.conf` | Terminal colour palette |
|
||||
| `kitty/kitty.conf` | Terminal background & accents |
|
||||
| `kitty/themes/cyberqueer.conf` | Kitty colour scheme definition |
|
||||
| `waybar/style.css` | Top bar widget colours |
|
||||
| `wofi/style.css` | App launcher colours |
|
||||
| `walker/themes/cyberqueer.css` | Walker launcher theme |
|
||||
| `nwg-dock-hyprland/style.css` | Application dock |
|
||||
| `nwg-drawer/drawer.css` | Application drawer |
|
||||
| `nwg-panel/menu-start.css` | Panel start menu |
|
||||
| `vicinae/cyberqueer.toml` | Gesture launcher |
|
||||
| `scripts/onscreenkb.sh` | On-screen keyboard colours |
|
||||
| `spicetify/Themes/*/color.ini` | Spotify client theme (×2 variants) |
|
||||
| `ulauncher/user-themes/cyberqueer/manifest.json` | uLauncher theme |
|
||||
| `ulauncher/user-themes/cyberqueer/theme.css` | uLauncher CSS |
|
||||
| `ulauncher/user-themes/cyberqueer/generated.css` | uLauncher generated CSS |
|
||||
| `Vencord/themes/cyberqueer.theme.css` | Discord theme |
|
||||
| `Vencord/themes/system24/…/cyberqueer.theme.css` | Discord system24 variant |
|
||||
|
||||
### System Files (applied via `sudo`)
|
||||
|
||||
| File | What it styles |
|
||||
|------|---------------|
|
||||
| `/etc/ly/config.ini` | TUI login manager colours |
|
||||
| `/usr/share/themes/cyberqueer/gtk-3.0/gtk.css` | GTK 3 theme |
|
||||
| `/usr/share/themes/cyberqueer/gtk-4.0/gtk.css` | GTK 4 theme |
|
||||
|
||||
---
|
||||
|
||||
## Customising the Palette
|
||||
|
||||
Edit `~/Dotfiles/colors.conf`, then run `apply-theme.sh`:
|
||||
|
||||
```bash
|
||||
# Example: shift the accent to cyan
|
||||
nano ~/Dotfiles/colors.conf
|
||||
# → COLOR_HIGHLIGHT=00B4D8
|
||||
|
||||
bash ~/Dotfiles/apply-theme.sh
|
||||
```
|
||||
|
||||
The tui-install.sh installer also offers a colorway dialog as its final step:
|
||||
enter new hex values in the form; leave them unchanged to skip.
|
||||
|
||||
---
|
||||
|
||||
## How It Works Internally
|
||||
|
||||
`apply-theme.sh` reads two key–value files and computes the diff:
|
||||
|
||||
```
|
||||
~/.config/colors.state (old values — what's currently applied)
|
||||
colors.conf (new values — what you want)
|
||||
```
|
||||
|
||||
For each changed key it runs:
|
||||
```bash
|
||||
sed -i "s/${OLD_HEX}/${NEW_HEX}/gI" <file>
|
||||
```
|
||||
|
||||
The case-insensitive (`I`) flag matches uppercase hex codes that some apps emit. After all replacements succeed, `colors.state` is updated.
|
||||
|
||||
### Symlink Guard
|
||||
|
||||
`apply-theme.sh` refuses to run if any deployed config path resolves back into `~/Dotfiles/` via symlink. This prevents theme changes from being committed directly into the git repository. The new-style install (via `tui-install.sh`) **copies** configs instead of symlinking them, so this guard is normally never triggered.
|
||||
|
||||
---
|
||||
|
||||
## Answerfile Theming
|
||||
|
||||
If you generate an answerfile with `generate-answerfile.sh`, custom colours can be embedded in it:
|
||||
|
||||
```json
|
||||
{
|
||||
"colors": {
|
||||
"COLOR_TEXT": "D6ABAB",
|
||||
"COLOR_BG": "1A1A1A",
|
||||
"COLOR_HIGHLIGHT": "E40046",
|
||||
"COLOR_DARK": "5018DD",
|
||||
"COLOR_RED": "F50505"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`tui-install.sh` will apply these at the end of an automated install.
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
# Utilities
|
||||
|
||||
Miscellaneous scripts and tools that live at the top level or in the `clamav/` directory.
|
||||
|
||||
---
|
||||
|
||||
## System Update
|
||||
|
||||
```bash
|
||||
bash ~/Dotfiles/update.sh
|
||||
```
|
||||
|
||||
Runs a full system update:
|
||||
1. `sudo pacman -Syu` — official repos
|
||||
2. `yay -Syu --answerdiff None --answerclean All --removemake` — AUR packages (no prompts)
|
||||
|
||||
For per-package AUR updates with confirmation:
|
||||
```bash
|
||||
bash ~/Dotfiles/update-aur-onebyone.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Package Audit
|
||||
|
||||
```bash
|
||||
bash ~/Dotfiles/setup/audit-packages.sh
|
||||
```
|
||||
|
||||
Audits installed packages — useful for finding orphans or unexpected installations.
|
||||
|
||||
---
|
||||
|
||||
## Encryption Utilities
|
||||
|
||||
Simple OpenSSL wrappers for encrypting/decrypting arbitrary strings. Useful for storing secrets in scripts or config files without plaintext exposure.
|
||||
|
||||
### Encrypt
|
||||
|
||||
```bash
|
||||
bash ~/Dotfiles/encrypt.sh "my secret text" "my-passphrase"
|
||||
# Output: base64-encoded AES-256-CBC ciphertext
|
||||
```
|
||||
|
||||
### Decrypt
|
||||
|
||||
```bash
|
||||
bash ~/Dotfiles/decrypt.sh "<ciphertext>" "my-passphrase"
|
||||
# Output: original plaintext
|
||||
```
|
||||
|
||||
Both use AES-256-CBC with PBKDF2 key derivation via OpenSSL.
|
||||
|
||||
---
|
||||
|
||||
## Credential Storage
|
||||
|
||||
### Initial Setup
|
||||
|
||||
```bash
|
||||
bash ~/Dotfiles/setup-creds-missing.sh
|
||||
```
|
||||
|
||||
Installs `gnome-keyring` and `seahorse` (GUI manager), then sets git's credential helper to `store`.
|
||||
|
||||
### Git Credentials
|
||||
|
||||
`git/` contains `.gitconfig` with:
|
||||
|
||||
```ini
|
||||
[user]
|
||||
name = The_miro
|
||||
email = amir@abdelbaki.eu
|
||||
|
||||
[credential]
|
||||
helper = store
|
||||
|
||||
[init]
|
||||
defaultBranch = main
|
||||
|
||||
[push]
|
||||
autoSetupRemote = true
|
||||
```
|
||||
|
||||
The `store` helper writes credentials to `~/.git-credentials`. For higher security, `gnome-keyring` intercepts this and stores the credentials in the system keyring instead of plaintext.
|
||||
|
||||
---
|
||||
|
||||
## Zsh Plugins
|
||||
|
||||
```bash
|
||||
bash ~/Dotfiles/zshplugins.sh
|
||||
```
|
||||
|
||||
Clones (or updates) the two Oh My Zsh community plugins:
|
||||
- `zsh-syntax-highlighting` — real-time syntax colouring in the prompt
|
||||
- `zsh-autosuggestions` — fish-style history-based suggestions
|
||||
|
||||
These are referenced in `.zshrc` and active after the next shell start.
|
||||
|
||||
---
|
||||
|
||||
## ClamAV On-Access Scanning
|
||||
|
||||
Full real-time antivirus scanning via ClamAV's `clamonacc` daemon.
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
bash ~/Dotfiles/clamav/install-clam-onaccess.sh
|
||||
```
|
||||
|
||||
What it does:
|
||||
1. Installs `clamav`
|
||||
2. Copies `clamd.conf` to `/etc/clamav/`
|
||||
3. Installs `clamav-clamonacc.service` to `/etc/systemd/system/`
|
||||
4. Installs the sudoers entry from `clamav-sudoer`
|
||||
5. Updates virus definitions (`freshclam`)
|
||||
6. Enables and starts `clamd` + `clamav-clamonacc`
|
||||
|
||||
### Key Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `clamav/clamd.conf` | Daemon configuration (30 KB, full options) |
|
||||
| `clamav/clamav-clamonacc.service` | systemd unit for on-access scanning |
|
||||
| `clamav/clamav-sudoer` | sudoers rule for ClamAV processes |
|
||||
| `clamav/virus-event.bash` | Handler executed when a virus is detected |
|
||||
|
||||
### Virus Event Handler
|
||||
|
||||
`virus-event.bash` is called by clamonacc when a threat is found. Customise it to send notifications, quarantine files, or alert an admin.
|
||||
|
||||
---
|
||||
|
||||
## Shell Configuration
|
||||
|
||||
### `.zshrc`
|
||||
|
||||
- **Framework**: Oh My Zsh
|
||||
- **Theme**: robbyrussell (overridden visually by Starship)
|
||||
- **Plugins**: syntax-highlighting, autosuggestions
|
||||
- **Walk integration**: `lk` function opens the `walk` file navigator
|
||||
- **`WALK_MAIN_COLOR`**: set to `#5018DD` (CyberQueer violet)
|
||||
- Sources Starship init at the end
|
||||
|
||||
### `.bashrc`
|
||||
|
||||
Minimal bash config — sets `PS1`, loads `~/.bash_profile` if present.
|
||||
|
||||
### Starship Prompt
|
||||
|
||||
`starship.toml` at the repo root is deployed to `~/.config/starship.toml`.
|
||||
|
||||
Key customisations:
|
||||
- OS, username, directory, git, language, docker, and time segments
|
||||
- CyberQueer colours throughout (colour-substitution target)
|
||||
- Directory abbreviated to 3 levels with `…/` truncation
|
||||
- Common directory substitutions (`~/Documents` → `📄`, etc.)
|
||||
|
||||
---
|
||||
|
||||
## Login Manager (ly)
|
||||
|
||||
`etc-ly-config.ini` is deployed to `/etc/ly/config.ini` during Hyprland install and kept as a colour-substitution target in `apply-theme.sh`.
|
||||
|
||||
ly is a minimal TUI display manager that runs on `tty1`:
|
||||
|
||||
```
|
||||
systemctl enable ly@tty1
|
||||
```
|
||||
|
||||
Session selection, auto-login, and timeout settings are all in the config.
|
||||
|
|
@ -1,11 +1,18 @@
|
|||
#!/usr/bin/env bash
|
||||
# arch-autoinstall.sh — automated Arch Linux base installer
|
||||
#
|
||||
# If /answerfile.json exists (e.g. embedded in the ISO via build.sh --preconf),
|
||||
# all prompts are answered from it. Missing fields fall back to interactive prompts.
|
||||
#
|
||||
# Answerfile fields: drive, kernel, hostname, username, encrypt, fido2_root,
|
||||
# fido2_user, run_tui (password always prompted interactively)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
############################################
|
||||
# LOGGING
|
||||
############################################
|
||||
LOGFILE="$HOME/arch-autoinstall.log"
|
||||
|
||||
{
|
||||
echo
|
||||
echo "############################################"
|
||||
|
|
@ -13,16 +20,53 @@ LOGFILE="$HOME/arch-autoinstall.log"
|
|||
echo "############################################"
|
||||
echo
|
||||
} >> "$LOGFILE"
|
||||
|
||||
exec > >(tee -a "$LOGFILE") 2>&1
|
||||
|
||||
############################################
|
||||
# ANSWERFILE
|
||||
############################################
|
||||
ANSWERFILE="${ANSWERFILE:-/answerfile.json}"
|
||||
AF_MODE=false
|
||||
[[ -f "$ANSWERFILE" ]] && AF_MODE=true
|
||||
|
||||
af_get() {
|
||||
# af_get <jq-filter> [default]
|
||||
local val
|
||||
val=$(jq -r "${1} // empty" "$ANSWERFILE" 2>/dev/null || true)
|
||||
if [[ -z "$val" ]]; then printf '%s' "${2:-}"; else printf '%s' "$val"; fi
|
||||
}
|
||||
|
||||
af_bool() {
|
||||
# Returns YES or NO from a JSON boolean field
|
||||
local val; val=$(jq -r "${1} // false" "$ANSWERFILE" 2>/dev/null || true)
|
||||
[[ "$val" == "true" ]] && echo "YES" || echo "NO"
|
||||
}
|
||||
|
||||
get_mac_suffix() {
|
||||
local mac
|
||||
mac=$(ip link show 2>/dev/null \
|
||||
| awk '/^[0-9]+: [^l][^o]/{iface=1} iface && /link\/ether/{print $2; iface=0; exit}')
|
||||
printf '%s' "${mac//:/}"
|
||||
}
|
||||
|
||||
if $AF_MODE; then
|
||||
echo "Answerfile detected: $ANSWERFILE"
|
||||
# Ensure jq is available
|
||||
command -v jq &>/dev/null || pacman -Sy --noconfirm jq
|
||||
fi
|
||||
|
||||
############################################
|
||||
# SAFETY WARNING
|
||||
############################################
|
||||
echo "WARNING: This will ERASE ALL DATA on the selected drive!"
|
||||
read -rp "Type 'YES' to continue: " confirm
|
||||
[[ "$confirm" == "YES" ]] || { echo "Aborted."; exit 1; }
|
||||
if $AF_MODE; then
|
||||
echo "WARNING: Automated install — all data on $(af_get '.drive' '/dev/?') will be ERASED."
|
||||
echo "Proceeding in 5 seconds... (Ctrl-C to abort)"
|
||||
sleep 5
|
||||
else
|
||||
echo "WARNING: This will ERASE ALL DATA on the selected drive!"
|
||||
read -rp "Type 'YES' to continue: " confirm
|
||||
[[ "$confirm" == "YES" ]] || { echo "Aborted."; exit 1; }
|
||||
fi
|
||||
|
||||
############################################
|
||||
# REQUIRED PACKAGES FOR INSTALL ENVIRONMENT
|
||||
|
|
@ -30,10 +74,57 @@ read -rp "Type 'YES' to continue: " confirm
|
|||
pacman -Sy --noconfirm parted cryptsetup libfido2 pam-u2f
|
||||
|
||||
############################################
|
||||
# DISK SELECTION
|
||||
# DRIVE SELECTION
|
||||
############################################
|
||||
lsblk
|
||||
read -rp "Enter target drive (e.g., /dev/sda): " DRIVE
|
||||
if $AF_MODE && [[ -n "$(af_get '.drive')" ]]; then
|
||||
DRIVE=$(af_get '.drive')
|
||||
echo "Drive (from answerfile): $DRIVE"
|
||||
else
|
||||
read -rp "Enter target drive (e.g., /dev/sda): " DRIVE
|
||||
fi
|
||||
|
||||
############################################
|
||||
# USER INPUT
|
||||
############################################
|
||||
if $AF_MODE; then
|
||||
KERNEL=$(af_get '.kernel' 'linux')
|
||||
RAW_HOSTNAME=$(af_get '.hostname' '')
|
||||
if [[ -n "$RAW_HOSTNAME" ]]; then
|
||||
HOSTNAME="${RAW_HOSTNAME}-$(get_mac_suffix)"
|
||||
else
|
||||
HOSTNAME="arch"
|
||||
fi
|
||||
USERNAME=$(af_get '.username' '')
|
||||
ENCRYPT_DISK=$(af_bool '.encrypt')
|
||||
FIDO_ROOT=$(af_bool '.fido2_root')
|
||||
FIDO_USER=$(af_bool '.fido2_user')
|
||||
RUN_TUI=$(af_bool '.run_tui')
|
||||
echo "Kernel: $KERNEL"
|
||||
echo "Hostname: $HOSTNAME"
|
||||
echo "Username: $USERNAME"
|
||||
echo "Encrypt: $ENCRYPT_DISK / FIDO2 root: $FIDO_ROOT / FIDO2 user: $FIDO_USER"
|
||||
else
|
||||
read -rp "Enter kernel package (e.g., linux, linux-lts): " KERNEL
|
||||
read -rp "Enter hostname: " HOSTNAME
|
||||
read -rp "Enter username: " USERNAME
|
||||
read -rp "Enable disk encryption? (YES/NO): " ENCRYPT_DISK
|
||||
FIDO_ROOT="NO"
|
||||
if [[ "$ENCRYPT_DISK" == "YES" ]]; then
|
||||
read -rp "Enable FIDO2 unlocking for root partition? (YES/NO): " FIDO_ROOT
|
||||
fi
|
||||
read -rp "Enable FIDO2 authentication for user login? (YES/NO): " FIDO_USER
|
||||
fi
|
||||
|
||||
# Password always prompted — never stored in answerfile
|
||||
read -rsp "Enter password for $USERNAME: " USERPASS; echo
|
||||
[[ -z "$USERPASS" ]] && { echo "Error: password cannot be empty."; exit 1; }
|
||||
|
||||
if ! $AF_MODE; then
|
||||
read -rp "Run dotfiles TUI setup inside chroot now? [YES/no]: " _RUN_TUI_IN
|
||||
_RUN_TUI_IN="${_RUN_TUI_IN:-YES}"
|
||||
[[ "${_RUN_TUI_IN^^}" == "YES" ]] && RUN_TUI="YES" || RUN_TUI="NO"
|
||||
fi
|
||||
|
||||
############################################
|
||||
# RAM / PARTITION SIZING
|
||||
|
|
@ -44,7 +135,6 @@ SWAP_SIZE="${RAM_GB}GiB"
|
|||
|
||||
DISK_SIZE=$(lsblk -b -dn -o SIZE "$DRIVE")
|
||||
DISK_GIB=$((DISK_SIZE / 1024 / 1024 / 1024))
|
||||
|
||||
ROOT_GIB=$((DISK_GIB - RAM_GB - 15))
|
||||
|
||||
echo "Partition plan:"
|
||||
|
|
@ -73,44 +163,74 @@ mkswap "$SWAP_PART"
|
|||
swapon "$SWAP_PART"
|
||||
|
||||
############################################
|
||||
# ASK ABOUT FIDO2 LUKS ENROLLMENT
|
||||
# ENCRYPTION (OPTIONAL)
|
||||
############################################
|
||||
read -rp "Enable FIDO2 unlocking for root partition? (YES/NO): " FIDO_ROOT
|
||||
LUKS_BACKUP_KEY="" # path to key file, set only when encryption is active
|
||||
|
||||
############################################
|
||||
# LUKS ENCRYPT ROOT
|
||||
############################################
|
||||
echo "Encrypting root partition..."
|
||||
cryptsetup -v luksFormat "$ROOT_PART"
|
||||
cryptsetup open "$ROOT_PART" cryptroot
|
||||
if [[ "$ENCRYPT_DISK" == "YES" ]]; then
|
||||
echo "Encrypting root partition..."
|
||||
cryptsetup -v luksFormat "$ROOT_PART"
|
||||
cryptsetup open "$ROOT_PART" cryptroot
|
||||
|
||||
############################################
|
||||
# OPTIONAL FIDO2 ENROLLMENT
|
||||
############################################
|
||||
if [[ "$FIDO_ROOT" == "YES" ]]; then
|
||||
echo "Insert FIDO2 key for LUKS and touch when prompted..."
|
||||
systemd-cryptenroll "$ROOT_PART" --fido2-device=auto --fido2-with-client-pin=no
|
||||
# ── Auto-generate backup LUKS key ──────────────────────────────────────────
|
||||
# A random key is enrolled as a second LUKS slot so recovery is possible
|
||||
# without the primary passphrase. It is written to /_LUKS_BACKUP_KEY in the
|
||||
# new system (inside the encrypted container) where only root can read it.
|
||||
LUKS_BACKUP_KEY=$(mktemp /tmp/luks-backup-key.XXXXXX)
|
||||
dd if=/dev/urandom bs=64 count=1 2>/dev/null | base64 -w0 > "$LUKS_BACKUP_KEY"
|
||||
echo "Enrolling auto-generated backup LUKS key..."
|
||||
cryptsetup luksAddKey "$ROOT_PART" "$LUKS_BACKUP_KEY"
|
||||
|
||||
# ── Optional FIDO2 enrollment ─────────────────────────────────────────────
|
||||
if [[ "$FIDO_ROOT" == "YES" ]]; then
|
||||
echo "Insert FIDO2 key for LUKS and touch when prompted..."
|
||||
systemd-cryptenroll "$ROOT_PART" --fido2-device=auto --fido2-with-client-pin=no
|
||||
fi
|
||||
|
||||
############################################
|
||||
# BTRFS ON ENCRYPTED ROOT
|
||||
############################################
|
||||
mkfs.btrfs /dev/mapper/cryptroot
|
||||
mount /dev/mapper/cryptroot /mnt
|
||||
btrfs subvolume create /mnt/@
|
||||
btrfs subvolume create /mnt/@home
|
||||
umount /mnt
|
||||
|
||||
mount -o subvol=@ /dev/mapper/cryptroot /mnt
|
||||
mkdir -p /mnt/home
|
||||
mount -o subvol=@home /dev/mapper/cryptroot /mnt/home
|
||||
|
||||
else
|
||||
echo "Skipping encryption — formatting root directly."
|
||||
|
||||
############################################
|
||||
# BTRFS ON UNENCRYPTED ROOT
|
||||
############################################
|
||||
mkfs.btrfs "$ROOT_PART"
|
||||
mount "$ROOT_PART" /mnt
|
||||
btrfs subvolume create /mnt/@
|
||||
btrfs subvolume create /mnt/@home
|
||||
umount /mnt
|
||||
|
||||
mount -o subvol=@ "$ROOT_PART" /mnt
|
||||
mkdir -p /mnt/home
|
||||
mount -o subvol=@home "$ROOT_PART" /mnt/home
|
||||
fi
|
||||
|
||||
############################################
|
||||
# BTRFS SUBVOLUMES
|
||||
############################################
|
||||
mkfs.btrfs /dev/mapper/cryptroot
|
||||
mount /dev/mapper/cryptroot /mnt
|
||||
btrfs subvolume create /mnt/@
|
||||
btrfs subvolume create /mnt/@home
|
||||
umount /mnt
|
||||
|
||||
mount -o subvol=@ /dev/mapper/cryptroot /mnt
|
||||
mkdir /mnt/home
|
||||
mount -o subvol=@home /dev/mapper/cryptroot /mnt/home
|
||||
mkdir /mnt/boot
|
||||
mkdir -p /mnt/boot
|
||||
mount "$BOOT_PART" /mnt/boot
|
||||
|
||||
# Place backup key inside the new system (only accessible when disk is unlocked)
|
||||
if [[ -n "$LUKS_BACKUP_KEY" ]]; then
|
||||
install -m 400 "$LUKS_BACKUP_KEY" /mnt/_LUKS_BACKUP_KEY
|
||||
rm -f "$LUKS_BACKUP_KEY"
|
||||
echo "Backup LUKS key written to /_LUKS_BACKUP_KEY in new system."
|
||||
fi
|
||||
|
||||
############################################
|
||||
# GPU DETECTION
|
||||
############################################
|
||||
GPU_INFO=$(lspci | grep -E "VGA|3D")
|
||||
GPU_INFO=$(lspci | grep -E "VGA|3D" || true)
|
||||
GPU_PKGS=""
|
||||
if echo "$GPU_INFO" | grep -qi "NVIDIA"; then
|
||||
GPU_PKGS="nvidia nvidia-utils"
|
||||
|
|
@ -119,39 +239,36 @@ elif echo "$GPU_INFO" | grep -qi "AMD"; then
|
|||
elif echo "$GPU_INFO" | grep -qi "Intel"; then
|
||||
GPU_PKGS="xf86-video-intel"
|
||||
fi
|
||||
|
||||
echo "Detected GPU: $GPU_INFO"
|
||||
|
||||
############################################
|
||||
# USER INPUT
|
||||
############################################
|
||||
read -rp "Enter kernel package (e.g., linux, linux-lts): " KERNEL
|
||||
read -rp "Enter hostname: " HOSTNAME
|
||||
read -rp "Enter username: " USERNAME
|
||||
read -rsp "Enter password for $USERNAME: " USERPASS
|
||||
echo
|
||||
read -rp "Enable FIDO2 authentication for user login? (YES/NO): " FIDO_USER
|
||||
echo "Detected GPU: ${GPU_INFO:-none}"
|
||||
|
||||
############################################
|
||||
# BASE INSTALL
|
||||
############################################
|
||||
# shellcheck disable=SC2086
|
||||
pacstrap -K /mnt base base-devel "$KERNEL" linux-firmware vim bash zsh git less btop fastfetch \
|
||||
networkmanager grub cryptsetup libfido2 pam-u2f efibootmgr sudo btrfs-progs lvm2 $GPU_PKGS
|
||||
networkmanager grub cryptsetup libfido2 pam-u2f efibootmgr sudo btrfs-progs lvm2 jq $GPU_PKGS
|
||||
|
||||
############################################
|
||||
# FSTAB
|
||||
############################################
|
||||
genfstab -U /mnt >> /mnt/etc/fstab
|
||||
|
||||
############################################
|
||||
# COPY ANSWERFILE INTO NEW SYSTEM
|
||||
############################################
|
||||
if $AF_MODE; then
|
||||
install -m 644 "$ANSWERFILE" /mnt/answerfile.json
|
||||
fi
|
||||
|
||||
############################################
|
||||
# PASS VARIABLES INTO CHROOT
|
||||
############################################
|
||||
export HOSTNAME USERNAME USERPASS ROOT_PART FIDO_ROOT FIDO_USER
|
||||
export HOSTNAME USERNAME USERPASS ROOT_PART KERNEL FIDO_ROOT FIDO_USER ENCRYPT_DISK
|
||||
|
||||
############################################
|
||||
# CHROOT CONFIGURATION
|
||||
############################################
|
||||
arch-chroot /mnt /bin/bash <<'EOF'
|
||||
arch-chroot /mnt /bin/bash <<'CHROOT_EOF'
|
||||
set -euo pipefail
|
||||
|
||||
# Locale
|
||||
|
|
@ -175,10 +292,12 @@ echo "%wheel ALL=(ALL:ALL) ALL" >> /etc/sudoers
|
|||
###################################################
|
||||
# INITRAMFS CONFIG
|
||||
###################################################
|
||||
if [[ "$FIDO_ROOT" == "YES" ]]; then
|
||||
sed -i 's/^HOOKS=.*/HOOKS=(base btrfs udev systemd microcode kms autodetect modconf consolefont block sd-encrypt encrypt lvm2 filesystems keyboard keymap fsck)/' /etc/mkinitcpio.conf
|
||||
if [[ "$ENCRYPT_DISK" == "YES" && "$FIDO_ROOT" == "YES" ]]; then
|
||||
sed -i 's/^HOOKS=.*/HOOKS=(base udev systemd autodetect microcode modconf kms consolefont block sd-encrypt lvm2 btrfs filesystems keyboard keymap fsck)/' /etc/mkinitcpio.conf
|
||||
elif [[ "$ENCRYPT_DISK" == "YES" ]]; then
|
||||
sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect microcode modconf kms consolefont block encrypt lvm2 btrfs filesystems keyboard keymap fsck)/' /etc/mkinitcpio.conf
|
||||
else
|
||||
sed -i 's/^HOOKS=.*/HOOKS=(base btrfs udev systemd microcode kms autodetect modconf consolefont block encrypt encrypt lvm2 filesystems keyboard keymap fsck)/' /etc/mkinitcpio.conf
|
||||
sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect microcode modconf kms consolefont block btrfs filesystems keyboard fsck)/' /etc/mkinitcpio.conf
|
||||
fi
|
||||
|
||||
mkinitcpio -p "$KERNEL"
|
||||
|
|
@ -187,49 +306,46 @@ mkinitcpio -p "$KERNEL"
|
|||
# GRUB CONFIG
|
||||
###################################################
|
||||
UUID=$(blkid -s UUID -o value "$ROOT_PART")
|
||||
|
||||
if [[ "$FIDO_ROOT" == "YES" ]]; then
|
||||
KERNEL_CMD="rd.luks.name=${UUID}=cryptroot root=/dev/mapper/cryptroot"
|
||||
if [[ "$ENCRYPT_DISK" == "YES" ]]; then
|
||||
if [[ "$FIDO_ROOT" == "YES" ]]; then
|
||||
KERNEL_CMD="rd.luks.name=${UUID}=cryptroot root=/dev/mapper/cryptroot"
|
||||
else
|
||||
KERNEL_CMD="cryptdevice=UUID=${UUID}:cryptroot root=/dev/mapper/cryptroot"
|
||||
fi
|
||||
else
|
||||
KERNEL_CMD="cryptdevice=UUID=${UUID}:cryptroot root=/dev/mapper/cryptroot"
|
||||
KERNEL_CMD="root=UUID=${UUID} rootflags=subvol=@"
|
||||
fi
|
||||
|
||||
sed -i "s|^GRUB_CMDLINE_LINUX=.*|GRUB_CMDLINE_LINUX=\"$KERNEL_CMD\"|" /etc/default/grub
|
||||
|
||||
grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=M-Archy-GRUB-CuIn
|
||||
grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=M-Archy-GRUB
|
||||
grub-mkconfig -o /boot/grub/grub.cfg
|
||||
|
||||
###################################################
|
||||
# USER FIDO2 LOGIN
|
||||
###################################################
|
||||
if [[ "$FIDO_USER" == "YES" ]]; then
|
||||
mkdir -p /home/$USERNAME/.config/Yubico
|
||||
mkdir -p "/home/$USERNAME/.config/Yubico"
|
||||
echo "Insert FIDO2 key for user login and touch when prompted..."
|
||||
sudo -u "$USERNAME" pamu2fcfg -u "$USERNAME" > /home/$USERNAME/.config/Yubico/u2f_keys
|
||||
chown "$USERNAME":"$USERNAME" /home/$USERNAME/.config/Yubico/u2f_keys
|
||||
|
||||
sudo -u "$USERNAME" pamu2fcfg -u "$USERNAME" > "/home/$USERNAME/.config/Yubico/u2f_keys"
|
||||
chown "$USERNAME":"$USERNAME" "/home/$USERNAME/.config/Yubico/u2f_keys"
|
||||
echo "auth required pam_u2f.so" >> /etc/pam.d/system-local-login
|
||||
fi
|
||||
|
||||
|
||||
###################################################
|
||||
# CLONE DOTFILES FOR POST-INSTALL SETUP
|
||||
# CLONE DOTFILES
|
||||
###################################################
|
||||
echo "Cloning dotfiles..."
|
||||
git clone https://git.abdelbaki.eu/The_miro/Dotfiles.git /home/$USERNAME/Dotfiles \
|
||||
&& chown -R $USERNAME:$USERNAME /home/$USERNAME/Dotfiles \
|
||||
git clone https://git.abdelbaki.eu/The_miro/Dotfiles.git "/home/$USERNAME/Dotfiles" \
|
||||
&& chown -R "$USERNAME":"$USERNAME" "/home/$USERNAME/Dotfiles" \
|
||||
|| echo "Warning: dotfiles clone failed — clone manually after first boot."
|
||||
|
||||
EOF
|
||||
CHROOT_EOF
|
||||
|
||||
############################################
|
||||
# DOTFILES SETUP (in-chroot, optional)
|
||||
# DOTFILES TUI SETUP (in-chroot, optional)
|
||||
############################################
|
||||
echo
|
||||
read -rp "Run dotfiles TUI setup inside chroot now? [YES/no]: " _RUN_TUI
|
||||
_RUN_TUI="${_RUN_TUI:-YES}"
|
||||
|
||||
if [[ "${_RUN_TUI^^}" == "YES" ]]; then
|
||||
if [[ "${RUN_TUI^^}" == "YES" ]]; then
|
||||
echo "${USERNAME} ALL=(ALL) NOPASSWD: ALL" \
|
||||
| arch-chroot /mnt tee /etc/sudoers.d/99-setup-nopasswd > /dev/null
|
||||
|
||||
|
|
@ -241,43 +357,47 @@ if [[ "${_RUN_TUI^^}" == "YES" ]]; then
|
|||
arch-chroot /mnt rm -f /etc/sudoers.d/99-setup-nopasswd
|
||||
fi
|
||||
|
||||
# Remove answerfile from new system after setup is complete (contains sensitive paths/config)
|
||||
if $AF_MODE && [[ -f /mnt/answerfile.json ]]; then
|
||||
rm -f /mnt/answerfile.json
|
||||
fi
|
||||
|
||||
############################################
|
||||
# SUMMARY OUTPUT (NEW)
|
||||
# SUMMARY
|
||||
############################################
|
||||
echo
|
||||
echo "############################################"
|
||||
echo " INSTALL SUMMARY"
|
||||
echo "############################################"
|
||||
echo "Drive: $DRIVE"
|
||||
echo "Boot partition: $BOOT_PART"
|
||||
echo "Root partition: $ROOT_PART"
|
||||
echo "Swap partition: $SWAP_PART"
|
||||
echo "Drive: $DRIVE"
|
||||
echo "Boot partition: $BOOT_PART"
|
||||
echo "Root partition: $ROOT_PART"
|
||||
echo "Swap partition: $SWAP_PART"
|
||||
echo
|
||||
echo "Hostname: $HOSTNAME"
|
||||
echo "Username: $USERNAME"
|
||||
echo "Kernel: $KERNEL"
|
||||
echo "GPU detected: $GPU_INFO"
|
||||
echo "Hostname: $HOSTNAME"
|
||||
echo "Username: $USERNAME"
|
||||
echo "Kernel: $KERNEL"
|
||||
echo "GPU detected: ${GPU_INFO:-none}"
|
||||
echo
|
||||
echo "Disk encryption: $ENCRYPT_DISK"
|
||||
echo "FIDO2 root unlock: $FIDO_ROOT"
|
||||
echo "FIDO2 user login: $FIDO_USER"
|
||||
[[ "$ENCRYPT_DISK" == "YES" ]] && echo "LUKS backup key: /_LUKS_BACKUP_KEY (in new system)"
|
||||
echo
|
||||
echo "Boot size: $BOOT_SIZE"
|
||||
echo "Root size: ${ROOT_GIB}GiB"
|
||||
echo "Swap size: $SWAP_SIZE"
|
||||
echo
|
||||
echo "Log file saved to: $LOGFILE"
|
||||
echo "Log file: $LOGFILE"
|
||||
echo "############################################"
|
||||
echo
|
||||
|
||||
cp $LOGFILE /mnt/boot/
|
||||
cp "$LOGFILE" /mnt/boot/ 2>/dev/null || true
|
||||
|
||||
############################################
|
||||
# DONE
|
||||
############################################
|
||||
echo "Installation complete! You can now unmount and reboot."
|
||||
echo
|
||||
if [[ "${_RUN_TUI^^}" != "YES" ]]; then
|
||||
echo "Installation complete! Unmount and reboot:"
|
||||
echo " umount -R /mnt && reboot"
|
||||
if [[ "${RUN_TUI^^}" != "YES" ]]; then
|
||||
echo
|
||||
echo "After first boot, login as $USERNAME and run:"
|
||||
echo " ~/Dotfiles/setup/tui-install.sh"
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
# archbaseos-guided-install.sh — guided (dialog-based) Arch Linux base installer
|
||||
#
|
||||
# If /answerfile.json exists (e.g. embedded via build.sh --preconf), all prompts
|
||||
# are answered from it. Missing fields fall back to interactive prompts.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
############################################
|
||||
# Helper Functions
|
||||
# Helper Functions
|
||||
############################################
|
||||
|
||||
confirm() {
|
||||
|
|
@ -23,20 +28,57 @@ pause() {
|
|||
}
|
||||
|
||||
############################################
|
||||
# Begin
|
||||
# ANSWERFILE
|
||||
############################################
|
||||
ANSWERFILE="${ANSWERFILE:-/answerfile.json}"
|
||||
AF_MODE=false
|
||||
[[ -f "$ANSWERFILE" ]] && AF_MODE=true
|
||||
|
||||
echo "== Arch Linux FIDO2-Ready Installer =="
|
||||
af_get() {
|
||||
local val
|
||||
val=$(jq -r "${1} // empty" "$ANSWERFILE" 2>/dev/null || true)
|
||||
if [[ -z "$val" ]]; then printf '%s' "${2:-}"; else printf '%s' "$val"; fi
|
||||
}
|
||||
|
||||
af_bool() {
|
||||
local val; val=$(jq -r "${1} // false" "$ANSWERFILE" 2>/dev/null || true)
|
||||
[[ "$val" == "true" ]] && echo "YES" || echo "NO"
|
||||
}
|
||||
|
||||
get_mac_suffix() {
|
||||
local mac
|
||||
mac=$(ip link show 2>/dev/null \
|
||||
| awk '/^[0-9]+: [^l][^o]/{iface=1} iface && /link\/ether/{print $2; iface=0; exit}')
|
||||
printf '%s' "${mac//:/}"
|
||||
}
|
||||
|
||||
if $AF_MODE; then
|
||||
echo "== Arch Linux Guided Installer (answerfile mode) =="
|
||||
command -v jq &>/dev/null || pacman -Sy --noconfirm jq
|
||||
else
|
||||
echo "== Arch Linux FIDO2-Ready Installer =="
|
||||
fi
|
||||
|
||||
############################################
|
||||
# Begin
|
||||
############################################
|
||||
lsblk
|
||||
DRIVE=$(ask "Enter install drive (e.g., /dev/sda)")
|
||||
confirm "$DRIVE" || exit 1
|
||||
|
||||
if $AF_MODE && [[ -n "$(af_get '.drive')" ]]; then
|
||||
DRIVE=$(af_get '.drive')
|
||||
echo "Drive (from answerfile): $DRIVE"
|
||||
echo "WARNING: All data on $DRIVE will be erased. Proceeding in 5 seconds..."
|
||||
sleep 5
|
||||
else
|
||||
DRIVE=$(ask "Enter install drive (e.g., /dev/sda)")
|
||||
confirm "$DRIVE" || exit 1
|
||||
fi
|
||||
|
||||
# Required packages
|
||||
pacman -Syd --noconfirm parted cryptsetup libfido2 pam-u2f systemd-ukify
|
||||
pacman -Syd --noconfirm parted cryptsetup libfido2 pam-u2f systemd-ukify jq
|
||||
|
||||
############################################
|
||||
# Partitioning
|
||||
# Partitioning
|
||||
############################################
|
||||
|
||||
RAM_GB=$(free --giga | awk '/Mem/ {print $2}')
|
||||
|
|
@ -68,53 +110,101 @@ mkswap "$SWAP_PART"
|
|||
swapon "$SWAP_PART"
|
||||
|
||||
############################################
|
||||
# Encryption
|
||||
# User input
|
||||
############################################
|
||||
|
||||
echo
|
||||
read -rp "Enable FIDO2 for unlocking root? (YES/NO): " ENABLE_FIDO_ROOT
|
||||
|
||||
echo "Formatting LUKS2 root..."
|
||||
cryptsetup luksFormat "$ROOT_PART" --type luks2
|
||||
cryptsetup open "$ROOT_PART" cryptroot
|
||||
|
||||
if [[ $ENABLE_FIDO_ROOT == "YES" ]]; then
|
||||
echo "Enroll FIDO2 key for LUKS2"
|
||||
pause
|
||||
systemd-cryptenroll "$ROOT_PART" --fido2-device=auto --fido2-with-client-pin=no
|
||||
if $AF_MODE; then
|
||||
KERNEL=$(af_get '.kernel' 'linux')
|
||||
RAW_HOSTNAME=$(af_get '.hostname' '')
|
||||
if [[ -n "$RAW_HOSTNAME" ]]; then
|
||||
HOSTNAME="${RAW_HOSTNAME}-$(get_mac_suffix)"
|
||||
else
|
||||
HOSTNAME="arch"
|
||||
fi
|
||||
USERNAME=$(af_get '.username' '')
|
||||
ENCRYPT_DISK=$(af_bool '.encrypt')
|
||||
ENABLE_FIDO_ROOT=$(af_bool '.fido2_root')
|
||||
ENABLE_FIDO_USER=$(af_bool '.fido2_user')
|
||||
RUN_TUI=$(af_bool '.run_tui')
|
||||
echo "Kernel: $KERNEL / Hostname: $HOSTNAME / Username: $USERNAME"
|
||||
echo "Encrypt: $ENCRYPT_DISK / FIDO2 root: $ENABLE_FIDO_ROOT / FIDO2 user: $ENABLE_FIDO_USER"
|
||||
else
|
||||
KERNEL=$(ask "Kernel (linux, linux-lts, linux-zen)")
|
||||
HOSTNAME=$(ask "Hostname")
|
||||
USERNAME=$(ask "Username")
|
||||
read -rp "Enable disk encryption? (YES/NO): " ENCRYPT_DISK
|
||||
ENABLE_FIDO_ROOT="NO"
|
||||
if [[ "$ENCRYPT_DISK" == "YES" ]]; then
|
||||
read -rp "Enable FIDO2 for unlocking root? (YES/NO): " ENABLE_FIDO_ROOT
|
||||
fi
|
||||
read -rp "Enable FIDO2 for user login? (YES/NO): " ENABLE_FIDO_USER
|
||||
fi
|
||||
|
||||
# Add fallback password
|
||||
echo "Adding fallback LUKS password (recommended)"
|
||||
cryptsetup luksAddKey "$ROOT_PART"
|
||||
read -rsp "Password for $USERNAME: " USERPASS; echo
|
||||
[[ -z "$USERPASS" ]] && { echo "Error: password cannot be empty."; exit 1; }
|
||||
|
||||
############################################
|
||||
# Filesystem
|
||||
# Encryption (optional)
|
||||
############################################
|
||||
LUKS_BACKUP_KEY=""
|
||||
|
||||
mkfs.btrfs /dev/mapper/cryptroot
|
||||
if [[ "$ENCRYPT_DISK" == "YES" ]]; then
|
||||
echo "Formatting LUKS2 root..."
|
||||
cryptsetup luksFormat "$ROOT_PART" --type luks2
|
||||
cryptsetup open "$ROOT_PART" cryptroot
|
||||
|
||||
mount /dev/mapper/cryptroot /mnt
|
||||
btrfs subvolume create /mnt/@
|
||||
btrfs subvolume create /mnt/@home
|
||||
umount /mnt
|
||||
# ── Auto-generate backup LUKS key ─────────────────────────────────────────
|
||||
LUKS_BACKUP_KEY=$(mktemp /tmp/luks-backup-key.XXXXXX)
|
||||
dd if=/dev/urandom bs=64 count=1 2>/dev/null | base64 -w0 > "$LUKS_BACKUP_KEY"
|
||||
echo "Enrolling auto-generated backup LUKS key..."
|
||||
cryptsetup luksAddKey "$ROOT_PART" "$LUKS_BACKUP_KEY"
|
||||
|
||||
mount -o subvol=@ /dev/mapper/cryptroot /mnt
|
||||
mkdir -p /mnt/home
|
||||
mount -o subvol=@home /dev/mapper/cryptroot /mnt/home
|
||||
if [[ "$ENABLE_FIDO_ROOT" == "YES" ]]; then
|
||||
echo "Enroll FIDO2 key for LUKS2"
|
||||
pause
|
||||
systemd-cryptenroll "$ROOT_PART" --fido2-device=auto --fido2-with-client-pin=no
|
||||
fi
|
||||
|
||||
mkfs.btrfs /dev/mapper/cryptroot
|
||||
mount /dev/mapper/cryptroot /mnt
|
||||
btrfs subvolume create /mnt/@
|
||||
btrfs subvolume create /mnt/@home
|
||||
umount /mnt
|
||||
|
||||
mount -o subvol=@ /dev/mapper/cryptroot /mnt
|
||||
mkdir -p /mnt/home
|
||||
mount -o subvol=@home /dev/mapper/cryptroot /mnt/home
|
||||
|
||||
else
|
||||
echo "Skipping encryption — formatting root directly."
|
||||
|
||||
mkfs.btrfs "$ROOT_PART"
|
||||
mount "$ROOT_PART" /mnt
|
||||
btrfs subvolume create /mnt/@
|
||||
btrfs subvolume create /mnt/@home
|
||||
umount /mnt
|
||||
|
||||
mount -o subvol=@ "$ROOT_PART" /mnt
|
||||
mkdir -p /mnt/home
|
||||
mount -o subvol=@home "$ROOT_PART" /mnt/home
|
||||
fi
|
||||
|
||||
mkdir -p /mnt/boot
|
||||
mount "$EFI_PART" /mnt/boot
|
||||
|
||||
############################################
|
||||
# Base System Install
|
||||
############################################
|
||||
# Place backup key inside the new system (readable only by root, inside LUKS container)
|
||||
if [[ -n "$LUKS_BACKUP_KEY" ]]; then
|
||||
install -m 400 "$LUKS_BACKUP_KEY" /mnt/_LUKS_BACKUP_KEY
|
||||
rm -f "$LUKS_BACKUP_KEY"
|
||||
echo "Backup LUKS key written to /_LUKS_BACKUP_KEY in new system."
|
||||
fi
|
||||
|
||||
GPU_INFO=$(lspci | grep -E "VGA|3D")
|
||||
############################################
|
||||
# Base System Install
|
||||
############################################
|
||||
GPU_INFO=$(lspci | grep -E "VGA|3D" || true)
|
||||
GPU_PKGS=""
|
||||
|
||||
if echo "$GPU_INFO" | grep -qi nvidia; then
|
||||
#GPU_PKGS="nvidia nvidia-utils"
|
||||
GPU_PKGS="nvidia-open"
|
||||
elif echo "$GPU_INFO" | grep -qi amd; then
|
||||
GPU_PKGS="xf86-video-amdgpu"
|
||||
|
|
@ -122,34 +212,35 @@ elif echo "$GPU_INFO" | grep -qi intel; then
|
|||
GPU_PKGS="xf86-video-intel"
|
||||
fi
|
||||
|
||||
KERNEL=$(ask "Kernel (linux, linux-lts, linux-zen)")
|
||||
HOSTNAME=$(ask "Hostname")
|
||||
USERNAME=$(ask "Username")
|
||||
read -rsp "Password for $USERNAME: " USERPASS; echo
|
||||
|
||||
read -rp "Enable FIDO2 for user login? (YES/NO): " ENABLE_FIDO_USER
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
pacstrap /mnt \
|
||||
base base-devel "$KERNEL" linux-firmware vim zsh git networkmanager grub efibootmgr \
|
||||
btrfs-progs cryptsetup libfido2 pam-u2f sudo less "$GPU_PKGS"
|
||||
btrfs-progs cryptsetup libfido2 pam-u2f sudo less jq $GPU_PKGS
|
||||
|
||||
genfstab -U /mnt >> /mnt/etc/fstab
|
||||
|
||||
############################################
|
||||
# CHROOT Configuration
|
||||
# COPY ANSWERFILE INTO NEW SYSTEM
|
||||
############################################
|
||||
if $AF_MODE; then
|
||||
install -m 644 "$ANSWERFILE" /mnt/answerfile.json
|
||||
fi
|
||||
|
||||
############################################
|
||||
# CHROOT Configuration
|
||||
############################################
|
||||
ROOT_UUID=$(blkid -s UUID -o value "$ROOT_PART")
|
||||
|
||||
|
||||
arch-chroot /mnt /usr/bin/env \
|
||||
HOSTNAME="$HOSTNAME" \
|
||||
USERNAME="$USERNAME" \
|
||||
USERPASS="$USERPASS" \
|
||||
ENCRYPT_DISK="$ENCRYPT_DISK" \
|
||||
ENABLE_FIDO_ROOT="$ENABLE_FIDO_ROOT" \
|
||||
ENABLE_FIDO_USER="$ENABLE_FIDO_USER" \
|
||||
ROOT_UUID="$ROOT_UUID" \
|
||||
/bin/bash <<'EOF'
|
||||
ROOT_PART="$ROOT_PART" \
|
||||
/bin/bash <<'CHROOT_EOF'
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
|
|
@ -176,19 +267,26 @@ chown -R "$USERNAME:$USERNAME" "/home/$USERNAME"
|
|||
|
||||
echo "%wheel ALL=(ALL) ALL" >> /etc/sudoers
|
||||
|
||||
# Initramfs + GRUB for FIDO2
|
||||
if [[ "$ENABLE_FIDO_ROOT" == "YES" ]]; then
|
||||
sed -i 's/^HOOKS=.*/HOOKS=(base systemd autodetect modconf block sd-encrypt filesystems keyboard fsck)/' /etc/mkinitcpio.conf
|
||||
# Initramfs
|
||||
if [[ "$ENCRYPT_DISK" == "YES" && "$ENABLE_FIDO_ROOT" == "YES" ]]; then
|
||||
sed -i 's/^HOOKS=.*/HOOKS=(base udev systemd autodetect microcode modconf kms consolefont block sd-encrypt lvm2 btrfs filesystems keyboard keymap fsck)/' /etc/mkinitcpio.conf
|
||||
elif [[ "$ENCRYPT_DISK" == "YES" ]]; then
|
||||
sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect microcode modconf kms consolefont block encrypt lvm2 btrfs filesystems keyboard keymap fsck)/' /etc/mkinitcpio.conf
|
||||
else
|
||||
sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect modconf block encrypt filesystems keyboard fsck)/' /etc/mkinitcpio.conf
|
||||
sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect microcode modconf kms consolefont block btrfs filesystems keyboard fsck)/' /etc/mkinitcpio.conf
|
||||
fi
|
||||
|
||||
mkinitcpio -P
|
||||
|
||||
if [[ "$ENABLE_FIDO_ROOT" == "YES" ]]; then
|
||||
GRUB_CMDLINE="rd.luks.name=$ROOT_UUID=cryptroot rd.luks.options=fido2-device=auto root=/dev/mapper/cryptroot"
|
||||
# GRUB
|
||||
if [[ "$ENCRYPT_DISK" == "YES" ]]; then
|
||||
if [[ "$ENABLE_FIDO_ROOT" == "YES" ]]; then
|
||||
GRUB_CMDLINE="rd.luks.name=$ROOT_UUID=cryptroot rd.luks.options=fido2-device=auto root=/dev/mapper/cryptroot"
|
||||
else
|
||||
GRUB_CMDLINE="cryptdevice=UUID=$ROOT_UUID:cryptroot root=/dev/mapper/cryptroot"
|
||||
fi
|
||||
else
|
||||
GRUB_CMDLINE="cryptdevice=UUID=$ROOT_UUID:cryptroot root=/dev/mapper/cryptroot"
|
||||
GRUB_CMDLINE="root=UUID=${ROOT_UUID} rootflags=subvol=@"
|
||||
fi
|
||||
|
||||
sed -i "s|^GRUB_CMDLINE_LINUX=.*|GRUB_CMDLINE_LINUX=\"$GRUB_CMDLINE\"|" /etc/default/grub
|
||||
|
|
@ -199,23 +297,26 @@ grub-mkconfig -o /boot/grub/grub.cfg
|
|||
# User login FIDO2
|
||||
if [[ "$ENABLE_FIDO_USER" == "YES" ]]; then
|
||||
echo "Enrolling FIDO2 for user login"
|
||||
mkdir -p /home/$USERNAME/.config/Yubico
|
||||
chown $USERNAME:$USERNAME /home/$USERNAME/.config/Yubico
|
||||
mkdir -p "/home/$USERNAME/.config/Yubico"
|
||||
chown "$USERNAME:$USERNAME" "/home/$USERNAME/.config/Yubico"
|
||||
sudo -u "$USERNAME" bash -c "pamu2fcfg >> /home/$USERNAME/.config/Yubico/u2f_keys"
|
||||
echo "auth required pam_u2f.so" >> /etc/pam.d/system-auth
|
||||
fi
|
||||
|
||||
|
||||
EOF
|
||||
CHROOT_EOF
|
||||
|
||||
############################################
|
||||
# DOTFILES SETUP (in-chroot, optional)
|
||||
# DOTFILES SETUP (in-chroot, optional)
|
||||
############################################
|
||||
echo
|
||||
read -rp "Run dotfiles TUI setup inside chroot now? [YES/no]: " _RUN_TUI
|
||||
_RUN_TUI="${_RUN_TUI:-YES}"
|
||||
if $AF_MODE; then
|
||||
_DO_TUI="${RUN_TUI}"
|
||||
else
|
||||
read -rp "Run dotfiles TUI setup inside chroot now? [YES/no]: " _TUI_IN
|
||||
_TUI_IN="${_TUI_IN:-YES}"
|
||||
[[ "${_TUI_IN^^}" == "YES" ]] && _DO_TUI="YES" || _DO_TUI="NO"
|
||||
fi
|
||||
|
||||
if [[ "${_RUN_TUI^^}" == "YES" ]]; then
|
||||
if [[ "${_DO_TUI^^}" == "YES" ]]; then
|
||||
echo "${USERNAME} ALL=(ALL) NOPASSWD: ALL" \
|
||||
| arch-chroot /mnt tee /etc/sudoers.d/99-setup-nopasswd > /dev/null
|
||||
|
||||
|
|
@ -227,10 +328,20 @@ if [[ "${_RUN_TUI^^}" == "YES" ]]; then
|
|||
arch-chroot /mnt rm -f /etc/sudoers.d/99-setup-nopasswd
|
||||
fi
|
||||
|
||||
# Remove answerfile from new system after setup completes
|
||||
if $AF_MODE && [[ -f /mnt/answerfile.json ]]; then
|
||||
rm -f /mnt/answerfile.json
|
||||
fi
|
||||
|
||||
echo "Installation complete!"
|
||||
echo " umount -R /mnt && reboot"
|
||||
if [[ "${_RUN_TUI^^}" != "YES" ]]; then
|
||||
if [[ "${_DO_TUI^^}" != "YES" ]]; then
|
||||
echo
|
||||
echo "After first boot, login as ${USERNAME} and run:"
|
||||
echo " ~/Dotfiles/setup/tui-install.sh"
|
||||
fi
|
||||
if [[ "$ENCRYPT_DISK" == "YES" ]]; then
|
||||
echo
|
||||
echo "LUKS backup key stored at /_LUKS_BACKUP_KEY in the new system."
|
||||
echo "Keep this file secure — it can unlock the root partition."
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,10 +1,48 @@
|
|||
#!/usr/bin/env bash
|
||||
# build.sh — build the M-Archy Arch Linux ISO
|
||||
#
|
||||
# Usage:
|
||||
# bash build.sh [--preconf [FILE]] [OUT_DIR]
|
||||
#
|
||||
# --preconf Embed ~/answerfile.json into the ISO at /answerfile.json
|
||||
# --preconf FILE Embed the specified answerfile instead
|
||||
# OUT_DIR Output directory (default: ~/m-archy-out, or $OUT_DIR env)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DOTFILES_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
# ── Argument parsing ───────────────────────────────────────────────────────────
|
||||
PRECONF_FILE=""
|
||||
OUT_ARG=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--preconf)
|
||||
# Optional next arg: a file path (doesn't start with -)
|
||||
if [[ $# -gt 1 && "${2:0:1}" != "-" ]]; then
|
||||
PRECONF_FILE="$2"; shift
|
||||
else
|
||||
PRECONF_FILE="$HOME/answerfile.json"
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--preconf=*)
|
||||
PRECONF_FILE="${1#--preconf=}"
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
echo "Unknown flag: $1" >&2; exit 1
|
||||
;;
|
||||
*)
|
||||
OUT_ARG="$1"; shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
WORK_DIR="${WORK_DIR:-$HOME/m-archy-build}"
|
||||
OUT_DIR="${1:-${OUT_DIR:-$HOME/m-archy-out}}"
|
||||
OUT_DIR="${OUT_ARG:-${OUT_DIR:-$HOME/m-archy-out}}"
|
||||
PROFILE="$WORK_DIR/profile"
|
||||
RELENG="/usr/share/archiso/configs/releng"
|
||||
|
||||
|
|
@ -15,6 +53,16 @@ fi
|
|||
|
||||
[[ -d "$RELENG" ]] || { echo "ERROR: $RELENG not found — is archiso installed?"; exit 1; }
|
||||
|
||||
# Validate answerfile early if --preconf was given
|
||||
if [[ -n "$PRECONF_FILE" ]]; then
|
||||
[[ -f "$PRECONF_FILE" ]] \
|
||||
|| { echo "ERROR: answerfile not found: $PRECONF_FILE"; exit 1; }
|
||||
command -v jq &>/dev/null \
|
||||
&& jq empty "$PRECONF_FILE" \
|
||||
|| echo "Warning: jq not available — skipping answerfile JSON validation"
|
||||
echo "Answerfile to embed: $PRECONF_FILE"
|
||||
fi
|
||||
|
||||
rm -rf "$WORK_DIR"
|
||||
mkdir -p "$WORK_DIR" "$OUT_DIR"
|
||||
|
||||
|
|
@ -43,9 +91,19 @@ chmod 755 \
|
|||
"$PROFILE/airootfs/usr/local/bin/install-arch" \
|
||||
"$PROFILE/airootfs/root/installer/"*.sh
|
||||
|
||||
# ── Embed answerfile (--preconf) ───────────────────────────────────────────────
|
||||
if [[ -n "$PRECONF_FILE" ]]; then
|
||||
echo "Embedding answerfile: $PRECONF_FILE → /answerfile.json"
|
||||
install -m 644 "$PRECONF_FILE" "$PROFILE/airootfs/answerfile.json"
|
||||
fi
|
||||
|
||||
echo "Building ISO (this may take a while)..."
|
||||
sudo mkarchiso -v -w "$WORK_DIR/mkarchiso" -o "$OUT_DIR" "$PROFILE"
|
||||
|
||||
echo
|
||||
echo "Done."
|
||||
ls -lh "$OUT_DIR/"*.iso 2>/dev/null || true
|
||||
|
||||
if [[ -n "$PRECONF_FILE" ]]; then
|
||||
echo "Answerfile embedded — automated install will activate on boot."
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -0,0 +1,377 @@
|
|||
#!/bin/bash
|
||||
# generate-answerfile.sh — dry-run the M-Archy installer dialogs and write
|
||||
# all selections to an answerfile.json. Nothing is installed.
|
||||
#
|
||||
# Usage:
|
||||
# bash generate-answerfile.sh [OUTPUT_PATH]
|
||||
# OUTPUT_PATH defaults to ~/answerfile.json
|
||||
#
|
||||
# The generated file can be placed at /answerfile.json on the installer USB
|
||||
# (use build.sh --preconf to embed it automatically) so that the full
|
||||
# install — base OS + dotfiles — runs without any manual input.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DOTFILES_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
OUTPUT="${1:-$HOME/answerfile.json}"
|
||||
TMP_D="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP_D"' EXIT
|
||||
|
||||
BACKTITLE="M-Archy Answerfile Generator"
|
||||
|
||||
# ── Dialog theme ──────────────────────────────────────────────────────────────
|
||||
export DIALOGRC="$TMP_D/dialogrc"
|
||||
cat > "$DIALOGRC" <<'EOF'
|
||||
use_shadow = ON
|
||||
use_colors = ON
|
||||
screen_color = (BLACK,BLACK,ON)
|
||||
shadow_color = (BLACK,BLACK,ON)
|
||||
title_color = (MAGENTA,BLACK,ON)
|
||||
border_color = (MAGENTA,BLACK,ON)
|
||||
button_active_color = (BLACK,MAGENTA,ON)
|
||||
button_inactive_color = (WHITE,BLACK,OFF)
|
||||
button_key_active_color = (BLACK,CYAN,ON)
|
||||
button_key_inactive_color = (CYAN,BLACK,ON)
|
||||
button_label_active_color = (BLACK,MAGENTA,ON)
|
||||
button_label_inactive_color = (WHITE,BLACK,OFF)
|
||||
inputbox_color = (WHITE,BLACK,OFF)
|
||||
inputbox_border_color = (MAGENTA,BLACK,ON)
|
||||
menubox_color = (WHITE,BLACK,OFF)
|
||||
menubox_border_color = (MAGENTA,BLACK,ON)
|
||||
item_color = (WHITE,BLACK,OFF)
|
||||
item_selected_color = (BLACK,MAGENTA,ON)
|
||||
tag_color = (CYAN,BLACK,ON)
|
||||
tag_selected_color = (BLACK,CYAN,ON)
|
||||
tag_key_color = (CYAN,BLACK,ON)
|
||||
tag_key_selected_color = (BLACK,CYAN,ON)
|
||||
check_color = (WHITE,BLACK,OFF)
|
||||
check_selected_color = (BLACK,MAGENTA,ON)
|
||||
uarrow_color = (MAGENTA,BLACK,ON)
|
||||
darrow_color = (MAGENTA,BLACK,ON)
|
||||
EOF
|
||||
|
||||
# ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
require_dialog() {
|
||||
command -v dialog &>/dev/null && return
|
||||
echo "dialog not found — installing..."
|
||||
sudo pacman -S --noconfirm dialog || { echo "Failed to install dialog."; exit 1; }
|
||||
}
|
||||
|
||||
require_jq() {
|
||||
command -v jq &>/dev/null && return
|
||||
echo "jq not found — installing..."
|
||||
sudo pacman -S --noconfirm jq || { echo "Failed to install jq."; exit 1; }
|
||||
}
|
||||
|
||||
die() { clear; printf "\n Error: %s\n\n" "$1" >&2; exit 1; }
|
||||
|
||||
# json_str: emit a properly-quoted JSON string value (handles empty → null)
|
||||
json_str() {
|
||||
local v="$1"
|
||||
if [[ -z "$v" ]]; then printf 'null'; else printf '"%s"' "$v"; fi
|
||||
}
|
||||
|
||||
# json_bool: emit true/false from YES/NO or true/false input
|
||||
json_bool() {
|
||||
local v="${1,,}"
|
||||
[[ "$v" == "yes" || "$v" == "true" ]] && echo "true" || echo "false"
|
||||
}
|
||||
|
||||
# ── Preflight ─────────────────────────────────────────────────────────────────
|
||||
require_dialog
|
||||
require_jq
|
||||
|
||||
# ── Welcome ───────────────────────────────────────────────────────────────────
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " Answerfile Generator " \
|
||||
--msgbox "\n\
|
||||
This tool walks you through all installer dialogs\n\
|
||||
and saves your choices to an answerfile.\n\
|
||||
─────────────────────────────────────────────────\n\
|
||||
\n\
|
||||
NO software will be installed — this is a dry run.\n\
|
||||
\n\
|
||||
Output: $OUTPUT\n\
|
||||
\n\
|
||||
To use on a USB: build.sh --preconf $OUTPUT\n" 16 62
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# PART 1 — Base OS install options
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
# ── Drive ─────────────────────────────────────────────────────────────────────
|
||||
AVAIL_DRIVES=$(lsblk -dn -o NAME,SIZE,MODEL 2>/dev/null | awk '{printf "%s \"%s %s\" off ", $1, $2, $3}' || true)
|
||||
|
||||
AF_DRIVE=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Target Drive " \
|
||||
--inputbox "\n Enter the install drive (e.g. /dev/sda, /dev/nvme0n1).\n\n Available drives:\n$(lsblk -dn -o NAME,SIZE,MODEL 2>/dev/null | sed 's/^/ /')\n" \
|
||||
16 64 "/dev/sda" \
|
||||
3>&1 1>&2 2>&3) || AF_DRIVE=""
|
||||
|
||||
# ── Kernel ────────────────────────────────────────────────────────────────────
|
||||
AF_KERNEL=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Kernel " \
|
||||
--menu "Select kernel package:" 12 54 3 \
|
||||
"linux" "Stable kernel" \
|
||||
"linux-lts" "Long-term support kernel" \
|
||||
"linux-zen" "Zen performance kernel" \
|
||||
3>&1 1>&2 2>&3) || AF_KERNEL="linux"
|
||||
|
||||
# ── Hostname ──────────────────────────────────────────────────────────────────
|
||||
AF_HOSTNAME=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Hostname " \
|
||||
--inputbox "\n Hostname for the new system.\n\n Leave blank to keep default.\n\n Note: a MAC address suffix will be appended\n automatically when the answerfile is applied,\n preventing hostname conflicts across machines.\n" \
|
||||
14 62 "" \
|
||||
3>&1 1>&2 2>&3) || AF_HOSTNAME=""
|
||||
|
||||
# ── Username ──────────────────────────────────────────────────────────────────
|
||||
AF_USERNAME=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Username " \
|
||||
--inputbox "\n Name for the primary user account.\n" \
|
||||
9 54 "" \
|
||||
3>&1 1>&2 2>&3) || AF_USERNAME=""
|
||||
|
||||
# NOTE: passwords are intentionally NOT stored in the answerfile.
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " Password " \
|
||||
--msgbox "\n Passwords are NOT stored in the answerfile.\n\n You will be prompted for the user password\n at install time even in automated mode.\n" \
|
||||
10 56
|
||||
|
||||
# ── Disk encryption ───────────────────────────────────────────────────────────
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " Disk Encryption " \
|
||||
--yesno "\n Enable LUKS2 disk encryption on the root partition?\n\n If yes, a backup LUKS key will be auto-generated\n and placed at /_LUKS_BACKUP_KEY in the new system.\n" \
|
||||
11 62 && AF_ENCRYPT="true" || AF_ENCRYPT="false"
|
||||
|
||||
AF_FIDO2_ROOT="false"
|
||||
AF_FIDO2_USER="false"
|
||||
if [[ "$AF_ENCRYPT" == "true" ]]; then
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " FIDO2 Root Unlock " \
|
||||
--yesno "\n Enable FIDO2 hardware key for LUKS root unlock?\n" \
|
||||
8 56 && AF_FIDO2_ROOT="true" || AF_FIDO2_ROOT="false"
|
||||
fi
|
||||
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " FIDO2 User Login " \
|
||||
--yesno "\n Enable FIDO2 hardware key for user login (PAM)?\n" \
|
||||
8 56 && AF_FIDO2_USER="true" || AF_FIDO2_USER="false"
|
||||
|
||||
# ── Run TUI installer after base install ──────────────────────────────────────
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " Dotfiles Setup " \
|
||||
--yesno "\n Automatically run the dotfiles TUI installer\n inside the chroot after base install completes?\n" \
|
||||
9 58 && AF_RUN_TUI="true" || AF_RUN_TUI="false"
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# PART 2 — Dotfiles / TUI installer options
|
||||
# (only shown if run_tui is true)
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
AF_COMPONENTS=""
|
||||
AF_DE="none"
|
||||
AF_APPS=""
|
||||
AF_COLOR_TEXT=""
|
||||
AF_COLOR_BG=""
|
||||
AF_COLOR_HIGHLIGHT=""
|
||||
AF_COLOR_DARK=""
|
||||
AF_COLOR_RED=""
|
||||
|
||||
if [[ "$AF_RUN_TUI" == "true" ]]; then
|
||||
|
||||
# ── Components ────────────────────────────────────────────────────────────
|
||||
AF_COMPONENTS=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Select Components " \
|
||||
--checklist "Space toggles · Enter confirms · Esc quits" 15 68 4 \
|
||||
"pkg" "Package managers yay · nvm · rust" on \
|
||||
"core" "Core packages 100+ base system packages" on \
|
||||
"svc" "Core services NetworkManager · cronie · fail2ban" on \
|
||||
"shell" "Shell setup zsh · nvim · yazi · micro · starship" on \
|
||||
3>&1 1>&2 2>&3) || AF_COMPONENTS=""
|
||||
|
||||
# ── DE ────────────────────────────────────────────────────────────────────
|
||||
AF_DE=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Desktop Environment " \
|
||||
--menu "Select a desktop environment · Esc / none to skip:" 20 70 8 \
|
||||
"hyprland" "Hyprland — Wayland WM, full setup (primary)" \
|
||||
"sway" "Sway — Wayland tiling WM" \
|
||||
"kde-plasma" "KDE Plasma — feature-rich Wayland/X11 DE" \
|
||||
"gnome" "GNOME — modern Wayland DE" \
|
||||
"cosmic" "COSMIC — Rust-built Wayland DE (System76)" \
|
||||
"xfce" "XFCE — lightweight X11 DE" \
|
||||
"lxqt" "LXQt — lightweight Qt X11 DE" \
|
||||
"none" "Skip DE installation" \
|
||||
3>&1 1>&2 2>&3) || AF_DE="none"
|
||||
|
||||
# ── Apps ──────────────────────────────────────────────────────────────────
|
||||
AF_APPS=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Applications " \
|
||||
--checklist "Optional applications — installed after base components:" 40 76 32 \
|
||||
"ollama" "Ollama local LLM runner + API server" off \
|
||||
"llama-cpp" "llama.cpp standalone inference CLI + server" off \
|
||||
"open-webui" "Open WebUI browser UI for Ollama / LLM backends" off \
|
||||
"claude" "Claude Code Anthropic CLI via npm" off \
|
||||
"networking-cli" "Networking CLI nmap · nethogs · mitmproxy · httpie" off \
|
||||
"disk-recovery" "Disk Recovery ddrescue · f3" off \
|
||||
"himalaya" "Himalaya terminal email client (AUR)" off \
|
||||
"gnuplot" "Gnuplot scientific plotting" off \
|
||||
"povray" "POV-Ray ray-tracing renderer" off \
|
||||
"blender" "Blender 3D creation suite" off \
|
||||
"toot" "toot Mastodon CLI client (AUR)" off \
|
||||
"db-clients" "DB Clients pgcli · mycli" off \
|
||||
"mysql" "MySQL / MariaDB mariadb server + setup" off \
|
||||
"productivity" "Productivity taskwarrior · watson · jrnl" off \
|
||||
"yt-dlp" "yt-dlp YouTube / media downloader" off \
|
||||
"sox" "SoX audio processing toolkit" off \
|
||||
"imagemagick" "ImageMagick image manipulation" off \
|
||||
"ffmpeg" "FFmpeg extras thumbnailer · GStreamer codecs" off \
|
||||
"localtunnel" "LocalTunnel expose localhost via tunnel" off \
|
||||
"butter" "butter btrfs snapshot backup (AUR)" off \
|
||||
"tlp" "TLP laptop power management" off \
|
||||
"steam" "Steam gaming platform" off \
|
||||
"vesktop" "Vesktop Discord + Vencord theme" off \
|
||||
"spotify" "Spotify launcher + Spicetify theming" off \
|
||||
"prism" "PrismLauncher Minecraft launcher (Flatpak)" off \
|
||||
"vintagestory" "Vintage Story survival game (AUR)" off \
|
||||
"localsend" "LocalSend LAN file transfer (AUR)" off \
|
||||
"croc" "croc cross-platform file transfer" off \
|
||||
"onlyoffice" "OnlyOffice office suite (AUR)" off \
|
||||
"wireshark" "Wireshark network packet analyser (GUI)" off \
|
||||
"k8s" "Kubernetes tools kubectl · podman-desktop" off \
|
||||
"docker" "Docker docker · docker-compose" off \
|
||||
"podman" "Podman rootless containers · buildah" off \
|
||||
"cockpit" "Cockpit web UI · machines · podman" off \
|
||||
"ssh-server" "SSH server openssh · key-auth · enabled" off \
|
||||
"freeipa-client" "FreeIPA Client sssd + ipa-client-install + enrollment" off \
|
||||
"freeipa-server" "FreeIPA Server interactive server setup + client gen" off \
|
||||
"freeipa-image" "FreeIPA Image OCI/LXC/Proxmox LXC builder + Keycloak" off \
|
||||
"python" "Python tools pyright · pipx · pynvim" off \
|
||||
"zfs" "ZFS zfs-dkms kernel module" off \
|
||||
"wprs" "WPRS wprs-git (AUR)" off \
|
||||
"chromium" "Chromium open-source browser (official)" off \
|
||||
"firefox-browser" "Firefox Mozilla browser (official)" off \
|
||||
"zen-browser" "Zen Browser Firefox-based privacy browser (AUR)" off \
|
||||
"nyxt" "Nyxt keyboard-driven browser (AUR)" off \
|
||||
"librewolf" "LibreWolf hardened Firefox fork (AUR)" off \
|
||||
"min-browser" "Min minimal Electron browser (AUR)" off \
|
||||
"vscodium" "VSCodium telemetry-free VS Code (AUR)" off \
|
||||
"zed-ide" "Zed high-performance Rust IDE (official)" off \
|
||||
"geany" "Geany lightweight IDE + plugins (official)" off \
|
||||
"codeblocks" "Code::Blocks C/C++ IDE (official)" off \
|
||||
"kate" "Kate KDE advanced text editor (official)" off \
|
||||
3>&1 1>&2 2>&3) || AF_APPS=""
|
||||
|
||||
# ── Colorway ──────────────────────────────────────────────────────────────
|
||||
# Read defaults from repo colors.conf
|
||||
declare -A _cdef
|
||||
if [[ -f "$DOTFILES_DIR/colors.conf" ]]; then
|
||||
while IFS='=' read -r k v; do
|
||||
k="${k%%[[:space:]]*}"
|
||||
[[ "$k" =~ ^[[:space:]]*# || -z "$k" ]] && continue
|
||||
v="${v%%#*}"; v="${v//[[:space:]]/}"; v="${v^^}"
|
||||
_cdef[$k]="$v"
|
||||
done < "$DOTFILES_DIR/colors.conf"
|
||||
fi
|
||||
DEF_TEXT="${_cdef[COLOR_TEXT]:-D6ABAB}"
|
||||
DEF_BG="${_cdef[COLOR_BG]:-1A1A1A}"
|
||||
DEF_HIGHLIGHT="${_cdef[COLOR_HIGHLIGHT]:-E40046}"
|
||||
DEF_DARK="${_cdef[COLOR_DARK]:-5018DD}"
|
||||
DEF_RED="${_cdef[COLOR_RED]:-F50505}"
|
||||
|
||||
COLORWAY_RAW=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Colorway (optional) " \
|
||||
--form "\n Customize theme colors — bare 6-digit hex, no #.\n Leave unchanged to omit colors from answerfile.\n" \
|
||||
16 62 5 \
|
||||
"COLOR_TEXT " 1 1 "$DEF_TEXT" 1 18 10 7 \
|
||||
"COLOR_BG " 2 1 "$DEF_BG" 2 18 10 7 \
|
||||
"COLOR_HIGHLIGHT " 3 1 "$DEF_HIGHLIGHT" 3 18 10 7 \
|
||||
"COLOR_DARK " 4 1 "$DEF_DARK" 4 18 10 7 \
|
||||
"COLOR_RED " 5 1 "$DEF_RED" 5 18 10 7 \
|
||||
3>&1 1>&2 2>&3) || COLORWAY_RAW=""
|
||||
|
||||
if [[ -n "$COLORWAY_RAW" ]]; then
|
||||
mapfile -t _cv <<< "$COLORWAY_RAW"
|
||||
N_TEXT="${_cv[0]:-$DEF_TEXT}"
|
||||
N_BG="${_cv[1]:-$DEF_BG}"
|
||||
N_HIGHLIGHT="${_cv[2]:-$DEF_HIGHLIGHT}"
|
||||
N_DARK="${_cv[3]:-$DEF_DARK}"
|
||||
N_RED="${_cv[4]:-$DEF_RED}"
|
||||
# Only save colors if any differ from defaults
|
||||
if [[ "${N_TEXT^^}" != "$DEF_TEXT" || \
|
||||
"${N_BG^^}" != "$DEF_BG" || \
|
||||
"${N_HIGHLIGHT^^}" != "$DEF_HIGHLIGHT" || \
|
||||
"${N_DARK^^}" != "$DEF_DARK" || \
|
||||
"${N_RED^^}" != "$DEF_RED" ]]; then
|
||||
AF_COLOR_TEXT="${N_TEXT^^}"
|
||||
AF_COLOR_BG="${N_BG^^}"
|
||||
AF_COLOR_HIGHLIGHT="${N_HIGHLIGHT^^}"
|
||||
AF_COLOR_DARK="${N_DARK^^}"
|
||||
AF_COLOR_RED="${N_RED^^}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Confirmation ──────────────────────────────────────────────────────────────
|
||||
SUMMARY=""
|
||||
[[ -n "$AF_DRIVE" ]] && SUMMARY+=" Drive: $AF_DRIVE\n"
|
||||
[[ -n "$AF_KERNEL" ]] && SUMMARY+=" Kernel: $AF_KERNEL\n"
|
||||
[[ -n "$AF_HOSTNAME" ]] && SUMMARY+=" Hostname: $AF_HOSTNAME (+ MAC suffix at deploy)\n"
|
||||
[[ -n "$AF_USERNAME" ]] && SUMMARY+=" Username: $AF_USERNAME\n"
|
||||
SUMMARY+=" Encrypt: $AF_ENCRYPT\n"
|
||||
SUMMARY+=" FIDO2 root: $AF_FIDO2_ROOT / FIDO2 user: $AF_FIDO2_USER\n"
|
||||
SUMMARY+=" Run TUI: $AF_RUN_TUI\n"
|
||||
[[ -n "$AF_DE" && "$AF_DE" != "none" ]] && SUMMARY+=" DE: $AF_DE\n"
|
||||
[[ -n "$AF_COLOR_TEXT" ]] && SUMMARY+=" Colors: custom\n"
|
||||
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " Confirm " \
|
||||
--yesno "\n Save answerfile with these settings:\n\n${SUMMARY}\n Output: $OUTPUT\n\n Proceed?" \
|
||||
22 66 || { clear; echo "Aborted."; exit 0; }
|
||||
|
||||
# ── Build JSON arrays from space-separated dialog output ──────────────────────
|
||||
_words_to_json_array() {
|
||||
local input="$1"
|
||||
local first=1
|
||||
printf '['
|
||||
for w in $input; do
|
||||
[[ $first -eq 0 ]] && printf ','
|
||||
printf '"%s"' "$w"
|
||||
first=0
|
||||
done
|
||||
printf ']'
|
||||
}
|
||||
|
||||
# ── Write answerfile ──────────────────────────────────────────────────────────
|
||||
mkdir -p "$(dirname "$OUTPUT")"
|
||||
|
||||
{
|
||||
printf '{\n'
|
||||
printf ' "_generated": "%s",\n' "$(date -Iseconds)"
|
||||
printf ' "drive": %s,\n' "$(json_str "$AF_DRIVE")"
|
||||
printf ' "kernel": %s,\n' "$(json_str "$AF_KERNEL")"
|
||||
printf ' "hostname": %s,\n' "$(json_str "$AF_HOSTNAME")"
|
||||
printf ' "username": %s,\n' "$(json_str "$AF_USERNAME")"
|
||||
printf ' "encrypt": %s,\n' "$AF_ENCRYPT"
|
||||
printf ' "fido2_root": %s,\n' "$AF_FIDO2_ROOT"
|
||||
printf ' "fido2_user": %s,\n' "$AF_FIDO2_USER"
|
||||
printf ' "run_tui": %s,\n' "$AF_RUN_TUI"
|
||||
printf ' "components": %s,\n' "$(_words_to_json_array "$AF_COMPONENTS")"
|
||||
printf ' "desktop_environment": %s,\n' "$(json_str "$AF_DE")"
|
||||
printf ' "apps": %s' "$(_words_to_json_array "$AF_APPS")"
|
||||
|
||||
if [[ -n "$AF_COLOR_TEXT" ]]; then
|
||||
printf ',\n "colors": {\n'
|
||||
printf ' "COLOR_TEXT": "%s",\n' "$AF_COLOR_TEXT"
|
||||
printf ' "COLOR_BG": "%s",\n' "$AF_COLOR_BG"
|
||||
printf ' "COLOR_HIGHLIGHT": "%s",\n' "$AF_COLOR_HIGHLIGHT"
|
||||
printf ' "COLOR_DARK": "%s",\n' "$AF_COLOR_DARK"
|
||||
printf ' "COLOR_RED": "%s"\n' "$AF_COLOR_RED"
|
||||
printf ' }'
|
||||
fi
|
||||
|
||||
printf '\n}\n'
|
||||
} > "$OUTPUT"
|
||||
|
||||
clear
|
||||
printf "\n Answerfile saved to: %s\n\n" "$OUTPUT"
|
||||
printf " To embed in ISO: bash setup/archiso/build.sh --preconf %s\n\n" "$OUTPUT"
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
---
|
||||
# collect-luks-keys.yml — fetch LUKS backup keys from enrolled clients.
|
||||
#
|
||||
# When a client was installed with disk encryption via the M-Archy installer,
|
||||
# a backup LUKS key is stored at /_LUKS_BACKUP_KEY inside the encrypted root.
|
||||
# This playbook fetches those keys to the controller and names each copy
|
||||
# <HOSTNAME>_LUKS_BACKUP_KEY so they can be archived securely.
|
||||
#
|
||||
# Keys are stored in luks-keys/ relative to the playbook directory.
|
||||
# Protect that directory carefully — keys can unlock client root partitions.
|
||||
#
|
||||
# Usage:
|
||||
# ansible-playbook -i inventory collect-luks-keys.yml
|
||||
# ansible-playbook -i inventory collect-luks-keys.yml -e luks_keys_store=/secure/path
|
||||
#
|
||||
# To run automatically, add a cron job on the Ansible controller:
|
||||
# 0 3 * * * cd /path/to/playbooks && ansible-playbook -i inventory collect-luks-keys.yml
|
||||
|
||||
- name: Collect LUKS backup keys from enrolled clients
|
||||
hosts: all
|
||||
become: yes
|
||||
|
||||
vars:
|
||||
luks_key_path: /_LUKS_BACKUP_KEY
|
||||
luks_keys_store: "{{ playbook_dir }}/luks-keys"
|
||||
|
||||
tasks:
|
||||
|
||||
- name: Ensure local key store directory exists
|
||||
file:
|
||||
path: "{{ luks_keys_store }}"
|
||||
state: directory
|
||||
mode: '0700'
|
||||
delegate_to: localhost
|
||||
run_once: true
|
||||
become: false
|
||||
|
||||
- name: Check for LUKS backup key on client
|
||||
stat:
|
||||
path: "{{ luks_key_path }}"
|
||||
register: luks_key_stat
|
||||
|
||||
- name: Fetch LUKS backup key to controller
|
||||
fetch:
|
||||
src: "{{ luks_key_path }}"
|
||||
dest: "{{ luks_keys_store }}/{{ inventory_hostname }}_LUKS_BACKUP_KEY"
|
||||
flat: yes
|
||||
when: luks_key_stat.stat.exists
|
||||
register: luks_key_fetch
|
||||
|
||||
- name: Secure fetched key permissions
|
||||
file:
|
||||
path: "{{ luks_keys_store }}/{{ inventory_hostname }}_LUKS_BACKUP_KEY"
|
||||
mode: '0400'
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
when:
|
||||
- luks_key_stat.stat.exists
|
||||
- luks_key_fetch is changed
|
||||
|
||||
- name: Report key status
|
||||
debug:
|
||||
msg: >-
|
||||
{{ inventory_hostname }}:
|
||||
{% if luks_key_stat.stat.exists %}
|
||||
key found and fetched to {{ luks_keys_store }}/{{ inventory_hostname }}_LUKS_BACKUP_KEY
|
||||
{% else %}
|
||||
no /_LUKS_BACKUP_KEY present (unencrypted or already collected)
|
||||
{% endif %}
|
||||
|
|
@ -12,6 +12,10 @@ LOG="$HOME/dotfiles-install.log"
|
|||
TMP_D="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP_D"' EXIT
|
||||
|
||||
ANSWERFILE="${ANSWERFILE:-/answerfile.json}"
|
||||
ANSWERFILE_MODE=false
|
||||
[[ -f "$ANSWERFILE" ]] && ANSWERFILE_MODE=true
|
||||
|
||||
BACKTITLE="the_miro's Arch Dotfiles"
|
||||
|
||||
# ── Cyberqueer dialog theme ───────────────────────────────────────────────────
|
||||
|
|
@ -56,6 +60,12 @@ require_dialog() {
|
|||
sudo pacman -S --noconfirm dialog || { echo "Failed to install dialog."; exit 1; }
|
||||
}
|
||||
|
||||
require_jq() {
|
||||
command -v jq &>/dev/null && return
|
||||
echo "jq not found — installing..."
|
||||
sudo pacman -S --noconfirm jq || { echo "Failed to install jq."; exit 1; }
|
||||
}
|
||||
|
||||
die() {
|
||||
clear
|
||||
printf "\n Error: %s\n\n" "$1" >&2
|
||||
|
|
@ -79,10 +89,14 @@ run_module() {
|
|||
bash "$script" 2>&1 | tee -a "$LOG" || rc=${PIPESTATUS[0]}
|
||||
|
||||
if [[ $rc -ne 0 ]]; then
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " Module Failed " \
|
||||
--yesno "$label exited with code $rc.\n\nContinue anyway?" 8 54 \
|
||||
|| { clear; exit 1; }
|
||||
if [[ $ANSWERFILE_MODE == true ]]; then
|
||||
printf "\n Warning: %s exited with code %d — continuing.\n" "$label" "$rc" | tee -a "$LOG"
|
||||
else
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " Module Failed " \
|
||||
--yesno "$label exited with code $rc.\n\nContinue anyway?" 8 54 \
|
||||
|| { clear; exit 1; }
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -148,22 +162,63 @@ count_steps() {
|
|||
[[ "$a" == *"kate"* ]] && TOTAL=$(( TOTAL + 1 ))
|
||||
}
|
||||
|
||||
# ── Answerfile ────────────────────────────────────────────────────────────────
|
||||
AF_HOSTNAME=""
|
||||
AF_COMPONENTS=""
|
||||
AF_DE="none"
|
||||
AF_APPS=""
|
||||
AF_COLOR_TEXT=""
|
||||
AF_COLOR_BG=""
|
||||
AF_COLOR_HIGHLIGHT=""
|
||||
AF_COLOR_DARK=""
|
||||
AF_COLOR_RED=""
|
||||
|
||||
load_answerfile() {
|
||||
require_jq
|
||||
AF_HOSTNAME=$(jq -r '.hostname // ""' "$ANSWERFILE")
|
||||
AF_COMPONENTS=$(jq -r '(.components // []) | join(" ")' "$ANSWERFILE")
|
||||
AF_DE=$(jq -r '.desktop_environment // "none"' "$ANSWERFILE")
|
||||
AF_APPS=$(jq -r '(.apps // []) | join(" ")' "$ANSWERFILE")
|
||||
AF_COLOR_TEXT=$(jq -r '.colors.COLOR_TEXT // ""' "$ANSWERFILE")
|
||||
AF_COLOR_BG=$(jq -r '.colors.COLOR_BG // ""' "$ANSWERFILE")
|
||||
AF_COLOR_HIGHLIGHT=$(jq -r '.colors.COLOR_HIGHLIGHT // ""' "$ANSWERFILE")
|
||||
AF_COLOR_DARK=$(jq -r '.colors.COLOR_DARK // ""' "$ANSWERFILE")
|
||||
AF_COLOR_RED=$(jq -r '.colors.COLOR_RED // ""' "$ANSWERFILE")
|
||||
}
|
||||
|
||||
# ── MAC address helper ────────────────────────────────────────────────────────
|
||||
get_mac_suffix() {
|
||||
local mac
|
||||
mac=$(ip link show 2>/dev/null \
|
||||
| awk '/^[0-9]+: [^l][^o]/{iface=1} iface && /link\/ether/{print $2; iface=0; exit}')
|
||||
printf '%s' "${mac//:/}"
|
||||
}
|
||||
|
||||
# ── Preflight ─────────────────────────────────────────────────────────────────
|
||||
[[ $EUID -eq 0 ]] && die "Run as your normal user (not root)."
|
||||
command -v pacman &>/dev/null || die "pacman not found — Arch Linux required."
|
||||
|
||||
require_dialog
|
||||
|
||||
if $ANSWERFILE_MODE; then
|
||||
load_answerfile
|
||||
printf "Answerfile mode: %s\n" "$ANSWERFILE" | tee -a "$LOG"
|
||||
fi
|
||||
|
||||
if ! ping -c1 -W3 archlinux.org &>/dev/null; then
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " No Network Detected " \
|
||||
--msgbox "\n No internet connection found.\n\n nmtui will open so you can configure networking.\n Close nmtui when done to continue the installer.\n" 11 58
|
||||
nmtui
|
||||
if ! ping -c1 -W3 archlinux.org &>/dev/null; then
|
||||
if $ANSWERFILE_MODE; then
|
||||
printf "Warning: no internet connection detected.\n" | tee -a "$LOG"
|
||||
else
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " Still Offline " \
|
||||
--yesno "\n Still no internet connection.\n\n Packages cannot be downloaded without network access.\n\n Continue anyway?" 11 58 \
|
||||
|| { clear; echo "Aborted — no network."; exit 1; }
|
||||
--title " No Network Detected " \
|
||||
--msgbox "\n No internet connection found.\n\n nmtui will open so you can configure networking.\n Close nmtui when done to continue the installer.\n" 11 58
|
||||
nmtui
|
||||
if ! ping -c1 -W3 archlinux.org &>/dev/null; then
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " Still Offline " \
|
||||
--yesno "\n Still no internet connection.\n\n Packages cannot be downloaded without network access.\n\n Continue anyway?" 11 58 \
|
||||
|| { clear; echo "Aborted — no network."; exit 1; }
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
@ -171,9 +226,10 @@ fi
|
|||
printf "Dotfiles install: %s\nDotfiles dir: %s\n" "$(date)" "$DOTFILES_DIR" >> "$LOG"
|
||||
|
||||
# ── Welcome ───────────────────────────────────────────────────────────────────
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " Welcome " \
|
||||
--msgbox "\n\
|
||||
if ! $ANSWERFILE_MODE; then
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " Welcome " \
|
||||
--msgbox "\n\
|
||||
the_miro's Arch dotfiles installer\n\
|
||||
Cyberqueer · Wayland · Hyprland\n\
|
||||
─────────────────────────────────────────\n\
|
||||
|
|
@ -182,159 +238,197 @@ dialog --backtitle "$BACKTITLE" \
|
|||
\n\
|
||||
Source: $DOTFILES_DIR\n\
|
||||
Log: $LOG\n" 14 62
|
||||
|
||||
# ── Component selection ───────────────────────────────────────────────────────
|
||||
COMPONENTS=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Select Components " \
|
||||
--checklist "Space toggles · Enter confirms · Esc quits" 15 68 4 \
|
||||
"pkg" "Package managers yay · nvm · rust" on \
|
||||
"core" "Core packages 100+ base system packages" on \
|
||||
"svc" "Core services NetworkManager · cronie · fail2ban" on \
|
||||
"shell" "Shell setup zsh · nvim · yazi · micro · starship" on \
|
||||
3>&1 1>&2 2>&3) || { clear; echo "Aborted."; exit 0; }
|
||||
|
||||
# ── DE selection ──────────────────────────────────────────────────────────────
|
||||
DE=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Desktop Environment " \
|
||||
--menu "Select a desktop environment · Esc / none to skip:" 20 70 8 \
|
||||
"hyprland" "Hyprland — Wayland WM, full setup (primary)" \
|
||||
"sway" "Sway — Wayland tiling WM" \
|
||||
"kde-plasma" "KDE Plasma — feature-rich Wayland/X11 DE" \
|
||||
"gnome" "GNOME — modern Wayland DE" \
|
||||
"cosmic" "COSMIC — Rust-built Wayland DE (System76)" \
|
||||
"xfce" "XFCE — lightweight X11 DE" \
|
||||
"lxqt" "LXQt — lightweight Qt X11 DE" \
|
||||
"none" "Skip DE installation" \
|
||||
3>&1 1>&2 2>&3) || DE="none"
|
||||
|
||||
# ── Apps selection ────────────────────────────────────────────────────────────
|
||||
SELECTED_APPS=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Applications " \
|
||||
--checklist "Optional applications — installed after base components:" 40 76 32 \
|
||||
"ollama" "Ollama local LLM runner + API server" off \
|
||||
"llama-cpp" "llama.cpp standalone inference CLI + server" off \
|
||||
"open-webui" "Open WebUI browser UI for Ollama / LLM backends" off \
|
||||
"claude" "Claude Code Anthropic CLI via npm" off \
|
||||
"networking-cli" "Networking CLI nmap · nethogs · mitmproxy · httpie" off \
|
||||
"disk-recovery" "Disk Recovery ddrescue · f3" off \
|
||||
"himalaya" "Himalaya terminal email client (AUR)" off \
|
||||
"gnuplot" "Gnuplot scientific plotting" off \
|
||||
"povray" "POV-Ray ray-tracing renderer" off \
|
||||
"blender" "Blender 3D creation suite" off \
|
||||
"toot" "toot Mastodon CLI client (AUR)" off \
|
||||
"db-clients" "DB Clients pgcli · mycli" off \
|
||||
"mysql" "MySQL / MariaDB mariadb server + setup" off \
|
||||
"productivity" "Productivity taskwarrior · watson · jrnl" off \
|
||||
"yt-dlp" "yt-dlp YouTube / media downloader" off \
|
||||
"sox" "SoX audio processing toolkit" off \
|
||||
"imagemagick" "ImageMagick image manipulation" off \
|
||||
"ffmpeg" "FFmpeg extras thumbnailer · GStreamer codecs" off \
|
||||
"localtunnel" "LocalTunnel expose localhost via tunnel" off \
|
||||
"butter" "butter btrfs snapshot backup (AUR)" off \
|
||||
"tlp" "TLP laptop power management" off \
|
||||
"steam" "Steam gaming platform" off \
|
||||
"vesktop" "Vesktop Discord + Vencord theme" off \
|
||||
"spotify" "Spotify launcher + Spicetify theming" off \
|
||||
"prism" "PrismLauncher Minecraft launcher (Flatpak)" off \
|
||||
"vintagestory" "Vintage Story survival game (AUR)" off \
|
||||
"localsend" "LocalSend LAN file transfer (AUR)" off \
|
||||
"croc" "croc cross-platform file transfer" off \
|
||||
"onlyoffice" "OnlyOffice office suite (AUR)" off \
|
||||
"wireshark" "Wireshark network packet analyser (GUI)" off \
|
||||
"k8s" "Kubernetes tools kubectl · podman-desktop" off \
|
||||
"docker" "Docker docker · docker-compose" off \
|
||||
"podman" "Podman rootless containers · buildah" off \
|
||||
"cockpit" "Cockpit web UI · machines · podman" off \
|
||||
"ssh-server" "SSH server openssh · key-auth · enabled" off \
|
||||
"freeipa-client" "FreeIPA Client sssd + ipa-client-install + enrollment" off \
|
||||
"freeipa-server" "FreeIPA Server interactive server setup + client gen" off \
|
||||
"freeipa-image" "FreeIPA Image OCI/LXC/Proxmox LXC builder + Keycloak" off \
|
||||
"python" "Python tools pyright · pipx · pynvim" off \
|
||||
"zfs" "ZFS zfs-dkms kernel module" off \
|
||||
"wprs" "WPRS wprs-git (AUR)" off \
|
||||
\
|
||||
"chromium" "Chromium open-source browser (official)" off \
|
||||
"firefox-browser" "Firefox Mozilla browser (official)" off \
|
||||
"zen-browser" "Zen Browser Firefox-based privacy browser (AUR)" off \
|
||||
"nyxt" "Nyxt keyboard-driven browser (AUR)" off \
|
||||
"librewolf" "LibreWolf hardened Firefox fork (AUR)" off \
|
||||
"min-browser" "Min minimal Electron browser (AUR)" off \
|
||||
\
|
||||
"vscodium" "VSCodium telemetry-free VS Code (AUR)" off \
|
||||
"zed-ide" "Zed high-performance Rust IDE (official)" off \
|
||||
"geany" "Geany lightweight IDE + plugins (official)" off \
|
||||
"codeblocks" "Code::Blocks C/C++ IDE (official)" off \
|
||||
"kate" "Kate KDE advanced text editor (official)" off \
|
||||
3>&1 1>&2 2>&3) || SELECTED_APPS=""
|
||||
|
||||
# ── Confirmation ──────────────────────────────────────────────────────────────
|
||||
SUMMARY=""
|
||||
[[ "$COMPONENTS" == *"pkg"* ]] && SUMMARY+=" ✦ Package managers (yay, nvm, rust)\n"
|
||||
[[ "$COMPONENTS" == *"core"* ]] && SUMMARY+=" ✦ Core packages\n"
|
||||
[[ "$COMPONENTS" == *"svc"* ]] && SUMMARY+=" ✦ Core services\n"
|
||||
[[ "$COMPONENTS" == *"shell"* ]] && SUMMARY+=" ✦ Shell setup\n"
|
||||
[[ "$DE" != "none" ]] && SUMMARY+=" ✦ Desktop environment: $DE\n"
|
||||
|
||||
if [[ -n "$SELECTED_APPS" ]]; then
|
||||
SUMMARY+="\n Applications:\n"
|
||||
[[ "$SELECTED_APPS" == *"ollama"* ]] && SUMMARY+=" ✦ Ollama\n"
|
||||
[[ "$SELECTED_APPS" == *"llama-cpp"* ]] && SUMMARY+=" ✦ llama.cpp\n"
|
||||
[[ "$SELECTED_APPS" == *"open-webui"* ]] && SUMMARY+=" ✦ Open WebUI\n"
|
||||
[[ "$SELECTED_APPS" == *"claude"* ]] && SUMMARY+=" ✦ Claude Code\n"
|
||||
[[ "$SELECTED_APPS" == *"networking-cli"* ]] && SUMMARY+=" ✦ Networking CLI (nmap, nethogs, mitmproxy, httpie)\n"
|
||||
[[ "$SELECTED_APPS" == *"disk-recovery"* ]] && SUMMARY+=" ✦ Disk Recovery (ddrescue, f3)\n"
|
||||
[[ "$SELECTED_APPS" == *"himalaya"* ]] && SUMMARY+=" ✦ Himalaya\n"
|
||||
[[ "$SELECTED_APPS" == *"gnuplot"* ]] && SUMMARY+=" ✦ Gnuplot\n"
|
||||
[[ "$SELECTED_APPS" == *"povray"* ]] && SUMMARY+=" ✦ POV-Ray\n"
|
||||
[[ "$SELECTED_APPS" == *"blender"* ]] && SUMMARY+=" ✦ Blender\n"
|
||||
[[ "$SELECTED_APPS" == *"toot"* ]] && SUMMARY+=" ✦ toot\n"
|
||||
[[ "$SELECTED_APPS" == *"db-clients"* ]] && SUMMARY+=" ✦ DB Clients (pgcli, mycli)\n"
|
||||
[[ "$SELECTED_APPS" == *"mysql"* ]] && SUMMARY+=" ✦ MySQL / MariaDB\n"
|
||||
[[ "$SELECTED_APPS" == *"productivity"* ]] && SUMMARY+=" ✦ Productivity (taskwarrior, watson, jrnl)\n"
|
||||
[[ "$SELECTED_APPS" == *"yt-dlp"* ]] && SUMMARY+=" ✦ yt-dlp\n"
|
||||
[[ "$SELECTED_APPS" == *"sox"* ]] && SUMMARY+=" ✦ SoX\n"
|
||||
[[ "$SELECTED_APPS" == *"imagemagick"* ]] && SUMMARY+=" ✦ ImageMagick\n"
|
||||
[[ "$SELECTED_APPS" == *"ffmpeg"* ]] && SUMMARY+=" ✦ FFmpeg extras\n"
|
||||
[[ "$SELECTED_APPS" == *"localtunnel"* ]] && SUMMARY+=" ✦ LocalTunnel\n"
|
||||
[[ "$SELECTED_APPS" == *"butter"* ]] && SUMMARY+=" ✦ butter (btrfs backup)\n"
|
||||
[[ "$SELECTED_APPS" == *"tlp"* ]] && SUMMARY+=" ✦ TLP\n"
|
||||
[[ "$SELECTED_APPS" == *"steam"* ]] && SUMMARY+=" ✦ Steam\n"
|
||||
[[ "$SELECTED_APPS" == *"vesktop"* ]] && SUMMARY+=" ✦ Vesktop + Vencord theme\n"
|
||||
[[ "$SELECTED_APPS" == *"spotify"* ]] && SUMMARY+=" ✦ Spotify + Spicetify\n"
|
||||
[[ "$SELECTED_APPS" == *"prism"* ]] && SUMMARY+=" ✦ PrismLauncher\n"
|
||||
[[ "$SELECTED_APPS" == *"vintagestory"* ]] && SUMMARY+=" ✦ Vintage Story\n"
|
||||
[[ "$SELECTED_APPS" == *"localsend"* ]] && SUMMARY+=" ✦ LocalSend\n"
|
||||
[[ "$SELECTED_APPS" == *"croc"* ]] && SUMMARY+=" ✦ croc\n"
|
||||
[[ "$SELECTED_APPS" == *"onlyoffice"* ]] && SUMMARY+=" ✦ OnlyOffice\n"
|
||||
[[ "$SELECTED_APPS" == *"wireshark"* ]] && SUMMARY+=" ✦ Wireshark\n"
|
||||
[[ "$SELECTED_APPS" == *"k8s"* ]] && SUMMARY+=" ✦ Kubernetes tools\n"
|
||||
[[ "$SELECTED_APPS" == *"docker"* ]] && SUMMARY+=" ✦ Docker + Compose\n"
|
||||
[[ "$SELECTED_APPS" == *"podman"* ]] && SUMMARY+=" ✦ Podman (rootless) + Buildah\n"
|
||||
[[ "$SELECTED_APPS" == *"cockpit"* ]] && SUMMARY+=" ✦ Cockpit web UI\n"
|
||||
[[ "$SELECTED_APPS" == *"ssh-server"* ]] && SUMMARY+=" ✦ SSH server (openssh, key auth)\n"
|
||||
[[ "$SELECTED_APPS" == *"freeipa-client"* ]] && SUMMARY+=" ✦ FreeIPA Client\n"
|
||||
[[ "$SELECTED_APPS" == *"freeipa-server"* ]] && SUMMARY+=" ✦ FreeIPA Server\n"
|
||||
[[ "$SELECTED_APPS" == *"freeipa-image"* ]] && SUMMARY+=" ✦ FreeIPA Image Builder\n"
|
||||
[[ "$SELECTED_APPS" == *"python"* ]] && SUMMARY+=" ✦ Python tools\n"
|
||||
[[ "$SELECTED_APPS" == *"zfs"* ]] && SUMMARY+=" ✦ ZFS\n"
|
||||
[[ "$SELECTED_APPS" == *"wprs"* ]] && SUMMARY+=" ✦ WPRS\n"
|
||||
[[ "$SELECTED_APPS" == *"chromium"* ]] && SUMMARY+=" ✦ Chromium\n"
|
||||
[[ "$SELECTED_APPS" == *"firefox-browser"* ]] && SUMMARY+=" ✦ Firefox\n"
|
||||
[[ "$SELECTED_APPS" == *"zen-browser"* ]] && SUMMARY+=" ✦ Zen Browser\n"
|
||||
[[ "$SELECTED_APPS" == *"nyxt"* ]] && SUMMARY+=" ✦ Nyxt\n"
|
||||
[[ "$SELECTED_APPS" == *"librewolf"* ]] && SUMMARY+=" ✦ LibreWolf\n"
|
||||
[[ "$SELECTED_APPS" == *"min-browser"* ]] && SUMMARY+=" ✦ Min Browser\n"
|
||||
[[ "$SELECTED_APPS" == *"vscodium"* ]] && SUMMARY+=" ✦ VSCodium\n"
|
||||
[[ "$SELECTED_APPS" == *"zed-ide"* ]] && SUMMARY+=" ✦ Zed IDE\n"
|
||||
[[ "$SELECTED_APPS" == *"geany"* ]] && SUMMARY+=" ✦ Geany\n"
|
||||
[[ "$SELECTED_APPS" == *"codeblocks"* ]] && SUMMARY+=" ✦ Code::Blocks\n"
|
||||
[[ "$SELECTED_APPS" == *"kate"* ]] && SUMMARY+=" ✦ Kate\n"
|
||||
fi
|
||||
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " Confirm Installation " \
|
||||
--yesno "\n Components to install:\n\n${SUMMARY}\n Log: $LOG\n\n Proceed?" \
|
||||
24 62 || { clear; echo "Aborted."; exit 0; }
|
||||
# ── Hostname ──────────────────────────────────────────────────────────────────
|
||||
HOSTNAME_SET=""
|
||||
if $ANSWERFILE_MODE; then
|
||||
if [[ -n "$AF_HOSTNAME" ]]; then
|
||||
MAC=$(get_mac_suffix)
|
||||
HOSTNAME_SET="${AF_HOSTNAME}-${MAC}"
|
||||
printf "Hostname (from answerfile + MAC): %s\n" "$HOSTNAME_SET" | tee -a "$LOG"
|
||||
fi
|
||||
else
|
||||
HOSTNAME_INPUT=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Hostname " \
|
||||
--inputbox "\n Hostname for this machine (leave blank to keep default).\n" 9 54 "" \
|
||||
3>&1 1>&2 2>&3) || HOSTNAME_INPUT=""
|
||||
HOSTNAME_SET="$HOSTNAME_INPUT"
|
||||
fi
|
||||
|
||||
if [[ -n "$HOSTNAME_SET" ]]; then
|
||||
sudo hostnamectl set-hostname "$HOSTNAME_SET" 2>/dev/null \
|
||||
|| echo "$HOSTNAME_SET" | sudo tee /etc/hostname > /dev/null
|
||||
printf "Hostname set: %s\n" "$HOSTNAME_SET" >> "$LOG"
|
||||
fi
|
||||
|
||||
# ── Component selection ───────────────────────────────────────────────────────
|
||||
if $ANSWERFILE_MODE; then
|
||||
COMPONENTS="$AF_COMPONENTS"
|
||||
else
|
||||
COMPONENTS=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Select Components " \
|
||||
--checklist "Space toggles · Enter confirms · Esc quits" 15 68 4 \
|
||||
"pkg" "Package managers yay · nvm · rust" on \
|
||||
"core" "Core packages 100+ base system packages" on \
|
||||
"svc" "Core services NetworkManager · cronie · fail2ban" on \
|
||||
"shell" "Shell setup zsh · nvim · yazi · micro · starship" on \
|
||||
3>&1 1>&2 2>&3) || { clear; echo "Aborted."; exit 0; }
|
||||
fi
|
||||
|
||||
# ── DE selection ──────────────────────────────────────────────────────────────
|
||||
if $ANSWERFILE_MODE; then
|
||||
DE="$AF_DE"
|
||||
else
|
||||
DE=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Desktop Environment " \
|
||||
--menu "Select a desktop environment · Esc / none to skip:" 20 70 8 \
|
||||
"hyprland" "Hyprland — Wayland WM, full setup (primary)" \
|
||||
"sway" "Sway — Wayland tiling WM" \
|
||||
"kde-plasma" "KDE Plasma — feature-rich Wayland/X11 DE" \
|
||||
"gnome" "GNOME — modern Wayland DE" \
|
||||
"cosmic" "COSMIC — Rust-built Wayland DE (System76)" \
|
||||
"xfce" "XFCE — lightweight X11 DE" \
|
||||
"lxqt" "LXQt — lightweight Qt X11 DE" \
|
||||
"none" "Skip DE installation" \
|
||||
3>&1 1>&2 2>&3) || DE="none"
|
||||
fi
|
||||
|
||||
# ── Apps selection ────────────────────────────────────────────────────────────
|
||||
if $ANSWERFILE_MODE; then
|
||||
SELECTED_APPS="$AF_APPS"
|
||||
else
|
||||
SELECTED_APPS=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Applications " \
|
||||
--checklist "Optional applications — installed after base components:" 40 76 32 \
|
||||
"ollama" "Ollama local LLM runner + API server" off \
|
||||
"llama-cpp" "llama.cpp standalone inference CLI + server" off \
|
||||
"open-webui" "Open WebUI browser UI for Ollama / LLM backends" off \
|
||||
"claude" "Claude Code Anthropic CLI via npm" off \
|
||||
"networking-cli" "Networking CLI nmap · nethogs · mitmproxy · httpie" off \
|
||||
"disk-recovery" "Disk Recovery ddrescue · f3" off \
|
||||
"himalaya" "Himalaya terminal email client (AUR)" off \
|
||||
"gnuplot" "Gnuplot scientific plotting" off \
|
||||
"povray" "POV-Ray ray-tracing renderer" off \
|
||||
"blender" "Blender 3D creation suite" off \
|
||||
"toot" "toot Mastodon CLI client (AUR)" off \
|
||||
"db-clients" "DB Clients pgcli · mycli" off \
|
||||
"mysql" "MySQL / MariaDB mariadb server + setup" off \
|
||||
"productivity" "Productivity taskwarrior · watson · jrnl" off \
|
||||
"yt-dlp" "yt-dlp YouTube / media downloader" off \
|
||||
"sox" "SoX audio processing toolkit" off \
|
||||
"imagemagick" "ImageMagick image manipulation" off \
|
||||
"ffmpeg" "FFmpeg extras thumbnailer · GStreamer codecs" off \
|
||||
"localtunnel" "LocalTunnel expose localhost via tunnel" off \
|
||||
"butter" "butter btrfs snapshot backup (AUR)" off \
|
||||
"tlp" "TLP laptop power management" off \
|
||||
"steam" "Steam gaming platform" off \
|
||||
"vesktop" "Vesktop Discord + Vencord theme" off \
|
||||
"spotify" "Spotify launcher + Spicetify theming" off \
|
||||
"prism" "PrismLauncher Minecraft launcher (Flatpak)" off \
|
||||
"vintagestory" "Vintage Story survival game (AUR)" off \
|
||||
"localsend" "LocalSend LAN file transfer (AUR)" off \
|
||||
"croc" "croc cross-platform file transfer" off \
|
||||
"onlyoffice" "OnlyOffice office suite (AUR)" off \
|
||||
"wireshark" "Wireshark network packet analyser (GUI)" off \
|
||||
"k8s" "Kubernetes tools kubectl · podman-desktop" off \
|
||||
"docker" "Docker docker · docker-compose" off \
|
||||
"podman" "Podman rootless containers · buildah" off \
|
||||
"cockpit" "Cockpit web UI · machines · podman" off \
|
||||
"ssh-server" "SSH server openssh · key-auth · enabled" off \
|
||||
"freeipa-client" "FreeIPA Client sssd + ipa-client-install + enrollment" off \
|
||||
"freeipa-server" "FreeIPA Server interactive server setup + client gen" off \
|
||||
"freeipa-image" "FreeIPA Image OCI/LXC/Proxmox LXC builder + Keycloak" off \
|
||||
"python" "Python tools pyright · pipx · pynvim" off \
|
||||
"zfs" "ZFS zfs-dkms kernel module" off \
|
||||
"wprs" "WPRS wprs-git (AUR)" off \
|
||||
\
|
||||
"chromium" "Chromium open-source browser (official)" off \
|
||||
"firefox-browser" "Firefox Mozilla browser (official)" off \
|
||||
"zen-browser" "Zen Browser Firefox-based privacy browser (AUR)" off \
|
||||
"nyxt" "Nyxt keyboard-driven browser (AUR)" off \
|
||||
"librewolf" "LibreWolf hardened Firefox fork (AUR)" off \
|
||||
"min-browser" "Min minimal Electron browser (AUR)" off \
|
||||
\
|
||||
"vscodium" "VSCodium telemetry-free VS Code (AUR)" off \
|
||||
"zed-ide" "Zed high-performance Rust IDE (official)" off \
|
||||
"geany" "Geany lightweight IDE + plugins (official)" off \
|
||||
"codeblocks" "Code::Blocks C/C++ IDE (official)" off \
|
||||
"kate" "Kate KDE advanced text editor (official)" off \
|
||||
3>&1 1>&2 2>&3) || SELECTED_APPS=""
|
||||
fi
|
||||
|
||||
# ── Confirmation (interactive mode only) ──────────────────────────────────────
|
||||
if ! $ANSWERFILE_MODE; then
|
||||
SUMMARY=""
|
||||
[[ -n "$HOSTNAME_SET" ]] && SUMMARY+=" ✦ Hostname: $HOSTNAME_SET\n"
|
||||
[[ "$COMPONENTS" == *"pkg"* ]] && SUMMARY+=" ✦ Package managers (yay, nvm, rust)\n"
|
||||
[[ "$COMPONENTS" == *"core"* ]] && SUMMARY+=" ✦ Core packages\n"
|
||||
[[ "$COMPONENTS" == *"svc"* ]] && SUMMARY+=" ✦ Core services\n"
|
||||
[[ "$COMPONENTS" == *"shell"* ]] && SUMMARY+=" ✦ Shell setup\n"
|
||||
[[ "$DE" != "none" ]] && SUMMARY+=" ✦ Desktop environment: $DE\n"
|
||||
|
||||
if [[ -n "$SELECTED_APPS" ]]; then
|
||||
SUMMARY+="\n Applications:\n"
|
||||
[[ "$SELECTED_APPS" == *"ollama"* ]] && SUMMARY+=" ✦ Ollama\n"
|
||||
[[ "$SELECTED_APPS" == *"llama-cpp"* ]] && SUMMARY+=" ✦ llama.cpp\n"
|
||||
[[ "$SELECTED_APPS" == *"open-webui"* ]] && SUMMARY+=" ✦ Open WebUI\n"
|
||||
[[ "$SELECTED_APPS" == *"claude"* ]] && SUMMARY+=" ✦ Claude Code\n"
|
||||
[[ "$SELECTED_APPS" == *"networking-cli"* ]] && SUMMARY+=" ✦ Networking CLI (nmap, nethogs, mitmproxy, httpie)\n"
|
||||
[[ "$SELECTED_APPS" == *"disk-recovery"* ]] && SUMMARY+=" ✦ Disk Recovery (ddrescue, f3)\n"
|
||||
[[ "$SELECTED_APPS" == *"himalaya"* ]] && SUMMARY+=" ✦ Himalaya\n"
|
||||
[[ "$SELECTED_APPS" == *"gnuplot"* ]] && SUMMARY+=" ✦ Gnuplot\n"
|
||||
[[ "$SELECTED_APPS" == *"povray"* ]] && SUMMARY+=" ✦ POV-Ray\n"
|
||||
[[ "$SELECTED_APPS" == *"blender"* ]] && SUMMARY+=" ✦ Blender\n"
|
||||
[[ "$SELECTED_APPS" == *"toot"* ]] && SUMMARY+=" ✦ toot\n"
|
||||
[[ "$SELECTED_APPS" == *"db-clients"* ]] && SUMMARY+=" ✦ DB Clients (pgcli, mycli)\n"
|
||||
[[ "$SELECTED_APPS" == *"mysql"* ]] && SUMMARY+=" ✦ MySQL / MariaDB\n"
|
||||
[[ "$SELECTED_APPS" == *"productivity"* ]] && SUMMARY+=" ✦ Productivity (taskwarrior, watson, jrnl)\n"
|
||||
[[ "$SELECTED_APPS" == *"yt-dlp"* ]] && SUMMARY+=" ✦ yt-dlp\n"
|
||||
[[ "$SELECTED_APPS" == *"sox"* ]] && SUMMARY+=" ✦ SoX\n"
|
||||
[[ "$SELECTED_APPS" == *"imagemagick"* ]] && SUMMARY+=" ✦ ImageMagick\n"
|
||||
[[ "$SELECTED_APPS" == *"ffmpeg"* ]] && SUMMARY+=" ✦ FFmpeg extras\n"
|
||||
[[ "$SELECTED_APPS" == *"localtunnel"* ]] && SUMMARY+=" ✦ LocalTunnel\n"
|
||||
[[ "$SELECTED_APPS" == *"butter"* ]] && SUMMARY+=" ✦ butter (btrfs backup)\n"
|
||||
[[ "$SELECTED_APPS" == *"tlp"* ]] && SUMMARY+=" ✦ TLP\n"
|
||||
[[ "$SELECTED_APPS" == *"steam"* ]] && SUMMARY+=" ✦ Steam\n"
|
||||
[[ "$SELECTED_APPS" == *"vesktop"* ]] && SUMMARY+=" ✦ Vesktop + Vencord theme\n"
|
||||
[[ "$SELECTED_APPS" == *"spotify"* ]] && SUMMARY+=" ✦ Spotify + Spicetify\n"
|
||||
[[ "$SELECTED_APPS" == *"prism"* ]] && SUMMARY+=" ✦ PrismLauncher\n"
|
||||
[[ "$SELECTED_APPS" == *"vintagestory"* ]] && SUMMARY+=" ✦ Vintage Story\n"
|
||||
[[ "$SELECTED_APPS" == *"localsend"* ]] && SUMMARY+=" ✦ LocalSend\n"
|
||||
[[ "$SELECTED_APPS" == *"croc"* ]] && SUMMARY+=" ✦ croc\n"
|
||||
[[ "$SELECTED_APPS" == *"onlyoffice"* ]] && SUMMARY+=" ✦ OnlyOffice\n"
|
||||
[[ "$SELECTED_APPS" == *"wireshark"* ]] && SUMMARY+=" ✦ Wireshark\n"
|
||||
[[ "$SELECTED_APPS" == *"k8s"* ]] && SUMMARY+=" ✦ Kubernetes tools\n"
|
||||
[[ "$SELECTED_APPS" == *"docker"* ]] && SUMMARY+=" ✦ Docker + Compose\n"
|
||||
[[ "$SELECTED_APPS" == *"podman"* ]] && SUMMARY+=" ✦ Podman (rootless) + Buildah\n"
|
||||
[[ "$SELECTED_APPS" == *"cockpit"* ]] && SUMMARY+=" ✦ Cockpit web UI\n"
|
||||
[[ "$SELECTED_APPS" == *"ssh-server"* ]] && SUMMARY+=" ✦ SSH server (openssh, key auth)\n"
|
||||
[[ "$SELECTED_APPS" == *"freeipa-client"* ]] && SUMMARY+=" ✦ FreeIPA Client\n"
|
||||
[[ "$SELECTED_APPS" == *"freeipa-server"* ]] && SUMMARY+=" ✦ FreeIPA Server\n"
|
||||
[[ "$SELECTED_APPS" == *"freeipa-image"* ]] && SUMMARY+=" ✦ FreeIPA Image Builder\n"
|
||||
[[ "$SELECTED_APPS" == *"python"* ]] && SUMMARY+=" ✦ Python tools\n"
|
||||
[[ "$SELECTED_APPS" == *"zfs"* ]] && SUMMARY+=" ✦ ZFS\n"
|
||||
[[ "$SELECTED_APPS" == *"wprs"* ]] && SUMMARY+=" ✦ WPRS\n"
|
||||
[[ "$SELECTED_APPS" == *"chromium"* ]] && SUMMARY+=" ✦ Chromium\n"
|
||||
[[ "$SELECTED_APPS" == *"firefox-browser"* ]] && SUMMARY+=" ✦ Firefox\n"
|
||||
[[ "$SELECTED_APPS" == *"zen-browser"* ]] && SUMMARY+=" ✦ Zen Browser\n"
|
||||
[[ "$SELECTED_APPS" == *"nyxt"* ]] && SUMMARY+=" ✦ Nyxt\n"
|
||||
[[ "$SELECTED_APPS" == *"librewolf"* ]] && SUMMARY+=" ✦ LibreWolf\n"
|
||||
[[ "$SELECTED_APPS" == *"min-browser"* ]] && SUMMARY+=" ✦ Min Browser\n"
|
||||
[[ "$SELECTED_APPS" == *"vscodium"* ]] && SUMMARY+=" ✦ VSCodium\n"
|
||||
[[ "$SELECTED_APPS" == *"zed-ide"* ]] && SUMMARY+=" ✦ Zed IDE\n"
|
||||
[[ "$SELECTED_APPS" == *"geany"* ]] && SUMMARY+=" ✦ Geany\n"
|
||||
[[ "$SELECTED_APPS" == *"codeblocks"* ]] && SUMMARY+=" ✦ Code::Blocks\n"
|
||||
[[ "$SELECTED_APPS" == *"kate"* ]] && SUMMARY+=" ✦ Kate\n"
|
||||
fi
|
||||
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " Confirm Installation " \
|
||||
--yesno "\n Components to install:\n\n${SUMMARY}\n Log: $LOG\n\n Proceed?" \
|
||||
24 62 || { clear; echo "Aborted."; exit 0; }
|
||||
fi
|
||||
|
||||
count_steps "$COMPONENTS" "$DE" "$SELECTED_APPS"
|
||||
|
||||
|
|
@ -410,10 +504,85 @@ fi
|
|||
[[ "$SELECTED_APPS" == *"codeblocks"* ]] && run_module "Code::Blocks" "$APPS/codeblocks.sh"
|
||||
[[ "$SELECTED_APPS" == *"kate"* ]] && run_module "Kate" "$APPS/kate.sh"
|
||||
|
||||
# ── Done ──────────────────────────────────────────────────────────────────────
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " Done " \
|
||||
--msgbox "\n All selected components installed.\n\n Log: $LOG\n\n A reboot may be required for all changes to take effect.\n" 12 58
|
||||
# ── Colorway (final step) ─────────────────────────────────────────────────────
|
||||
# Read defaults from repo colors.conf for pre-population
|
||||
declare -A _cdef
|
||||
if [[ -f "$DOTFILES_DIR/colors.conf" ]]; then
|
||||
while IFS='=' read -r k v; do
|
||||
k="${k%%[[:space:]]*}"
|
||||
[[ "$k" =~ ^[[:space:]]*# || -z "$k" ]] && continue
|
||||
v="${v%%#*}"; v="${v//[[:space:]]/}"; v="${v^^}"
|
||||
_cdef[$k]="$v"
|
||||
done < "$DOTFILES_DIR/colors.conf"
|
||||
fi
|
||||
DEF_TEXT="${_cdef[COLOR_TEXT]:-D6ABAB}"
|
||||
DEF_BG="${_cdef[COLOR_BG]:-1A1A1A}"
|
||||
DEF_HIGHLIGHT="${_cdef[COLOR_HIGHLIGHT]:-E40046}"
|
||||
DEF_DARK="${_cdef[COLOR_DARK]:-5018DD}"
|
||||
DEF_RED="${_cdef[COLOR_RED]:-F50505}"
|
||||
|
||||
clear
|
||||
printf "\n Done. Log: %s\n\n" "$LOG"
|
||||
_write_colors_conf() {
|
||||
local out="$1" t="$2" b="$3" h="$4" d="$5" r="$6"
|
||||
printf 'COLOR_TEXT=%s\nCOLOR_BG=%s\nCOLOR_HIGHLIGHT=%s\nCOLOR_DARK=%s\nCOLOR_RED=%s\n' \
|
||||
"${t^^}" "${b^^}" "${h^^}" "${d^^}" "${r^^}" > "$out"
|
||||
}
|
||||
|
||||
if $ANSWERFILE_MODE; then
|
||||
# Apply colors from answerfile if any are set
|
||||
if [[ -n "$AF_COLOR_TEXT$AF_COLOR_BG$AF_COLOR_HIGHLIGHT$AF_COLOR_DARK$AF_COLOR_RED" ]]; then
|
||||
TMP_COLORS="$TMP_D/colors.conf"
|
||||
_write_colors_conf "$TMP_COLORS" \
|
||||
"${AF_COLOR_TEXT:-$DEF_TEXT}" \
|
||||
"${AF_COLOR_BG:-$DEF_BG}" \
|
||||
"${AF_COLOR_HIGHLIGHT:-$DEF_HIGHLIGHT}" \
|
||||
"${AF_COLOR_DARK:-$DEF_DARK}" \
|
||||
"${AF_COLOR_RED:-$DEF_RED}"
|
||||
printf "Applying colorway from answerfile...\n" | tee -a "$LOG"
|
||||
bash "$DOTFILES_DIR/apply-theme.sh" "$TMP_COLORS" 2>&1 | tee -a "$LOG" || true
|
||||
fi
|
||||
else
|
||||
# Interactive: show color form dialog
|
||||
COLORWAY_RAW=$(dialog --backtitle "$BACKTITLE" \
|
||||
--title " Colorway (optional) " \
|
||||
--form "\n Customize theme colors — bare 6-digit hex, no #.\n Leave unchanged to skip colorway setup.\n" \
|
||||
16 62 5 \
|
||||
"COLOR_TEXT " 1 1 "$DEF_TEXT" 1 18 10 7 \
|
||||
"COLOR_BG " 2 1 "$DEF_BG" 2 18 10 7 \
|
||||
"COLOR_HIGHLIGHT " 3 1 "$DEF_HIGHLIGHT" 3 18 10 7 \
|
||||
"COLOR_DARK " 4 1 "$DEF_DARK" 4 18 10 7 \
|
||||
"COLOR_RED " 5 1 "$DEF_RED" 5 18 10 7 \
|
||||
3>&1 1>&2 2>&3) || COLORWAY_RAW=""
|
||||
|
||||
if [[ -n "$COLORWAY_RAW" ]]; then
|
||||
mapfile -t _cv <<< "$COLORWAY_RAW"
|
||||
N_TEXT="${_cv[0]:-$DEF_TEXT}"
|
||||
N_BG="${_cv[1]:-$DEF_BG}"
|
||||
N_HIGHLIGHT="${_cv[2]:-$DEF_HIGHLIGHT}"
|
||||
N_DARK="${_cv[3]:-$DEF_DARK}"
|
||||
N_RED="${_cv[4]:-$DEF_RED}"
|
||||
|
||||
if [[ "${N_TEXT^^}" != "$DEF_TEXT" || \
|
||||
"${N_BG^^}" != "$DEF_BG" || \
|
||||
"${N_HIGHLIGHT^^}" != "$DEF_HIGHLIGHT" || \
|
||||
"${N_DARK^^}" != "$DEF_DARK" || \
|
||||
"${N_RED^^}" != "$DEF_RED" ]]; then
|
||||
TMP_COLORS="$TMP_D/colors.conf"
|
||||
_write_colors_conf "$TMP_COLORS" "$N_TEXT" "$N_BG" "$N_HIGHLIGHT" "$N_DARK" "$N_RED"
|
||||
clear
|
||||
printf "\n\033[1;35m Applying colorway...\033[0m\n\n"
|
||||
bash "$DOTFILES_DIR/apply-theme.sh" "$TMP_COLORS" 2>&1 | tee -a "$LOG" || true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Done ──────────────────────────────────────────────────────────────────────
|
||||
if $ANSWERFILE_MODE; then
|
||||
printf "\nDone. Log: %s\n" "$LOG"
|
||||
else
|
||||
dialog --backtitle "$BACKTITLE" \
|
||||
--title " Done " \
|
||||
--msgbox "\n All selected components installed.\n\n Log: $LOG\n\n A reboot may be required for all changes to take effect.\n" 12 58
|
||||
|
||||
clear
|
||||
printf "\n Done. Log: %s\n\n" "$LOG"
|
||||
fi
|
||||
|
|
|
|||
Loading…
Reference in New Issue