Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 9 additions & 5 deletions .github/workflows/discord-discussions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
# automatically once this merges to the default branch.
command -v discord_applied_tags >/dev/null 2>&1 || discord_applied_tags() { printf '[]'; }
command -v extract_discord_thread >/dev/null 2>&1 || extract_discord_thread() { :; }
command -v discord_archive_thread >/dev/null 2>&1 || discord_archive_thread() { :; }
command -v discord_sync_thread >/dev/null 2>&1 || discord_sync_thread() { :; }

NUMBER="${{ github.event.discussion.number }}"
OWNER="${REPO%/*}"
Expand Down Expand Up @@ -335,13 +335,17 @@ jobs:
fi
fi

# ── Mirror closed/open state onto the forum thread (archive) ─────
# No-op unless DISCORD_GITHUB_BOT_TOKEN is set.
# ── Sync forum thread: archive flag + live tags in ONE PATCH ─────
# No-op unless DISCORD_GITHUB_BOT_TOKEN is set. archived + applied_tags
# are set together so re-tagging also works on reopen and on threads
# Discord auto-archived by inactivity (an applied_tags edit on an
# already-archived thread is rejected with 400). Tags cover state,
# answer status, category, and labels.
FINAL_THREAD_ID="${DISCORD_THREAD_ID:-}"
[ -z "$FINAL_THREAD_ID" ] && FINAL_THREAD_ID="${WINNER_THREAD_ID:-}"
[ -z "$FINAL_THREAD_ID" ] && FINAL_THREAD_ID="${NEW_THREAD_ID:-}"
if [ "$IS_CLOSED" = "true" ]; then
discord_archive_thread "$FINAL_THREAD_ID" true
discord_sync_thread "$FINAL_THREAD_ID" true "$APPLIED_TAGS"
else
discord_archive_thread "$FINAL_THREAD_ID" false
discord_sync_thread "$FINAL_THREAD_ID" false "$APPLIED_TAGS"
fi
15 changes: 9 additions & 6 deletions .github/workflows/discord-issues.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:
# automatically once this merges to the default branch.
command -v discord_applied_tags >/dev/null 2>&1 || discord_applied_tags() { printf '[]'; }
command -v extract_discord_thread >/dev/null 2>&1 || extract_discord_thread() { :; }
command -v discord_archive_thread >/dev/null 2>&1 || discord_archive_thread() { :; }
command -v discord_sync_thread >/dev/null 2>&1 || discord_sync_thread() { :; }

NUMBER="${{ github.event.issue.number }}"

Expand Down Expand Up @@ -229,14 +229,17 @@ jobs:
fi
fi

# ── Mirror closed/open state onto the forum thread (archive) ─────
# No-op unless DISCORD_GITHUB_BOT_TOKEN is set. Prefer the stored thread id,
# then the race winner's, then a freshly created one.
# ── Sync forum thread: archive flag + live tags in ONE PATCH ─────
# No-op unless DISCORD_GITHUB_BOT_TOKEN is set. Prefer the stored thread
# id, then the race winner's, then a freshly created one. archived +
# applied_tags are set together so re-tagging also works on reopen and
# on threads Discord auto-archived by inactivity (an applied_tags edit
# on an already-archived thread is rejected with 400).
FINAL_THREAD_ID="${DISCORD_THREAD_ID:-}"
[ -z "$FINAL_THREAD_ID" ] && FINAL_THREAD_ID="${WINNER_THREAD_ID:-}"
[ -z "$FINAL_THREAD_ID" ] && FINAL_THREAD_ID="${NEW_THREAD_ID:-}"
if [ "$STATE" = "open" ]; then
discord_archive_thread "$FINAL_THREAD_ID" false
discord_sync_thread "$FINAL_THREAD_ID" false "$APPLIED_TAGS"
else
discord_archive_thread "$FINAL_THREAD_ID" true
discord_sync_thread "$FINAL_THREAD_ID" true "$APPLIED_TAGS"
fi
15 changes: 9 additions & 6 deletions .github/workflows/discord-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ jobs:
# automatically once this merges to the default branch.
command -v discord_applied_tags >/dev/null 2>&1 || discord_applied_tags() { printf '[]'; }
command -v extract_discord_thread >/dev/null 2>&1 || extract_discord_thread() { :; }
command -v discord_archive_thread >/dev/null 2>&1 || discord_archive_thread() { :; }
command -v discord_sync_thread >/dev/null 2>&1 || discord_sync_thread() { :; }

# ── Resolve PR number ────────────────────────────────────────────
if [ "$EVENT_NAME" = "pull_request" ] || [ "$EVENT_NAME" = "pull_request_target" ] || [ "$EVENT_NAME" = "pull_request_review" ]; then
Expand Down Expand Up @@ -331,14 +331,17 @@ jobs:
fi
fi

# ── Mirror closed/merged state onto the forum thread (archive) ───
# No-op unless DISCORD_GITHUB_BOT_TOKEN is set. Closed AND merged PRs both
# have PR_STATE=closed, so both archive; reopened PRs unarchive.
# ── Sync forum thread: archive flag + live tags in ONE PATCH ─────
# No-op unless DISCORD_GITHUB_BOT_TOKEN is set. Closed AND merged PRs
# both have PR_STATE=closed, so both archive; reopened PRs unarchive.
# archived + applied_tags are set together so re-tagging also works on
# reopen and on threads Discord auto-archived by inactivity (an
# applied_tags edit on an already-archived thread is rejected with 400).
FINAL_THREAD_ID="${DISCORD_THREAD_ID:-}"
[ -z "$FINAL_THREAD_ID" ] && FINAL_THREAD_ID="${WINNER_THREAD_ID:-}"
[ -z "$FINAL_THREAD_ID" ] && FINAL_THREAD_ID="${NEW_THREAD_ID:-}"
if [ "$PR_STATE" = "open" ]; then
discord_archive_thread "$FINAL_THREAD_ID" false
discord_sync_thread "$FINAL_THREAD_ID" false "$APPLIED_TAGS"
else
discord_archive_thread "$FINAL_THREAD_ID" true
discord_sync_thread "$FINAL_THREAD_ID" true "$APPLIED_TAGS"
fi
58 changes: 45 additions & 13 deletions .github/workflows/scripts/discord-helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -150,21 +150,53 @@ discord_applied_tags() {
| .[0:5]'
}

