Skip to content

Commit d1b9462

Browse files
committed
chore: align git workflow and deployments
1 parent 244b967 commit d1b9462

5 files changed

Lines changed: 428 additions & 48 deletions

File tree

.github/workflows/deploy-dev.yml

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
name: Deploy Dev
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- dev
8+
9+
permissions:
10+
contents: read
11+
packages: write
12+
13+
concurrency:
14+
group: deploy-development
15+
cancel-in-progress: true
16+
17+
env:
18+
REGISTRY: ghcr.io
19+
IMAGE_NAME: devkor-github/ontime-back
20+
IMAGE_TAG: dev-${{ github.sha }}
21+
22+
jobs:
23+
build-and-push:
24+
runs-on: ubuntu-latest
25+
steps:
26+
- name: Checkout repository
27+
uses: actions/checkout@v4
28+
29+
- name: Set up Docker Buildx
30+
uses: docker/setup-buildx-action@v3
31+
32+
- name: Log in to GHCR
33+
uses: docker/login-action@v3
34+
with:
35+
registry: ${{ env.REGISTRY }}
36+
username: ${{ github.actor }}
37+
password: ${{ secrets.GITHUB_TOKEN }}
38+
39+
- name: Build and push image
40+
uses: docker/build-push-action@v6
41+
with:
42+
context: ./ontime-back
43+
file: ./ontime-back/Dockerfile
44+
push: true
45+
tags: |
46+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}
47+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:dev-latest
48+
cache-from: type=gha
49+
cache-to: type=gha,mode=max
50+
51+
deploy-to-dev-ec2:
52+
needs: build-and-push
53+
runs-on: ubuntu-latest
54+
environment: development
55+
steps:
56+
- name: Checkout repository
57+
uses: actions/checkout@v4
58+
59+
- name: Upload compose file to dev EC2
60+
uses: appleboy/scp-action@v0.1.7
61+
with:
62+
host: ${{ secrets.DEV_EC2_HOST }}
63+
username: ${{ secrets.DEV_EC2_USER }}
64+
key: ${{ secrets.DEV_EC2_SSH_KEY }}
65+
source: "ontime-back/docker-compose.yml"
66+
target: "/home/ubuntu/OnTime-back-dev"
67+
strip_components: 1
68+
69+
- name: Pull image and restart dev container
70+
uses: appleboy/ssh-action@v1.0.3
71+
with:
72+
host: ${{ secrets.DEV_EC2_HOST }}
73+
username: ${{ secrets.DEV_EC2_USER }}
74+
key: ${{ secrets.DEV_EC2_SSH_KEY }}
75+
script: |
76+
set -eu
77+
78+
DEPLOY_DIR="/home/ubuntu/OnTime-back-dev"
79+
CONTAINER_NAME="ontime-dev-container"
80+
81+
mkdir -p "$DEPLOY_DIR"
82+
cd "$DEPLOY_DIR"
83+
84+
umask 077
85+
cat > .env <<'EOF'
86+
IMAGE_TAG=${{ env.IMAGE_TAG }}
87+
BACKEND_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
88+
BACKEND_CONTAINER_NAME=ontime-dev-container
89+
BACKEND_HTTP_PORT=${{ secrets.DEV_BACKEND_HTTP_PORT || '8081' }}
90+
SERVER_PORT=8080
91+
SPRING_PROFILES_ACTIVE=prod
92+
JAVA_TOOL_OPTIONS=-XX:InitialRAMPercentage=50.0 -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/./urandom
93+
94+
SPRING_APPLICATION_NAME=${{ secrets.DEV_SPRING_APPLICATION_NAME }}
95+
SPRING_DATASOURCE_URL=${{ secrets.DEV_SPRING_DATASOURCE_URL }}
96+
SPRING_DATASOURCE_USERNAME=${{ secrets.DEV_SPRING_DATASOURCE_USERNAME }}
97+
SPRING_DATASOURCE_PASSWORD=${{ secrets.DEV_SPRING_DATASOURCE_PASSWORD }}
98+
SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver
99+
SPRING_JPA_HIBERNATE_DDL_AUTO=validate
100+
101+
SPRING_FLYWAY_ENABLED=true
102+
SPRING_FLYWAY_BASELINE_ON_MIGRATE=false
103+
104+
JWT_SECRET_KEY=${{ secrets.DEV_JWT_SECRETKEY }}
105+
JWT_ACCESS_EXPIRATION=${{ secrets.DEV_JWT_ACCESS_EXPIRATION }}
106+
JWT_REFRESH_EXPIRATION=${{ secrets.DEV_JWT_REFRESH_EXPIRATION }}
107+
JWT_ACCESS_HEADER=${{ secrets.DEV_JWT_ACCESS_HEADER }}
108+
JWT_REFRESH_HEADER=${{ secrets.DEV_JWT_REFRESH_HEADER }}
109+
110+
GOOGLE_WEB_CLIENT_ID=${{ secrets.DEV_GOOGLE_WEB_CLIENT_ID }}
111+
GOOGLE_APP_CLIENT_ID=${{ secrets.DEV_GOOGLE_APP_CLIENT_ID }}
112+
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT_SECRET=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT_SECRET }}
113+
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_SCOPE=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_SCOPE }}
114+
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_REDIRECT_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_REDIRECT_URI }}
115+
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_AUTHORIZATION_GRANT_TYPE=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_AUTHORIZATION_GRANT_TYPE }}
116+
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT_NAME=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_GOOGLE_CLIENT_NAME }}
117+
SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_AUTHORIZATION_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_AUTHORIZATION_URI }}
118+
SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_TOKEN_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_TOKEN_URI }}
119+
SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_USER_INFO_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_USER_INFO_URI }}
120+
SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_USER_NAME_ATTRIBUTE=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_GOOGLE_USER_NAME_ATTRIBUTE }}
121+
122+
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_ID }}
123+
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_SCOPE }}
124+
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT_URI }}
125+
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_AUTHORIZATION_GRANT_TYPE }}
126+
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT_NAME }}
127+
SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_AUTHORIZATION_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_AUTHORIZATION_URI }}
128+
SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_TOKEN_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_TOKEN_URI }}
129+
SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_USER_INFO_URI=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_USER_INFO_URI }}
130+
SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE=${{ secrets.DEV_SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_KAKAO_USER_NAME_ATTRIBUTE }}
131+
132+
APPLE_CLIENT_ID=${{ secrets.DEV_APPLE_CLIENT_ID }}
133+
APPLE_TEAM_ID=${{ secrets.DEV_APPLE_TEAM_ID }}
134+
APPLE_LOGIN_KEY=${{ secrets.DEV_APPLE_LOGIN_KEY }}
135+
APPLE_PRIVATE_KEY_BASE64=${{ secrets.DEV_APPLE_PRIVATE_KEY_BASE64 }}
136+
FEATURE_APPLE_LOGIN_ENABLED=${{ secrets.DEV_FEATURE_APPLE_LOGIN_ENABLED || 'true' }}
137+
138+
FIREBASE_CREDENTIALS_BASE64=${{ secrets.DEV_FIREBASE_CREDENTIALS_BASE64 }}
139+
EOF
140+
141+
fail_deploy() {
142+
echo "Unsafe development database configuration: $1" >&2
143+
exit 1
144+
}
145+
146+
get_env_value() {
147+
grep -E "^$1=" .env | tail -n 1 | cut -d= -f2-
148+
}
149+
150+
DB_URL="$(get_env_value SPRING_DATASOURCE_URL)"
151+
DB_USERNAME="$(get_env_value SPRING_DATASOURCE_USERNAME)"
152+
DB_PASSWORD="$(get_env_value SPRING_DATASOURCE_PASSWORD)"
153+
DDL_AUTO="$(get_env_value SPRING_JPA_HIBERNATE_DDL_AUTO)"
154+
FLYWAY_BASELINE="$(get_env_value SPRING_FLYWAY_BASELINE_ON_MIGRATE)"
155+
NORMALIZED_DB_USERNAME="$(printf '%s' "$DB_USERNAME" | tr '[:upper:]' '[:lower:]')"
156+
157+
[ -n "$DB_URL" ] || fail_deploy "SPRING_DATASOURCE_URL is required."
158+
[ -n "$DB_USERNAME" ] || fail_deploy "SPRING_DATASOURCE_USERNAME is required."
159+
[ -n "$DB_PASSWORD" ] || fail_deploy "SPRING_DATASOURCE_PASSWORD is required."
160+
[ "$NORMALIZED_DB_USERNAME" != "root" ] || fail_deploy "SPRING_DATASOURCE_USERNAME must not be root."
161+
[ "$DDL_AUTO" = "validate" ] || fail_deploy "SPRING_JPA_HIBERNATE_DDL_AUTO must be validate."
162+
[ "$FLYWAY_BASELINE" = "false" ] || fail_deploy "SPRING_FLYWAY_BASELINE_ON_MIGRATE must be false."
163+
164+
echo "${{ secrets.GHCR_READ_TOKEN }}" | sudo docker login ghcr.io -u "${{ secrets.GHCR_USERNAME }}" --password-stdin
165+
166+
if sudo docker compose version >/dev/null 2>&1; then
167+
sudo docker compose pull
168+
sudo docker compose up -d --remove-orphans
169+
else
170+
sudo docker-compose pull
171+
sudo docker-compose up -d --remove-orphans
172+
fi
173+
174+
for attempt in $(seq 1 30); do
175+
STATUS="$(sudo docker inspect -f '{{.State.Health.Status}}' "$CONTAINER_NAME" 2>/dev/null || true)"
176+
if [ "$STATUS" = "healthy" ]; then
177+
echo "Container is healthy."
178+
exit 0
179+
fi
180+
echo "Waiting for healthy container status; current status: ${STATUS:-unknown}"
181+
sleep 5
182+
done
183+
184+
sudo docker logs --tail=200 "$CONTAINER_NAME" || true
185+
exit 1

