Files
test/gradle/mac/pack_offline_bundle_airgap_macos.sh
2026-01-22 19:05:45 +09:00

572 lines
19 KiB
Bash
Executable File

#!/bin/bash
# pack_offline_bundle_airgap_macos.sh
# ============================================================================
# Gradle Offline Bundle Packer (macOS)
# ============================================================================
# Version: 4.0
#
# WORKFLOW:
# 1. [ONLINE] Build project (./gradlew bootJar) - downloads all deps
# 2. [ONLINE] Test run (./gradlew bootRun) - verify app works
# 3. [OFFLINE TEST] Verify offline build works
# 4. Create bundle with all cached dependencies
#
# REQUIREMENTS:
# - Internet connection (for initial build)
# - Project with gradlew
# - macOS 10.13+ (High Sierra or later)
# ============================================================================
set -e
# ============================================================================
# Configuration
# ============================================================================
WRAPPER_SEED_PATH="wrapper_jar_seed"
OFFLINE_HOME_NAME="_offline_gradle_home"
BOOTRUN_TIMEOUT_SECONDS=60
# Color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
GRAY='\033[0;90m'
WHITE='\033[1;37m'
NC='\033[0m' # No Color
echo ""
echo -e "${CYAN}============================================================${NC}"
echo -e "${CYAN} Gradle Offline Bundle Packer v4.0 (macOS)${NC}"
echo -e "${CYAN}============================================================${NC}"
echo ""
echo -e "${WHITE} This script will:${NC}"
echo -e "${GRAY} 1. Build project with internet (download dependencies)${NC}"
echo -e "${GRAY} 2. Test run application (verify it works)${NC}"
echo -e "${GRAY} 3. Test offline build (verify cache is complete)${NC}"
echo -e "${GRAY} 4. Create offline bundle for air-gapped environment${NC}"
echo ""
echo -e "${CYAN}============================================================${NC}"
echo ""
# ============================================================================
# [1/20] Check Current Directory
# ============================================================================
echo -e "${YELLOW}==[1/20] Check Current Directory ==${NC}"
ROOT="$(pwd)"
echo "ROOT_DIR: $ROOT"
echo ""
# ============================================================================
# [2/20] Check Required Files
# ============================================================================
echo -e "${YELLOW}==[2/20] Check Required Files ==${NC}"
if [ ! -f "./gradlew" ]; then
echo -e "${RED}ERROR: gradlew not found. Run from project root.${NC}"
exit 1
fi
chmod +x ./gradlew
echo -e "${GREEN}[OK] gradlew${NC}"
BUILD_FILE=""
if [ -f "./build.gradle" ]; then
BUILD_FILE="build.gradle"
elif [ -f "./build.gradle.kts" ]; then
BUILD_FILE="build.gradle.kts"
else
echo -e "${RED}ERROR: build.gradle(.kts) not found.${NC}"
exit 1
fi
echo -e "${GREEN}[OK] $BUILD_FILE${NC}"
SETTINGS_FILE=""
if [ -f "./settings.gradle" ]; then
SETTINGS_FILE="settings.gradle"
echo -e "${GREEN}[OK] $SETTINGS_FILE${NC}"
elif [ -f "./settings.gradle.kts" ]; then
SETTINGS_FILE="settings.gradle.kts"
echo -e "${GREEN}[OK] $SETTINGS_FILE${NC}"
fi
echo ""
# ============================================================================
# [3/20] Check Gradle Wrapper
# ============================================================================
echo -e "${YELLOW}==[3/20] Check Gradle Wrapper ==${NC}"
WRAPPER_DIR="$ROOT/gradle/wrapper"
WRAPPER_JAR="$WRAPPER_DIR/gradle-wrapper.jar"
WRAPPER_PROP="$WRAPPER_DIR/gradle-wrapper.properties"
mkdir -p "$WRAPPER_DIR"
if [ ! -f "$WRAPPER_PROP" ]; then
echo -e "${RED}ERROR: gradle-wrapper.properties not found.${NC}"
exit 1
fi
if [ ! -f "$WRAPPER_JAR" ]; then
SEED_JAR="$ROOT/$WRAPPER_SEED_PATH/gradle-wrapper.jar"
if [ -f "$SEED_JAR" ]; then
cp "$SEED_JAR" "$WRAPPER_JAR"
echo -e "${GREEN}[OK] Wrapper jar injected from seed${NC}"
else
echo -e "${RED}ERROR: gradle-wrapper.jar missing${NC}"
exit 1
fi
else
echo -e "${GREEN}[OK] gradle-wrapper.jar exists${NC}"
fi
# Create seed backup
SEED_DIR="$ROOT/$WRAPPER_SEED_PATH"
if [ ! -d "$SEED_DIR" ]; then
mkdir -p "$SEED_DIR"
cp "$WRAPPER_JAR" "$SEED_DIR/gradle-wrapper.jar"
fi
echo ""
# ============================================================================
# [4/20] Set GRADLE_USER_HOME (Project Local)
# ============================================================================
echo -e "${YELLOW}==[4/20] Set GRADLE_USER_HOME ==${NC}"
OFFLINE_HOME="$ROOT/$OFFLINE_HOME_NAME"
mkdir -p "$OFFLINE_HOME"
export GRADLE_USER_HOME="$OFFLINE_HOME"
echo -e "${CYAN}GRADLE_USER_HOME = $GRADLE_USER_HOME${NC}"
echo -e "${GRAY}[INFO] All dependencies will be cached in project folder${NC}"
echo ""
# ============================================================================
# [5/20] Check Internet Connection
# ============================================================================
echo -e "${YELLOW}==[5/20] Check Internet Connection ==${NC}"
HAS_INTERNET=false
TEST_HOSTS=("plugins.gradle.org" "repo.maven.apache.org" "repo1.maven.org")
for TEST_HOST in "${TEST_HOSTS[@]}"; do
# macOS ping doesn't have -W, use -t instead
if ping -c 1 -t 3 "$TEST_HOST" &>/dev/null; then
HAS_INTERNET=true
echo -e "${GREEN}[OK] Connected to $TEST_HOST${NC}"
break
fi
done
if [ "$HAS_INTERNET" = false ]; then
# Try DNS resolution as fallback
if nslookup google.com &>/dev/null || host google.com &>/dev/null; then
HAS_INTERNET=true
echo -e "${GREEN}[OK] Internet available (DNS)${NC}"
fi
fi
if [ "$HAS_INTERNET" = false ]; then
echo ""
echo -e "${RED}============================================================${NC}"
echo -e "${RED} ERROR: No Internet Connection!${NC}"
echo -e "${RED}============================================================${NC}"
echo ""
echo -e "${YELLOW}This script requires internet for initial build.${NC}"
echo -e "${YELLOW}Please connect to internet and run again.${NC}"
echo ""
exit 1
fi
echo ""
# ============================================================================
# [6/20] Initial Gradle Setup
# ============================================================================
echo -e "${YELLOW}==[6/20] Initial Gradle Setup ==${NC}"
echo -e "${GRAY}[INFO] Downloading Gradle distribution...${NC}"
if ./gradlew --version &>/dev/null; then
GRADLE_VERSION=$(./gradlew --version 2>&1 | grep "^Gradle" | awk '{print $2}')
echo -e "${GREEN}[OK] Gradle $GRADLE_VERSION${NC}"
else
echo -e "${RED}[ERROR] Gradle setup failed${NC}"
exit 1
fi
echo ""
# ============================================================================
# [7/20] ONLINE BUILD - bootJar (Download All Dependencies)
# ============================================================================
echo -e "${YELLOW}==[7/20] ONLINE BUILD - bootJar ==${NC}"
echo ""
echo -e "${CYAN}============================================================${NC}"
echo -e "${CYAN} ONLINE BUILD (with Internet)${NC}"
echo -e "${CYAN} Downloading all dependencies to local cache${NC}"
echo -e "${CYAN}============================================================${NC}"
echo ""
BUILD_SUCCESS=false
./gradlew clean bootJar --no-daemon
if [ $? -eq 0 ]; then
BUILD_SUCCESS=true
echo ""
echo -e "${GREEN}============================================================${NC}"
echo -e "${GREEN} ONLINE BUILD SUCCESS!${NC}"
echo -e "${GREEN}============================================================${NC}"
echo ""
if [ -d "./build/libs" ]; then
echo -e "${CYAN}JAR files:${NC}"
ls -lh ./build/libs/*.jar 2>/dev/null | awk '{print " " $9 " (" $5 ")"}'
fi
else
echo ""
echo -e "${RED}============================================================${NC}"
echo -e "${RED} BUILD FAILED!${NC}"
echo -e "${RED}============================================================${NC}"
echo ""
echo -e "${YELLOW}Build failed. Cannot continue.${NC}"
exit 1
fi
echo ""
# ============================================================================
# [8/20] Stop Daemons
# ============================================================================
echo -e "${YELLOW}==[8/20] Stop Daemons ==${NC}"
./gradlew --stop &>/dev/null || true
sleep 2
echo -e "${GREEN}[OK] Daemons stopped${NC}"
echo ""
# ============================================================================
# [9/20] ONLINE TEST - bootRun (Verify Application Works)
# ============================================================================
echo -e "${YELLOW}==[9/20] ONLINE TEST - bootRun ==${NC}"
echo ""
echo -e "${CYAN}============================================================${NC}"
echo -e "${CYAN} Testing application startup (timeout: ${BOOTRUN_TIMEOUT_SECONDS}s)${NC}"
echo -e "${CYAN} Will automatically stop after successful startup${NC}"
echo -e "${CYAN}============================================================${NC}"
echo ""
BOOTRUN_SUCCESS=false
# macOS uses gtimeout if available, otherwise perl-based timeout
if command -v gtimeout &>/dev/null; then
gtimeout ${BOOTRUN_TIMEOUT_SECONDS}s ./gradlew bootRun --no-daemon &
else
# Fallback: start in background and kill after timeout
./gradlew bootRun --no-daemon &
fi
BOOTRUN_PID=$!
sleep 10
if ps -p $BOOTRUN_PID &>/dev/null; then
BOOTRUN_SUCCESS=true
echo ""
echo -e "${GREEN}[OK] Application started successfully${NC}"
kill $BOOTRUN_PID &>/dev/null || true
sleep 2
else
echo ""
echo -e "${YELLOW}[WARN] Application may not have started properly${NC}"
fi
# Cleanup - macOS process cleanup
pkill -f "gradle.*bootRun" &>/dev/null || true
sleep 2
echo ""
# ============================================================================
# [10/20] Stop Daemons Again
# ============================================================================
echo -e "${YELLOW}==[10/20] Stop Daemons Again ==${NC}"
./gradlew --stop &>/dev/null || true
sleep 2
echo -e "${GREEN}[OK] Daemons stopped${NC}"
echo ""
# ============================================================================
# [11/20] OFFLINE BUILD TEST (Verify Cache Completeness)
# ============================================================================
echo -e "${YELLOW}==[11/20] OFFLINE BUILD TEST ==${NC}"
echo ""
echo -e "${CYAN}============================================================${NC}"
echo -e "${CYAN} OFFLINE BUILD TEST (--offline flag)${NC}"
echo -e "${CYAN} Verifying all dependencies are cached${NC}"
echo -e "${CYAN}============================================================${NC}"
echo ""
OFFLINE_SUCCESS=false
./gradlew clean bootJar --offline --no-daemon
if [ $? -eq 0 ]; then
OFFLINE_SUCCESS=true
echo ""
echo -e "${GREEN}============================================================${NC}"
echo -e "${GREEN} OFFLINE BUILD TEST PASSED!${NC}"
echo -e "${GREEN}============================================================${NC}"
echo ""
echo -e "${GREEN}[OK] All dependencies are cached${NC}"
else
echo ""
echo -e "${RED}============================================================${NC}"
echo -e "${RED} OFFLINE BUILD TEST FAILED!${NC}"
echo -e "${RED}============================================================${NC}"
echo ""
echo -e "${YELLOW}Some dependencies may be missing from cache.${NC}"
echo -e "${YELLOW}The bundle may not work in air-gapped environment.${NC}"
echo ""
read -p "Continue anyway? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
echo ""
# ============================================================================
# [12/20] Stop Daemons Before Archive
# ============================================================================
echo -e "${YELLOW}==[12/20] Stop Daemons Before Archive ==${NC}"
./gradlew --stop &>/dev/null || true
sleep 2
echo -e "${GREEN}[OK] Daemons stopped${NC}"
echo ""
# ============================================================================
# [13/20] Verify settings.gradle for Offline
# ============================================================================
echo -e "${YELLOW}==[13/20] Verify settings.gradle ==${NC}"
if [ -n "$SETTINGS_FILE" ]; then
if grep -q "mavenLocal()" "$SETTINGS_FILE" && grep -q "pluginManagement" "$SETTINGS_FILE"; then
echo -e "${GREEN}[OK] settings.gradle configured for offline${NC}"
else
echo -e "${YELLOW}[WARN] settings.gradle may need offline configuration${NC}"
echo -e "${GRAY}[INFO] Consider adding mavenLocal() to pluginManagement and repositories${NC}"
fi
else
echo -e "${GRAY}[INFO] No settings.gradle found${NC}"
fi
echo ""
# ============================================================================
# [14/20] Create Helper Scripts
# ============================================================================
echo -e "${YELLOW}==[14/20] Create Helper Scripts ==${NC}"
# run_offline_build.sh
cat > "$ROOT/run_offline_build.sh" << 'EOF'
#!/bin/bash
# run_offline_build.sh - Build JAR offline
export GRADLE_USER_HOME="$(pwd)/_offline_gradle_home"
echo "GRADLE_USER_HOME = $GRADLE_USER_HOME"
echo ""
./gradlew --offline bootJar --no-daemon
if [ $? -eq 0 ]; then
echo ""
echo "BUILD SUCCESS!"
echo ""
echo "JAR files:"
ls -lh ./build/libs/*.jar 2>/dev/null | awk '{print " " $9}'
else
echo "BUILD FAILED"
fi
EOF
chmod +x "$ROOT/run_offline_build.sh"
echo -e "${GREEN}[OK] run_offline_build.sh${NC}"
# run_offline_bootrun.sh
cat > "$ROOT/run_offline_bootrun.sh" << 'EOF'
#!/bin/bash
# run_offline_bootrun.sh - Run application offline
export GRADLE_USER_HOME="$(pwd)/_offline_gradle_home"
echo "GRADLE_USER_HOME = $GRADLE_USER_HOME"
echo ""
echo "Starting application (Ctrl+C to stop)..."
echo ""
./gradlew --offline bootRun --no-daemon
EOF
chmod +x "$ROOT/run_offline_bootrun.sh"
echo -e "${GREEN}[OK] run_offline_bootrun.sh${NC}"
echo ""
# ============================================================================
# [15/20] Final Daemon Cleanup
# ============================================================================
echo -e "${YELLOW}==[15/20] Final Daemon Cleanup ==${NC}"
./gradlew --stop &>/dev/null || true
sleep 2
echo -e "${GREEN}[OK] Daemons stopped${NC}"
echo ""
# ============================================================================
# [16/20] Clean Lock Files
# ============================================================================
echo -e "${YELLOW}==[16/20] Clean Lock Files ==${NC}"
DAEMON_DIR="$OFFLINE_HOME/daemon"
if [ -d "$DAEMON_DIR" ]; then
rm -rf "$DAEMON_DIR" 2>/dev/null || true
fi
find "$OFFLINE_HOME" -type f \( -name "*.lock" -o -name "*.log" -o -name "*.tmp" \) -delete 2>/dev/null || true
echo -e "${GREEN}[OK] Lock files cleaned${NC}"
echo ""
# ============================================================================
# [17/20] Calculate Cache Size
# ============================================================================
echo -e "${YELLOW}==[17/20] Cache Summary ==${NC}"
CACHES_DIR="$OFFLINE_HOME/caches"
WRAPPER_DISTS="$OFFLINE_HOME/wrapper/dists"
TOTAL_SIZE=0
if [ -d "$CACHES_DIR" ]; then
# macOS uses different options for du
if du -k "$CACHES_DIR" &>/dev/null; then
SIZE=$(du -sk "$CACHES_DIR" 2>/dev/null | cut -f1)
SIZE=$((SIZE * 1024)) # Convert to bytes
else
SIZE=0
fi
TOTAL_SIZE=$((TOTAL_SIZE + SIZE))
SIZE_MB=$(awk "BEGIN {printf \"%.2f\", $SIZE / 1048576}")
echo -e "${CYAN}[INFO] Dependencies: ${SIZE_MB} MB${NC}"
fi
if [ -d "$WRAPPER_DISTS" ]; then
if du -k "$WRAPPER_DISTS" &>/dev/null; then
SIZE=$(du -sk "$WRAPPER_DISTS" 2>/dev/null | cut -f1)
SIZE=$((SIZE * 1024))
else
SIZE=0
fi
TOTAL_SIZE=$((TOTAL_SIZE + SIZE))
SIZE_MB=$(awk "BEGIN {printf \"%.2f\", $SIZE / 1048576}")
echo -e "${CYAN}[INFO] Gradle dist: ${SIZE_MB} MB${NC}"
fi
TOTAL_MB=$(awk "BEGIN {printf \"%.2f\", $TOTAL_SIZE / 1048576}")
echo -e "${CYAN}[INFO] Total cache: ${TOTAL_MB} MB${NC}"
echo ""
# ============================================================================
# [18/20] Create Archive
# ============================================================================
echo -e "${YELLOW}==[18/20] Create Archive ==${NC}"
BASE_NAME=$(basename "$ROOT")
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
PARENT=$(dirname "$ROOT")
ARCHIVE_PATH="${PARENT}/${BASE_NAME}_offline_bundle_${TIMESTAMP}.tar.gz"
echo "Archive: $ARCHIVE_PATH"
echo -e "${GRAY}[INFO] Creating archive (this may take several minutes)...${NC}"
# macOS tar with BSD options
tar -czf "$ARCHIVE_PATH" \
--exclude=".git" \
--exclude=".idea" \
--exclude=".DS_Store" \
--exclude="*.log" \
--exclude="*.lock" \
--exclude="_offline_gradle_home/daemon" \
--exclude="_offline_gradle_home/native" \
--exclude="_offline_gradle_home/jdks" \
--exclude="build" \
--exclude="out" \
--exclude=".gradle" \
-C "$ROOT" .
if [ $? -ne 0 ]; then
echo -e "${RED}ERROR: tar failed${NC}"
exit 1
fi
# macOS stat command
ARCHIVE_SIZE=$(stat -f%z "$ARCHIVE_PATH" 2>/dev/null)
ARCHIVE_SIZE_MB=$(awk "BEGIN {printf \"%.2f\", $ARCHIVE_SIZE / 1048576}")
echo -e "${GREEN}[OK] Archive created: ${ARCHIVE_SIZE_MB} MB${NC}"
echo ""
# ============================================================================
# [19/20] Verify Archive
# ============================================================================
echo -e "${YELLOW}==[19/20] Verify Archive ==${NC}"
CHECKS=(
"gradle/wrapper/gradle-wrapper.jar"
"gradlew"
"_offline_gradle_home/caches"
"run_offline_build.sh"
)
for CHECK in "${CHECKS[@]}"; do
if tar -tzf "$ARCHIVE_PATH" | grep -q "$CHECK"; then
echo -e " ${GREEN}[OK] $CHECK${NC}"
else
echo -e " ${YELLOW}[WARN] $CHECK${NC}"
fi
done
echo ""
# ============================================================================
# [20/20] Complete
# ============================================================================
echo -e "${GREEN}============================================================${NC}"
echo -e "${GREEN} BUNDLE CREATION COMPLETE!${NC}"
echo -e "${GREEN}============================================================${NC}"
echo ""
echo -e "${CYAN}Archive: $ARCHIVE_PATH${NC}"
echo -e "${CYAN}Size: ${ARCHIVE_SIZE_MB} MB${NC}"
echo ""
echo -e "${CYAN}============================================================${NC}"
echo -e "${CYAN} Test Results${NC}"
echo -e "${CYAN}============================================================${NC}"
if [ "$BUILD_SUCCESS" = true ]; then
echo -e " Online build (bootJar): ${GREEN}PASSED${NC}"
else
echo -e " Online build (bootJar): ${RED}FAILED${NC}"
fi
if [ "$BOOTRUN_SUCCESS" = true ]; then
echo -e " Online test (bootRun): ${GREEN}PASSED${NC}"
else
echo -e " Online test (bootRun): ${YELLOW}SKIPPED${NC}"
fi
if [ "$OFFLINE_SUCCESS" = true ]; then
echo -e " Offline build test: ${GREEN}PASSED${NC}"
else
echo -e " Offline build test: ${RED}FAILED${NC}"
fi
echo ""
echo -e "${YELLOW}============================================================${NC}"
echo -e "${YELLOW} Usage in Air-gapped Environment${NC}"
echo -e "${YELLOW}============================================================${NC}"
echo ""
echo -e "${WHITE}Option 1: Use unpack script${NC}"
echo -e "${GRAY} ./unpack_and_offline_build_airgap.sh${NC}"
echo ""
echo -e "${WHITE}Option 2: Manual extraction${NC}"
echo -e "${GRAY} tar -xzf <archive>.tar.gz${NC}"
echo -e "${GRAY} cd <project>${NC}"
echo -e "${GRAY} ./run_offline_build.sh${NC}"
echo ""
echo -e "${WHITE}Option 3: Direct commands${NC}"
echo -e "${GRAY} export GRADLE_USER_HOME=\"./_offline_gradle_home\"${NC}"
echo -e "${GRAY} ./gradlew --offline bootJar --no-daemon${NC}"
echo ""