⚙️ [기능추가][배포][인증] 인증서 생성 및 https 프록시 설정 필요 #9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # =================================================================== | |
| # 자동 QA 이슈 생성 워크플로우 | |
| # =================================================================== | |
| # | |
| # 이 워크플로우는 GitHub 이슈/PR 댓글에서 @suh-lab 멘션을 감지하여 | |
| # QA(시험요청) 이슈를 자동으로 생성합니다. | |
| # | |
| # 작동 방식: | |
| # 1. 이슈 또는 PR 댓글에서 "@suh-lab" + "create" + "qa" 감지 (순서 무관) | |
| # 2. 댓글에 👀 리액션 추가 (확인 표시) | |
| # 3. 원본 이슈/PR 제목 또는 브랜치명에서 이슈 번호 추출 | |
| # 4. 제목에서 불필요한 이모지 및 키워드 제거 | |
| # 5. "🔍 [시험요청]" 접두사를 붙여 QA 이슈 생성 | |
| # 6. 시험 템플릿이 적용된 본문 자동 생성 | |
| # 7. 원본 이슈/PR에 QA 이슈 링크 댓글 작성 | |
| # | |
| # 지원 기능: | |
| # - 이슈와 PR 모두 지원 | |
| # - PR 브랜치명에서 이슈 번호 자동 추출 | |
| # - 명령어 순서 무관 (@suh-lab create qa, @suh-lab qa create 모두 가능) | |
| # - 이모지 자동 제거 (🚀, 🔥, ⌛ 등) | |
| # - 이슈 타입 키워드 제거 ([버그], [디자인], [기능요청] 등) | |
| # - 카테고리 및 내용 자동 추출 | |
| # - QA 템플릿 자동 적용 | |
| # - 담당자 자동 할당 | |
| # - 확인 리액션 (👀) 자동 추가 | |
| # | |
| # 사용 예시: | |
| # 원본 이슈: "🚀 [기능개발][로그인] 소셜 로그인 추가" | |
| # QA 이슈: "🔍 [시험요청][로그인] 소셜 로그인 추가" | |
| # | |
| # 트리거 방법: | |
| # 이슈/PR 댓글에 "@suh-lab create qa" 또는 "@suh-lab qa create" 입력 | |
| # | |
| # =================================================================== | |
| name: PROJECT-CREATE-QA-ISSUE | |
| on: | |
| issue_comment: | |
| types: [created] | |
| # =================================================================== | |
| # 설정 변수 | |
| # =================================================================== | |
| env: | |
| # QA 이슈 기본 담당자 설정 | |
| # - 'default': 댓글 작성자가 담당자로 지정됨 | |
| # - GitHub ID (예: 'Cassiiopeia'): 해당 사용자가 담당자로 지정됨 | |
| DEFAULT_QA_ASSIGNEE: 'default' | |
| jobs: | |
| create-qa: | |
| # @suh-lab과 create, qa 키워드가 모두 포함되어 있으면 실행 (순서 무관) | |
| if: | | |
| contains(github.event.comment.body, '@suh-lab') && | |
| contains(github.event.comment.body, 'create') && | |
| contains(github.event.comment.body, 'qa') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| pull-requests: write | |
| contents: read | |
| steps: | |
| - name: 댓글에 👀 리액션 추가 | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const commentId = context.payload.comment.id; | |
| await github.rest.reactions.createForIssueComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: commentId, | |
| content: 'eyes' | |
| }); | |
| console.log('👀 댓글에 확인 리액션 추가 완료'); | |
| - name: QA 이슈 생성 | |
| uses: actions/github-script@v7 | |
| env: | |
| DEFAULT_QA_ASSIGNEE: ${{ env.DEFAULT_QA_ASSIGNEE }} | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const issue = context.payload.issue; | |
| const commenter = context.payload.comment.user.login; | |
| // 담당자 결정: 'default'면 댓글 작성자, 그 외에는 지정된 GitHub ID | |
| const defaultAssignee = process.env.DEFAULT_QA_ASSIGNEE || 'default'; | |
| const assignee = (defaultAssignee === 'default') ? commenter : defaultAssignee; | |
| console.log(`👤 담당자 설정: ${assignee} (설정값: ${defaultAssignee})`); | |
| const issueOrPrNumber = issue.number; | |
| const isPullRequest = !!issue.pull_request; | |
| console.log(`📌 타입: ${isPullRequest ? 'Pull Request' : 'Issue'}`); | |
| console.log(`📌 번호: #${issueOrPrNumber}`); | |
| let originalIssueNumber = issueOrPrNumber; | |
| let title = issue.title; | |
| // PR인 경우 브랜치명에서 이슈 번호 추출 | |
| if (isPullRequest) { | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: issueOrPrNumber | |
| }); | |
| const branchName = pr.head.ref; | |
| console.log(`🌿 브랜치명: ${branchName}`); | |
| // 브랜치명에서 #31 형태의 이슈 번호 추출 | |
| const issueMatch = branchName.match(/#(\d+)/); | |
| if (issueMatch) { | |
| originalIssueNumber = parseInt(issueMatch[1]); | |
| console.log(`🔍 추출된 이슈 번호: #${originalIssueNumber}`); | |
| // 원본 이슈 정보 가져오기 | |
| try { | |
| const { data: originalIssue } = await github.rest.issues.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: originalIssueNumber | |
| }); | |
| title = originalIssue.title; | |
| console.log(`📋 원본 이슈 제목: ${title}`); | |
| } catch (error) { | |
| console.log(`⚠️ 이슈 #${originalIssueNumber}를 찾을 수 없어 PR 제목 사용`); | |
| } | |
| } | |
| } | |
| console.log('📝 원본 제목:', title); | |
| // 1. 이모지 제거 | |
| title = title.replace(/[\u{1F300}-\u{1F9FF}]/gu, '') | |
| .replace(/[\u{2600}-\u{26FF}]/gu, '') | |
| .replace(/[\u{2700}-\u{27BF}]/gu, ''); | |
| // 2. 특정 키워드 앞부분까지 제거 | |
| const keywordPattern = /^.*?\[(버그|디자인|기능요청|기능추가|기능개선)\]/; | |
| title = title.replace(keywordPattern, ''); | |
| // 3. 공백 정리 | |
| title = title.replace(/^\s+/, '').trim(); | |
| console.log('✨ 정제된 제목:', title); | |
| // 4. QA 이슈 제목 생성 | |
| const qaTitle = `🔍 [시험요청]${title}`; | |
| console.log('🎯 QA 이슈 제목:', qaTitle); | |
| // QA 이슈 본문 생성 | |
| const qaBody = `🔗 ISSUE 정보 | |
| --- | |
| - #${originalIssueNumber} | |
| ${isPullRequest ? `- PR: #${issueOrPrNumber}` : ''} | |
| 🔗 PR 정보 | |
| --- | |
| ${isPullRequest ? `- #${issueOrPrNumber}` : '<!-- PR 번호나 URL을 작성해주세요 -->'} | |
| 🧩 시험 대상 | |
| --- | |
| ${title.replace(/^\[.*?\]\s*/, '')}에 대한 기능 시험 | |
| 📋 테스트 시나리오 | |
| --- | |
| 1. 기본 기능 동작 확인 | |
| 2. 엣지 케이스 테스트 | |
| 3. UI/UX 확인 | |
| 4. 에러 처리 확인 | |
| ⚙️ 테스트 환경 | |
| --- | |
| - **프로젝트 Version**: | |
| - **OS**: | |
| - **브라우저**: | |
| - **기기**: | |
| 🙋♂️ 담당자 | |
| --- | |
| - **시험담당**: 이름 | |
| - **요청자**: @${issue.user.login} | |
| --- | |
| *🤖 이 이슈는 @suh-lab 봇에 의해 자동 생성되었습니다.*`; | |
| try { | |
| // QA 이슈 생성 | |
| const { data: qaIssue } = await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: qaTitle, | |
| body: qaBody, | |
| labels: ['작업 전'], | |
| assignees: [assignee] | |
| }); | |
| console.log(`✅ QA 이슈 생성 완료: #${qaIssue.number}`); | |
| // 원본 이슈/PR에 댓글 | |
| const commentBody = isPullRequest | |
| ? [ | |
| '✅ **시험(QA) 요청 이슈가 생성되었습니다!**', | |
| '', | |
| '📋 **시험(QA) 요청**', | |
| `- #${qaIssue.number}`, | |
| '', | |
| '🔗 **이슈정보**', | |
| `- #${originalIssueNumber}`, | |
| '', | |
| '시험 완료 후 결과를 QA 이슈에 기록해주세요.' | |
| ].join('\n') | |
| : [ | |
| '✅ **시험(QA) 요청 이슈가 생성되었습니다!**', | |
| '', | |
| '📋 **시험(QA) 요청**', | |
| `- #${qaIssue.number}`, | |
| '', | |
| '시험 완료 후 결과를 QA 이슈에 기록해주세요.' | |
| ].join('\n'); | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issueOrPrNumber, | |
| body: commentBody | |
| }); | |
| console.log('💬 원본에 댓글 작성 완료'); | |
| console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); | |
| console.log('🎉 QA 이슈 생성 프로세스 완료!'); | |
| console.log(` • ${isPullRequest ? 'PR' : 'Issue'}: #${issueOrPrNumber}`); | |
| console.log(` • 원본 Issue: #${originalIssueNumber}`); | |
| console.log(` • QA Issue: #${qaIssue.number}`); | |
| console.log(` • 담당자: @${assignee}`); | |
| console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); | |
| } catch (error) { | |
| console.error('❌ 에러 발생:', error); | |
| // 에러 발생 시 댓글 | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issueOrPrNumber, | |
| body: `❌ QA 이슈 생성 중 오류가 발생했습니다.\n\n\`\`\`\n${error.message}\n\`\`\`` | |
| }); | |
| } |