#!/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"