Skip to content

Commit 5a3046c

Browse files
committed
ci: auto-post LU-branded release announcement to Discord on release:published
Fires after tauri-action finishes uploading installers (polls up to 12 min for at least 4 installer-suffix artifacts before announcing). Posts the release body verbatim to #releases and a short bullet-teaser to #announcements via two channel-bound webhooks — no bot token involved. Replaces the manual 'paste the release notes into Discord' step. Whatever `gh release create --notes` wrote is what the server sees. Webhooks live as DISCORD_RELEASES_WEBHOOK + DISCORD_ANNOUNCEMENTS_WEBHOOK GitHub secrets (already configured).
1 parent 0104c27 commit 5a3046c

1 file changed

Lines changed: 124 additions & 0 deletions

File tree

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
name: Discord release announcement
2+
3+
# Fires when a GitHub Release is published. Posts an LU-branded announcement
4+
# to the Discord server via two webhooks:
5+
# #releases — full release body (what's new / fixed / upgrade instructions)
6+
# #announcements — short blurb with link
7+
#
8+
# The release body is whatever `gh release create --notes "…"` wrote, so the
9+
# human writing the release notes owns the announcement content. No emojis
10+
# are injected here — follow whatever style the release body uses.
11+
#
12+
# Runs after tauri-action so users see the announcement AFTER installers are up.
13+
14+
on:
15+
release:
16+
types: [published]
17+
18+
permissions:
19+
contents: read
20+
21+
jobs:
22+
wait-for-build:
23+
# Poll the build-tauri job on the same release. Not strictly required —
24+
# auto-update uses the release's latest.json which only uploads after
25+
# tauri-action finishes — but announcing before installers exist confuses
26+
# people who click the download link and hit 404. Single 6-min ceiling
27+
# matches our normal build time (~5 min).
28+
runs-on: ubuntu-latest
29+
steps:
30+
- name: Wait for tauri artifacts
31+
env:
32+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33+
TAG: ${{ github.event.release.tag_name }}
34+
run: |
35+
set -euo pipefail
36+
echo "Waiting for $TAG installers to appear on the release…"
37+
for i in $(seq 1 72); do
38+
count=$(gh release view "$TAG" --json assets --jq '[.assets[] | select(.name | test("setup\\.exe$|\\.msi$|\\.AppImage$|\\.deb$|\\.rpm$"))] | length')
39+
if [ "$count" -ge 4 ]; then
40+
echo "Found $count installer artifacts — proceeding."
41+
exit 0
42+
fi
43+
echo " ($i/72) only $count so far, sleeping 10s…"
44+
sleep 10
45+
done
46+
echo "Timeout waiting for artifacts — announcing anyway."
47+
48+
announce:
49+
needs: wait-for-build
50+
runs-on: ubuntu-latest
51+
steps:
52+
- name: Post to #releases
53+
env:
54+
WEBHOOK: ${{ secrets.DISCORD_RELEASES_WEBHOOK }}
55+
TAG: ${{ github.event.release.tag_name }}
56+
TITLE: ${{ github.event.release.name }}
57+
BODY: ${{ github.event.release.body }}
58+
URL: ${{ github.event.release.html_url }}
59+
run: |
60+
python3 - <<'PY'
61+
import json, os, urllib.request, urllib.error, sys
62+
webhook = os.environ.get("WEBHOOK", "").strip()
63+
if not webhook:
64+
sys.exit("DISCORD_RELEASES_WEBHOOK not set")
65+
tag = os.environ["TAG"]
66+
body = (os.environ.get("BODY") or "").strip()
67+
url = os.environ["URL"]
68+
# Discord single-message limit is 2000 chars. Leave room for our
69+
# own header (~120) and footer (~100), cap the body at 1700.
70+
header = f"# 🚀 Locally Uncensored {tag} is live\n\nAuto-update picks this up on your next launch.\n\n"
71+
footer = f"\n\nFull release notes + installers: {url}"
72+
budget = 2000 - len(header) - len(footer) - 20
73+
if len(body) > budget:
74+
body = body[:budget].rsplit("\n", 1)[0] + "\n\n*(truncated — see full notes link below)*"
75+
content = header + body + footer
76+
req = urllib.request.Request(
77+
webhook,
78+
data=json.dumps({"content": content, "flags": 4096}).encode("utf-8"), # 4096 = SUPPRESS_EMBEDS
79+
headers={"Content-Type": "application/json", "User-Agent": "LocallyUncensored/release-announce"},
80+
method="POST",
81+
)
82+
with urllib.request.urlopen(req, timeout=30) as r:
83+
print(f"#releases POST status: {r.status}")
84+
PY
85+
86+
- name: Post short blurb to #announcements
87+
env:
88+
WEBHOOK: ${{ secrets.DISCORD_ANNOUNCEMENTS_WEBHOOK }}
89+
TAG: ${{ github.event.release.tag_name }}
90+
URL: ${{ github.event.release.html_url }}
91+
BODY: ${{ github.event.release.body }}
92+
run: |
93+
python3 - <<'PY'
94+
import json, os, re, urllib.request, sys
95+
webhook = os.environ.get("WEBHOOK", "").strip()
96+
if not webhook:
97+
sys.exit("DISCORD_ANNOUNCEMENTS_WEBHOOK not set")
98+
tag = os.environ["TAG"]
99+
url = os.environ["URL"]
100+
raw = (os.environ.get("BODY") or "").strip()
101+
# Extract first non-empty bullet line that mentions "fix" or "add"
102+
# to produce a single-line teaser. Fallback: first paragraph.
103+
teaser = ""
104+
for line in raw.splitlines():
105+
s = line.strip().lstrip("-•* ").strip()
106+
if not s or s.startswith("#"):
107+
continue
108+
teaser = s
109+
break
110+
teaser = re.sub(r"\*\*([^*]+)\*\*", r"**\1**", teaser)[:400]
111+
msg = (
112+
f"Hey everyone — **{tag} is out**. Auto-update rolls on next launch.\n\n"
113+
+ (f"> {teaser}\n\n" if teaser else "")
114+
+ f"Full notes: {url}"
115+
)
116+
req = urllib.request.Request(
117+
webhook,
118+
data=json.dumps({"content": msg, "flags": 4096}).encode("utf-8"),
119+
headers={"Content-Type": "application/json", "User-Agent": "LocallyUncensored/release-announce"},
120+
method="POST",
121+
)
122+
with urllib.request.urlopen(req, timeout=30) as r:
123+
print(f"#announcements POST status: {r.status}")
124+
PY

0 commit comments

Comments
 (0)