From 731d4dfd939b6498b9b27390d1de6a410cdd23ed Mon Sep 17 00:00:00 2001 From: Lectom C Han Date: Wed, 11 Feb 2026 08:55:50 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=EC=8B=9C=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EB=B3=80=EC=88=98=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/staging_code_pull.yml | 308 ++++++++++++------------- 1 file changed, 146 insertions(+), 162 deletions(-) diff --git a/.gitea/workflows/staging_code_pull.yml b/.gitea/workflows/staging_code_pull.yml index 98d770f0..8f05c67b 100644 --- a/.gitea/workflows/staging_code_pull.yml +++ b/.gitea/workflows/staging_code_pull.yml @@ -1,181 +1,165 @@ name: Release Baron SSO to Staging on: - workflow_dispatch: - inputs: - rc_version_tag: - description: "The version tag to deploy to staging (e.g., v1.2601.1-RC1)" - required: true - type: string + workflow_dispatch: jobs: - deploy-staging: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 + deploy-staging: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 - - name: Setup SSH - uses: webfactory/ssh-agent@v0.9.0 - with: - ssh-private-key: ${{ secrets.STAGE_SSH_PRIVATE_KEY }} + - name: Setup SSH + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.STAGE_SSH_PRIVATE_KEY }} - - name: Deploy to Staging - env: - IMAGE_TAG: ${{ github.event.inputs.rc_version_tag }} - BACKEND_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/backend - USERFRONT_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/userfront - ADMINFRONT_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/adminfront - DEVFRONT_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/devfront - - # Staging-specific variables - DEPLOY_PATH: ${{ vars.STAGE_DEPLOY_PATH }} - STAGE_HOST: ${{ vars.STAGE_HOST }} - STAGE_USER: ${{ vars.STAGE_USER }} + - name: Deploy to Staging + env: + DEPLOY_PATH: ${{ vars.STAGE_DEPLOY_PATH }} + STAGE_HOST: ${{ vars.STAGE_HOST }} + STAGE_USER: ${{ vars.STAGE_USER }} + run: | + set -euo pipefail - HARBOR_ENDPOINT: ${{ vars.HARBOR_ENDPOINT }} - HARBOR_ROBOT_ACCOUNT: ${{ vars.HARBOR_ROBOT_ACCOUNT }} - HARBOR_ROBOT_KEY: ${{ secrets.HARBOR_ROBOT_KEY }} - run: | - set -euo pipefail + echo "DEBUG: STAGE_USER='${STAGE_USER}'" + echo "DEBUG: STAGE_HOST='${STAGE_HOST}'" + echo "DEBUG: DEPLOY_PATH='${DEPLOY_PATH}'" - echo "DEBUG: STAGE_USER='${STAGE_USER}'" - echo "DEBUG: STAGE_HOST='${STAGE_HOST}'" - echo "DEBUG: DEPLOY_PATH='${DEPLOY_PATH}'" + # Sanity check + if [ -z "${STAGE_USER}" ] || [ -z "${STAGE_HOST}" ] || [ -z "${DEPLOY_PATH}" ]; then + echo "::error::Missing required vars (STAGE_USER/STAGE_HOST/DEPLOY_PATH)." + exit 1 + fi - # Sanity check - if [ -z "${STAGE_USER}" ] || [ -z "${STAGE_HOST}" ] || [ -z "${DEPLOY_PATH}" ]; then - echo "::error::Missing required vars (STAGE_USER/STAGE_HOST/DEPLOY_PATH)." - exit 1 - fi + ssh-keyscan -H "${STAGE_HOST}" >> ~/.ssh/known_hosts + ssh "${STAGE_USER}@${STAGE_HOST}" "mkdir -p '${DEPLOY_PATH}'" - ssh-keyscan -H "${STAGE_HOST}" >> ~/.ssh/known_hosts - ssh "${STAGE_USER}@${STAGE_HOST}" "mkdir -p '${DEPLOY_PATH}'" + # .env 파일 생성 + cat <<'EOF' > .env + APP_ENV=${{ vars.APP_ENV }} + TZ=Asia/Seoul + IDP_PROVIDER=ory - # .env 파일 생성 - cat <<'EOF' > .env - APP_ENV=${{ vars.APP_ENV }} - TZ=Asia/Seoul - IDP_PROVIDER=ory - - # DB & Clickhouse - DB_PORT=${{ vars.DB_PORT }} - CLICKHOUSE_PORT_HTTP=${{ vars.CLICKHOUSE_PORT_HTTP }} - CLICKHOUSE_PORT_NATIVE=${{ vars.CLICKHOUSE_PORT_NATIVE }} - CLICKHOUSE_HOST=${{ vars.CLICKHOUSE_HOST }} - CLICKHOUSE_USER=${{ vars.CLICKHOUSE_USER }} - CLICKHOUSE_PASSWORD=${{ vars.CLICKHOUSE_PASSWORD }} + # DB & Clickhouse + DB_PORT=${{ vars.DB_PORT }} + CLICKHOUSE_PORT_HTTP=${{ vars.CLICKHOUSE_PORT_HTTP }} + CLICKHOUSE_PORT_NATIVE=${{ vars.CLICKHOUSE_PORT_NATIVE }} + CLICKHOUSE_HOST=${{ vars.CLICKHOUSE_HOST }} + CLICKHOUSE_USER=${{ vars.CLICKHOUSE_USER }} + CLICKHOUSE_PASSWORD=${{ vars.CLICKHOUSE_PASSWORD }} - BACKEND_PORT=${{ vars.BACKEND_PORT }} - ADMINFRONT_PORT=${{ vars.ADMINFRONT_PORT }} - DEVFRONT_PORT=${{ vars.DEVFRONT_PORT }} - USERFRONT_PORT=${{ vars.USERFRONT_PORT }} - - OATHKEEPER_API_URL=${{ vars.OATHKEEPER_API_URL }} + BACKEND_PORT=${{ vars.BACKEND_PORT }} + ADMINFRONT_PORT=${{ vars.ADMINFRONT_PORT }} + DEVFRONT_PORT=${{ vars.DEVFRONT_PORT }} + USERFRONT_PORT=${{ vars.USERFRONT_PORT }} - DB_USER=${{ vars.DB_USER }} - DB_PASSWORD=${{ secrets.STG_DB_PASSWORD }} - DB_NAME=${{ vars.DB_NAME }} - COOKIE_SECRET=${{ secrets.STG_COOKIE_SECRET }} - JWT_SECRET=${{ secrets.STG_JWT_SECRET }} - REDIS_ADDR=${{ vars.REDIS_ADDR }} - CORS_ALLOWED_ORIGINS=${{ vars.CORS_ALLOWED_ORIGINS }} - AUDIT_WORKER_COUNT=5 - AUDIT_QUEUE_SIZE=2000 - PROFILE_CACHE_TTL=${{ vars.PROFILE_CACHE_TTL }} - DESCOPE_TEST_ACCOUNT=${{ vars.DESCOPE_TEST_ACCOUNT }} - NAVER_CLOUD_ACCESS_KEY=${{ vars.NAVER_CLOUD_ACCESS_KEY }} - NAVER_CLOUD_SECRET_KEY=${{ secrets.NAVER_CLOUD_SECRET_KEY }} - NAVER_CLOUD_SERVICE_ID=${{ vars.NAVER_CLOUD_SERVICE_ID }} - NAVER_SENDER_PHONE_NUMBER=${{ vars.NAVER_SENDER_PHONE_NUMBER }} - AWS_REGION=${{ vars.AWS_REGION }} - AWS_ACCESS_KEY_ID=${{ vars.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_SES_SENDER=${{ vars.AWS_SES_SENDER }} - ADMIN_EMAIL=${{ vars.ADMIN_EMAIL }} - ADMIN_PASSWORD=${{ secrets.STG_ADMIN_PASSWORD }} - USERFRONT_URL=${{ vars.USERFRONT_URL }} - BACKEND_URL=${{ vars.BACKEND_URL }} - OATHKEEPER_PUBLIC_URL=${{ vars.OATHKEEPER_PUBLIC_URL }} - ORY_POSTGRES_TAG=${{ vars.ORY_POSTGRES_TAG }} - ORY_POSTGRES_USER=${{ vars.ORY_POSTGRES_USER }} - ORY_POSTGRES_PASSWORD=${{ secrets.STG_ORY_POSTGRES_PASSWORD }} - ORY_POSTGRES_DB=${{ vars.ORY_POSTGRES_DB }} - KRATOS_DB=${{ vars.KRATOS_DB }} - HYDRA_DB=${{ vars.HYDRA_DB }} - KETO_DB=${{ vars.KETO_DB }} - KRATOS_VERSION=${{ vars.KRATOS_VERSION }} - KRATOS_UI_NODE_VERSION=${{ vars.KRATOS_UI_NODE_VERSION }} - HYDRA_VERSION=${{ vars.HYDRA_VERSION }} - KETO_VERSION=${{ vars.KETO_VERSION }} - ORY_SDK_URL=${{ vars.ORY_SDK_URL }} - KRATOS_PUBLIC_URL=${{ vars.KRATOS_PUBLIC_URL }} - KRATOS_ADMIN_URL=${{ vars.KRATOS_ADMIN_URL }} - KRATOS_BROWSER_URL=${{ vars.KRATOS_BROWSER_URL }} - KRATOS_UI_URL=${{ vars.KRATOS_UI_URL }} - HYDRA_ADMIN_URL=${{ vars.HYDRA_ADMIN_URL }} - HYDRA_PUBLIC_URL=${{ vars.HYDRA_PUBLIC_URL }} - JWKS_URL=${{ vars.JWKS_URL }} - OATHKEEPER_VERSION=${{ vars.OATHKEEPER_VERSION }} - OATHKEEPER_UID=${{ vars.OATHKEEPER_UID }} - OATHKEEPER_GID=${{ vars.OATHKEEPER_GID }} - OATHKEEPER_HEALTH_URL=${{ vars.OATHKEEPER_HEALTH_URL }} - OATHKEEPER_HEALTH_INTERVAL_SECONDS=${{ vars.OATHKEEPER_HEALTH_INTERVAL_SECONDS }} - OATHKEEPER_HEALTH_TIMEOUT_SECONDS=${{ vars.OATHKEEPER_HEALTH_TIMEOUT_SECONDS }} - OATHKEEPER_HEALTH_ENABLED=${{ vars.OATHKEEPER_HEALTH_ENABLED }} - CSRF_COOKIE_NAME=${{ vars.CSRF_COOKIE_NAME }} - CSRF_COOKIE_SECRET=${{ secrets.STG_CSRF_COOKIE_SECRET }} - # OATHKEEPER_INTROSPECT_CLIENT_ID=${{ vars.OATHKEEPER_INTROSPECT_CLIENT_ID }} - # OATHKEEPER_INTROSPECT_CLIENT_SECRET=${{ secrets.STG_OATHKEEPER_INTROSPECT_CLIENT_SECRET }} - EOF + OATHKEEPER_API_URL=${{ vars.OATHKEEPER_API_URL }} - # 코드 업데이트 (Git) - ssh "${STAGE_USER}@${STAGE_HOST}" "mkdir -p '${DEPLOY_PATH}' && cd '${DEPLOY_PATH}' && \ - git fetch origin main && \ - git checkout -B main origin/main && \ - git pull --ff-only" + DB_USER=${{ vars.DB_USER }} + DB_PASSWORD=${{ secrets.STG_DB_PASSWORD }} + DB_NAME=${{ vars.DB_NAME }} + COOKIE_SECRET=${{ secrets.STG_COOKIE_SECRET }} + JWT_SECRET=${{ secrets.STG_JWT_SECRET }} + REDIS_ADDR=${{ vars.REDIS_ADDR }} + CORS_ALLOWED_ORIGINS=${{ vars.CORS_ALLOWED_ORIGINS }} + AUDIT_WORKER_COUNT=5 + AUDIT_QUEUE_SIZE=2000 + PROFILE_CACHE_TTL=${{ vars.PROFILE_CACHE_TTL }} + DESCOPE_TEST_ACCOUNT=${{ vars.DESCOPE_TEST_ACCOUNT }} + NAVER_CLOUD_ACCESS_KEY=${{ vars.NAVER_CLOUD_ACCESS_KEY }} + NAVER_CLOUD_SECRET_KEY=${{ secrets.NAVER_CLOUD_SECRET_KEY }} + NAVER_CLOUD_SERVICE_ID=${{ vars.NAVER_CLOUD_SERVICE_ID }} + NAVER_SENDER_PHONE_NUMBER=${{ vars.NAVER_SENDER_PHONE_NUMBER }} + AWS_REGION=${{ vars.AWS_REGION }} + AWS_ACCESS_KEY_ID=${{ vars.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_SES_SENDER=${{ vars.AWS_SES_SENDER }} + ADMIN_EMAIL=${{ vars.ADMIN_EMAIL }} + ADMIN_PASSWORD=${{ secrets.STG_ADMIN_PASSWORD }} + USERFRONT_URL=${{ vars.USERFRONT_URL }} + BACKEND_URL=${{ vars.BACKEND_URL }} + OATHKEEPER_PUBLIC_URL=${{ vars.OATHKEEPER_PUBLIC_URL }} + ORY_POSTGRES_TAG=${{ vars.ORY_POSTGRES_TAG }} + ORY_POSTGRES_USER=${{ vars.ORY_POSTGRES_USER }} + ORY_POSTGRES_PASSWORD=${{ secrets.STG_ORY_POSTGRES_PASSWORD }} + ORY_POSTGRES_DB=${{ vars.ORY_POSTGRES_DB }} + KRATOS_DB=${{ vars.KRATOS_DB }} + HYDRA_DB=${{ vars.HYDRA_DB }} + KETO_DB=${{ vars.KETO_DB }} + KRATOS_VERSION=${{ vars.KRATOS_VERSION }} + KRATOS_UI_NODE_VERSION=${{ vars.KRATOS_UI_NODE_VERSION }} + HYDRA_VERSION=${{ vars.HYDRA_VERSION }} + KETO_VERSION=${{ vars.KETO_VERSION }} + ORY_SDK_URL=${{ vars.ORY_SDK_URL }} + KRATOS_PUBLIC_URL=${{ vars.KRATOS_PUBLIC_URL }} + KRATOS_ADMIN_URL=${{ vars.KRATOS_ADMIN_URL }} + KRATOS_BROWSER_URL=${{ vars.KRATOS_BROWSER_URL }} + KRATOS_UI_URL=${{ vars.KRATOS_UI_URL }} + HYDRA_ADMIN_URL=${{ vars.HYDRA_ADMIN_URL }} + HYDRA_PUBLIC_URL=${{ vars.HYDRA_PUBLIC_URL }} + JWKS_URL=${{ vars.JWKS_URL }} + OATHKEEPER_VERSION=${{ vars.OATHKEEPER_VERSION }} + OATHKEEPER_UID=${{ vars.OATHKEEPER_UID }} + OATHKEEPER_GID=${{ vars.OATHKEEPER_GID }} + OATHKEEPER_HEALTH_URL=${{ vars.OATHKEEPER_HEALTH_URL }} + OATHKEEPER_HEALTH_INTERVAL_SECONDS=${{ vars.OATHKEEPER_HEALTH_INTERVAL_SECONDS }} + OATHKEEPER_HEALTH_TIMEOUT_SECONDS=${{ vars.OATHKEEPER_HEALTH_TIMEOUT_SECONDS }} + OATHKEEPER_HEALTH_ENABLED=${{ vars.OATHKEEPER_HEALTH_ENABLED }} + CSRF_COOKIE_NAME=${{ vars.CSRF_COOKIE_NAME }} + CSRF_COOKIE_SECRET=${{ secrets.STG_CSRF_COOKIE_SECRET }} + # OATHKEEPER_INTROSPECT_CLIENT_ID=${{ vars.OATHKEEPER_INTROSPECT_CLIENT_ID }} + # OATHKEEPER_INTROSPECT_CLIENT_SECRET=${{ secrets.STG_OATHKEEPER_INTROSPECT_CLIENT_SECRET }} + EOF - # .env 파일 복사 - scp .env "${STAGE_USER}@${STAGE_HOST}:${DEPLOY_PATH}/" + # 코드 업데이트 (Git) + ssh "${STAGE_USER}@${STAGE_HOST}" "mkdir -p '${DEPLOY_PATH}' && cd '${DEPLOY_PATH}' && \ + git fetch origin main && \ + git checkout -B main origin/main && \ + git pull --ff-only" - # 배포 실행 - echo "${HARBOR_ROBOT_KEY}" | ssh "${STAGE_USER}@${STAGE_HOST}" \ - "export DEPLOY_PATH='${DEPLOY_PATH}'; \ - export BACKEND_IMAGE_NAME='${BACKEND_IMAGE_NAME}'; \ - export USERFRONT_IMAGE_NAME='${USERFRONT_IMAGE_NAME}'; \ - export ADMINFRONT_IMAGE_NAME='${ADMINFRONT_IMAGE_NAME}'; \ - export DEVFRONT_IMAGE_NAME='${DEVFRONT_IMAGE_NAME}'; \ - export IMAGE_TAG='${IMAGE_TAG}'; \ - export HARBOR_ENDPOINT='${HARBOR_ENDPOINT}'; \ - export HARBOR_ROBOT_ACCOUNT='${HARBOR_ROBOT_ACCOUNT}'; \ - cd \"\${DEPLOY_PATH}\"; \ - docker login \"\${HARBOR_ENDPOINT}\" -u \"\${HARBOR_ROBOT_ACCOUNT}\" --password-stdin; \ - set -a; . ./.env; set +a; \ - - # 네트워크 생성 - for net in baron_net public_net ory-net hydranet kratosnet; do - docker network inspect \"\$net\" >/dev/null 2>&1 || docker network create \"\$net\" - done - - envsubst < docker/docker-compose.staging.template.yaml > docker-compose.yml; \ + # .env 파일 복사 + scp .env "${STAGE_USER}@${STAGE_HOST}:${DEPLOY_PATH}/" - # [중요] 설정 파일 권한 문제 해결 (Ory 이미지는 root가 아닌 사용자로 실행됨) - chmod -R 777 docker/ory - - docker compose -f docker/compose.infra.yaml -f docker/compose.ory.yaml -f docker-compose.yml pull; \ - - # [주의] DB 초기화 스크립트는 '새로운 볼륨'에서만 실행됨. - # DB 초기화 문제를 확실히 해결하기 위해 기존 볼륨을 날리고 다시 띄움 (데이터 삭제됨 주의) - # 스테이징이므로 초기화 진행. 데이터 보존이 필요하면 이 줄 제거하고 수동으로 DB 만들어야 함. - docker compose -f docker/compose.infra.yaml -f docker/compose.ory.yaml -f docker-compose.yml down -v || true - - docker compose -f docker/compose.infra.yaml -f docker/compose.ory.yaml -f docker-compose.yml up -d --remove-orphans; \ - - # 배포 후 상태 확인 (실패 시 로그 출력을 위함) - sleep 10; \ - if [ \$(docker inspect -f '{{.State.ExitCode}}' baron-sso-staging-kratos-migrate-1) -ne 0 ]; then \ - echo 'Kratos Migrate Failed. Logs:'; \ - docker logs baron-sso-staging-kratos-migrate-1; \ - exit 1; \ - fi" \ No newline at end of file + # 배포 실행 + echo "${HARBOR_ROBOT_KEY}" | ssh "${STAGE_USER}@${STAGE_HOST}" \ + "export DEPLOY_PATH='${DEPLOY_PATH}'; \ + export BACKEND_IMAGE_NAME='${BACKEND_IMAGE_NAME}'; \ + export USERFRONT_IMAGE_NAME='${USERFRONT_IMAGE_NAME}'; \ + export ADMINFRONT_IMAGE_NAME='${ADMINFRONT_IMAGE_NAME}'; \ + export DEVFRONT_IMAGE_NAME='${DEVFRONT_IMAGE_NAME}'; \ + export IMAGE_TAG='${IMAGE_TAG}'; \ + export HARBOR_ENDPOINT='${HARBOR_ENDPOINT}'; \ + export HARBOR_ROBOT_ACCOUNT='${HARBOR_ROBOT_ACCOUNT}'; \ + cd \"\${DEPLOY_PATH}\"; \ + docker login \"\${HARBOR_ENDPOINT}\" -u \"\${HARBOR_ROBOT_ACCOUNT}\" --password-stdin; \ + set -a; . ./.env; set +a; \ + + # 네트워크 생성 + for net in baron_net public_net ory-net hydranet kratosnet; do + docker network inspect \"\$net\" >/dev/null 2>&1 || docker network create \"\$net\" + done + + envsubst < docker/docker-compose.staging.template.yaml > docker-compose.yml; \ + + # [중요] 설정 파일 권한 문제 해결 (Ory 이미지는 root가 아닌 사용자로 실행됨) + chmod -R 777 docker/ory + + docker compose -f docker/compose.infra.yaml -f docker/compose.ory.yaml -f docker-compose.yml pull; \ + + # [주의] DB 초기화 스크립트는 '새로운 볼륨'에서만 실행됨. + # DB 초기화 문제를 확실히 해결하기 위해 기존 볼륨을 날리고 다시 띄움 (데이터 삭제됨 주의) + # 스테이징이므로 초기화 진행. 데이터 보존이 필요하면 이 줄 제거하고 수동으로 DB 만들어야 함. + docker compose -f docker/compose.infra.yaml -f docker/compose.ory.yaml -f docker-compose.yml down || true + + docker compose -f docker/compose.infra.yaml -f docker/compose.ory.yaml -f docker-compose.yml up -d --remove-orphans; \ + + # 배포 후 상태 확인 (실패 시 로그 출력을 위함) + sleep 10; \ + if [ \$(docker inspect -f '{{.State.ExitCode}}' baron-sso-staging-kratos-migrate-1) -ne 0 ]; then \ + echo 'Kratos Migrate Failed. Logs:'; \ + docker logs baron-sso-staging-kratos-migrate-1; \ + exit 1; \ + fi"