From 64bfb6fb569fe09c9ffa5e413fa22effa517ac5c Mon Sep 17 00:00:00 2001 From: jjoonleo Date: Fri, 8 May 2026 03:10:57 +0900 Subject: [PATCH] Remove dev server deployment workflow --- .github/workflows/deploy-dev.yml | 185 ----------------------------- docs/deployment.md | 10 +- docs/git-workflow.md | 67 ++++------- ontime-back/docs/deployment/ec2.md | 2 +- 4 files changed, 27 insertions(+), 237 deletions(-) delete mode 100644 .github/workflows/deploy-dev.yml diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml deleted file mode 100644 index 7eca883..0000000 --- a/.github/workflows/deploy-dev.yml +++ /dev/null @@ -1,185 +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-dev-ec2: - needs: build-and-push - runs-on: ubuntu-latest - environment: development - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Upload compose file to dev EC2 - uses: appleboy/scp-action@v0.1.7 - with: - host: ${{ secrets.DEV_EC2_HOST }} - username: ${{ secrets.DEV_EC2_USER }} - key: ${{ secrets.DEV_EC2_SSH_KEY }} - source: "ontime-back/docker-compose.yml" - target: "/home/ubuntu/OnTime-back-dev" - strip_components: 1 - - - name: Pull image and restart dev container - uses: appleboy/ssh-action@v1.0.3 - with: - host: ${{ secrets.DEV_EC2_HOST }} - username: ${{ secrets.DEV_EC2_USER }} - key: ${{ secrets.DEV_EC2_SSH_KEY }} - script: | - set -eu - - DEPLOY_DIR="/home/ubuntu/OnTime-back-dev" - 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' }} - SERVER_PORT=8080 - SPRING_PROFILES_ACTIVE=prod - JAVA_TOOL_OPTIONS=-XX:InitialRAMPercentage=50.0 -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/./urandom - - SPRING_APPLICATION_NAME=${{ secrets.DEV_SPRING_APPLICATION_NAME }} - SPRING_DATASOURCE_URL=${{ secrets.DEV_SPRING_DATASOURCE_URL }} - SPRING_DATASOURCE_USERNAME=${{ secrets.DEV_SPRING_DATASOURCE_USERNAME }} - SPRING_DATASOURCE_PASSWORD=${{ secrets.DEV_SPRING_DATASOURCE_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 }} - JWT_ACCESS_EXPIRATION=${{ secrets.DEV_JWT_ACCESS_EXPIRATION }} - JWT_REFRESH_EXPIRATION=${{ secrets.DEV_JWT_REFRESH_EXPIRATION }} - JWT_ACCESS_HEADER=${{ secrets.DEV_JWT_ACCESS_HEADER }} - JWT_REFRESH_HEADER=${{ secrets.DEV_JWT_REFRESH_HEADER }} - - GOOGLE_WEB_CLIENT_ID=${{ secrets.DEV_GOOGLE_WEB_CLIENT_ID }} - GOOGLE_APP_CLIENT_ID=${{ secrets.DEV_GOOGLE_APP_CLIENT_ID }} - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT_SECRET=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT_SECRET }} - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_SCOPE=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_SCOPE }} - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_REDIRECT_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_REDIRECT_URI }} - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_AUTHORIZATION_GRANT_TYPE=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_AUTHORIZATION_GRANT_TYPE }} - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT_NAME=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT_NAME }} - SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_AUTHORIZATION_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_AUTHORIZATION_URI }} - SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_TOKEN_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_TOKEN_URI }} - SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_USER_INFO_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_USER_INFO_URI }} - SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_USER_NAME_ATTRIBUTE=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_USER_NAME_ATTRIBUTE }} - - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID }} - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE }} - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI }} - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE }} - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME }} - SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_AUTHORIZATION_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_AUTHORIZATION_URI }} - SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_TOKEN_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_TOKEN_URI }} - SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_USER_INFO_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_USER_INFO_URI }} - SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE }} - - APPLE_CLIENT_ID=${{ secrets.DEV_APPLE_CLIENT_ID }} - APPLE_TEAM_ID=${{ secrets.DEV_APPLE_TEAM_ID }} - APPLE_LOGIN_KEY=${{ secrets.DEV_APPLE_LOGIN_KEY }} - APPLE_PRIVATE_KEY_BASE64=${{ secrets.DEV_APPLE_PRIVATE_KEY_BASE64 }} - FEATURE_APPLE_LOGIN_ENABLED=${{ secrets.DEV_FEATURE_APPLE_LOGIN_ENABLED || 'true' }} - - FIREBASE_CREDENTIALS_BASE64=${{ secrets.DEV_FIREBASE_CREDENTIALS_BASE64 }} - EOF - - fail_deploy() { - echo "Unsafe development database configuration: $1" >&2 - exit 1 - } - - get_env_value() { - grep -E "^$1=" .env | tail -n 1 | cut -d= -f2- - } - - DB_URL="$(get_env_value SPRING_DATASOURCE_URL)" - DB_USERNAME="$(get_env_value SPRING_DATASOURCE_USERNAME)" - DB_PASSWORD="$(get_env_value SPRING_DATASOURCE_PASSWORD)" - DDL_AUTO="$(get_env_value SPRING_JPA_HIBERNATE_DDL_AUTO)" - FLYWAY_BASELINE="$(get_env_value SPRING_FLYWAY_BASELINE_ON_MIGRATE)" - NORMALIZED_DB_USERNAME="$(printf '%s' "$DB_USERNAME" | tr '[:upper:]' '[:lower:]')" - - [ -n "$DB_URL" ] || fail_deploy "SPRING_DATASOURCE_URL is required." - [ -n "$DB_USERNAME" ] || fail_deploy "SPRING_DATASOURCE_USERNAME is required." - [ -n "$DB_PASSWORD" ] || fail_deploy "SPRING_DATASOURCE_PASSWORD is required." - [ "$NORMALIZED_DB_USERNAME" != "root" ] || fail_deploy "SPRING_DATASOURCE_USERNAME must not be root." - [ "$DDL_AUTO" = "validate" ] || fail_deploy "SPRING_JPA_HIBERNATE_DDL_AUTO must be validate." - [ "$FLYWAY_BASELINE" = "false" ] || fail_deploy "SPRING_FLYWAY_BASELINE_ON_MIGRATE must be false." - - 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 - sudo docker compose pull - sudo docker compose up -d --remove-orphans - else - sudo docker-compose pull - sudo docker-compose up -d --remove-orphans - fi - - 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." - exit 0 - fi - echo "Waiting for healthy container status; current status: ${STATUS:-unknown}" - sleep 5 - done - - sudo docker logs --tail=200 "$CONTAINER_NAME" || true - exit 1 diff --git a/docs/deployment.md b/docs/deployment.md index 4ee20dd..a34dcf2 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -71,14 +71,6 @@ Firebase: - `FIREBASE_CREDENTIALS_BASE64` -Development server: - -- `DEV_EC2_HOST` -- `DEV_EC2_USER` -- `DEV_EC2_SSH_KEY` -- `DEV_BACKEND_HTTP_PORT` (optional, defaults to `8081`) -- Runtime secrets matching the production names with a `DEV_` prefix, for example `DEV_SPRING_DATASOURCE_URL`, `DEV_JWT_SECRETKEY`, and `DEV_FIREBASE_CREDENTIALS_BASE64` - Set the base64 secrets from the ignored local credential files: ```bash @@ -93,7 +85,7 @@ 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. -Push to the `dev` branch, or run `.github/workflows/deploy-dev.yml` manually, to deploy the development server. +Pushes to `dev` run CI only. There is no dev-server deploy workflow in the one-EC2 plan. The workflow: diff --git a/docs/git-workflow.md b/docs/git-workflow.md index d5b1c06..1a0c6d4 100644 --- a/docs/git-workflow.md +++ b/docs/git-workflow.md @@ -1,14 +1,14 @@ -# Git Workflow And Deployment Strategy +# Git Workflow And Production Deployment Strategy -This document describes the recommended Git strategy for OnTime-back when running both a development server and a production server. +This document describes the recommended Git strategy for OnTime-back with one production EC2 server and one private production RDS instance. ## Goals - Keep one clear production source of truth. -- Support a separate development server for integration testing. - 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. +- Keep `dev` as an integration branch only; it must not deploy a long-running dev backend. ## Branch Model @@ -16,7 +16,7 @@ Use a lightweight environment-branch workflow: ```text feature/*, fix/*, chore/* -> short-lived work branches -dev -> development server source +dev -> integration branch for CI and review main -> production server source ``` @@ -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 | 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,21 +59,19 @@ feature/* -> dev 5. After review, merge into `dev`. -6. GitHub Actions deploys the updated `dev` branch to the development server. +6. Validate the integrated code without running a long-lived dev backend on EC2. -7. Validate the change using the development server. - -8. When the release candidate is ready, open a pull request from `dev` into `main`. +7. When the release candidate is ready, open a pull request from `dev` into `main`. ```text dev -> main ``` -9. After review and CI pass, merge into `main`. +8. After review and CI pass, merge into `main`. -10. GitHub Actions deploys `main` to the production server. +9. GitHub Actions deploys `main` to the production server. -11. Tag the production release. +10. Tag the production release. ```bash git tag prod-YYYY-MM-DD @@ -82,10 +80,10 @@ git push origin prod-YYYY-MM-DD ## Server Mapping -Use branch-based deployments with separate GitHub Actions environments: +Use branch-based CI and production deployment: ```text -push to dev -> development environment -> dev server +pull_request to dev/main -> test workflow push to main -> production environment -> production server ``` @@ -93,19 +91,15 @@ Recommended GitHub environments: | Environment | Source Branch | Server | Approval | | --- | --- | --- | --- | -| `development` | `dev` | Dev server | Usually automatic | | `production` | `main` | Production server | Manual approval recommended | ## CI/CD Workflow -Use either two workflows or one branch-aware workflow. - Recommended simple setup: ```text .github/workflows/test.yml -.github/workflows/deploy-dev.yml -.github/workflows/deploy-prod.yml +.github/workflows/deploy.yml ``` Expected triggers: @@ -113,34 +107,23 @@ Expected triggers: ```text pull_request to dev -> run tests pull_request to main -> run tests -push to dev -> deploy to dev server +push to dev -> no deployment push to main -> deploy to production server workflow_dispatch -> allow manual redeploy or rollback support ``` -Development deploy should use development secrets only: - -```text -DEV_EC2_HOST -DEV_EC2_USER -DEV_EC2_SSH_KEY -DEV_DATASOURCE_URL -DEV_DATASOURCE_USERNAME -DEV_DATASOURCE_PASSWORD -``` - Production deploy should use production secrets only: ```text -PROD_EC2_HOST -PROD_EC2_USER -PROD_EC2_SSH_KEY -PROD_DATASOURCE_URL -PROD_DATASOURCE_USERNAME -PROD_DATASOURCE_PASSWORD +EC2_HOST +EC2_USER +EC2_SSH_KEY +SPRING_DATASOURCE_URL +SPRING_DATASOURCE_USERNAME +SPRING_DATASOURCE_PASSWORD ``` -Do not share databases, Firebase credentials, OAuth redirect URIs, or private keys between development and production unless there is a deliberate reason. +Do not add `DEV_*` deployment secrets or a dev-server workflow unless the infrastructure plan changes deliberately. ## Branch Protection @@ -188,7 +171,7 @@ The current repository has `main` and `deploy` as separate long-lived branches. ```text deploy branch -> retired main branch -> production -dev branch -> development server +dev branch -> integration and CI only ``` Recommended migration sequence: @@ -206,8 +189,8 @@ git push origin dev ``` 5. Change production deployment to trigger from `main`. -6. Add development deployment to trigger from `dev`. -7. Update GitHub environment secrets for `development` and `production`. +6. Ensure there is no workflow that deploys from `dev`. +7. Update GitHub production environment secrets. 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. @@ -218,7 +201,7 @@ git push origin dev - Normal PR target is `dev`. - Release PR target is `main`. - Production deploys only from `main`. -- Development deploys only from `dev`. +- `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/docs/deployment/ec2.md b/ontime-back/docs/deployment/ec2.md index df6ed5b..1ae690e 100644 --- a/ontime-back/docs/deployment/ec2.md +++ b/ontime-back/docs/deployment/ec2.md @@ -6,7 +6,7 @@ This service deploys to Amazon EC2 through `.github/workflows/deploy.yml`. 1. Make sure the EC2 instance has Docker installed and the security group allows inbound traffic for the service port, currently `8080`. 2. Add the required GitHub Actions secrets listed below. -3. Run the `Deploy` workflow manually from GitHub Actions, or push to the `deploy` branch. +3. Run the `Deploy` workflow manually from GitHub Actions, or push to the `main` branch. The workflow builds a Docker image, pushes it to GHCR, uploads `docker-compose.yml` to `/home/ubuntu/OnTime-back`, writes a production `.env` from GitHub Secrets, verifies private RDS connectivity, and restarts Docker Compose on the EC2 instance.