diff --git a/fedac/native/scripts/flash-mac.sh b/fedac/native/scripts/flash-mac.sh index 737fc8a088..2d31ece438 100755 --- a/fedac/native/scripts/flash-mac.sh +++ b/fedac/native/scripts/flash-mac.sh @@ -453,6 +453,16 @@ mount_fat() { # $1=partition device $2=mount point mount_msdos "$1" "$2" } log "Mounting partitions…" +# macOS 26+ no longer loads the msdosfs kext (mount_msdos fails with "msdos +# filesystem is not available"); FAT is mounted by a userland driver that +# diskutil knows how to reach. diskutil mount also works on older releases. +mount_fat() { + local part="$1" mnt="$2" + diskutil unmount "${part}" >/dev/null 2>&1 || true + diskutil mount -mountPoint "${mnt}" "${part}" >/dev/null \ + || mount_msdos "${part}" "${mnt}" \ + || die "Could not mount ${part} at ${mnt}" +} mount_fat "${P1}" "${M1}" mount_fat "${P2}" "${M2}" @@ -463,11 +473,12 @@ cp "${KERNEL}" "${M1}/EFI/BOOT/BOOTX64.EFI" cp "${INITRAMFS}" "${M1}/initramfs.cpio.gz" # Boot piece: aesel by default (the Claude session that writes pieces this # machine runs). Override per-flash with AC_BOOT_PIECE (e.g. -# AC_BOOT_PIECE=notepat flash-mac.sh ...). The kernel resolves the name to -# /pieces/.mjs at boot — see the "Boot piece from config" block in -# ac-native.c. Devices already flashed keep whatever piece their -# /mnt/config.json names. -BOOT_PIECE="${AC_BOOT_PIECE:-aesel}" +# AC_BOOT_PIECE=notepat flash-mac.sh ...) or with usbConfig.piece in the +# inscription. The kernel resolves the name to /pieces/.mjs at boot — +# see the "Boot piece from config" block in ac-native.c. Devices already +# flashed keep whatever piece their /mnt/config.json names. +INSCRIBED_PIECE=$(node -e 'try { const c=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8")).usbConfig||{}; if (typeof c.piece === "string" && /^[a-z0-9-]+$/.test(c.piece)) process.stdout.write(c.piece); } catch {}' "${INSCRIPTION_FILE}") +BOOT_PIECE="${AC_BOOT_PIECE:-${INSCRIBED_PIECE:-aesel}}" # Write a device config.json. Base identity fields come from the shell vars # (set from the inscription OR the legacy API path); the boot-personalization @@ -485,7 +496,8 @@ write_device_config() { # $1=dest $2=udp(1=include udpMidiBroadcast) if (c.city) cfg.city = c.city; if (Array.isArray(c.colors) && c.colors.length) cfg.colors = c.colors; if (c.mood) cfg.mood = c.mood; - if (c.mono) cfg.mono = true; + if (typeof c.mono === "boolean") cfg.mono = c.mono; + if (["left", "right", "both"].includes(c.monoOutput)) cfg.monoOutput = c.monoOutput; } catch (e) { /* no inscription (anon/legacy) — base fields only */ } fs.writeFileSync(dest, JSON.stringify(cfg) + "\n"); ' "$1" "${USER_HANDLE}" "${BOOT_PIECE}" "${USER_SUB}" "${USER_EMAIL}" "$2" "${INSCRIPTION_FILE}" diff --git a/fedac/native/scripts/flash-usbs.sh b/fedac/native/scripts/flash-usbs.sh new file mode 100755 index 0000000000..9d2f8616ac --- /dev/null +++ b/fedac/native/scripts/flash-usbs.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# flash-usbs.sh — flash every attached external physical disk with the cached +# AC OS release in /tmp/ac-os-pull. Dry run by default: lists the disks it +# would WIPE. Pass --go to flash them all, one after another. +# Disks over 256 GB are skipped unless --big is also passed (backup-drive guard). +set -uo pipefail +SRC=/tmp/ac-os-pull +FLASH=/Users/jas/aesthetic-computer/fedac/native/scripts/flash-mac.sh +LOG="${LOG:-$HOME/.ac-os/flash-six.log}" +mkdir -p "$(dirname "$LOG")" +GO=0; BIG=0 +for a in "$@"; do [ "$a" = "--go" ] && GO=1; [ "$a" = "--big" ] && BIG=1; done +[ -f "$SRC/vmlinuz" ] && [ -f "$SRC/initramfs.cpio.gz" ] || { echo "cache missing in $SRC — run: ac-os pull"; exit 1; } +DISKS=$(diskutil list external physical 2>/dev/null | awk '/^\/dev\/disk/{print $1}') +[ -z "$DISKS" ] && { echo "no external disks attached"; exit 1; } +TARGETS=() +for d in $DISKS; do + info=$(diskutil info "$d") + name=$(echo "$info" | awk -F': *' '/Device \/ Media Name/{print $2}') + bytes=$(echo "$info" | awk -F'[()]' '/Disk Size/{print $2}' | awk '{print $1}') + gb=$(( ${bytes:-0} / 1000000000 )) + removable=$(echo "$info" | awk -F': *' '/Removable Media/{print $2}') + vols=$(diskutil list "$d" | awk 'NR>2 && $NF ~ /disk/ {print $(NF-3)}' | tr '\n' ',' | sed 's/,$//') + flag="" + if [ "$gb" -gt 256 ] && [ "$BIG" -ne 1 ]; then flag=" SKIP (>256 GB, pass --big)"; else TARGETS+=("$d"); fi + printf '%-12s %-28s %5s GB removable=%-3s vols=%s%s\n' "$d" "$name" "$gb" "$removable" "$vols" "$flag" +done +[ "$GO" -eq 1 ] || { echo; echo "dry run — pass --go to ERASE and flash the ${#TARGETS[@]} disk(s) above"; exit 0; } +ok=0; bad=0 +for d in "${TARGETS[@]}"; do + echo; echo "=== $(date '+%H:%M:%S') flashing $d" + if sudo "$FLASH" "$d" "$SRC" >>"$LOG" 2>&1; then + echo "$(date '+%H:%M:%S') DONE $d"; ok=$((ok+1)) + echo "$(date -u +%FT%TZ) DONE $d" >> "$LOG" + else + echo "$(date '+%H:%M:%S') FAILED $d — see $LOG"; bad=$((bad+1)); tail -8 "$LOG" + fi +done +echo; echo "flashed $ok, failed $bad, log: $LOG" +[ "$bad" -eq 0 ]