#!/usr/bin/env bash set -euo pipefail ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) LAB2_ASSETS_DIR="$ROOT_DIR/assets/lab2" BASE_REPO_URL="https://huggingface.co/WhiteRabbitNeo/WhiteRabbitNeo-V3-7B" BASE_REPO_REF="main" BASE_REPO_DIR="$LAB2_ASSETS_DIR/WhiteRabbitNeo-V3-7B" GGUF_DIR="$LAB2_ASSETS_DIR/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF" REFRESH_DOWNLOADS=0 GGUF_URLS=( "https://huggingface.co/bartowski/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF/resolve/main/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-Q4_K_M.gguf?download=true" "https://huggingface.co/bartowski/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF/resolve/main/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-Q8_0.gguf?download=true" "https://huggingface.co/bartowski/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF/resolve/main/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-IQ2_M.gguf?download=true" ) CLONED_ITEMS=() REFRESHED_ITEMS=() DOWNLOADED_ITEMS=() SKIPPED_ITEMS=() FAILED_ITEMS=() usage() { cat <<'EOF' Usage: ./labctl assets lab2 [--refresh] Populate repo-local lab 2 assets from Hugging Face without touching `up` or `preflight`. Actions: - Clone or refresh WhiteRabbitNeo-V3-7B into assets/lab2/WhiteRabbitNeo-V3-7B - Download the supported GGUF files into assets/lab2/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF Options: --refresh Re-download GGUF files even if matching files already exist -h, --help Show this help text EOF } record_failure() { FAILED_ITEMS+=("$1") } record_skip() { SKIPPED_ITEMS+=("$1") } record_clone() { CLONED_ITEMS+=("$1") } record_refresh() { REFRESHED_ITEMS+=("$1") } record_download() { DOWNLOADED_ITEMS+=("$1") } print_summary() { echo echo "Lab 2 asset bootstrap summary:" if [ "${#CLONED_ITEMS[@]}" -gt 0 ]; then echo " Cloned:" printf ' - %s\n' "${CLONED_ITEMS[@]}" fi if [ "${#REFRESHED_ITEMS[@]}" -gt 0 ]; then echo " Refreshed:" printf ' - %s\n' "${REFRESHED_ITEMS[@]}" fi if [ "${#DOWNLOADED_ITEMS[@]}" -gt 0 ]; then echo " Downloaded:" printf ' - %s\n' "${DOWNLOADED_ITEMS[@]}" fi if [ "${#SKIPPED_ITEMS[@]}" -gt 0 ]; then echo " Skipped:" printf ' - %s\n' "${SKIPPED_ITEMS[@]}" fi if [ "${#FAILED_ITEMS[@]}" -gt 0 ]; then echo " Failed:" printf ' - %s\n' "${FAILED_ITEMS[@]}" fi } normalize_url() { printf '%s' "${1%/}" } require_cmd() { if ! command -v "$1" >/dev/null 2>&1; then echo "Missing required command: $1" >&2 exit 1 fi } require_git_lfs() { if ! git lfs version >/dev/null 2>&1; then echo "Missing required command: git-lfs" >&2 exit 1 fi } parse_args() { while [ $# -gt 0 ]; do case "$1" in --refresh) REFRESH_DOWNLOADS=1 ;; -h|--help) usage exit 0 ;; *) echo "Unknown option: $1" >&2 usage >&2 exit 1 ;; esac shift done } file_size() { if stat -c '%s' "$1" >/dev/null 2>&1; then stat -c '%s' "$1" else stat -f '%z' "$1" fi } remote_content_length() { curl -fsSI -L "$1" | tr -d '\r' | awk -F': ' 'tolower($1) == "content-length" { print $2 }' | tail -n 1 } ensure_parent_dirs() { mkdir -p "$LAB2_ASSETS_DIR" "$GGUF_DIR" } ensure_expected_repo_checkout() { local current_remote local normalized_current local normalized_expected if [ -e "$BASE_REPO_DIR" ] && [ ! -d "$BASE_REPO_DIR/.git" ]; then echo "Refusing to reuse $BASE_REPO_DIR because it exists but is not a git checkout." >&2 echo "Move it aside or remove it, then rerun ./labctl assets lab2." >&2 return 1 fi if [ ! -d "$BASE_REPO_DIR/.git" ]; then return 0 fi current_remote=$(git -C "$BASE_REPO_DIR" remote get-url origin 2>/dev/null || true) normalized_current=$(normalize_url "$current_remote") normalized_expected=$(normalize_url "$BASE_REPO_URL") if [ -z "$current_remote" ] || [ "$normalized_current" != "$normalized_expected" ]; then echo "Refusing to reuse $BASE_REPO_DIR because its origin remote is unexpected." >&2 echo "Expected: $BASE_REPO_URL" >&2 echo "Found: ${current_remote:-}" >&2 echo "Move it aside or remove it, then rerun ./labctl assets lab2." >&2 return 1 fi if [ -n "$(git -C "$BASE_REPO_DIR" status --porcelain --untracked-files=all)" ]; then echo "Refusing to refresh $BASE_REPO_DIR because it has local changes." >&2 echo "Commit, stash, or remove that checkout first, then rerun ./labctl assets lab2." >&2 return 1 fi return 0 } prepare_base_repo() { if ! ensure_expected_repo_checkout; then record_failure "WhiteRabbitNeo-V3-7B checkout" return 1 fi git lfs install --skip-repo >/dev/null if [ ! -d "$BASE_REPO_DIR/.git" ]; then echo "Cloning WhiteRabbitNeo-V3-7B into $BASE_REPO_DIR" GIT_LFS_SKIP_SMUDGE=1 git clone --depth=1 "$BASE_REPO_URL" "$BASE_REPO_DIR" record_clone "assets/lab2/WhiteRabbitNeo-V3-7B" else echo "Refreshing WhiteRabbitNeo-V3-7B in $BASE_REPO_DIR" git -C "$BASE_REPO_DIR" fetch --depth=1 origin "$BASE_REPO_REF" git -C "$BASE_REPO_DIR" checkout -f --detach FETCH_HEAD record_refresh "assets/lab2/WhiteRabbitNeo-V3-7B" fi git -C "$BASE_REPO_DIR" lfs install --local >/dev/null git -C "$BASE_REPO_DIR" lfs pull origin } download_gguf() { local url=$1 local filename=$2 local destination="$GGUF_DIR/$filename" local partial="$destination.part" local expected_size local actual_size expected_size=$(remote_content_length "$url" || true) if [ -f "$destination" ] && [ "$REFRESH_DOWNLOADS" -eq 0 ]; then if [ -n "$expected_size" ] && [ "$(file_size "$destination")" = "$expected_size" ]; then echo "Skipping $filename; matching file already exists." record_skip "assets/lab2/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF/$filename" return 0 fi if [ -z "$expected_size" ] && [ "$(file_size "$destination")" -gt 0 ]; then echo "Skipping $filename; existing file size is non-zero and no remote size was available." record_skip "assets/lab2/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF/$filename" return 0 fi fi if [ "$REFRESH_DOWNLOADS" -eq 1 ] && [ -f "$destination" ]; then rm -f "$destination" fi echo "Downloading $filename" if ! curl -fL --progress-bar -C - -o "$partial" "$url"; then echo "Failed to download $filename. Partial data, if any, remains at $partial." >&2 record_failure "assets/lab2/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF/$filename" return 1 fi if [ ! -f "$partial" ]; then echo "Download for $filename did not produce an output file." >&2 record_failure "assets/lab2/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF/$filename" return 1 fi if [ -n "$expected_size" ]; then actual_size=$(file_size "$partial") if [ "$actual_size" != "$expected_size" ]; then echo "Downloaded size mismatch for $filename: expected $expected_size bytes, got $actual_size bytes." >&2 record_failure "assets/lab2/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF/$filename" return 1 fi fi mv -f "$partial" "$destination" record_download "assets/lab2/WhiteRabbitNeo_WhiteRabbitNeo-V3-7B-GGUF/$filename" return 0 } download_ggufs() { local url local filename local failures=0 for url in "${GGUF_URLS[@]}"; do filename=${url##*/} filename=${filename%%\?*} if ! download_gguf "$url" "$filename"; then failures=1 fi done return "$failures" } main() { local status=0 parse_args "$@" require_cmd git require_cmd curl require_git_lfs ensure_parent_dirs if ! prepare_base_repo; then status=1 fi if ! download_ggufs; then status=1 fi print_summary if [ "$status" -ne 0 ]; then echo "Lab 2 asset bootstrap did not complete cleanly." >&2 exit "$status" fi echo "Lab 2 repo-local assets are ready." } main "$@"