cfw/jb: Add dynamic deb download + install to machine setup

Fetch debs listed in debs.list into a cached debs/ dir (skipping already
-cached files, honoring manually-added ones), stage the whole cache into
the per-boot preboot dir alongside Sileo, and install on first boot.

- fetch_debs.sh: manifest fetch, atomic download, non-fatal on failure,
  chowns cache back to invoking user under sudo
- cfw_install_jb.sh / cfw_install_exp.sh: fetch + stage into $BOOT_HASH/debs
- vphone_jb_setup.sh (5b): idempotent install — skip packages already at
  >= the staged version, install the rest in one dpkg -i so inter-package
  deps resolve in a single pass
- debs.list tracked manifest; debs/ cache gitignored

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
zqxwce
2026-07-06 21:41:55 +03:00
committed by zqxwce
co-authored by Claude Opus 4.8
parent fcc30e1657
commit 33072cf954
6 changed files with 174 additions and 0 deletions
+1
View File
@@ -330,3 +330,4 @@ setup_logs/
/research/xnu
/vm
/vm.backups
/debs/
+17
View File
@@ -0,0 +1,17 @@
# debs.list — URLs of .deb packages to download during machine setup (JB/EXP).
#
# One URL per line. Blank lines and lines starting with '#' are ignored.
#
# Downloads are cached in ./debs/ (keyed by filename); a file already present
# there is not re-downloaded. You may also drop .deb files into ./debs/
# manually — they are installed on first boot alongside the downloaded ones.
#
# A failed download prints an error and is skipped; it never aborts setup.
# Packages are installed with `dpkg -i` on first boot (same as Sileo); this
# does not resolve dependencies.
#
# Example:
# https://havoc.app/get/com.example.pkg/com.example.pkg_1.0_iphoneos-arm64.deb
https://apt.procurs.us/pool/main/iphoneos-arm64-rootless/3000/openssh/openssh-client_9.7p1-1_iphoneos-arm64.deb
https://apt.procurs.us/pool/main/iphoneos-arm64-rootless/3000/openssh/openssh-sftp-server_9.7p1-1_iphoneos-arm64.deb
https://apt.procurs.us/pool/main/iphoneos-arm64-rootless/3000/openssh/openssh-server_9.7p1-1_iphoneos-arm64.deb
+18
View File
@@ -473,6 +473,24 @@ if [[ -f "$SILEO_DEB" ]]; then
cp -R "$SILEO_DEB" "$MNT5/$BOOT_HASH/org.coolstar.sileo_2.5.1_iphoneos-arm64.deb"
fi
# ── Extra debs: download from manifest, then stage the whole cache ──────
echo " Fetching extra debs..."
zsh "$SCRIPT_DIR/fetch_debs.sh" || true
DEBS_CACHE="${SCRIPT_DIR:h}/debs"
DEBS_DEST="$MNT5/$BOOT_HASH/debs"
/bin/rm -rf "$DEBS_DEST"
deb_count=0
for deb in "$DEBS_CACHE"/*.deb(N); do
(( deb_count == 0 )) && /bin/mkdir -p "$DEBS_DEST"
cp -R "$deb" "$DEBS_DEST/"
deb_count=$((deb_count + 1))
done
if (( deb_count > 0 )); then
echo " [+] Staged $deb_count extra deb(s) for first-boot install"
else
echo " [=] No extra debs to stage"
fi
JB_DIR_NAME="jb-vphone"
/bin/rm -rf $MNT5/$BOOT_HASH/jb
/bin/rm -rf $MNT5/$BOOT_HASH/$JB_DIR_NAME
+18
View File
@@ -270,6 +270,24 @@ if [[ -f "$SILEO_DEB" ]]; then
cp -R "$SILEO_DEB" "$MNT5/$BOOT_HASH/org.coolstar.sileo_2.5.1_iphoneos-arm64.deb"
fi
# ── Extra debs: download from manifest, then stage the whole cache ──────
echo " Fetching extra debs..."
zsh "$SCRIPT_DIR/fetch_debs.sh" || true
DEBS_CACHE="${SCRIPT_DIR:h}/debs"
DEBS_DEST="$MNT5/$BOOT_HASH/debs"
/bin/rm -rf "$DEBS_DEST"
deb_count=0
for deb in "$DEBS_CACHE"/*.deb(N); do
(( deb_count == 0 )) && /bin/mkdir -p "$DEBS_DEST"
cp -R "$deb" "$DEBS_DEST/"
deb_count=$((deb_count + 1))
done
if (( deb_count > 0 )); then
echo " [+] Staged $deb_count extra deb(s) for first-boot install"
else
echo " [=] No extra debs to stage"
fi
JB_DIR_NAME="jb-vphone"
/bin/rm -rf $MNT5/$BOOT_HASH/jb
/bin/rm -rf $MNT5/$BOOT_HASH/$JB_DIR_NAME
+82
View File
@@ -0,0 +1,82 @@
#!/bin/zsh
# fetch_debs.sh — Download debs from a URL manifest into a cache dir.
# Skips files already cached; leaves manually-added debs alone. A failed
# download is reported and skipped, never fatal (always exits 0).
#
# Usage: fetch_debs.sh [cache_dir] [manifest_file]
# defaults: <repo>/debs and <repo>/debs.list
set -uo pipefail
[[ -n "${_VPHONE_PATH:-}" ]] && export PATH="$_VPHONE_PATH"
SCRIPT_DIR="${0:a:h}"
REPO_ROOT="${SCRIPT_DIR:h}"
CACHE_DIR="${1:-$REPO_ROOT/debs}"
MANIFEST="${2:-$REPO_ROOT/debs.list}"
mkdir -p "$CACHE_DIR"
deb_filename_from_url() {
local url="$1"
url="${url%%\#*}"
url="${url%%\?*}"
print -r -- "${url##*/}"
}
downloaded=0
cached=0
failed=0
if [[ -f "$MANIFEST" ]]; then
while IFS= read -r line || [[ -n "$line" ]]; do
line="${line#"${line%%[![:space:]]*}"}"
line="${line%"${line##*[![:space:]]}"}"
[[ -z "$line" || "$line" == \#* ]] && continue
name="$(deb_filename_from_url "$line")"
if [[ "$name" != *.deb ]]; then
echo " [!] ERROR: URL does not resolve to a .deb filename, skipping: $line" >&2
failed=$((failed + 1))
continue
fi
dest="$CACHE_DIR/$name"
if [[ -s "$dest" ]]; then
echo " [=] Cached: $name"
cached=$((cached + 1))
continue
fi
echo " [>] Downloading: $line"
tmp="$dest.download"
if curl -fL --retry 2 --connect-timeout 20 \
--speed-limit 1024 --speed-time 30 -o "$tmp" "$line"; then
mv -f "$tmp" "$dest"
echo " [+] Downloaded: $name"
downloaded=$((downloaded + 1))
else
rc=$?
rm -f "$tmp"
echo " [!] ERROR: download failed (curl exit $rc), skipping: $line" >&2
failed=$((failed + 1))
fi
done < "$MANIFEST"
else
echo " [=] No manifest at $MANIFEST (skipping downloads)"
fi
total=0
for f in "$CACHE_DIR"/*.deb(N); do
total=$((total + 1))
done
echo " [i] debs: $total in cache ($downloaded downloaded, $cached already cached, $failed failed)"
# Return the cache to the invoking user when run under sudo.
if [[ "$(id -u)" == "0" && -n "${SUDO_USER:-}" ]]; then
chown -R "$SUDO_USER" "$CACHE_DIR" 2>/dev/null || true
fi
exit 0
+38
View File
@@ -171,6 +171,44 @@ else
fi
fi
# ═══════════ 5b/8 INSTALL EXTRA DEBS ═════════════════════════
log "[5b/8] Installing extra debs..."
DEBS_DIR="/private/preboot/$BOOT_HASH/debs"
if [ -d "$DEBS_DIR" ]; then
to_install=()
for deb in "$DEBS_DIR"/*.deb; do
[ -f "$deb" ] || continue
name="$(basename "$deb")"
pkg="$(dpkg-deb -f "$deb" Package 2>/dev/null)"
ver="$(dpkg-deb -f "$deb" Version 2>/dev/null)"
if [ -z "$pkg" ]; then
log " WARNING: cannot read Package field from $name, will install anyway"
to_install+=("$deb")
continue
fi
cur="$(dpkg-query -W -f='${Version}' "$pkg" 2>/dev/null)"
if [ -n "$cur" ] && dpkg --compare-versions "$cur" ge "$ver" 2>/dev/null; then
log " $pkg $cur already installed (>= $ver), skipping"
else
to_install+=("$deb")
fi
done
if [ "${#to_install[@]}" -gt 0 ]; then
names="$(for d in "${to_install[@]}"; do basename "$d"; done | tr '\n' ' ')"
log " Installing ${#to_install[@]} deb(s): $names"
if dpkg -i "${to_install[@]}"; then
log " Extra debs installed"
else
rc=$?
log " WARNING: dpkg -i exited with $rc (unmet external deps are non-fatal)"
fi
else
log " All extra debs already installed, nothing to do"
fi
else
log " No extra debs staged"
fi
uicache -a 2>/dev/null || true
log " uicache refreshed"