Skip to content

Commit fe04fd4

Browse files
Merge pull request #11 from comeevery-git/feature/funny-githubactions
코드 리뷰 Github Action 테스트를 위한 일부 코드 리팩토링
2 parents a4ab5db + 6e45dda commit fe04fd4

File tree

5 files changed

+259
-92
lines changed

5 files changed

+259
-92
lines changed

.github/code-review.yml

Lines changed: 0 additions & 67 deletions
This file was deleted.

.github/workflows/code-review.yml

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
name: Code Review with GPT
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize]
6+
workflow_dispatch:
7+
8+
jobs:
9+
code_review:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Checkout code
14+
uses: actions/checkout@v3
15+
16+
- name: Set up Python
17+
uses: actions/setup-python@v4
18+
with:
19+
python-version: '3.x'
20+
21+
- name: Install dependencies
22+
run: |
23+
pip install openai requests
24+
25+
- name: Generate diff for code review
26+
id: diff
27+
run: |
28+
git fetch origin ${{ github.event.pull_request.base.ref }} --depth=1
29+
git diff origin/${{ github.event.pull_request.base.ref }} ${{ github.sha }} > pr_diff.txt
30+
shell: bash
31+
32+
- name: Perform Code Review with GPT and Comment on Changed Lines
33+
id: gpt_review
34+
run: |
35+
python <<EOF
36+
import openai
37+
import requests
38+
import json
39+
40+
# OpenAI API Key 설정
41+
openai.api_key = "${{ secrets.OPENAI_API_KEY }}"
42+
43+
# 시스템 프롬프트 및 모델명 가져오기
44+
system_prompt = """${{ secrets.SYSTEM_PROMPT }}"""
45+
model_name = "${{ secrets.OPENAI_MODEL }}"
46+
47+
# 처리된 파일과 라인 기록 (중복 방지)
48+
processed_files = set() # 각 파일을 기록
49+
processed_lines = {} # 각 파일별로 라인을 기록하기 위한 딕셔너리로 수정
50+
51+
# GitHub API 호출 로그
52+
print("Fetching changed files from GitHub API...")
53+
54+
# 변경된 파일과 라인 정보를 가져오기 위한 GitHub API 호출
55+
files_url = f"https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files"
56+
headers = {"Authorization": f"token ${{ secrets.GITHUB_TOKEN }}"}
57+
try:
58+
response = requests.get(files_url, headers=headers, timeout=10)
59+
print(f"GitHub API status code: {response.status_code}") # 상태 코드 로그 출력
60+
response.raise_for_status()
61+
except requests.exceptions.RequestException as e:
62+
print(f"Error fetching files: {e}")
63+
exit(1)
64+
65+
if response.status_code == 200:
66+
print("Files fetched successfully. Analyzing changes...")
67+
files_changed = response.json()
68+
69+
# .java 파일만 필터링
70+
java_files = [file for file in files_changed if file['filename'].endswith('.java')]
71+
72+
if not java_files:
73+
print("No .java files to review.")
74+
exit(0)
75+
76+
for file in java_files:
77+
file_path = file['filename']
78+
79+
# 이미 처리한 파일은 건너뛰기
80+
if file_path in processed_files:
81+
print(f"Skipping already processed file: {file_path}")
82+
continue
83+
84+
print(f"Processing file: {file_path}")
85+
patch = file.get('patch', '')
86+
print(f"Analyzing patch for file: {file_path}")
87+
88+
# 각 파일마다 중복 라인 처리를 위한 집합 생성
89+
if file_path not in processed_lines:
90+
processed_lines[file_path] = set()
91+
92+
# 전체 패치 내용을 GPT에게 전달
93+
if patch: # 패치가 존재할 경우에만 처리
94+
print(f"Calling GPT API for patch in file: {file_path}")
95+
try:
96+
gpt_response = openai.chat.completions.create(
97+
model=model_name,
98+
messages=[
99+
{"role": "system", "content": system_prompt},
100+
{"role": "user", "content": f"Here is the code diff for context:\n{patch}"}
101+
],
102+
timeout=30
103+
)
104+
except Exception as e:
105+
print(f"Error in GPT request: {e}")
106+
continue
107+
108+
review_comment = gpt_response.choices[0].message.content
109+
print(f"GPT response received for file: {file_path}")
110+
print(f"Review comment: {review_comment}")
111+
112+
# 변경된 파일과 라인에 리뷰 코멘트를 추가
113+
commit_id = "${{ github.event.pull_request.head.sha }}"
114+
line_number = file.get('patch').split('\n').index(next(line for line in file['patch'].split('\n') if line.startswith('+'))) + 1
115+
comment_body = {
116+
"body": review_comment,
117+
"path": file_path,
118+
"line": line_number,
119+
"side": "RIGHT",
120+
"commit_id": commit_id
121+
}
122+
123+
# 코멘트를 추가하기 전 파일 경로와 위치를 로그로 출력
124+
print(f"Commenting on file: {file_path}, line: {line_number}")
125+
126+
comment_url = f"https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments"
127+
response = requests.post(comment_url, headers=headers, data=json.dumps(comment_body))
128+
129+
# 응답 상태 확인
130+
if response.status_code == 201:
131+
print(f"Comment posted successfully for file: {file_path}")
132+
else:
133+
print(f"Failed to post comment. Status code: {response.status_code}, Response: {response.text}")
134+
135+
# 각 파일을 처리한 후 파일 이름을 기록
136+
processed_files.add(file_path)
137+
138+
else:
139+
print(f"Unexpected status code: {response.status_code}")
140+
exit(1)
141+
142+
# 최종 리뷰 요약 코멘트 추가
143+
final_comment = "### 최종 리뷰 요약: .java 파일에 대한 모든 변경 사항을 검토 완료했습니다."
144+
comment_url = f"https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments"
145+
requests.post(comment_url, headers=headers, data=json.dumps({"body": final_comment}))
146+
print("Final review comment posted.")
147+
exit(0)
148+
EOF
Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,35 @@
11
package app.common.infrastructure.exception;
22

