Add Tonuino Manager website and Sparkle updates
This commit is contained in:
commit
1cac427ff2
44 files changed
+5861
No files matched your search
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
BUILD_DIR="$ROOT_DIR/.build"
|
||||
ARCHIVE_PATH="$BUILD_DIR/TonuinoManager.xcarchive"
|
||||
APP_VERSION="${APP_VERSION:-1.0.0}"
|
||||
APP_BUILD="${APP_BUILD:-1}"
|
||||
|
||||
: "${DEVELOPMENT_TEAM:?Set DEVELOPMENT_TEAM to the Apple Developer Team ID}"
|
||||
: "${DEVELOPER_ID_APPLICATION:?Set DEVELOPER_ID_APPLICATION to the Developer ID Application signing identity}"
|
||||
: "${NOTARY_PROFILE:?Set NOTARY_PROFILE to a stored notarytool keychain profile}"
|
||||
: "${FFMPEG_BINARY:?Set FFMPEG_BINARY to a self-contained universal FFmpeg executable with libmp3lame enabled}"
|
||||
: "${YTDLP_BINARY:?Set YTDLP_BINARY to a standalone universal yt-dlp executable}"
|
||||
: "${DENO_BINARY:?Set DENO_BINARY to a universal Deno executable for YouTube JavaScript challenges}"
|
||||
: "${FPCALC_BINARY:?Set FPCALC_BINARY to the universal Chromaprint fpcalc executable}"
|
||||
[[ -x "$FFMPEG_BINARY" ]] || { echo "FFMPEG_BINARY is not executable" >&2; exit 1; }
|
||||
[[ -x "$YTDLP_BINARY" ]] || { echo "YTDLP_BINARY is not executable" >&2; exit 1; }
|
||||
[[ -x "$DENO_BINARY" ]] || { echo "DENO_BINARY is not executable" >&2; exit 1; }
|
||||
[[ -x "$FPCALC_BINARY" ]] || { echo "FPCALC_BINARY is not executable" >&2; exit 1; }
|
||||
for TOOL_BINARY in "$FFMPEG_BINARY" "$YTDLP_BINARY" "$DENO_BINARY" "$FPCALC_BINARY"; do
|
||||
TOOL_ARCHS="$(lipo -archs "$TOOL_BINARY")"
|
||||
[[ "$TOOL_ARCHS" == *arm64* && "$TOOL_ARCHS" == *x86_64* ]] || { echo "$TOOL_BINARY must contain arm64 and x86_64 slices" >&2; exit 1; }
|
||||
done
|
||||
|
||||
cd "$ROOT_DIR"
|
||||
xcodegen generate
|
||||
xcodebuild -project TonuinoManager.xcodeproj -scheme TonuinoManager -configuration Release \
|
||||
-archivePath "$ARCHIVE_PATH" -derivedDataPath "$BUILD_DIR/DerivedData" \
|
||||
-clonedSourcePackagesDirPath "$BUILD_DIR/DerivedData/SourcePackages" \
|
||||
-destination 'generic/platform=macOS' \
|
||||
ARCHS='arm64 x86_64' ONLY_ACTIVE_ARCH=NO DEVELOPMENT_TEAM="$DEVELOPMENT_TEAM" \
|
||||
CODE_SIGN_IDENTITY="$DEVELOPER_ID_APPLICATION" MARKETING_VERSION="$APP_VERSION" \
|
||||
CURRENT_PROJECT_VERSION="$APP_BUILD" archive
|
||||
|
||||
APP_PATH="$(find "$ARCHIVE_PATH/Products/Applications" -maxdepth 1 -type d -name '*.app' -print -quit)"
|
||||
[[ -n "$APP_PATH" ]] || { echo "App bundle is missing from the archive" >&2; exit 1; }
|
||||
|
||||
mkdir -p "$APP_PATH/Contents/Resources"
|
||||
install -m 755 "$FFMPEG_BINARY" "$APP_PATH/Contents/Resources/ffmpeg"
|
||||
install -m 755 "$YTDLP_BINARY" "$APP_PATH/Contents/Resources/yt-dlp"
|
||||
install -m 755 "$DENO_BINARY" "$APP_PATH/Contents/Resources/deno"
|
||||
install -m 755 "$FPCALC_BINARY" "$APP_PATH/Contents/Resources/fpcalc"
|
||||
codesign --force --options runtime --timestamp --sign "$DEVELOPER_ID_APPLICATION" "$APP_PATH/Contents/Resources/ffmpeg"
|
||||
codesign --force --options runtime --timestamp --sign "$DEVELOPER_ID_APPLICATION" "$APP_PATH/Contents/Resources/yt-dlp"
|
||||
codesign --force --options runtime --timestamp --sign "$DEVELOPER_ID_APPLICATION" "$APP_PATH/Contents/Resources/deno"
|
||||
codesign --force --options runtime --timestamp --sign "$DEVELOPER_ID_APPLICATION" "$APP_PATH/Contents/Resources/fpcalc"
|
||||
codesign --force --deep --options runtime --timestamp --entitlements Sources/TonuinoManager/TonuinoManager.entitlements \
|
||||
--sign "$DEVELOPER_ID_APPLICATION" "$APP_PATH"
|
||||
|
||||
mkdir -p "$BUILD_DIR/release"
|
||||
NOTARY_ZIP="$BUILD_DIR/Tonuino-Manager-notary.zip"
|
||||
ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "$NOTARY_ZIP"
|
||||
xcrun notarytool submit "$NOTARY_ZIP" --keychain-profile "$NOTARY_PROFILE" --wait
|
||||
xcrun stapler staple "$APP_PATH"
|
||||
rm -f "$NOTARY_ZIP"
|
||||
ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "$BUILD_DIR/release/Tonuino-Manager-$APP_VERSION.zip"
|
||||
hdiutil create -volname 'Tonuino Manager' -srcfolder "$APP_PATH" -ov -format UDZO \
|
||||
"$BUILD_DIR/release/Tonuino-Manager-$APP_VERSION-universal.dmg"
|
||||
xcrun notarytool submit "$BUILD_DIR/release/Tonuino-Manager-$APP_VERSION-universal.dmg" --keychain-profile "$NOTARY_PROFILE" --wait
|
||||
xcrun stapler staple "$BUILD_DIR/release/Tonuino-Manager-$APP_VERSION-universal.dmg"
|
||||
echo "Created $BUILD_DIR/release/Tonuino-Manager-$APP_VERSION-universal.dmg and Sparkle ZIP"
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -ne 5 ]]; then
|
||||
echo "Usage: $0 /path/to/App.app /path/to/ffmpeg /path/to/yt-dlp /path/to/deno /path/to/fpcalc" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
APP_PATH="$1"
|
||||
FFMPEG_BINARY="$2"
|
||||
YTDLP_BINARY="$3"
|
||||
DENO_BINARY="$4"
|
||||
FPCALC_BINARY="$5"
|
||||
|
||||
[[ -d "$APP_PATH/Contents" ]] || { echo "App bundle not found: $APP_PATH" >&2; exit 1; }
|
||||
for TOOL_BINARY in "$FFMPEG_BINARY" "$YTDLP_BINARY" "$DENO_BINARY" "$FPCALC_BINARY"; do
|
||||
[[ -x "$TOOL_BINARY" ]] || { echo "Tool is not executable: $TOOL_BINARY" >&2; exit 1; }
|
||||
done
|
||||
|
||||
FFMPEG_ENCODERS="$("$FFMPEG_BINARY" -hide_banner -encoders 2>/dev/null)"
|
||||
[[ "$FFMPEG_ENCODERS" == *libmp3lame* ]] || { echo "FFmpeg must include libmp3lame" >&2; exit 1; }
|
||||
|
||||
RESOURCES="$APP_PATH/Contents/Resources"
|
||||
mkdir -p "$RESOURCES"
|
||||
install -m 755 "$FFMPEG_BINARY" "$RESOURCES/ffmpeg"
|
||||
install -m 755 "$YTDLP_BINARY" "$RESOURCES/yt-dlp"
|
||||
install -m 755 "$DENO_BINARY" "$RESOURCES/deno"
|
||||
install -m 755 "$FPCALC_BINARY" "$RESOURCES/fpcalc"
|
||||
codesign --force --deep --sign - "$APP_PATH"
|
||||
echo "Bundled FFmpeg, yt-dlp, Deno and fpcalc into $APP_PATH"
|
||||
Executable
+108
@@ -0,0 +1,108 @@
|
||||
#!/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'''<item>
|
||||
<title>Version {html.escape(version)}</title>
|
||||
<pubDate>{pubdate}</pubDate>
|
||||
<sparkle:version>{html.escape(build)}</sparkle:version>
|
||||
<sparkle:shortVersionString>{html.escape(version)}</sparkle:shortVersionString>
|
||||
<enclosure url="{html.escape(url, quote=True)}" length="{int(length)}" type="application/octet-stream" sparkle:edSignature="{html.escape(signature, quote=True)}" />
|
||||
</item>'''
|
||||
xml = f'''<?xml version="1.0" encoding="utf-8"?>
|
||||
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<channel>
|
||||
<title>Tonuino Manager Updates</title>
|
||||
<link>https://tonuino.maddin.app/</link>
|
||||
<description>Updates for Tonuino Manager for macOS</description>
|
||||
<language>de</language>
|
||||
{item}
|
||||
</channel>
|
||||
</rss>
|
||||
'''
|
||||
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 = '<a class="download" href="/downloads/Tonuino-Manager.dmg">↓ App für macOS laden</a>'
|
||||
page = page.replace("<!-- DOWNLOAD_LINK -->", link)
|
||||
page = page.replace("<!-- APP_VERSION -->", 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."
|
||||
Reference in new issue
Block a user