Send changelog to Discord webhook. (#14292)
This commit is contained in:
committed by
GitHub
parent
6ac3c4dc08
commit
61a671bbc6
5
.github/workflows/publish.yml
vendored
5
.github/workflows/publish.yml
vendored
@@ -57,3 +57,8 @@ jobs:
|
|||||||
key: ${{ secrets.CENTCOMM_WIZARDS_BUILDS_PUSH_KEY }}
|
key: ${{ secrets.CENTCOMM_WIZARDS_BUILDS_PUSH_KEY }}
|
||||||
script: /home/wizards-build-push/push.ps1 ${{ github.sha }}
|
script: /home/wizards-build-push/push.ps1 ${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Publish changelog
|
||||||
|
run: Tools/actions_changelogs_since_last_run.py
|
||||||
|
with:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
DISCORD_WEBHOOK_URL: ${{ secrets.CHANGELOG_DISCORD_WEBHOOK }}
|
||||||
|
|||||||
133
Tools/actions_changelogs_since_last_run.py
Executable file
133
Tools/actions_changelogs_since_last_run.py
Executable file
@@ -0,0 +1,133 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
#
|
||||||
|
# Sends updates to a Discord webhook for new changelog entries since the last GitHub Actions publish run.
|
||||||
|
# Automatically figures out the last run and changelog contents with the GitHub API.
|
||||||
|
#
|
||||||
|
|
||||||
|
import io
|
||||||
|
import itertools
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import yaml
|
||||||
|
from typing import Any, Iterable
|
||||||
|
|
||||||
|
GITHUB_API_URL = os.environ.get("GITHUB_API_URL", "https://api.github.com")
|
||||||
|
GITHUB_REPO = os.environ["GITHUB_REPO"]
|
||||||
|
GITHUB_RUN = os.environ["GITHUB_RUN_ID"]
|
||||||
|
GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]
|
||||||
|
|
||||||
|
DISCORD_WEBHOOK_URL = os.environ.get("DISCORD_WEBHOOK_URL")
|
||||||
|
|
||||||
|
CHANGELOG_FILE = "Resources/Changelog/Changelog.yml"
|
||||||
|
|
||||||
|
TYPES_TO_EMOJI = {
|
||||||
|
"Fix": "🐛",
|
||||||
|
"Add": "🆕",
|
||||||
|
"Remove": "❌",
|
||||||
|
"Tweak": "⚒️"
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangelogEntry = dict[str, Any]
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if not DISCORD_WEBHOOK_URL:
|
||||||
|
return
|
||||||
|
|
||||||
|
session = requests.Session()
|
||||||
|
session.headers["Authorization"] = f"Bearer {GITHUB_TOKEN}"
|
||||||
|
session.headers["Accept"] = "Accept: application/vnd.github+json"
|
||||||
|
session.headers["X-GitHub-Api-Version"] = "2022-11-28"
|
||||||
|
|
||||||
|
most_recent = get_most_recent_workflow(session)
|
||||||
|
last_sha = most_recent['head_commit']['id']
|
||||||
|
print(f"Last successsful publish job was {most_recent['id']}: {last_sha}")
|
||||||
|
last_changelog = yaml.safe_load(get_last_changelog(session, last_sha))
|
||||||
|
with open(CHANGELOG_FILE, "r") as f:
|
||||||
|
cur_changelog = yaml.safe_load(f)
|
||||||
|
|
||||||
|
diff = diff_changelog(last_changelog, cur_changelog)
|
||||||
|
send_to_discord(diff)
|
||||||
|
|
||||||
|
|
||||||
|
def get_most_recent_workflow(sess: requests.Session) -> Any:
|
||||||
|
workflow_run = get_current_run(sess)
|
||||||
|
past_runs = get_past_runs(sess, workflow_run)
|
||||||
|
for run in past_runs['workflow_runs']:
|
||||||
|
# First past successful run that isn't our current run.
|
||||||
|
if run["id"] == workflow_run["id"]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
return run
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_run(sess: requests.Session) -> Any:
|
||||||
|
resp = sess.get(f"{GITHUB_API_URL}/repos/{GITHUB_REPO}/actions/runs/{GITHUB_RUN}")
|
||||||
|
resp.raise_for_status()
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
|
||||||
|
def get_past_runs(sess: requests.Session, current_run: Any) -> Any:
|
||||||
|
"""
|
||||||
|
Get all successful workflow runs before our current one.
|
||||||
|
"""
|
||||||
|
params = {
|
||||||
|
"status": "success",
|
||||||
|
"created": f"<={current_run['created_at']}"
|
||||||
|
}
|
||||||
|
resp = sess.get(f"{current_run['workflow_url']}/runs", params=params)
|
||||||
|
resp.raise_for_status()
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
|
||||||
|
def get_last_changelog(sess: requests.Session, sha: str) -> str:
|
||||||
|
"""
|
||||||
|
Use GitHub API to get the previous version of the changelog YAML (Actions builds are fetched with a shallow clone)
|
||||||
|
"""
|
||||||
|
params = {
|
||||||
|
"ref": sha,
|
||||||
|
}
|
||||||
|
headers = {
|
||||||
|
"Accept": "application/vnd.github.raw"
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = sess.get(f"{GITHUB_API_URL}/repos/{GITHUB_REPO}/contents/{CHANGELOG_FILE}", headers=headers, params=params)
|
||||||
|
resp.raise_for_status()
|
||||||
|
return resp.text
|
||||||
|
|
||||||
|
|
||||||
|
def diff_changelog(old: dict[str, Any], cur: dict[str, Any]) -> Iterable[ChangelogEntry]:
|
||||||
|
"""
|
||||||
|
Find all new entries not present in the previous publish.
|
||||||
|
"""
|
||||||
|
old_entry_ids = {e["id"] for e in old["Entries"]}
|
||||||
|
return (e for e in cur["Entries"] if e["id"] not in old_entry_ids)
|
||||||
|
|
||||||
|
|
||||||
|
def send_to_discord(entries: Iterable[ChangelogEntry]) -> None:
|
||||||
|
if not DISCORD_WEBHOOK_URL:
|
||||||
|
return
|
||||||
|
|
||||||
|
content = io.StringIO()
|
||||||
|
for name, group in itertools.groupby(entries, lambda x: x["author"]):
|
||||||
|
content.write(f"**{name}** updated:\n")
|
||||||
|
for entry in group:
|
||||||
|
for change in entry["changes"]:
|
||||||
|
emoji = TYPES_TO_EMOJI.get(change['type'], "❓")
|
||||||
|
message = change['message']
|
||||||
|
content.write(f"{emoji} {message}\n")
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"content": content.getvalue(),
|
||||||
|
# Do not allow any mentions.
|
||||||
|
"allowed_mentions": {
|
||||||
|
"parse": []
|
||||||
|
},
|
||||||
|
# SUPPRESS_EMBEDS
|
||||||
|
"flags": 1 << 2
|
||||||
|
}
|
||||||
|
|
||||||
|
requests.post(DISCORD_WEBHOOK_URL, json=body)
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user