.github/workflows/deploy.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
name: Deploy
22

33
on:
4+
workflow_dispatch:
45
push:
56
branches:
6-
- deploy
7+
- main
78

89
permissions:
910
contents: read
@@ -50,6 +51,7 @@ jobs:
5051
deploy-to-ec2:
5152
needs: build-and-push
5253
runs-on: ubuntu-latest
54+
environment: production
5355
steps:
5456
- name: Checkout repository
5557
uses: actions/checkout@v4

.github/workflows/test.yml

Lines changed: 3 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
# PR이 올라왔을 때 자동으로 테스트코드를 실행하고 하나라도 Fail했을 시, PR을 Close하고 실패 코멘트를 달고 성공시에는 머지를 가능케하는 파이프라인.
1+
# PR이 올라왔을 때 자동으로 테스트코드를 실행하고 실패 시 머지를 막는 파이프라인.
22
name: PR Test
33

44
on:
55
pull_request:
66
branches:
7-
- main # main 브랜치로 머지되는 PR에서 실행
7+
- dev
8+
- main
89

910
jobs:
1011
test:
1112
runs-on: ubuntu-latest
12-
outputs:
13-
test_result: ${{ steps.run-tests.outcome }} # 테스트 결과를 output으로 저장
1413

1514
services:
1615
mysql:
@@ -64,8 +63,6 @@ jobs:
6463
6564
# 4. Gradle 빌드 & JUnit 테스트 실행
6665
- name: Run Tests with Gradle
67-
id: run-tests # 실행 결과를 output으로 저장할 id 추가
68-
continue-on-error: true
6966
env:
7067
SPRING_PROFILES_ACTIVE: test
7168
run: |
@@ -76,43 +73,3 @@ jobs:
7673
run: |
7774
echo "Checking Flyway migration history..."
7875
mysql -h 127.0.0.1 -u test_user --password=test_password -e "SELECT * FROM test_db.flyway_schema_history;"
79-
80-
81-
handle-failure:
82-
needs: test
83-
if: needs.test.outputs.test_result == 'failure' # test job의 output 값이 실패(failure)일 때 실행
84-
runs-on: ubuntu-latest
85-
permissions:
86-
pull-requests: write
87-
issues: write
88-
steps:
89-
- name: Close PR
90-
uses: octokit/request-action@v2.x
91-
with:
92-
route: PATCH /repos/{owner}/{repo}/pulls/{pull_number}
93-
owner: ${{ github.repository_owner }}
94-
repo: ${{ github.event.repository.name }}
95-
pull_number: ${{ github.event.pull_request.number }}
96-
state: closed
97-
env:
98-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
99-
100-
- name: Wait for PR to close
101-
run: sleep 5
102-
103-
- name: Comment on PR
104-
uses: actions/github-script@v6
105-
with:
106-
github-token: ${{ secrets.GITHUB_TOKEN }}
107-
script: |
108-
const prNumber = context.payload.pull_request?.number || context.payload.issue?.number;
109-
if (!prNumber) {
110-
console.log("PR 번호를 찾을 수 없습니다.");
111-
return;
112-
}
113-
github.rest.issues.createComment({
114-
owner: context.repo.owner,
115-
repo: context.repo.repo,
116-
issue_number: prNumber,
117-
body: "실패하는 테스트코드가 있어 PR이 자동으로 닫혔습니다.\nGithub Action에서 자세한 실패 로그를 확인하고 코드를 수정하세요."
118-
});

