Compare commits
7
Commits
140a1c8f88
...
9a0a6fef00
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a0a6fef00 | ||
|
|
9496559875 | ||
|
|
ddab0d827f | ||
|
|
aacdf8eb4a | ||
|
|
a6c65ca973 | ||
|
|
19fd4b1ed4 | ||
|
|
185d098bdd |
@@ -0,0 +1,57 @@
|
|||||||
|
# XLibre Updater Design
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
Add a repository-managed workflow for updating and building the Stormux XLibre package set in the correct dependency order, while keeping `xlibre-video-dummy-with-vt` manually maintained and reviewed against upstream `xlibre-video-dummy`.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
This design covers:
|
||||||
|
- correcting the package build order in `scripts/upgrade-xlibre.sh`
|
||||||
|
- reviewing and updating `scripts/xlibre-video-dummy-with-vt/PKGBUILD`
|
||||||
|
- regenerating `.SRCINFO` after PKGBUILD changes
|
||||||
|
|
||||||
|
This design does not automate AUR publication or rewrite the downstream package from upstream sources.
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
The updater script currently builds packages in an order that does not match the XLibre dependency chain. `xlibre-video-dummy-with-vt` is also version-skewed relative to the current AUR `xlibre-video-dummy` package and needs a manual rebase of relevant packaging changes.
|
||||||
|
|
||||||
|
## Dependency Order
|
||||||
|
The package build order will be:
|
||||||
|
|
||||||
|
1. `xlibre-xserver-common`
|
||||||
|
2. `xlibre-xserver-devel`
|
||||||
|
3. `xlibre-input-libinput`
|
||||||
|
4. `xlibre-xserver`
|
||||||
|
5. `xlibre-video-fbdev`
|
||||||
|
6. `xlibre-video-dummy-with-vt`
|
||||||
|
|
||||||
|
This order reflects current AUR dependencies: `xlibre-xserver` requires `xlibre-xserver-common` and `xlibre-input-libinput`, while the input and video driver packages require `xlibre-xserver-devel` to build.
|
||||||
|
|
||||||
|
## Downstream Package Policy
|
||||||
|
`xlibre-video-dummy-with-vt` remains a separate, manually maintained package because it has a different maintainer and should not be auto-derived from the upstream AUR package.
|
||||||
|
|
||||||
|
The maintenance rule is:
|
||||||
|
- treat AUR `xlibre-video-dummy` as the packaging baseline
|
||||||
|
- manually port relevant upstream PKGBUILD changes into `xlibre-video-dummy-with-vt`
|
||||||
|
- keep only the intentional downstream delta needed for VT behavior
|
||||||
|
|
||||||
|
## Implementation Shape
|
||||||
|
`scripts/upgrade-xlibre.sh` will continue cloning packages from AUR and building them locally, but with the corrected order.
|
||||||
|
|
||||||
|
`scripts/xlibre-video-dummy-with-vt/PKGBUILD` will be updated to match the current upstream package structure where appropriate:
|
||||||
|
- current version and release
|
||||||
|
- current `depends` and `makedepends`
|
||||||
|
- current build flags and source layout
|
||||||
|
- regenerated checksums
|
||||||
|
|
||||||
|
The VT-specific patch remains the only behavioral divergence.
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
The script should fail fast on clone or build errors and stop rather than continuing with a broken package chain. Because package order is intentional, partial success should be considered incomplete.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
Verification will be task-focused:
|
||||||
|
- confirm the updater script contains the corrected package order
|
||||||
|
- compare the downstream PKGBUILD against current upstream `xlibre-video-dummy`
|
||||||
|
- regenerate and verify `.SRCINFO`
|
||||||
|
- run `shellcheck` on the updater script
|
||||||
|
- run `makepkg --printsrcinfo` in the downstream package directory
|
||||||
Executable
+194
@@ -0,0 +1,194 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
shopt -s nullglob
|
||||||
|
|
||||||
|
# ---- Configurable Variables ----
|
||||||
|
repoDir="/var/www/packages.stormux.org"
|
||||||
|
keyId="52ADA49000F1FF0456F8AEEFB4CDE1CD56EF8E82"
|
||||||
|
repoName="stormux"
|
||||||
|
dbName="${repoName}.db.tar.gz"
|
||||||
|
filesName="${repoName}.files.tar.gz"
|
||||||
|
rebuildDb="${REBUILD_DB:-false}"
|
||||||
|
|
||||||
|
require_cmd() {
|
||||||
|
local cmd="$1"
|
||||||
|
|
||||||
|
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||||
|
echo "❌ Required command not found: $cmd"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- Safety Checks ----
|
||||||
|
if [[ ! -d "$repoDir" ]]; then
|
||||||
|
echo "❌ Repo dir does not exist: $repoDir"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
require_cmd "gpg"
|
||||||
|
require_cmd "repo-add"
|
||||||
|
require_cmd "repo-remove"
|
||||||
|
|
||||||
|
# ---- Create Architecture Directories ----
|
||||||
|
mkdir -p "$repoDir/x86_64" "$repoDir/aarch64"
|
||||||
|
|
||||||
|
# ---- Process Each Architecture ----
|
||||||
|
process_arch() {
|
||||||
|
local arch="$1"
|
||||||
|
local archDir="$repoDir/$arch"
|
||||||
|
local pkgFiles=()
|
||||||
|
local selectedPkgFiles=()
|
||||||
|
local repoAddArgs=()
|
||||||
|
local -A currentPkgNames=()
|
||||||
|
local -A newestPkgByName=()
|
||||||
|
local repoPkgNames=()
|
||||||
|
local dbFile="$dbName"
|
||||||
|
local filesFile="$filesName"
|
||||||
|
local dbSig="${dbFile}.sig"
|
||||||
|
local filesSig="${filesFile}.sig"
|
||||||
|
local dbLink="${repoName}.db"
|
||||||
|
local filesLink="${repoName}.files"
|
||||||
|
|
||||||
|
echo "🏗️ Processing $arch packages..."
|
||||||
|
|
||||||
|
# Enter arch directory (packages should already be sorted by update.sh)
|
||||||
|
cd "$archDir" || return 1
|
||||||
|
|
||||||
|
# Select only the newest archive for each package name. repo-add cannot
|
||||||
|
# safely consume multiple versions of the same package in a single run.
|
||||||
|
pkgFiles=( *.pkg.tar.zst *.pkg.tar.xz )
|
||||||
|
for pkg in "${pkgFiles[@]}"; do
|
||||||
|
local pkgName pkgVersion existingPkg existingVersion
|
||||||
|
pkgName="$(bsdtar -xOqf "$pkg" .PKGINFO | sed -n 's/^pkgname = //p' | head -n1)"
|
||||||
|
pkgVersion="$(bsdtar -xOqf "$pkg" .PKGINFO | sed -n 's/^pkgver = //p' | head -n1)-$(bsdtar -xOqf "$pkg" .PKGINFO | sed -n 's/^pkgrel = //p' | head -n1)"
|
||||||
|
|
||||||
|
if [[ -z "$pkgName" || -z "$pkgVersion" ]]; then
|
||||||
|
echo "❌ Unable to determine package metadata for $pkg"
|
||||||
|
cd "$repoDir" || exit 1
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
existingPkg="${currentPkgNames[$pkgName]:-}"
|
||||||
|
if [[ -z "$existingPkg" ]]; then
|
||||||
|
currentPkgNames["$pkgName"]="$pkg"
|
||||||
|
else
|
||||||
|
existingVersion="$(bsdtar -xOqf "$existingPkg" .PKGINFO | sed -n 's/^pkgver = //p' | head -n1)-$(bsdtar -xOqf "$existingPkg" .PKGINFO | sed -n 's/^pkgrel = //p' | head -n1)"
|
||||||
|
if (( $(vercmp "$pkgVersion" "$existingVersion") > 0 )); then
|
||||||
|
currentPkgNames["$pkgName"]="$pkg"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
for pkgName in "${!currentPkgNames[@]}"; do
|
||||||
|
newestPkgByName["$pkgName"]="${currentPkgNames[$pkgName]}"
|
||||||
|
selectedPkgFiles+=( "${currentPkgNames[$pkgName]}" )
|
||||||
|
done
|
||||||
|
|
||||||
|
pkgFiles=( "${selectedPkgFiles[@]}" )
|
||||||
|
|
||||||
|
# Sign all unsigned selected packages
|
||||||
|
echo "🔏 Signing $arch packages..."
|
||||||
|
for pkg in "${pkgFiles[@]}"; do
|
||||||
|
if [[ ! -f "$pkg.sig" ]]; then
|
||||||
|
echo " 📝 Signing $pkg"
|
||||||
|
gpg --default-key "$keyId" --detach-sign "$pkg"
|
||||||
|
else
|
||||||
|
echo " ✅ $pkg already signed"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Track which package names should remain in the repo after this run.
|
||||||
|
for pkg in "${pkgFiles[@]}"; do
|
||||||
|
local pkgName
|
||||||
|
pkgName="$(bsdtar -xOqf "$pkg" .PKGINFO | sed -n 's/^pkgname = //p' | head -n1)"
|
||||||
|
if [[ -n "$pkgName" ]]; then
|
||||||
|
currentPkgNames["$pkgName"]=1
|
||||||
|
else
|
||||||
|
echo "❌ Unable to determine package name for $pkg"
|
||||||
|
cd "$repoDir" || exit 1
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Rebuild database for this architecture
|
||||||
|
if [[ "$rebuildDb" == "true" ]]; then
|
||||||
|
echo "🗃️ Rebuilding $arch repo database..."
|
||||||
|
rm -f "$dbFile" "$dbSig" "$filesFile" "$filesSig" "$dbLink" "$filesLink"
|
||||||
|
elif [[ -f "$dbFile" ]]; then
|
||||||
|
echo "🗃️ Updating $arch repo database..."
|
||||||
|
else
|
||||||
|
echo "🆕 Creating new $arch repo database..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove packages that still exist in the repo database but are no longer
|
||||||
|
# present in the current directory. repo-remove with --remove also deletes
|
||||||
|
# the matching package archive and detached signature from disk.
|
||||||
|
if [[ -f "$dbFile" ]]; then
|
||||||
|
mapfile -t repoPkgNames < <(
|
||||||
|
bsdtar -tf "$dbFile" |
|
||||||
|
awk -F/ 'NF == 2 && $2 == "desc" {print $1}' |
|
||||||
|
while read -r entry; do
|
||||||
|
bsdtar -xOf "$dbFile" "${entry}/desc" |
|
||||||
|
awk 'found {print; exit} /^%NAME%$/ {found=1}'
|
||||||
|
done |
|
||||||
|
sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
for pkgName in "${repoPkgNames[@]}"; do
|
||||||
|
if [[ -z "${currentPkgNames[$pkgName]:-}" ]]; then
|
||||||
|
echo "🧹 Removing stale repo package $pkgName"
|
||||||
|
repo-remove --sign --key "$keyId" --remove "$dbFile" "$pkgName"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Only run repo-add if there are packages
|
||||||
|
if ((${#pkgFiles[@]} > 0)); then
|
||||||
|
repoAddArgs=(--sign --key "$keyId" --remove)
|
||||||
|
if [[ "$rebuildDb" != "true" && -f "$dbFile" ]]; then
|
||||||
|
repoAddArgs+=(--verify)
|
||||||
|
fi
|
||||||
|
|
||||||
|
repo-add "${repoAddArgs[@]}" "$dbFile" "${pkgFiles[@]}"
|
||||||
|
|
||||||
|
if [[ ! -f "$dbFile" ]]; then
|
||||||
|
echo "❌ repo-add did not create $dbFile"
|
||||||
|
cd "$repoDir" || exit 1
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -e "$dbLink" ]]; then
|
||||||
|
ln -s "$dbFile" "$dbLink"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$filesFile" && ! -e "$filesLink" ]]; then
|
||||||
|
ln -s "$filesFile" "$filesLink"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ $arch repo updated successfully"
|
||||||
|
else
|
||||||
|
echo "ℹ️ No $arch packages found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove orphaned package archives and detached signatures that are not part
|
||||||
|
# of the current package set. This keeps the on-disk repo contents aligned
|
||||||
|
# with the package database after rebuilds and package removals.
|
||||||
|
for pkg in *.pkg.tar.zst *.pkg.tar.xz; do
|
||||||
|
local pkgName
|
||||||
|
pkgName="$(bsdtar -xOqf "$pkg" .PKGINFO | sed -n 's/^pkgname = //p' | head -n1)"
|
||||||
|
if [[ -z "${newestPkgByName[$pkgName]:-}" || "${newestPkgByName[$pkgName]}" != "$pkg" ]]; then
|
||||||
|
echo "🧹 Removing orphaned package file $pkg"
|
||||||
|
rm -f "$pkg" "$pkg.sig"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
cd "$repoDir" || exit
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- Process Both Architectures ----
|
||||||
|
process_arch "x86_64"
|
||||||
|
process_arch "aarch64"
|
||||||
|
|
||||||
|
# ---- Done ----
|
||||||
|
echo "✅ All repositories updated and signed successfully."
|
||||||
Executable
+28
@@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
startDir="$(pwd)"
|
||||||
|
buildDir="${startDir}/xlibre-build"
|
||||||
|
|
||||||
|
packageList=(
|
||||||
|
xlibre-input-libinput
|
||||||
|
xlibre-xserver
|
||||||
|
xlibre-video-amdgpu
|
||||||
|
xlibre-video-ati
|
||||||
|
xlibre-video-fbdev
|
||||||
|
xlibre-video-intel
|
||||||
|
xlibre-video-nouveau
|
||||||
|
xlibre-video-vesa
|
||||||
|
xlibre-video-dummy-with-vt
|
||||||
|
)
|
||||||
|
|
||||||
|
mkdir -p "${buildDir}"
|
||||||
|
|
||||||
|
for i in "${packageList[@]}" ; do
|
||||||
|
yay -Ga "$i"
|
||||||
|
pushd "$i"
|
||||||
|
makepkg -Acrsf
|
||||||
|
cp -v ./*.pkg.tar.* "${buildDir}/"
|
||||||
|
popd
|
||||||
|
done
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
pkgbase = xlibre-video-dummy-with-vt
|
||||||
|
pkgdesc = XLibre dummy video driver with an allocated vt
|
||||||
|
pkgver = 25.0.0
|
||||||
|
pkgrel = 5
|
||||||
|
url = https://github.com/X11Libre/xf86-video-dummy
|
||||||
|
arch = x86_64
|
||||||
|
arch = aarch64
|
||||||
|
groups = xlibre-drivers
|
||||||
|
license = MIT
|
||||||
|
license = X11
|
||||||
|
makedepends = xlibre-xserver-devel>=25.0
|
||||||
|
makedepends = xorgproto
|
||||||
|
depends = xlibre-xserver>=25.0
|
||||||
|
depends = glibc
|
||||||
|
provides = xf86-video-dummy
|
||||||
|
provides = x11win-video-dummy
|
||||||
|
conflicts = xf86-video-dummy
|
||||||
|
source = https://github.com/X11Libre/xf86-video-dummy/archive/refs/tags/xlibre-xf86-video-dummy-25.0.0.tar.gz
|
||||||
|
source = dummy_driver.patch
|
||||||
|
sha256sums = b56e610705cd3d4d86422a11c6b0d93357e4d4749a05178a85fd250301d357b9
|
||||||
|
sha256sums = 68cdcf21e9b54a7fdb8e968292e1ef9ad154ddb1361b141a0a635c2a13c92bfa
|
||||||
|
|
||||||
|
pkgname = xlibre-video-dummy-with-vt
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
*
|
||||||
|
!PKGBUILD
|
||||||
|
!.SRCINFO
|
||||||
|
!.gitignore
|
||||||
|
!.nvchecker.toml
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
[xlibre-video-dummy]
|
||||||
|
source = "git"
|
||||||
|
git = "https://github.com/x11libre/xf86-video-dummy.git"
|
||||||
|
include_regex = "xlibre-xf86-video-dummy-.*"
|
||||||
|
prefix = "xlibre-xf86-video-dummy-"
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Maintainer: Storm Dragon <storm_dragon@linux-a11y.org>
|
||||||
|
|
||||||
|
pkgname=xlibre-video-dummy-with-vt
|
||||||
|
pkgver=25.0.0
|
||||||
|
pkgrel=5
|
||||||
|
pkgdesc="XLibre dummy video driver with an allocated vt"
|
||||||
|
arch=(x86_64 aarch64)
|
||||||
|
_pkgname=xf86-video-dummy
|
||||||
|
url="https://github.com/X11Libre/${_pkgname}"
|
||||||
|
license=('MIT' 'X11')
|
||||||
|
depends=("xlibre-xserver>=${pkgver%.*}" 'glibc')
|
||||||
|
makedepends=("xlibre-xserver-devel>=${pkgver%.*}" 'xorgproto')
|
||||||
|
conflicts=("${_pkgname}")
|
||||||
|
provides=("${_pkgname}" 'x11win-video-dummy')
|
||||||
|
source=("${url}/archive/refs/tags/xlibre-${_pkgname}-${pkgver}.tar.gz"
|
||||||
|
"dummy_driver.patch")
|
||||||
|
groups=('xlibre-drivers')
|
||||||
|
sha256sums=('b56e610705cd3d4d86422a11c6b0d93357e4d4749a05178a85fd250301d357b9'
|
||||||
|
'68cdcf21e9b54a7fdb8e968292e1ef9ad154ddb1361b141a0a635c2a13c92bfa')
|
||||||
|
|
||||||
|
prepare() {
|
||||||
|
cd "${srcdir}/${_pkgname}-xlibre-${_pkgname}-${pkgver}/src"
|
||||||
|
patch -i "${srcdir}/dummy_driver.patch"
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
case "$CARCH" in
|
||||||
|
"x86_64")
|
||||||
|
CFLAGS=" -march=x86-64"
|
||||||
|
;;
|
||||||
|
"aarch64")
|
||||||
|
CFLAGS=" -march=armv8-a"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
CFLAGS=" -march=native"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
CFLAGS+=" -mtune=generic -O2 -pipe -fexceptions -Wp,-D_FORTIFY_SOURCE=3 -Wformat -Werror=format-security"
|
||||||
|
CFLAGS+=" -fstack-clash-protection -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"
|
||||||
|
LDFLAGS=" -Wl,-O1 -Wl,--sort-common -Wl,--as-needed -Wl,-z,lazy -Wl,-z,relro -Wl,-z,pack-relative-relocs"
|
||||||
|
if [[ $CARCH != 'aarch64' ]]; then
|
||||||
|
CFLAGS+=" -fcf-protection"
|
||||||
|
fi
|
||||||
|
CXXFLAGS="${CFLAGS} -Wp,-D_GLIBCXX_ASSERTIONS"
|
||||||
|
export CFLAGS="${CFLAGS}"
|
||||||
|
export CXXFLAGS="${CXXFLAGS}"
|
||||||
|
export LDFLAGS="${LDFLAGS}"
|
||||||
|
|
||||||
|
cd "${srcdir}/${_pkgname}-xlibre-${_pkgname}-${pkgver}"
|
||||||
|
./autogen.sh
|
||||||
|
./configure --prefix=/usr
|
||||||
|
make
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd "${srcdir}/${_pkgname}-xlibre-${_pkgname}-${pkgver}"
|
||||||
|
make DESTDIR="${pkgdir}" install
|
||||||
|
install -Dm644 COPYING "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
--- a/src/dummy_driver.c
|
||||||
|
+++ b/src/dummy_driver.c
|
||||||
|
@@ -1016,10 +1016,10 @@ dummyDriverFunc(ScrnInfoPtr pScrn, xorgDriverFuncOp op, pointer ptr)
|
||||||
|
CARD32 *flag;
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
- case GET_REQUIRED_HW_INTERFACES:
|
||||||
|
- flag = (CARD32*)ptr;
|
||||||
|
- (*flag) = HW_SKIP_CONSOLE;
|
||||||
|
- return TRUE;
|
||||||
|
+ case GET_REQUIRED_HW_INTERFACES:
|
||||||
|
+ flag = (CARD32*)ptr;
|
||||||
|
+ /* Allow the driver to allocate a VT instead of skipping the console. */
|
||||||
|
+ return TRUE;
|
||||||
|
default:
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
# install-stormux: Interactive Arch Linux installer for Stormux
|
# install-stormux: Interactive Arch Linux installer for Stormux
|
||||||
# Supports UEFI/BIOS, multiple partition layouts, and accessibility-first configuration
|
# Supports UEFI/BIOS, multiple partition layouts, and accessibility-first configuration
|
||||||
|
|
||||||
set -euo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
# Global variables
|
# Global variables
|
||||||
logFile="/tmp/install-stormux.log"
|
logFile="/tmp/install-stormux.log"
|
||||||
@@ -11,7 +11,8 @@ mountPoint="/mnt"
|
|||||||
bootMode="" # Will be "uefi" or "bios"
|
bootMode="" # Will be "uefi" or "bios"
|
||||||
targetDisk=""
|
targetDisk=""
|
||||||
homeDisk=""
|
homeDisk=""
|
||||||
partitionLayout="" # "single", "separate_home", "separate_disk"
|
partitionLayout="" # "single", "separate_home", "separate_disk", "manual_mnt"
|
||||||
|
useExistingMount=false # true when using manually mounted filesystems at /mnt
|
||||||
hostname=""
|
hostname=""
|
||||||
rootPassword=""
|
rootPassword=""
|
||||||
declare -a userNames=()
|
declare -a userNames=()
|
||||||
@@ -24,6 +25,11 @@ enableSsh="no" # "yes" or "no"
|
|||||||
installLinuxGameManager="no" # "yes" or "no"
|
installLinuxGameManager="no" # "yes" or "no"
|
||||||
installAudiogameManager="no" # "yes" or "no"
|
installAudiogameManager="no" # "yes" or "no"
|
||||||
autoLoginUser="" # User to auto-login for desktop environments
|
autoLoginUser="" # User to auto-login for desktop environments
|
||||||
|
minHomePartitionMiB=1024
|
||||||
|
errorCount=0
|
||||||
|
warningCount=0
|
||||||
|
currentStep="initialization"
|
||||||
|
cleanupOnError=false
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -35,15 +41,33 @@ log() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log_error() {
|
log_error() {
|
||||||
|
errorCount=$((errorCount + 1))
|
||||||
echo "ERROR: $*" | tee -a "$logFile" >&2
|
echo "ERROR: $*" | tee -a "$logFile" >&2
|
||||||
}
|
}
|
||||||
|
|
||||||
log_warning() {
|
log_warning() {
|
||||||
|
warningCount=$((warningCount + 1))
|
||||||
echo "WARNING: $*" | tee -a "$logFile" >&2
|
echo "WARNING: $*" | tee -a "$logFile" >&2
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info() {
|
log_info() {
|
||||||
echo "INFO: $*" | tee -a "$logFile"
|
echo "$*" | tee -a "$logFile"
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_unexpected_failure() {
|
||||||
|
local exitCode="$1"
|
||||||
|
|
||||||
|
if [[ "$exitCode" -eq 0 ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_error "Installation aborted during step: $currentStep (exit code $exitCode)"
|
||||||
|
|
||||||
|
if [[ "$cleanupOnError" == true ]]; then
|
||||||
|
cleanup_and_unmount
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit "$exitCode"
|
||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -92,7 +116,7 @@ check_internet() {
|
|||||||
|
|
||||||
check_required_tools() {
|
check_required_tools() {
|
||||||
log_info "Checking for required tools..."
|
log_info "Checking for required tools..."
|
||||||
local tools=(parted mkfs.vfat mkfs.ext4 pacstrap arch-chroot genfstab lsblk)
|
local tools=(parted mkfs.vfat mkfs.ext4 pacstrap arch-chroot genfstab lsblk blkid findmnt mountpoint)
|
||||||
local missing=()
|
local missing=()
|
||||||
|
|
||||||
for tool in "${tools[@]}"; do
|
for tool in "${tools[@]}"; do
|
||||||
@@ -130,8 +154,24 @@ preflight_checks() {
|
|||||||
#
|
#
|
||||||
|
|
||||||
list_available_disks() {
|
list_available_disks() {
|
||||||
# List disks excluding loop devices, the current root device, and ISO
|
# List disks excluding loop devices and the disk backing the live root filesystem.
|
||||||
lsblk -dno NAME,SIZE,TYPE | grep -E "^[^l].*disk$" | grep -v "$(df / | tail -1 | cut -d' ' -f1 | sed 's|/dev/||;s|[0-9]*$||')" || true
|
local rootSource rootDiskName
|
||||||
|
rootSource=$(findmnt -n -o SOURCE --target / 2>/dev/null || true)
|
||||||
|
|
||||||
|
if [[ "$rootSource" == /dev/* ]]; then
|
||||||
|
rootDiskName=$(lsblk -no PKNAME "$rootSource" 2>/dev/null | head -n 1 || true)
|
||||||
|
|
||||||
|
if [[ -z "$rootDiskName" ]]; then
|
||||||
|
rootDiskName=$(basename "$rootSource")
|
||||||
|
rootDiskName=$(echo "$rootDiskName" | sed -E 's/p?[0-9]+$//')
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${rootDiskName:-}" ]]; then
|
||||||
|
lsblk -dno NAME,SIZE,TYPE | awk -v excludedDisk="$rootDiskName" '$1 != excludedDisk && $3 == "disk"'
|
||||||
|
else
|
||||||
|
lsblk -dno NAME,SIZE,TYPE | awk '$3 == "disk"'
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
select_disk() {
|
select_disk() {
|
||||||
@@ -164,6 +204,88 @@ select_disk() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_disk_size_mib() {
|
||||||
|
local disk="$1"
|
||||||
|
local diskSizeBytes
|
||||||
|
|
||||||
|
diskSizeBytes=$(lsblk -bdno SIZE "$disk" 2>/dev/null | head -n 1 || true)
|
||||||
|
if [[ -z "$diskSizeBytes" || ! "$diskSizeBytes" =~ ^[0-9]+$ ]]; then
|
||||||
|
log_error "Could not determine disk size for $disk"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo $((diskSizeBytes / 1024 / 1024))
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_home_disk_size() {
|
||||||
|
local disk="$1"
|
||||||
|
local diskSizeMiB
|
||||||
|
|
||||||
|
diskSizeMiB=$(get_disk_size_mib "$disk") || return 1
|
||||||
|
|
||||||
|
if [[ "$diskSizeMiB" -lt "$minHomePartitionMiB" ]]; then
|
||||||
|
log_warning "Selected home disk $disk is too small (${diskSizeMiB}MiB)"
|
||||||
|
log_warning "Separate home disk requires at least ${minHomePartitionMiB}MiB"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
select_separate_home_disk() {
|
||||||
|
while true; do
|
||||||
|
if ! select_disk homeDisk "Select disk for /home partition:"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$homeDisk" == "$targetDisk" ]]; then
|
||||||
|
log_warning "Home disk must be different from target disk in separate home disk layout"
|
||||||
|
echo "Please choose a different disk."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! validate_home_disk_size "$homeDisk"; then
|
||||||
|
echo "Please choose a different disk for /home."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
select_install_target() {
|
||||||
|
echo "Select installation target:"
|
||||||
|
|
||||||
|
mapfile -t disks < <(list_available_disks | awk '{print $1}')
|
||||||
|
|
||||||
|
if [[ ${#disks[@]} -gt 0 ]]; then
|
||||||
|
# Display disk information
|
||||||
|
log "Available disks:"
|
||||||
|
lsblk -dno NAME,SIZE,TYPE,MODEL | grep -E "^($(IFS='|'; echo "${disks[*]}")).*disk" || true
|
||||||
|
else
|
||||||
|
log_warning "No suitable installation disks detected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local manualOption="Use existing /mnt (already mounted root/home/boot)"
|
||||||
|
PS3="Enter target number: "
|
||||||
|
select targetChoice in "${disks[@]}" "$manualOption" "Cancel"; do
|
||||||
|
if [[ "$targetChoice" == "Cancel" ]]; then
|
||||||
|
log_info "Target selection cancelled by user"
|
||||||
|
return 1
|
||||||
|
elif [[ "$targetChoice" == "$manualOption" ]]; then
|
||||||
|
useExistingMount=true
|
||||||
|
partitionLayout="manual_mnt"
|
||||||
|
log_info "Selected existing mount mode at $mountPoint"
|
||||||
|
return 0
|
||||||
|
elif [[ -n "$targetChoice" ]]; then
|
||||||
|
targetDisk="/dev/$targetChoice"
|
||||||
|
useExistingMount=false
|
||||||
|
log_info "Selected disk: $targetDisk"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
confirm_disk_destruction() {
|
confirm_disk_destruction() {
|
||||||
local disk="$1"
|
local disk="$1"
|
||||||
log ""
|
log ""
|
||||||
@@ -228,6 +350,51 @@ select_partition_layout() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
show_partition_plan() {
|
||||||
|
log ""
|
||||||
|
log "=== Planned Disk Layout ==="
|
||||||
|
log "Boot mode: $bootMode"
|
||||||
|
log "Target disk: $targetDisk"
|
||||||
|
|
||||||
|
case "$partitionLayout" in
|
||||||
|
single)
|
||||||
|
if [[ "$bootMode" == "uefi" ]]; then
|
||||||
|
log " ${targetDisk}1 -> EFI (FAT32) mounted at /boot"
|
||||||
|
log " ${targetDisk}2 -> root (ext4) mounted at /"
|
||||||
|
else
|
||||||
|
log " ${targetDisk}1 -> root (ext4, boot flag) mounted at /"
|
||||||
|
fi
|
||||||
|
log " /home will be inside root filesystem"
|
||||||
|
;;
|
||||||
|
separate_home)
|
||||||
|
if [[ "$bootMode" == "uefi" ]]; then
|
||||||
|
log " ${targetDisk}1 -> EFI (FAT32) mounted at /boot"
|
||||||
|
log " ${targetDisk}2 -> root (ext4) mounted at /"
|
||||||
|
log " ${targetDisk}3 -> home (ext4) mounted at /home"
|
||||||
|
else
|
||||||
|
log " ${targetDisk}1 -> root (ext4, boot flag) mounted at /"
|
||||||
|
log " ${targetDisk}2 -> home (ext4) mounted at /home"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
separate_disk)
|
||||||
|
if [[ "$bootMode" == "uefi" ]]; then
|
||||||
|
log " ${targetDisk}1 -> EFI (FAT32) mounted at /boot"
|
||||||
|
log " ${targetDisk}2 -> root (ext4) mounted at /"
|
||||||
|
else
|
||||||
|
log " ${targetDisk}1 -> root (ext4, boot flag) mounted at /"
|
||||||
|
fi
|
||||||
|
log "Home disk: $homeDisk"
|
||||||
|
log " ${homeDisk}1 -> home (ext4) mounted at /home"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ "$partitionLayout" == "separate_disk" ]]; then
|
||||||
|
log "WARNING: ALL DATA on $targetDisk and $homeDisk will be destroyed."
|
||||||
|
else
|
||||||
|
log "WARNING: ALL DATA on $targetDisk will be destroyed."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Partitioning functions
|
# Partitioning functions
|
||||||
#
|
#
|
||||||
@@ -323,6 +490,10 @@ partition_disk_uefi_home_only() {
|
|||||||
local disk="$1"
|
local disk="$1"
|
||||||
log_info "Creating home partition on $disk"
|
log_info "Creating home partition on $disk"
|
||||||
|
|
||||||
|
if ! validate_home_disk_size "$disk"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Wipe existing partition table
|
# Wipe existing partition table
|
||||||
wipefs -af "$disk"
|
wipefs -af "$disk"
|
||||||
|
|
||||||
@@ -416,6 +587,10 @@ partition_disk_bios_home_only() {
|
|||||||
local disk="$1"
|
local disk="$1"
|
||||||
log_info "Creating home partition on $disk (BIOS)"
|
log_info "Creating home partition on $disk (BIOS)"
|
||||||
|
|
||||||
|
if ! validate_home_disk_size "$disk"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Wipe existing partition table
|
# Wipe existing partition table
|
||||||
wipefs -af "$disk"
|
wipefs -af "$disk"
|
||||||
|
|
||||||
@@ -489,12 +664,31 @@ get_partition_device() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_home_partition_number() {
|
||||||
|
case "$partitionLayout" in
|
||||||
|
separate_home)
|
||||||
|
if [[ "$bootMode" == "uefi" ]]; then
|
||||||
|
echo 3
|
||||||
|
else
|
||||||
|
echo 2
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
separate_disk)
|
||||||
|
echo 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
create_filesystems() {
|
create_filesystems() {
|
||||||
log_info "=== Creating Filesystems ==="
|
log_info "=== Creating Filesystems ==="
|
||||||
|
|
||||||
local rootPart
|
local rootPart
|
||||||
local homePart
|
local homePart
|
||||||
local efiPart
|
local efiPart
|
||||||
|
local homePartNum
|
||||||
|
|
||||||
if [[ "$bootMode" == "uefi" ]]; then
|
if [[ "$bootMode" == "uefi" ]]; then
|
||||||
efiPart=$(get_partition_device "$targetDisk" 1)
|
efiPart=$(get_partition_device "$targetDisk" 1)
|
||||||
@@ -511,12 +705,14 @@ create_filesystems() {
|
|||||||
|
|
||||||
case "$partitionLayout" in
|
case "$partitionLayout" in
|
||||||
separate_home)
|
separate_home)
|
||||||
homePart=$(get_partition_device "$targetDisk" 3)
|
homePartNum=$(get_home_partition_number) || return 1
|
||||||
|
homePart=$(get_partition_device "$targetDisk" "$homePartNum")
|
||||||
log_info "Creating home filesystem on $homePart"
|
log_info "Creating home filesystem on $homePart"
|
||||||
mkfs.ext4 -F "$homePart"
|
mkfs.ext4 -F "$homePart"
|
||||||
;;
|
;;
|
||||||
separate_disk)
|
separate_disk)
|
||||||
homePart=$(get_partition_device "$homeDisk" 1)
|
homePartNum=$(get_home_partition_number) || return 1
|
||||||
|
homePart=$(get_partition_device "$homeDisk" "$homePartNum")
|
||||||
log_info "Creating home filesystem on $homePart"
|
log_info "Creating home filesystem on $homePart"
|
||||||
mkfs.ext4 -F "$homePart"
|
mkfs.ext4 -F "$homePart"
|
||||||
;;
|
;;
|
||||||
@@ -531,6 +727,7 @@ mount_filesystems() {
|
|||||||
local rootPart
|
local rootPart
|
||||||
local homePart
|
local homePart
|
||||||
local efiPart
|
local efiPart
|
||||||
|
local homePartNum
|
||||||
|
|
||||||
# Determine partition devices
|
# Determine partition devices
|
||||||
if [[ "$bootMode" == "uefi" ]]; then
|
if [[ "$bootMode" == "uefi" ]]; then
|
||||||
@@ -555,13 +752,15 @@ mount_filesystems() {
|
|||||||
# Mount home if separate
|
# Mount home if separate
|
||||||
case "$partitionLayout" in
|
case "$partitionLayout" in
|
||||||
separate_home)
|
separate_home)
|
||||||
homePart=$(get_partition_device "$targetDisk" 3)
|
homePartNum=$(get_home_partition_number) || return 1
|
||||||
|
homePart=$(get_partition_device "$targetDisk" "$homePartNum")
|
||||||
log_info "Mounting home partition $homePart to $mountPoint/home"
|
log_info "Mounting home partition $homePart to $mountPoint/home"
|
||||||
mkdir -p "$mountPoint/home"
|
mkdir -p "$mountPoint/home"
|
||||||
mount "$homePart" "$mountPoint/home"
|
mount "$homePart" "$mountPoint/home"
|
||||||
;;
|
;;
|
||||||
separate_disk)
|
separate_disk)
|
||||||
homePart=$(get_partition_device "$homeDisk" 1)
|
homePartNum=$(get_home_partition_number) || return 1
|
||||||
|
homePart=$(get_partition_device "$homeDisk" "$homePartNum")
|
||||||
log_info "Mounting home partition $homePart to $mountPoint/home"
|
log_info "Mounting home partition $homePart to $mountPoint/home"
|
||||||
mkdir -p "$mountPoint/home"
|
mkdir -p "$mountPoint/home"
|
||||||
mount "$homePart" "$mountPoint/home"
|
mount "$homePart" "$mountPoint/home"
|
||||||
@@ -571,6 +770,165 @@ mount_filesystems() {
|
|||||||
log_info "Filesystems mounted successfully"
|
log_info "Filesystems mounted successfully"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validate_existing_mountpoint() {
|
||||||
|
log_info "=== Validating Existing Mountpoint ==="
|
||||||
|
|
||||||
|
if [[ ! -d "$mountPoint" ]]; then
|
||||||
|
log_error "Mountpoint does not exist: $mountPoint"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! mountpoint -q "$mountPoint"; then
|
||||||
|
log_error "$mountPoint is not mounted"
|
||||||
|
log_error "Mount root to $mountPoint before running this installer mode"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local rootSource
|
||||||
|
rootSource=$(findmnt -n -o SOURCE --target "$mountPoint" 2>/dev/null || true)
|
||||||
|
if [[ -z "$rootSource" ]]; then
|
||||||
|
log_error "Could not determine mounted root source for $mountPoint"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ "$rootSource" != /dev/* ]]; then
|
||||||
|
log_error "Unsupported root source for manual install mode: $rootSource"
|
||||||
|
log_error "Root at $mountPoint must be backed by a /dev/* block device"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d "$mountPoint/home" ]]; then
|
||||||
|
log_error "Required directory is missing: $mountPoint/home"
|
||||||
|
log_error "Create or mount /home at $mountPoint/home before continuing"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$bootMode" == "uefi" ]]; then
|
||||||
|
if [[ ! -d "$mountPoint/boot" ]]; then
|
||||||
|
log_error "Required directory is missing for UEFI: $mountPoint/boot"
|
||||||
|
log_error "Mount the EFI system partition at $mountPoint/boot before continuing"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! mountpoint -q "$mountPoint/boot"; then
|
||||||
|
log_error "$mountPoint/boot is not mounted"
|
||||||
|
log_error "For UEFI installs, mount EFI at $mountPoint/boot before continuing"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local efiFsType
|
||||||
|
efiFsType=$(findmnt -n -o FSTYPE --target "$mountPoint/boot" 2>/dev/null || true)
|
||||||
|
if [[ "$efiFsType" != "vfat" && "$efiFsType" != "fat" && "$efiFsType" != "msdos" ]]; then
|
||||||
|
log_error "Unexpected EFI filesystem type at $mountPoint/boot: ${efiFsType:-unknown}"
|
||||||
|
log_error "UEFI boot partition must be FAT (vfat)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! mountpoint -q "$mountPoint/home"; then
|
||||||
|
log_warning "$mountPoint/home is not a separate mountpoint"
|
||||||
|
log_warning "Continuing with /home as a directory inside root"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local writeTestFile="$mountPoint/.stormux-write-test-$$"
|
||||||
|
if ! touch "$writeTestFile" 2>/dev/null; then
|
||||||
|
log_error "$mountPoint is not writable"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
rm -f "$writeTestFile"
|
||||||
|
|
||||||
|
log ""
|
||||||
|
log "Manual mount summary (current mounts):"
|
||||||
|
log " $(findmnt -n -o SOURCE,FSTYPE,TARGET --target "$mountPoint" 2>/dev/null || echo "$mountPoint not mounted")"
|
||||||
|
|
||||||
|
if mountpoint -q "$mountPoint/home"; then
|
||||||
|
log " $(findmnt -n -o SOURCE,FSTYPE,TARGET --target "$mountPoint/home" 2>/dev/null || echo "$mountPoint/home not mounted")"
|
||||||
|
else
|
||||||
|
log " $mountPoint/home is a directory inside root (not a separate mount)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$bootMode" == "uefi" ]]; then
|
||||||
|
log " $(findmnt -n -o SOURCE,FSTYPE,TARGET --target "$mountPoint/boot" 2>/dev/null || echo "$mountPoint/boot not mounted")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Type 'YES' (in capital letters) to continue using existing mounts at $mountPoint:"
|
||||||
|
read -r manualMountConfirmation
|
||||||
|
if [[ "$manualMountConfirmation" != "YES" ]]; then
|
||||||
|
log_info "Existing mount mode not confirmed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Existing mountpoint validation passed"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
get_root_partition_device() {
|
||||||
|
if [[ "$useExistingMount" == true ]]; then
|
||||||
|
findmnt -n -o SOURCE --target "$mountPoint" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
if [[ "$bootMode" == "uefi" ]]; then
|
||||||
|
get_partition_device "$targetDisk" 2
|
||||||
|
else
|
||||||
|
get_partition_device "$targetDisk" 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_parent_disk_device() {
|
||||||
|
local currentDevice="$1"
|
||||||
|
local maxHops=20
|
||||||
|
local hop=0
|
||||||
|
|
||||||
|
while [[ "$hop" -lt "$maxHops" ]]; do
|
||||||
|
local currentType parentName
|
||||||
|
currentType=$(lsblk -no TYPE "$currentDevice" 2>/dev/null | head -n 1 || true)
|
||||||
|
|
||||||
|
if [[ "$currentType" == "disk" ]]; then
|
||||||
|
echo "$currentDevice"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
parentName=$(lsblk -no PKNAME "$currentDevice" 2>/dev/null | head -n 1 || true)
|
||||||
|
if [[ -z "$parentName" ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
currentDevice="/dev/$parentName"
|
||||||
|
hop=$((hop + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get_bios_install_disk() {
|
||||||
|
if [[ "$useExistingMount" == false ]]; then
|
||||||
|
echo "$targetDisk"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local rootPart
|
||||||
|
rootPart=$(get_root_partition_device)
|
||||||
|
if [[ -z "$rootPart" ]]; then
|
||||||
|
log_error "Could not determine root source from mounted $mountPoint"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$rootPart" != /dev/* ]]; then
|
||||||
|
log_error "Unsupported root source for BIOS bootloader installation: $rootPart"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local installDisk
|
||||||
|
installDisk=$(resolve_parent_disk_device "$rootPart" || true)
|
||||||
|
if [[ -z "$installDisk" ]]; then
|
||||||
|
log_error "Could not determine BIOS install disk from root source: $rootPart"
|
||||||
|
log_error "Set root on a block device that resolves to a physical disk (for example /dev/sdX or /dev/nvmeXnY)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$installDisk"
|
||||||
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# System information gathering
|
# System information gathering
|
||||||
#
|
#
|
||||||
@@ -927,10 +1285,18 @@ install_base_system() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Generate fstab
|
# Generate or preserve fstab
|
||||||
log_info "Generating fstab"
|
if [[ "$useExistingMount" == true ]]; then
|
||||||
log_info "Generating fstab"
|
if [[ -s "$mountPoint/etc/fstab" ]]; then
|
||||||
genfstab -U "$mountPoint" > "$mountPoint/etc/fstab"
|
log_info "Manual mount mode: preserving existing /etc/fstab"
|
||||||
|
else
|
||||||
|
log_warning "Manual mount mode: /etc/fstab not found, generating one"
|
||||||
|
genfstab -U "$mountPoint" > "$mountPoint/etc/fstab"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "Generating fstab"
|
||||||
|
genfstab -U "$mountPoint" > "$mountPoint/etc/fstab"
|
||||||
|
fi
|
||||||
|
|
||||||
log_info "Base system installed"
|
log_info "Base system installed"
|
||||||
log_info "Base system installation complete"
|
log_info "Base system installation complete"
|
||||||
@@ -1144,7 +1510,9 @@ echo \"Created user: $username\"
|
|||||||
stormuxPackagesCmd="
|
stormuxPackagesCmd="
|
||||||
# Install Stormux-specific packages
|
# Install Stormux-specific packages
|
||||||
echo \"Installing Stormux-specific packages: ${stormuxPackages[*]}\"
|
echo \"Installing Stormux-specific packages: ${stormuxPackages[*]}\"
|
||||||
pacman -Sy --noconfirm --needed ${stormuxPackages[*]} 2>/dev/null || true
|
if ! pacman -Sy --noconfirm --needed ${stormuxPackages[*]}; then
|
||||||
|
echo \"WARNING: Failed to install one or more Stormux-specific packages: ${stormuxPackages[*]}\"
|
||||||
|
fi
|
||||||
"
|
"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1157,14 +1525,18 @@ echo \"Installing packages for audiogame-manager...\"
|
|||||||
# Critical packages: wine, p7zip, curl, dialog, sox, cabextract, unzip, xz
|
# Critical packages: wine, p7zip, curl, dialog, sox, cabextract, unzip, xz
|
||||||
# Optional packages: winetricks, wine_gecko, wine-mono, translate-shell, gawk, perl, xclip, xdotool
|
# Optional packages: winetricks, wine_gecko, wine-mono, translate-shell, gawk, perl, xclip, xdotool
|
||||||
# Audio packages: gst-plugins for multimedia support
|
# Audio packages: gst-plugins for multimedia support
|
||||||
pacman -S --noconfirm --needed wine winetricks wine_gecko wine-mono p7zip curl dialog sox cabextract unzip xz translate-shell gawk perl xclip xdotool gst-plugins-bad gst-plugins-good gst-plugins-ugly gst-libav 2>/dev/null || true
|
if ! pacman -S --noconfirm --needed wine winetricks wine_gecko wine-mono p7zip curl dialog sox cabextract unzip xz translate-shell gawk perl xclip xdotool gst-plugins-bad gst-plugins-good gst-plugins-ugly gst-libav; then
|
||||||
|
echo \"WARNING: Failed to install one or more audiogame-manager dependencies\"
|
||||||
|
fi
|
||||||
"
|
"
|
||||||
fi
|
fi
|
||||||
if [[ "$installLinuxGameManager" == "yes" ]]; then
|
if [[ "$installLinuxGameManager" == "yes" ]]; then
|
||||||
gameManagerPackagesCmd+="
|
gameManagerPackagesCmd+="
|
||||||
# Install packages for linux-game-manager
|
# Install packages for linux-game-manager
|
||||||
echo \"Installing packages for linux-game-manager...\"
|
echo \"Installing packages for linux-game-manager...\"
|
||||||
pacman -S --noconfirm --needed p7zip curl dialog yad unzip 2>/dev/null || true
|
if ! pacman -S --noconfirm --needed p7zip curl dialog yad unzip; then
|
||||||
|
echo \"WARNING: Failed to install one or more linux-game-manager dependencies\"
|
||||||
|
fi
|
||||||
"
|
"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1172,12 +1544,14 @@ pacman -S --noconfirm --needed p7zip curl dialog yad unzip 2>/dev/null || true
|
|||||||
local pipewireConfigCmd=""
|
local pipewireConfigCmd=""
|
||||||
for username in "${userNames[@]}"; do
|
for username in "${userNames[@]}"; do
|
||||||
pipewireConfigCmd+="
|
pipewireConfigCmd+="
|
||||||
sudo -u $(printf %q "$username") /usr/share/fenrirscreenreader/tools/configure_pipewire.sh 2>/dev/null || true
|
if ! sudo -u $(printf %q "$username") /usr/share/fenrirscreenreader/tools/configure_pipewire.sh; then
|
||||||
|
echo \"WARNING: Failed to configure PipeWire for user $(printf %q "$username")\"
|
||||||
|
fi
|
||||||
"
|
"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Run configuration in chroot using heredoc (like pi4 script)
|
# Run configuration in chroot using heredoc (like pi4 script)
|
||||||
if ! arch-chroot "$mountPoint" /bin/bash <<EOF
|
if ! arch-chroot "$mountPoint" /bin/bash <<EOF 2>&1 | tee -a "$logFile"
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Set timezone
|
# Set timezone
|
||||||
@@ -1229,7 +1603,9 @@ echo '%wheel ALL=(ALL) ALL' > /etc/sudoers.d/wheel
|
|||||||
chmod 440 /etc/sudoers.d/wheel
|
chmod 440 /etc/sudoers.d/wheel
|
||||||
|
|
||||||
# Disable systemd-networkd in favor of NetworkManager
|
# Disable systemd-networkd in favor of NetworkManager
|
||||||
systemctl disable systemd-networkd.service systemd-networkd.socket 2>/dev/null || true
|
if ! systemctl disable systemd-networkd.service systemd-networkd.socket; then
|
||||||
|
echo "WARNING: Could not disable systemd-networkd services (may already be disabled)"
|
||||||
|
fi
|
||||||
|
|
||||||
# Enable system services
|
# Enable system services
|
||||||
systemctl enable NetworkManager.service
|
systemctl enable NetworkManager.service
|
||||||
@@ -1239,7 +1615,9 @@ systemctl enable cronie.service
|
|||||||
systemctl enable ssh-login-monitor.service
|
systemctl enable ssh-login-monitor.service
|
||||||
|
|
||||||
# Enable bluetooth if present
|
# Enable bluetooth if present
|
||||||
systemctl enable bluetooth.service 2>/dev/null || true
|
if ! systemctl enable bluetooth.service; then
|
||||||
|
echo "WARNING: Could not enable bluetooth.service"
|
||||||
|
fi
|
||||||
|
|
||||||
# Enable SSH if requested
|
# Enable SSH if requested
|
||||||
if [[ "${enableSsh}" == "yes" ]]; then
|
if [[ "${enableSsh}" == "yes" ]]; then
|
||||||
@@ -1255,9 +1633,11 @@ if [[ "${desktopEnvironment}" == "i3" ]]; then
|
|||||||
mkdir -p /home/\$firstUser/git
|
mkdir -p /home/\$firstUser/git
|
||||||
chown \$firstUser:users /home/\$firstUser/git
|
chown \$firstUser:users /home/\$firstUser/git
|
||||||
|
|
||||||
# Clone I38 to ~/git/I38
|
# Clone I38 to ~/git/I38
|
||||||
echo "Cloning I38 accessibility configuration..."
|
echo "Cloning I38 accessibility configuration..."
|
||||||
sudo -u \$firstUser git clone https://git.stormux.org/storm/I38 /home/\$firstUser/git/I38 2>/dev/null || true
|
if ! sudo -u \$firstUser git clone https://git.stormux.org/storm/I38 /home/\$firstUser/git/I38; then
|
||||||
|
echo "WARNING: Failed to clone I38 repository"
|
||||||
|
fi
|
||||||
|
|
||||||
# Run I38 setup to generate accessible i3 configuration
|
# Run I38 setup to generate accessible i3 configuration
|
||||||
if [[ -d /home/\$firstUser/git/I38 ]]; then
|
if [[ -d /home/\$firstUser/git/I38 ]]; then
|
||||||
@@ -1267,14 +1647,20 @@ if [[ "${desktopEnvironment}" == "i3" ]]; then
|
|||||||
# Ensure xdotool is available for I38 setup
|
# Ensure xdotool is available for I38 setup
|
||||||
if ! command -v xdotool >/dev/null 2>&1; then
|
if ! command -v xdotool >/dev/null 2>&1; then
|
||||||
echo "Installing xdotool for I38..."
|
echo "Installing xdotool for I38..."
|
||||||
pacman -Sy --noconfirm --needed xdotool 2>/dev/null || true
|
if ! pacman -Sy --noconfirm --needed xdotool; then
|
||||||
|
echo "WARNING: Failed to install xdotool for I38 setup"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run I38 setup scripts as the user
|
# Run I38 setup scripts as the user
|
||||||
# -x generates xinitrc and xprofile
|
# -x generates xinitrc and xprofile
|
||||||
# Main script generates i3 config with accessibility features
|
# Main script generates i3 config with accessibility features
|
||||||
sudo -u \$firstUser ./i38.sh -x || true
|
if ! sudo -u \$firstUser ./i38.sh -x; then
|
||||||
sudo -u \$firstUser ./i38.sh || true
|
echo "WARNING: I38 xinit/xprofile setup reported an error"
|
||||||
|
fi
|
||||||
|
if ! sudo -u \$firstUser ./i38.sh; then
|
||||||
|
echo "WARNING: I38 main setup reported an error"
|
||||||
|
fi
|
||||||
|
|
||||||
cd - > /dev/null || exit 1
|
cd - > /dev/null || exit 1
|
||||||
fi
|
fi
|
||||||
@@ -1318,16 +1704,24 @@ export QT_LINUX_ACCESSIBILITY_ALWAYS_ON=1
|
|||||||
export SAL_USE_VCLPLUGIN=gtk3
|
export SAL_USE_VCLPLUGIN=gtk3
|
||||||
|
|
||||||
# Enable Orca screen reader
|
# Enable Orca screen reader
|
||||||
gsettings set org.gnome.desktop.a11y.applications screen-reader-enabled true 2>/dev/null || true
|
if ! gsettings set org.gnome.desktop.a11y.applications screen-reader-enabled true; then
|
||||||
gsettings set org.mate.interface accessibility true 2>/dev/null || true
|
echo "WARNING: Could not set GNOME screen-reader-enabled flag"
|
||||||
gsettings set org.mate.applications-at-visual startup true 2>/dev/null || true
|
fi
|
||||||
|
if ! gsettings set org.mate.interface accessibility true; then
|
||||||
|
echo "WARNING: Could not set MATE accessibility flag"
|
||||||
|
fi
|
||||||
|
if ! gsettings set org.mate.applications-at-visual startup true; then
|
||||||
|
echo "WARNING: Could not set MATE startup applications accessibility flag"
|
||||||
|
fi
|
||||||
XPROFILE_MATE_EOF
|
XPROFILE_MATE_EOF
|
||||||
chmod 755 /home/\$firstUser/.xprofile
|
chmod 755 /home/\$firstUser/.xprofile
|
||||||
chown \$firstUser:users /home/\$firstUser/.xprofile
|
chown \$firstUser:users /home/\$firstUser/.xprofile
|
||||||
|
|
||||||
# Configure speech-dispatcher for MATE
|
# Configure speech-dispatcher for MATE
|
||||||
if [[ ! -d /home/\$firstUser/.config/speech-dispatcher ]]; then
|
if [[ ! -d /home/\$firstUser/.config/speech-dispatcher ]]; then
|
||||||
sudo -u \$firstUser spd-conf -n 2>/dev/null || true
|
if ! sudo -u \$firstUser spd-conf -n; then
|
||||||
|
echo "WARNING: Failed to initialize speech-dispatcher configuration for \$firstUser"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1337,8 +1731,12 @@ pacman-key --populate archlinux
|
|||||||
|
|
||||||
# Import Stormux key if present
|
# Import Stormux key if present
|
||||||
if [[ -f /usr/share/pacman/keyrings/stormux.gpg ]]; then
|
if [[ -f /usr/share/pacman/keyrings/stormux.gpg ]]; then
|
||||||
pacman-key --add /usr/share/pacman/keyrings/stormux.gpg 2>/dev/null || true
|
if ! pacman-key --add /usr/share/pacman/keyrings/stormux.gpg; then
|
||||||
pacman-key --lsign-key 52ADA49000F1FF0456F8AEEFB4CDE1CD56EF8E82 2>/dev/null || true
|
echo "WARNING: Could not add Stormux signing key to pacman keyring"
|
||||||
|
fi
|
||||||
|
if ! pacman-key --lsign-key 52ADA49000F1FF0456F8AEEFB4CDE1CD56EF8E82; then
|
||||||
|
echo "WARNING: Could not locally sign Stormux pacman key"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install Stormux-specific packages (built dynamically before chroot)
|
# Install Stormux-specific packages (built dynamically before chroot)
|
||||||
@@ -1352,7 +1750,9 @@ systemctl --global enable pipewire.service pipewire-pulse.service wireplumber.se
|
|||||||
|
|
||||||
# Configure pipewire for Fenrir screen reader (run as root first)
|
# Configure pipewire for Fenrir screen reader (run as root first)
|
||||||
if [[ -x /usr/share/fenrirscreenreader/tools/configure_pipewire.sh ]]; then
|
if [[ -x /usr/share/fenrirscreenreader/tools/configure_pipewire.sh ]]; then
|
||||||
/usr/share/fenrirscreenreader/tools/configure_pipewire.sh 2>/dev/null || true
|
if ! /usr/share/fenrirscreenreader/tools/configure_pipewire.sh; then
|
||||||
|
echo "WARNING: Failed to configure PipeWire for root/Fenrir"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Configure pipewire for each user (built dynamically before chroot)
|
# Configure pipewire for each user (built dynamically before chroot)
|
||||||
@@ -1425,9 +1825,18 @@ EOF
|
|||||||
|
|
||||||
# Get root partition UUID
|
# Get root partition UUID
|
||||||
local rootPart
|
local rootPart
|
||||||
rootPart=$(get_partition_device "$targetDisk" 2)
|
rootPart=$(get_root_partition_device)
|
||||||
|
if [[ -z "$rootPart" ]]; then
|
||||||
|
log_error "Could not determine root partition device"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
local rootUUID
|
local rootUUID
|
||||||
rootUUID=$(blkid -s UUID -o value "$rootPart")
|
rootUUID=$(blkid -s UUID -o value "$rootPart")
|
||||||
|
if [[ -z "$rootUUID" ]]; then
|
||||||
|
log_error "Could not determine root partition UUID for $rootPart"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Create boot entry
|
# Create boot entry
|
||||||
cat > "$mountPoint/boot/loader/entries/arch.conf" <<EOF
|
cat > "$mountPoint/boot/loader/entries/arch.conf" <<EOF
|
||||||
@@ -1444,8 +1853,11 @@ EOF
|
|||||||
log_info "Installing GRUB for BIOS"
|
log_info "Installing GRUB for BIOS"
|
||||||
log_info "Installing GRUB"
|
log_info "Installing GRUB"
|
||||||
|
|
||||||
|
local grubTargetDisk
|
||||||
|
grubTargetDisk=$(get_bios_install_disk) || return 1
|
||||||
|
|
||||||
# Install GRUB to disk
|
# Install GRUB to disk
|
||||||
arch-chroot "$mountPoint" grub-install --target=i386-pc "$targetDisk"
|
arch-chroot "$mountPoint" grub-install --target=i386-pc "$grubTargetDisk"
|
||||||
|
|
||||||
# Generate GRUB config
|
# Generate GRUB config
|
||||||
arch-chroot "$mountPoint" grub-mkconfig -o /boot/grub/grub.cfg
|
arch-chroot "$mountPoint" grub-mkconfig -o /boot/grub/grub.cfg
|
||||||
@@ -1596,6 +2008,7 @@ log_info "=== Stormux Installer Started ==="
|
|||||||
log_info "Installation log: $logFile"
|
log_info "Installation log: $logFile"
|
||||||
|
|
||||||
# Pre-flight checks
|
# Pre-flight checks
|
||||||
|
currentStep="pre-flight checks"
|
||||||
if ! preflight_checks; then
|
if ! preflight_checks; then
|
||||||
log_error "Pre-flight checks failed"
|
log_error "Pre-flight checks failed"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -1603,130 +2016,152 @@ fi
|
|||||||
|
|
||||||
# Disk selection
|
# Disk selection
|
||||||
log_info "=== Disk Selection ==="
|
log_info "=== Disk Selection ==="
|
||||||
if ! select_disk targetDisk "Select target disk for Stormux installation:"; then
|
currentStep="disk selection"
|
||||||
log_error "Target disk selection cancelled"
|
if ! select_install_target; then
|
||||||
|
log_error "Target selection cancelled"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! confirm_disk_destruction "$targetDisk"; then
|
if [[ "$useExistingMount" == false ]]; then
|
||||||
log_error "Disk destruction not confirmed"
|
# Partition layout selection
|
||||||
echo "Installation cancelled."
|
currentStep="partition layout selection"
|
||||||
exit 1
|
if ! select_partition_layout; then
|
||||||
fi
|
log_error "Partition layout selection cancelled"
|
||||||
|
|
||||||
# Partition layout selection
|
|
||||||
if ! select_partition_layout; then
|
|
||||||
log_error "Partition layout selection cancelled"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If separate disk layout, select home disk
|
|
||||||
if [[ "$partitionLayout" == "separate_disk" ]]; then
|
|
||||||
if ! select_disk homeDisk "Select disk for /home partition:"; then
|
|
||||||
log_error "Home disk selection cancelled"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! confirm_disk_destruction "$homeDisk"; then
|
# If separate disk layout, select home disk
|
||||||
log_error "Home disk destruction not confirmed"
|
if [[ "$partitionLayout" == "separate_disk" ]]; then
|
||||||
|
currentStep="home disk selection"
|
||||||
|
if ! select_separate_home_disk; then
|
||||||
|
log_error "Home disk selection cancelled"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
currentStep="partition plan summary"
|
||||||
|
show_partition_plan
|
||||||
|
|
||||||
|
currentStep="target disk confirmation"
|
||||||
|
if ! confirm_disk_destruction "$targetDisk"; then
|
||||||
|
log_error "Disk destruction not confirmed"
|
||||||
echo "Installation cancelled."
|
echo "Installation cancelled."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "$partitionLayout" == "separate_disk" ]]; then
|
||||||
|
currentStep="home disk confirmation"
|
||||||
|
if ! confirm_disk_destruction "$homeDisk"; then
|
||||||
|
log_error "Home disk destruction not confirmed"
|
||||||
|
echo "Installation cancelled."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
currentStep="existing mount validation"
|
||||||
|
if ! validate_existing_mountpoint; then
|
||||||
|
log_error "Existing mountpoint validation failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Gather system information
|
# Gather system information
|
||||||
|
currentStep="system configuration prompts"
|
||||||
if ! gather_system_info; then
|
if ! gather_system_info; then
|
||||||
log_error "System information gathering failed"
|
log_error "System information gathering failed"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Partition disks
|
trap 'handle_unexpected_failure "$?"' ERR
|
||||||
if ! partition_disks; then
|
|
||||||
log_error "Disk partitioning failed"
|
|
||||||
echo "ERROR: Disk partitioning failed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create filesystems
|
if [[ "$useExistingMount" == false ]]; then
|
||||||
if ! create_filesystems; then
|
# Partition disks
|
||||||
log_error "Filesystem creation failed"
|
currentStep="disk partitioning"
|
||||||
echo "ERROR: Filesystem creation failed"
|
partition_disks
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Mount filesystems
|
# Create filesystems
|
||||||
if ! mount_filesystems; then
|
currentStep="filesystem creation"
|
||||||
log_error "Filesystem mounting failed"
|
create_filesystems
|
||||||
echo "ERROR: Filesystem mounting failed"
|
|
||||||
exit 1
|
# Mount filesystems
|
||||||
|
cleanupOnError=true
|
||||||
|
currentStep="filesystem mounting"
|
||||||
|
mount_filesystems
|
||||||
|
else
|
||||||
|
cleanupOnError=true
|
||||||
|
log_info "Skipping partitioning, filesystem creation, and mounting (using existing $mountPoint)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install base system
|
# Install base system
|
||||||
if ! install_base_system; then
|
currentStep="base system installation"
|
||||||
log_error "Base system installation failed"
|
install_base_system
|
||||||
cleanup_and_unmount
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install audio configurations
|
# Install audio configurations
|
||||||
if ! install_audio_configs; then
|
currentStep="audio configuration"
|
||||||
log_error "Audio configuration failed"
|
install_audio_configs
|
||||||
cleanup_and_unmount
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install SSH login monitor files (script + service)
|
# Install SSH login monitor files (script + service)
|
||||||
if ! install_ssh_login_monitor; then
|
currentStep="ssh login monitor installation"
|
||||||
log_error "SSH login monitor installation failed"
|
install_ssh_login_monitor
|
||||||
cleanup_and_unmount
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Configure system
|
# Configure system
|
||||||
if ! configure_system; then
|
currentStep="chroot system configuration"
|
||||||
log_error "System configuration failed"
|
configure_system
|
||||||
cleanup_and_unmount
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install bootloader
|
# Install bootloader
|
||||||
if ! install_bootloader; then
|
currentStep="bootloader installation"
|
||||||
log_error "Bootloader installation failed"
|
install_bootloader
|
||||||
cleanup_and_unmount
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install game managers
|
# Install game managers
|
||||||
if ! install_game_managers; then
|
currentStep="game manager installation"
|
||||||
log_warning "Game manager installation had issues (non-fatal)"
|
install_game_managers
|
||||||
fi
|
|
||||||
|
|
||||||
# Cleanup and unmount
|
# Cleanup and unmount
|
||||||
|
currentStep="cleanup"
|
||||||
cleanup_and_unmount
|
cleanup_and_unmount
|
||||||
|
cleanupOnError=false
|
||||||
|
|
||||||
# Success message
|
summaryWarningCount=$(grep -c '^WARNING:' "$logFile" 2>/dev/null || true)
|
||||||
log_info "=== Stormux Installer Completed Successfully ==="
|
summaryErrorCount=$(grep -c '^ERROR:' "$logFile" 2>/dev/null || true)
|
||||||
log ""
|
|
||||||
log "╔════════════════════════════════════════════╗"
|
|
||||||
log "║ Installation Complete Successfully! ║"
|
|
||||||
log "╚════════════════════════════════════════════╝"
|
|
||||||
log ""
|
|
||||||
log "Next steps:"
|
|
||||||
log " 1. Remove the installation media"
|
|
||||||
log " 2. Reboot your system"
|
|
||||||
log " 3. Log in with one of your user accounts: ${userNames[*]}"
|
|
||||||
|
|
||||||
if [[ "$desktopEnvironment" == "i3" ]]; then
|
log ""
|
||||||
log " 4. Your i3 desktop is configured with I38 for accessibility"
|
log "=== Installation Summary ==="
|
||||||
log " - I38 source is available in ~/git/I38 for customization"
|
log " Errors: ${summaryErrorCount:-0}"
|
||||||
log " - Press Alt+Shift+F1 for I38 help after login"
|
log " Warnings: ${summaryWarningCount:-0}"
|
||||||
|
log " Log file: $logFile"
|
||||||
|
log ""
|
||||||
|
|
||||||
|
if [[ "$errorCount" -gt 0 ]]; then
|
||||||
|
log_warning "Installation completed with ${summaryErrorCount:-$errorCount} error(s)."
|
||||||
|
log_warning "Review the installation log before rebooting: $logFile"
|
||||||
|
finalExitCode=1
|
||||||
|
else
|
||||||
|
# Success message
|
||||||
|
log_info "=== Stormux Installer Completed Successfully ==="
|
||||||
|
log ""
|
||||||
|
log "╔════════════════════════════════════════════╗"
|
||||||
|
log "║ Installation Complete Successfully! ║"
|
||||||
|
log "╚════════════════════════════════════════════╝"
|
||||||
|
log ""
|
||||||
|
log "Next steps:"
|
||||||
|
log " 1. Remove the installation media"
|
||||||
|
log " 2. Reboot your system"
|
||||||
|
log " 3. Log in with one of your user accounts: ${userNames[*]}"
|
||||||
|
|
||||||
|
if [[ "$desktopEnvironment" == "i3" ]]; then
|
||||||
|
log " 4. Your i3 desktop is configured with I38 for accessibility"
|
||||||
|
log " - I38 source is available in ~/git/I38 for customization"
|
||||||
|
log " - Press Alt+Shift+F1 for I38 help after login"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log ""
|
||||||
|
log "Installation log saved to: $logFile"
|
||||||
|
log ""
|
||||||
|
|
||||||
|
# Play success sound
|
||||||
|
play_success_sound
|
||||||
|
finalExitCode=0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log ""
|
|
||||||
log "Installation log saved to: $logFile"
|
|
||||||
log ""
|
|
||||||
|
|
||||||
# Play success sound
|
|
||||||
play_success_sound
|
|
||||||
|
|
||||||
read -rp "Press Enter to exit..."
|
read -rp "Press Enter to exit..."
|
||||||
|
exit "$finalExitCode"
|
||||||
|
|||||||
Reference in New Issue
Block a user