Add release.sh: one-command build + tag + Gitea release upload

Usage: ./release.sh 1.0.0 'Release notes here'
Or:    ./release.sh 1.0.0   (prompts for notes)

Requires GITEA_TOKEN env var (or prompts).
Does: version bump → commit → build DMG → tag → push → create
Gitea release → upload DMG → print download URL.
This commit is contained in:
Kyle Bolen
2026-08-02 03:46:26 +00:00
parent 694dca8817
commit 0042f3b6ba

141
release.sh Executable file
View File

@@ -0,0 +1,141 @@
#!/bin/bash
# release.sh — build and publish a PhotoPhetch release to Gitea
#
# Usage:
# ./release.sh 1.0.0 "First release with Android and iPhone support"
# ./release.sh 1.1.0 # prompts for release notes
#
# Requires:
# - curl (built into macOS)
# - git
# - ./gradlew (Gradle wrapper in this directory)
# - GITEA_TOKEN environment variable, or it will prompt
# Get a token: https://git.bolenpad.com/user/settings/applications
set -euo pipefail
# ── Config ──────────────────────────────────────────────────────────────────
GITEA_URL="https://git.bolenpad.com"
GITEA_USER="kbolen"
GITEA_REPO="photophetch"
# ── Arguments ───────────────────────────────────────────────────────────────
if [ $# -lt 1 ]; then
echo "Usage: $0 <version> [release-notes]"
echo " Example: $0 1.0.0 'First stable release'"
exit 1
fi
VERSION="$1"
TAG="v${VERSION}"
# Release notes: from argument or prompt
if [ $# -ge 2 ]; then
NOTES="$2"
else
echo "Enter release notes (press Enter twice when done):"
NOTES=""
while IFS= read -r line; do
[[ -z "$line" ]] && break
NOTES="${NOTES}${line}\n"
done
fi
# Gitea token
if [ -z "${GITEA_TOKEN:-}" ]; then
echo "GITEA_TOKEN not set."
read -rsp "Enter Gitea API token: " GITEA_TOKEN
echo
fi
# ── Pre-flight checks ────────────────────────────────────────────────────────
echo "→ Checking for uncommitted changes..."
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "ERROR: Uncommitted changes exist. Commit or stash them first."
exit 1
fi
echo "→ Checking we're on main..."
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$BRANCH" != "main" ]; then
echo "ERROR: Not on main branch (currently on '$BRANCH'). Switch to main first."
exit 1
fi
echo "→ Checking tag $TAG doesn't already exist..."
if git tag | grep -q "^${TAG}$"; then
echo "ERROR: Tag $TAG already exists."
exit 1
fi
# ── Update version in build.gradle.kts ──────────────────────────────────────
echo "→ Updating version to $VERSION in build.gradle.kts..."
sed -i.bak "s/packageVersion = \"[^\"]*\"/packageVersion = \"${VERSION}\"/" build.gradle.kts
rm -f build.gradle.kts.bak
# ── Commit version bump ──────────────────────────────────────────────────────
echo "→ Committing version bump..."
git add build.gradle.kts
git commit -m "Bump version to $VERSION"
# ── Build DMG ───────────────────────────────────────────────────────────────
echo "→ Building DMG (this takes a few minutes)..."
./gradlew packageDmg
DMG_PATH=$(find build/compose/binaries/main/dmg -name "*.dmg" | head -1)
if [ -z "$DMG_PATH" ]; then
echo "ERROR: No DMG found in build/compose/binaries/main/dmg/"
exit 1
fi
DMG_NAME=$(basename "$DMG_PATH")
echo "→ Built: $DMG_PATH"
# ── Tag and push ─────────────────────────────────────────────────────────────
echo "→ Creating tag $TAG..."
git tag -a "$TAG" -m "Release $TAG"
echo "→ Pushing commits and tag..."
git push origin main
git push origin "$TAG"
# ── Create Gitea release ─────────────────────────────────────────────────────
echo "→ Creating Gitea release $TAG..."
RELEASE_RESPONSE=$(curl -s -X POST \
"${GITEA_URL}/api/v1/repos/${GITEA_USER}/${GITEA_REPO}/releases" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"tag_name\": \"${TAG}\",
\"name\": \"PhotoPhetch ${TAG}\",
\"body\": \"${NOTES}\",
\"draft\": false,
\"prerelease\": false
}")
RELEASE_ID=$(echo "$RELEASE_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])" 2>/dev/null)
if [ -z "$RELEASE_ID" ]; then
echo "ERROR: Failed to create release. Response:"
echo "$RELEASE_RESPONSE"
exit 1
fi
echo "→ Created release ID $RELEASE_ID"
# ── Upload DMG ───────────────────────────────────────────────────────────────
echo "→ Uploading $DMG_NAME..."
UPLOAD_RESPONSE=$(curl -s -X POST \
"${GITEA_URL}/api/v1/repos/${GITEA_USER}/${GITEA_REPO}/releases/${RELEASE_ID}/assets?name=${DMG_NAME}" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary "@${DMG_PATH}")
DOWNLOAD_URL=$(echo "$UPLOAD_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin).get('browser_download_url',''))" 2>/dev/null)
if [ -z "$DOWNLOAD_URL" ]; then
echo "WARNING: Upload may have failed. Response:"
echo "$UPLOAD_RESPONSE"
else
echo "✓ Released: $DOWNLOAD_URL"
fi
echo ""
echo "✓ Done! PhotoPhetch $TAG released."
echo " Release page: ${GITEA_URL}/${GITEA_USER}/${GITEA_REPO}/releases/tag/${TAG}"