Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
8e1e241
fix: Sheet API ์š”์ฒญ์ž ์ ‘๊ทผ ๊ถŒํ•œ ๊ฒ€์ฆ ์ถ”๊ฐ€
JanooGwan Apr 2, 2026
af24113
test: ๊ทธ๋ฃน ์ฑ„ํŒ…๋ฐฉ ์ƒ์„ฑ ๋ฐ ๋ฉค๋ฒ„ ๊ฐ•ํ‡ด ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ (#482)
dh2906 Apr 2, 2026
4c38d56
fix: getChatRooms์—์„œ GROUP ์ฑ„ํŒ…๋ฐฉ ๋ˆ„๋ฝ ๋ฒ„๊ทธ ์ˆ˜์ • (#497)
dh2906 Apr 4, 2026
d020892
fix: ์–ด๋“œ๋ฏผ ์œ ์ €๊ฐ€ ์ฑ„ํŒ…๋ฐฉ์„ ๋‚˜๊ฐ€๋„ ๋ชฉ๋ก์— ์กฐํšŒ๋˜๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ (#498)
dh2906 Apr 4, 2026
1b49315
fix: ์–ด๋“œ๋ฏผ์ด ๋ฌธ์˜ ์ฑ„ํŒ…๋ฐฉ ๋‚˜๊ฐ€๊ณ  ๋‹ค์‹œ ๋ฉ”์‹œ์ง€๊ฐ€ ์˜ค๋ฉด ๋ชป๋ณด๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ (#499)
dh2906 Apr 5, 2026
37ae494
fix: ๋™์•„๋ฆฌ ์ง€์› ๊ฑฐ์ ˆ ์•Œ๋ฆผ์„ AFTER_COMMIT ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ณ€๊ฒฝ (#500)
dh2906 Apr 6, 2026
3398919
fix: ์–ด๋“œ๋ฏผ ๊ณ„์ •์œผ๋กœ๋„ ๋ถ€์› ์ง์ฑ… ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋„๋ก ์ˆ˜์ •
JanooGwan Apr 6, 2026
0706ba3
refactor: ๋™์•„๋ฆฌ ์ฑ„ํŒ…๋ฐฉ ๋ฉค๋ฒ„์‹ญ Lazy ์ƒ์„ฑ์œผ๋กœ ๋ณ€๊ฒฝ (#502)
dh2906 Apr 6, 2026
0c3de0b
refactor: ์‹œํŠธ ๋“ฑ๋ก ์‹œ ๊ถŒํ•œ ์กฐ๊ฑด ์ œ๊ฑฐ
JanooGwan Apr 7, 2026
5814a63
fix: ๋ฌธ์˜ํ•˜๊ธฐ์—์„œ ๊ธฐ์กด ๋ฌธ์˜ ์ฑ„ํŒ…๋ฐฉ์ด ์•„๋‹Œ ์ƒˆ ์ฑ„ํŒ…๋ฐฉ์ด ์ƒ์„ฑ๋˜๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ (#504)
dh2906 Apr 7, 2026
aa19667
fix: ADMIN ๋ฉค๋ฒ„ ์ค‘๋ณต ์ƒ์„ฑ ๋ฐฉ์ง€ SQL ์ฟผ๋ฆฌ ์ˆ˜์ •
dh2906 Apr 7, 2026
d11bb54
fix: Auth ์–ด๋…ธํ…Œ์ด์…˜ ์ธ์‹ ๋ฌธ์ œ ํ•ด๊ฒฐ (#507)
dh2906 Apr 7, 2026
e213e32
refactor: ์ฑ„ํŒ… ์„œ๋น„์Šค ์ฝ”๋“œ ์ค‘๋ณต ์ œ๊ฑฐ ๋ฐ ๋‹จ์ˆœํ™” (#508)
dh2906 Apr 7, 2026
d8333ca
feat: ์‹œํŠธ ๋ถ€์› ๋ฏธ๋ฆฌ๋ณด๊ธฐ API ์ถ”๊ฐ€ (#506)
JanooGwan Apr 7, 2026
8531b08
test: ์ฑ„ํŒ… API ์—ฃ์ง€ ์ผ€์ด์Šค ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ (#509)
dh2906 Apr 7, 2026
d67bc39
feat: ์ง€์› ์‘๋‹ต์— userId ํ•„๋“œ ์ถ”๊ฐ€ ๋ฐ ๋งคํ•‘ ์ž‘์„ฑ (#510)
dh2906 Apr 8, 2026
3ea7ddc
feat: ๋™์•„๋ฆฌ ์‚ฌ์ „ ๋“ฑ๋ก ํšŒ์› ๋ฐฐ์น˜ ๋“ฑ๋ก API ์ถ”๊ฐ€ (#512)
dh2906 Apr 8, 2026
2e27c99
fix: ์ค‘๋ณต๋˜๋Š” ์ฑ„ํŒ… ๋ฐฉ ๋ฉ”์‹œ์ง€ ๋ณ‘ํ•ฉ (#511)
dh2906 Apr 8, 2026
ca99360
fix: V69 ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ MySQL zero date ์ฒ˜๋ฆฌ
dh2906 Apr 8, 2026
d775576
fix: V69 ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ MySQL zero date ์„œ๋ธŒ์ฟผ๋ฆฌ ์ฒ˜๋ฆฌ
dh2906 Apr 8, 2026
02a7829
fix: V69 ์‹คํ–‰ ์ค‘์—๋งŒ zero datetime ๊ด€๋ จ sql_mode๋ฅผ ์™„ํ™”
dh2906 Apr 8, 2026
dbdaad9
fix: ํ…Œ์ŠคํŠธ ๊ณ„์•ฝ๊ณผ ๊ตฌ์กฐ๋ฅผ ์ •๋ฆฌํ•ด ์ „์ฒด ํ…Œ์ŠคํŠธ๋ฅผ ์•ˆ์ •ํ™” (#514)
dh2906 Apr 9, 2026
d99b445
fix: ๊ตฌ๊ธ€ ์‹œํŠธ preview API์˜ request ์ œ๊ฑฐ (#513)
JanooGwan Apr 9, 2026
f27ad4c
test: ๊ฐ์ข… ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ๋ณด๊ฐ• (#515)
dh2906 Apr 9, 2026
c2530b6
test: ๊ฐ์ข… ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ (#516)
dh2906 Apr 10, 2026
a838340
chore: ๋Ÿฐํƒ€์ž„์—์„œ OTel javaagent ์ฃผ์ž…์„ ์ œ์™ธ (#519)
dh2906 Apr 10, 2026
3edf362
refactor: ์ˆœ๊ณต ์‹œ๊ฐ„ ๋ฆฌํŽ™ํ† ๋ง (#520)
Soundbar91 Apr 10, 2026
c4de318
test: UploadService ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ ๋ฐ ๋ฒ„๊ทธ ์ˆ˜์ • (#525)
dh2906 Apr 11, 2026
3e87a82
test: ClubService ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ (#527)
dh2906 Apr 11, 2026
aa00849
test: ClubApplicationService ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ (#526)
dh2906 Apr 11, 2026
1216f23
test: ChatService ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€ (#528)
dh2906 Apr 11, 2026
fc19604
ci: ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์น˜ ์ฒดํฌ ์›Œํฌํ”Œ๋กœ์šฐ ์ถ”๊ฐ€ (#529)
dh2906 Apr 12, 2026
b389ea7
fix: PR ์ปค๋ฒ„๋ฆฌ์ง€ ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ ๋ณ€๊ฒฝ ํŒŒ์ผ์„ ๊ฐ์ง€ํ•˜์ง€ ๋ชปํ•˜๋Š” ๋ฌธ์ œ ์ˆ˜์ • (#531)
dh2906 Apr 12, 2026
26dae75
refactor: Lua ์Šคํฌ๋ฆฝํŠธ๋ฅผ Redis GETDEL ๋„ค์ดํ‹ฐ๋ธŒ ๋ช…๋ น์–ด๋กœ ๊ต์ฒด (#530)
dh2906 Apr 12, 2026
72a752a
fix: ํ…Œ์ŠคํŠธ ๋ฏธ์‹คํ–‰ ์‹œ JaCoCo ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ๋ฐ ๊ฐ€๋“œ ์ถ”๊ฐ€ (#532)
dh2906 Apr 12, 2026
ebe0efc
feat: ์ฑ„ํŒ… ๋ฉ”์‹œ์ง€ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ์—์„œ ํŠน์ • ๋ฉ”์‹œ์ง€ ์‹œ์ ์œผ๋กœ ํŽ˜์ด์ง€ ์ด๋™ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ (#533)
dh2906 Apr 13, 2026
0ed5604
feat: ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ์‹œ ์š”์ฒญ ํšŸ์ˆ˜ ์ œํ•œ ์ถ”๊ฐ€ (#535)
dh2906 Apr 13, 2026
0fe2411
chore: JVM ๋ฉ”๋ชจ๋ฆฌ ์„ค์ • ๋ฐ Hibernate ์ฟผ๋ฆฌ ํ”Œ๋žœ ์บ์‹œ ์ถ•์†Œ (#536)
dh2906 Apr 13, 2026
d2e270a
update: DB ์œ„์น˜ ๋ณ€๊ฒฝ์— ๋”ฐ๋ฅธ dump ๊ด€๋ จ ์„ค์ • ๋ณ€๊ฒฝ
JanooGwan Apr 13, 2026
39fbc37
refactor: gc.log ๊ฒฝ๋กœ ์ˆ˜์ •
JanooGwan Apr 13, 2026
ff9ecb0
chore: ๊ฒฝ๋กœ ๋ณ€๊ฒฝ ๋กค๋ฐฑ
dh2906 Apr 13, 2026
5ce9e42
chore: ํž™ ๋คํ”„ ๋กœ๊น… ์ œ๊ฑฐ
dh2906 Apr 13, 2026
fd3633e
chore: ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ ์‚ฌ์ด์ฆˆ ์ตœ๋Œ€ ํฌ๊ธฐ ์ฆ๊ฐ€
dh2906 Apr 13, 2026
baaceab
chore: JVM ๋กœ๊น… ์ถ”๊ธฐ (#537)
dh2906 Apr 13, 2026
40ca8bf
chore: Dockerfile ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ถŒํ•œ ์„ค์ • ์ถ”๊ฐ€ (#538)
dh2906 Apr 13, 2026
88c09f7
chore: Dockerfile RUN ๋ช…๋ น์–ด ๋งˆ์ง€๋ง‰ ๋ถˆํ•„์š”ํ•œ '\' ์ œ๊ฑฐ
dh2906 Apr 13, 2026
9103b82
chore: Dockerfile ์œ ์ € ๊ถŒํ•œ ๋ฐ ๋กœ๊ทธ ๊ฒฝ๋กœ ์„ค์ • ๊ฐœ์„ 
dh2906 Apr 13, 2026
d7586dd
chore: DB dump ์œ„์น˜ ๋ณ€๊ฒฝ์„ ์œ„ํ•œ deploy-prod.yml ์ˆ˜์ •
JanooGwan Apr 13, 2026
0175c70
chore: ์˜คํƒ€ ์ˆ˜์ •(echo ๋ถ€๋ถ„)
JanooGwan Apr 13, 2026
ad9bed2
chore: PR ์ปค๋ฒ„๋ฆฌ์ง€ ์›Œํฌํ”Œ๋กœ ๋ธŒ๋žœ์น˜ ์กฐ๊ฑด ์ œ๊ฑฐ (#547)
dh2906 Apr 14, 2026
3a01152
Merge branch 'main' into develop
dh2906 Apr 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
272 changes: 272 additions & 0 deletions .github/workflows/pr-coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
name: PR Coverage Check

on:
pull_request_target:
paths:
- 'src/main/java/**/*.java'
- 'src/test/java/**/*.java'
- 'build.gradle'

jobs:
coverage:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write

steps:
- name: Checkout PR code
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'

- name: Cache Gradle packages
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: gradle-${{ runner.os }}-

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Get changed Java files
id: changed-files
run: |
set -euo pipefail

CHANGED_MAIN_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | grep '^src/main/java/.*\.java$' || true)
CHANGED_MAIN_CLASSES=$(printf '%s\n' "$CHANGED_MAIN_FILES" | sed '/^$/d' | sed 's|src/main/java/||' | sed 's|/|.|g' | sed 's|\.java$||')

echo "Changed main Java files (package format):"
echo "$CHANGED_MAIN_CLASSES"

{
echo 'CHANGED_FILES<<EOF'
echo "$CHANGED_MAIN_CLASSES"
echo 'EOF'
} >> "$GITHUB_ENV"

FILE_COUNT=$(printf '%s\n' "$CHANGED_MAIN_CLASSES" | sed '/^$/d' | wc -l | tr -d ' ')
echo "FILE_COUNT=$FILE_COUNT" >> "$GITHUB_ENV"
echo "Changed file count: $FILE_COUNT"

- name: Run tests with JaCoCo
id: run-tests
run: |
./gradlew test jacocoTestReport --no-daemon --build-cache

- name: Parse coverage for changed files
id: parse-coverage
if: always()
env:
CHANGED_FILES_OUTCOME: ${{ steps.changed-files.outcome }}
RUN_TESTS_OUTCOME: ${{ steps.run-tests.outcome }}
run: |
python3 << 'PYEOF'
import os
import re
import xml.etree.ElementTree as ET

def write_outputs(overall_coverage, total_lines, covered_lines, body_type, coverage_table):
with open(os.getenv('GITHUB_OUTPUT'), 'a') as f:
f.write(f"overall_coverage={overall_coverage}\n")
f.write(f"total_lines={total_lines}\n")
f.write(f"covered_lines={covered_lines}\n")
f.write(f"body_type={body_type}\n")
f.write("coverage_table<<TABLEEOF\n")
f.write(coverage_table)
f.write("\nTABLEEOF\n")

report_path = 'build/reports/jacoco/test/jacocoTestReport.xml'
changed_raw = os.getenv('CHANGED_FILES', '').strip()

# ์„ ํ–‰ ์Šคํ… ์‹คํŒจ ์—ฌ๋ถ€๋ฅผ ๋จผ์ € ํ™•์ธํ•˜์—ฌ ์‹ค์ œ ์›์ธ์„ ๋ช…ํ™•ํžˆ ์ „๋‹ฌ
changed_files_outcome = os.getenv('CHANGED_FILES_OUTCOME', '')
run_tests_outcome = os.getenv('RUN_TESTS_OUTCOME', '')

if changed_files_outcome != 'success':
write_outputs('N/A', 0, 0, 'workflow-error', '๋ณ€๊ฒฝ ํŒŒ์ผ ๋ชฉ๋ก์„ ๊ณ„์‚ฐํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.')
raise SystemExit(1)
if run_tests_outcome != 'success':
write_outputs('N/A', 0, 0, 'workflow-error', 'ํ…Œ์ŠคํŠธ ์‹คํ–‰์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.')
raise SystemExit(1)

if not changed_raw:
write_outputs('N/A', 0, 0, 'no-main-changes', '๋ณ€๊ฒฝ๋œ main Java ์†Œ์Šค ํŒŒ์ผ์ด ์—†์–ด ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ๊ณ„์‚ฐํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.')
raise SystemExit(0)

if not os.path.exists(report_path):
write_outputs('N/A', 0, 0, 'no-report', 'JaCoCo ๋ฆฌํฌํŠธ ํŒŒ์ผ์ด ์ƒ์„ฑ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ์‹คํ–‰์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”.')
raise SystemExit(1)

tree = ET.parse(report_path)
root = tree.getroot()
changed_classes = [c.strip() for c in changed_raw.split('\n') if c.strip()]

results = []
overall_covered = 0
overall_missed = 0

for package in root.findall('.//package'):
pkg_name = package.get('name', '').replace('/', '.')

for srcfile in package.findall('.//sourcefile'):
filename = srcfile.get('name', '')
full_class = f"{pkg_name}.{filename.replace('.java', '')}"

if not any(re.search(re.escape(c) + r'($|\$)', full_class) for c in changed_classes):
continue

counter = srcfile.find('counter[@type="LINE"]')
if counter is None:
continue

missed = int(counter.get('missed', 0))
covered = int(counter.get('covered', 0))
total = missed + covered
if total == 0:
continue

ratio = (covered / total) * 100
results.append({
'class': full_class,
'covered': covered,
'total': total,
'ratio': ratio,
})
overall_covered += covered
overall_missed += missed

if not results:
write_outputs('0.0', 0, 0, 'no-coverage-data', '๋ณ€๊ฒฝ๋œ main Java ์†Œ์Šค ํŒŒ์ผ์— ๋Œ€ํ•œ JaCoCo ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.')
raise SystemExit(0)

results.sort(key=lambda x: x['ratio'])
total_lines = overall_covered + overall_missed
overall_ratio = (overall_covered / total_lines) * 100 if total_lines > 0 else 0

table_lines = [
'| Class | Coverage | Lines | Status |',
'|-------|----------|-------|--------|',
]
for result in results:
status = 'โœ…' if result['ratio'] >= 70 else ('โš ๏ธ' if result['ratio'] >= 50 else 'โŒ')
class_name = result['class'].split('.')[-1]
pkg = '.'.join(result['class'].split('.')[:-1])
table_lines.append(
f"| {class_name}<br><sub>{pkg}</sub> | **{result['ratio']:.1f}%** | {result['covered']}/{result['total']} | {status} |"
)

write_outputs(f"{overall_ratio:.1f}", total_lines, overall_covered, 'coverage', '\n'.join(table_lines))
PYEOF

- name: Fail if coverage below threshold
if: steps.parse-coverage.outputs.body_type == 'coverage'
env:
OVERALL_COVERAGE: ${{ steps.parse-coverage.outputs.overall_coverage }}
run: |
COVERAGE_VAL=$(echo "$OVERALL_COVERAGE" | cut -d'.' -f1)
if [ "$COVERAGE_VAL" -lt 50 ]; then
echo "โŒ ์ปค๋ฒ„๋ฆฌ์ง€ ${OVERALL_COVERAGE}% ๊ฐ€ 50% ์ž„๊ณ„๊ฐ’ ๋ฏธ๋งŒ์ž…๋‹ˆ๋‹ค."
exit 1
fi
echo "โœ… ์ปค๋ฒ„๋ฆฌ์ง€ ${OVERALL_COVERAGE}% ๊ฐ€ ์ž„๊ณ„๊ฐ’์„ ์ถฉ์กฑํ•ฉ๋‹ˆ๋‹ค."

- name: Comment PR with coverage results
if: always()
uses: actions/github-script@v7
env:
OVERALL_COVERAGE: ${{ steps.parse-coverage.outputs.overall_coverage }}
TOTAL_LINES: ${{ steps.parse-coverage.outputs.total_lines }}
COVERED_LINES: ${{ steps.parse-coverage.outputs.covered_lines }}
BODY_TYPE: ${{ steps.parse-coverage.outputs.body_type }}
COVERAGE_TABLE: ${{ steps.parse-coverage.outputs.coverage_table }}
FILE_COUNT: ${{ env.FILE_COUNT }}
with:
script: |
const coverage = process.env.OVERALL_COVERAGE;
const totalLines = process.env.TOTAL_LINES;
const coveredLines = process.env.COVERED_LINES;
const bodyType = process.env.BODY_TYPE;
const table = process.env.COVERAGE_TABLE || '';
const fileCount = process.env.FILE_COUNT || '0';

let body = '## ๐Ÿงช JaCoCo Coverage Report (Changed Files)\n\n';

if (bodyType === 'no-main-changes') {
body += '> ์ด PR์—์„œ ๋ณ€๊ฒฝ๋œ main Java ์†Œ์Šค ํŒŒ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค.\n';
} else if (bodyType === 'no-coverage-data') {
body += '> ๋ณ€๊ฒฝ๋œ main Java ์†Œ์Šค ํŒŒ์ผ์— ๋Œ€ํ•œ JaCoCo ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.\n';
} else if (bodyType === 'workflow-error') {
body += '> ์›Œํฌํ”Œ๋กœ์šฐ ์‹คํ–‰ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”.\n';
} else if (bodyType === 'no-report') {
body += '> JaCoCo ๋ฆฌํฌํŠธ ํŒŒ์ผ์ด ์ƒ์„ฑ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ์‹คํ–‰์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”.\n';
} else if (!bodyType) {
body += '> ์ด์ „ ์Šคํ…์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ด ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ๊ณ„์‚ฐํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.\n';
} else {
const ratio = parseFloat(coverage);
const status = ratio >= 70 ? 'โœ…' : (ratio >= 50 ? 'โš ๏ธ' : 'โŒ');

body += `### Summary\n`;
body += `- **Overall Coverage:** ${coverage}% ${status}\n`;
body += `- **Covered Lines:** ${coveredLines} / ${totalLines}\n`;
body += `- **Changed Files:** ${fileCount}\n\n`;
body += `### Coverage by File\n`;
body += `${table}\n\n`;

if (ratio < 50) {
body += '> โŒ **์•Œ๋ฆผ:** ์ปค๋ฒ„๋ฆฌ์ง€๊ฐ€ 50% ๋ฏธ๋งŒ์ž…๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.\n';
} else if (ratio < 70) {
body += '> โš ๏ธ **์•Œ๋ฆผ:** ์ปค๋ฒ„๋ฆฌ์ง€๊ฐ€ 70% ๋ฏธ๋งŒ์ž…๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.\n';
}
}

body += `\n[๐Ÿ“Š View Workflow Run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})\n`;

const comments = await github.paginate(
github.rest.issues.listComments,
{
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
}
);

const existingComment = comments.find(c =>
c.user.login === 'github-actions[bot]' &&
c.body.includes('JaCoCo Coverage Report')
);

if (existingComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existingComment.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}

- name: Upload JaCoCo report
if: always()
uses: actions/upload-artifact@v4
with:
name: jacoco-report
path: build/reports/jacoco/test/
retention-days: 7
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,4 @@ mcp-bridge/node_modules/
mcp-bridge/.env

**/google-service-account.json
.omx/
26 changes: 17 additions & 9 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
FROM amazoncorretto:21-alpine

ARG OTEL_JAVA_AGENT_VERSION=2.18.1

WORKDIR /app

RUN addgroup -S konect && adduser -S konect -G konect

COPY build/libs/KONECT_API.jar KONECT_API.jar
COPY opentelemetry-javaagent.jar opentelemetry-javaagent.jar
RUN addgroup -g 1000 -S konect \
&& adduser -u 1000 -S konect -G konect \
&& mkdir -p /app /app/logs \
&& chown -R 1000:1000 /app \
&& chmod 755 /app /app/logs

RUN chown -R konect:konect /app
COPY --chown=1000:1000 build/libs/KONECT_API.jar KONECT_API.jar
COPY --chown=1000:1000 opentelemetry-javaagent.jar opentelemetry-javaagent.jar

USER konect:konect
USER 1000:1000

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1

ENTRYPOINT ["java", "-jar", "KONECT_API.jar"]
ENTRYPOINT ["java", \
"-Xms128m", \
"-Xmx256m", \
"-XX:MaxMetaspaceSize=256m", \
"-XX:+UseStringDeduplication", \
"-Xlog:gc*:file=/app/logs/gc.log:time,uptime,level,tags:filecount=5,filesize=10m", \
"-XX:+HeapDumpOnOutOfMemoryError", \
"-XX:HeapDumpPath=/app/logs/heapdump.hprof", \
"-jar", "KONECT_API.jar"]
43 changes: 43 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ plugins {
id 'org.springframework.boot' version '3.5.8'
id 'io.spring.dependency-management' version '1.1.7'
id 'checkstyle'
id 'jacoco'
}

group = 'gg.agit'
Expand Down Expand Up @@ -85,6 +86,7 @@ dependencies {
testImplementation 'org.testcontainers:testcontainers'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:mysql'
testImplementation 'com.github.codemonstur:embedded-redis:1.4.3'
testRuntimeOnly 'com.h2database:h2'
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
Expand All @@ -106,6 +108,47 @@ tasks.named('test') {
useJUnitPlatform()

maxParallelForks = Math.min(Runtime.runtime.availableProcessors(), 4)
reports {
// CI๋Š” JUnit XML๋งŒ์œผ๋กœ ์ถฉ๋ถ„ํ•˜๋ฏ€๋กœ HTML ๋ฆฌํฌํŠธ ์ƒ์„ฑ ๋น„์šฉ์€ ์ค„์ธ๋‹ค.
html.required = !System.getenv().containsKey('CI')
}
}

// JaCoCo ์ปค๋ฒ„๋ฆฌ์ง€ ๋ฆฌํฌํŠธ ์„ค์ •
jacocoTestReport {
dependsOn test

reports {
xml.required = true
html.required = true
}

// ์ปค๋ฒ„๋ฆฌ์ง€์—์„œ ์ œ์™ธํ•  ํด๋ž˜์Šค ์„ค์ •
afterEvaluate {
classDirectories.setFrom(
fileTree(layout.buildDirectory.dir("classes/java/main")) {
exclude([
// DTO (dto ํŒจํ‚ค์ง€ ๋‚ด๋ถ€๋งŒ ์ œ์™ธ)
"**/dto/*.class",
// Entity
"**/domain/**/model/*.class",
"**/model/*.class",
// Configuration
"**/config/*.class",
"**/*Config.class",
// Exception
"**/exception/*.class",
"**/*Exception.class",
// ๊ธฐํƒ€
"**/Application.class", // Spring Boot ๋ฉ”์ธ ํด๋ž˜์Šค
])
}
)
}
}

tasks.named('check') {
dependsOn jacocoTestReport
}

checkstyle {
Expand Down
3 changes: 3 additions & 0 deletions lombok.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# JaCoCo์—์„œ ๋กฌ๋ณต ์ƒ์„ฑ ์ฝ”๋“œ ์ œ์™ธ
config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true
10 changes: 10 additions & 0 deletions skills-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"version": 1,
"skills": {
"gh-address-comments": {
"source": "smithery.ai",
"sourceType": "well-known",
"computedHash": "39acf09da5896afde2b61b22f4354a72dbed6633da9e8a578eacec4899b0ecfb"
}
}
}
Loading
Loading