Skip to content

Commit 9f90ef0

Browse files
committed
ci: add discord repository notifications
1 parent a2abd8d commit 9f90ef0

3 files changed

Lines changed: 165 additions & 0 deletions

File tree

.github/scripts/notify-discord.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#!/usr/bin/env python3
2+
from __future__ import annotations
3+
4+
import json
5+
import os
6+
import sys
7+
import urllib.error
8+
import urllib.request
9+
from pathlib import Path
10+
11+
12+
def main() -> int:
13+
kind = sys.argv[1] if len(sys.argv) > 1 else os.getenv("NOTIFICATION_KIND", "")
14+
webhook_url = os.getenv("DISCORD_WEBHOOK_URL", "").strip()
15+
if webhook_url == "":
16+
print("Discord notification skipped: webhook secret is not configured.")
17+
return 0
18+
19+
payload = build_release_payload() if kind == "release" else build_push_payload()
20+
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
21+
request = urllib.request.Request(
22+
webhook_url,
23+
data=data,
24+
headers={
25+
"Content-Type": "application/json",
26+
"User-Agent": "ImperaZim-Repository-Notifications/1.0",
27+
},
28+
method="POST",
29+
)
30+
31+
try:
32+
with urllib.request.urlopen(request, timeout=10) as response:
33+
print(f"Discord notification sent with HTTP {response.status}.")
34+
except urllib.error.HTTPError as error:
35+
print(f"Discord notification failed with HTTP {error.code}; release/build will continue.")
36+
except urllib.error.URLError as error:
37+
print(f"Discord notification failed: {error.reason}; release/build will continue.")
38+
39+
return 0
40+
41+
42+
def build_push_payload() -> dict[str, object]:
43+
event = read_event()
44+
repository = os.getenv("GITHUB_REPOSITORY", event.get("repository", {}).get("full_name", "unknown/repo"))
45+
branch = os.getenv("GITHUB_REF_NAME", event.get("ref", "").replace("refs/heads/", ""))
46+
actor = os.getenv("GITHUB_ACTOR", event.get("pusher", {}).get("name", "unknown"))
47+
compare_url = event.get("compare") or repository_url(repository)
48+
commits = event.get("commits", [])
49+
head = event.get("head_commit") or {}
50+
head_message = first_line(str(head.get("message", "No commit message.")), 180)
51+
head_sha = str(head.get("id", os.getenv("GITHUB_SHA", "")))[:7]
52+
53+
return {
54+
"username": "ImperaZim Repository",
55+
"embeds": [
56+
{
57+
"title": f"Push: {repository}",
58+
"url": compare_url,
59+
"description": head_message,
60+
"color": 0x3BA55D,
61+
"fields": [
62+
{"name": "Branch", "value": branch or "unknown", "inline": True},
63+
{"name": "Actor", "value": actor, "inline": True},
64+
{"name": "Commits", "value": str(len(commits)), "inline": True},
65+
{"name": "Head", "value": f"`{head_sha}`", "inline": True},
66+
],
67+
}
68+
],
69+
}
70+
71+
72+
def build_release_payload() -> dict[str, object]:
73+
repository = os.getenv("GITHUB_REPOSITORY", "unknown/repo")
74+
name = os.getenv("RELEASE_NAME", repository.rsplit("/", 1)[-1])
75+
version = os.getenv("RELEASE_VERSION", "").strip()
76+
tag = os.getenv("RELEASE_TAG", os.getenv("GITHUB_REF_NAME", "")).strip()
77+
assets = [asset.strip() for asset in os.getenv("RELEASE_ASSETS", "").split(",") if asset.strip()]
78+
release_url = f"{repository_url(repository)}/releases/tag/{tag}" if tag else repository_url(repository)
79+
title = f"Release: {name}" + (f" {version}" if version else "")
80+
81+
fields = [
82+
{"name": "Repository", "value": repository, "inline": True},
83+
{"name": "Tag", "value": tag or "unknown", "inline": True},
84+
{"name": "Trigger", "value": os.getenv("GITHUB_EVENT_NAME", "unknown"), "inline": True},
85+
]
86+
if assets:
87+
fields.append({"name": "Assets", "value": "\n".join(f"`{Path(asset).name}`" for asset in assets), "inline": False})
88+
89+
return {
90+
"username": "ImperaZim Releases",
91+
"embeds": [
92+
{
93+
"title": title,
94+
"url": release_url,
95+
"description": "Release assets were published and verified.",
96+
"color": 0x5865F2,
97+
"fields": fields,
98+
}
99+
],
100+
}
101+
102+
103+
def read_event() -> dict[str, object]:
104+
event_path = os.getenv("GITHUB_EVENT_PATH")
105+
if not event_path:
106+
return {}
107+
108+
try:
109+
with open(event_path, "r", encoding="utf-8") as file:
110+
data = json.load(file)
111+
return data if isinstance(data, dict) else {}
112+
except (OSError, json.JSONDecodeError):
113+
return {}
114+
115+
116+
def repository_url(repository: str) -> str:
117+
return f"{os.getenv('GITHUB_SERVER_URL', 'https://github.com')}/{repository}"
118+
119+
120+
def first_line(value: str, max_length: int) -> str:
121+
line = value.splitlines()[0] if value.splitlines() else value
122+
return line if len(line) <= max_length else line[: max_length - 3] + "..."
123+
124+
125+
if __name__ == "__main__":
126+
raise SystemExit(main())

.github/workflows/release.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,12 @@ jobs:
9191
URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/${ASSET}"
9292
curl --fail --location --retry 10 --retry-delay 3 --retry-all-errors --output dist/published.phar "$URL"
9393
test "$(sha256sum dist/published.phar | cut -d' ' -f1)" = "$(sha256sum "$PHAR" | cut -d' ' -f1)"
94+
- name: Send Discord release notification
95+
env:
96+
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_RELEASE_WEBHOOK_URL }}
97+
NOTIFICATION_KIND: release
98+
RELEASE_NAME: ${{ steps.metadata.outputs.name }}
99+
RELEASE_VERSION: ${{ steps.metadata.outputs.version }}
100+
RELEASE_TAG: ${{ steps.metadata.outputs.tag }}
101+
RELEASE_ASSETS: ${{ steps.metadata.outputs.phar }},dist/checksums.txt
102+
run: python3 .github/scripts/notify-discord.py release
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Repository Notifications
2+
3+
on:
4+
push:
5+
branches:
6+
- "**"
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: read
11+
12+
concurrency:
13+
group: repository-notifications-${{ github.ref }}
14+
cancel-in-progress: false
15+
16+
jobs:
17+
push:
18+
name: Notify push channel
19+
runs-on: ubuntu-latest
20+
if: ${{ github.event_name == 'push' }}
21+
22+
steps:
23+
- name: Checkout
24+
uses: actions/checkout@v4
25+
26+
- name: Send Discord push notification
27+
env:
28+
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_PUSH_WEBHOOK_URL }}
29+
NOTIFICATION_KIND: push
30+
run: python3 .github/scripts/notify-discord.py push

0 commit comments

Comments
 (0)