#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" # --- Colors --- RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' BOLD='\033[1m' DIM='\033[2m' NC='\033[0m' # --- Config --- KEYSTORE_DIR="keystore" KEYSTORE_FILE="$KEYSTORE_DIR/radio247-release.jks" KEY_ALIAS="radio247" DIST_DIR="dist" GRADLE_BUILD_FILE="app/build.gradle.kts" banner() { echo "" echo -e "${CYAN}╔══════════════════════════════════╗${NC}" echo -e "${CYAN}║${BOLD} Radio 247 Build Tool ${NC}${CYAN}║${NC}" echo -e "${CYAN}╚══════════════════════════════════╝${NC}" echo "" } info() { echo -e "${BLUE}▸${NC} $1"; } success() { echo -e "${GREEN}✓${NC} $1"; } warn() { echo -e "${YELLOW}⚠${NC} $1"; } error() { echo -e "${RED}✗${NC} $1"; } get_version_name() { if [[ ! -f "$GRADLE_BUILD_FILE" ]]; then error "Build file not found: $GRADLE_BUILD_FILE" exit 1 fi local v v=$(grep 'versionName' "$GRADLE_BUILD_FILE" | head -1 | sed 's/.*"\([^"]*\)".*/\1/') if [[ -z "$v" ]]; then error "Could not extract version from $GRADLE_BUILD_FILE" exit 1 fi echo "$v" } preflight_check() { local ok=true if ! command -v java &>/dev/null && [[ -z "${JAVA_HOME:-}" ]]; then error "Java not found. Install JDK 17+ or set JAVA_HOME." ok=false fi if ! command -v keytool &>/dev/null; then error "keytool not found. It ships with the JDK — check your JAVA_HOME/PATH." ok=false fi if [[ ! -x "./gradlew" ]]; then error "Gradle wrapper not found or not executable." ok=false fi if [[ "$ok" != true ]]; then exit 1 fi } ensure_dist_dir() { mkdir -p "$DIST_DIR" } main_menu() { echo -e "${BOLD}What would you like to do?${NC}" echo "" echo -e " ${BOLD}1)${NC} Build Release APK ${DIM}(signed, shareable)${NC}" echo -e " ${BOLD}2)${NC} Build Debug APK ${DIM}(quick, for testing)${NC}" echo -e " ${BOLD}3)${NC} Manage Keystore ${DIM}(create or check signing key)${NC}" echo -e " ${BOLD}4)${NC} Clean ${DIM}(remove build artifacts)${NC}" echo -e " ${BOLD}5)${NC} Exit" echo "" local choice read -rp "> " choice case $choice in 1) build_release ;; 2) build_debug ;; 3) manage_keystore ;; 4) clean_build ;; 5) echo ""; exit 0 ;; *) warn "Invalid choice. Pick 1-5." ;; esac } read_keystore_password() { local password read -rsp "Keystore password: " password echo >&2 echo "$password" } create_keystore() { echo -e "${BOLD}No keystore found. Let's create one.${NC}" echo "" local cn org password password_confirm read -rp "Your name (for the certificate): " cn [[ -z "$cn" ]] && { error "Name is required."; return 1; } read -rp "Organization (optional, Enter to skip): " org while true; do read -rsp "Password (min 6 chars): " password echo if [[ ${#password} -lt 6 ]]; then warn "Password must be at least 6 characters." continue fi read -rsp "Confirm password: " password_confirm echo if [[ "$password" != "$password_confirm" ]]; then warn "Passwords don't match. Try again." continue fi break done mkdir -p "$KEYSTORE_DIR" local dname="CN=$cn" [[ -n "$org" ]] && dname="$dname, O=$org" info "Creating keystore..." local keytool_err keytool_err=$(mktemp) keytool -genkeypair \ -alias "$KEY_ALIAS" \ -keyalg RSA \ -keysize 2048 \ -validity 10000 \ -keystore "$KEYSTORE_FILE" \ -storepass "$password" \ -keypass "$password" \ -dname "$dname" \ 2>"$keytool_err" || true if [[ -f "$KEYSTORE_FILE" ]]; then rm -f "$keytool_err" echo "" success "Keystore created at ${BOLD}$KEYSTORE_FILE${NC}" echo "" warn "IMPORTANT: Back up this file and remember your password." warn "If you lose either, you cannot update the app on devices" warn "that already have this version installed." else error "Keystore creation failed." if [[ -s "$keytool_err" ]]; then echo -e "${DIM}$(cat "$keytool_err")${NC}" >&2 fi rm -f "$keytool_err" return 1 fi } manage_keystore() { if [[ -f "$KEYSTORE_FILE" ]]; then success "Keystore exists at ${BOLD}$KEYSTORE_FILE${NC}" echo "" info "Keystore details:" keytool -list -keystore "$KEYSTORE_FILE" -alias "$KEY_ALIAS" \ -storepass "$(read_keystore_password)" 2>/dev/null \ || warn "Could not read keystore (wrong password?)" return fi create_keystore } copy_apk() { local build_type="$1" local version version="$(get_version_name)" local source_apk="app/build/outputs/apk/$build_type/app-$build_type.apk" if [[ ! -f "$source_apk" ]]; then error "APK not found at $source_apk" return 1 fi ensure_dist_dir local dest_name="radio247-v${version}-${build_type}.apk" local dest_path="$DIST_DIR/$dest_name" cp "$source_apk" "$dest_path" local size size="$(du -h "$dest_path" | cut -f1 | xargs)" echo "" success "Build complete!" echo "" echo -e " ${BOLD}APK:${NC} $dest_path" echo -e " ${BOLD}Size:${NC} $size" echo "" echo -e " ${DIM}Share this file with anyone — they can install it${NC}" echo -e " ${DIM}on Android 9+ by opening the APK on their device.${NC}" } build_release() { info "Preparing release build..." echo "" if [[ ! -f "$KEYSTORE_FILE" ]]; then warn "No keystore found. You need one to sign a release APK." echo "" local yn read -rp "Create one now? (y/n) " yn case $yn in [Yy]*) create_keystore || return 1 ;; *) return 0 ;; esac echo "" fi local password read -rsp "Keystore password: " password echo "" echo "" info "Building release APK... (this may take a minute)" echo "" local store_file store_file="$(cd "$(dirname "$KEYSTORE_FILE")" && pwd)/$(basename "$KEYSTORE_FILE")" if ./gradlew assembleRelease \ -Pandroid.injected.signing.store.file="$store_file" \ -Pandroid.injected.signing.store.password="$password" \ -Pandroid.injected.signing.key.alias="$KEY_ALIAS" \ -Pandroid.injected.signing.key.password="$password"; then copy_apk "release" else echo "" error "Build failed. Check the output above for details." return 1 fi } build_debug() { info "Building debug APK..." echo "" if ./gradlew assembleDebug; then copy_apk "debug" else echo "" error "Build failed. Check the output above for details." return 1 fi } clean_build() { info "Cleaning build artifacts..." ./gradlew clean 2>/dev/null if [[ -d "$DIST_DIR" ]]; then rm -rf "$DIST_DIR" success "Removed $DIST_DIR/" fi success "Clean complete." } # --- Main --- preflight_check banner while true; do main_menu echo "" done