9 Commits
Author SHA1 Message Date
alvinlollo f6f00018d4 feat(iso): Point end4-pC scripts at fork and set its default wallpaper
Upstream pin check / quickshell-pin (push) Successful in 34s
Tests / tests (push) Successful in 3m15s
The fork's colour/wallpaper/kvantum scripts still hard-code
QUICKSHELL_CONFIG_NAME="ii", so they read and write
~/.config/quickshell/ii instead of end4-pC - rewrite them to end4-pC
at build time (verified against the current fork HEAD: 5 scripts match
the anchored pattern, re-runs are no-ops).

Also replace the fork's assets/images/default_wallpaper.png - fed to
switchwall.sh by FirstRunExperience.qml on first login - with the
wallpaper now shipped in skel's ~/Pictures/Wallpapers, so the first
login lands on the japan-street wallpaper instead of the fork's
default.
2026-10-15 15:47:23 +11:00
alvinlollo e377f9313a feat(installer): Offer repair chroot on post-pacstrap failure
Tests / tests (push) Successful in 3m5s
- offer_chroot(): optional arch-chroot shell into the still-mounted target,
  from install_cleanup() on success and from the EXIT trap when a step fails
  after pacstrap (TARGET_INSTALLED flag); temp NOPASSWD sudoers drop-ins are
  always removed before the shell is opened.
- INT/TERM handler re-exits with 128+N: without it bash runs the EXIT trap
  with $? = 0 for a signal-killed shell and the rc != 0 test would skip the
  offer. A signal during teardown only aborts the prompt (first) so the
  unmount still runs; a second forces the exit.
- cleanup(): report a failed umount -R / cryptsetup close instead of hiding
  stderr, so a repair shell leaving busy mounts is visible before reboot.
- Document the flow in AGENTS.md.
2026-10-14 23:08:18 +11:00
alvinlollo 00c89f23f0 feat(installer): Skip login manager when unl0kr handles the passphrase
Tests / tests (push) Successful in 2m56s
unl0kr already prompts for the LUKS passphrase on screen, so a login
manager on top of it would be a second prompt. The login screen now only
collects a fallback (none/greetd/sddm/gdm) when unl0kr is selected;
resolve_login_manager applies it only when install_unl0kr falls back to
the systemd initramfs, and the summary shows both states.

