Compare commits

..
Author SHA1 Message Date
Ryan Hughes 9a604ef1e7 Add limine 2025-11-26 01:28:23 -05:00
Ryan Hughes 9debbec097 Tail both logs to get additional output 2025-11-16 22:05:04 -05:00
Ryan Hughes 9147ed0f54 Fix builds 2025-11-16 22:04:52 -05:00
Ryan Hughes 76699b4de0 Refactor work 2025-11-16 19:31:01 -05:00
25 changed files with 797 additions and 731 deletions

No files matched your search

+1 -1
View File
@@ -36,4 +36,4 @@ Run `./bin/omarchy-iso-upload [release/omarchy.iso]`. This requires you've confi
## Full release of the ISO
Run `./bin/omarchy-iso-release VERSION` to create, test, sign, and upload the ISO in one flow. Add `--rc` to release an RC build instead.
Run `./bin/omarchy-iso-release` to create, test, sign, and upload the ISO in one flow.
+1 -1
Submodule archiso updated: 424e78130d...69047bdc03.
+4 -2
View File
@@ -18,11 +18,13 @@ if [ -z "$ISO" ]; then
exit 1
fi
omarchy-pkg-add qemu-full edk2-ovmf
if ! pacman -Q qemu-full >/dev/null || ! pacman -Q edk2-ovmf >/dev/null; then
sudo pacman -S --noconfirm --needed --quiet qemu-full edk2-ovmf
fi
DISK="/tmp/omarchy-iso-boot.qcow2"
if [[ ! -f $DISK || $REUSE != "reuse" ]]; then
qemu-img create -f qcow2 "$DISK" 40G
qemu-img create -f qcow2 "$DISK" 20G
cp /usr/share/edk2/x64/OVMF_VARS.4m.fd /tmp/OVMF_VARS.4m.fd
fi
+32 -16
View File
@@ -17,30 +17,20 @@ while [[ $# -gt 0 ]]; do
;;
--local-source)
LOCAL_SOURCE=1
OMARCHY_MIRROR=edge
shift
;;
--dev)
OMARCHY_INSTALLER_REF=dev
OMARCHY_MIRROR=edge
shift
;;
--rc)
OMARCHY_MIRROR=rc
OMARCHY_INSTALLER_REF=rc
shift
;;
*)
echo "Unknown option: $1"
echo "Usage: $0 [--no-cache] [--no-boot-offer] [--local-source] [--dev] [--rc]"
echo "Usage: $0 [--no-cache] [--no-boot-offer] [--local-source] [--dev]"
exit 1
;;
esac
done
# Clear out old packages
sudo rm -rf /var/cache/pacman/pkg/*
# Ensure we have the submodule checked out
git submodule update --init --recursive --jobs=8
@@ -52,14 +42,12 @@ mkdir -p "$BUILD_RELEASE_PATH"
OMARCHY_INSTALLER_REPO="${OMARCHY_INSTALLER_REPO:-basecamp/omarchy}"
OMARCHY_INSTALLER_REF="${OMARCHY_INSTALLER_REF:-master}"
OMARCHY_MIRROR="${OMARCHY_MIRROR:-stable}"
DOCKER_ARGS=(
--rm
--privileged
-e "OMARCHY_INSTALLER_REPO=$OMARCHY_INSTALLER_REPO"
-e "OMARCHY_INSTALLER_REF=$OMARCHY_INSTALLER_REF"
-e "OMARCHY_MIRROR=$OMARCHY_MIRROR"
-e "HOST_UID=$(id -u)"
-e "HOST_GID=$(id -g)"
-v "$BUILD_RELEASE_PATH/:/out/"
@@ -69,9 +57,37 @@ DOCKER_ARGS=(
-v "$BUILD_ROOT/configs:/configs:ro"
)
# Use local source of Omarchy from $OMARCHY_PATH instead of git checkout
if [[ -n "${LOCAL_SOURCE-}" ]]; then
DOCKER_ARGS+=(-v "$OMARCHY_PATH:/omarchy:ro")
# Mount omarchy-pkgs if it exists (for local package building)
# Can be overridden with OMARCHY_PKGS_PATH environment variable
if [ -z "$OMARCHY_PKGS_PATH" ]; then
OMARCHY_PKGS_PATH="$BUILD_ROOT/../omarchy-pkgs"
fi
if [ -d "$OMARCHY_PKGS_PATH" ]; then
echo "Mounting omarchy-pkgs from: $OMARCHY_PKGS_PATH"
OMARCHY_PKGS_PATH=$(realpath "$OMARCHY_PKGS_PATH")
DOCKER_ARGS+=(-v "$OMARCHY_PKGS_PATH:/omarchy-pkgs:ro")
else
echo "Note: omarchy-pkgs not found at $OMARCHY_PKGS_PATH (build will fail)"
fi
# Mount omarchy-installer for dev builds (optional)
# Can be overridden with OMARCHY_INSTALLER_PATH environment variable
# If not set, will auto-detect ../omarchy-installer for dev builds
if [ -n "$LOCAL_SOURCE" ]; then
if [ -z "$OMARCHY_INSTALLER_PATH" ]; then
OMARCHY_INSTALLER_PATH="$BUILD_ROOT/../omarchy-installer"
fi
if [ -d "$OMARCHY_INSTALLER_PATH" ]; then
echo "Mounting omarchy-installer from: $OMARCHY_INSTALLER_PATH (dev mode)"
OMARCHY_INSTALLER_PATH=$(realpath "$OMARCHY_INSTALLER_PATH")
DOCKER_ARGS+=(-v "$OMARCHY_INSTALLER_PATH:/omarchy-installer:ro")
DOCKER_ARGS+=(-e "OMARCHY_DEV_BUILD=1")
else
echo "Note: omarchy-installer not found at $OMARCHY_INSTALLER_PATH"
echo " Will use git clone from PKGBUILD"
fi
fi
# Use local pacman cache if you already have one on host to speed up repeat runs
-2
View File
@@ -1,6 +1,4 @@
#!/bin/bash
omarchy-pkg-add rclone
mkdir -p ~/.config/rclone
access_key=$(op read "op://Shared/Cloudflare Buckets/iso.omarchy.org/access_key_id" --account Omarchy)
secret_access_key=$(op read "op://Shared/Cloudflare Buckets/iso.omarchy.org/secret_access_key" --account Omarchy)
+15 -46
View File
@@ -1,62 +1,31 @@
#!/bin/bash
MAKE_ARGS=()
INSTALLER_REF=master
RELEASE_SUFFIX=""
while [[ $# -gt 0 ]]; do
case $1 in
--no-make)
MAKE_ARGS+=("$1")
shift
;;
--rc)
MAKE_ARGS+=("$1")
INSTALLER_REF=rc
RELEASE_SUFFIX="-rc"
shift
;;
-*)
echo "Unknown option: $1"
echo "Usage: omarchy-iso-release [--no-make] [--rc] [version]"
exit 1
;;
*)
if [[ -n "${BUILD_VERSION-}" ]]; then
echo "Usage: omarchy-iso-release [--no-make] [--rc] [version]"
exit 1
fi
BUILD_VERSION="$1"
shift
;;
esac
done
if [[ -z "${BUILD_VERSION-}" ]]; then
echo "Usage: omarchy-iso-release [--no-make] [--rc] [version]"
if [ $# -eq 0 ]; then
echo "Usage: omarchy-iso-release [version]"
exit 1
fi
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
BUILD_RELEASE_PATH="$BUILD_ROOT/release"
BULD_VERSION="$1"
# Make ISOs
if [[ ! " ${MAKE_ARGS[*]} " =~ " --no-make " ]]; then
echo -e "\e[32mMaking $INSTALLER_REF ISO\e[0m"
omarchy-iso-make --no-cache --no-boot-offer "${MAKE_ARGS[@]}"
if [[ $1 != "--no-make" ]]; then
echo -e "\e[32mMaking master ISO\e[0m"
OMARCHY_INSTALLER_REF=master omarchy-iso-make --no-cache --no-boot-offer
else
echo -e "\e[31mSkipping remaking ISOs\e[0m"
fi
# Sign and upload ISO
latest_iso=$(\ls -t "$BUILD_RELEASE_PATH"/*x86_64-"$INSTALLER_REF".iso | head -n1)
release_iso="$BUILD_RELEASE_PATH/omarchy-$BUILD_VERSION$RELEASE_SUFFIX.iso"
cp "$latest_iso" "$release_iso"
# Sign and upload master ISO
latest_master_iso=$(\ls -t "$BUILD_RELEASE_PATH"/*x86_64-master.iso | head -n1)
release_master_iso="$BUILD_RELEASE_PATH/omarchy-$BULD_VERSION.iso"
cp "$latest_master_iso" "$release_master_iso"
echo -e "\e[32mSigning $INSTALLER_REF ISO\e[0m"
omarchy-iso-sign "$release_iso"
echo -e "\e[32mSigning master ISO\e[0m"
omarchy-iso-sign "$release_master_iso"
iso_sha=$(sha256sum "$release_iso")
echo -e "\e[32mSHA256 for $INSTALLER_REF ISO: $iso_sha\e[0m"
master_sha=$(sha256sum "$release_master_iso")
echo -e "\e[32mSHA256 for master ISO: $master_sha\e[0m"
omarchy-iso-upload "$release_iso"
omarchy-iso-upload "$release_master_iso"
+143
View File
@@ -0,0 +1,143 @@
# Omarchy ISO Builder
## Overview
The ISO build system now integrates local package building for development workflows.
## Build Scripts
### build-iso.sh
Main ISO build script that:
1. Sets up Arch Linux build environment
2. Builds omarchy-settings package locally (if omarchy-pkgs mounted)
3. Installs omarchy-settings in the ISO
4. Downloads remaining packages to offline mirror
5. Assembles the final ISO image
### build-omarchy-packages.sh
Builds omarchy-settings package locally from source:
- Runs inside Docker container
- Builds as `nobody` user (makepkg requirement)
- Outputs package to `/tmp/omarchy-pkg-build/`
- Exports `OMARCHY_SETTINGS_PKG` variable for build-iso.sh
## Package Integration
### omarchy-settings
**Installed in ISO** - Provides EVERYTHING needed:
- User skeleton files (`/etc/skel/`)
- System configurations (`/etc/`)
- Plymouth boot theme
- Essential binaries (`omarchy-debug`, `omarchy-upload-log`)
- Install scripts (`/usr/share/omarchy/install/`)
- Default configs and migrations
**Size**: ~5-10MB
**Purpose**: Complete self-contained package for ISO - no git clone needed!
### omarchy (future)
**In offline mirror** - Full desktop environment:
- All omarchy-* binaries
- Install scripts and migrations
- Themes and assets
- Heavy dependencies (Hyprland, Waybar, etc.)
**Installed during**: Post-install phase in chroot
## Development Workflow
### Quick Build
```bash
# From omarchy-iso directory (uses default paths)
omarchy-iso-make-dev
# Or with custom paths
export OMARCHY_PKGS_PATH=/path/to/pkgs
export OMARCHY_INSTALLER_PATH=/path/to/installer
omarchy-iso-make-dev
```
This will:
1. Mount omarchy-pkgs into Docker container
2. Mount omarchy-installer (if OMARCHY_INSTALLER_PATH set)
3. Build omarchy-settings from local source
4. Install it in the ISO
5. Create bootable ISO with latest changes
See [ENV_VARS.md](../ENV_VARS.md) for all configuration options.
### Without Package Build
If `omarchy-pkgs` directory is not mounted:
- **Build will fail** with helpful error message
- The ISO build now requires omarchy-settings package
- No fallback to git clone (removed for simplicity)
- Set `OMARCHY_PKGS_PATH` to enable builds
## Directory Structure
```
omarchy-iso/
├── bin/
│ └── omarchy-iso-make # Main entry point (updated to mount omarchy-pkgs)
├── builder/
│ ├── build-iso.sh # Main ISO build (updated for package integration)
│ ├── build-omarchy-packages.sh # New: Local package builder
│ └── README.md # This file
├── configs/
│ ├── profiledef.sh # ISO profile (updated to remove manual file perms)
│ └── ...
└── release/
└── omarchy-*.iso # Built ISOs
```
## How It Works
1. **omarchy-iso-make** detects `../omarchy-pkgs` directory
2. Mounts it read-only into Docker container at `/omarchy-pkgs`
3. **build-iso.sh** checks for `/omarchy-pkgs` mount (REQUIRED!)
4. Sources **build-omarchy-packages.sh** to build package
5. Package is built with makepkg as `nobody` user
6. Built package added to `packages.x86_64` list
7. Copied to offline mirror
8. mkarchiso installs it during ISO assembly
9. ISO boots with omarchy-settings pre-installed
10. **Everything works from package** - no git clone, no manual copying!
## Benefits
### For Development
- ✅ No need to publish packages for testing
- ✅ Instant iteration on configs and binaries
- ✅ Test package integration in real ISO environment
- ✅ Single command builds everything
### For Production
- ✅ Same workflow works with published packages
- ✅ Fallback to manual copying if needed
- ✅ Reproducible builds
- ✅ Version-locked packages
## Troubleshooting
### Package build fails
```bash
# Check that omarchy-pkgs is mounted
docker run --rm -v "$PWD/../omarchy-pkgs:/omarchy-pkgs:ro" archlinux ls -la /omarchy-pkgs
```
### Manual file copying used instead of package
- Ensure omarchy-pkgs directory exists at `../omarchy-pkgs`
- Check Docker mount in omarchy-iso-make
- Look for "Mounting omarchy-pkgs" message during build
### Permission errors
- build-omarchy-packages.sh runs makepkg as `nobody` user
- Temporary sudo access granted for pacman operations
- Cleaned up after build
## Future Enhancements
1. Build full `omarchy` package locally
2. Remove git clone of omarchy-installer entirely
3. Use only packages for all file installation
4. Support building other omarchy-* packages
+73 -27
View File
@@ -9,7 +9,7 @@ pacman --noconfirm -Sy archiso git sudo base-devel jq grub
# Install omarchy-keyring for package verification during build
# The [omarchy] repo is defined in /configs/pacman-online.conf with SigLevel = Optional TrustAll
pacman --config /configs/pacman-online-${OMARCHY_MIRROR}.conf --noconfirm -Sy omarchy-keyring
pacman --config /configs/pacman-online.conf --noconfirm -Sy omarchy-keyring
pacman-key --populate omarchy
# Setup build locations
@@ -23,31 +23,50 @@ cp -r /archiso/configs/releng/* $build_cache_dir/
rm "$build_cache_dir/airootfs/etc/motd"
# Avoid using reflector for mirror identification as we are relying on the global CDN
rm -rf "$build_cache_dir/airootfs/etc/systemd/system/multi-user.target.wants/reflector.service"
rm "$build_cache_dir/airootfs/etc/systemd/system/multi-user.target.wants/reflector.service"
rm -rf "$build_cache_dir/airootfs/etc/systemd/system/reflector.service.d"
rm -rf "$build_cache_dir/airootfs/etc/xdg/reflector"
# Bring in our configs
cp -r /configs/* $build_cache_dir/
# Persist OMARCHY_MIRROR so it's available at install time
echo "$OMARCHY_MIRROR" > "$build_cache_dir/airootfs/root/omarchy_mirror"
# Setup Omarchy itself
if [[ -d /omarchy ]]; then
cp -rp /omarchy "$build_cache_dir/airootfs/root/omarchy"
# Build omarchy-settings package locally
# This package provides EVERYTHING needed for the ISO:
# - User configs (/etc/skel/)
# - System configs (/etc/)
# - Plymouth theme
# - Essential binaries (debug, upload-log)
# - Install scripts (/usr/share/omarchy/install/)
if [ -d "/omarchy-pkgs" ]; then
echo "========================================="
echo "Building omarchy-settings package locally"
echo "========================================="
source /builder/build-omarchy-packages.sh
# Add omarchy packages to ISO
# omarchy-settings provides configs and will be installed in ISO
# omarchy-installer provides install scripts and will be installed in ISO
echo "omarchy-settings" >> "$build_cache_dir/packages.x86_64"
echo "omarchy-installer" >> "$build_cache_dir/packages.x86_64"
# Copy built packages to offline mirror (for installation on target system)
cp "$OMARCHY_SETTINGS_PKG" "$offline_mirror_dir/"
cp "$OMARCHY_INSTALLER_PKG" "$offline_mirror_dir/"
cp "$OMARCHY_PKG" "$offline_mirror_dir/"
cp "$OMARCHY_LIMINE_PKG" "$offline_mirror_dir/"
echo "✓ omarchy-settings will be installed in ISO (configs, binaries)"
echo "✓ omarchy-installer will be installed in ISO (install scripts, helpers)"
echo "✓ omarchy-limine available in offline mirror for installation"
echo "✓ All omarchy packages available in offline mirror for installation"
echo "✓ No git clone needed - everything comes from packages!"
else
git clone -b $OMARCHY_INSTALLER_REF https://github.com/$OMARCHY_INSTALLER_REPO.git "$build_cache_dir/airootfs/root/omarchy"
echo "ERROR: /omarchy-pkgs not mounted!"
echo "The ISO build now requires omarchy-settings package."
echo "Please set OMARCHY_PKGS_PATH or ensure ../omarchy-pkgs exists."
exit 1
fi
# Make log uploader available in the ISO too
mkdir -p "$build_cache_dir/airootfs/usr/local/bin/"
cp "$build_cache_dir/airootfs/root/omarchy/bin/omarchy-upload-log" "$build_cache_dir/airootfs/usr/local/bin/omarchy-upload-log"
# Copy the Omarchy Plymouth theme to the ISO
mkdir -p "$build_cache_dir/airootfs/usr/share/plymouth/themes/omarchy"
cp -r "$build_cache_dir/airootfs/root/omarchy/default/plymouth/"* "$build_cache_dir/airootfs/usr/share/plymouth/themes/omarchy/"
# Download and verify Node.js binary for offline installation
NODE_DIST_URL="https://nodejs.org/dist/latest"
@@ -70,19 +89,41 @@ mkdir -p "$build_cache_dir/airootfs/opt/packages/"
cp "/tmp/$NODE_FILENAME" "$build_cache_dir/airootfs/opt/packages/"
# Add our additional packages to packages.x86_64
arch_packages=(linux-t2 git gum jq openssl plymouth tzupdate omarchy-keyring)
arch_packages=(linux-t2 git gum jq openssl plymouth tzupdate omarchy-keyring python-terminaltexteffects)
printf '%s\n' "${arch_packages[@]}" >>"$build_cache_dir/packages.x86_64"
# Build list of all the packages needed for the offline mirror
all_packages=($(cat "$build_cache_dir/packages.x86_64"))
all_packages+=($(grep -v '^#' "$build_cache_dir/airootfs/root/omarchy/install/omarchy-base.packages" | grep -v '^$'))
all_packages+=($(grep -v '^#' "$build_cache_dir/airootfs/root/omarchy/install/omarchy-other.packages" | grep -v '^$'))
all_packages+=($(grep -v '^#' /builder/archinstall.packages | grep -v '^$'))
# Start with packages.x86_64 but exclude locally-built packages
all_packages=($(cat "$build_cache_dir/packages.x86_64" | grep -vE '^(omarchy|omarchy-settings|omarchy-installer|omarchy-limine)$'))
# Add omarchy package lists from source, excluding locally-built packages
if [ -n "$OMARCHY_INSTALLER_SRC" ] && [ -d "$OMARCHY_INSTALLER_SRC" ]; then
# Filter out locally-built packages and known problematic packages
all_packages+=($(grep -v '^#' "$OMARCHY_INSTALLER_SRC/install/omarchy-base.packages" | grep -v '^$' | grep -vE '^(omarchy|omarchy-settings|omarchy-installer|omarchy-limine)$'))
all_packages+=($(grep -v '^#' "$OMARCHY_INSTALLER_SRC/install/omarchy-other.packages" | grep -v '^$' | grep -vE '^qt5-remoteobjects$'))
else
echo "WARNING: Could not find omarchy package lists, offline mirror may be incomplete"
fi
all_packages+=($(grep -v '^#' /builder/archinstall.packages | grep -v '^$' | grep -vE '^(omarchy|omarchy-settings|omarchy-installer|omarchy-limine)$'))
# Remove duplicates
all_packages=($(printf '%s\n' "${all_packages[@]}" | sort -u))
# Download all the packages to the offline mirror inside the ISO
# Use --needed to skip packages that are already in cache
# Use --noconfirm to avoid interactive prompts
mkdir -p /tmp/offlinedb
pacman --config /configs/pacman-online-${OMARCHY_MIRROR}.conf --noconfirm -Syw "${all_packages[@]}" --cachedir $offline_mirror_dir/ --dbpath /tmp/offlinedb
repo-add --new "$offline_mirror_dir/offline.db.tar.gz" "$offline_mirror_dir/"*.pkg.tar.zst
echo "Downloading ${#all_packages[@]} packages to offline mirror..."
pacman --config /configs/pacman-online.conf --noconfirm -Syw "${all_packages[@]}" --cachedir $offline_mirror_dir/ --dbpath /tmp/offlinedb --needed
# Build repository database for the offline mirror
# First, create the database from all downloaded packages
echo "Building offline repository database..."
repo-add "$offline_mirror_dir/offline.db.tar.gz" "$offline_mirror_dir/"*.pkg.tar.zst
# Note: Our locally built packages (omarchy, omarchy-settings) were copied earlier
# and are now included in the repository database
# Create a symlink to the offline mirror instead of duplicating it.
# mkarchiso needs packages at /var/cache/omarchy/mirror/offline in the container,
@@ -90,9 +131,14 @@ repo-add --new "$offline_mirror_dir/offline.db.tar.gz" "$offline_mirror_dir/"*.p
mkdir -p /var/cache/omarchy/mirror
ln -s "$offline_mirror_dir" "/var/cache/omarchy/mirror/offline"
# Copy the offline pacman.conf to the ISO's /etc directory so the live environment uses our
# same config when booted.
cp $build_cache_dir/pacman-offline.conf "$build_cache_dir/airootfs/etc/pacman.conf"
# Copy the pacman.conf to the ISO's /etc directory so the live environment uses our
# same config when booted
cp $build_cache_dir/pacman.conf "$build_cache_dir/airootfs/etc/pacman.conf"
# Create missing device-mapper udev rules file to silence mkinitcpio warning
# This file was removed in recent device-mapper versions but mkinitcpio still references it
mkdir -p /usr/lib/initcpio/udev
touch /usr/lib/initcpio/udev/11-dm-initramfs.rules
# Finally, we assemble the entire ISO
mkarchiso -v -w "$build_cache_dir/work/" -o "/out/" "$build_cache_dir/"
+219
View File
@@ -0,0 +1,219 @@
#!/bin/bash
# Build omarchy packages locally for ISO integration
# This runs inside the Docker container during ISO build
set -e
echo "========================================="
echo "Building omarchy-settings package locally"
echo "========================================="
# Packages will be built in /tmp/omarchy-pkg-build
BUILD_DIR="/tmp/omarchy-pkg-build"
# Clean old builds to avoid checksum issues
rm -rf "$BUILD_DIR"
mkdir -p "$BUILD_DIR"
# Create a writable build directory
WORK_DIR="/tmp/omarchy-pkg-work"
# Clean old work directory
rm -rf "$WORK_DIR"
mkdir -p "$WORK_DIR"
# Copy package files to writable location
echo "Setting up build directory..."
cp -r /omarchy-pkgs/pkgbuilds/omarchy-settings "$WORK_DIR/"
cd "$WORK_DIR/omarchy-settings"
# Set PKGDEST to control where built packages go
export PKGDEST="$BUILD_DIR"
# Check if we should use dev build (local source)
if [ -n "$OMARCHY_DEV_BUILD" ] && [ -d "/omarchy-installer" ]; then
echo "========================================="
echo "DEV MODE: Using locally mounted source"
echo "========================================="
# Source the dev PKGBUILD patch
source /builder/templates/dev-pkgbuild-patch.sh
convert_to_dev_pkgbuild "PKGBUILD"
BUILD_FILE="PKGBUILD.dev"
BUILD_FLAGS="--noconfirm --skippgpcheck --skipchecksums -f"
echo "✓ Will build from local source (no git clone)"
else
echo "========================================="
echo "PRODUCTION MODE: Using git source"
echo "========================================="
BUILD_FILE="PKGBUILD"
BUILD_FLAGS="--noconfirm --skippgpcheck"
echo "✓ Will git clone from repository"
fi
# Sync package databases (needed for dependency resolution)
echo "Syncing package databases..."
pacman -Sy --noconfirm
# Build the package
# Note: The container runs as root, but makepkg refuses to run as root
# Create a fresh build user (nobody account is expired in base image)
if ! id builder &>/dev/null; then
useradd -m -s /bin/bash builder
fi
# Give builder ownership
chown -R builder:builder "$WORK_DIR/omarchy-settings"
chown -R builder:builder "$BUILD_DIR"
# Allow builder to run pacman without password (needed for dependency installation)
echo "builder ALL=(ALL) NOPASSWD: /usr/bin/pacman" > /etc/sudoers.d/99-builder-pacman
chmod 440 /etc/sudoers.d/99-builder-pacman
# Build the package as builder user (makepkg will install dependencies via sudo)
su builder -c "cd $WORK_DIR/omarchy-settings && makepkg -p $BUILD_FILE $BUILD_FLAGS -s"
# Find the built package
BUILT_PACKAGE=$(ls -t "$BUILD_DIR"/omarchy-settings-*.pkg.tar.zst 2>/dev/null | head -n1)
if [ -z "$BUILT_PACKAGE" ]; then
echo "ERROR: Failed to build omarchy-settings package"
exit 1
fi
echo "✓ Successfully built: $(basename $BUILT_PACKAGE)"
echo "Package location: $BUILT_PACKAGE"
# Export the path for use by build-iso.sh
export OMARCHY_SETTINGS_PKG="$BUILT_PACKAGE"
# Export the source directory for accessing package lists before package is installed
# Check both possible locations (dev mode uses /omarchy-installer, prod uses src/)
if [ -d "/omarchy-installer" ]; then
export OMARCHY_INSTALLER_SRC="/omarchy-installer"
echo "✓ Package source available at: $OMARCHY_INSTALLER_SRC (mounted)"
elif [ -d "$WORK_DIR/omarchy-settings/src/omarchy-installer" ]; then
export OMARCHY_INSTALLER_SRC="$WORK_DIR/omarchy-settings/src/omarchy-installer"
echo "✓ Package source available at: $OMARCHY_INSTALLER_SRC (from build)"
fi
echo "✓ Package contains everything needed for ISO (configs, package lists, binaries, theme)"
# Now build the omarchy-installer package
echo ""
echo "========================================="
echo "Building omarchy-installer package"
echo "========================================="
# Copy omarchy-installer package files to writable location
cp -r /omarchy-pkgs/pkgbuilds/omarchy-installer "$WORK_DIR/"
cd "$WORK_DIR/omarchy-installer"
# Check if we should use dev build
if [ -n "$OMARCHY_DEV_BUILD" ] && [ -d "/omarchy-installer" ]; then
echo "DEV MODE: Using locally mounted source"
# Generate dev PKGBUILD
source /builder/templates/dev-pkgbuild-patch.sh
convert_to_dev_pkgbuild "PKGBUILD"
INSTALLER_BUILD_FILE="PKGBUILD.dev"
INSTALLER_BUILD_FLAGS="--noconfirm --skippgpcheck --skipchecksums -f"
else
echo "PRODUCTION MODE: Using git source"
INSTALLER_BUILD_FILE="PKGBUILD"
INSTALLER_BUILD_FLAGS="--noconfirm --skippgpcheck"
fi
# Give builder ownership
chown -R builder:builder "$WORK_DIR/omarchy-installer"
# Build the omarchy-installer package
su builder -c "cd $WORK_DIR/omarchy-installer && makepkg -p $INSTALLER_BUILD_FILE $INSTALLER_BUILD_FLAGS -d"
# Find the built package
OMARCHY_INSTALLER_PKG=$(ls -t "$BUILD_DIR"/omarchy-installer-*.pkg.tar.zst 2>/dev/null | head -n1)
if [ -z "$OMARCHY_INSTALLER_PKG" ]; then
echo "ERROR: Failed to build omarchy-installer package"
exit 1
fi
echo "✓ Successfully built: $(basename $OMARCHY_INSTALLER_PKG)"
echo "Package location: $OMARCHY_INSTALLER_PKG"
# Now build the full omarchy package for offline mirror
echo ""
echo "========================================="
echo "Building omarchy meta-package"
echo "========================================="
# Copy omarchy package files to writable location
cp -r /omarchy-pkgs/pkgbuilds/omarchy "$WORK_DIR/"
cd "$WORK_DIR/omarchy"
# Check if we should use dev build
if [ -n "$OMARCHY_DEV_BUILD" ] && [ -d "/omarchy-installer" ]; then
echo "DEV MODE: Using locally mounted source"
# Generate dev PKGBUILD
source /builder/templates/dev-pkgbuild-patch.sh
convert_to_dev_pkgbuild "PKGBUILD"
OMARCHY_BUILD_FILE="PKGBUILD.dev"
OMARCHY_BUILD_FLAGS="--noconfirm --skippgpcheck --skipchecksums -f"
else
echo "PRODUCTION MODE: Using git source"
OMARCHY_BUILD_FILE="PKGBUILD"
OMARCHY_BUILD_FLAGS="--noconfirm --skippgpcheck"
fi
# Give builder ownership
chown -R builder:builder "$WORK_DIR/omarchy"
# Build the omarchy package
# Note: Skip dependency checks (-d) since we're just packaging files, not running them
# The dependencies will be resolved when the package is installed in the actual system
su builder -c "cd $WORK_DIR/omarchy && makepkg -p $OMARCHY_BUILD_FILE $OMARCHY_BUILD_FLAGS -d"
# Find the built package
OMARCHY_PKG=$(ls -t "$BUILD_DIR"/omarchy-*.pkg.tar.zst 2>/dev/null | head -n1)
if [ -z "$OMARCHY_PKG" ]; then
echo "ERROR: Failed to build omarchy package"
exit 1
fi
echo "✓ Successfully built: $(basename $OMARCHY_PKG)"
echo "Package location: $OMARCHY_PKG"
# Now build the omarchy-limine package
echo ""
echo "========================================="
echo "Building omarchy-limine package"
echo "========================================="
# Copy omarchy-limine package files to writable location
cp -r /omarchy-pkgs/pkgbuilds/omarchy-limine "$WORK_DIR/"
cd "$WORK_DIR/omarchy-limine"
# Give builder ownership
chown -R builder:builder "$WORK_DIR/omarchy-limine"
# Build the omarchy-limine package (no source needed, just config files)
su builder -c "cd $WORK_DIR/omarchy-limine && makepkg --noconfirm --skippgpcheck -d"
# Find the built package
OMARCHY_LIMINE_PKG=$(ls -t "$BUILD_DIR"/omarchy-limine-*.pkg.tar.zst 2>/dev/null | head -n1)
if [ -z "$OMARCHY_LIMINE_PKG" ]; then
echo "ERROR: Failed to build omarchy-limine package"
exit 1
fi
echo "✓ Successfully built: $(basename $OMARCHY_LIMINE_PKG)"
echo "Package location: $OMARCHY_LIMINE_PKG"
# Export for use by build-iso.sh
export OMARCHY_PKG="$OMARCHY_PKG"
export OMARCHY_INSTALLER_PKG="$OMARCHY_INSTALLER_PKG"
export OMARCHY_LIMINE_PKG="$OMARCHY_LIMINE_PKG"
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
# Patch to convert production PKGBUILD to dev PKGBUILD
# This script modifies PKGBUILD to use local source instead of git clone
convert_to_dev_pkgbuild() {
local pkgbuild_path="$1"
local dev_pkgbuild_path="${pkgbuild_path}.dev"
echo "Creating dev PKGBUILD from production version..."
# Copy production PKGBUILD
cp "$pkgbuild_path" "$dev_pkgbuild_path"
# Remove git source (replace with empty source array)
sed -i '/^source=/c\source=()' "$dev_pkgbuild_path"
sed -i '/^sha256sums=/c\sha256sums=()' "$dev_pkgbuild_path"
# Replace cd "omarchy-installer" with cd "/omarchy-installer" to use mounted directory directly
sed -i 's|cd "omarchy-installer"|cd "/omarchy-installer"|g' "$dev_pkgbuild_path"
echo "✓ Dev PKGBUILD created: $dev_pkgbuild_path"
echo "✓ Will use /omarchy-installer directly (no git clone, no symlinks)"
}
@@ -1,14 +0,0 @@
# Fix Panther Lake audio: blacklist SOF modules that conflict with HDA drivers
# (prevents boot issues on Dell XPS 2026 and similar Panther Lake systems)
blacklist snd_sof_pci_intel_ptl
blacklist snd_sof_pci_intel_lnl
blacklist snd_sof_pci_intel_mtl
blacklist snd_sof_intel_hda_generic
blacklist snd_sof_intel_hda_common
blacklist snd_sof_intel_hda
blacklist snd_sof_pci
blacklist snd_sof
blacklist soundwire_intel
blacklist snd_soc_cs35l56_sdw
blacklist snd_soc_cs35l56
blacklist snd_soc_skl_hda_dsp
+1 -1
View File
@@ -1,3 +1,3 @@
[Daemon]
Theme=omarchy
ShowDelay=0
ShowDelay=0
+127 -104
View File
@@ -2,41 +2,147 @@
set -euo pipefail
use_omarchy_helpers() {
export OMARCHY_PATH="/root/omarchy"
export OMARCHY_INSTALL="/root/omarchy/install"
export OMARCHY_INSTALL_LOG_FILE="/var/log/omarchy-install.log"
export OMARCHY_MIRROR="$(cat /root/omarchy_mirror)"
source /root/omarchy/install/helpers/all.sh
# Use omarchy-settings package location (installed in ISO)
export OMARCHY_PATH="/usr/share/omarchy"
export OMARCHY_INSTALL="/usr/share/omarchy/install"
export OMARCHY_INSTALL_LOG_FILE="/var/log/archinstall/install.log"
# Load presentation helpers (clear_logo, etc.) and logging helpers (start_log_output, etc.)
source /usr/share/omarchy/install/helpers/all.sh
}
run_configurator() {
set_tokyo_night_colors
./configurator
export OMARCHY_USER="$(jq -r '.users[0].username' user_credentials.json)"
export OMARCHY_USER="$(jq -r '.username' user_info.json)"
export OMARCHY_USER_PASSWORD_HASH="$(jq -r '.password_hash' user_info.json)"
export OMARCHY_USER_NAME="$(cat user_full_name.txt)"
export OMARCHY_USER_EMAIL="$(cat user_email_address.txt)"
}
install_arch() {
install_omarchy() {
clear_logo
gum style --foreground 3 --padding "1 0 0 $PADDING_LEFT" "Installing..."
echo
# Copy user info to /tmp on the ISO side
# omarchy-install will copy these into the chroot at the right time
cp user_full_name.txt /tmp/omarchy-user-name.txt 2>/dev/null || true
cp user_email_address.txt /tmp/omarchy-user-email.txt 2>/dev/null || true
# Check if advanced disk mode (partition TUI)
EDIT_PARTITIONS_FLAG=""
if [[ -f edit_partitions_flag.txt ]]; then
EDIT_PARTITIONS_FLAG=$(cat edit_partitions_flag.txt | tr -d '\n' | xargs)
fi
# ADVANCED MODE: Run disk configuration TUI before installation
if [[ -n "$EDIT_PARTITIONS_FLAG" ]]; then
clear_logo
gum style --foreground 3 --padding "1 0 0 $PADDING_LEFT" "Configuring disk layout..."
echo
# Run omarchy-disk-config to show partition TUI and validate
# This updates user_configuration.json in place
/usr/share/omarchy/bin/omarchy-disk-config \
--config user_configuration.json \
--creds user_credentials.json
disk_config_exit_code=$?
if [[ $disk_config_exit_code -ne 0 ]]; then
echo "ERROR: Disk configuration failed or was cancelled"
exit $disk_config_exit_code
fi
clear_logo
gum style --foreground 3 --padding "1 0 0 $PADDING_LEFT" "Disk configuration complete!"
echo
sleep 1
fi
# Run installation with the configuration (possibly updated by disk-config)
# Create backup log file for raw output (with ANSI codes stripped)
touch /var/log/omarchy-install.log
# Ensure archinstall log directory exists
sudo mkdir -p /var/log/archinstall
# Prepare chroot log location (will be created by chroot script when it runs)
sudo mkdir -p /mnt/var/log
CHROOT_LOG="/mnt/var/log/omarchy-install-chroot.log"
# Start tailing BOTH logs with tail -F (follows multiple files, waits for files to appear)
# This will automatically pick up the chroot log when it's created
start_log_output "/var/log/omarchy-install.log" "$CHROOT_LOG"
# Run omarchy-install and capture output
# archinstall writes to /var/log/archinstall/install.log internally
# We also capture stdout/stderr to our backup log (stripping ANSI codes for clean viewing)
/usr/share/omarchy/bin/omarchy-install \
--config user_configuration.json \
--creds user_credentials.json \
2>&1 | sed -u 's/\x1b\[[0-9;]*[a-zA-Z]//g' | tee -a /var/log/omarchy-install.log >/dev/null
install_exit_code=$?
# Check if omarchy-install failed
if [[ $install_exit_code -ne 0 ]]; then
stop_log_output
echo
echo "ERROR: Installation failed (exit code $install_exit_code)"
echo "Check /var/log/omarchy-install.log and /mnt/var/log/omarchy-install-chroot.log for details"
exit $install_exit_code
fi
start_log_output
# Set CURRENT_SCRIPT for the trap to display better when nothing is returned for some reason
CURRENT_SCRIPT="install_base_system"
install_base_system > >(sed -u 's/\x1b\[[0-9;]*[a-zA-Z]//g' >>/var/log/omarchy-install.log) 2>&1
unset CURRENT_SCRIPT
# Installation succeeded - stop log output BEFORE doing anything else
stop_log_output
}
install_omarchy() {
chroot_bash -lc "sudo pacman -S --noconfirm --needed gum" >/dev/null
chroot_bash -lc "source /home/$OMARCHY_USER/.local/share/omarchy/install.sh || bash"
# Reboot if requested by installer
if [[ -f /mnt/var/tmp/omarchy-install-completed ]]; then
# Copy and merge logs to installed system for user access (silent)
{
sudo cp /var/log/omarchy-install.log /mnt/var/log/omarchy-install.log 2>/dev/null || true
sudo cp /var/log/archinstall/install.log /mnt/var/log/archinstall-install.log 2>/dev/null || true
# Create a merged log with all phases
{
echo "========================================"
echo "Omarchy Installation - Complete Log"
echo "Generated: $(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================"
echo
echo "=== Phase 1: Main Installation (ISO) ==="
cat /var/log/omarchy-install.log 2>/dev/null || echo "(No main install log found)"
echo
echo "=== Phase 2: Chroot Configuration ==="
cat "$CHROOT_LOG" 2>/dev/null || echo "(No chroot log found)"
echo
echo "========================================"
echo "Installation Complete"
echo "========================================"
} | sudo tee /mnt/var/log/omarchy-install-full.log >/dev/null
} >/dev/null 2>&1
# Show completion screen
clear
echo
tte -i /usr/share/omarchy/logo.txt --canvas-width 0 --anchor-text c --frame-rate 920 laseretch
echo
# Display installation time if available
if [[ -f /mnt/tmp/omarchy-install-time.txt ]]; then
TOTAL_TIME=$(cat /mnt/tmp/omarchy-install-time.txt)
echo "Installed in $TOTAL_TIME" | tte --canvas-width 0 --anchor-text c --frame-rate 640 print
else
echo "Finished installing" | tte --canvas-width 0 --anchor-text c --frame-rate 640 print
fi
echo
# Prompt for reboot (centered)
BUTTON_WIDTH=12 # "Reboot Now" + padding
BUTTON_PADDING=$(( (TERM_WIDTH - BUTTON_WIDTH) / 2 ))
if gum confirm --padding "0 0 0 $BUTTON_PADDING" --show-help=false --default --affirmative "Reboot Now" --negative "" ""; then
clear
reboot
fi
}
@@ -68,91 +174,8 @@ set_tokyo_night_colors() {
fi
}
install_base_system() {
# Initialize and populate the keyring
pacman-key --init
pacman-key --populate archlinux
pacman-key --populate omarchy
# Sync the offline database so pacman can find packages
pacman -Sy --noconfirm
# Ensure that no mounts exist from past install attempts
findmnt -R /mnt >/dev/null && umount -R /mnt
# Workarounds for archinstall 4.2 regressions under Python 3.14:
# 1. sync_log_to_install_medium: `self.target / absolute_logfile` drops
# self.target because the RHS is absolute, so Path.copy() raises EINVAL
# (source == target).
# 2. _add_limine_bootloader: `Path.copy(efi_dir_path)` raises IsADirectoryError
# because 3.14's Path.copy treats target as a literal path, not a directory
# (shutil.copy used to auto-append the source filename).
sed -i \
-e 's|logfile_target = self\.target / absolute_logfile$|logfile_target = self.target / absolute_logfile.relative_to("/")|' \
-e 's|(limine_path / file)\.copy(efi_dir_path)|(limine_path / file).copy(efi_dir_path / file)|' \
-e "s|(limine_path / 'limine-bios.sys')\.copy(boot_limine_path)|(limine_path / 'limine-bios.sys').copy(boot_limine_path / 'limine-bios.sys')|" \
/usr/lib/python3.14/site-packages/archinstall/lib/installer.py
# Install using files generated by the ./configurator
# Skip NTP and WKD sync since we're offline (keyring is pre-populated in ISO)
archinstall \
--config user_configuration.json \
--creds user_credentials.json \
--silent \
--skip-ntp \
--skip-wkd \
--skip-wifi-check
# After archinstall sets up the base system but before our installer runs,
# we need to ensure the offline pacman.conf is in place
cp /etc/pacman.conf /mnt/etc/pacman.conf
# Mount the offline mirror so it's accessible in the chroot
mkdir -p /mnt/var/cache/omarchy/mirror/offline
mount --bind /var/cache/omarchy/mirror/offline /mnt/var/cache/omarchy/mirror/offline
# Mount the packages dir so it's accessible in the chroot
mkdir -p /mnt/opt/packages
mount --bind /opt/packages /mnt/opt/packages
# No need to ask for sudo during the installation (omarchy itself responsible for removing after install)
mkdir -p /mnt/etc/sudoers.d
cat >/mnt/etc/sudoers.d/99-omarchy-installer <<EOF
root ALL=(ALL:ALL) NOPASSWD: ALL
%wheel ALL=(ALL:ALL) NOPASSWD: ALL
$OMARCHY_USER ALL=(ALL:ALL) NOPASSWD: ALL
EOF
chmod 440 /mnt/etc/sudoers.d/99-omarchy-installer
# Copy the local omarchy repo to the user's home directory
mkdir -p /mnt/home/$OMARCHY_USER/.local/share/
cp -r /root/omarchy /mnt/home/$OMARCHY_USER/.local/share/
chown -R 1000:1000 /mnt/home/$OMARCHY_USER/.local/
# Ensure all necessary scripts are executable
find /mnt/home/$OMARCHY_USER/.local/share/omarchy -type f -path "*/bin/*" -exec chmod +x {} \;
chmod +x /mnt/home/$OMARCHY_USER/.local/share/omarchy/boot.sh 2>/dev/null || true
chmod +x /mnt/home/$OMARCHY_USER/.local/share/omarchy/default/waybar/indicators/screen-recording.sh 2>/dev/null || true
chmod +x /mnt/home/$OMARCHY_USER/.local/share/omarchy/default/waybar/indicators/idle.sh 2>/dev/null || true
chmod +x /mnt/home/$OMARCHY_USER/.local/share/omarchy/default/waybar/indicators/notification-silencing.sh 2>/dev/null || true
}
chroot_bash() {
HOME=/home/$OMARCHY_USER \
arch-chroot -u $OMARCHY_USER /mnt/ \
env OMARCHY_CHROOT_INSTALL=1 \
OMARCHY_USER_NAME="$(<user_full_name.txt)" \
OMARCHY_USER_EMAIL="$(<user_email_address.txt)" \
OMARCHY_MIRROR="$OMARCHY_MIRROR" \
USER="$OMARCHY_USER" \
HOME="/home/$OMARCHY_USER" \
/bin/bash "$@"
}
if [[ $(tty) == "/dev/tty1" ]]; then
use_omarchy_helpers
run_configurator
install_arch
install_omarchy
fi
+149 -57
View File
@@ -6,6 +6,27 @@ source "$OMARCHY_INSTALL/helpers/all.sh"
# Ensure tzupdate is available for guessing timezone
command -v tzupdate >/dev/null || sudo pacman -Sy --noconfirm tzupdate
# Build the package list for archinstall from omarchy-base.packages
build_package_json() {
local packages=("base-devel" "git" "omarchy-keyring" "omarchy-settings")
# Read omarchy-base.packages and add to array
while IFS= read -r pkg; do
[[ -z "$pkg" || "$pkg" =~ ^# ]] && continue
packages+=("$pkg")
done < "$OMARCHY_INSTALL/omarchy-base.packages"
# Format as JSON array
local json="["
for i in "${!packages[@]}"; do
json+="\"${packages[$i]}\""
[[ $i -lt $((${#packages[@]} - 1)) ]] && json+=", "
done
json+="]"
echo "$json"
}
abort() {
gum style "${1:-Aborted installation}"
echo
@@ -176,37 +197,16 @@ done
get_disk_info() {
local device="$1"
local size vendor model label
local size model
# Get basic disk information
size=$(lsblk -dno SIZE "$device" 2>/dev/null)
vendor=$(lsblk -dno VENDOR "$device" 2>/dev/null | sed 's/ *$//')
model=$(lsblk -dno MODEL "$device" 2>/dev/null | sed 's/ *$//')
# Combine vendor and model, avoiding duplication
label=""
if [[ -n $vendor && -n $model ]]; then
if [[ $model == *$vendor* ]]; then
label="$model"
else
label="$vendor $model"
fi
elif [[ -n $model ]]; then
label="$model"
elif [[ -n $vendor ]]; then
label="$vendor"
fi
model=$(lsblk -dno MODEL "$device" 2>/dev/null | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
# Format display string
local display="$device"
[[ -n $size ]] && display="$display ($size)"
[[ -n $label ]] && display="$display - $label"
# Append compact partition summary
local part_summary
part_summary=$(lsblk -nro TYPE,NAME,FSTYPE,MOUNTPOINT "$device" 2>/dev/null | \
awk '$1=="part" { printf "%s%s%s", s, ($3==""?"unknown":$3), ($4==""?"":"("$4")"); s=", " }')
[[ -n $part_summary ]] && display+=" [$part_summary]"
[[ -n "$size" ]] && display="$display ($size)"
[[ -n "$model" ]] && display="$display - $model"
echo "$display"
}
@@ -235,23 +235,54 @@ disk_form() {
disk_options="$disk_options$disk_info"$'\n'
fi
done <<<"$available_disks"
# Add "Advanced Disk Config" option at the bottom
disk_options="${disk_options}Advanced Disk Config (custom partitioning)"
selected_display=$(echo "$disk_options" | gum choose --header "Select install disk") || abort
disk=$(echo "$selected_display" | awk '{print $1}')
# Check if user selected advanced config
if echo "$selected_display" | grep -q "Advanced Disk Config"; then
USE_ADVANCED_DISK_CONFIG=1
EDIT_PARTITIONS="--edit-partitions"
disk="" # Will be determined by TUI
else
USE_ADVANCED_DISK_CONFIG=0
disk=$(echo "$selected_display" | awk '{print $1}')
fi
}
disk_form
while true; do
gum style "Everything will be overwritten. There is no recovery possible."
# If user selected advanced config, skip to partition editor
if [[ $USE_ADVANCED_DISK_CONFIG -eq 1 ]]; then
gum style --foreground 3 "Advanced disk configuration selected."
gum style "You'll configure partitions in the archinstall TUI."
echo
if gum confirm --affirmative "Yes, format disk" --negative "No, change it" "Confirm overwriting ${disk}"; then
break
else
disk_form
fi
done
EDIT_PARTITIONS="--edit-partitions"
else
# Normal flow: confirm disk wipe and use default layout
while true; do
gum style "Everything will be overwritten. There is no recovery possible."
echo
if gum confirm --affirmative "Yes, format disk" --negative "No, change it" "Confirm overwriting ${disk}"; then
break
else
disk_form
# Check again if user switched to advanced config
if [[ $USE_ADVANCED_DISK_CONFIG -eq 1 ]]; then
gum style --foreground 3 "Advanced disk configuration selected."
gum style "You'll configure partitions in the archinstall TUI."
echo
EDIT_PARTITIONS="--edit-partitions"
break
fi
fi
done
# Normal flow always uses default layout (no editing)
EDIT_PARTITIONS=""
fi
clear
@@ -260,6 +291,9 @@ clear
echo "$full_name" >user_full_name.txt
echo "$email_address" >user_email_address.txt
# Save partition editing preference
echo "$EDIT_PARTITIONS" >edit_partitions_flag.txt
# Escape username and password for safe inclusion
password_escaped=$(echo -n "$password" | jq -Rsa)
password_hash_escaped=$(echo -n "$password_hash" | jq -Rsa)
@@ -271,27 +305,36 @@ cat <<-_EOF_ >user_credentials.json
"root_enc_password": $password_hash_escaped,
"users": [
{
"username": $username_escaped,
"enc_password": $password_hash_escaped,
"groups": [],
"sudo": true,
"username": $username_escaped
"sudo": true
}
]
}
_EOF_
# Setup partition layout
disk_size=$(lsblk -bdno SIZE "$disk")
mib=$((1024 * 1024))
gib=$((mib * 1024))
disk_size_in_mib=$((disk_size / mib * mib)) # Rounds to nearest MiB
# Save user info separately so we can create the user after packages are installed
cat <<-_EOF_ >user_info.json
{
"username": $username_escaped,
"password_hash": $password_hash_escaped
}
_EOF_
gpt_backup_reserve=$((mib))
boot_partition_start=$((mib))
boot_partition_size=$((2 * gib))
# Setup partition layout (only for non-advanced config)
if [[ $USE_ADVANCED_DISK_CONFIG -eq 0 ]]; then
disk_size=$(lsblk -bdno SIZE "$disk")
mib=$((1024 * 1024))
gib=$((mib * 1024))
disk_size_in_mib=$((disk_size / mib * mib)) # Rounds to nearest MiB
main_partition_start=$((boot_partition_size + boot_partition_start))
main_partition_size=$((disk_size_in_mib - main_partition_start - gpt_backup_reserve))
gpt_backup_reserve=$((mib))
boot_partition_start=$((mib))
boot_partition_size=$((2 * gib))
main_partition_start=$((boot_partition_size + boot_partition_start))
main_partition_size=$((disk_size_in_mib - main_partition_start - gpt_backup_reserve))
fi
# Detect T2 Mac and set appropriate kernel
if lspci -nn 2>/dev/null | grep -q "106b:180[12]"; then
@@ -300,14 +343,67 @@ else
kernel_choice="linux"
fi
cat <<-_EOF_ >user_configuration.json
# Build the complete package list including all Omarchy packages
packages_json=$(build_package_json)
# Generate configuration based on mode
if [[ $USE_ADVANCED_DISK_CONFIG -eq 1 ]]; then
# Minimal config for advanced mode - user will configure disk in TUI
cat <<-_EOF_ >user_configuration.json
{
"app_config": null,
"archinstall-language": "English",
"auth_config": {},
"audio_config": { "audio": "pipewire" },
"bootloader": "Limine",
"custom_commands": [],
"custom_commands": [
"/usr/share/omarchy/bin/omarchy-install-final-configurations.sh"
],
"hostname": "$hostname",
"kernels": [ "$kernel_choice" ],
"network_config": { "type": "iso" },
"ntp": true,
"parallel_downloads": 8,
"script": null,
"services": [],
"swap": true,
"timezone": "$timezone",
"locale_config": {
"kb_layout": "$keyboard",
"sys_enc": "UTF-8",
"sys_lang": "en_US.UTF-8"
},
"mirror_config": {
"custom_repositories": [],
"custom_servers": [
{"url": "https://mirror.omarchy.org/\$repo/os/\$arch"},
{"url": "https://mirror.rackspace.com/archlinux/\$repo/os/\$arch"},
{"url": "https://geo.mirror.pkgbuild.com/\$repo/os/\$arch"}
],
"mirror_regions": {},
"optional_repositories": []
},
"packages": $packages_json,
"profile_config": {
"gfx_driver": null,
"greeter": null,
"profile": {}
},
"version": "3.0.9"
}
_EOF_
else
# Full config with default partition layout
cat <<-_EOF_ >user_configuration.json
{
"app_config": null,
"archinstall-language": "English",
"auth_config": {},
"audio_config": { "audio": "pipewire" },
"bootloader": "Limine",
"custom_commands": [
"/usr/share/omarchy/bin/omarchy-install-final-configurations.sh"
],
"disk_config": {
"btrfs_options": {
"snapshot_config": {
@@ -374,8 +470,7 @@ cat <<-_EOF_ >user_configuration.json
"encryption_type": "luks",
"lvm_volumes": [],
"iter_time": 2000,
"partitions": [ "8c2c2b92-1070-455d-b76a-56263bab24aa" ],
"encryption_password": $password_escaped
"partitions": [ "8c2c2b92-1070-455d-b76a-56263bab24aa" ]
}
},
"hostname": "$hostname",
@@ -402,11 +497,7 @@ cat <<-_EOF_ >user_configuration.json
"mirror_regions": {},
"optional_repositories": []
},
"packages": [
"base-devel",
"git",
"omarchy-keyring"
],
"packages": $packages_json,
"profile_config": {
"gfx_driver": null,
"greeter": null,
@@ -415,6 +506,7 @@ cat <<-_EOF_ >user_configuration.json
"version": "3.0.9"
}
_EOF_
fi
if [[ $1 == "dry" ]]; then
echo -e "User Configuration:"
@@ -2,4 +2,4 @@ title Omarchy (x86_64, UEFI)
sort-key 01
linux /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux-t2
initrd /%INSTALL_DIR%/boot/x86_64/initramfs-linux-t2.img
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% quiet splash xe.enable_panel_replay=0
options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% quiet splash
+2 -2
View File
@@ -54,13 +54,13 @@ timeout_style=hidden
menuentry "Omarchy (%ARCH%, ${archiso_platform})" --class arch --class gnu-linux --class gnu --class os --id 'archlinux' {
set gfxpayload=keep
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-t2 archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% quiet splash xe.enable_panel_replay=0
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-t2 archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% quiet splash
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux-t2.img
}
menuentry "Omarchy with speakup screen reader (%ARCH%, ${archiso_platform})" --hotkey s --class arch --class gnu-linux --class gnu --class os --id 'archlinux-accessibility' {
set gfxpayload=keep
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-t2 archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on xe.enable_panel_replay=0
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-t2 archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux-t2.img
}
+2 -2
View File
@@ -30,13 +30,13 @@ timeout_style=hidden
menuentry "Omarchy (%ARCH%, ${archiso_platform})" --class arch --class gnu-linux --class gnu --class os --id 'archlinux' {
set gfxpayload=keep
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-t2 archisobasedir=%INSTALL_DIR% img_dev=UUID=${archiso_img_dev_uuid} img_loop="${iso_path}" quiet splash xe.enable_panel_replay=0
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-t2 archisobasedir=%INSTALL_DIR% img_dev=UUID=${archiso_img_dev_uuid} img_loop="${iso_path}" quiet splash
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux-t2.img
}
menuentry "Omarchy with speakup screen reader (%ARCH%, ${archiso_platform})" --hotkey s --class arch --class gnu-linux --class gnu --class os --id 'archlinux-accessibility' {
set gfxpayload=keep
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-t2 archisobasedir=%INSTALL_DIR% img_dev=UUID=${archiso_img_dev_uuid} img_loop="${iso_path}" accessibility=on xe.enable_panel_replay=0
linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux-t2 archisobasedir=%INSTALL_DIR% img_dev=UUID=${archiso_img_dev_uuid} img_loop="${iso_path}" accessibility=on
initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux-t2.img
}
-34
View File
@@ -1,34 +0,0 @@
#
# /etc/pacman.conf
#
# See the pacman.conf(5) manpage for option and repository directives
[options]
HoldPkg = pacman glibc
Architecture = auto
ParallelDownloads = 5
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.
SigLevel = Required DatabaseOptional
LocalFileSigLevel = Optional
[core]
Server = https://mirror.omarchy.org/$repo/os/$arch
Include = /etc/pacman.d/mirrorlist
[extra]
Server = https://mirror.omarchy.org/$repo/os/$arch
Include = /etc/pacman.d/mirrorlist
[multilib]
Server = https://mirror.omarchy.org/$repo/os/$arch
Include = /etc/pacman.d/mirrorlist
[omarchy]
SigLevel = Optional TrustAll
Server = https://pkgs.omarchy.org/edge/$arch
[arch-mact2]
Server = https://github.com/NoaHimesaka1873/arch-mact2-mirror/releases/download/release
SigLevel = Never
-34
View File
@@ -1,34 +0,0 @@
#
# /etc/pacman.conf
#
# See the pacman.conf(5) manpage for option and repository directives
[options]
HoldPkg = pacman glibc
Architecture = auto
ParallelDownloads = 5
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.
SigLevel = Required DatabaseOptional
LocalFileSigLevel = Optional
[core]
Server = https://rc-mirror.omarchy.org/$repo/os/$arch
Include = /etc/pacman.d/mirrorlist
[extra]
Server = https://rc-mirror.omarchy.org/$repo/os/$arch
Include = /etc/pacman.d/mirrorlist
[multilib]
Server = https://rc-mirror.omarchy.org/$repo/os/$arch
Include = /etc/pacman.d/mirrorlist
[omarchy]
SigLevel = Optional TrustAll
Server = https://pkgs.omarchy.org/edge/$arch
[arch-mact2]
Server = https://github.com/NoaHimesaka1873/arch-mact2-mirror/releases/download/release
SigLevel = Never
@@ -27,7 +27,7 @@ Include = /etc/pacman.d/mirrorlist
[omarchy]
SigLevel = Optional TrustAll
Server = https://pkgs.omarchy.org/stable/$arch
Server = https://pkgs.omarchy.org/$arch
[arch-mact2]
Server = https://github.com/NoaHimesaka1873/arch-mact2-mirror/releases/download/release
File renamed without changes.
+1 -2
View File
@@ -10,7 +10,7 @@ install_dir="arch"
buildmodes=('iso')
bootmodes=('bios.syslinux' 'uefi.grub')
arch="x86_64"
pacman_conf="pacman-offline.conf"
pacman_conf="pacman.conf"
airootfs_image_type="squashfs"
airootfs_image_tool_options=('-comp' 'xz' '-Xbcj' 'x86' '-b' '1M' '-Xdict-size' '1M')
bootstrap_tarball_compression=('zstd' '-c' '-T0' '--auto-threads=logical' '--long' '-19')
@@ -22,5 +22,4 @@ file_permissions=(
["/usr/local/bin/choose-mirror"]="0:0:755"
["/root/configurator"]="0:0:755"
["/var/cache/omarchy/mirror/offline/"]="0:0:775"
["/usr/local/bin/omarchy-upload-log"]="0:0:755"
)
+2 -2
View File
@@ -6,7 +6,7 @@ ENDTEXT
MENU LABEL Omarchy install medium (x86_64, BIOS)
LINUX /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux-t2
INITRD /%INSTALL_DIR%/boot/x86_64/initramfs-linux-t2.img
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% quiet splash xe.enable_panel_replay=0
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% quiet splash
# Accessibility boot option
LABEL arch64speech
@@ -17,4 +17,4 @@ ENDTEXT
MENU LABEL Omarchy install medium (x86_64, BIOS) with ^speech
LINUX /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux-t2
INITRD /%INSTALL_DIR%/boot/x86_64/initramfs-linux-t2.img
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on xe.enable_panel_replay=0
APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on
-158
View File
@@ -1,158 +0,0 @@
# Dual-Boot Installation Support for Omarchy ISO
## Context
Currently, the Omarchy installer wipes the entire selected disk (`"wipe": true`) and creates two partitions: a 2GB FAT32 ESP and a LUKS-encrypted Btrfs root. This means users must dedicate an entire disk to Omarchy. Adding dual-boot support lets users install alongside an existing OS (typically Windows) while keeping the Omarchy partition encrypted.
## Core Approach: `pre_mounted_config`
**Do our own partitioning, then hand off to archinstall for package installation only.**
archinstall's `manual_partitioning` mode has a long history of bugs with existing partitions and LUKS (archinstall issues #998, #2072, #2444). Instead, we:
1. Detect existing partitions and free space ourselves (with `sgdisk`/`parted`)
2. Create the LUKS partition and Btrfs subvolumes ourselves (with `cryptsetup`/`mkfs.btrfs`)
3. Mount everything at `/mnt/archinstall`
4. Tell archinstall to use `"config_type": "pre_mounted_config"` — it installs packages into our pre-mounted tree and does zero disk operations
5. Handle encryption config (crypttab, mkinitcpio hooks, bootloader cmdline) in post-install
The existing full-disk path stays completely unchanged. Dual-boot is a parallel code path.
## Implementation Guardrails
- **Run target changes in chroot** — all target-system commands (`mkinitcpio -P`, `limine-scan`, bootloader config updates) must run via `arch-chroot "$INSTALL_ROOT" ...`
- **Edit target files under `$INSTALL_ROOT` only** — write `mkinitcpio` hook config, `limine.conf`, `crypttab`, and related files in `$INSTALL_ROOT/etc` and `$INSTALL_ROOT/boot` (never in the live environment root)
- **Settle kernel partition state** — after creating the new partition, run `partprobe "$DISK"` and `udevadm settle` before `cryptsetup` to avoid device-node races
- **Deterministic fstab** — always generate `/etc/fstab` for dual-boot with overwrite semantics (e.g., `genfstab -U "$INSTALL_ROOT" > "$INSTALL_ROOT/etc/fstab"`) to avoid duplicate entries on retries
- **Hook compatibility** — when updating mkinitcpio hooks, add `encrypt` for busybox initramfs or `sd-encrypt` for systemd initramfs; never assume one universally
- **Safe rollback on failure** — on any dual-boot setup failure: unmount mounts, close LUKS mapping, and surface recovery guidance; optionally delete the just-created partition only if it was created in this run
## Scope Constraints
- **UEFI/GPT only** — dual-boot on MBR/BIOS is too fragile; those users get full-disk only
- **No auto-shrinking** — users must free up space from their existing OS first (e.g., Windows Disk Management). Too risky to shrink partitions automatically
- **Minimum 20GB free space** required for Omarchy
## User Flow
```
Select disk → Detect layout →
If disk has existing OS + ESP + ≥20GB free space:
"How would you like to install?"
→ "Install alongside Windows (use 85GB free space)"
→ "Erase entire disk"
If disk is empty or no free space:
Existing "Confirm overwriting" flow (unchanged)
```
## File Changes
### 1. `configs/airootfs/root/configurator`
**Add after disk selection (line 233), before config generation:**
- `detect_disk_layout()` — examines selected disk for:
- GPT partition table (required for dual-boot)
- Existing ESP partition (via `lsblk` PARTTYPE matching EFI GUID)
- Free unallocated space (via `sgdisk -F`/`-E` or `parted print free`)
- OS detection for installer UX warnings/prompts (not boot correctness): mount ESP read-only, check common vendor paths like `EFI/Microsoft`, `EFI/systemd`, `EFI/ubuntu`, `EFI/fedora`, `EFI/GRUB`
- `install_mode_form()` — shows gum choose with dual-boot vs erase options when conditions are met. Sets `DUAL_BOOT=true/false`.
**Generate different JSON when `DUAL_BOOT=true`:**
```json
{
"disk_config": {
"config_type": "pre_mounted_config",
"mountpoint": "/mnt/archinstall"
}
}
```
No `device_modifications`, no `disk_encryption`, no `btrfs_options` — we handle all of that ourselves. Everything else (hostname, kernels, packages, etc.) stays the same.
**Write `install_mode.sh`** alongside the other config files:
```bash
DUAL_BOOT=true
DISK=/dev/nvme0n1
ESP_PARTITION=/dev/nvme0n1p1
```
### 2. `configs/airootfs/root/.automated_script.sh`
**Source `install_mode.sh`** in `run_configurator()` if it exists.
**Add `setup_dual_boot_partitions()`** — runs before archinstall when `DUAL_BOOT=true`:
1. Select the largest contiguous free region and run `sgdisk` to create new partition there (type 8309 = Linux LUKS)
2. `partprobe "$DISK"` + `udevadm settle` so new partition node is present
3. `cryptsetup luksFormat` + `luksOpen` with user's password
4. `mkfs.btrfs` on `/dev/mapper/omarchy_root`
5. Create Btrfs subvolumes: `@`, `@home`, `@log`, `@pkg`
6. Mount subvolumes at `/mnt/archinstall` with `compress=zstd`
7. Mount existing ESP (untouched) at `/mnt/archinstall/boot`
8. Generate target `/etc/fstab` from mounted filesystems before post-install steps
**Add `configure_dual_boot_post_install()`** — runs after archinstall:
1. Write `/etc/crypttab` entry using LUKS UUID/PARTUUID (stable identifier), e.g. `omarchy_root UUID=<luks-uuid> none luks,discard`
2. Update mkinitcpio hooks before `filesystems`: `encrypt` (busybox) or `sd-encrypt` (systemd)
3. Regenerate initramfs with `arch-chroot "$INSTALL_ROOT" mkinitcpio -P`
4. Configure Limine kernel cmdline to match initramfs mode:
- busybox `encrypt`: `cryptdevice=UUID=<uuid>:omarchy_root root=/dev/mapper/omarchy_root`
- systemd `sd-encrypt`: `rd.luks.name=<uuid>=omarchy_root root=/dev/mapper/omarchy_root` (or equivalent `rd.luks.uuid=` form)
5. Run `arch-chroot "$INSTALL_ROOT" limine-scan` to detect other OS boot entries (Windows, etc.)
6. Preserve current firmware default boot order unless user explicitly chooses to make Limine first (capture and restore `BootOrder` if `efibootmgr` side effects reorder entries)
**Use `$INSTALL_ROOT` variable** — defaults to `/mnt` for full-disk, `/mnt/archinstall` for dual-boot. Post-install steps (offline mirror bind-mount, sudoers, omarchy copy) reference this instead of hardcoded `/mnt`.
### 3. `builder/build-iso.sh` + `builder/archinstall.packages`
Add `parted` to packages available in the live ISO (needed for free space detection).
## Bootloader Coexistence
Limine installs to `ESP/EFI/arch-limine/` and registers via `efibootmgr`. This sits alongside:
- Windows Boot Manager at `ESP/EFI/Microsoft/Boot/bootmgfw.efi`
- Any other bootloader in its own ESP subdirectory
`limine-scan` auto-detects other UEFI boot entries and adds them to `limine.conf`. Users can choose OS at boot.
**Boot order policy**: creating a new EFI entry must not silently change the current default boot target. Keep existing firmware boot order by default and only promote Limine when user opts in.
Implementation note: when registering Limine with `efibootmgr`, do not pass reorder flags by default; if firmware still changes order, explicitly restore prior `BootOrder` unless the user opted to promote Limine.
**ESP size note**: Windows typically creates a 100-260MB ESP. This is enough for Limine + kernel + initramfs, but Snapper boot snapshots (which copy kernels to ESP) will be limited or disabled.
**ESP free-space threshold**:
- If ESP has `<100MB` free at install time: show a strong warning (do not block install)
- If ESP has `<64MB` free: hide dual-boot option and require full-disk install (or user frees space first)
**BitLocker warning**: If Windows is detected, show a warning about having the BitLocker recovery key ready, since modifying ESP/UEFI entries can trigger recovery.
## Edge Cases
| Scenario | Behavior |
|----------|----------|
| Disk is empty | Full-disk install only (current behavior) |
| Existing OS but no free space | Full-disk install only, with note to free space first |
| Free space < 20GB | Dual-boot option not shown |
| MBR/BIOS disk | Dual-boot option not shown |
| ESP < 100MB free after mount | Warn user; continue allowed |
| ESP < 64MB free after mount | Dual-boot option not shown |
| Multiple free regions | Use largest contiguous region |
| Partitioning/setup failure mid-run | Cleanup mounts/LUKS and abort safely |
## Verification
1. **Regression**: Full-disk install in VM with empty disk — must work identically to today
2. **Dual-boot with Windows**: VM with Windows installed + 40GB free space — detect Windows, install alongside, both OSes boot
3. **Dual-boot with Linux**: VM with another Linux + free space — similar test
4. **No free space**: VM with fully partitioned disk — dual-boot option should not appear
5. **Encryption**: After dual-boot install, verify LUKS unlock prompt at boot and encrypted root
6. **Initramfs mode**: Validate both busybox and systemd hook paths boot correctly (`encrypt` vs `sd-encrypt`)
7. **Boot order**: Verify existing default UEFI entry remains unchanged unless user opted to promote Limine
-224
View File
@@ -1,224 +0,0 @@
# OEM Install + First-Boot Setup Plan
## Goal
Create a second ISO flavor for OEM preloading that is installed to internal storage at factory time, then boots into customer setup on first power-on (no external media required), and ends in a fully encrypted Omarchy install.
## Product Requirements
- Factory can preload one image to internal disk.
- Customer first boot launches setup flow automatically (already works: `.automated_script.sh` runs configurator on tty1).
- Customer chooses credentials/settings.
- Final installed OS uses full-disk encryption (LUKS) with customer password (already works: configurator collects password and passes it to archinstall as `encryption_password`).
- Existing consumer ISO behavior remains unchanged.
---
## Chosen Architecture
### Bootstrapping model
- OEM devices ship with an OEM bootstrap ISO image written to internal disk.
- On first boot, `copytoram=y` kernel arg loads the live environment into RAM, freeing the internal disk.
- Bootstrap environment launches existing configurator + installer.
- Installer performs a fresh install to internal disk with LUKS encryption enabled.
- Bootstrap environment is overwritten during install.
### Recovery model: zero-recovery
- No permanent recovery partition.
- Simplest and strongest security story.
- Recovery requires external reinstall media.
---
## Non-Goals
- No on-device recovery workflow.
- No changes to normal installer UX outside explicit OEM mode checks and warnings.
- No factory credential provisioning.
---
## Repo Implementation Plan
## 1) Add OEM build mode
### Files
- `bin/omarchy-iso-make`
- `builder/build-iso.sh`
### Changes
- Add CLI flag `--oem` to `bin/omarchy-iso-make`.
- Pass `OMARCHY_MODE=oem` into Docker build env (default `normal`).
- In `builder/build-iso.sh`, write mode marker `/root/omarchy_mode` containing `oem` or `normal` (same pattern as `omarchy_mirror`).
- Keep OEM on current boot stack (`uefi.grub` + `bios.syslinux`); do not add or depend on systemd-boot paths.
- When `OMARCHY_MODE=oem`, inject `copytoram=y` into all shipped OEM boot entry points before `mkarchiso` runs:
- `grub/grub.cfg`: append to `linux` cmdline entries.
- `grub/loopback.cfg`: append to `linux` cmdline entries.
- `syslinux/archiso_sys-linux.cfg`: append to `APPEND` lines.
- When `OMARCHY_MODE=oem`, remove accessibility/speakup boot entries:
- `grub/grub.cfg`: remove `archlinux-accessibility` menuentry.
- `grub/loopback.cfg`: remove `archlinux-accessibility` menuentry.
- `syslinux/archiso_sys-linux.cfg`: remove `arch64speech` label block.
- When `OMARCHY_MODE=oem`, override `iso_application` in `profiledef.sh` to `"Omarchy OEM Installer"`.
- Rename output ISO with `-oem` suffix (in addition to existing `-$OMARCHY_INSTALLER_REF` suffix).
### Hardening
- Add post-injection build assertion that fails build if any expected boot config does not contain `copytoram=y`.
- Add post-injection build assertion that fails build if any OEM boot config still contains accessibility/speakup entries.
- Make injection idempotent (do not duplicate `copytoram=y` if already present).
### Acceptance criteria
- `./bin/omarchy-iso-make --oem` builds successfully.
- OEM ISO boot entries contain `copytoram=y` in all supported boot paths.
- OEM ISO does not expose accessibility/speakup boot entries.
- Normal build output and behavior remain unchanged.
### Suggested verification commands
- `bsdtar -xOf out/<iso>.iso boot/grub/grub.cfg | rg copytoram=y`
- `bsdtar -xOf out/<iso>.iso boot/grub/loopback.cfg | rg copytoram=y`
- `bsdtar -xOf out/<iso>.iso boot/syslinux/archiso_sys-linux.cfg | rg copytoram=y`
- `bsdtar -xOf out/<iso>.iso boot/grub/grub.cfg | rg 'accessibility|speakup'` (should return no matches)
- `bsdtar -xOf out/<iso>.iso boot/syslinux/archiso_sys-linux.cfg | rg 'speech|accessibility'` (should return no matches)
---
## 2) Add OEM safety guardrails
### Why
OEM image boots from internal disk and later wipes that same disk. Live environment must be fully in RAM before install begins.
### File
- `configs/airootfs/root/.automated_script.sh`
### Changes
- Before `run_configurator`, if `/root/omarchy_mode` is `oem`:
- Verify `/proc/cmdline` contains `copytoram=y`; abort with clear actionable error if missing.
- Verify factory hardware minimum using `MemTotal`:
- Require `MemTotal >= 8 GiB`.
- Abort with clear error if below minimum.
- Verify runtime memory safety using `MemAvailable`:
- Minimum threshold: `1.5 x ISO size + 1 GiB`.
- Abort with clear error if below threshold.
- Verify runtime state indicates live root is in RAM (for example, expected archiso runtime mounts present and boot media not actively mounted as install target path).
### Acceptance criteria
- OEM mode aborts safely if `copytoram=y` missing from cmdline.
- OEM mode aborts safely if `MemTotal < 8 GiB`.
- OEM mode aborts safely if `MemAvailable` below threshold.
- OEM mode aborts safely if runtime state does not confirm RAM-backed live environment.
### Error message requirements
- State exactly what failed.
- State why install is blocked.
- Provide next step (reboot using proper OEM media or use external recovery media).
---
## 3) OEM disk warning
### Why
With `copytoram=y`, archiso copies squashfs to RAM and releases boot device. Existing configurator call `findmnt -no SOURCE /run/archiso/bootmnt` may return empty, so exclusion filter is skipped and all physical disks are shown, including former boot disk. No disk selection logic change is required.
OEM mode still needs an unmistakable warning before destructive install proceeds.
### File
- `configs/airootfs/root/configurator`
### Changes
- Read mode from `/root/omarchy_mode`.
- If mode is `oem`, add OEM-specific warning text before existing destructive confirmation.
- Upgrade confirmation in OEM mode to explicit typed confirmation (for example: `YES, WIPE DISK`) while preserving existing normal-mode flow.
### Acceptance criteria
- Normal ISO behavior is unchanged.
- OEM ISO shows OEM-specific warning before disk wipe confirmation.
- OEM typed confirmation is required before destructive action.
- In VM with `copytoram=y`, former boot disk appears in disk list without exclusion-logic changes.
---
## 4) Documentation
### File
- `README.md`
### Changes
- Document `--oem` build flag and resulting artifact naming.
- Document factory workflow: writing OEM ISO to internal disk and expected first-boot UX.
- Document failure and recovery behavior:
- Missing `copytoram=y`, `MemTotal < 8 GiB`, or low `MemAvailable` causes safe abort.
- Interrupted/failed install requires external reinstall media.
- Add short operator checklist for manufacturing line validation.
---
## 5) Validation
### VM test matrix
1. Build OEM ISO.
2. Write OEM ISO to virtual disk (not external media emulation).
3. Boot from virtual disk and verify:
- `/proc/cmdline` contains `copytoram=y`.
- Guardrail checks pass in healthy case.
- OEM boot menu does not include accessibility/speech entries.
4. Complete setup flow on first boot.
5. Install to same disk.
6. Reboot into installed OS.
7. Verify encryption:
- `lsblk` shows crypt mapping.
- `cryptsetup status` reports active LUKS device.
- Boot requires unlock passphrase.
8. Validate OEM warning + typed confirmation appears and gates destructive action.
### Boot path coverage
- UEFI + GRUB path.
- BIOS/syslinux path (if still shipped).
### Hardware test matrix
- UEFI test on at least two hardware families (Intel + AMD).
- Confirm no external media required.
- Confirm first boot starts setup automatically.
### Negative tests
- OEM boot without `copytoram=y` -> safe abort with clear error.
- `MemTotal < 8 GiB` -> safe abort with clear error.
- Low `MemAvailable` -> safe abort with clear error.
- Runtime state not RAM-backed -> safe abort with clear error.
- Power interruption before installer starts -> deterministic reboot behavior.
- Power interruption during install -> recovery requires external media (documented).
---
## 6) Security hardening checklist
- No factory credentials exist; customer sets all passwords at first boot.
- Clear shell history and transient setup artifacts at end of install.
- Verify no secrets in logs shipped to customer.
- Regenerate host identity on first installed boot as needed:
- `machine-id`
- SSH host keys (if installed/enabled)
- Keep package signature verification and keyring handling intact.
---
## Rollout Plan
1. Implement OEM build flag, mode marker, boot config injection, and build assertions.
2. Implement copytoram, memory, and runtime-state guardrails in `.automated_script.sh`.
3. Add OEM warning text and typed destructive confirmation in configurator.
4. Add docs and operator checklist.
5. Run VM matrix, including negative tests.
6. Run hardware matrix.
7. Cut first OEM preproduction release for manufacturing pilot.
---
## Definition of Done
- `--oem` build exists and is documented.
- Device preloaded with OEM image boots directly to setup.
- Setup installs to internal disk without external media.
- Final installed system is LUKS-encrypted with customer-owned passphrase.
- OEM safety guardrails fail closed with actionable errors.
- Existing non-OEM ISO behavior remains unchanged.
- VM and hardware QA matrix pass.