98 lines
3.2 KiB
Bash
98 lines
3.2 KiB
Bash
|
|
#!/bin/bash
|
||
|
|
# Checks if cinnyapp/cinny has published a new stable release; notifies Matrix if so.
|
||
|
|
# Runs daily at noon via /etc/cron.d/cinny-upstream-check
|
||
|
|
# Requires /etc/cinny-monitor.env:
|
||
|
|
# MATRIX_TOKEN, MATRIX_SERVER, MATRIX_ROOM, MATRIX_PING_USER
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
ENV_FILE="/etc/cinny-monitor.env"
|
||
|
|
STATE_FILE="/var/lib/cinny-monitor/last-upstream-tag"
|
||
|
|
LOG="/var/log/cinny-monitor.log"
|
||
|
|
|
||
|
|
exec >> "$LOG" 2>&1
|
||
|
|
echo "=== $(date) === Cinny upstream release check ==="
|
||
|
|
|
||
|
|
[ -f "$ENV_FILE" ] || { echo "ERROR: $ENV_FILE missing"; exit 1; }
|
||
|
|
set -a
|
||
|
|
# shellcheck source=/dev/null
|
||
|
|
source "$ENV_FILE"
|
||
|
|
set +a
|
||
|
|
|
||
|
|
mkdir -p "$(dirname "$STATE_FILE")"
|
||
|
|
|
||
|
|
API_RESPONSE=$(curl -sf \
|
||
|
|
-H "Accept: application/vnd.github.v3+json" \
|
||
|
|
-H "User-Agent: lotus-cinny-monitor/1.0" \
|
||
|
|
"https://api.github.com/repos/cinnyapp/cinny/releases/latest") || {
|
||
|
|
echo "ERROR: GitHub API request failed"
|
||
|
|
exit 1
|
||
|
|
}
|
||
|
|
|
||
|
|
LATEST_TAG=$(echo "$API_RESPONSE" | jq -r '.tag_name')
|
||
|
|
LATEST_NAME=$(echo "$API_RESPONSE" | jq -r '.name')
|
||
|
|
RELEASE_URL=$(echo "$API_RESPONSE" | jq -r '.html_url')
|
||
|
|
RELEASE_BODY=$(echo "$API_RESPONSE" | jq -r '.body | split("\n")[0:3] | join(" | ")')
|
||
|
|
|
||
|
|
LAST_TAG=""
|
||
|
|
[ -f "$STATE_FILE" ] && LAST_TAG=$(cat "$STATE_FILE")
|
||
|
|
|
||
|
|
if [ "$LATEST_TAG" = "$LAST_TAG" ]; then
|
||
|
|
echo "No new release (still at $LATEST_TAG), nothing to do."
|
||
|
|
exit 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "New release: $LATEST_TAG (was: ${LAST_TAG:-none})"
|
||
|
|
|
||
|
|
export _CM_LATEST_TAG="$LATEST_TAG"
|
||
|
|
export _CM_LATEST_NAME="$LATEST_NAME"
|
||
|
|
export _CM_RELEASE_URL="$RELEASE_URL"
|
||
|
|
export _CM_RELEASE_BODY="$RELEASE_BODY"
|
||
|
|
export _CM_LAST_TAG="$LAST_TAG"
|
||
|
|
|
||
|
|
python3 << 'PYEOF'
|
||
|
|
import urllib.request, urllib.parse, json, time, sys, os
|
||
|
|
|
||
|
|
token = os.environ.get('MATRIX_TOKEN', '')
|
||
|
|
server = os.environ.get('MATRIX_SERVER', '').rstrip('/')
|
||
|
|
room = os.environ.get('MATRIX_ROOM', '')
|
||
|
|
ping = os.environ.get('MATRIX_PING_USER', '')
|
||
|
|
|
||
|
|
if not all([token, server, room]):
|
||
|
|
print("ERROR: MATRIX_TOKEN/MATRIX_SERVER/MATRIX_ROOM not set in env file")
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
tag = os.environ['_CM_LATEST_TAG']
|
||
|
|
name = os.environ['_CM_LATEST_NAME']
|
||
|
|
url = os.environ['_CM_RELEASE_URL']
|
||
|
|
body = os.environ['_CM_RELEASE_BODY']
|
||
|
|
last_tag = os.environ['_CM_LAST_TAG']
|
||
|
|
from_str = f" (was {last_tag})" if last_tag else ""
|
||
|
|
|
||
|
|
ping_safe = urllib.parse.quote(ping, safe=":@")
|
||
|
|
plain = (
|
||
|
|
f"{ping}: Cinny {tag} released{from_str}.\n"
|
||
|
|
f"{body}\n"
|
||
|
|
f"Review release notes, then send !cinny-update to merge + rebuild.\n{url}"
|
||
|
|
)
|
||
|
|
html = (
|
||
|
|
f"<a href='https://matrix.to/#/{ping_safe}'>{ping}</a>: "
|
||
|
|
f"<strong>Cinny {tag} released</strong>{from_str}.<br>"
|
||
|
|
f"<em>{body}</em><br>"
|
||
|
|
f"<a href='{url}'>Release notes</a> · Send <code>!cinny-update</code> to merge + rebuild."
|
||
|
|
)
|
||
|
|
|
||
|
|
txn = str(int(time.time() * 1000))
|
||
|
|
api = f"{server}/_matrix/client/v3/rooms/{urllib.parse.quote(room, safe='')}/send/m.room.message/{txn}"
|
||
|
|
body_json = json.dumps({"msgtype": "m.text", "body": plain,
|
||
|
|
"format": "org.matrix.custom.html", "formatted_body": html}).encode()
|
||
|
|
req = urllib.request.Request(api, data=body_json, method="PUT", headers={
|
||
|
|
"Authorization": f"Bearer {token}",
|
||
|
|
"Content-Type": "application/json",
|
||
|
|
})
|
||
|
|
urllib.request.urlopen(req, timeout=10)
|
||
|
|
print(f"Notification sent for {tag}")
|
||
|
|
PYEOF
|
||
|
|
|
||
|
|
echo "$LATEST_TAG" > "$STATE_FILE"
|
||
|
|
echo "State updated to $LATEST_TAG"
|