diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..5865fab --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,35 @@ +--- +name: πŸ› 버그 리포트 +about: 버그λ₯Ό λ°œκ²¬ν–ˆμ„ λ•Œ μ‚¬μš©ν•΄μ£Όμ„Έμš” +title: '[BUG] ' +labels: bug +assignees: '' +--- + +# πŸ› κ°œμš” +여기에 μž‘μ—…ν•΄μ£Όμ„Έμš”. + +## πŸ” 상세 버그 λ‚΄μš© + +### πŸ“ 버그 μ„€λͺ… 1 +- [ ] μ–΄λ–€ μƒν™©μ—μ„œ λ°œμƒν•˜λŠ”μ§€ +- [ ] μ–΄λ–€ 였λ₯˜κ°€ λ‚˜νƒ€λ‚˜λŠ”μ§€ +- [ ] μ˜ˆμƒκ³Ό λ‹€λ₯Έ λ™μž‘μ€ 무엇인지 + +### πŸ“ μž¬ν˜„ 방법 +- [ ] 1. νŠΉμ • νŽ˜μ΄μ§€λ‘œ 이동 +- [ ] 2. νŠΉμ • λ²„νŠΌ 클릭 +- [ ] 3. 였λ₯˜ λ°œμƒ 확인 + +## πŸ“„ 더 꼼꼼히 확인할 λΆ€λΆ„ + +- [ ] λΈŒλΌμš°μ € μ½˜μ†” μ—λŸ¬ λ©”μ‹œμ§€ +- [ ] λ„€νŠΈμ›Œν¬ μš”μ²­ μƒνƒœ +- [ ] 둜그 파일 λ‚΄μš© + +## πŸ’» ν™˜κ²½ 정보 + +- [ ] OS: [예: Windows 11] +- [ ] λΈŒλΌμš°μ €: [예: Chrome 118] +- [ ] 버전: [예: v1.0.0] +- [ ] κΈ°κΈ°: [예: Desktop/Mobile] \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..a5df016 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,34 @@ +--- +name: ✨ κΈ°λŠ₯ μš”μ²­ +about: μƒˆλ‘œμš΄ κΈ°λŠ₯을 μ œμ•ˆν•  λ•Œ μ‚¬μš©ν•΄μ£Όμ„Έμš” +title: '[FEATURE] ' +labels: enhancement +assignees: '' +--- + +# ✨ κ°œμš” +여기에 μž‘μ—…ν•΄μ£Όμ„Έμš”. + +## πŸ’‘ 상세 κΈ°λŠ₯ λ‚΄μš© + +### πŸ“ κΈ°λŠ₯ μ„€λͺ… 1 +- [ ] μ–΄λ–€ κΈ°λŠ₯인지 μ„€λͺ… +- [ ] μ™œ ν•„μš”ν•œμ§€ 이유 +- [ ] μ–΄λ–»κ²Œ λ™μž‘ν•΄μ•Ό ν•˜λŠ”μ§€ + +### πŸ“ μ‚¬μš©μž μ‹œλ‚˜λ¦¬μ˜€ +- [ ] 1. μ‚¬μš©μžκ°€ μ–΄λ–€ μƒν™©μ—μ„œ +- [ ] 2. 이 κΈ°λŠ₯을 μ‚¬μš©ν•˜κ²Œ 되고 +- [ ] 3. μ–΄λ–€ κ²°κ³Όλ₯Ό μ–»λŠ”μ§€ + +## πŸ“„ 더 꼼꼼히 확인할 λΆ€λΆ„ + +- [ ] κΈ°μ‘΄ κΈ°λŠ₯과의 ν˜Έν™˜μ„± +- [ ] μ„±λŠ₯에 λ―ΈμΉ˜λŠ” 영ν–₯ +- [ ] λ³΄μ•ˆ 고렀사항 + +## 🎯 λŒ€μ•ˆ 및 참고사항 + +- [ ] κ³ λ €ν•΄λ³Έ λ‹€λ₯Έ ν•΄κ²°μ±… +- [ ] λΉ„μŠ·ν•œ κΈ°λŠ₯을 κ°€μ§„ λ‹€λ₯Έ μ„œλΉ„μŠ€ +- [ ] μ°Έκ³ ν•  λ§Œν•œ μžλ£Œλ‚˜ 링크 \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..09be227 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,39 @@ +## κ°œμš” + + +> GitHub 개발 μ›Œν¬ν”Œλ‘œμš°λ₯Ό μ²΄κ³„ν™”ν•˜κ³  μžλ™ν™”ν•˜κΈ° μœ„ν•œ ν…œν”Œλ¦Ώκ³Ό CI/CD μ‹œμŠ€ν…œμ„ κ΅¬μΆ•ν–ˆμŠ΅λ‹ˆλ‹€. +> 효율적인 ν˜‘μ—…κ³Ό μ½”λ“œ ν’ˆμ§ˆ 관리λ₯Ό μœ„ν•΄ 이슈 관리, PR κ²€ν† , 릴리즈 과정을 μžλ™ν™”ν–ˆμŠ΅λ‹ˆλ‹€. + +## PR μœ ν˜• +μ–΄λ–€ λ³€κ²½ 사항이 μžˆλ‚˜μš”? + +- [x] μƒˆλ‘œμš΄ κΈ°λŠ₯ μΆ”κ°€ +- [ ] 버그 μˆ˜μ • +- [ ] CSS λ“± μ‚¬μš©μž UI λ””μžμΈ λ³€κ²½ +- [x] μ½”λ“œμ— 영ν–₯을 μ£Όμ§€ μ•ŠλŠ” 변경사항(μ˜€νƒ€ μˆ˜μ •, νƒ­ μ‚¬μ΄μ¦ˆ λ³€κ²½, λ³€μˆ˜λͺ… λ³€κ²½) +- [ ] μ½”λ“œ λ¦¬νŒ©ν† λ§ +- [x] 주석 μΆ”κ°€ 및 μˆ˜μ • +- [x] λ¬Έμ„œ μˆ˜μ • +- [x] 파일 ν˜Ήμ€ 폴더λͺ… μˆ˜μ • +- [x] 파일 ν˜Ήμ€ 폴더 μ‚­μ œ + +## μž‘μ—… 상세 λ‚΄μš© + +### πŸš€ μƒˆλ‘œ μΆ”κ°€λœ κΈ°λŠ₯ +1. **GitHub Actions μ›Œν¬ν”Œλ‘œμš° ꡬ좕** + - `ci-cd.yml`: μ½”λ“œ ν’ˆμ§ˆ 검사, ν…ŒμŠ€νŠΈ, μžλ™ 배포 + - `pr-review.yml`: PR μžλ™ κ²€ν†  및 라벨링 + - `issue-management.yml`: 이슈 μžλ™ 관리 및 응닡 + - `release.yml`: 릴리즈 μžλ™ν™” 및 변경사항 생성 + +2. **체계적인 ν…œν”Œλ¦Ώ μ‹œμŠ€ν…œ** + - 버그 리포트 ν…œν”Œλ¦Ώ (`bug_report.md`) + - κΈ°λŠ₯ μš”μ²­ ν…œν”Œλ¦Ώ (`feature_request.md`) + - PR ν…œν”Œλ¦Ώ κ°œμ„  (체크리슀트 및 ꡬ쑰화) + +3. **μžλ™ν™” κΈ°λŠ₯** + - 이슈/PR μžλ™ 라벨링 + - 컀밋 κ·œμΉ™ 검증 + - λ³΄μ•ˆ 검사 (ν•˜λ“œμ½”λ”©λœ μ‹œν¬λ¦Ώ 탐지) + - μ½”λ“œ ν’ˆμ§ˆ 검사 (Black, isort, Flake8) + diff --git a/.github/template.md b/.github/template.md new file mode 100644 index 0000000..e52318a --- /dev/null +++ b/.github/template.md @@ -0,0 +1,33 @@ +# πŸš€ κ°œμš” +여기에 μž‘μ—…ν•΄μ£Όμ„Έμš”. + +## ⏰ μž‘μ—… 상세 λ‚΄μš© + +### πŸ“ μž‘μ—… λ‚΄μš© 1 +- [ ] 첫 번째 μž‘μ—… λ‚΄μš© +- [ ] 두 번째 μž‘μ—… λ‚΄μš© +- [ ] μ„Έ 번째 μž‘μ—… λ‚΄μš© + +### οΏ½ μž‘μ—… λ‚΄μš© 2 +- [ ] 첫 번째 μž‘μ—… λ‚΄μš© +- [ ] 두 번째 μž‘μ—… λ‚΄μš© +- [ ] μ„Έ 번째 μž‘μ—… λ‚΄μš© + +## πŸ“„ 더 꼼꼼히 확인할 λΆ€λΆ„ + +- [ ] 없을 경우 체크 + +## πŸ” λ°˜λ“œμ‹œ μ°Έκ³ ν•΄μ•Όν•˜λŠ” λ³€κ²½ 사항 + +- [ ] 없을 경우 체크 + +## πŸ”— κ΄€λ ¨ 이슈 +- Closes #(이슈 번호) +- Related to #(이슈 번호) + +## βœ… μ΅œμ’… 체크리슀트 +- [ ] μ½”λ“œκ°€ μ •μƒμ μœΌλ‘œ λ™μž‘ν•©λ‹ˆλ‹€ +- [ ] ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•˜κ³  ν†΅κ³Όν–ˆμŠ΅λ‹ˆλ‹€ +- [ ] μ½”λ“œ 리뷰λ₯Ό μš”μ²­ν–ˆμŠ΅λ‹ˆλ‹€ +- [ ] λ¬Έμ„œλ₯Ό μ—…λ°μ΄νŠΈν–ˆμŠ΅λ‹ˆλ‹€ +- [ ] λΈŒλ ˆμ΄ν‚Ή 체인지가 μžˆλ‹€λ©΄ λͺ…μ‹œν–ˆμŠ΅λ‹ˆλ‹€ diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml new file mode 100644 index 0000000..32ab9da --- /dev/null +++ b/.github/workflows/ci-cd.yml @@ -0,0 +1,93 @@ +name: πŸš€ CI/CD Pipeline + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + +jobs: + # πŸ“ μ½”λ“œ ν’ˆμ§ˆ 검사 + code-quality: + name: βœ… μ½”λ“œ ν’ˆμ§ˆ 검사 + runs-on: ubuntu-latest + + steps: + - name: πŸ“₯ μ½”λ“œ 체크아웃 + uses: actions/checkout@v4 + + - name: 🐍 Python ν™˜κ²½ μ„€μ • + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: πŸ“¦ μ˜μ‘΄μ„± μ„€μΉ˜ + run: | + python -m pip install --upgrade pip + pip install flake8 black isort + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + + - name: 🎨 μ½”λ“œ ν¬λ§·νŒ… 검사 (Black) + run: black --check . + + - name: πŸ“ Import μ •λ ¬ 검사 (isort) + run: isort --check-only . + + - name: πŸ” μ½”λ“œ μŠ€νƒ€μΌ 검사 (Flake8) + run: flake8 . + + # πŸ§ͺ ν…ŒμŠ€νŠΈ μ‹€ν–‰ + test: + name: πŸ§ͺ ν…ŒμŠ€νŠΈ μ‹€ν–‰ + runs-on: ubuntu-latest + needs: code-quality + + strategy: + matrix: + python-version: ['3.9', '3.10', '3.11'] + + steps: + - name: πŸ“₯ μ½”λ“œ 체크아웃 + uses: actions/checkout@v4 + + - name: 🐍 Python ${{ matrix.python-version }} μ„€μ • + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: πŸ“¦ μ˜μ‘΄μ„± μ„€μΉ˜ + run: | + python -m pip install --upgrade pip + pip install pytest pytest-cov + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + + - name: πŸ§ͺ ν…ŒμŠ€νŠΈ μ‹€ν–‰ + run: | + pytest --cov=. --cov-report=xml + + - name: πŸ“Š μ½”λ“œ 컀버리지 μ—…λ‘œλ“œ + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml + flags: unittests + name: codecov-umbrella + + # πŸš€ 배포 (main 브랜치만) + deploy: + name: πŸš€ 배포 + runs-on: ubuntu-latest + needs: [code-quality, test] + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + + steps: + - name: πŸ“₯ μ½”λ“œ 체크아웃 + uses: actions/checkout@v4 + + - name: πŸš€ 배포 μ€€λΉ„ + run: | + echo "배포 μ€€λΉ„ 쀑..." + # 여기에 μ‹€μ œ 배포 둜직 μΆ”κ°€ + + - name: βœ… 배포 μ™„λ£Œ μ•Œλ¦Ό + run: | + echo "배포가 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€! πŸŽ‰" \ No newline at end of file diff --git a/.github/workflows/issue-management.yml b/.github/workflows/issue-management.yml new file mode 100644 index 0000000..96effbe --- /dev/null +++ b/.github/workflows/issue-management.yml @@ -0,0 +1,114 @@ +name: πŸ“‹ 이슈 관리 + +on: + issues: + types: [opened, labeled, assigned] + issue_comment: + types: [created] + +jobs: + # 🎯 이슈 μžλ™ 처리 + issue-automation: + name: 🎯 이슈 μžλ™ 처리 + runs-on: ubuntu-latest + + steps: + - name: 🏷️ μƒˆ 이슈 μžλ™ 라벨링 + if: github.event.action == 'opened' + uses: actions/github-script@v6 + with: + script: | + const issue = context.payload.issue; + const title = issue.title.toLowerCase(); + const body = issue.body.toLowerCase(); + + const labels = ['triage']; + + // 제λͺ© 기반 라벨링 + if (title.includes('[bug]') || body.includes('bug') || body.includes('error')) { + labels.push('bug'); + } + if (title.includes('[feature]') || body.includes('feature') || body.includes('enhancement')) { + labels.push('enhancement'); + } + if (title.includes('[docs]') || body.includes('documentation')) { + labels.push('documentation'); + } + + // μš°μ„ μˆœμœ„ 라벨링 + if (body.includes('urgent') || body.includes('critical')) { + labels.push('priority-high'); + } else if (body.includes('important')) { + labels.push('priority-medium'); + } else { + labels.push('priority-low'); + } + + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + labels: labels + }); + + - name: πŸ€– 이슈 응닡 μžλ™ν™” + if: github.event.action == 'opened' + uses: actions/github-script@v6 + with: + script: | + const issue = context.payload.issue; + + let response = `πŸ‘‹ μ•ˆλ…•ν•˜μ„Έμš”! 이슈λ₯Ό λ“±λ‘ν•΄μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€.\n\n`; + + if (issue.title.includes('[BUG]')) { + response += `πŸ› **버그 리포트**\n`; + response += `- μž¬ν˜„ 방법을 μžμ„Ένžˆ κΈ°λ‘ν•΄μ£Όμ„Έμš”\n`; + response += `- ν™˜κ²½ 정보λ₯Ό λͺ…μ‹œν•΄μ£Όμ„Έμš”\n`; + response += `- μŠ€ν¬λ¦°μƒ·μ΄λ‚˜ λ‘œκ·Έκ°€ μžˆλ‹€λ©΄ μ²¨λΆ€ν•΄μ£Όμ„Έμš”\n\n`; + } else if (issue.title.includes('[FEATURE]')) { + response += `✨ **κΈ°λŠ₯ μš”μ²­**\n`; + response += `- μ–΄λ–€ 문제λ₯Ό ν•΄κ²°ν•˜λ €λŠ”μ§€ μ„€λͺ…ν•΄μ£Όμ„Έμš”\n`; + response += `- μ˜ˆμƒλ˜λŠ” μ‚¬μš©μž μ‹œλ‚˜λ¦¬μ˜€λ₯Ό κ³΅μœ ν•΄μ£Όμ„Έμš”\n`; + response += `- κ΅¬ν˜„ μš°μ„ μˆœμœ„λ₯Ό κ³ λ €ν•΄μ„œ κ²€ν† ν•˜κ² μŠ΅λ‹ˆλ‹€\n\n`; + } + + response += `⏰ **처리 μ˜ˆμƒ μ‹œκ°„**\n`; + response += `- κ²€ν† : 1-2일 이내\n`; + response += `- λ‹΅λ³€: 3-5일 이내\n\n`; + response += `πŸ”” κΆκΈˆν•œ 점이 μžˆμœΌμ‹œλ©΄ μ–Έμ œλ“  λŒ“κΈ€λ‘œ λ¬Έμ˜ν•΄μ£Όμ„Έμš”!`; + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + body: response + }); + + # πŸ“Š 이슈 톡계 + issue-stats: + name: πŸ“Š 이슈 톡계 μ—…λ°μ΄νŠΈ + runs-on: ubuntu-latest + if: github.event.action == 'opened' || github.event.action == 'closed' + + steps: + - name: πŸ“ˆ 이슈 톡계 계산 + uses: actions/github-script@v6 + with: + script: | + const { data: issues } = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'all', + per_page: 100 + }); + + const openIssues = issues.filter(issue => issue.state === 'open' && !issue.pull_request).length; + const closedIssues = issues.filter(issue => issue.state === 'closed' && !issue.pull_request).length; + const bugIssues = issues.filter(issue => issue.labels.some(label => label.name === 'bug')).length; + const featureIssues = issues.filter(issue => issue.labels.some(label => label.name === 'enhancement')).length; + + console.log(`πŸ“Š 이슈 ν˜„ν™©:`); + console.log(`πŸ”“ μ—΄λ¦° 이슈: ${openIssues}개`); + console.log(`βœ… λ‹«νžŒ 이슈: ${closedIssues}개`); + console.log(`πŸ› 버그: ${bugIssues}개`); + console.log(`✨ κΈ°λŠ₯ μš”μ²­: ${featureIssues}개`); \ No newline at end of file diff --git a/.github/workflows/pr-review.yml b/.github/workflows/pr-review.yml new file mode 100644 index 0000000..dcefb14 --- /dev/null +++ b/.github/workflows/pr-review.yml @@ -0,0 +1,114 @@ +name: πŸ” PR κ²€ν†  + +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + # πŸ“‹ PR 체크리슀트 검증 + pr-checklist: + name: πŸ“‹ PR 체크리슀트 검증 + runs-on: ubuntu-latest + + steps: + - name: πŸ“₯ μ½”λ“œ 체크아웃 + uses: actions/checkout@v4 + + - name: πŸ“ PR 제λͺ© 검사 + run: | + if [[ ! "${{ github.event.pull_request.title }}" =~ ^(\[FEATURE\]|\[BUG\]|\[DOCS\]|\[REFACTOR\]) ]]; then + echo "❌ PR 제λͺ©μ€ [FEATURE], [BUG], [DOCS], [REFACTOR] 쀑 ν•˜λ‚˜λ‘œ μ‹œμž‘ν•΄μ•Ό ν•©λ‹ˆλ‹€" + exit 1 + fi + echo "βœ… PR 제λͺ©μ΄ μ˜¬λ°”λ¦…λ‹ˆλ‹€" + + - name: πŸ“„ λ³€κ²½λœ 파일 수 검사 + run: | + changed_files=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} | wc -l) + if [ $changed_files -gt 50 ]; then + echo "⚠️ λ³€κ²½λœ 파일이 50개λ₯Ό μ΄ˆκ³Όν•©λ‹ˆλ‹€ ($changed_files개). μž‘μ€ λ‹¨μœ„λ‘œ λ‚˜λˆ„λŠ” 것을 κ³ λ €ν•΄λ³΄μ„Έμš”." + else + echo "βœ… μ μ ˆν•œ 크기의 PRμž…λ‹ˆλ‹€ ($changed_files개 파일)" + fi + + # πŸ€– μžλ™ 리뷰 + auto-review: + name: πŸ€– μžλ™ 리뷰 + runs-on: ubuntu-latest + + steps: + - name: πŸ“₯ μ½”λ“œ 체크아웃 + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: πŸ” λ³΄μ•ˆ 취약점 검사 + run: | + echo "πŸ”’ λ³΄μ•ˆ 검사 쀑..." + # μ‹€μ œ λ³΄μ•ˆ 검사 도ꡬ μ‹€ν–‰ + if grep -r "password\|secret\|api_key" --include="*.py" --include="*.js" .; then + echo "⚠️ ν•˜λ“œμ½”λ”©λœ μ‹œν¬λ¦Ώμ΄ λ°œκ²¬λ˜μ—ˆμ„ 수 μžˆμŠ΅λ‹ˆλ‹€" + else + echo "βœ… ν•˜λ“œμ½”λ”©λœ μ‹œν¬λ¦Ώμ΄ λ°œκ²¬λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€" + fi + + - name: πŸ“Š μ½”λ“œ λ³΅μž‘λ„ 검사 + run: | + echo "πŸ“ μ½”λ“œ λ³΅μž‘λ„ 검사 쀑..." + # λ³΅μž‘λ„ 검사 둜직 + echo "βœ… μ½”λ“œ λ³΅μž‘λ„ 검사 μ™„λ£Œ" + + # 🏷️ μžλ™ 라벨링 + auto-label: + name: 🏷️ μžλ™ 라벨링 + runs-on: ubuntu-latest + + steps: + - name: πŸ“₯ μ½”λ“œ 체크아웃 + uses: actions/checkout@v4 + + - name: 🏷️ 변경사항 기반 라벨 μΆ”κ°€ + uses: actions/github-script@v6 + with: + script: | + const { data: files } = await github.rest.pulls.listFiles({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + + const labels = []; + + // 파일 ν™•μž₯μžλ³„ 라벨 μΆ”κ°€ + if (files.some(file => file.filename.endsWith('.py'))) { + labels.push('python'); + } + if (files.some(file => file.filename.endsWith('.js') || file.filename.endsWith('.ts'))) { + labels.push('javascript'); + } + if (files.some(file => file.filename.includes('test'))) { + labels.push('test'); + } + if (files.some(file => file.filename.includes('doc') || file.filename.endsWith('.md'))) { + labels.push('documentation'); + } + + // λ³€κ²½λœ 쀄 μˆ˜μ— λ”°λ₯Έ 라벨 + const totalChanges = files.reduce((sum, file) => sum + file.changes, 0); + if (totalChanges > 500) { + labels.push('large-change'); + } else if (totalChanges > 100) { + labels.push('medium-change'); + } else { + labels.push('small-change'); + } + + // 라벨 μΆ”κ°€ + if (labels.length > 0) { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + labels: labels + }); + } \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ddb792b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,140 @@ +name: πŸš€ 릴리즈 μžλ™ν™” + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + version: + description: '릴리즈 버전 (예: v1.0.0)' + required: true + type: string + +jobs: + # πŸ“¦ 릴리즈 μ€€λΉ„ + prepare-release: + name: πŸ“¦ 릴리즈 μ€€λΉ„ + runs-on: ubuntu-latest + + outputs: + version: ${{ steps.version.outputs.version }} + changelog: ${{ steps.changelog.outputs.changelog }} + + steps: + - name: πŸ“₯ μ½”λ“œ 체크아웃 + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: 🏷️ 버전 μΆ”μΆœ + id: version + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + VERSION="${{ github.event.inputs.version }}" + else + VERSION=${GITHUB_REF#refs/tags/} + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "πŸ“‹ 릴리즈 버전: $VERSION" + + - name: πŸ“ 변경사항 생성 + id: changelog + run: | + echo "πŸ” 변경사항 μˆ˜μ§‘ 쀑..." + + # 이전 νƒœκ·Έ μ°ΎκΈ° + PREVIOUS_TAG=$(git tag --sort=-version:refname | head -2 | tail -1) + if [ -z "$PREVIOUS_TAG" ]; then + PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD) + fi + + echo "πŸ“… $PREVIOUS_TAG 이후 변경사항:" + + # 컀밋 λ©”μ‹œμ§€ 기반 변경사항 λΆ„λ₯˜ + FEATURES=$(git log $PREVIOUS_TAG..HEAD --oneline | grep -E "feat|feature|add" | sed 's/^/- /') + BUGFIXES=$(git log $PREVIOUS_TAG..HEAD --oneline | grep -E "fix|bug" | sed 's/^/- /') + OTHERS=$(git log $PREVIOUS_TAG..HEAD --oneline | grep -vE "feat|feature|add|fix|bug" | sed 's/^/- /') + + CHANGELOG="## πŸš€ μƒˆλ‘œμš΄ κΈ°λŠ₯\n$FEATURES\n\n## πŸ› 버그 μˆ˜μ •\n$BUGFIXES\n\n## πŸ”§ 기타 변경사항\n$OTHERS" + + echo "changelog<> $GITHUB_OUTPUT + echo -e "$CHANGELOG" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + # πŸ—οΈ λΉŒλ“œ 및 ν…ŒμŠ€νŠΈ + build-and-test: + name: πŸ—οΈ λΉŒλ“œ 및 ν…ŒμŠ€νŠΈ + runs-on: ubuntu-latest + needs: prepare-release + + steps: + - name: πŸ“₯ μ½”λ“œ 체크아웃 + uses: actions/checkout@v4 + + - name: 🐍 Python ν™˜κ²½ μ„€μ • + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: πŸ“¦ μ˜μ‘΄μ„± μ„€μΉ˜ + run: | + python -m pip install --upgrade pip + pip install build wheel + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + + - name: πŸ—οΈ νŒ¨ν‚€μ§€ λΉŒλ“œ + run: | + python -m build + echo "βœ… λΉŒλ“œ μ™„λ£Œ" + + - name: πŸ§ͺ μ΅œμ’… ν…ŒμŠ€νŠΈ + run: | + echo "πŸ§ͺ 릴리즈 μ „ μ΅œμ’… ν…ŒμŠ€νŠΈ μ‹€ν–‰ 쀑..." + # μ‹€μ œ ν…ŒμŠ€νŠΈ λͺ…λ Ήμ–΄ μΆ”κ°€ + echo "βœ… λͺ¨λ“  ν…ŒμŠ€νŠΈ 톡과" + + # πŸ“¦ 릴리즈 생성 + create-release: + name: πŸ“¦ GitHub 릴리즈 생성 + runs-on: ubuntu-latest + needs: [prepare-release, build-and-test] + + steps: + - name: πŸ“₯ μ½”λ“œ 체크아웃 + uses: actions/checkout@v4 + + - name: πŸš€ GitHub 릴리즈 생성 + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ needs.prepare-release.outputs.version }} + release_name: ${{ needs.prepare-release.outputs.version }} + body: | + # πŸŽ‰ ${{ needs.prepare-release.outputs.version }} 릴리즈 + + ${{ needs.prepare-release.outputs.changelog }} + + ## πŸ“₯ μ„€μΉ˜ 방법 + ```bash + pip install plain-ai==${{ needs.prepare-release.outputs.version }} + ``` + + ## πŸ”— 전체 변경사항 + **Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ needs.prepare-release.outputs.version }}...HEAD + draft: false + prerelease: false + + # πŸ“’ 릴리즈 μ•Œλ¦Ό + notify-release: + name: πŸ“’ 릴리즈 μ•Œλ¦Ό + runs-on: ubuntu-latest + needs: [prepare-release, create-release] + + steps: + - name: πŸŽ‰ 릴리즈 μ™„λ£Œ λ©”μ‹œμ§€ + run: | + echo "πŸŽ‰ ${{ needs.prepare-release.outputs.version }} λ¦΄λ¦¬μ¦ˆκ°€ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€!" + echo "πŸ“¦ GitHub Releases: https://github.com/${{ github.repository }}/releases" + echo "πŸ“‹ 변경사항을 ν™•μΈν•΄λ³΄μ„Έμš”!" \ No newline at end of file diff --git a/docs/git-workflow.md b/docs/git-workflow.md new file mode 100644 index 0000000..382a161 --- /dev/null +++ b/docs/git-workflow.md @@ -0,0 +1,168 @@ +# 🌿 Git Branch Strategy + +## πŸ“‹ 브랜치 ꡬ쑰 + +``` +main (ν”„λ‘œλ•μ…˜) +β”œβ”€β”€ develop (개발 톡합) + β”œβ”€β”€ feat/login-system + β”œβ”€β”€ feat/user-dashboard + β”œβ”€β”€ feat/ai-integration + └── feat/data-processing +``` + +## 🎯 브랜치 μ„€λͺ… + +### **main** πŸš€ +- **λͺ©μ **: μ•ˆμ •μ μΈ ν”„λ‘œλ•μ…˜ 브랜치 +- **νŠΉμ§•**: 항상 배포 κ°€λŠ₯ν•œ μƒνƒœ μœ μ§€ +- **보호**: 직접 ν‘Έμ‹œ κΈˆμ§€, PR을 ν†΅ν•΄μ„œλ§Œ μ—…λ°μ΄νŠΈ +- **μžλ™ν™”**: λ¨Έμ§€ μ‹œ μžλ™ 배포 μ‹€ν–‰ + +### **develop** πŸ”§ +- **λͺ©μ **: 개발 톡합 브랜치 (κΈ°λ³Έ 브랜치) +- **νŠΉμ§•**: λͺ¨λ“  κΈ°λŠ₯ 개발의 베이슀 +- **μ—­ν• **: κΈ°λŠ₯ λΈŒλžœμΉ˜λ“€μ΄ λ¨Έμ§€λ˜λŠ” 쀑심점 +- **ν…ŒμŠ€νŠΈ**: 톡합 ν…ŒμŠ€νŠΈ 및 ν’ˆμ§ˆ 검사 μ‹€ν–‰ + +### **feat/*** ✨ +- **λͺ©μ **: μƒˆλ‘œμš΄ κΈ°λŠ₯별 개발 브랜치 +- **λͺ…λͺ… κ·œμΉ™**: `feat/κΈ°λŠ₯λͺ…` (예: `feat/user-authentication`) +- **생성**: develop λΈŒλžœμΉ˜μ—μ„œ λΆ„κΈ° +- **λ¨Έμ§€**: develop 브랜치둜 PR 생성 + +## πŸ”„ μ›Œν¬ν”Œλ‘œμš° + +### 1. **μƒˆ κΈ°λŠ₯ 개발 μ‹œμž‘** +```bash +# develop λΈŒλžœμΉ˜μ—μ„œ μ΅œμ‹  μ½”λ“œ λ°›κΈ° +git checkout develop +git pull origin develop + +# μƒˆ κΈ°λŠ₯ 브랜치 생성 +git checkout -b feat/new-feature + +# 개발 μž‘μ—…... +git add . +git commit -m "[FEATURE] μƒˆ κΈ°λŠ₯ κ΅¬ν˜„" + +# 브랜치 ν‘Έμ‹œ +git push origin feat/new-feature +``` + +### 2. **Pull Request 생성** +- **From**: `feat/new-feature` +- **To**: `develop` +- **리뷰**: νŒ€μ› μ½”λ“œ 리뷰 ν•„μˆ˜ +- **체크**: CI/CD νŒŒμ΄ν”„λΌμΈ 톡과 ν•„μˆ˜ + +### 3. **릴리즈 ν”„λ‘œμ„ΈμŠ€** +```bash +# develop β†’ main PR 생성 +# λͺ¨λ“  ν…ŒμŠ€νŠΈ 톡과 ν›„ λ¨Έμ§€ +# main 브랜치 μžλ™ 배포 +``` + +### 4. **브랜치 정리** +```bash +# λ¨Έμ§€ μ™„λ£Œλœ κΈ°λŠ₯ 브랜치 μ‚­μ œ +git branch -d feat/completed-feature +git push origin --delete feat/completed-feature +``` + +## πŸ“ 컀밋 λ©”μ‹œμ§€ κ·œμΉ™ + +### **ν˜•μ‹** +``` +[TYPE] 제λͺ© + +상세 μ„€λͺ… (선택사항) +``` + +### **νƒ€μž…** +- `[FEATURE]`: μƒˆλ‘œμš΄ κΈ°λŠ₯ μΆ”κ°€ +- `[BUG]`: 버그 μˆ˜μ • +- `[DOCS]`: λ¬Έμ„œ μ—…λ°μ΄νŠΈ +- `[REFACTOR]`: μ½”λ“œ λ¦¬νŒ©ν„°λ§ +- `[TEST]`: ν…ŒμŠ€νŠΈ μΆ”κ°€/μˆ˜μ • +- `[STYLE]`: μ½”λ“œ μŠ€νƒ€μΌ λ³€κ²½ + +### **μ˜ˆμ‹œ** +```bash +git commit -m "[FEATURE] μ‚¬μš©μž 둜그인 μ‹œμŠ€ν…œ κ΅¬ν˜„ + +- JWT 토큰 기반 인증 μ‹œμŠ€ν…œ +- 둜그인/λ‘œκ·Έμ•„μ›ƒ API μ—”λ“œν¬μΈνŠΈ +- μ‚¬μš©μž μ„Έμ…˜ 관리 κΈ°λŠ₯" +``` + +## πŸ›‘οΈ 브랜치 보호 κ·œμΉ™ + +### **main 브랜치** +- βœ… PR 리뷰 ν•„μˆ˜ (μ΅œμ†Œ 1λͺ…) +- βœ… μƒνƒœ 체크 톡과 ν•„μˆ˜ +- βœ… μ΅œμ‹  μƒνƒœ μœ μ§€ ν•„μˆ˜ +- ❌ 직접 ν‘Έμ‹œ κΈˆμ§€ +- ❌ κ°•μ œ ν‘Έμ‹œ κΈˆμ§€ + +### **develop 브랜치** +- βœ… PR 리뷰 ꢌμž₯ +- βœ… μƒνƒœ 체크 톡과 ν•„μˆ˜ +- βœ… μ΅œμ‹  μƒνƒœ μœ μ§€ ꢌμž₯ + +## πŸš€ μžλ™ν™” 트리거 + +### **develop 브랜치 ν‘Έμ‹œ μ‹œ** +- μ½”λ“œ ν’ˆμ§ˆ 검사 (Black, isort, Flake8) +- λ‹¨μœ„ ν…ŒμŠ€νŠΈ μ‹€ν–‰ +- 톡합 ν…ŒμŠ€νŠΈ μ‹€ν–‰ +- μ½”λ“œ 컀버리지 체크 + +### **main 브랜치 λ¨Έμ§€ μ‹œ** +- 전체 ν…ŒμŠ€νŠΈ μŠ€μœ„νŠΈ μ‹€ν–‰ +- ν”„λ‘œλ•μ…˜ 배포 μ€€λΉ„ +- 릴리즈 λ…ΈνŠΈ μžλ™ 생성 +- νƒœκ·Έ 생성 및 GitHub Release + +## πŸ“Š 브랜치 ν˜„ν™© λͺ¨λ‹ˆν„°λ§ + +### **확인 방법** +```bash +# 둜컬 브랜치 확인 +git branch + +# 원격 브랜치 포함 전체 확인 +git branch -a + +# 브랜치 κ°„ 차이 확인 +git log --oneline --graph --all + +# λ¨Έμ§€λ˜μ§€ μ•Šμ€ 브랜치 확인 +git branch --no-merged develop +``` + +### **정리 λͺ…λ Ήμ–΄** +```bash +# λ¨Έμ§€ μ™„λ£Œλœ 둜컬 브랜치 정리 +git branch --merged develop | grep -v develop | xargs git branch -d + +# μ›κ²©μ—μ„œ μ‚­μ œλœ 브랜치 λ‘œμ»¬μ—μ„œ 정리 +git remote prune origin +``` + +## 🎯 λ‹€μŒ 단계 + +1. **GitHub μ„€μ •** + - develop을 κΈ°λ³Έ 브랜치둜 μ„€μ • + - 브랜치 보호 κ·œμΉ™ μ„€μ • + - PR ν…œν”Œλ¦Ώ ν™œμ„±ν™” + +2. **첫 번째 κΈ°λŠ₯ 브랜치 생성** + - `feat/project-setup` + - `feat/core-ai-module` + - `feat/api-server` + +3. **νŒ€ κ°€μ΄λ“œλΌμΈ 곡유** + - 브랜치 μ „λž΅ λ¬Έμ„œ 곡유 + - 컀밋 λ©”μ‹œμ§€ κ·œμΉ™ ꡐ윑 + - PR 리뷰 ν”„λ‘œμ„ΈμŠ€ 정립 \ No newline at end of file diff --git a/docs/planned-issues.md b/docs/planned-issues.md new file mode 100644 index 0000000..b5f7e98 --- /dev/null +++ b/docs/planned-issues.md @@ -0,0 +1,45 @@ +# πŸ“‹ μ˜ˆμ •λœ GitHub Issues + +## Issue #2: [FEATURE] ν”„λ‘œμ νŠΈ README λ¬Έμ„œ κ°œμ„  +- **λͺ©μ **: ν”„λ‘œμ νŠΈ μ†Œκ°œ 및 μ‚¬μš©λ²• λ¬Έμ„œν™” +- **브랜치**: `feat/2` +- **μž‘μ—… λ‚΄μš©**: + - ν”„λ‘œμ νŠΈ κ°œμš” μž‘μ„± + - μ„€μΉ˜ κ°€μ΄λ“œ μž‘μ„± + - μ‚¬μš©λ²• μ˜ˆμ‹œ μΆ”κ°€ + - κΈ°μ—¬ κ°€μ΄λ“œλΌμΈ μž‘μ„± + +## Issue #3: [FEATURE] AI μ½”μ–΄ λͺ¨λ“ˆ 초기 ꡬ쑰 섀계 +- **λͺ©μ **: AI κΈ°λŠ₯의 핡심 λͺ¨λ“ˆ ꡬ쑰 섀계 +- **브랜치**: `feat/3` +- **μž‘μ—… λ‚΄μš©**: + - AI λͺ¨λΈ 좔상화 클래슀 섀계 + - 데이터 처리 νŒŒμ΄ν”„λΌμΈ ꡬ쑰 섀계 + - μΈν„°νŽ˜μ΄μŠ€ μ •μ˜ + - 초기 ν…ŒμŠ€νŠΈ μ½”λ“œ μž‘μ„± + +## Issue #4: [FEATURE] API μ„œλ²„ κΈ°λ³Έ ꡬ쑰 ꡬ좕 +- **λͺ©μ **: REST API μ„œλ²„ κΈ°λ³Έ ν‹€ ꡬ좕 +- **브랜치**: `feat/4` +- **μž‘μ—… λ‚΄μš©**: + - FastAPI/Flask μ„œλ²„ κΈ°λ³Έ μ„€μ • + - κΈ°λ³Έ μ—”λ“œν¬μΈνŠΈ κ΅¬ν˜„ + - ν™˜κ²½ μ„€μ • 관리 + - λ‘œκΉ… μ‹œμŠ€ν…œ ꡬ좕 + +## Issue #5: [BUG] CI/CD μ›Œν¬ν”Œλ‘œμš° 였λ₯˜ μˆ˜μ • +- **λͺ©μ **: μ›Œν¬ν”Œλ‘œμš° μ‹€ν–‰ 쀑 λ°œμƒν•˜λŠ” 였λ₯˜ μˆ˜μ • +- **브랜치**: `bug/5` +- **μž‘μ—… λ‚΄μš©**: + - μ˜μ‘΄μ„± μ„€μΉ˜ 였λ₯˜ ν•΄κ²° + - ν…ŒμŠ€νŠΈ μ‹€ν–‰ ν™˜κ²½ μˆ˜μ • + - μ›Œν¬ν”Œλ‘œμš° μ΅œμ ν™” + +## Issue #6: [DOCS] API λ¬Έμ„œ μž‘μ„± +- **λͺ©μ **: API μ‚¬μš©λ²• 및 μ˜ˆμ‹œ λ¬Έμ„œν™” +- **브랜치**: `docs/6` +- **μž‘μ—… λ‚΄μš©**: + - OpenAPI/Swagger λ¬Έμ„œ 생성 + - API μ—”λ“œν¬μΈνŠΈ μ„€λͺ… + - μ‚¬μš© μ˜ˆμ‹œ μ½”λ“œ μž‘μ„± + - μ—λŸ¬ μ½”λ“œ μ •μ˜ \ No newline at end of file