# ── Forum thread archiving ───────────────────────────────────────────────
# discord_archive_thread <thread_id> <true|false> — archive/unarchive a forum
# thread so its Discord state mirrors the GitHub issue/PR/discussion (closed →
# archived, reopened → unarchived). Forum threads can only be (un)archived with
# a BOT token that has Manage Threads — webhooks cannot do it — so this is a
# no-op (returns 0) unless DISCORD_GITHUB_BOT_TOKEN is set and a thread id is known.
# Best-effort: archiving is cosmetic (the embed already reflects state), so a
# transient failure must never abort the calling workflow.
discord_archive_thread() {
local thread_id="$1" archived="$2"
# ── Forum thread sync (archive state + tags in one PATCH) ────────────────
# discord_sync_thread <thread_id> <archived:true|false> <applied_tags_json>
# — mirror the GitHub state onto the forum thread: set the archived flag AND
# re-apply applied_tags so tags track state/labels (open→closed→merged, label
# adds). Both are set in a SINGLE PATCH on purpose: Discord rejects an
# applied_tags edit on an already-archived thread (400), so setting tags and
# then (un)archiving in separate calls fails on reopen or on threads Discord
# auto-archived by inactivity. Combining them works from any state.
#
# Forum threads can only be edited with a BOT token that has Manage Threads/
# Posts — webhooks cannot — so this is a no-op unless DISCORD_GITHUB_BOT_TOKEN
# and a thread id are set. If <applied_tags_json> is empty ("" or "[]") only the
# archived flag is sent (an empty list means the channel has no tag config, so
# never clobber it to untagged). Best-effort: a transient failure never aborts
# the calling workflow (the embed already reflects state).
discord_sync_thread() {
local thread_id="$1" archived="$2" tags="$3" body
[ -z "${DISCORD_GITHUB_BOT_TOKEN:-}" ] && return 0
[ -z "$thread_id" ] && return 0
curl -sS -o /dev/null --connect-timeout 10 --max-time 30 -X PATCH \
if [ -z "$tags" ] || [ "$tags" = "[]" ]; then
# Omitting the key (never sending applied_tags:[]) is deliberate: an empty
# resolve can mean "no tag config for this channel" OR "config present but
# nothing matched" (e.g. a state/label name missing from the ids map).
# Either way, leaving existing tags untouched is safer than clobbering them
# to empty on a config typo — but warn so a real misconfiguration is visible.
body="{\"archived\": ${archived}}"
echo "::warning::Discord thread sync: no tags resolved for thread ${thread_id} — leaving existing tags untouched (check discord-forum-tags.json if this is unexpected)" >&2
else
body="{\"archived\": ${archived}, \"applied_tags\": ${tags}}"
fi
Comment thread
coderabbitai[bot] marked this conversation as resolved.
# Route through discord_curl (not a hand-rolled curl call) so this PATCH gets
# the same 429/5xx retry as the create path. Safe here because the PATCH sets
# an absolute target state (archived + full applied_tags replace), so it's
# idempotent and retry-safe — unlike the create POST, this must NOT pass
# --no-retry-5xx.
local status
discord_curl -X PATCH \
-H "Authorization: Bot ${DISCORD_GITHUB_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"archived\": ${archived}}" \
"https://discord.com/api/v10/channels/${thread_id}" || true
-d "$body" \
"https://discord.com/api/v10/channels/${thread_id}" >/dev/null 2>&1 || true
Comment on lines +189 to +193

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Preserve discord_curl retry diagnostics.

Line [193] redirects both stdout and stderr to /dev/null, discarding the shared helper’s 429/5xx and transport-failure warnings. Discard only the response body while keeping stderr visible:

Proposed fix
-    "https://discord.com/api/v10/channels/${thread_id}" >/dev/null 2>&1 || true
+    "https://discord.com/api/v10/channels/${thread_id}" >/dev/null || true
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
discord_curl -X PATCH \
-H "Authorization: Bot ${DISCORD_GITHUB_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"archived\": ${archived}}" \
"https://discord.com/api/v10/channels/${thread_id}" || true
-d "$body" \
"https://discord.com/api/v10/channels/${thread_id}" >/dev/null 2>&1 || true
discord_curl -X PATCH \
-H "Authorization: Bot ${DISCORD_GITHUB_BOT_TOKEN}" \
-H "Content-Type: application/json" \
-d "$body" \
"https://discord.com/api/v10/channels/${thread_id}" >/dev/null || true
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/scripts/discord-helper.sh around lines 189 - 193, Update
the PATCH request using discord_curl so it suppresses only the response body
while preserving stderr output, including retry diagnostics for 429/5xx
responses and transport failures; keep the existing || true behavior unchanged.

status=$(cat "$DISCORD_STATUS_FILE" 2>/dev/null || echo "")
# Best-effort: never fail the job, but surface non-2xx (e.g. missing Manage
# Threads → 403, bad payload → 400) so it's diagnosable in the run log.
case "$status" in
2??) : ;;
*) echo "::warning::Discord thread sync returned '${status:-no response}' for thread ${thread_id} (archive/tags not applied)" >&2 ;;
esac
return 0
}
Loading