When the system has disk I/O errors (errno 5), subprocess.run raises
OSError before a process can even start. The run() and shell() helpers
now catch OSError: if check=False the error is logged as a warning and
a returncode=1 CompletedProcess is returned so callers like
install_system_packages can continue; if check=True the error is
re-raised as before.
https://claude.ai/code/session_01Gsfw2QLhQiFVEvZQC5nVcU
- curl and unzip were pipe-chained, but -o writes to a file so the pipe
produced nothing; split into separate steps with &&
- Extracted directory is bootstrap_dev_env-main, not bootstrap
- Script is bootstrap_environment.py, not bootstrap_dev_env
https://claude.ai/code/session_01CkJR1eHoBVMyNNeGdbRBYK
Two issues caused failures on Debian 13 (Trixie) ARM64:
1. apt-get update was never called before installing gnupg, leaving the
package cache stale. The cached version (gnupg2 2.4.7-21+b3) was no
longer available on the ARM64 mirror, producing 404 errors. Adding
apt-get update before the install fixes this.
2. The Docker GPG key and apt repo URL were hardcoded to the Ubuntu
endpoint. Debian has its own Docker repo at
https://download.docker.com/linux/debian. The distro ID is now read
from /etc/os-release and used to select the correct URL.
https://claude.ai/code/session_01CkJR1eHoBVMyNNeGdbRBYK
Pick a hypervisor based on the host's actual nested-virt capability
instead of always using QEMU/HVF (which doesn't expose nested KVM):
* Apple Silicon M3+ on macOS 15 Sequoia+: QEMU/HVF with
-cpu host,el2=on so the Linux guest's KVM (and therefore
firecracker microVMs) actually works.
* Intel Mac: VirtualBox with --nested-hw-virt on. Cask is
installed on demand; the seed ISO and SSH key flow are shared
with the QEMU path.
* Apple Silicon M1/M2 (any macOS), or M3+ pre-Sequoia: skip the
whole VM step and print a notice pointing at a Linux cloud VM
as the only path to firecracker on that hardware.
Backend selection is centralized in _select_vm_backend(); the rest
of setup_firecracker_vm (image download/verify, cloud-init seed,
SSH wait, in-VM firecracker probe, zsh wrapper) is shared.
https://claude.ai/code/session_01R2CnCeEBYT5QoHiwxczDNq
After the normal package install on macOS, fetch the latest Fedora
cloud qcow2 (auto-discovered from dl.fedoraproject.org), verify its
SHA256, build a cloud-init seed ISO with hdiutil, boot it under
QEMU/HVF (UEFI on aarch64, q35 on x86_64), and wait for cloud-init
to install firecracker inside the guest.
Then write a `firecracker()` function block into ~/.zshrc that
starts the backing VM on demand and proxies invocations via SSH.
The host's own firecracker custom-package install is dropped on
macOS since it's Linux-only. Suppress the whole flow with --no-vm.
https://claude.ai/code/session_01R2CnCeEBYT5QoHiwxczDNq
Detect Darwin automatically and, on macOS, install the Xcode Command
Line Tools and Homebrew as the first actions before any package work.
Route system packages through brew (formulae and casks), skip the
Flatpak section entirely, refuse to run as root (brew won't), and
adjust custom-package URL/install-path templates with new {os}, {os_go},
{os_zig}, {os_nvim} substitutions. Architecture detection remains
dynamic on both Apple Silicon and Intel.
https://claude.ai/code/session_01R2CnCeEBYT5QoHiwxczDNq
- Add zsh to SYSTEM_PACKAGES
- Detect RHEL-family vs Debian-family from /etc/os-release ID/ID_LIKE
(falls back to PKG_MGR if os-release is unreadable)
- ensure_zsh_default sets zsh as the invoking user's login shell after
system install: usermod -s on RHEL-family (chsh under default
authselect refuses other-user changes), chsh -s elsewhere; SUDO_USER
is preferred so sudo invocations target the real user
- Add oh-my-zsh to CUSTOM_PACKAGES; the installer runs the official
unattended script (requires zsh + git, which are already installed by
this point) and rewrites ZSH_THEME to "gnzh" in ~/.zshrc
- As a final step, run 'zsh -c "source ~/.zshrc"' to validate the rc
file (the user's interactive shell is unaffected — they need a new
terminal to pick up the new default shell)
pip is unusual among the custom packages because it ships inside CPython
itself, so it doesn't need a URL or archive. The handler bootstraps it
from the standard-library wheel via 'python3 -m ensurepip --upgrade' and
then self-upgrades to the latest version. Detection uses
'python3 -m pip --version' instead of a filesystem path.
- Move SYSTEM_PACKAGES, FLATPAK_PACKAGES, and CUSTOM_PACKAGES into
formatted_packages.py as typed Python data; drop the bespoke parser
- Custom package URLs are now string templates with {version}, {arch},
and {arch_go} substitutions, so the same entry works on x86_64 and
aarch64 without hand-editing the URL
- Add best-effort latest-version resolvers for Go, Firecracker, and Zig
(using go.dev JSON, GitHub releases, and ziglang index.json
respectively); on any failure the pinned version + sha256 are used
and the fallback is logged
- Make Neovim's archive name and install dir arch-aware (x86_64/arm64)
- Document that --no-gui skips the entire Flatpak section, including
installing flatpak itself