From ef994ec5b30be22fac263ae94bdb622778e9f8ac Mon Sep 17 00:00:00 2001 From: Taek_2 Date: Thu, 22 Jan 2026 10:39:09 +0900 Subject: [PATCH 1/5] =?UTF-8?q?chore:=20=EB=A6=AC=EB=B7=B0=20=EB=A6=AC?= =?UTF-8?q?=EB=A7=88=EC=9D=B8=EB=8D=94=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20?= =?UTF-8?q?=EB=8F=84=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/scripts/review-reminder.js | 122 +++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 .github/scripts/review-reminder.js diff --git a/.github/scripts/review-reminder.js b/.github/scripts/review-reminder.js new file mode 100644 index 00000000..f5e62af0 --- /dev/null +++ b/.github/scripts/review-reminder.js @@ -0,0 +1,122 @@ +module.exports = async ({github, context, core}) => { + + // 최소 PR 생성 시간 + const LIMIT_HOURS = 0; + const LIMIT_MS = LIMIT_HOURS * 60 * 60 * 1000; + + const WEBHOOK_URL = process.env.SLACK_WEBHOOK_REVIEW; + + // GitHub, Slack 정보 + const USER_MAP = { + 'etama123': 'U0995MPSZ62', + 'oungsi2000': 'U098U2R57NK', + 'parkjiminnnn': 'U098U8SLXHD' + }; + + const repoName = context.repo.repo; + + if (!WEBHOOK_URL) { + core.setFailed("❌ Error: SLACK_WEBHOOK_REVIEW 환경변수가 설정되지 않았습니다."); + return; + } + + try { + // GitHub API 호출로 Open PR 목록 요청 + const {data: prs} = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', // 오픈 PR + sort: 'created', // 정렬 기준 + direction: 'asc' // 오름차순 + }); + + const now = new Date(); + const delayedPrs = []; // 알림 대상 + + for (const pr of prs) { + const createdDate = new Date(pr.created_at); + const diffTime = now - createdDate; // PR 생성 시간 차이 (ms) + + if (diffTime >= LIMIT_MS) { + + // 리뷰어 정보 가공 (없으면 '미지정' 처리) + const reviewers = pr.requested_reviewers.length > 0 + ? pr.requested_reviewers.map(r => { + const slackId = USER_MAP[r.login]; + return slackId ? `<@${slackId}>` : r.login; + }).join(', ') + : '(리뷰어 미지정)'; + + // 지난 시간 + const passedHours = Math.floor(diffTime / (1000 * 60 * 60)); + + const authorSlackId = USER_MAP[pr.user.login]; + const authorDisplay = authorSlackId ? `<@${authorSlackId}>` : pr.user.login; + + // 알림 전송 객체 생성 + delayedPrs.push({ + title: pr.title, + url: pr.html_url, + author: authorDisplay, + reviewers: reviewers, + hours: passedHours + }); + } + } + + // 지연 PR 없을 시 종료 + if (delayedPrs.length === 0) { + return; + } + + const message = { + text: `🚨 (${repoName}) 코드 리뷰 리마인더`, + blocks: [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": `🔥 (${repoName}) 코드 리뷰 리마인더`, + "emoji": true + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": `현재 *${LIMIT_HOURS}시간* 이상 대기 중인 PR이 *${delayedPrs.length}건* 있습니다.` + } + }, + {"type": "divider"} + ] + }; + + // 지연 PR 하나씩 메시지 추가 + delayedPrs.forEach((pr, index) => { + message.blocks.push({ + "type": "section", + "text": { + "type": "mrkdwn", + "text": `*${index + 1}. <${pr.url}|${pr.title}>*\n` + + `⏳ *${pr.hours}시간* 경과\n` + + `👤 작성자: ${pr.author}\n` + + `👀 리뷰어: ${pr.reviewers}` + } + }); + message.blocks.push({"type": "divider"}); + }); + + const response = await fetch(WEBHOOK_URL, { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(message) + }); + + if (!response.ok) { + throw new Error(`Slack 전송 실패 Status: ${response.status}`); + } + + } catch (error) { + core.setFailed(`❌ 스크립트 실행 중 에러 발생: ${error.message}`); + } +}; From 3d6df828e2318bf3f4b1798dad125b041a335b25 Mon Sep 17 00:00:00 2001 From: Taek_2 Date: Thu, 22 Jan 2026 10:40:28 +0900 Subject: [PATCH 2/5] =?UTF-8?q?chore:=20=EB=A6=AC=EB=B7=B0=20=EB=A6=AC?= =?UTF-8?q?=EB=A7=88=EC=9D=B8=EB=8D=94=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20YML?= =?UTF-8?q?=20(=ED=85=8C=EC=8A=A4=ED=8A=B8=20PUSh=20=ED=8A=B8=EB=A6=AC?= =?UTF-8?q?=EA=B1=B0=20=ED=8F=AC=ED=95=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common-slack-review-reminder.yml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/common-slack-review-reminder.yml diff --git a/.github/workflows/common-slack-review-reminder.yml b/.github/workflows/common-slack-review-reminder.yml new file mode 100644 index 00000000..9e16aff6 --- /dev/null +++ b/.github/workflows/common-slack-review-reminder.yml @@ -0,0 +1,26 @@ +name: Slack Notification (Review Reminder) + +on: + push: + schedule: + # 매일 한국 시간 오후 2시 (KST 14:00 == UTC 05:00) + - cron: '0 5 * * *' + workflow_dispatch: + +jobs: + remind: + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Run Reminder Script + uses: actions/github-script@v7 + env: + SLACK_WEBHOOK_REVIEW: ${{ secrets.SLACK_WEBHOOK_REVIEW }} + with: + script: | + // .github/scripts/review-reminder.js 파일을 불러와서 실행 + const script = require('./.github/scripts/review-reminder.js'); + await script({ github, context, core }); From d90594f065ba1ab2adcde6b82cd3642af7466e4f Mon Sep 17 00:00:00 2001 From: Taek_2 Date: Thu, 22 Jan 2026 10:42:41 +0900 Subject: [PATCH 3/5] =?UTF-8?q?chore:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=8A=B8=EB=A6=AC=EA=B1=B0=20=EC=A3=BC=EC=84=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/common-slack-review-reminder.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/common-slack-review-reminder.yml b/.github/workflows/common-slack-review-reminder.yml index 9e16aff6..2bdcfa2e 100644 --- a/.github/workflows/common-slack-review-reminder.yml +++ b/.github/workflows/common-slack-review-reminder.yml @@ -1,6 +1,7 @@ name: Slack Notification (Review Reminder) on: + # 테스트용 트리거 추가 push: schedule: # 매일 한국 시간 오후 2시 (KST 14:00 == UTC 05:00) From 219feedbb7f40739032aa58ecd22a1c1e58c2252 Mon Sep 17 00:00:00 2001 From: Taek_2 Date: Thu, 22 Jan 2026 10:43:26 +0900 Subject: [PATCH 4/5] =?UTF-8?q?chore:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EB=B0=8F=20=ED=8A=B8=EB=A6=AC=EA=B1=B0=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/common-slack-review-reminder.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/common-slack-review-reminder.yml b/.github/workflows/common-slack-review-reminder.yml index 2bdcfa2e..226dd785 100644 --- a/.github/workflows/common-slack-review-reminder.yml +++ b/.github/workflows/common-slack-review-reminder.yml @@ -1,8 +1,6 @@ name: Slack Notification (Review Reminder) on: - # 테스트용 트리거 추가 - push: schedule: # 매일 한국 시간 오후 2시 (KST 14:00 == UTC 05:00) - cron: '0 5 * * *' From 7006cb28367eb34c93c62cb608dc59465ff4b95b Mon Sep 17 00:00:00 2001 From: Taek_2 Date: Thu, 22 Jan 2026 10:43:55 +0900 Subject: [PATCH 5/5] =?UTF-8?q?chore:=20=EC=83=9D=EC=84=B1=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=200=20->=2012=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/scripts/review-reminder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/review-reminder.js b/.github/scripts/review-reminder.js index f5e62af0..2bb45174 100644 --- a/.github/scripts/review-reminder.js +++ b/.github/scripts/review-reminder.js @@ -1,7 +1,7 @@ module.exports = async ({github, context, core}) => { // 최소 PR 생성 시간 - const LIMIT_HOURS = 0; + const LIMIT_HOURS = 12; const LIMIT_MS = LIMIT_HOURS * 60 * 60 * 1000; const WEBHOOK_URL = process.env.SLACK_WEBHOOK_REVIEW;