Skip to content

20260403 fe add adminpage absent list (#351) #282

20260403 fe add adminpage absent list (#351)

20260403 fe add adminpage absent list (#351) #282

Workflow file for this run

name: Build & Deploy (backend + frontend)
on:
workflow_dispatch: {}
push:
branches: ["main"]
paths:
- "backend/**"
- "frontend/**" # 프론트 변경에도 트리거
- "docker-compose.yml"
- ".github/workflows/deploy.yml"
concurrency:
group: deploy-main
cancel-in-progress: true
env:
REGISTRY: ghcr.io
IMAGE_BACK: ghcr.io/sisc-it/sisc-web-back
IMAGE_FRONT: ghcr.io/sisc-it/sisc-web-front # 프론트 이미지 추가
jobs:
# 1. 변경 감지 (변동 없음)
changes:
runs-on: ubuntu-latest
outputs:
back: ${{ steps.filter.outputs.back }}
front: ${{ steps.filter.outputs.front }}
steps:
- name: Checkout (full history)
uses: actions/checkout@v4
#with: # Git 히스토리를 처음부터 끝까지 가져오기.
# fetch-depth: 0 # 개발 배포는 최신 코드만 있으면 됨 (기본값 1)
- name: Detect path changes
id: filter
uses: dorny/paths-filter@v3
with:
filters: |
back:
- 'backend/**'
- 'docker-compose.yml' # ⬅ 추가
- '.github/workflows/deploy.yml' # ⬅ 추가
front:
- 'frontend/**'
- 'docker-compose.yml' # ⬅ 추가
- '.github/workflows/deploy.yml' # ⬅ 추가
# 2. 백엔드 빌드
build-back:
needs: [changes] # deploy는 job 들이 병렬 처리가 되므로, 리스트 항목이 모두 끝나야 시작된다는 조건 추가
#if: ${{ needs.changes.outputs.back == 'true' || github.event_name == 'workflow_dispatch' }}
environment: development
runs-on: ubuntu-latest
permissions: { contents: read, packages: write }
#defaults:
# run:
# working-directory: backend # context: ./backend 로 지정했기 때문에 필요X
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} # 실행 중인 유저 이름 자동 할당
password: ${{ secrets.GITHUB_TOKEN }} # 별도 설정 없이 바로 사용 가능
- uses: docker/setup-buildx-action@v3
# 이미지 태그를 예쁘게 만들지만, 개발서버는 latest만 있으면 됨. (예: sha-a1b2c3d, v1.0.0, pr-123)
#- name: Meta (tags/labels) - backend
# id: meta-back
# uses: docker/metadata-action@v5
# with:
# images: ${{ env.IMAGE_BACK }}
# tags: |
# type=raw,value=latest
# type=sha,prefix=sha-,format=short
- name: Build & Push (backend)
uses: docker/build-push-action@v6
with:
context: ./backend
push: true
tags: ${{ env.IMAGE_BACK }}:latest
#tags: ${{ steps.meta-back.outputs.tags }}
#labels: ${{ steps.meta-back.outputs.labels }}
#cache-from: type=gha
#cache-to: type=gha,mode=max
build-front:
needs: [changes] # deploy는 job 들이 병렬 처리가 되므로, 리스트 항목이 모두 끝나야 시작된다는 조건 추가
#if: ${{ needs.changes.outputs.front == 'true' || github.event_name == 'workflow_dispatch' }}
runs-on: ubuntu-latest
environment: development # development 이라는 environment에 있는 secrets 들을 쓰기 위함 (secrets.FRONTEND_URL)
permissions: { contents: read, packages: write }
#defaults: # context: ./frontend
# run:
# working-directory: frontend
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} # 실행 중인 유저 이름 자동 할당
password: ${{ secrets.GITHUB_TOKEN }} # 별도 설정 없이 바로 사용 가능
#- name: Meta (tags/labels) - frontend
# id: meta-front
# uses: docker/metadata-action@v5
# with:
# images: ${{ env.IMAGE_FRONT }}
# tags: |
# type=raw,value=latest
# type=sha,prefix=sha-,format=short
- uses: docker/setup-buildx-action@v3
- name: Build & Push (frontend)
uses: docker/build-push-action@v6
with:
context: ./frontend # ⬅ 프론트 도커 컨텍스트 위치
push: true
platforms: linux/amd64
tags: ${{ env.IMAGE_FRONT }}:latest
#tags: ${{ steps.meta-front.outputs.tags }}
#labels: ${{ steps.meta-front.outputs.labels }}
build-args: |
VITE_API_URL=${{ secrets.SPRING_API_URL }}
#cache-from: type=gha
#cache-to: type=gha,mode=max
deploy:
#if: ${{ always() && (needs.build-back.result == 'success' || needs.build-front.result == 'success') }}
needs: [changes, build-back, build-front] # deploy는 job 들이 병렬 처리가 되므로, 리스트 항목이 모두 끝나야 시작된다는 조건 추가
# - manual 실행(workflow_dispatch)이면 무조건 실행
# - push일 때는 둘 중 하나라도 성공한 경우에만 실행 (둘 다 failure가 아닌 이상 OK)
runs-on: ubuntu-latest
environment: development # development 환경의 repository secrets 사용
steps:
- uses: actions/checkout@v4
- name: Make .env file
run: |
# 1. 기본 설정, 첫 줄은 > (덮어쓰기, >>:이어쓰기)
echo "SPRING_PROFILES_ACTIVE=prod" > .env
# 2. DB (GitHub Secrets)
echo "DB_URL=${{ secrets.DB_URL }}" >> .env
echo "DB_USERNAME=${{ secrets.DB_USERNAME }}" >> .env
echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> .env
# 3. 기타 Secrets
echo "SPRING_API_URL=${{ secrets.SPRING_API_URL }}" >> .env
echo "FRONTEND_URL=${{ secrets.FRONTEND_URL }}" >> .env
echo "JWT_SECRET=${{ secrets.JWT_SECRET }}" >> .env
echo "MAIL_USERNAME=${{ secrets.MAIL_USERNAME }}" >> .env
echo "MAIL_PASSWORD=${{ secrets.MAIL_PASSWORD }}" >> .env
echo "SBA_ADMIN_NAME=${{ secrets.SBA_ADMIN_NAME }}" >> .env
echo "SBA_ADMIN_PASSWORD=${{ secrets.SBA_ADMIN_PASSWORD }}" >> .env
# 필요하다면 풀 사이즈 변수도 추가
# echo "DB_POOL_MAX=30" >> .env
# echo "STOCK_POOL_MAX=10" >> .env
- name: 파일 전송 (docker-compose, .env to EC2)
uses: appleboy/scp-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
source: "docker-compose.yml, .env"
target: "/home/${{ secrets.SSH_USER }}/app/sisc-web/"
- name: SSH deploy
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
script_stop: true
script: |
set -euo pipefail
# 필수 폴더 생성 및 권한 설정 (최초 1회 혹은 누락 대비)
# sudo mkdir -p /mnt/storage/pg_ai_data /mnt/storage/logs /mnt/storage/uploads ./data/postgres
# sudo chown -R 70:70 /mnt/storage/pg_ai_data ./data/postgres # DB 전용
# sudo chmod -R 777 /mnt/storage/logs /mnt/storage/uploads # 앱 공용
cd ~/app/sisc-web
# GHCR 로그인 (백/프론트 둘 다 같은 레지스트리 사용)
echo "${{ secrets.GHCR_READ_TOKEN }}" | docker login ghcr.io -u ${{ secrets.GHCR_READ_USER }} --password-stdin
# 실행
docker compose pull api web redis npm # 최신 이미지 받기 (backend, frontend만)
docker compose up -d --remove-orphans api web redis npm db # 컨테이너 재기동 (backend, frontend만)
docker image prune -a -f
# 백엔드 헬스체크 (서비스 이름이 api일 때)
if docker ps --format '{{.Names}}' | grep -q "^api$"; then
echo "Waiting for api to be healthy..."
for i in {1..30}; do
status=$(docker inspect --format='{{json .State.Health.Status}}' api 2>/dev/null || echo '"none"')
if echo "$status" | grep -q healthy; then
echo "=== 서버 정상 작동 확인 ==="; break
fi
sleep 2
done
fi
# 도커 용량 최적화
- name: Cleanup Docker
run: |
# 빌드 과정에서 생긴 모든 캐시 삭제 (-a 옵션이 핵심)
docker builder prune -a -f
# 태그가 없어진 이미지 삭제
docker image prune -a -f