Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

flash-mac.sh: run as root + wait for partition nodes after sgdisk

Two robustness fixes pulled in alongside the new ac-flash-mac sudoers
whitelist (which scopes NOPASSWD at the script path, not individual
sub-commands):

1. Re-exec under sudo when invoked as a regular user. Drops every
inner `sudo ...` call — the script now always runs as root, so each
privileged step (sgdisk, dd, newfs_msdos, mount_msdos, etc.) just
works without per-command sudoers entries.

2. After `sgdisk --new` writes the new partition table, macOS doesn't
reload it automatically — the s1/s2 device nodes are stale or
missing for ~1-3 seconds. Without this wait, newfs_msdos errors
with "No such file or directory" on a fresh wipe-and-flash. Poll
for the nodes (up to 10s) and prod the kernel via `diskutil list`
between checks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+48 -29
+48 -29
fedac/native/scripts/flash-mac.sh
··· 34 34 USB_DEV="${1:?usage: $0 /dev/diskN [SRC_DIR]}" 35 35 SRC_DIR="${2:-/tmp/ac-os-pull}" 36 36 37 + # This script needs root for diskutil/sgdisk/dd/newfs_msdos/mount_msdos. 38 + # Re-exec under sudo if invoked as a regular user (sudoers.d/ac-flash-mac 39 + # whitelists this exact path NOPASSWD). 40 + if [ "$(id -u)" != "0" ]; then 41 + exec sudo --preserve-env=PATH "$0" "$@" 42 + fi 43 + 37 44 SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 38 45 REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" 39 46 NATIVE_DIR="${REPO_ROOT}/native" ··· 41 48 42 49 KERNEL="${SRC_DIR}/vmlinuz" 43 50 INITRAMFS="${SRC_DIR}/initramfs.cpio.gz" 44 - SPLASH_EFI="${NATIVE_DIR}/boot/splash.efi" 51 + SPLASH_EFI="${NATIVE_DIR}/bootloader/splash.efi" 45 52 SDBOOT_EFI="${NATIVE_DIR}/boot/systemd-bootx64.efi" 46 53 47 54 log() { echo "[flash-mac] $*"; } ··· 90 97 91 98 # --- wipe + repartition --- 92 99 log "Unmounting…" 93 - sudo diskutil unmountDisk force "${USB_DEV}" >/dev/null 100 + diskutil unmountDisk force "${USB_DEV}" >/dev/null 94 101 95 102 log "Zapping GPT + clearing first 16 MiB…" 96 - sudo sgdisk --zap-all "${USB_DEV}" >/dev/null 97 - sudo dd if=/dev/zero of="${USB_DEV}" bs=1m count=16 status=none 103 + sgdisk --zap-all "${USB_DEV}" >/dev/null 104 + dd if=/dev/zero of="${USB_DEV}" bs=1m count=16 status=none 98 105 99 106 log "Creating GPT layout (ACBOOT + ACEFI)…" 100 - sudo sgdisk \ 107 + sgdisk \ 101 108 --new=1:0:+${MAIN_MB}M --typecode=1:0700 --change-name=1:ACBOOT \ 102 109 --new=2:0:0 --typecode=2:ef00 --change-name=2:ACEFI \ 103 110 "${USB_DEV}" >/dev/null 104 111 105 - # Re-read partition table so /dev/diskNs* nodes appear. 106 - sudo diskutil unmountDisk force "${USB_DEV}" >/dev/null 2>&1 || true 107 - sleep 1 112 + # Force macOS to re-read the partition table after sgdisk wrote it. The 113 + # kernel caches the old layout until we explicitly notify it; without this, 114 + # the s1/s2 nodes either don't exist yet or still point at the pre-zap 115 + # partitions and newfs_msdos errors with "No such file or directory". 116 + diskutil unmountDisk force "${USB_DEV}" >/dev/null 2>&1 || true 117 + diskutil list "${USB_DEV}" >/dev/null 2>&1 || true 108 118 109 119 P1="${USB_DEV}s1" 110 120 P2="${USB_DEV}s2" 111 121 RAW1="/dev/r$(basename "${P1}")" 112 122 RAW2="/dev/r$(basename "${P2}")" 113 123 124 + # Wait up to 10s for both partition nodes to materialize. 125 + for i in $(seq 1 20); do 126 + [ -e "${P1}" ] && [ -e "${P2}" ] && break 127 + sleep 0.5 128 + diskutil list "${USB_DEV}" >/dev/null 2>&1 || true 129 + done 130 + [ -e "${P1}" ] || die "Partition ${P1} did not appear after sgdisk + reread." 131 + [ -e "${P2}" ] || die "Partition ${P2} did not appear after sgdisk + reread." 132 + 114 133 log "Formatting FAT32 partitions…" 115 - sudo newfs_msdos -F 32 -v ACBOOT "${RAW1}" >/dev/null 116 - sudo newfs_msdos -F 32 -v ACEFI "${RAW2}" >/dev/null 134 + newfs_msdos -F 32 -v ACBOOT "${RAW1}" >/dev/null 135 + newfs_msdos -F 32 -v ACEFI "${RAW2}" >/dev/null 117 136 118 137 # --- mount --- 119 138 M1=$(mktemp -d /tmp/ac-main.XXXXXX) 120 139 M2=$(mktemp -d /tmp/ac-efi.XXXXXX) 121 - trap "sudo umount '${M1}' 2>/dev/null; sudo umount '${M2}' 2>/dev/null; rmdir '${M1}' '${M2}' 2>/dev/null; true" EXIT 140 + trap "umount '${M1}' 2>/dev/null; umount '${M2}' 2>/dev/null; rmdir '${M1}' '${M2}' 2>/dev/null; true" EXIT 122 141 123 142 log "Mounting partitions…" 124 - sudo mount_msdos "${P1}" "${M1}" 125 - sudo mount_msdos "${P2}" "${M2}" 143 + mount_msdos "${P1}" "${M1}" 144 + mount_msdos "${P2}" "${M2}" 126 145 127 146 # --- layout ACBOOT (kernel-direct + config) --- 128 147 log "Writing ACBOOT (kernel-direct boot tree)…" 129 - sudo mkdir -p "${M1}/EFI/BOOT" 130 - sudo cp "${KERNEL}" "${M1}/EFI/BOOT/BOOTX64.EFI" 131 - sudo cp "${INITRAMFS}" "${M1}/initramfs.cpio.gz" 132 - echo '{"handle":"","piece":"notepat","sub":"","email":""}' | sudo tee "${M1}/config.json" >/dev/null 133 - [ -f "${SRC_DIR}/wifi_creds.json" ] && sudo cp "${SRC_DIR}/wifi_creds.json" "${M1}/wifi_creds.json" 148 + mkdir -p "${M1}/EFI/BOOT" 149 + cp "${KERNEL}" "${M1}/EFI/BOOT/BOOTX64.EFI" 150 + cp "${INITRAMFS}" "${M1}/initramfs.cpio.gz" 151 + echo '{"handle":"","piece":"notepat","sub":"","email":""}' | tee "${M1}/config.json" >/dev/null 152 + [ -f "${SRC_DIR}/wifi_creds.json" ] && cp "${SRC_DIR}/wifi_creds.json" "${M1}/wifi_creds.json" 134 153 135 154 # --- layout ACEFI (systemd-boot universal) --- 136 155 log "Writing ACEFI (splash → systemd-boot → kernel)…" 137 - sudo mkdir -p "${M2}/EFI/BOOT" "${M2}/loader/entries" 138 - sudo cp "${SPLASH_EFI}" "${M2}/EFI/BOOT/BOOTX64.EFI" 139 - sudo cp "${SDBOOT_EFI}" "${M2}/EFI/BOOT/LOADER.EFI" 140 - sudo cp "${KERNEL}" "${M2}/EFI/BOOT/KERNEL.EFI" 141 - sudo cp "${INITRAMFS}" "${M2}/initramfs.cpio.gz" 142 - sudo tee "${M2}/loader/loader.conf" >/dev/null <<'EOF' 156 + mkdir -p "${M2}/EFI/BOOT" "${M2}/loader/entries" 157 + cp "${SPLASH_EFI}" "${M2}/EFI/BOOT/BOOTX64.EFI" 158 + cp "${SDBOOT_EFI}" "${M2}/EFI/BOOT/LOADER.EFI" 159 + cp "${KERNEL}" "${M2}/EFI/BOOT/KERNEL.EFI" 160 + cp "${INITRAMFS}" "${M2}/initramfs.cpio.gz" 161 + tee "${M2}/loader/loader.conf" >/dev/null <<'EOF' 143 162 default ac-native.conf 144 163 timeout 0 145 164 EOF 146 - sudo tee "${M2}/loader/entries/ac-native.conf" >/dev/null <<'EOF' 165 + tee "${M2}/loader/entries/ac-native.conf" >/dev/null <<'EOF' 147 166 title AC Native OS 148 167 linux /EFI/BOOT/KERNEL.EFI 149 168 initrd /initramfs.cpio.gz 150 169 options console=tty0 quiet loglevel=3 vt.global_cursor_default=0 init=/init nomodeset efi=noruntime 151 170 EOF 152 - echo '{"handle":"","piece":"notepat","sub":"","email":""}' | sudo tee "${M2}/config.json" >/dev/null 153 - [ -f "${SRC_DIR}/wifi_creds.json" ] && sudo cp "${SRC_DIR}/wifi_creds.json" "${M2}/wifi_creds.json" 171 + echo '{"handle":"","piece":"notepat","sub":"","email":""}' | tee "${M2}/config.json" >/dev/null 172 + [ -f "${SRC_DIR}/wifi_creds.json" ] && cp "${SRC_DIR}/wifi_creds.json" "${M2}/wifi_creds.json" 154 173 155 174 # --- verify (sha256 round-trip on every kernel + initramfs copy) --- 156 175 log "Verifying integrity…" ··· 169 188 # --- finalize --- 170 189 sync 171 190 log "Unmounting + ejecting…" 172 - sudo umount "${M1}" "${M2}" 173 - sudo diskutil eject "${USB_DEV}" >/dev/null 191 + umount "${M1}" "${M2}" 192 + diskutil eject "${USB_DEV}" >/dev/null 174 193 trap - EXIT 175 194 rmdir "${M1}" "${M2}" 2>/dev/null || true 176 195