diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..d786536 --- /dev/null +++ b/install.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash +set -euo pipefail + +BASE_URL="${BASE_URL:-https://git.jewguard.xyz/linusware/main/raw/branch/main}" +INSTALL_DIR="${HOME}/linusware" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +info() { + echo -e "${GREEN}[*]${NC} $1" +} +warn() { + echo -e "${YELLOW}[!]${NC} $1" +} +error() { + echo -e "${RED}[x]${NC} $1" + exit 1 +} + +command -v curl >/dev/null 2>&1 || error "curl is required" +command -v md5sum >/dev/null 2>&1 || error "md5sum is required" + +mkdir -p "${INSTALL_DIR}/external" "${INSTALL_DIR}/internal" + +download_bin() { + local url="$1" dest="$2" + local tmp; tmp="$(mktemp)" + curl -fsSL -o "$tmp" "$url" || { rm -f "$tmp"; return 1; } + mv "$tmp" "$dest" +} + +install_edition() { + local edition="$1" + local status_url="${BASE_URL}/${edition}/status.json" + local status_file="${INSTALL_DIR}/${edition}/status.json" + local bin_dir="${INSTALL_DIR}/${edition}/linusware" + local bin_path="${bin_dir}/linusware" + + info "Checking ${edition} edition..." + + download_bin "$status_url" "$status_file" || { + warn "Could not fetch status.json for ${edition}, skipping" + return + } + + local updated + updated=$(sed -n 's/.*"updated":[[:space:]]*\([a-z]*\).*/\1/p' "$status_file") + if [ "$updated" != "true" ]; then + info "${edition}: not yet released, skipping" + return + fi + + local version + version=$(sed -n 's/.*"version":[[:space:]]*"\([^"]*\)".*/\1/p' "$status_file") + info "Downloading ${edition} v${version}..." + + local md5_url="${BASE_URL}/${edition}/bin/MD5SUM" + local md5_file; md5_file="$(mktemp)" + download_bin "$md5_url" "$md5_file" || { + rm -f "$md5_file" + warn "Could not fetch MD5SUM for ${edition}" + return + } + local expected_hash; expected_hash=$(tr -cd '[:xdigit:]' < "$md5_file"); rm -f "$md5_file" + + local bin_url="${BASE_URL}/${edition}/bin/linusware" + local tmp_bin; tmp_bin="$(mktemp)" + download_bin "$bin_url" "$tmp_bin" || { + rm -f "$tmp_bin" + error "Failed to download ${edition} binary" + } + + local actual_hash + actual_hash=$(md5sum "$tmp_bin" | cut -d' ' -f1) + if [ "$actual_hash" != "$expected_hash" ]; then + rm -f "$tmp_bin" + error "MD5 mismatch for ${edition} (expected ${expected_hash}, got ${actual_hash})" + fi + + mkdir -p "$bin_dir" + mv "$tmp_bin" "$bin_path" + chmod +x "$bin_path" + echo "$version" > "${INSTALL_DIR}/${edition}/version.txt" + + info "${edition} v${version} installed successfully" +} + +# main + +info "Installing LinusWare to ${INSTALL_DIR}" +echo "" + +install_edition external +install_edition internal + +info "Downloading launcher and updater..." +download_bin "${BASE_URL}/launcher.sh" "${INSTALL_DIR}/launcher.sh" +download_bin "${BASE_URL}/update.sh" "${INSTALL_DIR}/update.sh" +chmod +x "${INSTALL_DIR}/launcher.sh" "${INSTALL_DIR}/update.sh" + +echo "" +info "Installation complete!" +echo "" +echo " Run: ${INSTALL_DIR}/launcher.sh" diff --git a/launcher.sh b/launcher.sh new file mode 100755 index 0000000..7eb04d9 --- /dev/null +++ b/launcher.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash + +INSTALL_DIR="${HOME}/linusware" +RED='\033[0;31m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' + +# discover bins +declare -a editions edition_names +for dir in "${INSTALL_DIR}"/*/; do + [ -d "$dir" ] || continue + edition=$(basename "$dir") + bin="${dir}/linusware/linusware" + if [ -f "$bin" ] && [ -x "$bin" ]; then + edition_names+=("$edition") + editions+=("$bin") + fi +done + +if [ ${#editions[@]} -eq 0 ]; then + echo -e "${YELLOW}No LinusWare editions installed.${NC}" + echo "Run the installer first:" + echo " curl -fsSL | bash" + exit 1 +fi + +get_version() { + local f="${INSTALL_DIR}/$1/version.txt" + [ -f "$f" ] && cat "$f" || echo "?" +} + +cap() { + local s="$1" + echo "$(echo "${s:0:1}" | tr '[:lower:]' '[:upper:]')${s:1}" +} + +run_updater() { + [ -x "${INSTALL_DIR}/update.sh" ] && "${INSTALL_DIR}/update.sh" + echo "" + read -rp "Press Enter to return to menu..." _ +} + +# whiptail TUI +if command -v whiptail >/dev/null 2>&1; then + while true; do + menu_items=() + for i in "${!edition_names[@]}"; do + v=$(get_version "${edition_names[$i]}") + menu_items+=("$((i+1))" "$(cap "${edition_names[$i]}") v${v}") + done + menu_items+=("u" "Check for Updates") + menu_items+=("q" "Quit") + + choice=$(whiptail --title "LinusWare Launcher" \ + --menu "Select an edition to launch:" \ + 15 55 5 "${menu_items[@]}" \ + 3>&1 1>&2 2>&3) || choice="Q" + + case "$choice" in + u) run_updater;; + q|"") exit 0;; + *) [[ "$choice" =~ ^[0-9]+$ ]] && exec sudo "${editions[$((choice-1))]}" ;; + esac + done +fi + +# fallback TUI +while true; do + echo "" + echo -e "${CYAN}------ LinusWare Launcher ------${NC}" + for i in "${!edition_names[@]}"; do + v=$(get_version "${edition_names[$i]}") + echo " $((i+1))) $(cap "${edition_names[$i]}") (v${v})" + done + echo " u) Check for Updates" + echo " q) Quit" + echo "" + read -rp "Select: " choice + + case "$choice" in + [qQ]) exit 0;; + [uU]) run_updater;; + *) + if [[ "$choice" =~ ^[0-9]+$ ]] && \ + [ "$choice" -ge 1 ] && \ + [ "$choice" -le "${#editions[@]}" ]; then + exec sudo "${editions[$((choice-1))]}" + else + echo -e "${RED}Invalid choice${NC}" + fi + ;; + esac +done diff --git a/update.sh b/update.sh new file mode 100755 index 0000000..0cfb44a --- /dev/null +++ b/update.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash +set -euo pipefail + +BASE_URL="${BASE_URL:-https://git.jewguard.xyz/linusware/main/raw/branch/main}" +INSTALL_DIR="${HOME}/linusware" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +info() { + echo -e "${GREEN}[*]${NC} $1" +} +warn() { + echo -e "${YELLOW}[!]${NC} $1" +} +error() { + echo -e "${RED}[x]${NC} $1" +} + +command -v curl >/dev/null 2>&1 || { error "curl is required"; exit 1; } +command -v md5sum >/dev/null 2>&1 || { error "md5sum is required"; exit 1; } + +download() { + local url="$1" dest="$2" + local tmp; tmp="$(mktemp)" + curl -fsSL -o "$tmp" "$url" || { rm -f "$tmp"; return 1; } + mv "$tmp" "$dest" +} + +update_edition() { + local edition="$1" + local bin_path="${INSTALL_DIR}/${edition}/linusware/linusware" + local ver_file="${INSTALL_DIR}/${edition}/version.txt" + + [ -f "$bin_path" ] || return + + local cur_ver="" + [ -f "$ver_file" ] && cur_ver=$(cat "$ver_file") + + local tmp_status; tmp_status="$(mktemp)" + if ! download "${BASE_URL}/${edition}/status.json" "$tmp_status"; then + rm -f "$tmp_status" + warn "${edition}: could not fetch remote status" + return + fi + + local remote_ver updated + remote_ver=$(sed -n 's/.*"version":[[:space:]]*"\([^"]*\)".*/\1/p' "$tmp_status") + updated=$(sed -n 's/.*"updated":[[:space:]]*\([a-z]*\).*/\1/p' "$tmp_status") + rm -f "$tmp_status" + + if [ "$updated" != "true" ]; then + info "${edition}: not available (not yet released)" + return + fi + + if [ "$remote_ver" = "$cur_ver" ]; then + info "${edition}: already up-to-date (v${cur_ver})" + return + fi + + info "${edition}: ${cur_ver:-none} -> v${remote_ver}" + + local md5_file; md5_file="$(mktemp)" + if ! download "${BASE_URL}/${edition}/bin/MD5SUM" "$md5_file"; then + rm -f "$md5_file" + warn "${edition}: could not fetch MD5SUM" + return + fi + local expected_hash; expected_hash=$(tr -cd '[:xdigit:]' < "$md5_file"); rm -f "$md5_file" + + local tmp_bin; tmp_bin="$(mktemp)" + if ! download "${BASE_URL}/${edition}/bin/linusware" "$tmp_bin"; then + rm -f "$tmp_bin" + warn "${edition}: download failed" + return + fi + + local actual_hash + actual_hash=$(md5sum "$tmp_bin" | cut -d' ' -f1) + if [ "$actual_hash" != "$expected_hash" ]; then + rm -f "$tmp_bin" + error "${edition}: MD5 mismatch" + return + fi + + mv "$tmp_bin" "$bin_path" + chmod +x "$bin_path" + echo "$remote_ver" > "$ver_file" + info "${edition}: updated to v${remote_ver}" +} + +# main +echo -e "${GREEN}------ LinusWare Updater ------${NC}" +echo "" + +for dir in "${INSTALL_DIR}"/*/; do + [ -d "$dir" ] || continue + update_edition "$(basename "$dir")" +done + +echo "" +info "Done."