|
| 1 | +#!/usr/bin/env bash |
| 2 | +# |
| 3 | +# Download the latest Ubuntu live-server installer ISO into iso/. |
| 4 | +# |
| 5 | +# Pulls SHA256SUMS from releases.ubuntu.com first, derives the real |
| 6 | +# filename from it (e.g. ubuntu-24.04.4-live-server-amd64.iso), and |
| 7 | +# only downloads if the file isn't already present and valid. Keeps a |
| 8 | +# stable `latest-ubuntu-<edition>-<arch>.iso` symlink in iso/ pointing |
| 9 | +# at the newest snapshot. |
| 10 | + |
| 11 | +set -euo pipefail |
| 12 | + |
| 13 | +RELEASE="26.04" # 24.04, 22.04, 26.04, etc. |
| 14 | +EDITION="live-server" # live-server | desktop |
| 15 | +ARCH="amd64" |
| 16 | +DEST_DIR="iso" |
| 17 | + |
| 18 | +while (($#)); do |
| 19 | + case "$1" in |
| 20 | + --release) RELEASE="${2:?missing value}"; shift 2 ;; |
| 21 | + --edition) EDITION="${2:?missing value}"; shift 2 ;; |
| 22 | + --arch) ARCH="${2:?missing value}"; shift 2 ;; |
| 23 | + --dest) DEST_DIR="${2:?missing value}"; shift 2 ;; |
| 24 | + -h|--help) |
| 25 | + awk '/^#!/ {next} /^[^#]/ {exit} {sub(/^# ?/, ""); print}' "$0" |
| 26 | + exit 0 ;; |
| 27 | + *) echo "[-] unknown arg: $1" >&2; exit 1 ;; |
| 28 | + esac |
| 29 | +done |
| 30 | + |
| 31 | +BASE="https://releases.ubuntu.com/${RELEASE}" |
| 32 | +LATEST_NAME="latest-ubuntu-${EDITION}-${ARCH}.iso" |
| 33 | + |
| 34 | +mkdir -p "$DEST_DIR" |
| 35 | + |
| 36 | +# Fetch SHA256SUMS, which lists every artifact for the release. Lines |
| 37 | +# look like: <hash> *ubuntu-24.04.4-live-server-amd64.iso |
| 38 | +echo "[+] Fetching ${BASE}/SHA256SUMS" |
| 39 | +tmp_sums="$(mktemp)" |
| 40 | +trap 'rm -f "$tmp_sums"' EXIT |
| 41 | +curl --fail --location --silent --show-error --output "$tmp_sums" \ |
| 42 | + "${BASE}/SHA256SUMS" \ |
| 43 | + || { echo "[-] SHA256SUMS fetch failed (is release '${RELEASE}' published?)" >&2; exit 2; } |
| 44 | + |
| 45 | +# Pick the line matching our edition + arch. The pattern intentionally |
| 46 | +# anchors on '-${EDITION}-${ARCH}.iso' to avoid accidentally matching |
| 47 | +# e.g. zsync or manifest files. |
| 48 | +match_line="$(grep -E "[ *]ubuntu-[^ ]*-${EDITION}-${ARCH}\.iso\$" "$tmp_sums" | head -n1 || true)" |
| 49 | +if [[ -z "$match_line" ]]; then |
| 50 | + echo "[-] no ${EDITION}-${ARCH} ISO listed in SHA256SUMS:" >&2 |
| 51 | + cat "$tmp_sums" >&2 |
| 52 | + exit 2 |
| 53 | +fi |
| 54 | + |
| 55 | +# Field 2 is the filename, possibly prefixed by '*' (binary mode marker |
| 56 | +# from sha256sum -b). Strip it. |
| 57 | +real_name="$(awk '{print $2}' <<<"$match_line")" |
| 58 | +real_name="${real_name#\*}" |
| 59 | + |
| 60 | +iso="${DEST_DIR}/${real_name}" |
| 61 | +sum="${DEST_DIR}/${real_name}.sha256" |
| 62 | + |
| 63 | +# Build a single-line sha256 sidecar in the format `sha256sum -c` |
| 64 | +# expects, so make-usb.sh can verify it the same way it does for the |
| 65 | +# NixOS path. |
| 66 | +expected_hash="$(awk '{print $1}' <<<"$match_line")" |
| 67 | +printf '%s %s\n' "$expected_hash" "$real_name" > "${sum}.tmp" |
| 68 | + |
| 69 | +if [[ -f "$iso" ]]; then |
| 70 | + mv "${sum}.tmp" "$sum" |
| 71 | + if ( cd "$DEST_DIR" && sha256sum --status -c "${real_name}.sha256" ); then |
| 72 | + echo "[+] ${iso} already present and verified." |
| 73 | + ln -sfn "${real_name}" "${DEST_DIR}/${LATEST_NAME}" |
| 74 | + rm -f "$tmp_sums" |
| 75 | + trap - EXIT |
| 76 | + exit 0 |
| 77 | + fi |
| 78 | + echo "[!] ${iso} present but sha256 mismatch. Re-downloading from scratch." |
| 79 | + rm -f "$iso" |
| 80 | +else |
| 81 | + mv "${sum}.tmp" "$sum" |
| 82 | +fi |
| 83 | + |
| 84 | +rm -f "$tmp_sums" |
| 85 | +trap - EXIT |
| 86 | + |
| 87 | +echo "[+] Downloading ${BASE}/${real_name}" |
| 88 | +echo " -> ${iso}" |
| 89 | +curl --fail --location --progress-bar --continue-at - \ |
| 90 | + --output "$iso" "${BASE}/${real_name}" \ |
| 91 | + || { echo "[-] download failed" >&2; exit 2; } |
| 92 | + |
| 93 | +echo "[+] Verifying sha256..." |
| 94 | +( cd "$DEST_DIR" && sha256sum -c "${real_name}.sha256" ) \ |
| 95 | + || { echo "[-] sha256 verification failed" >&2; exit 3; } |
| 96 | + |
| 97 | +ln -sfn "${real_name}" "${DEST_DIR}/${LATEST_NAME}" |
| 98 | +echo "[+] ${iso}" |
| 99 | +echo "[+] symlink: ${DEST_DIR}/${LATEST_NAME} -> ${real_name}" |
0 commit comments