Unify ISO install orchestration
Drive installs through the common ISO pipeline, carry install intent in configurator JSON, and finalize Limine boot setup from the ISO.
This commit is contained in:
17 files changed
+958
-814
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# Omarchy ISO
|
||||
|
||||
The Omarchy ISO streamlines [the installation of Omarchy](https://learn.omacom.io/2/the-omarchy-manual/50/getting-started). It includes the Omarchy Configurator as a front-end to archinstall and automatically launches the [Omarchy Installer](https://github.com/basecamp/omarchy) after base arch has been setup.
|
||||
The Omarchy ISO is the only supported way to install Omarchy. It ships the Omarchy Configurator, installs Arch Linux, installs the Omarchy packages from the bundled mirror, runs target system setup in the chroot, creates the user, and runs `omarchy-setup-user` for that user.
|
||||
|
||||
## Downloading the latest ISO
|
||||
|
||||
@@ -8,20 +8,18 @@ See the ISO link on [omarchy.org](https://omarchy.org).
|
||||
|
||||
## Creating the ISO
|
||||
|
||||
Run `./bin/omarchy-iso-make` and the output goes into `./release`. You can build from your local $OMARCHY_PATH for testing by using `--local-source` or from a checkout of the dev branch (instead of master) by using `--dev`.
|
||||
Run `./bin/omarchy-iso-make`; output goes into `./release`.
|
||||
|
||||
### Environment Variables
|
||||
For local development, build the ISO from sibling checkouts:
|
||||
|
||||
You can customize the repositories used during the build process by passing in variables:
|
||||
|
||||
- `OMARCHY_INSTALLER_REPO` - GitHub repository for the installer (default: `basecamp/omarchy`)
|
||||
- `OMARCHY_INSTALLER_REF` - Git ref (branch/tag) for the installer (default: `master`)
|
||||
|
||||
Example usage:
|
||||
```bash
|
||||
OMARCHY_INSTALLER_REPO="myuser/omarchy-fork" OMARCHY_INSTALLER_REF="some-feature" ./bin/omarchy-iso-make
|
||||
./bin/omarchy-iso-make --local-source ../omarchy-installer ../omarchy-pkgs
|
||||
```
|
||||
|
||||
Despite the local folder name, the first argument is the Omarchy source checkout (runtime commands, configs, setup scripts, themes, shell, migrations). The installer itself lives in this ISO repo.
|
||||
|
||||
Use `--dev` or `--rc` to build against those package channels.
|
||||
|
||||
## Testing the ISO
|
||||
|
||||
Run `./bin/omarchy-iso-boot [release/omarchy.iso]`.
|
||||
@@ -32,7 +30,7 @@ Run `./bin/omarchy-iso-sign [gpg-user] [release/omarchy.iso]`.
|
||||
|
||||
## Uploading the ISO
|
||||
|
||||
Run `./bin/omarchy-iso-upload [release/omarchy.iso]`. This requires you've configured rclone (use `rclone config`).
|
||||
Run `./bin/omarchy-iso-upload [release/omarchy.iso]`. This requires rclone configuration (`rclone config`).
|
||||
|
||||
## Full release of the ISO
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ logs=(
|
||||
"pacman-key: populating archlinux keyring"
|
||||
"mount: /mnt ready"
|
||||
"pacstrap: installing linux btrfs-progs limine"
|
||||
"pacman: installing omarchy-installer"
|
||||
"pacman: installing omarchy"
|
||||
"useradd: creating ryan with /etc/skel defaults"
|
||||
"limine-update: generating omarchy_linux.efi"
|
||||
"snapper: creating root config"
|
||||
|
||||
@@ -3,25 +3,13 @@ set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
cleanup() {
|
||||
[[ -n "${mock_omarchy_install:-}" ]] && rm -rf "$(dirname "$mock_omarchy_install")"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if [[ -z "${OMARCHY_INSTALL:-}" ]]; then
|
||||
if [[ -r /root/omarchy/install/helpers/all.sh ]]; then
|
||||
export OMARCHY_INSTALL=/root/omarchy/install
|
||||
else
|
||||
mock_root="$(mktemp -d)"
|
||||
mock_omarchy_install="$mock_root/install"
|
||||
mkdir -p "$mock_omarchy_install/helpers"
|
||||
cat >"$mock_omarchy_install/helpers/all.sh" <<'EOF'
|
||||
PADDING_LEFT=0
|
||||
PADDING_LEFT_SPACES=""
|
||||
clear_logo() { clear; }
|
||||
EOF
|
||||
export OMARCHY_INSTALL="$mock_omarchy_install"
|
||||
fi
|
||||
if [[ -z "${OMARCHY_PATH:-}" ]]; then
|
||||
for candidate in "$repo_root/../omarchy-installer" "$repo_root/../omarchy" /usr/share/omarchy; do
|
||||
if [[ -f "$candidate/logo.txt" ]]; then
|
||||
export OMARCHY_PATH="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
exec bash "$repo_root/configs/airootfs/root/configurator" dry "$@"
|
||||
+20
-45
@@ -1,11 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Abort if anything fails
|
||||
set -e
|
||||
|
||||
# Parse command line arguments
|
||||
NO_CACHE=""
|
||||
LOCAL_INSTALLER_PATH=""
|
||||
LOCAL_OMARCHY_PATH=""
|
||||
LOCAL_PKGS_PATH=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
@@ -22,42 +20,38 @@ while [[ $# -gt 0 ]]; do
|
||||
shift
|
||||
;;
|
||||
--local-source)
|
||||
# --local-source <installer-checkout> <pkgs-checkout>
|
||||
# Builds omarchy* from the two checkouts and bundles them in the ISO's
|
||||
# offline mirror instead of pulling published versions from the omarchy
|
||||
# network mirror.
|
||||
LOCAL_INSTALLER_PATH="${2:-}"
|
||||
LOCAL_OMARCHY_PATH="${2:-}"
|
||||
LOCAL_PKGS_PATH="${3:-}"
|
||||
if [[ -z $LOCAL_INSTALLER_PATH || -z $LOCAL_PKGS_PATH ]]; then
|
||||
echo "Error: --local-source requires two args: <installer-checkout> <pkgs-checkout>" >&2
|
||||
if [[ -z $LOCAL_OMARCHY_PATH || -z $LOCAL_PKGS_PATH ]]; then
|
||||
echo "Error: --local-source requires two args: <omarchy-checkout> <pkgs-checkout>" >&2
|
||||
exit 1
|
||||
fi
|
||||
OMARCHY_MIRROR=edge
|
||||
OMARCHY_ISO_REF=local
|
||||
shift 3
|
||||
;;
|
||||
--dev)
|
||||
OMARCHY_INSTALLER_REF=dev
|
||||
OMARCHY_ISO_REF=dev
|
||||
OMARCHY_MIRROR=edge
|
||||
shift
|
||||
;;
|
||||
--rc)
|
||||
OMARCHY_ISO_REF=rc
|
||||
OMARCHY_MIRROR=rc
|
||||
OMARCHY_INSTALLER_REF=rc
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
echo "Usage: $0 [--no-cache] [--no-boot-offer] [--debug] [--dev|--rc] [--local-source <installer-checkout> <pkgs-checkout>]"
|
||||
echo "Usage: $0 [--no-cache] [--no-boot-offer] [--debug] [--dev|--rc] [--local-source <omarchy-checkout> <pkgs-checkout>]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate local-source paths up front (before any sudo / network work).
|
||||
if [[ -n $LOCAL_INSTALLER_PATH ]]; then
|
||||
[[ -d $LOCAL_INSTALLER_PATH ]] || { echo "Error: installer checkout not found at $LOCAL_INSTALLER_PATH" >&2; exit 1; }
|
||||
[[ -d $LOCAL_PKGS_PATH ]] || { echo "Error: pkgs checkout not found at $LOCAL_PKGS_PATH" >&2; exit 1; }
|
||||
LOCAL_INSTALLER_PATH=$(realpath "$LOCAL_INSTALLER_PATH")
|
||||
if [[ -n $LOCAL_OMARCHY_PATH ]]; then
|
||||
[[ -d $LOCAL_OMARCHY_PATH ]] || { echo "Error: Omarchy checkout not found at $LOCAL_OMARCHY_PATH" >&2; exit 1; }
|
||||
[[ -d $LOCAL_PKGS_PATH ]] || { echo "Error: pkgs checkout not found at $LOCAL_PKGS_PATH" >&2; exit 1; }
|
||||
LOCAL_OMARCHY_PATH=$(realpath "$LOCAL_OMARCHY_PATH")
|
||||
LOCAL_PKGS_PATH=$(realpath "$LOCAL_PKGS_PATH")
|
||||
fi
|
||||
|
||||
@@ -96,27 +90,21 @@ lint_file_permissions() {
|
||||
|
||||
lint_file_permissions
|
||||
|
||||
# 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
|
||||
|
||||
# Prepare build repo
|
||||
BUILD_SCRIPT="builder/build-iso.sh"
|
||||
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
|
||||
BUILD_RELEASE_PATH="$BUILD_ROOT/release"
|
||||
mkdir -p "$BUILD_RELEASE_PATH"
|
||||
|
||||
OMARCHY_INSTALLER_REPO="${OMARCHY_INSTALLER_REPO:-basecamp/omarchy}"
|
||||
OMARCHY_INSTALLER_REF="${OMARCHY_INSTALLER_REF:-master}"
|
||||
OMARCHY_ISO_REF="${OMARCHY_ISO_REF:-stable}"
|
||||
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_ISO_REF=$OMARCHY_ISO_REF"
|
||||
-e "OMARCHY_MIRROR=$OMARCHY_MIRROR"
|
||||
-e "OMARCHY_INSTALL_DEBUG=${OMARCHY_INSTALL_DEBUG:-}"
|
||||
-e "HOST_UID=$(id -u)"
|
||||
@@ -128,43 +116,30 @@ DOCKER_ARGS=(
|
||||
-v "$BUILD_ROOT/configs:/configs:ro"
|
||||
)
|
||||
|
||||
# Mount the local checkouts when --local-source was passed (paths already
|
||||
# validated/resolved above).
|
||||
if [[ -n $LOCAL_INSTALLER_PATH ]]; then
|
||||
if [[ -n $LOCAL_OMARCHY_PATH ]]; then
|
||||
DOCKER_ARGS+=(
|
||||
-v "$LOCAL_INSTALLER_PATH:/omarchy-installer:ro"
|
||||
-v "$LOCAL_OMARCHY_PATH:/omarchy-source:ro"
|
||||
-v "$LOCAL_PKGS_PATH:/omarchy-pkgs:ro"
|
||||
)
|
||||
fi
|
||||
|
||||
# Use local pacman cache if you already have one on host to speed up repeat runs
|
||||
if [ -d "/var/cache/pacman/pkg" ]; then
|
||||
if [[ -d /var/cache/pacman/pkg ]]; then
|
||||
DOCKER_ARGS+=(-v "/var/cache/pacman/pkg:/var/cache/pacman/pkg")
|
||||
fi
|
||||
|
||||
# Mount the build cache directory where packages are downloaded. Keyed by
|
||||
# channel so stable/edge/rc don't pollute each other but the cache persists
|
||||
# across days (different channels have different package sets; days don't).
|
||||
# omarchy* packages always rebuild fresh inside the container regardless of
|
||||
# what's in the cache (build-omarchy-packages.sh nukes its work dir and the
|
||||
# final mv overwrites the cached .pkg.tar.zst). --no-cache bypasses the mount
|
||||
# entirely if you want a truly clean download.
|
||||
if [[ -z "$NO_CACHE" ]]; then
|
||||
if [[ -z $NO_CACHE ]]; then
|
||||
OFFLINE_REPO_BUILD_CACHE_DIR="$HOME/.cache/omarchy/iso_${OMARCHY_MIRROR}/airootfs/var/cache/omarchy"
|
||||
mkdir -p "$OFFLINE_REPO_BUILD_CACHE_DIR"
|
||||
DOCKER_ARGS+=(-v "$OFFLINE_REPO_BUILD_CACHE_DIR:/var/cache/airootfs/var/cache/omarchy")
|
||||
fi
|
||||
|
||||
# Run the builder with assembled args
|
||||
docker run "${DOCKER_ARGS[@]}" archlinux/archlinux:latest /$BUILD_SCRIPT
|
||||
|
||||
# Move as install ref
|
||||
latest_iso=$(\ls -t "$BUILD_RELEASE_PATH"/*.iso | head -n1)
|
||||
iso_ref="${latest_iso%.*}-$OMARCHY_INSTALLER_REF.iso"
|
||||
iso_ref="${latest_iso%.*}-$OMARCHY_ISO_REF.iso"
|
||||
mv -f "$latest_iso" "$iso_ref"
|
||||
|
||||
# Offer to boot the new build
|
||||
if [[ -z "$NO_BOOT_OFFER" ]]; then
|
||||
if [[ -z "${NO_BOOT_OFFER:-}" ]]; then
|
||||
echo
|
||||
gum confirm "Boot $iso_ref?" && omarchy-iso-boot "$iso_ref"
|
||||
fi
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
MAKE_ARGS=()
|
||||
INSTALLER_REF=master
|
||||
ISO_REF=stable
|
||||
RELEASE_SUFFIX=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
@@ -12,7 +12,7 @@ while [[ $# -gt 0 ]]; do
|
||||
;;
|
||||
--rc)
|
||||
MAKE_ARGS+=("$1")
|
||||
INSTALLER_REF=rc
|
||||
ISO_REF=rc
|
||||
RELEASE_SUFFIX="-rc"
|
||||
shift
|
||||
;;
|
||||
@@ -42,21 +42,21 @@ BUILD_RELEASE_PATH="$BUILD_ROOT/release"
|
||||
|
||||
# Make ISOs
|
||||
if [[ ! " ${MAKE_ARGS[*]} " =~ " --no-make " ]]; then
|
||||
echo -e "\e[32mMaking $INSTALLER_REF ISO\e[0m"
|
||||
echo -e "\e[32mMaking $ISO_REF ISO\e[0m"
|
||||
omarchy-iso-make --no-cache --no-boot-offer "${MAKE_ARGS[@]}"
|
||||
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)
|
||||
latest_iso=$(\ls -t "$BUILD_RELEASE_PATH"/*x86_64-"$ISO_REF".iso | head -n1)
|
||||
release_iso="$BUILD_RELEASE_PATH/omarchy-$BUILD_VERSION$RELEASE_SUFFIX.iso"
|
||||
cp "$latest_iso" "$release_iso"
|
||||
|
||||
echo -e "\e[32mSigning $INSTALLER_REF ISO\e[0m"
|
||||
echo -e "\e[32mSigning $ISO_REF ISO\e[0m"
|
||||
omarchy-iso-sign "$release_iso"
|
||||
|
||||
iso_sha=$(sha256sum "$release_iso")
|
||||
echo -e "\e[32mSHA256 for $INSTALLER_REF ISO: $iso_sha\e[0m"
|
||||
echo -e "\e[32mSHA256 for $ISO_REF ISO: $iso_sha\e[0m"
|
||||
|
||||
omarchy-iso-upload "$release_iso"
|
||||
+19
-20
@@ -5,7 +5,7 @@ set -e
|
||||
# Packages installed into the Arch container used to build the ISO.
|
||||
pacman-key --init
|
||||
pacman --noconfirm -Sy archlinux-keyring
|
||||
pacman --noconfirm -Sy archiso git sudo base-devel jq grub
|
||||
pacman --noconfirm -Sy archiso git sudo base-devel jq grub imagemagick neovim nodejs npm tree-sitter-cli
|
||||
|
||||
# Pre-import the omarchy signing key (so pacman trusts our [omarchy] repo
|
||||
# during the build without keyserver lookups).
|
||||
@@ -48,10 +48,10 @@ if [[ ${OMARCHY_INSTALL_DEBUG:-} == "1" ]]; then
|
||||
echo "debug=1"
|
||||
echo "built_at=$(date -Is)"
|
||||
echo "mirror=$OMARCHY_MIRROR"
|
||||
if [[ -d /omarchy-installer ]]; then
|
||||
echo "omarchy_installer_source=/omarchy-installer"
|
||||
git -c safe.directory=/omarchy-installer -C /omarchy-installer rev-parse HEAD 2>/dev/null | sed 's/^/omarchy_installer_commit=/' || true
|
||||
git -c safe.directory=/omarchy-installer -C /omarchy-installer status --short 2>/dev/null | sed 's/^/omarchy_installer_status=/' || true
|
||||
if [[ -d /omarchy-source ]]; then
|
||||
echo "omarchy_source=/omarchy-source"
|
||||
git -c safe.directory=/omarchy-source -C /omarchy-source rev-parse HEAD 2>/dev/null | sed 's/^/omarchy_commit=/' || true
|
||||
git -c safe.directory=/omarchy-source -C /omarchy-source status --short 2>/dev/null | sed 's/^/omarchy_status=/' || true
|
||||
fi
|
||||
if [[ -d /omarchy-pkgs ]]; then
|
||||
echo "omarchy_pkgs_source=/omarchy-pkgs"
|
||||
@@ -64,7 +64,7 @@ fi
|
||||
# When --local-source is in effect, build omarchy* from the mounted source
|
||||
# trees and drop them in the offline mirror. Otherwise pacman -Syw below
|
||||
# downloads the published versions from the omarchy network mirror.
|
||||
if [[ -d /omarchy-installer && -d /omarchy-pkgs ]]; then
|
||||
if [[ -d /omarchy-source && -d /omarchy-pkgs ]]; then
|
||||
bash /builder/build-omarchy-packages.sh "$offline_mirror_dir"
|
||||
LOCAL_OMARCHY_BUILD=1
|
||||
fi
|
||||
@@ -80,22 +80,17 @@ mkdir -p "$build_cache_dir/airootfs/opt/packages/"
|
||||
cp "/tmp/$NODE_FILENAME" "$build_cache_dir/airootfs/opt/packages/"
|
||||
|
||||
# Packages installed into the live ISO environment itself (NOT the target system).
|
||||
# omarchy-installer + omarchy-settings need to be HERE so:
|
||||
# 1. /usr/share/omarchy/{install,default,bin} is available to the configurator
|
||||
# and .automated_script.sh natively (no .pkg.tar.zst extraction hack)
|
||||
# 2. omarchy-settings's post_install hook drops omarchy's plymouthd.conf into
|
||||
# /etc/plymouth so mkinitcpio's plymouth hook picks up the Omarchy theme
|
||||
# when mkarchiso builds the live initramfs (otherwise plymouth shows
|
||||
# the default theme on boot).
|
||||
arch_packages=(linux-t2 git gum jq openssl plymouth python-terminaltexteffects tzupdate omarchy-keyring omarchy-settings omarchy-installer lvm2 cryptsetup parted)
|
||||
# omarchy-settings is needed here so its post_install hook drops Omarchy's
|
||||
# plymouthd.conf into /etc/plymouth before mkarchiso builds the live initramfs.
|
||||
arch_packages=(linux-t2 git gum jq openssl plymouth python-terminaltexteffects tzupdate omarchy-keyring omarchy-settings lvm2 cryptsetup parted)
|
||||
printf '%s\n' "${arch_packages[@]}" >> "$build_cache_dir/packages.x86_64"
|
||||
|
||||
# Build the offline mirror: everything pacstrap might want during the target
|
||||
# install. With --local-source, the omarchy* packages we just built are
|
||||
# already in the mirror and we filter them out below. Without it, pacman -Syw
|
||||
# pulls the published omarchy* from the network mirror like any other package.
|
||||
if [[ -d /omarchy-installer ]]; then
|
||||
base_pkg_lists=(/omarchy-installer/install/omarchy-base.packages /omarchy-installer/install/omarchy-other.packages)
|
||||
if [[ -d /omarchy-source ]]; then
|
||||
base_pkg_lists=(/omarchy-source/install/omarchy-base.packages /omarchy-source/install/omarchy-other.packages)
|
||||
else
|
||||
# Pull the same package lists out of the freshly-downloaded omarchy package
|
||||
# so we don't need a local checkout in the non-local-source path.
|
||||
@@ -106,6 +101,10 @@ else
|
||||
base_pkg_lists=(/tmp/omarchy-pkglists/usr/share/omarchy/install/omarchy-base.packages /tmp/omarchy-pkglists/usr/share/omarchy/install/omarchy-other.packages)
|
||||
fi
|
||||
|
||||
mkdir -p "$build_cache_dir/airootfs/usr/share/omarchy-iso"
|
||||
cp "${base_pkg_lists[0]}" "$build_cache_dir/airootfs/usr/share/omarchy-iso/omarchy-base.packages"
|
||||
cp "${base_pkg_lists[1]}" "$build_cache_dir/airootfs/usr/share/omarchy-iso/omarchy-other.packages"
|
||||
|
||||
# Collect every package we want available in the offline mirror.
|
||||
declare -a all_packages
|
||||
mapfile -t all_packages < <(
|
||||
@@ -113,9 +112,9 @@ mapfile -t all_packages < <(
|
||||
cat "$build_cache_dir/packages.x86_64"
|
||||
grep -hv '^#\|^$' "${base_pkg_lists[@]}"
|
||||
grep -hv '^#\|^$' /builder/archinstall.packages
|
||||
# Always include the omarchy packages so the target install can find
|
||||
# omarchy-installer and companion packages in the offline mirror.
|
||||
printf 'omarchy\nomarchy-settings\nomarchy-installer\nomarchy-limine\nomarchy-nvim\n'
|
||||
# Always include the Omarchy packages so the target install can find
|
||||
# the runtime and companion packages in the offline mirror.
|
||||
printf 'omarchy\nomarchy-settings\nomarchy-limine\nomarchy-nvim\n'
|
||||
} | sort -u
|
||||
)
|
||||
|
||||
@@ -123,7 +122,7 @@ mapfile -t all_packages < <(
|
||||
# the mirror; strip them from the pacman -Syw list so it doesn't try to fetch
|
||||
# the published versions on top.
|
||||
if [[ -n ${LOCAL_OMARCHY_BUILD:-} ]]; then
|
||||
all_packages=($(printf '%s\n' "${all_packages[@]}" | grep -vE '^(omarchy|omarchy-settings|omarchy-installer|omarchy-limine|omarchy-nvim)$'))
|
||||
all_packages=($(printf '%s\n' "${all_packages[@]}" | grep -vE '^(omarchy|omarchy-settings|omarchy-limine|omarchy-nvim)$'))
|
||||
fi
|
||||
|
||||
mkdir -p /tmp/offlinedb
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
#!/bin/bash
|
||||
# Build omarchy + companion omarchy packages from mounted source
|
||||
# (/omarchy-installer + /omarchy-pkgs) and drop the .pkg.tar.zst
|
||||
# files into the offline-mirror directory passed as $1.
|
||||
#
|
||||
# Run inside the ISO build container (build-iso.sh sources this).
|
||||
# Build Omarchy packages from mounted source (/omarchy-source + /omarchy-pkgs)
|
||||
# and place the resulting .pkg.tar.zst files in the offline mirror.
|
||||
|
||||
set -e
|
||||
|
||||
@@ -13,8 +10,8 @@ if [[ -z $offline_mirror_dir ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d /omarchy-installer ]]; then
|
||||
echo "ERROR: /omarchy-installer not mounted (pass --local-source or set OMARCHY_INSTALLER_PATH)" >&2
|
||||
if [[ ! -d /omarchy-source ]]; then
|
||||
echo "ERROR: /omarchy-source not mounted (pass --local-source or set OMARCHY_SOURCE_PATH)" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -d /omarchy-pkgs ]]; then
|
||||
@@ -26,9 +23,6 @@ work_dir=/tmp/omarchy-pkg-build
|
||||
rm -rf "$work_dir"
|
||||
mkdir -p "$work_dir"
|
||||
|
||||
# makepkg refuses to run as root; create a builder user with passwordless
|
||||
# sudo for pacman dep installation during the build, and hand it ownership
|
||||
# of the work dir so PKGDEST writes succeed.
|
||||
if ! id builder &>/dev/null; then
|
||||
useradd -m -s /bin/bash builder
|
||||
fi
|
||||
@@ -38,16 +32,10 @@ chown builder:builder "$work_dir"
|
||||
|
||||
pacman -Sy --noconfirm
|
||||
|
||||
# Order matters: omarchy depends on omarchy-settings/-installer which depend on
|
||||
# the base packages; omarchy-installer depends on the others. Build leaves
|
||||
# first so dependents can pacman -S them from /tmp once needed. We don't
|
||||
# actually install during the build, so the order is mostly cosmetic — but
|
||||
# keeping it stable helps logs.
|
||||
packages=(
|
||||
omarchy-limine
|
||||
omarchy-settings
|
||||
omarchy
|
||||
omarchy-installer
|
||||
omarchy-nvim
|
||||
)
|
||||
|
||||
@@ -59,13 +47,10 @@ for pkg in "${packages[@]}"; do
|
||||
cp -a "/omarchy-pkgs/pkgbuilds/$pkg" "$pkg_work"
|
||||
chown -R builder:builder "$pkg_work"
|
||||
|
||||
# --nodeps: don't install deps into the build container. We just need to
|
||||
# produce the .pkg.tar.zst; pacman resolves runtime deps at install time on
|
||||
# the target system from the offline mirror.
|
||||
su builder -c "
|
||||
cd '$pkg_work' &&
|
||||
PKGDEST='$work_dir' \
|
||||
OMARCHY_SRC=/omarchy-installer \
|
||||
OMARCHY_SRC=/omarchy-source \
|
||||
makepkg --noconfirm --skippgpcheck --skipchecksums --nodeps -f
|
||||
"
|
||||
done
|
||||
@@ -74,5 +59,5 @@ mkdir -p "$offline_mirror_dir"
|
||||
mv "$work_dir"/*.pkg.tar.zst "$offline_mirror_dir/"
|
||||
|
||||
echo
|
||||
echo "Built omarchy packages, placed in $offline_mirror_dir:"
|
||||
echo "Built Omarchy packages, placed in $offline_mirror_dir:"
|
||||
ls "$offline_mirror_dir"/omarchy*.pkg.tar.zst | sed 's|^| |'
|
||||
@@ -0,0 +1,21 @@
|
||||
# remove from airootfs!
|
||||
#
|
||||
# Mirror omarchy-settings' /etc/skel shell defaults into /root so the live ISO
|
||||
# console has the same bashrc, starship prompt, and aliases as an installed
|
||||
# Omarchy system. Fires during airootfs pacstrap (when omarchy-settings is
|
||||
# installed); the "remove from airootfs" magic string causes
|
||||
# zzzz99-remove-custom-hooks-from-airootfs.hook to delete this file before
|
||||
# the squashfs is built, so no runtime hook remains.
|
||||
|
||||
[Trigger]
|
||||
Operation = Install
|
||||
Operation = Upgrade
|
||||
Type = Package
|
||||
Target = omarchy-settings
|
||||
|
||||
[Action]
|
||||
Description = Mirroring /etc/skel shell defaults into /root for the live ISO...
|
||||
When = PostTransaction
|
||||
Depends = coreutils
|
||||
Depends = omarchy-settings
|
||||
Exec = /bin/sh -c 'install -d -m 0700 /root/.config && for f in .bashrc .bash_profile; do [ -f "/etc/skel/$f" ] && install -m 0644 "/etc/skel/$f" "/root/$f"; done; [ -f /etc/skel/.config/starship.toml ] && install -m 0644 /etc/skel/.config/starship.toml /root/.config/starship.toml; true'
|
||||
@@ -19,7 +19,6 @@ export OMARCHY_INSTALL_LOG_FILE=/var/log/omarchy-install.log
|
||||
if [[ -f /usr/share/omarchy-iso/install-debug ]]; then
|
||||
export OMARCHY_INSTALL_DEBUG=1
|
||||
fi
|
||||
source "$OMARCHY_INSTALL/helpers/all.sh"
|
||||
|
||||
# Tokyo Night palette so the live VT matches the installed look.
|
||||
set_tokyo_night_colors() {
|
||||
@@ -46,7 +45,7 @@ export FORCE_COLOR=1
|
||||
if [[ ${OMARCHY_INSTALL_DEBUG:-} == "1" ]]; then
|
||||
echo "=== Omarchy ISO debug build ==="
|
||||
[[ -f /usr/share/omarchy-iso/build-info ]] && cat /usr/share/omarchy-iso/build-info
|
||||
pacman -Q omarchy-installer omarchy-settings omarchy-keyring 2>/dev/null || true
|
||||
pacman -Q omarchy-settings omarchy-keyring 2>/dev/null || true
|
||||
echo "================================"
|
||||
fi
|
||||
|
||||
|
||||
+307
-102
@@ -1,7 +1,54 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Rely on Omarchy for styling and error handling
|
||||
source "$OMARCHY_INSTALL/helpers/all.sh"
|
||||
# Minimal live-ISO presentation helpers. Keep these local to the ISO: the
|
||||
# installed Omarchy package owns target setup helpers, while the ISO owns the
|
||||
# installer UI that runs before any target packages exist.
|
||||
OMARCHY_PATH="${OMARCHY_PATH:-/usr/share/omarchy}"
|
||||
LOGO_PATH="${LOGO_PATH:-$OMARCHY_PATH/logo.txt}"
|
||||
|
||||
if [[ -e /dev/tty ]]; then
|
||||
TERM_SIZE=$(stty size 2>/dev/null </dev/tty || true)
|
||||
fi
|
||||
if [[ -n ${TERM_SIZE:-} ]]; then
|
||||
TERM_HEIGHT=${TERM_SIZE%% *}
|
||||
TERM_WIDTH=${TERM_SIZE##* }
|
||||
else
|
||||
TERM_HEIGHT=${LINES:-24}
|
||||
TERM_WIDTH=${COLUMNS:-80}
|
||||
fi
|
||||
|
||||
if [[ -f $LOGO_PATH ]]; then
|
||||
LOGO_WIDTH=$(awk '{ if (length > max) max = length } END { print max+0 }' "$LOGO_PATH" 2>/dev/null || echo 0)
|
||||
else
|
||||
LOGO_WIDTH=7
|
||||
fi
|
||||
(( LOGO_WIDTH > 0 )) || LOGO_WIDTH=7
|
||||
PADDING_LEFT=$(((TERM_WIDTH - LOGO_WIDTH) / 2))
|
||||
(( PADDING_LEFT < 0 )) && PADDING_LEFT=0
|
||||
PADDING_LEFT_SPACES=$(printf "%*s" "$PADDING_LEFT" "")
|
||||
PADDING="0 0 0 $PADDING_LEFT"
|
||||
|
||||
export TERM_HEIGHT TERM_WIDTH LOGO_PATH PADDING_LEFT PADDING_LEFT_SPACES PADDING
|
||||
export GUM_CONFIRM_PROMPT_FOREGROUND="6"
|
||||
export GUM_CONFIRM_SELECTED_FOREGROUND="0"
|
||||
export GUM_CONFIRM_SELECTED_BACKGROUND="2"
|
||||
export GUM_CONFIRM_UNSELECTED_FOREGROUND="7"
|
||||
export GUM_CONFIRM_UNSELECTED_BACKGROUND="0"
|
||||
export GUM_CHOOSE_PADDING="$PADDING"
|
||||
export GUM_FILTER_PADDING="$PADDING"
|
||||
export GUM_INPUT_PADDING="$PADDING"
|
||||
export GUM_SPIN_PADDING="$PADDING"
|
||||
export GUM_TABLE_PADDING="$PADDING"
|
||||
export GUM_CONFIRM_PADDING="$PADDING"
|
||||
|
||||
clear_logo() {
|
||||
printf "\033[H\033[2J"
|
||||
if [[ -f $LOGO_PATH ]]; then
|
||||
gum style --foreground 2 --padding "1 0 0 $PADDING_LEFT" "$(<"$LOGO_PATH")"
|
||||
else
|
||||
gum style --foreground 2 --padding "1 0 0 $PADDING_LEFT" "Omarchy"
|
||||
fi
|
||||
}
|
||||
|
||||
# Ensure tzupdate is available for guessing timezone
|
||||
command -v tzupdate >/dev/null || sudo pacman -Sy --noconfirm tzupdate
|
||||
@@ -16,10 +63,15 @@ abort() {
|
||||
step() {
|
||||
clear_logo
|
||||
echo
|
||||
gum style "$1"
|
||||
gum style --padding "0 0 0 $PADDING_LEFT" "$1"
|
||||
echo
|
||||
}
|
||||
|
||||
# Padded body text to line up with the logo and gum widgets.
|
||||
say() {
|
||||
gum style --padding "0 0 0 $PADDING_LEFT" "$@"
|
||||
}
|
||||
|
||||
notice() {
|
||||
clear_logo
|
||||
echo
|
||||
@@ -27,6 +79,76 @@ notice() {
|
||||
echo
|
||||
}
|
||||
|
||||
# Tools menu — recovery-style actions kept off the main install path.
|
||||
# Inlines its own disk picker so the launcher can run before disk_form is
|
||||
# defined further down in the script.
|
||||
tools_menu() {
|
||||
while true; do
|
||||
step "Tools"
|
||||
local choice
|
||||
choice=$(gum choose --header "Pick a tool" \
|
||||
"Open partition tool" \
|
||||
"Drop to shell" \
|
||||
"Reboot" \
|
||||
"Power off" \
|
||||
"Back to installer") || return 0
|
||||
|
||||
case "$choice" in
|
||||
"Open partition tool")
|
||||
local d disks
|
||||
disks=$(lsblk -dpno NAME,TYPE 2>/dev/null | awk '$2=="disk"{print $1}' | grep -E '/dev/(sd|hd|vd|nvme|mmcblk|xv)' || true)
|
||||
if [[ -z "$disks" ]]; then
|
||||
notice "No suitable disks found" 2
|
||||
continue
|
||||
fi
|
||||
d=$(echo "$disks" | gum choose --header "Select disk") || continue
|
||||
clear
|
||||
cfdisk "$d" || true
|
||||
partprobe "$d" 2>/dev/null || true
|
||||
sync
|
||||
sleep 1
|
||||
;;
|
||||
"Drop to shell")
|
||||
clear
|
||||
echo "Type 'exit' to return to the tools menu."
|
||||
bash --norc || true
|
||||
;;
|
||||
"Reboot")
|
||||
reboot
|
||||
;;
|
||||
"Power off")
|
||||
poweroff
|
||||
;;
|
||||
"Back to installer")
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Landing screen: Enter installs, Ctrl-T opens the tools menu.
|
||||
launcher_form() {
|
||||
while true; do
|
||||
step "Install Omarchy"
|
||||
say "Press Enter to install Omarchy on this machine."
|
||||
echo
|
||||
say --foreground 8 "Ctrl-T Tools"
|
||||
|
||||
local key
|
||||
IFS= read -rsn1 key || true
|
||||
case "$key" in
|
||||
""|$'\n'|$'\r')
|
||||
return 0
|
||||
;;
|
||||
$'\x14')
|
||||
tools_menu
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
launcher_form
|
||||
|
||||
# STEP 1: KEYBOARD LAYOUT
|
||||
|
||||
keyboard_form() {
|
||||
@@ -280,6 +402,21 @@ to_gb() {
|
||||
awk -v bytes="$1" 'BEGIN {printf "%.1fGB", bytes/1024/1024/1024}'
|
||||
}
|
||||
|
||||
# Shared error screen for every "can't fit Omarchy here" outcome of the
|
||||
# partition install. Offers the partition tool as the meaningful second
|
||||
# action so users have a way to actually fix the situation.
|
||||
not_enough_space() {
|
||||
local available="${1:-0}"
|
||||
step "Not enough free space on $disk"
|
||||
say --foreground 1 "$disk has $(to_gb $available) of usable free space; Omarchy needs at least 32GB."
|
||||
say "Open the partition tool to free at least 32GB on $disk, then try again."
|
||||
echo
|
||||
if ! gum confirm --affirmative "Back" --negative "Open partition tool" "Return to installation mode?"; then
|
||||
open_partition_tool
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
run_partition_install() {
|
||||
local dry=false
|
||||
[[ "${1:-}" == "dry" ]] && dry=true
|
||||
@@ -288,8 +425,8 @@ run_partition_install() {
|
||||
local bl_part
|
||||
bl_part=$(detect_bitlocker || true)
|
||||
if [[ -n "$bl_part" ]]; then
|
||||
gum style --foreground 1 "BitLocker signature detected on $bl_part."
|
||||
gum style "Suspend or disable BitLocker in Windows before installing alongside it."
|
||||
say --foreground 1 "BitLocker signature detected on $bl_part."
|
||||
say "Suspend or disable BitLocker in Windows before installing alongside it."
|
||||
abort "Aborted: BitLocker is active on this disk."
|
||||
fi
|
||||
|
||||
@@ -298,7 +435,7 @@ run_partition_install() {
|
||||
shared_esp=$(detect_windows_esp || true)
|
||||
|
||||
if [[ -n "$shared_esp" ]]; then
|
||||
gum style "Found Windows ESP at $shared_esp — will install alongside."
|
||||
say "Found Windows ESP at $shared_esp — will install alongside."
|
||||
local tmp_mp esp_free_mib
|
||||
tmp_mp=$(mktemp -d)
|
||||
mount -o ro "$shared_esp" "$tmp_mp"
|
||||
@@ -307,39 +444,50 @@ run_partition_install() {
|
||||
if (( esp_free_mib < 100 )); then
|
||||
abort "Windows ESP $shared_esp has only ${esp_free_mib}MiB free — need at least 100MiB for Omarchy."
|
||||
fi
|
||||
gum style "Windows ESP has ${esp_free_mib}MiB free."
|
||||
say "Windows ESP has ${esp_free_mib}MiB free."
|
||||
else
|
||||
gum style "No Windows ESP detected on $disk — a new EFI partition will be created in free space."
|
||||
say "No Windows ESP detected on $disk — a new EFI partition will be created in free space."
|
||||
fi
|
||||
|
||||
step "Analyzing free space on $disk"
|
||||
partprobe "$disk" 2>/dev/null || true
|
||||
sleep 1
|
||||
parted --script "$disk" unit B print free 2>/dev/null || lsblk -b "$disk"
|
||||
echo
|
||||
|
||||
local FREE_SPACE_INFO
|
||||
FREE_SPACE_INFO=$(parted -m "$disk" unit B print free 2>/dev/null | \
|
||||
awk -F: '
|
||||
$NF ~ /free/ {
|
||||
start=$2; end=$3; size=$4;
|
||||
gsub(/B/, "", start);
|
||||
gsub(/B/, "", end);
|
||||
gsub(/B/, "", size);
|
||||
if (size+0 > max_size+0) {
|
||||
max_size=size; max_start=start; max_end=end;
|
||||
# An unlabeled disk has no partition table for parted to scan, so we
|
||||
# synthesize a whole-disk free region and mklabel gpt before mkpart.
|
||||
local FREE_SPACE_INFO needs_mklabel=false pt_type disk_size_b
|
||||
pt_type=$(lsblk -dno PTTYPE "$disk" 2>/dev/null)
|
||||
if [[ -z "$pt_type" ]]; then
|
||||
disk_size_b=$(lsblk -bdno SIZE "$disk" 2>/dev/null)
|
||||
if [[ -n "$disk_size_b" && "$disk_size_b" -gt 0 ]]; then
|
||||
local _mib=$((1024 * 1024))
|
||||
local _start=$_mib
|
||||
local _end=$(( (disk_size_b - _mib) / _mib * _mib ))
|
||||
FREE_SPACE_INFO="$_start $_end $((_end - _start))"
|
||||
needs_mklabel=true
|
||||
fi
|
||||
else
|
||||
# -s (script mode) keeps parted from prompting on stderr for warnings
|
||||
# like a stale GPT backup; without it the configurator hangs invisibly.
|
||||
FREE_SPACE_INFO=$(parted -ms "$disk" unit B print free </dev/null 2>/dev/null | \
|
||||
awk -F: '
|
||||
$NF ~ /free/ {
|
||||
start=$2; end=$3; size=$4;
|
||||
gsub(/B/, "", start);
|
||||
gsub(/B/, "", end);
|
||||
gsub(/B/, "", size);
|
||||
if (size+0 > max_size+0) {
|
||||
max_size=size; max_start=start; max_end=end;
|
||||
}
|
||||
}
|
||||
}
|
||||
END {
|
||||
if (max_size+0 > 0) printf "%s %s %s\n", max_start, max_end, max_size;
|
||||
}')
|
||||
END {
|
||||
if (max_size+0 > 0) printf "%s %s %s\n", max_start, max_end, max_size;
|
||||
}')
|
||||
fi
|
||||
|
||||
if [[ -z "$FREE_SPACE_INFO" ]]; then
|
||||
gum style --foreground 1 "No usable free space detected on $disk."
|
||||
gum style "Partition install uses unallocated free space, not a pre-created partition."
|
||||
gum style "Open the partition tool, delete/shrink partitions until the target area says Free space, then leave it unallocated."
|
||||
gum confirm --affirmative "Back" --negative "Back" "Return to installation mode?" >/dev/null 2>&1 || true
|
||||
return 1
|
||||
not_enough_space 0
|
||||
return $?
|
||||
fi
|
||||
|
||||
local FREE_START_B FREE_END_B FREE_SIZE_B
|
||||
@@ -363,9 +511,8 @@ run_partition_install() {
|
||||
ROOT_END_B=$((FREE_END_B / ALIGN * ALIGN))
|
||||
|
||||
if (( ROOT_END_B <= ROOT_START_B )); then
|
||||
gum style --foreground 1 "Not enough aligned free space for partitions after alignment."
|
||||
gum confirm --affirmative "Back" --negative "Back" "Return to installation mode?" >/dev/null 2>&1 || true
|
||||
return 1
|
||||
not_enough_space "$FREE_SIZE_B"
|
||||
return $?
|
||||
fi
|
||||
|
||||
local INSTALL_START_B INSTALL_MAX_B INSTALL_MAX_GIB INSTALL_SIZE_GIB
|
||||
@@ -377,22 +524,20 @@ run_partition_install() {
|
||||
INSTALL_MAX_B=$((ROOT_END_B - INSTALL_START_B))
|
||||
|
||||
if (( INSTALL_MAX_B < MIN_INSTALL_B )); then
|
||||
gum style --foreground 1 "Usable free space ($(to_gb $INSTALL_MAX_B)) is below the 32GB minimum for Omarchy."
|
||||
gum style "Open the partition tool, delete/shrink partitions until the target area says Free space, then leave it unallocated."
|
||||
gum confirm --affirmative "Back" --negative "Back" "Return to installation mode?" >/dev/null 2>&1 || true
|
||||
return 1
|
||||
not_enough_space "$INSTALL_MAX_B"
|
||||
return $?
|
||||
fi
|
||||
|
||||
INSTALL_MAX_GIB=$((INSTALL_MAX_B / gib))
|
||||
while true; do
|
||||
step "Choose Omarchy size"
|
||||
gum style "Free region: $(to_gb $FREE_START_B) — $(to_gb $FREE_END_B) ($(to_gb $FREE_SIZE_B))"
|
||||
say "Free region: $(to_gb $FREE_START_B) — $(to_gb $FREE_END_B) ($(to_gb $FREE_SIZE_B))"
|
||||
if [[ -z "$shared_esp" ]]; then
|
||||
gum style "The size includes a new 2GiB EFI partition; the rest becomes root."
|
||||
say "The size includes a new 2GiB EFI partition; the rest becomes root."
|
||||
else
|
||||
gum style "Using existing ESP: $shared_esp"
|
||||
say "Using existing ESP: $shared_esp"
|
||||
fi
|
||||
gum style "Omarchy can use 32–${INSTALL_MAX_GIB}GiB total. Leave the default to use all available space."
|
||||
say "Omarchy can use 32–${INSTALL_MAX_GIB}GiB total. Leave the default to use all available space."
|
||||
echo
|
||||
|
||||
INSTALL_SIZE_GIB=$(gum input \
|
||||
@@ -414,32 +559,61 @@ run_partition_install() {
|
||||
ROOT_END_B=$((ROOT_START_B + INSTALL_SIZE_GIB * gib))
|
||||
fi
|
||||
|
||||
step "Proposed layout on $disk"
|
||||
gum style "Free region: $(to_gb $FREE_START_B) — $(to_gb $FREE_END_B) ($(to_gb $FREE_SIZE_B))"
|
||||
if [[ -z "$shared_esp" ]]; then
|
||||
gum style " New EFI partition: $(to_gb $EFI_START_B) — $(to_gb $EFI_END_B) (2GB FAT32)"
|
||||
else
|
||||
gum style " Shared ESP: $shared_esp (Windows, mounted at /efi)"
|
||||
fi
|
||||
gum style " New root (Btrfs): $(to_gb $ROOT_START_B) — $(to_gb $ROOT_END_B) ($(to_gb $((ROOT_END_B - ROOT_START_B))))"
|
||||
echo
|
||||
# Shared Windows ESP can't host our LUKS layout, so encryption isn't
|
||||
# offered in that case. Otherwise default to encrypted and hide the
|
||||
# unencrypted path behind Ctrl+C, matching the full-disk flow.
|
||||
local mode="encrypted" affirmative confirm_status
|
||||
[[ -n "$shared_esp" ]] && mode="forced_unencrypted"
|
||||
|
||||
gum confirm --affirmative "Yes, partition" --negative "Cancel" \
|
||||
"Create Omarchy partitions in the free space on $disk?" || return 1
|
||||
while true; do
|
||||
step "Proposed layout on $disk"
|
||||
say "Free region: $(to_gb $FREE_START_B) — $(to_gb $FREE_END_B) ($(to_gb $FREE_SIZE_B))"
|
||||
if [[ -z "$shared_esp" ]]; then
|
||||
say " New EFI partition: $(to_gb $EFI_START_B) — $(to_gb $EFI_END_B) (2GB FAT32)"
|
||||
else
|
||||
say " Shared ESP: $shared_esp (Windows, mounted at /efi)"
|
||||
fi
|
||||
say " New root (Btrfs): $(to_gb $ROOT_START_B) — $(to_gb $ROOT_END_B) ($(to_gb $((ROOT_END_B - ROOT_START_B))))"
|
||||
echo
|
||||
|
||||
echo
|
||||
if gum confirm \
|
||||
--affirmative "Yes, encrypt" \
|
||||
--negative "No, leave unencrypted" \
|
||||
"Encrypt the Omarchy root partition with LUKS2?"; then
|
||||
encrypt_installation=true
|
||||
else
|
||||
encrypt_installation=false
|
||||
fi
|
||||
case $mode in
|
||||
encrypted)
|
||||
say --foreground 8 "Press Ctrl+C for unencrypted install."
|
||||
affirmative="Yes, install"
|
||||
;;
|
||||
unencrypted)
|
||||
affirmative="Yes, install without encryption"
|
||||
;;
|
||||
forced_unencrypted)
|
||||
say --foreground 8 "Shared Windows ESP cannot host encrypted Omarchy — installing unencrypted."
|
||||
affirmative="Yes, install"
|
||||
;;
|
||||
esac
|
||||
echo
|
||||
|
||||
if [[ "$encrypt_installation" == "true" && -n "$shared_esp" ]]; then
|
||||
abort "Encrypted install on a shared Windows ESP is not yet supported. Please choose unencrypted, or use Full Install."
|
||||
fi
|
||||
gum confirm --affirmative "$affirmative" --negative "Cancel" \
|
||||
"Create Omarchy partitions in the free space on $disk?"
|
||||
confirm_status=$?
|
||||
|
||||
case $confirm_status in
|
||||
0)
|
||||
[[ $mode == "encrypted" ]] && encrypt_installation=true || encrypt_installation=false
|
||||
break
|
||||
;;
|
||||
1)
|
||||
return 1
|
||||
;;
|
||||
130)
|
||||
case $mode in
|
||||
encrypted) mode="unencrypted" ;;
|
||||
unencrypted) mode="encrypted" ;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
abort
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
local esp_mount_in_target
|
||||
if [[ "$encrypt_installation" == "true" ]]; then
|
||||
@@ -465,20 +639,31 @@ run_partition_install() {
|
||||
root_part_num=$(( ${last_part:-0} + 1 ))
|
||||
fi
|
||||
|
||||
local efi_dev root_partition_device
|
||||
local efi_dev root_partition_device root_mapper
|
||||
if [[ -n "$shared_esp" ]]; then
|
||||
efi_dev="$shared_esp"
|
||||
else
|
||||
efi_dev=$(partition_path "$disk" "$efi_part_num")
|
||||
fi
|
||||
root_partition_device=$(partition_path "$disk" "$root_part_num")
|
||||
if [[ "$encrypt_installation" == "true" ]]; then
|
||||
root_mapper="/dev/mapper/omarchy_root"
|
||||
else
|
||||
root_mapper="$root_partition_device"
|
||||
fi
|
||||
|
||||
if $dry; then
|
||||
gum style "[dry] would create root partition at $root_partition_device"
|
||||
[[ -z "$shared_esp" ]] && gum style "[dry] would create EFI partition at $efi_dev"
|
||||
gum style "[dry] encrypted=$encrypt_installation kernel=$kernel_choice"
|
||||
$needs_mklabel && say "[dry] would initialize GPT on $disk"
|
||||
say "[dry] would create root partition at $root_partition_device"
|
||||
[[ -z "$shared_esp" ]] && say "[dry] would create EFI partition at $efi_dev"
|
||||
say "[dry] encrypted=$encrypt_installation kernel=$kernel_choice"
|
||||
else
|
||||
step "Creating partitions on $disk"
|
||||
if $needs_mklabel; then
|
||||
parted --script "$disk" mklabel gpt
|
||||
partprobe "$disk" 2>/dev/null || true
|
||||
sleep 1
|
||||
fi
|
||||
if [[ -z "$shared_esp" ]]; then
|
||||
parted --script "$disk" mkpart primary fat32 "${EFI_START_B}B" "${EFI_END_B}B"
|
||||
parted --script "$disk" set "$efi_part_num" esp on
|
||||
@@ -498,7 +683,6 @@ run_partition_install() {
|
||||
sleep 1
|
||||
done
|
||||
|
||||
local root_mapper
|
||||
if [[ "$encrypt_installation" == "true" ]]; then
|
||||
step "Setting up LUKS2 on $root_partition_device"
|
||||
printf "%s" "$password" | cryptsetup luksFormat --type luks2 --batch-mode "$root_partition_device" -
|
||||
@@ -568,6 +752,16 @@ $credentials_encryption_line
|
||||
}
|
||||
_EOF_
|
||||
|
||||
local luks_uuid_json="null" luks_uuid
|
||||
if [[ "$encrypt_installation" == "true" ]]; then
|
||||
if $dry; then
|
||||
luks_uuid="00000000-0000-0000-0000-000000000000"
|
||||
else
|
||||
luks_uuid=$(blkid -s UUID -o value "$root_partition_device")
|
||||
fi
|
||||
luks_uuid_json="\"$luks_uuid\""
|
||||
fi
|
||||
|
||||
cat <<-_EOF_ >user_configuration.json
|
||||
{
|
||||
"app_config": null,
|
||||
@@ -576,6 +770,23 @@ _EOF_
|
||||
"audio_config": { "audio": "pipewire" },
|
||||
"bootloader_config": { "bootloader": "Limine", "uki": false, "removable": false },
|
||||
"custom_commands": [],
|
||||
"omarchy_install": {
|
||||
"mode": "protected",
|
||||
"target_mount": "/mnt",
|
||||
"boot": {
|
||||
"esp_mount": "$esp_mount_in_target",
|
||||
"esp_path": "/EFI/limine",
|
||||
"efi_binary": "limine_x64.efi",
|
||||
"enable_fallback": false
|
||||
},
|
||||
"storage": {
|
||||
"esp_device": "$efi_dev",
|
||||
"root_device": "$root_partition_device",
|
||||
"root_mapper": "$root_mapper",
|
||||
"luks_uuid": $luks_uuid_json,
|
||||
"kernel": "$kernel_choice"
|
||||
}
|
||||
},
|
||||
"disk_config": {
|
||||
"config_type": "pre_mounted_config",
|
||||
"mountpoint": "/mnt"
|
||||
@@ -608,8 +819,7 @@ _EOF_
|
||||
"base-devel",
|
||||
"git",
|
||||
"omarchy-keyring",
|
||||
"omarchy",
|
||||
"omarchy-installer"
|
||||
"omarchy"
|
||||
],
|
||||
"profile_config": {
|
||||
"gfx_driver": null,
|
||||
@@ -620,27 +830,6 @@ _EOF_
|
||||
}
|
||||
_EOF_
|
||||
|
||||
local luks_uuid_json="null" luks_uuid
|
||||
if [[ "$encrypt_installation" == "true" ]]; then
|
||||
if $dry; then
|
||||
luks_uuid="00000000-0000-0000-0000-000000000000"
|
||||
else
|
||||
luks_uuid=$(blkid -s UUID -o value "$root_partition_device")
|
||||
fi
|
||||
luks_uuid_json="\"$luks_uuid\""
|
||||
fi
|
||||
|
||||
cat >/root/protected_install.json <<JSON
|
||||
{
|
||||
"esp_device": "$efi_dev",
|
||||
"esp_mount": "$esp_mount_in_target",
|
||||
"esp_path": "/EFI/limine",
|
||||
"luks_uuid": $luks_uuid_json,
|
||||
"root_device": "$root_partition_device",
|
||||
"kernel": "$kernel_choice"
|
||||
}
|
||||
JSON
|
||||
|
||||
if $dry; then
|
||||
echo -e "User Configuration:"
|
||||
cat user_configuration.json
|
||||
@@ -652,9 +841,7 @@ JSON
|
||||
cat user_email_address.txt
|
||||
echo -e "\nDisk Encryption:"
|
||||
cat user_encrypt_installation.txt
|
||||
echo -e "\nProtected Install Intent:"
|
||||
cat /root/protected_install.json
|
||||
rm -f user_configuration.json user_credentials.json user_full_name.txt user_email_address.txt user_encrypt_installation.txt /root/protected_install.json
|
||||
rm -f user_configuration.json user_credentials.json user_full_name.txt user_email_address.txt user_encrypt_installation.txt
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -709,11 +896,17 @@ open_partition_tool() {
|
||||
}
|
||||
|
||||
install_mode_form() {
|
||||
local choices=("Full disk install")
|
||||
|
||||
# The free-space/protected path registers an EFI boot entry and is UEFI-only.
|
||||
# Full-disk installs still support BIOS through the orchestrator's BIOS branch.
|
||||
if [[ -d /sys/firmware/efi ]]; then
|
||||
choices+=("Free space install (alongside existing data)")
|
||||
fi
|
||||
choices+=("Choose a different disk")
|
||||
|
||||
step "Installation mode"
|
||||
install_mode=$(gum choose --header "How should Omarchy be installed on $disk?" \
|
||||
"Full disk install" \
|
||||
"Free space install (alongside existing data)" \
|
||||
"Open partition tool") || abort
|
||||
install_mode=$(gum choose --header "How should Omarchy be installed on $disk?" "${choices[@]}") || abort
|
||||
}
|
||||
|
||||
while true; do
|
||||
@@ -728,8 +921,8 @@ while true; do
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
"Open partition tool")
|
||||
open_partition_tool
|
||||
"Choose a different disk")
|
||||
disk_form
|
||||
;;
|
||||
esac
|
||||
done
|
||||
@@ -855,6 +1048,19 @@ cat <<-_EOF_ >user_configuration.json
|
||||
"audio_config": { "audio": "pipewire" },
|
||||
"bootloader_config": { "bootloader": "Limine", "uki": false, "removable": false },
|
||||
"custom_commands": [],
|
||||
"omarchy_install": {
|
||||
"mode": "full_disk",
|
||||
"target_mount": "/mnt",
|
||||
"boot": {
|
||||
"esp_mount": "/boot",
|
||||
"esp_path": "/EFI/limine",
|
||||
"efi_binary": "limine_x64.efi",
|
||||
"enable_fallback": true
|
||||
},
|
||||
"storage": {
|
||||
"kernel": "$kernel_choice"
|
||||
}
|
||||
},
|
||||
"disk_config": {
|
||||
"btrfs_options": {
|
||||
"snapshot_config": {
|
||||
@@ -946,8 +1152,7 @@ cat <<-_EOF_ >user_configuration.json
|
||||
"base-devel",
|
||||
"git",
|
||||
"omarchy-keyring",
|
||||
"omarchy",
|
||||
"omarchy-installer"
|
||||
"omarchy"
|
||||
],
|
||||
"profile_config": {
|
||||
"gfx_driver": null,
|
||||
|
||||
@@ -201,6 +201,9 @@ weights = [
|
||||
('Installing base system', 8, 84, 85),
|
||||
('Configuring protected boot', 84, 86, 8),
|
||||
('Configuring hibernation', 86, 88, 5),
|
||||
('Configuring system', 88, 93, 10),
|
||||
('Finalizing Limine boot', 93, 96, 8),
|
||||
('Finalizing user', 96, 98, 10),
|
||||
('Finalizing in chroot', 88, 98, 10),
|
||||
('Configuring login', 98, 99, 3),
|
||||
('Validating protected boot setup', 99, 100, 5),
|
||||
|
||||
@@ -66,7 +66,7 @@ def load_arch_config(config_path: Path, creds_path: Path) -> ArchConfigHandler:
|
||||
import sys
|
||||
saved_argv = sys.argv
|
||||
sys.argv = [
|
||||
saved_argv[0] if saved_argv else "omarchy-install",
|
||||
saved_argv[0] if saved_argv else "omarchy-iso-install",
|
||||
"--config", str(config_path),
|
||||
"--creds", str(creds_path),
|
||||
]
|
||||
|
||||
@@ -21,6 +21,8 @@ class InstallContext:
|
||||
|
||||
user_configuration: dict
|
||||
user_credentials: dict
|
||||
arch_config_path: Path
|
||||
omarchy_install: dict[str, Any]
|
||||
|
||||
target: Path = Path("/mnt")
|
||||
omarchy_path: Path = Path("/usr/share/omarchy")
|
||||
@@ -42,6 +44,15 @@ class InstallContext:
|
||||
config_path = Path(config_str)
|
||||
creds_path = Path(creds_str)
|
||||
user_configuration = json.loads(config_path.read_text())
|
||||
omarchy_install = user_configuration.get("omarchy_install") or _default_omarchy_install(user_configuration)
|
||||
|
||||
arch_configuration = dict(user_configuration)
|
||||
arch_configuration.pop("omarchy_install", None)
|
||||
state_dir = Path(os.environ.get("OMARCHY_INSTALL_STATE_DIR", "/run/omarchy-install"))
|
||||
state_dir.mkdir(parents=True, exist_ok=True)
|
||||
arch_config_path = state_dir / "archinstall-user_configuration.json"
|
||||
arch_config_path.write_text(json.dumps(arch_configuration, indent=2) + "\n")
|
||||
|
||||
ctx = cls(
|
||||
config_path=config_path,
|
||||
creds_path=creds_path,
|
||||
@@ -50,13 +61,14 @@ class InstallContext:
|
||||
encrypt=_read_text(os.environ.get("OMARCHY_INSTALL_ENCRYPT_FILE")).lower() in ("true", "yes", "1"),
|
||||
user_configuration=user_configuration,
|
||||
user_credentials=json.loads(creds_path.read_text()),
|
||||
arch_config_path=arch_config_path,
|
||||
omarchy_install=omarchy_install,
|
||||
state_dir=state_dir,
|
||||
)
|
||||
disk_config = user_configuration.get("disk_config", {})
|
||||
if (
|
||||
disk_config.get("config_type") == "pre_mounted_config"
|
||||
and disk_config.get("mountpoint")
|
||||
):
|
||||
ctx.target = Path(disk_config["mountpoint"])
|
||||
target_mount = omarchy_install.get("target_mount") or disk_config.get("mountpoint")
|
||||
if target_mount:
|
||||
ctx.target = Path(target_mount)
|
||||
return ctx
|
||||
|
||||
@property
|
||||
@@ -65,6 +77,8 @@ class InstallContext:
|
||||
|
||||
@property
|
||||
def mode(self) -> str:
|
||||
if mode := self.omarchy_install.get("mode"):
|
||||
return mode
|
||||
cfg_type = self.user_configuration.get("disk_config", {}).get("config_type")
|
||||
return "protected" if cfg_type == "pre_mounted_config" else "full_disk"
|
||||
|
||||
@@ -73,6 +87,22 @@ class InstallContext:
|
||||
return self.mode == "protected"
|
||||
|
||||
|
||||
def _default_omarchy_install(user_configuration: dict) -> dict[str, Any]:
|
||||
disk_config = user_configuration.get("disk_config", {})
|
||||
mode = "protected" if disk_config.get("config_type") == "pre_mounted_config" else "full_disk"
|
||||
return {
|
||||
"mode": mode,
|
||||
"target_mount": disk_config.get("mountpoint") or "/mnt",
|
||||
"boot": {
|
||||
"esp_mount": "/boot",
|
||||
"esp_path": "/EFI/limine",
|
||||
"efi_binary": "limine_x64.efi",
|
||||
"enable_fallback": mode == "full_disk",
|
||||
},
|
||||
"storage": {},
|
||||
}
|
||||
|
||||
|
||||
def _read_text(path: str | None) -> str:
|
||||
if not path:
|
||||
return ""
|
||||
|
||||
@@ -23,43 +23,33 @@ def build_phases(ctx: InstallContext):
|
||||
hooks (limine-mkinitcpio-hook, in particular) and useradd happen at
|
||||
points where their prerequisites are guaranteed to be in place.
|
||||
|
||||
Two modes:
|
||||
- full_disk: archinstall owns disk layout + bootloader.
|
||||
- protected: Omarchy owns disk layout + bootloader; archinstall is
|
||||
used for pacstrap + users + packages only.
|
||||
Full-disk and protected installs use the same phase sequence. The
|
||||
configurator only changes the JSON input: full-disk asks archinstall to
|
||||
create/mount the layout, while protected provides an already-mounted target
|
||||
and the partition details Omarchy needs for boot/fstab generation.
|
||||
"""
|
||||
from .phases_impl import (
|
||||
prepare_live,
|
||||
arch_install_full,
|
||||
arch_install_base,
|
||||
verify_protected_mounts,
|
||||
configure_protected_boot,
|
||||
prepare_install_target,
|
||||
arch_install_system,
|
||||
configure_hibernation,
|
||||
run_system_finalizer,
|
||||
finalize_limine_boot,
|
||||
run_chroot_finalizer,
|
||||
configure_login,
|
||||
validate_boot_full,
|
||||
validate_boot_protected,
|
||||
validate_boot,
|
||||
)
|
||||
|
||||
if ctx.is_protected:
|
||||
return [
|
||||
("Preparing live environment", prepare_live),
|
||||
("Verifying protected mounts", verify_protected_mounts),
|
||||
("Installing base system", arch_install_base),
|
||||
("Configuring protected boot", configure_protected_boot),
|
||||
("Configuring hibernation", configure_hibernation),
|
||||
("Finalizing in chroot", run_chroot_finalizer),
|
||||
("Configuring login", configure_login),
|
||||
("Validating protected boot setup", validate_boot_protected),
|
||||
]
|
||||
|
||||
return [
|
||||
("Preparing live environment", prepare_live),
|
||||
("Installing Arch + Omarchy", arch_install_full),
|
||||
("Preparing install target", prepare_install_target),
|
||||
("Installing Arch + Omarchy", arch_install_system),
|
||||
("Configuring hibernation", configure_hibernation),
|
||||
("Finalizing in chroot", run_chroot_finalizer),
|
||||
("Configuring system", run_system_finalizer),
|
||||
("Finalizing Limine boot", finalize_limine_boot),
|
||||
("Finalizing user", run_chroot_finalizer),
|
||||
("Configuring login", configure_login),
|
||||
("Validating boot setup", validate_boot_full),
|
||||
("Validating boot setup", validate_boot),
|
||||
]
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -16,7 +16,7 @@ These exist outside omarchy-iso. Flagged for visibility — without them, nothin
|
||||
|
||||
1. **aarch64 base packages must exist somewhere we can pacman from.** Vanilla Arch (`geo.mirror.pkgbuild.com`) is x86_64-only — there is no `core/os/aarch64`. The realistic source is **Arch Linux ARM** (`mirror.archlinuxarm.org`) for `core`/`extra`/`alarm`/`aur`. Decision required: target Arch Linux ARM as the aarch64 base distribution.
|
||||
2. **`pkgs.omarchy.org/{stable,edge}/aarch64/`** must serve a real repo. Probed today, both return 404. omarchy-pkgs already has multi-arch build support per its README, so this is a publish step, not a port.
|
||||
3. **Omarchy installer guard** at `~/.local/share/omarchy/install/preflight/guard.sh:25` hard-aborts on `uname -m != x86_64`. Must be relaxed to allow aarch64 (or scoped behind a flag).
|
||||
3. **Omarchy ISO architecture guard** must allow supported architectures or scope any x86_64-only checks behind an explicit guard.
|
||||
4. **archinstall + Limine** must work end-to-end on aarch64. archinstall supports it; Limine supports aarch64 UEFI. Worth a manual verification before committing to this bootloader path on ARM.
|
||||
|
||||
---
|
||||
@@ -109,9 +109,9 @@ Both scripts hardcode the OVMF path; small, mechanical fix.
|
||||
### 10. Release script — `bin/omarchy-iso-release:52`
|
||||
|
||||
```bash
|
||||
latest_iso=$(\ls -t "$BUILD_RELEASE_PATH"/*x86_64-"$INSTALLER_REF".iso | head -n1)
|
||||
latest_iso=$(\ls -t "$BUILD_RELEASE_PATH"/*${OMARCHY_ARCH}-"$ISO_REF".iso | head -n1)
|
||||
```
|
||||
Hardcoded `x86_64` glob. Take an arch arg, or do `*${OMARCHY_ARCH}-${INSTALLER_REF}.iso`. The ISO filename already contains arch (set by archiso from `profiledef.sh`), so this just needs the glob parameterized.
|
||||
Hardcoded `x86_64` glob. Take an arch arg, or do `*${OMARCHY_ARCH}-${ISO_REF}.iso`. The ISO filename already contains arch (set by archiso from `profiledef.sh`), so this just needs the glob parameterized.
|
||||
|
||||
### 11. CI — `.github/workflows/nightly-build.yml`
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ Create a second ISO flavor for OEM preloading that is installed to internal stor
|
||||
- `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).
|
||||
- Rename output ISO with `-oem` suffix (in addition to existing `-$OMARCHY_ISO_REF` suffix).
|
||||
|
||||
### Hardening
|
||||
- Add post-injection build assertion that fails build if any expected boot config does not contain `copytoram=y`.
|
||||
|
||||
Reference in new issue
Block a user