mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-03 10:36:45 +09:00
backup_current tested `[ -f serverkit.db ]` FIRST and only fell through to the
PostgreSQL branch when no file was there. An install migrated SQLite ->
PostgreSQL keeps its old serverkit.db, and both deploy paths copy it forward on
file-existence alone (deploy_source, deploy_release), so the stale file is
carried into every future slot and never ages out. Such a box backed up the
abandoned file, skipped pg_dump entirely, and left PRE_UPGRADE_DUMP unset --
which migrate_database reads as "${PRE_UPGRADE_DUMP:-}", so its auto-restore
silently did nothing. PostgreSQL is the one engine that migrates IN PLACE and
cannot roll back by switching slots, so that dump is its only way home: the
exact safety net a61eab1/b48256f added was disabled for it.
migrate_database has always keyed off DATABASE_URL (its sqlite check). Test the
declared engine first so the two functions agree.
Deliberately NOT `grep DATABASE_URL=sqlite && [ -f db ]` as the reviewer
suggested: backend/config.py defaults DATABASE_URL when absent, so any install
whose .env lacks the line would fall to the else branch and update with NO
backup at all -- trading a mismatched backup for none. An explicit `postgres`
URL is a positive signal; the absence of a `sqlite` one is not.
Also:
* name the engine when it is one we cannot dump. "No SQLite database ..."
read like a benign nothing-to-do on a MySQL install that in fact had no
safety net. Prints the scheme only -- a DATABASE_URL carries a password and
this text lands in the update log.
* drop the stale "Python 3.11/3.12 is guaranteed by pre-flight" comment,
contradicted by locate_sqlite_python two lines below it (664a5e6).
* discord-notify: skip cleanly when DISCORD_WEBHOOK_URL is absent. Secrets
are not exposed to `pull_request` runs from a fork, so a merged fork PR
failed the job on an empty curl URL. Not switching to pull_request_target
for this -- handing a write-scoped token and the webhook to an
outsider-triggered run is a poor trade for a chat notification.
Tests: a PostgreSQL install carrying a leftover serverkit.db must still dump
and set PRE_UPGRADE_DUMP (fails on the parent commit, rc=1); an undumpable
engine must be named without leaking the password. Neither needs python3, so
both run on all 7 images. Workflow verified end-to-end against a synthetic
event payload: fork PR exits 0 without calling curl, normal PR still builds the
same embed.
Verified on the full CI matrix (ubuntu 22.04/24.04, debian:12, rockylinux:9,
almalinux:9, fedora:40, opensuse/leap:15.5), all six suites: 0 failed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
97 lines
3.9 KiB
YAML
97 lines
3.9 KiB
YAML
name: Discord PR Notify
|
|
|
|
on:
|
|
pull_request:
|
|
types: [closed] # merged only (filtered in the job `if:`)
|
|
|
|
jobs:
|
|
notify:
|
|
# Only when a PR is actually merged; closed-unmerged skips silently.
|
|
if: github.event.pull_request.merged == true
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Send merged PR to Discord
|
|
env:
|
|
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
# Repository secrets are NOT exposed to `pull_request` runs triggered
|
|
# from a fork, so a merged fork PR would reach curl with an empty URL
|
|
# and fail the job for no useful reason. Skip cleanly instead. The
|
|
# fix is deliberately NOT `pull_request_target`: that would hand a
|
|
# write-scoped token and this webhook to an outsider-triggered run,
|
|
# which is a poor trade for a chat notification.
|
|
if [ -z "${DISCORD_WEBHOOK_URL:-}" ]; then
|
|
echo "DISCORD_WEBHOOK_URL unavailable (fork PR?) — skipping notification."
|
|
exit 0
|
|
fi
|
|
|
|
# Read everything from the event payload via jq: PR text is
|
|
# attacker-controlled input, so it must never be interpolated
|
|
# into the script (backticks/$() would execute).
|
|
TITLE=$(jq -r '.pull_request.title' "$GITHUB_EVENT_PATH")
|
|
URL=$(jq -r '.pull_request.html_url' "$GITHUB_EVENT_PATH")
|
|
AUTHOR=$(jq -r '.pull_request.user.login' "$GITHUB_EVENT_PATH")
|
|
BASE=$(jq -r '.pull_request.base.ref' "$GITHUB_EVENT_PATH")
|
|
BODY=$(jq -r '.pull_request.body // ""' "$GITHUB_EVENT_PATH")
|
|
NUMBER=$(jq -r '.pull_request.number' "$GITHUB_EVENT_PATH")
|
|
MERGED_AT=$(jq -r '.pull_request.merged_at' "$GITHUB_EVENT_PATH")
|
|
|
|
echo "title_len=${#TITLE} body_len=${#BODY}"
|
|
|
|
# NOTE: use [ \t], never [[:space:]] — the runner's awk is mawk,
|
|
# which silently mis-parses POSIX character classes.
|
|
# Extract bullets from the "### Highlights" section (top 5).
|
|
# Stops at the next heading or the <details> block.
|
|
HIGHLIGHTS=$(printf '%s\n' "$BODY" | awk '
|
|
/^#+[ \t]*Highlights/ { f=1; next }
|
|
f && (/^#/ || /^</) { exit }
|
|
f && /^- / { print }
|
|
' | head -5)
|
|
|
|
if [ -n "$HIGHLIGHTS" ]; then
|
|
DESC="$HIGHLIGHTS"
|
|
else
|
|
# Lazy PR with no Highlights section: use the first prose
|
|
# paragraph of the body, truncated to 300 chars.
|
|
FIRST_PARA=$(printf '%s\n' "$BODY" | awk '
|
|
/^[ \t]*$/ { if (started) exit; next }
|
|
/^(#|<|```|\||- )/ { if (started) exit; next }
|
|
{ started=1; print }
|
|
' | tr '\n' ' ' | sed 's/ */ /g' | cut -c1-300)
|
|
if [ -n "$FIRST_PARA" ]; then
|
|
DESC="$FIRST_PARA"
|
|
else
|
|
DESC="_Merged without a description._"
|
|
fi
|
|
fi
|
|
|
|
# Discord embed descriptions cap at 4096 chars; stay under.
|
|
DESC=$(printf '%s' "$DESC" | cut -c1-3900)
|
|
|
|
jq -n \
|
|
--arg title "$TITLE" \
|
|
--arg url "$URL" \
|
|
--arg desc "$DESC" \
|
|
--arg author "$AUTHOR" \
|
|
--arg author_url "https://github.com/$AUTHOR" \
|
|
--arg avatar "https://github.com/$AUTHOR.png" \
|
|
--arg footer "PR #$NUMBER merged into $BASE" \
|
|
--arg ts "$MERGED_AT" \
|
|
'{
|
|
username: "ServerKit",
|
|
embeds: [{
|
|
author: { name: $author, url: $author_url, icon_url: $avatar },
|
|
title: $title,
|
|
url: $url,
|
|
description: $desc,
|
|
color: 10181046,
|
|
footer: { text: $footer },
|
|
timestamp: $ts
|
|
}]
|
|
}' > payload.json
|
|
|
|
curl -sS -f -X POST -H "Content-Type: application/json" \
|
|
-d @payload.json "$DISCORD_WEBHOOK_URL"
|