33
import app.common.domain.model.common.ResponseCode;
4-
import lombok.AllArgsConstructor;
54
import lombok.Getter;
65

7-
@AllArgsConstructor
6+
import java.io.Serializable;
7+
88
@Getter
9-
public class CustomException extends RuntimeException {
9+
public class CustomException extends RuntimeException implements Serializable {
10+
private static final long serialVersionUID = 1L;
11+
1012
private final ResponseCode responseCode;
13+
private final String additionalMessage;
14+
15+
public CustomException(ResponseCode responseCode) {
16+
this(responseCode, null, null);
17+
}
18+
19+
public CustomException(ResponseCode responseCode, String additionalMessage) {
20+
this(responseCode, additionalMessage, null);
21+
}
22+
23+
public CustomException(ResponseCode responseCode, String additionalMessage, Throwable cause) {
24+
super(additionalMessage, cause);
25+
this.responseCode = responseCode;
26+
this.additionalMessage = additionalMessage;
27+
}
28+
29+
@Override
30+
public String getMessage() {
31+
return (additionalMessage != null)
32+
? responseCode.getMessage() + ": " + additionalMessage
33+
: responseCode.getMessage();
34+
}
1135
}
Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,86 @@
11
package app.common.infrastructure.exception;
22

3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import org.springframework.http.HttpStatus;
6+
import org.springframework.http.ResponseEntity;
37
import org.springframework.web.bind.annotation.ExceptionHandler;
48
import org.springframework.web.bind.annotation.RestControllerAdvice;
9+
import org.springframework.web.servlet.NoHandlerFoundException;
10+
import org.springframework.web.HttpRequestMethodNotSupportedException;
11+
import org.springframework.web.bind.MethodArgumentNotValidException;
12+
import org.springframework.dao.DataAccessException;
13+
import org.springframework.security.access.AccessDeniedException;
14+
15+
import javax.validation.ConstraintViolationException;
516

617
import app.common.domain.model.common.BaseResponse;
718

819
@RestControllerAdvice
920
public class CustomExceptionHandler {
10-
@ExceptionHandler(Exception.class)
11-
protected BaseResponse handleException(Exception e) {
12-
return BaseResponse.failResponse(e);
13-
}
21+
22+
private static final Logger logger = LoggerFactory.getLogger(CustomExceptionHandler.class);
1423

1524
@ExceptionHandler(CustomException.class)
16-
protected BaseResponse handleCustomException(CustomException e) {
17-
return BaseResponse.failResponse(e.getResponseCode());
25+
protected ResponseEntity<BaseResponse> handleCustomException(CustomException e) {
26+
logger.error("CustomException occurred: ", e);
27+
BaseResponse response = BaseResponse.failResponse(e.getResponseCode());
28+
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
29+
}
30+
31+
@ExceptionHandler(IllegalArgumentException.class)
32+
protected ResponseEntity<BaseResponse> handleIllegalArgumentException(IllegalArgumentException e) {
33+
logger.error("IllegalArgumentException occurred: ", e);
34+
BaseResponse response = BaseResponse.failResponse("INVALID_ARGUMENT");
35+
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
36+
}
37+
38+
@ExceptionHandler(NoHandlerFoundException.class)
39+
protected ResponseEntity<BaseResponse> handleNoHandlerFoundException(NoHandlerFoundException e) {
40+
logger.error("NoHandlerFoundException occurred: ", e);
41+
BaseResponse response = BaseResponse.failResponse("RESOURCE_NOT_FOUND");
42+
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
43+
}
44+
45+
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
46+
protected ResponseEntity<BaseResponse> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
47+
logger.error("HttpRequestMethodNotSupportedException occurred: ", e);
48+
BaseResponse response = BaseResponse.failResponse("METHOD_NOT_ALLOWED");
49+
return new ResponseEntity<>(response, HttpStatus.METHOD_NOT_ALLOWED);
1850
}
1951

52+
@ExceptionHandler(MethodArgumentNotValidException.class)
53+
protected ResponseEntity<BaseResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
54+
logger.error("MethodArgumentNotValidException occurred: ", e);
55+
BaseResponse response = BaseResponse.failResponse("INVALID_INPUT");
56+
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
57+
}
58+
59+
@ExceptionHandler(ConstraintViolationException.class)
60+
protected ResponseEntity<BaseResponse> handleConstraintViolationException(ConstraintViolationException e) {
61+
logger.error("ConstraintViolationException occurred: ", e);
62+
BaseResponse response = BaseResponse.failResponse("CONSTRAINT_VIOLATION");
63+
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
64+
}
65+
66+
@ExceptionHandler(DataAccessException.class)
67+
protected ResponseEntity<BaseResponse> handleDataAccessException(DataAccessException e) {
68+
logger.error("DataAccessException occurred: ", e);
69+
BaseResponse response = BaseResponse.failResponse("DATABASE_ERROR");
70+
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
71+
}
72+
73+
@ExceptionHandler(AccessDeniedException.class)
74+
protected ResponseEntity<BaseResponse> handleAccessDeniedException(AccessDeniedException e) {
75+
logger.error("AccessDeniedException occurred: ", e);
76+
BaseResponse response = BaseResponse.failResponse("ACCESS_DENIED");
77+
return new ResponseEntity<>(response, HttpStatus.FORBIDDEN);
78+
}
79+
80+
@ExceptionHandler(Exception.class)
81+
protected ResponseEntity<BaseResponse> handleException(Exception e) {
82+
logger.error("Unexpected exception occurred: ", e);
83+
BaseResponse response = BaseResponse.failResponse("INTERNAL_SERVER_ERROR");
84+
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
85+
}
2086
}

