#!/bin/bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" BUILD_DIR="$ROOT_DIR/.build" RELEASE_DIR="$BUILD_DIR/release" APP_VERSION="${APP_VERSION:?Set APP_VERSION, for example 1.0.1}" APP_BUILD="${APP_BUILD:?Set APP_BUILD to a monotonically increasing build number}" GITEA_TOKEN="${GITEA_TOKEN:?Set GITEA_TOKEN with permission to create releases}" GITEA_API="${GITEA_API:-https://git.maddin.app/api/v1}" GITEA_REPO="${GITEA_REPO:-maddin/Tonuino-Manager}" WEBSITE_HOST="${WEBSITE_HOST:-mln}" WEBSITE_ROOT="${WEBSITE_ROOT:-/home/maddin/docker/tonuino-manager/site}" FEED_URL="https://git.maddin.app/maddin/Tonuino-Manager/raw/branch/main/appcast.xml" TAG="v$APP_VERSION" ZIP_PATH="$RELEASE_DIR/Tonuino-Manager-$APP_VERSION.zip" DMG_PATH="$RELEASE_DIR/Tonuino-Manager-$APP_VERSION-universal.dmg" SIGN_UPDATE="${SPARKLE_SIGN_UPDATE:-$BUILD_DIR/DerivedData/SourcePackages/artifacts/sparkle/Sparkle/bin/sign_update}" [[ -f "$ZIP_PATH" ]] || { echo "Missing Sparkle archive: $ZIP_PATH" >&2; exit 1; } [[ -f "$DMG_PATH" ]] || { echo "Missing installer: $DMG_PATH" >&2; exit 1; } [[ -x "$SIGN_UPDATE" ]] || { echo "Missing Sparkle sign_update: $SIGN_UPDATE" >&2; exit 1; } command -v rsync >/dev/null || { echo "rsync is required" >&2; exit 1; } command -v python3 >/dev/null || { echo "python3 is required" >&2; exit 1; } cd "$ROOT_DIR" git rev-parse --is-inside-work-tree >/dev/null 2>&1 || { echo "Run this from a Git checkout" >&2; exit 1; } [[ "$(git branch --show-current)" == main ]] || { echo "Publish from the main branch" >&2; exit 1; } [[ -z "$(git status --porcelain)" ]] || { echo "Commit or discard local changes before publishing" >&2; exit 1; } git remote get-url origin >/dev/null 2>&1 || { echo "Configure origin to the Gitea repository first" >&2; exit 1; } if git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null 2>&1; then echo "Release tag $TAG already exists" >&2 exit 1 fi SIGNATURE="$("$SIGN_UPDATE" -p "$ZIP_PATH")" ZIP_LENGTH="$(stat -f '%z' "$ZIP_PATH")" RELEASE_URL="https://git.maddin.app/$GITEA_REPO/releases/download/$TAG/$(basename "$ZIP_PATH")" APPCAST_TMP="$(mktemp)" trap 'rm -f "$APPCAST_TMP"' EXIT python3 - "$APPCAST_TMP" "$APP_VERSION" "$APP_BUILD" "$RELEASE_URL" "$ZIP_LENGTH" "$SIGNATURE" <<'PY' import datetime, html, sys path, version, build, url, length, signature = sys.argv[1:] pubdate = datetime.datetime.now(datetime.timezone.utc).strftime("%a, %d %b %Y %H:%M:%S +0000") item = f''' Version {html.escape(version)} {pubdate} {html.escape(build)} {html.escape(version)} ''' xml = f''' Tonuino Manager Updates https://tonuino.maddin.app/ Updates for Tonuino Manager for macOS de {item} ''' open(path, "w", encoding="utf-8").write(xml) PY RELEASE_JSON="$(python3 - "$TAG" <<'PY' import json, sys tag = sys.argv[1] print(json.dumps({"tag_name": tag, "target_commitish": "main", "name": "Tonuino Manager " + tag, "body": "Universal macOS-Version mit automatischen Sparkle-Updates.", "draft": False, "prerelease": False})) PY )" RELEASE_RESPONSE="$(curl --fail-with-body --silent --show-error \ -H "Authorization: token $GITEA_TOKEN" -H 'Content-Type: application/json' \ -d "$RELEASE_JSON" "$GITEA_API/repos/$GITEA_REPO/releases")" RELEASE_ID="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' <<<"$RELEASE_RESPONSE")" upload_asset() { local file="$1" curl --fail-with-body --silent --show-error -X POST \ -H "Authorization: token $GITEA_TOKEN" \ -F "attachment=@$file" \ "$GITEA_API/repos/$GITEA_REPO/releases/$RELEASE_ID/assets?name=$(basename "$file")" >/dev/null } upload_asset "$ZIP_PATH" cp "$APPCAST_TMP" "$ROOT_DIR/appcast.xml" git add appcast.xml git commit -m "Publish Sparkle appcast for $TAG" git push origin main SITE_STAGE="$(mktemp -d)" trap 'rm -f "$APPCAST_TMP"; rm -rf "$SITE_STAGE"' EXIT cp "$ROOT_DIR/website/icon.png" "$SITE_STAGE/icon.png" python3 - "$ROOT_DIR/website/index.html.template" "$SITE_STAGE/index.html" "$APP_VERSION" <<'PY' import html, sys source, target, version = sys.argv[1:] page = open(source, encoding="utf-8").read() link = '↓  App für macOS laden' page = page.replace("", link) page = page.replace("", html.escape(version)) open(target, "w", encoding="utf-8").write(page) PY mkdir -p "$SITE_STAGE/downloads" cp "$DMG_PATH" "$SITE_STAGE/downloads/Tonuino-Manager.dmg" ssh "$WEBSITE_HOST" "mkdir -p '$WEBSITE_ROOT/downloads'" rsync -az --delete "$SITE_STAGE/" "$WEBSITE_HOST:$WEBSITE_ROOT/" echo "Published $TAG, Sparkle feed, and website download."