docs/deployment.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ Firebase:
6565

6666
- `FIREBASE_CREDENTIALS_BASE64`
6767

68+
Development server:
69+
70+
- `DEV_EC2_HOST`
71+
- `DEV_EC2_USER`
72+
- `DEV_EC2_SSH_KEY`
73+
- `DEV_BACKEND_HTTP_PORT` (optional, defaults to `8081`)
74+
- Runtime secrets matching the production names with a `DEV_` prefix, for example `DEV_SPRING_DATASOURCE_URL`, `DEV_JWT_SECRETKEY`, and `DEV_FIREBASE_CREDENTIALS_BASE64`
75+
6876
Set the base64 secrets from the ignored local credential files:
6977

7078
```bash
@@ -77,7 +85,9 @@ base64 -i ontime-back/src/main/resources/key/AuthKey_743M7R5W3W.p8 | tr -d '\n'
7785

7886
## Build And Release Flow
7987

80-
Push to the `deploy` branch to trigger `.github/workflows/deploy.yml`.
88+
Push to the `main` branch, or run `.github/workflows/deploy.yml` manually, to deploy production.
89+
90+
Push to the `dev` branch, or run `.github/workflows/deploy-dev.yml` manually, to deploy the development server.
8191

8292
The workflow:
8393

0 commit comments

Comments
 (0)