src/main/java/app/member/domain/service/MemberService.java

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,19 @@ public class MemberService {
2424

2525
@Transactional
2626
public CreateMemberVo createMember(CreateMemberDto dto) {
27-
try {
28-
Member member = Member.builder()
29-
.name(dto.getName())
30-
.email(dto.getEmail())
31-
.role(dto.getRole())
32-
.build();
33-
Member result = memberRepository.save(member);
34-
log.info("### 회원 생성 결과: {}", result);
27+
validateNewMember(dto);
3528

36-
return CreateMemberVo.toVo(result);
37-
} catch (DataIntegrityViolationException e) {
38-
throw new CustomException(ResponseCode.CONFLICT_DATA);
39-
} catch (CustomException e) {
40-
throw new CustomException(e.getResponseCode());
41-
} catch (Exception e) {
42-
throw e;
43-
}
29+
Member member = Member.builder()
30+
.name(dto.getName())
31+
.email(dto.getEmail())
32+
.role(dto.getRole())
33+
.build();
34+
35+
Member savedMember = memberRepository.save(member);
36+
37+
log.info("New member created with ID: {}", savedMember.getId());
38+
39+
return CreateMemberVo.toVo(savedMember);
4440
}
4541

4642
@Transactional(readOnly = true)

0 commit comments

Comments
 (0)