From d338b451d3dbcc568bff962ccbf996638c505dca Mon Sep 17 00:00:00 2001 From: jjoonleo Date: Sat, 9 May 2026 04:21:54 +0900 Subject: [PATCH] Revert "Merge pull request #304 from DevKor-github/codexd/dev-remote-pc-deploy-workflow" This reverts commit 3afb97f839506c70848cff4d6bff2815b479f005, reversing changes made to 5685bef057e34c73542fba500a709b9ef18bd988. --- .github/workflows/deploy-dev.yml | 169 ------------------ docs/deployment.md | 81 +-------- docs/git-workflow.md | 42 ++--- ontime-back/docker-compose.dev.yml | 37 ---- .../main/resources/application-dev.properties | 53 ------ 5 files changed, 20 insertions(+), 362 deletions(-) delete mode 100644 .github/workflows/deploy-dev.yml delete mode 100644 ontime-back/docker-compose.dev.yml delete mode 100644 ontime-back/src/main/resources/application-dev.properties diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml deleted file mode 100644 index 836aaab..0000000 --- a/.github/workflows/deploy-dev.yml +++ /dev/null @@ -1,169 +0,0 @@ -name: Deploy Dev - -on: - workflow_dispatch: - push: - branches: - - dev - -permissions: - contents: read - packages: write - -concurrency: - group: deploy-development - cancel-in-progress: true - -env: - REGISTRY: ghcr.io - IMAGE_NAME: devkor-github/ontime-back - IMAGE_TAG: dev-${{ github.sha }} - -jobs: - build-and-push: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to GHCR - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push image - uses: docker/build-push-action@v6 - with: - context: ./ontime-back - file: ./ontime-back/Dockerfile - push: true - tags: | - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev-latest - cache-from: type=gha - cache-to: type=gha,mode=max - - deploy-to-remote-pc: - needs: build-and-push - runs-on: ubuntu-latest - environment: development - env: - DEV_DEPLOY_DIR: ${{ secrets.DEV_DEPLOY_DIR || format('/home/{0}/OnTime-back-dev', secrets.DEV_REMOTE_USER) }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Prepare dev deploy directory - uses: appleboy/ssh-action@v1.0.3 - with: - host: ${{ secrets.DEV_REMOTE_HOST }} - username: ${{ secrets.DEV_REMOTE_USER }} - key: ${{ secrets.DEV_REMOTE_SSH_KEY }} - script: | - set -eu - mkdir -p "${{ env.DEV_DEPLOY_DIR }}" - - - name: Upload compose files to remote PC - uses: appleboy/scp-action@v0.1.7 - with: - host: ${{ secrets.DEV_REMOTE_HOST }} - username: ${{ secrets.DEV_REMOTE_USER }} - key: ${{ secrets.DEV_REMOTE_SSH_KEY }} - source: "ontime-back/docker-compose.yml,ontime-back/docker-compose.dev.yml" - target: ${{ env.DEV_DEPLOY_DIR }} - strip_components: 1 - - - name: Pull image and restart dev containers - uses: appleboy/ssh-action@v1.0.3 - with: - host: ${{ secrets.DEV_REMOTE_HOST }} - username: ${{ secrets.DEV_REMOTE_USER }} - key: ${{ secrets.DEV_REMOTE_SSH_KEY }} - script: | - set -eu - - DEPLOY_DIR="${{ env.DEV_DEPLOY_DIR }}" - CONTAINER_NAME="ontime-dev-container" - - mkdir -p "$DEPLOY_DIR" - cd "$DEPLOY_DIR" - - umask 077 - cat > .env <<'EOF' - IMAGE_TAG=${{ env.IMAGE_TAG }} - BACKEND_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - BACKEND_CONTAINER_NAME=ontime-dev-container - BACKEND_HTTP_PORT=${{ secrets.DEV_BACKEND_HTTP_PORT || '8081' }} - BACKEND_MEMORY_LIMIT=${{ secrets.DEV_BACKEND_MEMORY_LIMIT || '768m' }} - BACKEND_CPU_LIMIT=${{ secrets.DEV_BACKEND_CPU_LIMIT || '1.0' }} - SERVER_PORT=8080 - SPRING_PROFILES_ACTIVE=dev - JAVA_TOOL_OPTIONS=-XX:InitialRAMPercentage=50.0 -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/./urandom - - MYSQL_DATABASE=${{ secrets.DEV_MYSQL_DATABASE || 'ontime_dev' }} - MYSQL_USER=${{ secrets.DEV_MYSQL_USER || 'ontime_dev' }} - MYSQL_PASSWORD=${{ secrets.DEV_MYSQL_PASSWORD || 'ontime_dev_password' }} - MYSQL_ROOT_PASSWORD=${{ secrets.DEV_MYSQL_ROOT_PASSWORD || 'ontime_dev_root_password' }} - - SPRING_APPLICATION_NAME=${{ secrets.DEV_SPRING_APPLICATION_NAME || 'ontime-back-dev' }} - SPRING_DATASOURCE_URL=${{ secrets.DEV_SPRING_DATASOURCE_URL || 'jdbc:mysql://mysql:3306/ontime_dev?useSSL=false&serverTimezone=Asia/Seoul&characterEncoding=UTF-8&allowPublicKeyRetrieval=true' }} - SPRING_DATASOURCE_USERNAME=${{ secrets.DEV_SPRING_DATASOURCE_USERNAME || secrets.DEV_MYSQL_USER || 'ontime_dev' }} - SPRING_DATASOURCE_PASSWORD=${{ secrets.DEV_SPRING_DATASOURCE_PASSWORD || secrets.DEV_MYSQL_PASSWORD || 'ontime_dev_password' }} - SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver - SPRING_JPA_HIBERNATE_DDL_AUTO=validate - - SPRING_FLYWAY_ENABLED=true - SPRING_FLYWAY_BASELINE_ON_MIGRATE=false - - JWT_SECRET_KEY=${{ secrets.DEV_JWT_SECRETKEY || 'dev_secret_key_for_ontime_back_remote_pc_development_1234567890' }} - JWT_ACCESS_EXPIRATION=${{ secrets.DEV_JWT_ACCESS_EXPIRATION || '3600000' }} - JWT_REFRESH_EXPIRATION=${{ secrets.DEV_JWT_REFRESH_EXPIRATION || '1209600000' }} - JWT_ACCESS_HEADER=${{ secrets.DEV_JWT_ACCESS_HEADER || 'Authorization' }} - JWT_REFRESH_HEADER=${{ secrets.DEV_JWT_REFRESH_HEADER || 'Authorization-refresh' }} - - GOOGLE_WEB_CLIENT_ID=${{ secrets.DEV_GOOGLE_WEB_CLIENT_ID || 'dev-google-web-client-id' }} - GOOGLE_APP_CLIENT_ID=${{ secrets.DEV_GOOGLE_APP_CLIENT_ID || 'dev-google-app-client-id' }} - - APPLE_CLIENT_ID=${{ secrets.DEV_APPLE_CLIENT_ID || 'dev-apple-client-id' }} - APPLE_TEAM_ID=${{ secrets.DEV_APPLE_TEAM_ID || 'dev-apple-team-id' }} - APPLE_LOGIN_KEY=${{ secrets.DEV_APPLE_LOGIN_KEY || 'dev-apple-key-id' }} - APPLE_PRIVATE_KEY_BASE64=${{ secrets.DEV_APPLE_PRIVATE_KEY_BASE64 }} - FEATURE_APPLE_LOGIN_ENABLED=${{ secrets.DEV_FEATURE_APPLE_LOGIN_ENABLED || 'false' }} - - FIREBASE_CREDENTIALS_BASE64=${{ secrets.DEV_FIREBASE_CREDENTIALS_BASE64 }} - EOF - - echo "${{ secrets.GHCR_READ_TOKEN }}" | sudo docker login ghcr.io -u "${{ secrets.GHCR_USERNAME }}" --password-stdin - - if sudo docker compose version >/dev/null 2>&1; then - COMPOSE="sudo docker compose" - else - COMPOSE="sudo docker-compose" - fi - - $COMPOSE -f docker-compose.yml -f docker-compose.dev.yml pull - $COMPOSE -f docker-compose.yml -f docker-compose.dev.yml up -d --remove-orphans - - HEALTHY=false - for attempt in $(seq 1 30); do - STATUS="$(sudo docker inspect -f '{{.State.Health.Status}}' "$CONTAINER_NAME" 2>/dev/null || true)" - if [ "$STATUS" = "healthy" ]; then - echo "Container is healthy." - HEALTHY=true - break - fi - echo "Waiting for healthy container status; current status: ${STATUS:-unknown}" - sleep 5 - done - - if [ "$HEALTHY" != "true" ]; then - sudo docker logs --tail=200 "$CONTAINER_NAME" || true - exit 1 - fi - - curl -fsS "http://127.0.0.1:${{ secrets.DEV_BACKEND_HTTP_PORT || '8081' }}/actuator/health/readiness" diff --git a/docs/deployment.md b/docs/deployment.md index 3b3b9b8..4d1f565 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -1,10 +1,8 @@ -# Deployment +# Production Deployment -This service deploys as an immutable Docker image published to GitHub Container Registry (GHCR). Runtime configuration is injected through `.env` files generated by GitHub Actions; private resource files are not copied into the image or bind-mounted from the host. +This service deploys as an immutable Docker image published to GitHub Container Registry (GHCR). Runtime configuration is injected through the EC2 `.env` file generated by GitHub Actions; private resource files are not copied into the image or bind-mounted from the host. -Production deploys to EC2 from `main`. Development deploys to a remote Ubuntu/Linux PC from `dev`. - -## Production GitHub Secrets +## Required GitHub Secrets Deployment access: @@ -87,7 +85,9 @@ base64 -i ontime-back/src/main/resources/key/AuthKey_743M7R5W3W.p8 | tr -d '\n' Push to the `main` branch, or run `.github/workflows/deploy.yml` manually, to deploy production. -The production workflow: +Pushes to `dev` run CI only. There is no dev-server deploy workflow in the one-EC2 plan. + +The workflow: 1. Builds `ontime-back/Dockerfile` from the `ontime-back/` context. 2. Pushes two GHCR tags: @@ -100,65 +100,6 @@ The production workflow: 7. Waits until the `ontime-container` Docker health status is `healthy`. 8. Installs Caddy if needed, configures `/etc/caddy/Caddyfile`, and verifies HTTPS for `ontime-back.duckdns.org`. -## Development Remote PC Deployment - -Push to the `dev` branch, or run `.github/workflows/deploy-dev.yml` manually, to deploy the development backend to the remote PC. - -The development workflow: - -1. Builds `ontime-back/Dockerfile` from the `ontime-back/` context. -2. Pushes two GHCR tags: - - `ghcr.io/devkor-github/ontime-back:dev-` - - `ghcr.io/devkor-github/ontime-back:dev-latest` -3. Uploads `docker-compose.yml` and `docker-compose.dev.yml` to the remote PC. -4. Writes a development `.env` from GitHub secrets and safe dev defaults. -5. Runs `docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --remove-orphans`. -6. Starts MySQL as a private Docker Compose service with persistent volume `ontime-dev-mysql-data`. -7. Waits until the `ontime-dev-container` Docker health status is `healthy`. - -Required development secrets: - -- `DEV_REMOTE_HOST` -- `DEV_REMOTE_USER` -- `DEV_REMOTE_SSH_KEY` -- `GHCR_USERNAME` -- `GHCR_READ_TOKEN` - -Optional development secrets: - -- `DEV_DEPLOY_DIR` (defaults to `/home//OnTime-back-dev`) -- `DEV_BACKEND_HTTP_PORT` (defaults to `8081`) -- `DEV_BACKEND_MEMORY_LIMIT` (defaults to `768m`) -- `DEV_BACKEND_CPU_LIMIT` (defaults to `1.0`) -- `DEV_MYSQL_DATABASE` (defaults to `ontime_dev`) -- `DEV_MYSQL_USER` (defaults to `ontime_dev`) -- `DEV_MYSQL_PASSWORD` (defaults to `ontime_dev_password`) -- `DEV_MYSQL_ROOT_PASSWORD` (defaults to `ontime_dev_root_password`) -- `DEV_SPRING_APPLICATION_NAME` (defaults to `ontime-back-dev`) -- `DEV_SPRING_DATASOURCE_URL` (defaults to the Compose MySQL service) -- `DEV_SPRING_DATASOURCE_USERNAME` (defaults to the dev MySQL user) -- `DEV_SPRING_DATASOURCE_PASSWORD` (defaults to the dev MySQL password) -- `DEV_JWT_SECRETKEY` -- `DEV_JWT_ACCESS_EXPIRATION` -- `DEV_JWT_REFRESH_EXPIRATION` -- `DEV_JWT_ACCESS_HEADER` -- `DEV_JWT_REFRESH_HEADER` -- `DEV_GOOGLE_WEB_CLIENT_ID` -- `DEV_GOOGLE_APP_CLIENT_ID` -- `DEV_APPLE_CLIENT_ID` -- `DEV_APPLE_TEAM_ID` -- `DEV_APPLE_LOGIN_KEY` -- `DEV_APPLE_PRIVATE_KEY_BASE64` -- `DEV_FEATURE_APPLE_LOGIN_ENABLED` (defaults to `false`) -- `DEV_FIREBASE_CREDENTIALS_BASE64` - -Remote PC prerequisites: - -- Ubuntu/Linux host with normal SSH access from GitHub Actions. -- Docker and the Docker Compose plugin installed. -- Inbound firewall access for the backend HTTP port, default `8081`. -- No public inbound MySQL port is required; MySQL stays inside the Docker network. - ## HTTPS Prerequisites Before running the production deploy, configure AWS and DNS: @@ -188,16 +129,6 @@ curl -fsS https://ontime-back.duckdns.org/actuator/health/readiness nc -zv ontime-prod.cpoeguokwaq5.ap-northeast-2.rds.amazonaws.com 3306 ``` -Manual checks on the remote development PC: - -```bash -cd /home//OnTime-back-dev -sudo docker compose -f docker-compose.yml -f docker-compose.dev.yml ps -sudo docker inspect -f '{{.State.Health.Status}}' ontime-dev-container -sudo docker logs --tail=200 ontime-dev-container -curl -fsS http://:8081/actuator/health/readiness -``` - ## Rollback Every deploy is tagged by commit SHA. To roll back, set `IMAGE_TAG` in `/home/ubuntu/OnTime-back/.env` to the previous known-good SHA, then restart from the existing Compose file: diff --git a/docs/git-workflow.md b/docs/git-workflow.md index fc7d6f6..1a0c6d4 100644 --- a/docs/git-workflow.md +++ b/docs/git-workflow.md @@ -1,6 +1,6 @@ -# Git Workflow And Deployment Strategy +# Git Workflow And Production Deployment Strategy -This document describes the recommended Git strategy for OnTime-back with one production EC2 server, one private production RDS instance, and one remote-PC development server. +This document describes the recommended Git strategy for OnTime-back with one production EC2 server and one private production RDS instance. ## Goals @@ -8,7 +8,7 @@ This document describes the recommended Git strategy for OnTime-back with one pr - Make every server deployment traceable to a Git branch and commit. - Avoid using deployment branches as places where product code diverges. - Keep feature branches short-lived and easy to review. -- Use `dev` as the integration branch and remote-PC development deploy source. +- Keep `dev` as an integration branch only; it must not deploy a long-running dev backend. ## Branch Model @@ -25,7 +25,7 @@ Branch responsibilities: | Branch | Purpose | Deployment | | --- | --- | --- | | `main` | Production-ready code and source of truth | Production server | -| `dev` | Integrated code for QA, frontend/mobile testing, and pre-release validation | Remote PC development server | +| `dev` | Integrated code for QA, frontend/mobile testing, and pre-release validation | No direct deployment | | `feature/*` | New feature work | No direct deployment | | `fix/*` | Bug fixes | No direct deployment | | `chore/*` | Maintenance, docs, config, CI changes | No direct deployment | @@ -59,7 +59,7 @@ feature/* -> dev 5. After review, merge into `dev`. -6. Validate the integrated code on the remote PC development backend. +6. Validate the integrated code without running a long-lived dev backend on EC2. 7. When the release candidate is ready, open a pull request from `dev` into `main`. @@ -84,7 +84,6 @@ Use branch-based CI and production deployment: ```text pull_request to dev/main -> test workflow -push to dev -> development environment -> remote PC development server push to main -> production environment -> production server ``` @@ -92,7 +91,6 @@ Recommended GitHub environments: | Environment | Source Branch | Server | Approval | | --- | --- | --- | --- | -| `development` | `dev` | Remote PC development server | Optional | | `production` | `main` | Production server | Manual approval recommended | ## CI/CD Workflow @@ -101,7 +99,6 @@ Recommended simple setup: ```text .github/workflows/test.yml -.github/workflows/deploy-dev.yml .github/workflows/deploy.yml ``` @@ -110,9 +107,9 @@ Expected triggers: ```text pull_request to dev -> run tests pull_request to main -> run tests -push to dev -> deploy to remote PC development server +push to dev -> no deployment push to main -> deploy to production server -workflow_dispatch -> allow manual redeploy for each deploy workflow +workflow_dispatch -> allow manual redeploy or rollback support ``` Production deploy should use production secrets only: @@ -126,17 +123,7 @@ SPRING_DATASOURCE_USERNAME SPRING_DATASOURCE_PASSWORD ``` -Development deploy should use development secrets only: - -```text -DEV_REMOTE_HOST -DEV_REMOTE_USER -DEV_REMOTE_SSH_KEY -GHCR_USERNAME -GHCR_READ_TOKEN -``` - -Optional `DEV_*` secrets can override the default dev deploy directory, HTTP port, MySQL credentials, and non-production OAuth/Firebase settings. +Do not add `DEV_*` deployment secrets or a dev-server workflow unless the infrastructure plan changes deliberately. ## Branch Protection @@ -184,7 +171,7 @@ The current repository has `main` and `deploy` as separate long-lived branches. ```text deploy branch -> retired main branch -> production -dev branch -> integration and remote PC development deployment +dev branch -> integration and CI only ``` Recommended migration sequence: @@ -202,20 +189,19 @@ git push origin dev ``` 5. Change production deployment to trigger from `main`. -6. Ensure `dev` deploys only to the remote PC development environment. +6. Ensure there is no workflow that deploys from `dev`. 7. Update GitHub production environment secrets. -8. Update GitHub development environment secrets for the remote PC. -9. Protect `main` and `dev`. -10. Stop using `deploy` for new work. -11. Delete or archive stale merged feature branches after confirming they are no longer needed. +8. Protect `main` and `dev`. +9. Stop using `deploy` for new work. +10. Delete or archive stale merged feature branches after confirming they are no longer needed. ## Practical Rules - New work branches from `dev`. - Normal PR target is `dev`. - Release PR target is `main`. -- `dev` deploys only to the remote PC development server. - Production deploys only from `main`. +- `dev` runs CI only and does not deploy. - Do not commit directly to `main`. - Do not commit directly to `dev` unless it is an emergency coordination fix. - Delete feature branches after merge. diff --git a/ontime-back/docker-compose.dev.yml b/ontime-back/docker-compose.dev.yml deleted file mode 100644 index 1d2bb07..0000000 --- a/ontime-back/docker-compose.dev.yml +++ /dev/null @@ -1,37 +0,0 @@ -services: - backend: - container_name: "${BACKEND_CONTAINER_NAME:-ontime-dev-container}" - depends_on: - mysql: - condition: service_healthy - - mysql: - image: mysql:8.0 - container_name: ontime-dev-mysql - environment: - MYSQL_DATABASE: "${MYSQL_DATABASE:-ontime_dev}" - MYSQL_USER: "${MYSQL_USER:-ontime_dev}" - MYSQL_PASSWORD: "${MYSQL_PASSWORD:-ontime_dev_password}" - MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD:-ontime_dev_root_password}" - TZ: Asia/Seoul - volumes: - - ontime-dev-mysql-data:/var/lib/mysql - restart: unless-stopped - command: - - --character-set-server=utf8mb4 - - --collation-server=utf8mb4_unicode_ci - - --default-time-zone=+09:00 - healthcheck: - test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -u root --password=$$MYSQL_ROOT_PASSWORD"] - interval: 10s - timeout: 5s - retries: 10 - start_period: 30s - logging: - driver: json-file - options: - max-size: "10m" - max-file: "3" - -volumes: - ontime-dev-mysql-data: diff --git a/ontime-back/src/main/resources/application-dev.properties b/ontime-back/src/main/resources/application-dev.properties deleted file mode 100644 index 953a8bd..0000000 --- a/ontime-back/src/main/resources/application-dev.properties +++ /dev/null @@ -1,53 +0,0 @@ -# Database Configuration -spring.datasource.url=${SPRING_DATASOURCE_URL:jdbc:mysql://mysql:3306/ontime_dev?useSSL=false&serverTimezone=Asia/Seoul&characterEncoding=UTF-8&allowPublicKeyRetrieval=true} -spring.datasource.username=${SPRING_DATASOURCE_USERNAME:ontime_dev} -spring.datasource.password=${SPRING_DATASOURCE_PASSWORD:ontime_dev_password} -spring.datasource.driver-class-name=${SPRING_DATASOURCE_DRIVER_CLASS_NAME:com.mysql.cj.jdbc.Driver} - -# JPA / Hibernate -spring.jpa.database=mysql -spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect -spring.jpa.hibernate.ddl-auto=${SPRING_JPA_HIBERNATE_DDL_AUTO:validate} -spring.jpa.show-sql=true -spring.jpa.properties.hibernate.format_sql=true - -# Flyway -spring.flyway.enabled=${SPRING_FLYWAY_ENABLED:true} -spring.flyway.baseline-on-migrate=${SPRING_FLYWAY_BASELINE_ON_MIGRATE:false} - -# JWT Configuration -jwt.secret.key=${JWT_SECRET_KEY:dev_secret_key_for_ontime_back_remote_pc_development_1234567890} -jwt.access.expiration=${JWT_ACCESS_EXPIRATION:3600000} -jwt.refresh.expiration=${JWT_REFRESH_EXPIRATION:1209600000} -jwt.access.header=${JWT_ACCESS_HEADER:Authorization} -jwt.refresh.header=${JWT_REFRESH_HEADER:Authorization-refresh} - -# Google OAuth -google.web.client-id=${GOOGLE_WEB_CLIENT_ID:dev-google-web-client-id} -google.app.client-id=${GOOGLE_APP_CLIENT_ID:dev-google-app-client-id} - -# Apple OAuth -apple.client.id=${APPLE_CLIENT_ID:dev-apple-client-id} -apple.team.id=${APPLE_TEAM_ID:dev-apple-team-id} -apple.login.key=${APPLE_LOGIN_KEY:dev-apple-key-id} -apple.client.secret=${APPLE_CLIENT_SECRET:} -apple.private-key.base64=${APPLE_PRIVATE_KEY_BASE64:} - -# Firebase -firebase.credentials.base64=${FIREBASE_CREDENTIALS_BASE64:} - -# Logging -logging.level.root=INFO -logging.level.devkor.ontime_back=DEBUG - -# Feature flags -feature.apple-login.enabled=${FEATURE_APPLE_LOGIN_ENABLED:false} - -# Actuator -management.endpoint.health.probes.enabled=true -management.endpoints.web.exposure.include=health -management.endpoint.health.show-details=always -management.health.readinessstate.enabled=true -management.health.livenessstate.enabled=true -server.shutdown=graceful -spring.lifecycle.timeout-per-shutdown-phase=30s