From e43f60104c3f80c7a09c05360bbe2a514d7629c1 Mon Sep 17 00:00:00 2001 From: sevin98 Date: Wed, 8 Jul 2026 01:39:33 +0900 Subject: [PATCH 1/4] =?UTF-8?q?PR=20=EB=B4=87=20=EC=8A=A4=EB=A0=88?= =?UTF-8?q?=EB=93=9C=EC=97=90=20=ED=8C=80=EC=9B=90=20=EC=9E=90=EB=8F=99=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=EB=A1=9C=20=ED=8C=80=20=EC=A0=84=EC=9B=90=20?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=EB=93=9C=EB=B0=94=20=EB=85=B8=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Discord 공개 스레드는 멤버(생성자·초대·멘션된 사람)만 사이드바에 뜬다. 부모 카드가 작성자만 @멘션해 작성자만 자기 PR 스레드가 보이고 남의 스레드는 안 떴다. - 스레드 생성 직후 DISCORD_USER_MAP 의 팀원 전원을 thread-members 로 추가해 새 PR 스레드가 팀 전원 사이드바에 뜨게 한다(멘션과 달리 조용히 목록에만 올림). - 이 변경 이전 열린 PR 스레드용으로 backfill-thread-members 잡을 추가한다 (workflow_dispatch 수동 실행). notify-discord 는 pull_request 로 가드해 dispatch 시엔 안 돈다. --- .github/workflows/discord-pr-bot.yml | 45 ++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/.github/workflows/discord-pr-bot.yml b/.github/workflows/discord-pr-bot.yml index 911a1a8e..1f0166fc 100644 --- a/.github/workflows/discord-pr-bot.yml +++ b/.github/workflows/discord-pr-bot.yml @@ -1,6 +1,8 @@ name: Discord PR Bot on: + # 기존 열린 PR 스레드에 팀원을 일괄 추가(백필)하는 수동 실행용. Actions → Run workflow. + workflow_dispatch: pull_request: types: - opened @@ -21,6 +23,7 @@ concurrency: jobs: notify-discord: + if: github.event_name == 'pull_request' runs-on: ubuntu-latest # metadata 코멘트는 PR(=issue #N)에 다는데, 대상이 PR 이라 생성엔 pull-requests write 가 필요하다 # (issues:write 만으론 403 Resource not accessible). 연관 이슈(close #N) 상태 조회는 issues:read. @@ -271,6 +274,14 @@ jobs: echo "Discord thread create failed"; echo "$TRESP"; exit 1 fi + # 팀 전원을 스레드 멤버로 추가 — 공개 스레드는 멤버만 사이드바에 뜨므로, DISCORD_USER_MAP 의 모든 + # discord id 를 넣어 팀 전원 사이드바에 노출한다(멘션과 달리 조용히 목록에만 올림). 실패해도 무해. + echo "$DISCORD_USER_MAP" | jq -r '.[]?' | while read -r uid; do + [ -z "$uid" ] && continue + curl -s -o /dev/null -X PUT "$DISCORD_API/channels/$THREAD_ID/thread-members/$uid" \ + -H "Authorization: Bot $DISCORD_BOT_TOKEN" + done + echo "message_id=$MESSAGE_ID" >> "$GITHUB_OUTPUT" echo "thread_id=$THREAD_ID" >> "$GITHUB_OUTPUT" @@ -535,3 +546,37 @@ jobs: --data "$(jq -n --arg name "$NEW_NAME" '{name:$name, archived:true, locked:true}')") [ "$CODE" = "200" ] && echo "스레드 정리됨: $NEW_NAME" || echo "::warning::스레드 정리 실패 (HTTP $CODE)" fi + + backfill-thread-members: + # 기존 열린 PR 들의 스레드에 팀원(DISCORD_USER_MAP)을 일괄 추가하는 수동 백필. Actions 에서 Run workflow 로 실행. + # 공개 스레드는 멤버만 사이드바에 뜨므로, 이 봇 변경 이전에 만들어진 스레드를 팀 전원 사이드바에 노출시킨다. + if: github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + permissions: + pull-requests: read + issues: read + env: + DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }} + DISCORD_USER_MAP: ${{ secrets.DISCORD_USER_MAP }} + DISCORD_API: https://discord.com/api/v10 + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Add team members to all open PR threads + shell: bash + run: | + [ -n "$DISCORD_USER_MAP" ] || DISCORD_USER_MAP='{}' + PRS=$(gh api "repos/${{ github.repository }}/pulls?state=open&per_page=100" --jq '.[].number') + for pr in $PRS; do + # 각 PR 의 메타 주석에서 thread_id 추출 (find_meta 와 같은 JSON 마커 매칭). + COMMENTS=$(curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/${{ github.repository }}/issues/$pr/comments") + META=$(echo "$COMMENTS" | jq -r '.[]? | objects | select((.body // "") | test("discord-pr-bot:\\s*\\{")) | .body' | tail -n 1) + THREAD_ID=$(echo "$META" | grep -oP 'discord-pr-bot:\s*\K\{[^}]*\}' | jq -r '.thread_id // empty' 2>/dev/null || true) + if [ -z "$THREAD_ID" ]; then echo "PR #$pr: 스레드 메타 없음, 건너뜀"; continue; fi + echo "$DISCORD_USER_MAP" | jq -r '.[]?' | while read -r uid; do + [ -z "$uid" ] && continue + curl -s -o /dev/null -X PUT "$DISCORD_API/channels/$THREAD_ID/thread-members/$uid" \ + -H "Authorization: Bot $DISCORD_BOT_TOKEN" + done + echo "PR #$pr (thread $THREAD_ID): 팀원 추가 완료" + done From 4ffaef73718cd9255020fff1c46247a5a5313031 Mon Sep 17 00:00:00 2001 From: sevin98 Date: Wed, 8 Jul 2026 01:45:44 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=EC=97=B0=EA=B4=80=20=EC=9D=B4=EC=8A=88=20?= =?UTF-8?q?=EB=A7=81=ED=81=AC=EC=9D=98=20Discord=20=EC=9E=84=EB=B2=A0?= =?UTF-8?q?=EB=93=9C=20=EC=B9=B4=EB=93=9C=20=EC=96=B5=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 부모 카드의 연관 이슈 링크가 GitHub 임베드 카드로 커지게 떠서 본문(작업 요약 등)을 아래로 밀어냈다. 이슈 URL 을 <> 로 감싸 임베드만 억제하고 클릭 링크는 유지한다. PR 타이틀 링크는 그대로 둬 PR 카드는 계속 뜬다. --- .github/workflows/discord-pr-bot.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/discord-pr-bot.yml b/.github/workflows/discord-pr-bot.yml index 1f0166fc..7fb60630 100644 --- a/.github/workflows/discord-pr-bot.yml +++ b/.github/workflows/discord-pr-bot.yml @@ -135,7 +135,9 @@ jobs: state=$(echo "$res" | jq -r '.state // "unknown"') if [ "$state" = "open" ]; then emoji="🟢"; elif [ "$state" = "closed" ]; then emoji="⚫"; else emoji="❓"; fi link="https://github.com/$REPO/issues/$num" - line="$emoji [#$num]($link)" + # URL 을 <> 로 감싸 Discord 임베드 카드를 억제한다(클릭은 유지). 이슈 카드가 커서 부모 카드 본문을 + # 밀어내므로, 연관 이슈는 링크만 남기고 카드는 PR(부모 타이틀 링크)만 뜨게 한다. + line="$emoji [#$num](<$link>)" if [ -z "$issue_text" ]; then issue_text="$line"; else issue_text=$(printf "%s\n%s" "$issue_text" "$line"); fi done <<< "$issues" [ -z "$issue_text" ] && issue_text="없음" From 6aa373968d55ec8e2a3637eedda4d6909495bfe2 Mon Sep 17 00:00:00 2001 From: sevin98 Date: Wed, 8 Jul 2026 01:51:58 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=EC=9E=91=EC=97=85=20=EC=9A=94=EC=95=BD=20?= =?UTF-8?q?=EC=B6=94=EC=B6=9C=EC=9D=B4=20STAR=20=EC=9D=98=20Task=20?= =?UTF-8?q?=EC=84=B9=EC=85=98=EB=8F=84=20=EC=9D=B8=EC=8B=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FE 팀 PR 은 ## 작업 요약, 서버 STAR 로 쓴 PR 은 ## Task 를 쓴다. build_summary 가 작업 요약만 찾아 STAR PR 은 '요약 없음'이 됐다. 두 섹션명을 모두 인식하게 해 어느 관례로 쓰든 요약이 채워지게 한다. --- .github/workflows/discord-pr-bot.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/discord-pr-bot.yml b/.github/workflows/discord-pr-bot.yml index 7fb60630..6d80becd 100644 --- a/.github/workflows/discord-pr-bot.yml +++ b/.github/workflows/discord-pr-bot.yml @@ -144,7 +144,8 @@ jobs: echo "$issue_text" } - # PR 본문의 ## 작업 요약 섹션 불릿을 "• ..." 요약으로(없으면 "요약 없음"). 순수(인자만). + # PR 본문의 ## 작업 요약(FE 관례) 또는 ## Task(서버 STAR) 섹션 불릿을 "• ..." 요약으로(없으면 "요약 없음"). + # 팀원 PR 은 작업 요약, STAR 로 쓴 PR 은 Task 를 쓰므로 둘 다 인식한다. 순수(인자만). build_summary() { local pr_body="$1" printf '%s\n' "$pr_body" | awk ' @@ -153,7 +154,7 @@ jobs: if (in_task == 1) exit r = $0 sub(/^##[[:space:]]+/, "", r) - ok = (match(r, /^작업 요약([^A-Za-z]|$)/) || match(r, /[[:space:]]작업 요약([^A-Za-z]|$)/)) + ok = (match(r, /^작업 요약([^A-Za-z]|$)/) || match(r, /[[:space:]]작업 요약([^A-Za-z]|$)/) || match(r, /^[Tt][Aa][Ss][Kk]([^A-Za-z]|$)/) || match(r, /[[:space:]][Tt][Aa][Ss][Kk]([^A-Za-z]|$)/)) if (ok) { in_task=1; next } next } From 3e3648a8801a0bdf4ed12889ce85261660850501 Mon Sep 17 00:00:00 2001 From: sevin98 Date: Wed, 8 Jul 2026 01:58:12 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=EC=9E=91=EC=97=85=20=EC=9A=94=EC=95=BD?= =?UTF-8?q?=EC=9D=98=20Task=20=EC=84=B9=EC=85=98=20=EC=9D=B8=EC=8B=9D=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 FE 팀은 ## 작업 요약을 쓰고 봇이 이미 이를 매칭하므로, STAR 의 ## Task 인식 추가는 불필요해 되돌린다. 요약은 PR 본문에 ## 작업 요약 을 두는 팀 관례로 채운다. --- .github/workflows/discord-pr-bot.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/discord-pr-bot.yml b/.github/workflows/discord-pr-bot.yml index 6d80becd..7fb60630 100644 --- a/.github/workflows/discord-pr-bot.yml +++ b/.github/workflows/discord-pr-bot.yml @@ -144,8 +144,7 @@ jobs: echo "$issue_text" } - # PR 본문의 ## 작업 요약(FE 관례) 또는 ## Task(서버 STAR) 섹션 불릿을 "• ..." 요약으로(없으면 "요약 없음"). - # 팀원 PR 은 작업 요약, STAR 로 쓴 PR 은 Task 를 쓰므로 둘 다 인식한다. 순수(인자만). + # PR 본문의 ## 작업 요약 섹션 불릿을 "• ..." 요약으로(없으면 "요약 없음"). 순수(인자만). build_summary() { local pr_body="$1" printf '%s\n' "$pr_body" | awk ' @@ -154,7 +153,7 @@ jobs: if (in_task == 1) exit r = $0 sub(/^##[[:space:]]+/, "", r) - ok = (match(r, /^작업 요약([^A-Za-z]|$)/) || match(r, /[[:space:]]작업 요약([^A-Za-z]|$)/) || match(r, /^[Tt][Aa][Ss][Kk]([^A-Za-z]|$)/) || match(r, /[[:space:]][Tt][Aa][Ss][Kk]([^A-Za-z]|$)/)) + ok = (match(r, /^작업 요약([^A-Za-z]|$)/) || match(r, /[[:space:]]작업 요약([^A-Za-z]|$)/)) if (ok) { in_task=1; next } next }