#!/usr/bin/env bash # arko_renual — 로컬 빌드 → 개발서버 WAR 업로드 (개발자 PC에서 실행) # # 표준 흐름: # 1) bash deploy/dev/build-and-upload.sh 빌드 + WAR 업로드 # 2) git push origin master → Gitea Actions가 자동 배포 # # 옵션: # --build-only 빌드만 (업로드 안 함) # --no-build 업로드만 (기존 target/arko.war 사용) # --deploy 업로드 후 서버 배포까지 직접 실행 (push 없이 즉시 반영할 때) # # 서버(2코어/메모리 여유 1G)에서 Maven을 돌리지 않기 위한 구조다. # 빌드는 개발자 PC에서, 서버는 전개만 한다. set -euo pipefail # ------------------------------------------------------------------ 설정 SERVER_IP=101.79.17.164 SERVER_USER=root SERVER_PW='1q2w3e!Q' HOSTKEY="SHA256:Zm5aAv5DJ/EnKiObKSadOxA5nKuXgToCGhNiyhVhAjY" REMOTE_INCOMING=/opt/arko-dev/incoming REMOTE_PARTS=/opt/arko-dev/incoming/parts REMOTE_DEPLOY=/opt/arko-dev/bin/arko-dev-deploy.sh CHUNK_SIZE=30m # pscp 연결이 30초 부근에서 끊기는 사례가 있어 조각 단위로 올린다 SITE_URL=https://csv.zioinfo.co.kr/ # arko는 maven-compiler-plugin이 source/target 1.7 → JDK 8로만 빌드된다 (17/21 불가) JDK8_HOME=${JDK8_HOME:-"C:/Program Files/Java/jdk1.8.0_202"} PROJECT_ROOT=$(cd "$(dirname "$0")/../.." && pwd) WAR="$PROJECT_ROOT/target/arko.war" DO_BUILD=1 DO_UPLOAD=1 DO_DEPLOY=0 # 기본은 업로드까지만 — 배포는 git push가 Gitea Actions로 트리거한다 for arg in "$@"; do case "$arg" in --build-only) DO_UPLOAD=0 ;; --no-build) DO_BUILD=0 ;; --deploy) DO_DEPLOY=1 ;; *) echo "사용법: $0 [--build-only] [--no-build] [--deploy]"; exit 1 ;; esac done log() { echo -e "\n[$(date +'%F %T')] === $* ==="; } # PuTTY plink/pscp 사용 (Windows 기본 ssh는 비밀번호 자동입력이 안 된다) PLINK() { plink -batch -hostkey "$HOSTKEY" -pw "$SERVER_PW" "$SERVER_USER@$SERVER_IP" "$@"; } PSCP() { pscp -batch -hostkey "$HOSTKEY" -pw "$SERVER_PW" "$@"; } # ------------------------------------------------------------------ 1. 빌드 if [ "$DO_BUILD" = "1" ]; then log "1. 로컬 빌드 (JDK 8)" [ -x "$JDK8_HOME/bin/javac" ] || { echo "JDK 8을 찾을 수 없다: $JDK8_HOME (JDK8_HOME 환경변수로 지정)"; exit 1; } export JAVA_HOME="$JDK8_HOME" cd "$PROJECT_ROOT" # -ntp(--no-transfer-progress)는 Maven 3.6.1+ 전용이다. PATH에 3.5.4가 잡히면 # "Unrecognized option: -ntp"로 즉시 죽으므로 쓰지 않는다. mvn -B clean package -DskipTests [ -f "$WAR" ] || { echo "WAR 생성 실패: $WAR"; exit 1; } # 바이트코드 버전 확인 — 51(0x33)=Java7. JDK 17/21로 잘못 빌드되면 서버에서 못 뜬다 CLS=$(find target/classes -name "*.class" | head -1) VER=$(od -An -t d1 -j 7 -N 1 "$CLS" | tr -d ' ') echo "바이트코드 major=$VER (51이어야 정상)" [ "$VER" = "51" ] || { echo "경고: JDK 8이 아닌 컴파일러로 빌드된 것으로 보인다"; exit 1; } ls -lh "$WAR" fi [ "$DO_UPLOAD" = "1" ] || { log "빌드만 수행하고 종료"; exit 0; } [ -f "$WAR" ] || { echo "WAR이 없다: $WAR (--no-build 이면 먼저 빌드할 것)"; exit 1; } # ------------------------------------------------------------- 2. 업로드 log "2. 서버 업로드 ($(du -h "$WAR" | cut -f1))" PLINK "mkdir -p $REMOTE_INCOMING" # 서버 디스크 여유 확인 — WAR 3배(업로드+전개+백업)가 필요하다 FREE_MB=$(PLINK "df -m $REMOTE_INCOMING | awk 'NR==2 {print \$4}'" | tr -d '\r') WAR_MB=$(( $(du -k "$WAR" | cut -f1) / 1024 )) echo "서버 여유 ${FREE_MB}MB / WAR ${WAR_MB}MB" [ "$FREE_MB" -gt $(( WAR_MB * 3 )) ] || { echo "서버 디스크 부족"; exit 1; } # pscp가 대용량 전송 중 약 30초 지점에서 조용히 끊기면서 exit 0을 반환한 사례가 있다 # (2026-07-30: 194MB WAR이 66MB/84MB로 잘린 채 "업로드 완료"로 처리되어 배포가 깨졌다). # 그래서 ① 조각으로 나눠 올리고 ② 조각마다 크기를 확인하고 ③ 서버에서 합친 뒤 # ④ size+md5가 로컬과 일치할 때만 arko.war 로 교체한다. 검증 실패 시 기존 WAR은 그대로 둔다. LOCAL_SIZE=$(stat -c %s "$WAR") LOCAL_MD5=$(md5sum "$WAR" | cut -d' ' -f1) echo "로컬 size=$LOCAL_SIZE md5=$LOCAL_MD5" PARTS_DIR="$PROJECT_ROOT/target/warparts" rm -rf "$PARTS_DIR"; mkdir -p "$PARTS_DIR" split -b "$CHUNK_SIZE" -d -a 2 "$WAR" "$PARTS_DIR/part_" PART_CNT=$(ls "$PARTS_DIR" | wc -l) echo "분할 ${PART_CNT}개 ($CHUNK_SIZE)" PLINK "rm -rf $REMOTE_PARTS && mkdir -p $REMOTE_PARTS" IDX=0 for P in "$PARTS_DIR"/part_*; do IDX=$((IDX+1)); NAME=$(basename "$P"); LSZ=$(stat -c %s "$P"); OK=0 for TRY in 1 2 3; do PSCP "$P" "$SERVER_USER@$SERVER_IP:$REMOTE_PARTS/$NAME" || true RSZ=$(PLINK "stat -c %s $REMOTE_PARTS/$NAME 2>/dev/null || echo 0" | tr -d '\r') if [ "$RSZ" = "$LSZ" ]; then echo " [$IDX/$PART_CNT] $NAME OK ($LSZ bytes)"; OK=1; break; fi echo " [$IDX/$PART_CNT] $NAME 크기 불일치 (remote=$RSZ local=$LSZ) — 재시도 $TRY/3" done [ "$OK" = "1" ] || { echo "조각 전송 3회 실패: $NAME — 중단"; exit 1; } done PLINK "cat $REMOTE_PARTS/part_* > $REMOTE_INCOMING/arko.war.uploading && rm -rf $REMOTE_PARTS" R_SIZE=$(PLINK "stat -c %s $REMOTE_INCOMING/arko.war.uploading" | tr -d '\r') R_MD5=$(PLINK "md5sum $REMOTE_INCOMING/arko.war.uploading | cut -d' ' -f1" | tr -d '\r') echo "서버 size=$R_SIZE md5=$R_MD5" [ "$R_SIZE" = "$LOCAL_SIZE" ] || { echo "업로드 크기 불일치 — arko.war 를 교체하지 않고 중단"; exit 1; } [ "$R_MD5" = "$LOCAL_MD5" ] || { echo "업로드 md5 불일치 — arko.war 를 교체하지 않고 중단"; exit 1; } rm -rf "$PARTS_DIR" PLINK "mv -f $REMOTE_INCOMING/arko.war.uploading $REMOTE_INCOMING/arko.war && ls -lh $REMOTE_INCOMING/arko.war" # ------------------------------------------------------------- 3. 배포 if [ "$DO_DEPLOY" != "1" ]; then log "업로드 완료 — 배포는 git push 가 트리거한다" cat <