diff --git a/.gitea/workflows/deploy-dev.yml b/.gitea/workflows/deploy-dev.yml index ca20989..7c89181 100644 --- a/.gitea/workflows/deploy-dev.yml +++ b/.gitea/workflows/deploy-dev.yml @@ -17,7 +17,10 @@ # ※ Gitea 1.22.3 기준으로 작성했다. 이 버전은 workflow_dispatch(수동 실행 버튼)와 # inputs 컨텍스트를 지원하지 않는다 — 넣으면 워크플로 자체가 목록에 뜨지 않는다. # 그래서 트리거는 push 하나만 둔다. -# 재배포는 커밋 없이도 `git commit --allow-empty` 후 push 하면 되고, +# ※ 빈 커밋(`git commit --allow-empty`)으로는 재배포되지 않는다 — 변경 경로가 없어 +# 아래 paths-ignore 조건에서 걸러지고 워크플로가 아예 실행되지 않는다(2026-07-30 확인). +# 재배포가 필요하면 아래 무시 대상이 아닌 파일을 실제로 변경해 push 하거나, +# 서버에서 `bash /opt/arko-dev/bin/arko-dev-deploy.sh /opt/arko-dev/incoming/arko.war` 를 직접 실행한다. # 롤백은 서버에서 직접 수행한다(deploy/dev/README.md 참고). name: deploy-dev diff --git a/deploy/dev/arko-dev-deploy.sh b/deploy/dev/arko-dev-deploy.sh index e11b0b2..126afd8 100644 --- a/deploy/dev/arko-dev-deploy.sh +++ b/deploy/dev/arko-dev-deploy.sh @@ -38,8 +38,11 @@ mkdir -p "$BACKUP_DIR" "$LOG_DIR" # ---------------------------------------------------------------- 1. WAR 검증 [ -n "$WAR_SRC" ] || die "사용법: $0 " [ -f "$WAR_SRC" ] || die "WAR 파일 없음: $WAR_SRC" -unzip -l "$WAR_SRC" > /dev/null 2>&1 || die "WAR 파일이 손상됨: $WAR_SRC" -unzip -l "$WAR_SRC" | grep -q "WEB-INF/web.xml" || die "WEB-INF/web.xml 없음 — 정상 WAR 아님" +# unzip을 두 번 돌리고 grep -q로 파이프하면 set -o pipefail과 맞물려 취약하다 +# (2026-07-30: 정상 WAR인데 Actions 실행 중 "web.xml 없음"으로 오판해 배포가 멈췄다). +# 목록을 한 번만 읽어 변수에 담고 파이프 없이 검사한다. +WAR_LIST=$(unzip -l "$WAR_SRC") || die "WAR 파일이 손상됨: $WAR_SRC" +grep -q "WEB-INF/web.xml" <<< "$WAR_LIST" || die "WEB-INF/web.xml 없음 — 정상 WAR 아님" WAR_SIZE=$(du -h "$WAR_SRC" | cut -f1) log "배포 시작: $WAR_SRC ($WAR_SIZE)" diff --git a/deploy/dev/build-and-upload.sh b/deploy/dev/build-and-upload.sh index b2d9493..556c743 100644 --- a/deploy/dev/build-and-upload.sh +++ b/deploy/dev/build-and-upload.sh @@ -20,7 +20,9 @@ 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 불가) @@ -53,7 +55,9 @@ if [ "$DO_BUILD" = "1" ]; then [ -x "$JDK8_HOME/bin/javac" ] || { echo "JDK 8을 찾을 수 없다: $JDK8_HOME (JDK8_HOME 환경변수로 지정)"; exit 1; } export JAVA_HOME="$JDK8_HOME" cd "$PROJECT_ROOT" - mvn -B -ntp clean package -DskipTests + # -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; } @@ -78,8 +82,40 @@ 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; } -# 전송 중 중단되어도 반쪽 WAR이 배포되지 않도록 임시 이름으로 올린 뒤 교체 -PSCP "$WAR" "$SERVER_USER@$SERVER_IP:$REMOTE_INCOMING/arko.war.uploading" +# 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. 배포