Also ships /etc/unl0kr.conf (breezy-dark) plus the empty
/usr/share/unl0kr/unl0kr.conf the package omits so unl0kr stops warning
about an unparseable config, and pins Hyprland in the user's
AccountsService record so gdm autologin does not fall back to
gnome.desktop.
2026-10-13 19:33:57 +11:00
alvinlollo 3b1e7d1982 chore: Add pre-commit hook running shellcheck on every commit
Tests / tests (push) Successful in 3m11s
2026-10-13 18:07:05 +11:00
alvinlollo 6a708c0303 feat(installer): Add warn-only post-install update step 2026-10-13 17:49:05 +11:00
alvinlollo 0cdaa6f82d fix(iso): Put [cachyos] ahead of [core] in live pacman.conf 2026-10-13 17:48:59 +11:00
alvinlollo 5735859179 chore(installer): Reformat alvin-install (shfmt, 2-space indent) 2026-10-13 17:48:42 +11:00
alvinlollo e57143c4bc docs/ci: implement yay 13 SIGSEGV workaround and update docs
Upstream pin check / quickshell-pin (push) Successful in 28s
Tests / tests (push) Successful in 3m31s
2026-10-12 12:57:37 +11:00
alvinlollo 3275ec9845 chore: add build runner CAP_SETFCAP check, fix MicroTeX compilation, and fix mono cert-sync
Upstream pin check / quickshell-pin (push) Successful in 18s
Tests / tests (push) Successful in 2m55s
2026-10-11 19:41:08 +11:00
9 changed files with 2200 additions and 1753 deletions
+17
View File
@@ -162,6 +162,23 @@ jobs:
rmdir "$d" rmdir "$d"
trap - EXIT trap - EXIT
- name: Require CAP_SETFCAP for file capabilities
run: |
set -euo pipefail
# Without CAP_SETFCAP, pacstrap cannot restore security.capability
# xattrs ("unable to set CAP_SETFCAP effective capability"), so the
# live ISO silently ships newuidmap/newgidmap, snap-confine, dumpcap,
# kwin_wayland, ... without their capabilities. Fix the runner
# (e.g. allow CAP_SETFCAP in the gitea-runner service) rather than
# building a broken image.
eff="$(awk '/^CapEff:/{print $2}' /proc/self/status)"
if (( (16#$eff >> 31) & 1 )); then
echo "✅ CAP_SETFCAP is available (CapEff=$eff)"
else
echo "❌ CAP_SETFCAP missing (CapEff=$eff) - file capabilities would be dropped from the ISO" >&2
exit 1
fi
- name: Build Arch ISO - name: Build Arch ISO
run: | run: |
set -euo pipefail set -euo pipefail
+18 -3
View File
@@ -53,7 +53,7 @@ install them into the target without the network.
| `pacman.conf` | Repos: cachyos → core → extra → multilib → chaotic-aur → local-aur | | `pacman.conf` | Repos: cachyos → core → extra → multilib → chaotic-aur → local-aur |
| `bootstrap_packages` | Minimal build-time packages (`base`, `arch-install-scripts`) | | `bootstrap_packages` | Minimal build-time packages (`base`, `arch-install-scripts`) |
| `airootfs/root/customize_airootfs.sh` | Runs inside chroot during build: keyrings, skel dotfiles (incl. end4-pC), qsConfig patch, end-4 `install-deps`, package harvest, uv/font pre-warm | | `airootfs/root/customize_airootfs.sh` | Runs inside chroot during build: keyrings, skel dotfiles (incl. end4-pC), qsConfig patch, end-4 `install-deps`, package harvest, uv/font pre-warm |
| `airootfs/root/.live-pacman.conf` | `/etc/pacman.conf` of the built image (adds `[local-aur]` = `file:///opt/localrepo`) | | `airootfs/root/.live-pacman.conf` | `/etc/pacman.conf` of the built image (adds `[local-aur]` = `file:///opt/localrepo`); also what pacstrap resolves the target from, so `[cachyos]` must stay ahead of `[core]` like in `pacman.conf` |
| `airootfs/usr/local/bin/alvin-install` | Installer: partitions, pacstrap, LUKS (systemd initramfs **or** experimental unl0kr + optional TPM2 unlock), user (fish shell), git identity + GitHub SSH keys, login manager (none/greetd/sddm/gdm) + autologin, optional sshd (default on), end-4 dotfiles (`--skip-alldeps`), boot | | `airootfs/usr/local/bin/alvin-install` | Installer: partitions, pacstrap, LUKS (systemd initramfs **or** experimental unl0kr + optional TPM2 unlock), user (fish shell), git identity + GitHub SSH keys, login manager (none/greetd/sddm/gdm) + autologin, optional sshd (default on), end-4 dotfiles (`--skip-alldeps`), boot |
| `localrepo/build-end4-deps.sh` | Non-interactive AUR builds for end-4 dependencies | | `localrepo/build-end4-deps.sh` | Non-interactive AUR builds for end-4 dependencies |
| `localrepo/update.sh` | Regenerates localrepo DB, syncs names to packages.x86_64, syncs `airootfs/opt/localrepo/` | | `localrepo/update.sh` | Regenerates localrepo DB, syncs names to packages.x86_64, syncs `airootfs/opt/localrepo/` |
@@ -74,7 +74,7 @@ The airootfs overlay ships it as `/usr/local/bin/alvin-install` (mode `0755` for
locale, login manager, sshd, summary) then a fixed order of `install_*` steps locale, login manager, sshd, summary) then a fixed order of `install_*` steps
(partition → LUKS → format/mount → pacstrap → fstab → repos → timezone → locale → (partition → LUKS → format/mount → pacstrap → fstab → repos → timezone → locale →
hostname → network → sshd → user → git/SSH → end-4 dots → mkinitcpio → bootloader → hostname → network → sshd → user → git/SSH → end-4 dots → mkinitcpio → bootloader →
TPM/unl0kr → login manager → autologin → cleanup). Screens and install steps are TPM/unl0kr → login manager (none if unl0kr succeeded; with unl0kr selected the login screen only asks for a fallback) → autologin → update (reflector + keyrings + `pacman -Syu`, warn-only) → cleanup, which offers an optional `arch-chroot` shell into the target before unmounting; the `EXIT` trap offers the same shell when a step after pacstrap fails or the install is interrupted (INT/TERM re-exit with 128+N so signals count as failure). Screens and install steps are
independent — edit one without independent — edit one without
touching the other. An `EXIT` trap unmounts `$TARGET` and closes `cryptroot` on any exit. touching the other. An `EXIT` trap unmounts `$TARGET` and closes `cryptroot` on any exit.
- **Package list**: `install_pacstrap` builds the *installed* system's list from the medium's - **Package list**: `install_pacstrap` builds the *installed* system's list from the medium's
@@ -97,6 +97,16 @@ yamllint -d "{extends: relaxed, rules: {document-start: disable, line-length: di
`packages.x86_64` syntax: one package per line, `[a-zA-Z0-9._@+\-]+` only, no trailing `packages.x86_64` syntax: one package per line, `[a-zA-Z0-9._@+\-]+` only, no trailing
whitespace. whitespace.
A **pre-commit hook** (`githooks/pre-commit`) runs the same ShellCheck pass on every
commit, so CI failures surface before you push. Enable it once per clone:
```bash
git config core.hooksPath githooks
```
It skips linting (with a warning) when `shellcheck` is missing rather than blocking the
commit; CI remains the enforcer. Bypass it with `git commit --no-verify`.
## Gotchas ## Gotchas
- The profile root is the **repo root** (not a `releng/` subdirectory). CI builds with `mkarchiso -v -w "$PWD/work" -o "$PWD/out" -- "$PWD"` (`.gitea/workflows/build-iso.yaml`), and the local build command above does the same — never pass a `releng` path. - The profile root is the **repo root** (not a `releng/` subdirectory). CI builds with `mkarchiso -v -w "$PWD/work" -o "$PWD/out" -- "$PWD"` (`.gitea/workflows/build-iso.yaml`), and the local build command above does the same — never pass a `releng` path.
@@ -106,10 +116,15 @@ whitespace.
- The **image** ships its own `[local-aur]` at `file:///opt/localrepo` (see `.live-pacman.conf`). It holds `localrepo/` (copied by CI/`update.sh`) plus everything `install-deps` built. pacstrap runs without `-P`, so the target keeps the stock `/etc/pacman.conf`; `alvin-install` strips `[local-aur]` from it anyway as a safety net, because `/opt/localrepo` exists only on the medium. - The **image** ships its own `[local-aur]` at `file:///opt/localrepo` (see `.live-pacman.conf`). It holds `localrepo/` (copied by CI/`update.sh`) plus everything `install-deps` built. pacstrap runs without `-P`, so the target keeps the stock `/etc/pacman.conf`; `alvin-install` strips `[local-aur]` from it anyway as a safety net, because `/opt/localrepo` exists only on the medium.
- `alvin-install` runs end-4 with `--skip-alldeps` — dependencies must come from the ISO. It feeds `yes i` to end-4's failure prompt: inside the installer chroot the medium's `/run` is bind-mounted in, so end-4's `systemctl --machine=…@.host --user enable … --now` lines can never work (and `--now` would start services on the live medium). `install_end4_dots` re-applies the enable-only part (bluetooth, ydotool, i2c module) outside the chroot and reports every step end-4 had to skip. If the end-4 step reports "incomplete", check that `hyprctl`/`quickshell`/the venv were resolvable (harvest) before re-running anything. - `alvin-install` runs end-4 with `--skip-alldeps` — dependencies must come from the ISO. It feeds `yes i` to end-4's failure prompt: inside the installer chroot the medium's `/run` is bind-mounted in, so end-4's `systemctl --machine=…@.host --user enable … --now` lines can never work (and `--now` would start services on the live medium). `install_end4_dots` re-applies the enable-only part (bluetooth, ydotool, i2c module) outside the chroot and reports every step end-4 had to skip. If the end-4 step reports "incomplete", check that `hyprctl`/`quickshell`/the venv were resolvable (harvest) before re-running anything.
- `/etc/skel/.cache/uv` + `/etc/skel/.local/share/uv/python` are pre-warmed by the build (~460 MB: CPython 3.12 and the pinned wheel cache) so end-4's `uv venv`/`uv pip install` at install time needs no network. - `/etc/skel/.cache/uv` + `/etc/skel/.local/share/uv/python` are pre-warmed by the build (~460 MB: CPython 3.12 and the pinned wheel cache) so end-4's `uv venv`/`uv pip install` at install time needs no network.
- **end4-pC** (`pctrade/end4-pC`) is the default quickshell config: cloned into `/etc/skel/.config/quickshell/end4-pC` at build time, selected by patching `hl.env("qsConfig", "ii")` → `"end4-pC"` in the **staged** `/opt/dots-hyprland/dots/.config/hypr/hyprland/variables.lua` (there is no `hypr/hyprland/` in this repo — `./setup install` copies it to the installed system). `alvin-install` re-copies end4-pC into the user's home *after* `./setup install`, because end-4's installer `rsync --delete`s all of `~/.config/quickshell`. - **end4-pC** (`pctrade/end4-pC`) is the default quickshell config: cloned into `/etc/skel/.config/quickshell/end4-pC` at build time, selected by patching `hl.env("qsConfig", "ii")` → `"end4-pC"` in the **staged** `/opt/dots-hyprland/dots/.config/hypr/hyprland/variables.lua` (there is no `hypr/hyprland/` in this repo — `./setup install` copies it to the installed system). `alvin-install` re-copies end4-pC into the user's home *after* `./setup install`, because end-4's installer `rsync --delete`s all of `~/.config/quickshell`. The build also rewrites the fork's hard-coded `QUICKSHELL_CONFIG_NAME="ii"` in its colour/wallpaper/kvantum scripts to `end4-pC` (otherwise they act on `~/.config/quickshell/ii`), and overwrites its `assets/images/default_wallpaper.png` (applied by `FirstRunExperience.qml` on first login) with `airootfs/etc/skel/Pictures/Wallpapers/Dynamic-Wallpapers/Dark/japan-street-Dark.png`.
- `airootfs/etc/skel/.config/illogical-impulse/presets/` carries the user's presets (`Laptop.json`, `Personal-Performance.json`, `Personal-Performance.zip`). They embed `/home/alvin/...` paths, so wallpapers/screenshots need fixing up after install. - `airootfs/etc/skel/.config/illogical-impulse/presets/` carries the user's presets (`Laptop.json`, `Personal-Performance.json`, `Personal-Performance.zip`). They embed `/home/alvin/...` paths, so wallpapers/screenshots need fixing up after install.
- End-4 metapackages are built from `localrepo/end4-pkgbuilds/` (quickshell-git, bibata-modern-classic-bin, microtex-git). - End-4 metapackages are built from `localrepo/end4-pkgbuilds/` (quickshell-git, bibata-modern-classic-bin, microtex-git).
- **quickshell pin / Qt 6.12**: end-4 pins quickshell to `41651d7` (Sep 25), which predates upstream fix `5d5d498` (Oct 3, *"use MOC includes instead of opaque pointer definitions — … Qt 6.12 compatibility"*). Arch shipped qt6 6.12.0 on Oct 8 and the old pin died with `Meta Types must be fully defined` in the moc build of quickshell-hyprland-ipc (failed CI run 137). `customize_airootfs.sh` therefore rewrites end-4's `_commit` to `11ca60be` after cloning — but **only** while it still matches `^_commit='41651d7`, so once end-4 bumps their own pin the guard goes inert and theirs wins. `localrepo/check-quickshell-pin.sh` (weekly `pin-check.yaml` job) fails CI if either pin goes stale or loses the fix; it warns instead of failing when end-4's known-stale pin is covered by the override. - **quickshell pin / Qt 6.12**: end-4 pins quickshell to `41651d7` (Sep 25), which predates upstream fix `5d5d498` (Oct 3, *"use MOC includes instead of opaque pointer definitions — … Qt 6.12 compatibility"*). Arch shipped qt6 6.12.0 on Oct 8 and the old pin died with `Meta Types must be fully defined` in the moc build of quickshell-hyprland-ipc (failed CI run 137). `customize_airootfs.sh` therefore rewrites end-4's `_commit` to `11ca60be` after cloning — but **only** while it still matches `^_commit='41651d7`, so once end-4 bumps their own pin the guard goes inert and theirs wins. `localrepo/check-quickshell-pin.sh` (weekly `pin-check.yaml` job) fails CI if either pin goes stale or loses the fix; it warns instead of failing when end-4's known-stale pin is covered by the override.
- **MicroTeX flags**: the image's `makepkg.conf` is CachyOS's (`-march=native -O3`, LTO). GCC 16.2 hit an internal compiler error (segfault) on MicroTeX's `gtkmm_main.cpp` with it (failed CI run 155; not reproducible locally), so `customize_airootfs.sh` patches end-4's `illogical-impulse-microtex-git` PKGBUILD to `options=(!lto)` + `-O2` (our `localrepo/end4-pkgbuilds/microtex-git` carries the same).
- **CAP_SETFCAP on the runner**: without it pacstrap drops `security.capability` xattrs (newuidmap/newgidmap, snap-confine, dumpcap, kwin_wayland, ...) from the live ISO with only a warning. `build-iso.yaml` fails the job early if the runner lacks it — fix the runner, don't remove the check.
- **yay SIGSEGV on exit**: yay 13 (libalpm via purego/`dyalpm`) can segfault in `alpm_release()` *after* a finished transaction (failed CI run at end-4's `illogical-impulse-kde` step), which aborted `install-deps`. `customize_airootfs.sh` puts a temporary `yay` shim first on the builder's `PATH`: a failed plain `yay -S <pkgs>` counts as success only if `pacman -T` confirms every target is installed (it logs `[yay-shim]`). Every other failure keeps yay's exit code.
- **gvfs / dead `~/.gvfs`**: `install_end4_dots` replays end-4's gsettings writes via `dbus-run-session` as the target user. A session bus D-Bus-activates gvfsd → gvfsd-fuse mounts `~/.gvfs` and dies with the bus, leaving a dead FUSE mount that root can't stat, so the home's catch-all `chown -R` failed (`cannot access '/mnt/home/<user>/.gvfs': Permission denied`) and aborted the install before the bootloader. The replay therefore runs with `GIO_USE_VFS=local GVFS_DISABLE_FUSE=1`, and the function unmounts anything mounted under the home before that chown — keep both.
- mono's `cert-sync` runs before `ca-certificates.crt` exists during pacstrap, so `customize_airootfs.sh` (live) and `install_pacstrap` (target) re-run it.
- `/etc/skel/.zshrc` is owned by `grml-zsh-config`, so ours is staged as `airootfs/root/.zshrc-skel` and installed by `customize_airootfs.sh` after pacstrap — never add files under `airootfs/` at paths a package owns, or pacstrap aborts with "exists in filesystem". - `/etc/skel/.zshrc` is owned by `grml-zsh-config`, so ours is staged as `airootfs/root/.zshrc-skel` and installed by `customize_airootfs.sh` after pacstrap — never add files under `airootfs/` at paths a package owns, or pacstrap aborts with "exists in filesystem".
- `packages.x86_64` is sorted alphabetically. Keep it sorted when adding/removing entries. - `packages.x86_64` is sorted alphabetically. Keep it sorted when adding/removing entries.
- `airootfs/opt/localrepo/` is a build artifact (gitignored) — never commit it. - `airootfs/opt/localrepo/` is a build artifact (gitignored) — never commit it.
+15 -1
View File
@@ -384,11 +384,25 @@ sudo mkarchiso -v -w work -o out/ .
### CI Checks (must pass before merge) ### CI Checks (must pass before merge)
- **ShellCheck**: all `.sh` files checked with `shellcheck -x -s bash` - **ShellCheck**: all `.sh` files, plus extensionless bash scripts in `airootfs/usr/local/bin/`, checked with `shellcheck -x -s bash`
- **yamllint**: all `.yml`/`.yaml` files (relaxed config) - **yamllint**: all `.yml`/`.yaml` files (relaxed config)
- **packages.x86_64 syntax**: one package per line, no trailing whitespace, `[a-zA-Z0-9._@+\-]+` only - **packages.x86_64 syntax**: one package per line, no trailing whitespace, `[a-zA-Z0-9._@+\-]+` only
- **Package repo check**: every package in `packages.x86_64` must be resolvable via `pacman -Si` in allowed repos (`cachyos`, `extra`, `multilib`, `chaotic-aur`), OR have a pre-built `.pkg.tar.zst` in `localrepo/`, OR appear in `localrepo/build-end4-deps.sh` - **Package repo check**: every package in `packages.x86_64` must be resolvable via `pacman -Si` in allowed repos (`cachyos`, `extra`, `multilib`, `chaotic-aur`), OR have a pre-built `.pkg.tar.zst` in `localrepo/`, OR appear in `localrepo/build-end4-deps.sh`
### Git Hooks
A pre-commit hook (`githooks/pre-commit`) runs the ShellCheck pass on every commit, so lint failures surface before you push instead of in CI.
```bash
# Enable once per clone
git config core.hooksPath githooks
# Bypass the hook for a single commit
git commit --no-verify
```
If `shellcheck` is not installed the hook warns and skips rather than blocking the commit; CI remains the enforcer.
### Recommended VirtualBox Settings (for testing) ### Recommended VirtualBox Settings (for testing)
- RAM: 4GB minimum, 8GB recommended - RAM: 4GB minimum, 8GB recommended
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 MiB

+8 -4
View File
@@ -33,6 +33,14 @@ LocalFileSigLevel = Optional
# - URLs will have $arch replaced by the name of the architecture # - URLs will have $arch replaced by the name of the architecture
# #
# [cachyos] first, same as the build's pacman.conf: pacstrap installs the
# target from this file, so with [core] ahead the target got Arch builds of
# everything cachyos also ships (pacman -2 vs the live -4 -> libalpm
# "unknown key '%INSTALLED_DB%'" warnings) and flipped them on the first -Syu.
[cachyos]
SigLevel = Required DatabaseOptional
Include = /etc/pacman.d/cachyos-mirrorlist
[core] [core]
Include = /etc/pacman.d/mirrorlist Include = /etc/pacman.d/mirrorlist
@@ -42,10 +50,6 @@ Include = /etc/pacman.d/mirrorlist
[multilib] [multilib]
Include = /etc/pacman.d/mirrorlist Include = /etc/pacman.d/mirrorlist
[cachyos]
SigLevel = Required DatabaseOptional
Include = /etc/pacman.d/cachyos-mirrorlist
[chaotic-aur] [chaotic-aur]
SigLevel = Required DatabaseOptional SigLevel = Required DatabaseOptional
Include = /etc/pacman.d/chaotic-mirrorlist Include = /etc/pacman.d/chaotic-mirrorlist
+82 -2
View File
@@ -113,6 +113,18 @@ if [[ -f "${QS_PKG}" ]] && grep -q "^_commit='41651d7" "${QS_PKG}"; then
sed -i "s|^_commit='41651d7[^']*'|_commit='11ca60be22b063478ed9586ca1d7f92f0f261caf'|" "${QS_PKG}" sed -i "s|^_commit='41651d7[^']*'|_commit='11ca60be22b063478ed9586ca1d7f92f0f261caf'|" "${QS_PKG}"
fi fi
# GCC 16.2 ICEs (segfault in ggc_set_mark) compiling MicroTeX's gtkmm_main.cpp
# - the LaTeX binary the package ships - under CachyOS's makepkg.conf
# (-march=native -O3 + -flto=auto), which killed CI run 155. Build it with -O2
# and no LTO; MicroTeX is tiny, the flags buy nothing. Re-running is a no-op.
MTX_PKG="${DOTS_REPO}/sdata/dist-arch/illogical-impulse-microtex-git/PKGBUILD"
if [[ -f "${MTX_PKG}" ]] && ! grep -q '^options=(!lto)' "${MTX_PKG}"; then
echo "[customize] Building MicroTeX with -O2 and no LTO (GCC 16 ICE)..."
# shellcheck disable=SC2016 # expanded by makepkg, not here
sed -i '/^build() {/a\ export CFLAGS="${CFLAGS//-O3/-O2}" CXXFLAGS="${CXXFLAGS//-O3/-O2}"' "${MTX_PKG}"
printf '\noptions=(!lto)\n' >> "${MTX_PKG}"
fi
# ── end4-pC: the default quickshell config, shipped in skel ────────────────── # ── end4-pC: the default quickshell config, shipped in skel ──────────────────
# pctrade/end4-pC is a fork of the ii shell config. It lives next to ii in # pctrade/end4-pC is a fork of the ii shell config. It lives next to ii in
# ~/.config/quickshell and does not modify it; which one runs is decided by # ~/.config/quickshell and does not modify it; which one runs is decided by
@@ -122,6 +134,29 @@ if [[ ! -d "${SKEL}/.config/quickshell/end4-pC" ]]; then
git clone --depth=1 https://github.com/pctrade/end4-pC.git \ git clone --depth=1 https://github.com/pctrade/end4-pC.git \
"${SKEL}/.config/quickshell/end4-pC" "${SKEL}/.config/quickshell/end4-pC"
fi fi
# The fork's colour/wallpaper/kvantum scripts still hard-code
# QUICKSHELL_CONFIG_NAME="ii", so they'd read and write ~/.config/quickshell/ii
# instead of end4-pC. Point them at the fork. Re-running is a no-op.
mapfile -t PC_II_SCRIPTS < <(grep -rl --exclude-dir=.git \
'^QUICKSHELL_CONFIG_NAME="ii"$' "${SKEL}/.config/quickshell/end4-pC" || true)
if (( ${#PC_II_SCRIPTS[@]} )); then
echo "[customize] Pointing ${#PC_II_SCRIPTS[@]} end4-pC scripts at end4-pC..."
sed -i 's/^QUICKSHELL_CONFIG_NAME="ii"$/QUICKSHELL_CONFIG_NAME="end4-pC"/' \
"${PC_II_SCRIPTS[@]}"
fi
# Default wallpaper: end4-pC's first run (services/FirstRunExperience.qml)
# feeds assets/images/default_wallpaper.png to switchwall.sh, so replace that
# image with the one shipped in skel's ~/Pictures/Wallpapers.
DEFAULT_WALL="${SKEL}/Pictures/Wallpapers/Dynamic-Wallpapers/Dark/japan-street-Dark.png"
PC_DEFAULT_WALL="${SKEL}/.config/quickshell/end4-pC/assets/images/default_wallpaper.png"
if [[ ! -f "${DEFAULT_WALL}" ]]; then
echo "[customize] Default wallpaper ${DEFAULT_WALL} is missing" >&2
exit 1
fi
if ! cmp -s "${DEFAULT_WALL}" "${PC_DEFAULT_WALL}"; then
echo "[customize] Setting end4-pC default wallpaper to ${DEFAULT_WALL##*/}..."
install -Dm644 "${DEFAULT_WALL}" "${PC_DEFAULT_WALL}"
fi
# ── Default quickshell config: ii -> end4-pC ───────────────────────────────── # ── Default quickshell config: ii -> end4-pC ─────────────────────────────────
# alvin-install copies this staged repo onto the target and runs `./setup # alvin-install copies this staged repo onto the target and runs `./setup
@@ -160,6 +195,36 @@ chmod 440 /etc/sudoers.d/99-ii-builder
# so the tree must be owned by the account that runs it. # so the tree must be owned by the account that runs it.
chown -R "${BUILD_USER}:${BUILD_USER}" "${DOTS_REPO}" chown -R "${BUILD_USER}:${BUILD_USER}" "${DOTS_REPO}"
# yay 13 (libalpm via purego/dyalpm) can SIGSEGV in alpm_release() at exit,
# *after* the transaction and its hooks completed (failed CI run: crash in
# dyalpm handle.Release). end-4 sees exit 2 and aborts. This shim, put first
# on PATH for the install-deps run only, turns a failed plain `yay -S <pkgs>`
# into success when every target is installed afterwards (pacman -T); any
# other failure keeps yay's exit code. It lives in /tmp and is removed after.
YAY_SHIM_DIR="$(mktemp -d /tmp/ii-yay-shim.XXXXXX)"
cat > "${YAY_SHIM_DIR}/yay" <<'EOF'
#!/bin/bash
"${YAY_SHIM_REAL:-/usr/bin/yay}" "$@"
rc=$?
(( rc == 0 )) && exit 0
sync=0
targets=()
for a in "$@"; do
case "$a" in
-S|--sync) sync=1 ;;
-S*) exit "$rc" ;; # -Ss/-Si/-Syu/...: not a plain install
-*) ;;
*) targets+=("$a") ;;
esac
done
if (( sync && ${#targets[@]} )) && pacman -T -- "${targets[@]}" >/dev/null; then
echo "[yay-shim] yay exited ${rc}, but all ${#targets[@]} targets are installed - continuing" >&2
exit 0
fi
exit "$rc"
EOF
chmod 755 "${YAY_SHIM_DIR}" "${YAY_SHIM_DIR}/yay"
# -f: never prompt (ask=false); -s: skip "pacman -Syu"; # -f: never prompt (ask=false); -s: skip "pacman -Syu";
# --skip-plasmaintg: optional Firefox widget that pulls 410 pkgs / 526 MiB of # --skip-plasmaintg: optional Firefox widget that pulls 410 pkgs / 526 MiB of
# KDE packages onto a non-KDE system; # KDE packages onto a non-KDE system;
@@ -168,11 +233,14 @@ chown -R "${BUILD_USER}:${BUILD_USER}" "${DOTS_REPO}"
# shellcheck disable=SC2024 # the redirect is meant for the child, not sudo # shellcheck disable=SC2024 # the redirect is meant for the child, not sudo
if ! sudo -u "${BUILD_USER}" env HOME="/home/${BUILD_USER}" \ if ! sudo -u "${BUILD_USER}" env HOME="/home/${BUILD_USER}" \
GIT_TERMINAL_PROMPT=0 \ GIT_TERMINAL_PROMPT=0 \
PATH="${YAY_SHIM_DIR}:/usr/local/sbin:/usr/local/bin:/usr/bin" \
bash -c "cd ${DOTS_REPO} && ./setup install-deps -f -s --skip-plasmaintg" \ bash -c "cd ${DOTS_REPO} && ./setup install-deps -f -s --skip-plasmaintg" \
< <(yes e); then < <(yes e); then
rm -rf "${YAY_SHIM_DIR}"
echo "[customize] end-4 ./setup install-deps failed" >&2 echo "[customize] end-4 ./setup install-deps failed" >&2
exit 1 exit 1
fi fi
rm -rf "${YAY_SHIM_DIR}"
# ── Harvest the packages install-deps just built into the in-image local repo ─ # ── Harvest the packages install-deps just built into the in-image local repo ─
# The live /etc/pacman.conf points [local-aur] at /opt/localrepo; without the # The live /etc/pacman.conf points [local-aur] at /opt/localrepo; without the
@@ -225,11 +293,13 @@ fi
# Rebuild the repo database (CI/update.sh ship a pre-built one alongside the # Rebuild the repo database (CI/update.sh ship a pre-built one alongside the
# packages) and refresh the live system's copy of it. No network needed: the # packages) and refresh the live system's copy of it. No network needed: the
# remote repos were synced during pacstrap. # remote repos were synced during pacstrap. No -R: the loop above already
# deleted the replaced archives, so repo-add's own cleanup only logged a
# spurious "Cannot remove file" ERROR.
local_pkgs=(/opt/localrepo/*.pkg.tar.zst) local_pkgs=(/opt/localrepo/*.pkg.tar.zst)
if ((${#local_pkgs[@]} > 0)); then if ((${#local_pkgs[@]} > 0)); then
echo "[customize] Rebuilding local-aur db (${#local_pkgs[@]} packages)..." echo "[customize] Rebuilding local-aur db (${#local_pkgs[@]} packages)..."
repo-add -R /opt/localrepo/local-aur.db.tar.gz "${local_pkgs[@]}" repo-add /opt/localrepo/local-aur.db.tar.gz "${local_pkgs[@]}"
mkdir -p /var/lib/pacman/sync mkdir -p /var/lib/pacman/sync
cp -fL /opt/localrepo/local-aur.db /var/lib/pacman/sync/local-aur.db cp -fL /opt/localrepo/local-aur.db /var/lib/pacman/sync/local-aur.db
if [[ -f /opt/localrepo/local-aur.files ]]; then if [[ -f /opt/localrepo/local-aur.files ]]; then
@@ -237,6 +307,16 @@ if ((${#local_pkgs[@]} > 0)); then
fi fi
fi fi
# ── mono certificate store ──────────────────────────────────────────────────
# mono's post_install runs cert-sync during pacstrap, before the
# post-transaction hook has generated ca-certificates.crt, so the store ships
# empty ("Couldn't retrieve the file") and mono apps reject every TLS cert.
if command -v cert-sync &>/dev/null; then
echo "[customize] Populating mono certificate store..."
cert-sync --quiet /etc/ssl/certs/ca-certificates.crt \
|| echo "[customize] WARNING: cert-sync failed, mono store left empty" >&2
fi
# ── makepkg leftovers + throwaway account do not belong in the ISO ────────── # ── makepkg leftovers + throwaway account do not belong in the ISO ──────────
if [[ -d "${DOTS_REPO}/sdata/dist-arch" ]]; then if [[ -d "${DOTS_REPO}/sdata/dist-arch" ]]; then
find "${DOTS_REPO}/sdata/dist-arch" -mindepth 2 -maxdepth 2 -type d \ find "${DOTS_REPO}/sdata/dist-arch" -mindepth 2 -maxdepth 2 -type d \
File diff suppressed because it is too large Load Diff
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
# pre-commit hook: run ShellCheck on every shell script in the repo.
#
# This mirrors the CI check in .gitea/workflows/tests.yaml so the local
# lint and the CI lint cannot drift apart. Enable it in a clone with:
#
# git config core.hooksPath githooks
set -u
if ! command -v shellcheck >/dev/null 2>&1; then
echo "pre-commit: shellcheck not found on PATH, skipping lint." >&2
echo "pre-commit: install it with 'pacman -S shellcheck'; CI still enforces it." >&2
exit 0
fi
# Same file set as CI: *.sh plus the extensionless bash scripts in
# airootfs/usr/local/bin picked up by their shebang.
# find's stderr is dropped because localrepo/end4-pkgbuilds/*/pkg is left
# at mode d--x--x--x by makepkg and prints "Permission denied" locally.
sh_files=$(
{
find . -name '*.sh' -not -path './.git/*' 2>/dev/null
find airootfs/usr/local/bin -type f ! -name '*.sh' \
-exec grep -lE '^#!(/usr/bin/env bash|/bin/bash)' {} + 2>/dev/null
} | sed '/^$/d' | sort -u
)
if [ -z "$sh_files" ]; then
echo "pre-commit: no shell scripts found."
exit 0
fi
failed=0
while IFS= read -r f; do
if shellcheck -x -s bash "$f"; then
echo " ok $f"
else
echo " FAIL $f" >&2
failed=1
fi
done <<< "$sh_files"
if [ "$failed" -ne 0 ]; then
echo "" >&2
echo "pre-commit: ShellCheck failed. Fix the issues above, or bypass with:" >&2
echo " git commit --no-verify" >&2
exit 1
fi
exit 0
@@ -18,6 +18,8 @@ depends=(
makedepends=("git" "cmake") makedepends=("git" "cmake")
source=("git+${url}.git") source=("git+${url}.git")
sha256sums=("SKIP") sha256sums=("SKIP")
# -O3 + LTO (CachyOS makepkg.conf) make GCC 16.2 ICE on gtkmm_main.cpp
options=(!lto)
prepare() { prepare() {
cd $_pkgname cd $_pkgname
@@ -26,6 +28,7 @@ prepare() {
} }
build() { build() {
export CFLAGS="${CFLAGS//-O3/-O2}" CXXFLAGS="${CXXFLAGS//-O3/-O2}"
cd $_pkgname cd $_pkgname
cmake -B build -S . -DCMAKE_BUILD_TYPE=None cmake -B build -S . -DCMAKE_BUILD_TYPE=None
cmake --build build cmake --build build