diff --git a/bin/omarchy-iso-make-offline b/bin/omarchy-iso-make-offline new file mode 100755 index 0000000..7d6ecbc --- /dev/null +++ b/bin/omarchy-iso-make-offline @@ -0,0 +1,70 @@ +#!/bin/bash + +# Abort if anything fails +set -e + +# Ensure we have the submodule checked out +git submodule update --init --recursive --jobs=8 + +# Prepare build repo +BUILD_SCRIPT="builder/offline.sh" +BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..") +BUILD_RELEASE_PATH="$BUILD_ROOT/release" +mkdir -p "$BUILD_RELEASE_PATH" + +OMARCHY_CONFIGURATOR_REPO="${OMARCHY_CONFIGURATOR_REPO:-omacom-io/omarchy-configurator}" +OMARCHY_CONFIGURATOR_REF="${OMARCHY_CONFIGURATOR_REF:-master}" + +OMARCHY_INSTALLER_REPO="${OMARCHY_INSTALLER_REPO:-basecamp/omarchy}" +OMARCHY_INSTALLER_REF="${OMARCHY_INSTALLER_REF:-master}" +OMARCHY_INSTALLER_URL="${OMARCHY_INSTALLER_URL:-https://omarchy.org/install}" + +DOCKER_ARGS=( + --rm + --privileged + -e "OMARCHY_CONFIGURATOR_REPO=$OMARCHY_CONFIGURATOR_REPO" + -e "OMARCHY_CONFIGURATOR_REF=$OMARCHY_CONFIGURATOR_REF" + -e "OMARCHY_INSTALLER_REPO=$OMARCHY_INSTALLER_REPO" + -e "OMARCHY_INSTALLER_REF=$OMARCHY_INSTALLER_REF" + -e "OMARCHY_INSTALLER_URL=$OMARCHY_INSTALLER_URL" + -v "$BUILD_RELEASE_PATH/:/out/" + -v "$BUILD_ROOT/$BUILD_SCRIPT:/$BUILD_SCRIPT:ro" + -v "$BUILD_ROOT/archiso:/archiso:ro" + -v "$BUILD_ROOT/builder:/builder:ro" +) + +# Use local pacman cache if you already have one on host to speed up repeat runs +if [ -d "/var/cache/pacman/pkg" ]; then + DOCKER_ARGS+=(-v "/var/cache/pacman/pkg:/var/cache/pacman/pkg") +fi + +# Mount persistent cache for offline mirror packages to avoid re-downloading +OFFLINE_CACHE_DIR="$HOME/.cache/omarchy-offline-mirror" +mkdir -p "$OFFLINE_CACHE_DIR" +DOCKER_ARGS+=(-v "$OFFLINE_CACHE_DIR:/var/cache/omarchy") + +# Also mount the build cache directory where packages are initially downloaded +# Use a fixed name to avoid timezone/date mismatch issues between host and container +BUILD_DATE=$(date +%Y-%m-%d) +BUILD_CACHE_DIR="$HOME/.cache/omarchy/iso_${BUILD_DATE}/airootfs/var/cache/omarchy/mirror/offline" +mkdir -p "$BUILD_CACHE_DIR" +# Pass the same date to the container via environment variable +DOCKER_ARGS+=(-e "ISO_BUILD_DATE=${BUILD_DATE}") +DOCKER_ARGS+=(-v "$BUILD_CACHE_DIR:/root/.cache/omarchy/iso_${BUILD_DATE}/airootfs/var/cache/omarchy/mirror/offline") + +# Run the builder with assembled args +docker run "${DOCKER_ARGS[@]}" archlinux/archlinux:latest /$BUILD_SCRIPT + +# Make iso accessible to all +sudo chmod 777 -R "$BUILD_RELEASE_PATH" + +# Move as install ref +latest_iso=$(\ls -t "$BUILD_RELEASE_PATH"/*.iso | head -n1) +iso_ref="${latest_iso%.*}-$OMARCHY_INSTALLER_REF.iso" +mv "$latest_iso" "$iso_ref" + +# Offer to boot the new build +if [[ $1 != "--no-boot-offer" ]]; then + echo + gum confirm "Boot $iso_ref?" && omarchy-iso-boot "$iso_ref" +fi diff --git a/builder/cmds/autostart-offline.sh b/builder/cmds/autostart-offline.sh new file mode 100644 index 0000000..03618ed --- /dev/null +++ b/builder/cmds/autostart-offline.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash +set -euo pipefail + +chroot_bash() { + HOME=/home/$OMARCHY_USER \ + arch-chroot -u $OMARCHY_USER /mnt/ \ + env OMARCHY_CHROOT_INSTALL=1 \ + OMARCHY_USER_NAME="$(/mnt/etc/sudoers.d/99-omarchy-installer </dev/null || true + + # Run Omarchy installer directly (skip boot.sh since it would try to clone again) + # boot.sh just clones the repo and runs install.sh, but we already have the repo + chroot_bash -lc "source /home/$OMARCHY_USER/.local/share/omarchy/install.sh || bash" +fi diff --git a/builder/offline.sh b/builder/offline.sh index 7b80618..397af6f 100755 --- a/builder/offline.sh +++ b/builder/offline.sh @@ -8,7 +8,12 @@ pacman-key --init pacman --noconfirm -Sy archlinux-keyring pacman --noconfirm -Sy archiso git python-pip sudo base-devel jq -cache_dir=$(realpath --canonicalize-missing ~/.cache/omarchy/iso_$(date +%Y-%m-%d)) +# Use the date from the host if provided (to avoid timezone mismatches), otherwise use container date +if [ -n "$ISO_BUILD_DATE" ]; then + cache_dir=$(realpath --canonicalize-missing ~/.cache/omarchy/iso_${ISO_BUILD_DATE}) +else + cache_dir=$(realpath --canonicalize-missing ~/.cache/omarchy/iso_$(date +%Y-%m-%d)) +fi offline_mirror_dir="$cache_dir/airootfs/var/cache/omarchy/mirror/offline" # We need to fiddle with pip settings @@ -27,6 +32,7 @@ python_packages=( arch_packages=( git gum + jq openssl tzupdate # This is technically an AUR package ) @@ -38,7 +44,7 @@ prepare_offline_mirror() { # Combine all packages from both files into one array all_packages=() - for package_file in omarchy.packages archinstall.packages; do + for package_file in /builder/packages/omarchy.packages /builder/packages/archinstall.packages; do if [ -f "$package_file" ]; then echo "Reading $package_file..." while IFS= read -r package; do @@ -62,6 +68,7 @@ prepare_offline_mirror() { sed -i 's/^#*DownloadUser = alpm/DownloadUser = root/' /etc/pacman.conf # Download all the packages to the offline mirror inside the ISO + echo "Downloading all packages (including AUR) to offline mirror: ${all_packages[@]}" pacman --config $cache_dir/pacman.conf \ --noconfirm -Syw "${all_packages[@]}" \ --cachedir $offline_mirror_dir/ \ @@ -81,14 +88,16 @@ make_archiso_offline() { # This function will simply disable any online activity we have. # for instance the reflector.service which tries to optimize # mirror order by fetching the latest mirror list by default. - # - # We'll leave some things online, like NTP as that won't - # interfere with anything, on the flip side it will help if we do - # have internet connectivity. + # Disable reflector rm "$cache_dir/airootfs/etc/systemd/system/multi-user.target.wants/reflector.service" rm -rf "$cache_dir/airootfs/etc/systemd/system/reflector.service.d" rm -rf "$cache_dir/airootfs/etc/xdg/reflector" + + # Disable the WKD sync service that would try to fetch keys from the network + # but ensure the keyring is already populated + rm -f "$cache_dir/airootfs/etc/systemd/system/multi-user.target.wants/archlinux-keyring-wkd-sync.service" + rm -f "$cache_dir/airootfs/etc/systemd/system/timers.target.wants/archlinux-keyring-wkd-sync.timer" } mkdir -p $cache_dir/ @@ -97,91 +106,41 @@ mkdir -p $offline_mirror_dir/ # We base our ISO on the official arch ISO (releng) config cp -r archiso/configs/releng/* $cache_dir/ +# Apply all the general patches early (same as online.sh) +# Need to do this from the cache_dir +cd $cache_dir +for patch in /builder/patches/*.patch; do + # Skip profiledef.patch as it conflicts with offline/permissions.patch + if [[ "$(basename "$patch")" != "profiledef.patch" ]]; then + git apply "$patch" + fi +done +cd - + prepare_offline_mirror make_archiso_offline # Insert the configurator in the root users home folder (default user in the official releng ISO profile). -curl -fsSL -o "airootfs/root/configurator" \ +mkdir -p "$cache_dir/airootfs/root" +curl -fsSL -o "$cache_dir/airootfs/root/configurator" \ "https://raw.githubusercontent.com/$OMARCHY_CONFIGURATOR_REPO/$OMARCHY_CONFIGURATOR_REF/configurator" # Clone Omarchy itself -git clone -b dev --single-branch https://github.com/basecamp/omarchy.git "$cache_dir/airootfs/root/omarchy" +git clone -b $OMARCHY_INSTALLER_REF --single-branch https://github.com/$OMARCHY_INSTALLER_REPO.git "$cache_dir/airootfs/root/omarchy" -# We add in our auto-start applications -# First we'll check for an active internet connection -# Then we'll start the omarchy installer -cat <<-_EOF_ | tee $cache_dir/airootfs/root/.automated_script.sh - #!/usr/bin/env bash +# Copy the autostart script (we'll need to create an offline version) +cp /builder/cmds/autostart-offline.sh $cache_dir/airootfs/root/.automated_script.sh - if [[ \$(tty) == "/dev/tty1" ]]; then - sh ./check_connectivity.sh && \ - sh ./installer && \ - archinstall \ - --config user_configuration.json \ - --creds user_credentials.json \ - --silent && \ - export OMARCHY_USER=\`ls /mnt/home/\` && \ - - 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/" && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/apps/mimetypes.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/apps/webapps.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/apps/xtras.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/config/detect-keyboard-layout.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/config/fix-fkeys.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/config/network.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/config/nvidia.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/config/power.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/config/timezones.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/config/config.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/config/identification.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/config/increase-sudo-tries.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/config/login.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/desktop/asdcontrol.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/desktop/bluetooth.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/desktop/fonts.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/desktop/hyprlandia.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/desktop/printer.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/desktop/theme.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/desktop/desktop.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/development/development.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/development/nvim.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/development/ruby.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/development/docker.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/development/firewall.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/development/terminal.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/preflight/migrations.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/preflight/aur.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/preflight/guard.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/preflight/gum.sh && \ - chmod +x /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/preflight/tte.sh && \ - echo '' > /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/preflight/tte.sh && \ - echo '' > /mnt/home/\$OMARCHY_USER/.local/share/omarchy/install/preflight/gum.sh && \ - - # Copy sudoers config to target system for passwordless sudo in chroot - mkdir -p /mnt/etc/sudoers.d && \ - cp /etc/sudoers.d/99-omarchy-installer /mnt/etc/sudoers.d/ && \ - echo "\$OMARCHY_USER ALL=(ALL:ALL) NOPASSWD: ALL" >> /mnt/etc/sudoers.d/99-omarchy-installer && \ - - HOME=/home/\$OMARCHY_USER arch-chroot -u \$OMARCHY_USER /mnt/ /bin/bash -c "source /home/\$OMARCHY_USER/.local/share/omarchy/install.sh" - fi -_EOF_ - -# We patch permissions, grub and efi loaders to our liking: +# Apply offline-specific patches (cd $cache_dir/ && git apply /builder/patches/offline/permissions.patch) -(cd $cache_dir/ && git apply /builder/patches/grub-autoboot.patch) -(cd $cache_dir/ && git apply /builder/patches/efi-autoboot.patch) -# We could also use: -# patch -p1 < aur-mirror.patch -# patch -p1 < permissions.patch -# patch -p1 < grub-autoboot.patch -# patch -p1 < efi-autoboot.patch # Remove the default motd rm "$cache_dir/airootfs/etc/motd" +# Ensure pacman cache directory exists with proper permissions +mkdir -p "$cache_dir/airootfs/var/cache/pacman/pkg" +chmod 755 "$cache_dir/airootfs/var/cache/pacman/pkg" + # Install Python packages for the installer into the ISO # file system. pip install "${python_packages[@]}" @@ -210,8 +169,8 @@ iso_packages=($(cat "$cache_dir/packages.x86_64")) mkdir -p /tmp/cleandb echo "Populating host offline mirror with ISO packages: ${iso_packages[@]}" -# And we have to use the hosts pacman.conf since all other pacman.conf -# files are prepped for offline use by this point. +# We need to exclude AUR packages like tzupdate since they're not in upstream repos +# They should already be downloaded to the offline mirror from the earlier step pacman --config /etc/pacman.conf \ --noconfirm -Syw $(echo "${iso_packages[@]}" | sed 's/tzupdate//g') \ --cachedir "/var/cache/omarchy/mirror/offline/" \