Files
actions/discourse-release/announce.sh
T
jcoffey-dev 1f0c7971d6 discourse-release: announce a given tag, for releases made by the job's own token
Gitea starts no workflow for events caused by the Actions bot, so a release
created with GITHUB_TOKEN never fires 'on: release'. With 'tag:' the action
fetches that release itself; drafts are skipped.
2026-09-26 19:26:53 -07:00

77 lines
3.1 KiB
Bash
Executable File

#!/bin/bash
# Called by action.yml. Everything user-written (release name, notes) comes in
# through the event JSON file and is only ever handled by jq -- never spliced
# into a command line or a JSON string by the shell.
set -euo pipefail
category_path=$(jq -r --arg r "$REPO" '.[$r] // empty' "$MAP")
if [ -z "$category_path" ]; then
echo "::error::$REPO is not in discourse-release/release-map.json"
exit 1
fi
if [ -n "${TAG:-}" ]; then
# Called with a tag (a release made by the job's own token): fetch it and
# treat it as the event, so everything below is the same either way.
base="${CI_SERVER_INTERNAL:-$GITHUB_SERVER_URL}"
EVENT_PATH=$(mktemp)
curl -fsS -H "Authorization: token $GITEA_TOKEN" "$base/api/v1/repos/$REPO/releases/tags/$TAG" |
jq '{release: .}' > "$EVENT_PATH"
fi
if [ "$(jq -r '.release.draft // false' "$EVENT_PATH")" = true ]; then
echo "release is a draft; not announcing"
exit 0
fi
tag=$(jq -r '.release.tag_name' "$EVENT_PATH")
# The category id, looked up anonymously (the category is public); the
# release-bot key is scoped to creating posts and nothing else.
category_id=$(curl -fsS "$FORUM/c/$category_path/find_by_slug.json" | jq -r '.category.id')
# One topic per repo+tag, ever: Discourse enforces external_id uniqueness.
external_id="rel-$(printf '%s@%s' "$REPO" "$tag" | sha1sum | cut -c1-40)"
payload=$(jq -n --arg repo "$REPO" --argjson category "$category_id" --arg ext "$external_id" \
--slurpfile ev "$EVENT_PATH" '
($ev[0].release) as $r
| ($repo | split("/") | last) as $name
| {
# repo + tag, not the release name: names vary by project (some repeat
# the tag, some carry a product name in capitals), tags do not.
title: "\($name) \($r.tag_name) released",
category: $category,
external_id: $ext,
raw: (
"**\($name) \($r.tag_name)** is out.\n\n"
+ (if ($r.body // "") == "" then "" else "\($r.body)\n\n" end)
+ "---\n[Release on Gitea](\($r.html_url))"
)
}')
status=$(curl -sS -o /tmp/discourse-release.out -w '%{http_code}' -X POST "$FORUM/posts.json" \
-H "Api-Key: $API_KEY" -H "Api-Username: release-bot" \
-H "Content-Type: application/json" --data-binary "$payload")
body=$(cat /tmp/discourse-release.out)
case "$status" in
200)
url="$FORUM/t/$(jq -r '.topic_slug' <<<"$body")/$(jq -r '.topic_id' <<<"$body")"
echo "announced: $url" ;;
422)
if jq -e '.errors | tostring | test("External ID"; "i")' <<<"$body" >/dev/null; then
echo "already announced (external_id $external_id); nothing to do"
exit 0
fi
echo "::error::forum refused the post: $body"; exit 1 ;;
*)
echo "::error::forum returned HTTP $status: $body"; exit 1 ;;
esac
if [ -n "${DISCORD_WEBHOOK:-}" ]; then
jq -n --arg u "$url" --slurpfile ev "$EVENT_PATH" --arg repo "$REPO" '
($ev[0].release) as $r
| {content: ("**\($repo | split("/") | last) \($r.tag_name)** released\n\($u)")[0:1900]}' |
curl -fsS -X POST "$DISCORD_WEBHOOK" -H "Content-Type: application/json" --data-binary @- >/dev/null \
&& echo "posted to Discord" \
|| echo "::warning::Discord webhook failed; the forum post stands"
fi