From cd34760ca110ed9194da51e73c2f99bb895bf1d2 Mon Sep 17 00:00:00 2001 From: Samuel Zielke Date: Mon, 14 Sep 2026 01:01:17 +0200 Subject: [PATCH] Add Release-Script --- scripts/publish-release.sh | 105 +++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100755 scripts/publish-release.sh diff --git a/scripts/publish-release.sh b/scripts/publish-release.sh new file mode 100755 index 0000000..520b4f1 --- /dev/null +++ b/scripts/publish-release.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash +# +# publish-release.sh — signiert ein fertiges DMG mit dem Sparkle-Schlüssel, +# trägt den Eintrag in den appcast ein und lädt beides auf den Update-Server. +# +# Läuft auf dem MAC, nicht auf dem Server. +# Voraussetzung: DMG ist bereits gebaut, signiert, notarisiert und gestapled. +# +# Aufruf aus dem Repo-Wurzelverzeichnis: +# ./scripts/publish-release.sh dist/drop.two-1.0.1.dmg 1.0.1 12 +# +# Alle Einstellungen unten lassen sich per Umgebungsvariable überschreiben, +# z. B.: DEPLOY_HOST=deploy@anderer-host ./scripts/publish-release.sh ... +# +set -euo pipefail + +DMG="${1:?Pfad zum DMG fehlt}" +SHORT_VERSION="${2:?Versionsstring fehlt, z.B. 1.0.1}" +BUILD="${3:?Buildnummer fehlt, z.B. 12}" + +# --- Einstellungen ---------------------------------------------------------- +HOST="${DEPLOY_HOST:-deploy@10.0.5.223}" +REMOTE_DIR="${DEPLOY_DIR:-/var/www/updates/droptwo}" +BASE_URL="${DEPLOY_BASE_URL:-https://update.zielke.studio/droptwo}" +SIGN_UPDATE="${SPARKLE_SIGN_UPDATE:-./bin/sign_update}" +# ---------------------------------------------------------------------------- + +[[ -f "$DMG" ]] || { echo "DMG nicht gefunden: $DMG" >&2; exit 1; } +[[ -x "$SIGN_UPDATE" ]] || { + echo "sign_update nicht gefunden unter: $SIGN_UPDATE" >&2 + echo "Aus dem Sparkle-Release holen oder SPARKLE_SIGN_UPDATE setzen." >&2 + exit 1 +} + +echo "==> Notarisierung prüfen" +if ! spctl -a -t open --context context:primary-signature "$DMG" 2>&1 | grep -q accepted; then + echo "FEHLER: DMG ist nicht notarisiert oder nicht gestapled." >&2 + echo "Ein Upload jetzt würde Kollegen eine blockierte App ausliefern." >&2 + exit 1 +fi + +echo "==> Signieren" +# sign_update liefert die fertigen Attribute inkl. length und edSignature +ATTRS="$("$SIGN_UPDATE" "$DMG")" +echo " $ATTRS" + +FILENAME="$(basename "$DMG")" +PUBDATE="$(LC_ALL=C date -u '+%a, %d %b %Y %H:%M:%S +0000')" + +echo "==> appcast-Eintrag bauen" +ITEM=$(cat < + ${SHORT_VERSION} + ${PUBDATE} + ${BUILD} + ${SHORT_VERSION} + 13.0 + + +XML +) + +echo "==> appcast vom Server holen" +scp -q "${HOST}:${REMOTE_DIR}/appcast.xml" /tmp/appcast.xml 2>/dev/null || { + echo " kein appcast vorhanden, lege neuen an" + cat > /tmp/appcast.xml < + + + drop.two + ${BASE_URL}/appcast.xml + Updates für drop.two + de + + +XML +} + +# Neuen Eintrag direkt nach drop.two einfügen — neueste oben +python3 - "$ITEM" "$BUILD" <<'PY' +import sys, io, re +item, build = sys.argv[1], sys.argv[2] +p = "/tmp/appcast.xml" +s = io.open(p, encoding="utf-8").read() + +if re.search(r"\s*%s\s*" % re.escape(build), s): + sys.exit("FEHLER: Buildnummer %s steht bereits im appcast. " + "Sparkle vergleicht nur diese Zahl — bitte hochzählen." % build) + +anchor = "drop.two" +if anchor not in s: + sys.exit("Anker im appcast nicht gefunden — Datei manuell prüfen.") + +s = s.replace(anchor, anchor + "\n" + item, 1) +io.open(p, "w", encoding="utf-8").write(s) +print(" Eintrag eingefügt") +PY + +echo "==> Hochladen" +scp -q "$DMG" "${HOST}:${REMOTE_DIR}/${FILENAME}" +scp -q /tmp/appcast.xml "${HOST}:${REMOTE_DIR}/appcast.xml" +# Kein chown nötig: das SGID-Bit auf dem Ordner vererbt die Gruppe www-data. + +echo "==> Fertig: ${BASE_URL}/${FILENAME}" +echo " Zum Prüfen: curl -s ${BASE_URL}/appcast.xml | head -20"