Files
shotscreen/release_hufterproof_v2.sh
Nick Roodenrijs 0dabed11d2 🎉 ShotScreen v1.0 - Initial Release
🚀 First official release of ShotScreen with complete feature set:

 Core Features:
- Advanced screenshot capture system
- Multi-monitor support
- Professional UI/UX design
- Automated update system with Sparkle
- Apple notarized & code signed

🛠 Technical Excellence:
- Native Swift macOS application
- Professional build & deployment pipeline
- Comprehensive error handling
- Memory optimized performance

📦 Distribution Ready:
- Professional DMG packaging
- Apple notarization complete
- No security warnings for users
- Ready for public distribution

This is the foundation release that establishes ShotScreen as a premium screenshot tool for macOS.
2025-06-28 16:15:15 +02:00

320 lines
10 KiB
Bash
Executable File

#!/bin/bash
# 🚀 SUPER HUFTERPROOF ShotScreen Release Script V2
# Zero HTML, Zero Problems, 100% Bulletproof
# 🎯 NEW: Auto-increment version detection!
#
# Usage:
# ./release_hufterproof_v2.sh # Auto-increment from current version
# ./release_hufterproof_v2.sh 1.15 # Use specific version
set -e
# Colors
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
PURPLE='\033[0;35m'
NC='\033[0m'
# Config
GITEA_URL="https://git.plet.i234.me"
RELEASES_REPO="Nick/shotscreen"
echo -e "${PURPLE}🚀 SUPER HUFTERPROOF Release Script V2${NC}"
echo -e "${PURPLE}=====================================${NC}"
# Get current version from Info.plist and auto-increment
get_next_version() {
if [ -f "Info.plist" ]; then
CURRENT_VERSION=$(plutil -extract CFBundleShortVersionString raw Info.plist 2>/dev/null || echo "1.0")
echo -e "${BLUE}🔍 Current version: $CURRENT_VERSION${NC}"
# Split version into parts (e.g., "1.12" -> major=1, minor=12)
IFS='.' read -r MAJOR MINOR <<< "$CURRENT_VERSION"
# Increment minor version
NEXT_MINOR=$((MINOR + 1))
SUGGESTED_VERSION="$MAJOR.$NEXT_MINOR"
echo -e "${GREEN}🚀 Suggested next version: $SUGGESTED_VERSION${NC}"
return 0
else
SUGGESTED_VERSION="1.0"
echo -e "${YELLOW}⚠️ No Info.plist found, suggesting: $SUGGESTED_VERSION${NC}"
return 1
fi
}
# Determine version
NEW_VERSION="$1"
if [ -z "$NEW_VERSION" ]; then
get_next_version
echo -e "${YELLOW}Press ENTER to use suggested version ($SUGGESTED_VERSION) or type custom version:${NC}"
read -r USER_INPUT
if [ -z "$USER_INPUT" ]; then
NEW_VERSION="$SUGGESTED_VERSION"
echo -e "${GREEN}✅ Using suggested version: $NEW_VERSION${NC}"
else
NEW_VERSION="$USER_INPUT"
echo -e "${BLUE}✅ Using custom version: $NEW_VERSION${NC}"
fi
fi
echo -e "${BLUE}📋 Building version: $NEW_VERSION${NC}"
# Update Info.plist
echo -e "${BLUE}📝 Updating Info.plist...${NC}"
plutil -replace CFBundleVersion -string "$NEW_VERSION" Info.plist
plutil -replace CFBundleShortVersionString -string "$NEW_VERSION" Info.plist
# Update build_release_signed.sh with new version
echo -e "${BLUE}📝 Updating build script version...${NC}"
sed -i '' "s/APP_VERSION=\".*\"/APP_VERSION=\"$NEW_VERSION\"/" build_release_signed.sh
# Build app with Developer ID signing and notarization
echo -e "${BLUE}🔨 Building app with Developer ID signing...${NC}"
if ! ./build_release_signed.sh; then
echo -e "${RED}❌ Build failed${NC}"
exit 1
fi
# Check DMG exists
DMG_PATH="./dist/ShotScreen-$NEW_VERSION.dmg"
if [ ! -f "$DMG_PATH" ]; then
echo -e "${RED}❌ DMG not found: $DMG_PATH${NC}"
exit 1
fi
# Get signature and size
echo -e "${BLUE}🔐 Getting signature...${NC}"
SIG_OUTPUT=$(./.build/artifacts/sparkle/Sparkle/bin/sign_update "$DMG_PATH")
SIGNATURE=$(echo "$SIG_OUTPUT" | grep -o 'sparkle:edSignature="[^"]*"' | cut -d'"' -f2)
DMG_SIZE=$(stat -f%z "$DMG_PATH")
if [ -z "$SIGNATURE" ]; then
echo -e "${RED}❌ Signature generation failed${NC}"
exit 1
fi
echo -e "${GREEN}✅ Signature: ${SIGNATURE:0:20}...${NC}"
echo -e "${GREEN}✅ Size: $DMG_SIZE bytes${NC}"
# Get release notes from TXT file (preserve original formatting)
RELEASE_NOTES=$(awk '
/^ShotScreen [0-9]/ {
if (count++ > 0) exit
next
}
/^=+$/ { next }
/^[A-Za-z]/ && !/^ShotScreen/ && NF > 0 {
print $0
}
/^ / && NF > 0 {
print $0
}
' release_notes.txt)
if [ -z "$RELEASE_NOTES" ]; then
RELEASE_NOTES="Release version $NEW_VERSION"
fi
# Create Gitea release
echo -e "${BLUE}📦 Creating Gitea release...${NC}"
# Format release notes beautifully for Gitea (markdown format)
FORMATTED_NOTES="## 🚀 ShotScreen $NEW_VERSION Features
$RELEASE_NOTES"
# Clean JSON (proper escaping for multiline text)
CLEAN_NOTES=$(printf "%s" "$FORMATTED_NOTES" | sed 's/"/\\"/g' | awk '{printf "%s\\n", $0}' | sed 's/\\n$//')
JSON_PAYLOAD="{
\"tag_name\": \"v$NEW_VERSION\",
\"name\": \"ShotScreen v$NEW_VERSION\",
\"body\": \"$CLEAN_NOTES\",
\"draft\": false,
\"prerelease\": false
}"
RESPONSE=$(curl -s -X POST \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "$JSON_PAYLOAD" \
"$GITEA_URL/api/v1/repos/$RELEASES_REPO/releases")
RELEASE_ID=$(echo "$RESPONSE" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
if [ -z "$RELEASE_ID" ]; then
echo -e "${YELLOW}⚠️ Checking for existing release...${NC}"
EXISTING=$(curl -s "$GITEA_URL/api/v1/repos/$RELEASES_REPO/releases/tags/v$NEW_VERSION")
RELEASE_ID=$(echo "$EXISTING" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
fi
if [ -z "$RELEASE_ID" ]; then
echo -e "${RED}❌ Could not create/find release${NC}"
exit 1
fi
echo -e "${GREEN}✅ Release ID: $RELEASE_ID${NC}"
# Upload DMG
echo -e "${BLUE}📤 Uploading DMG...${NC}"
UPLOAD_RESPONSE=$(curl -s -X POST \
-H "Authorization: token $GITEA_TOKEN" \
-F "attachment=@$DMG_PATH" \
"$GITEA_URL/api/v1/repos/$RELEASES_REPO/releases/$RELEASE_ID/assets")
ASSET_ID=$(echo "$UPLOAD_RESPONSE" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
if [ -z "$ASSET_ID" ]; then
echo -e "${RED}❌ DMG upload failed${NC}"
exit 1
fi
echo -e "${GREEN}✅ DMG uploaded${NC}"
# Create super simple appcast (no HTML, just plain text)
echo -e "${BLUE}📄 Creating simple appcast...${NC}"
# Convert plain text to simple HTML for appcast
HTML_NOTES=$(echo "$RELEASE_NOTES" | sed 's/^- /<li>/' | sed 's/$/<\/li>/')
if [[ "$HTML_NOTES" == *"<li>"* ]]; then
HTML_NOTES="<ul>$HTML_NOTES</ul>"
fi
# Clean up any old appcast first
rm -f appcast.xml
echo -e "${BLUE}📄 Generating appcast for version $NEW_VERSION...${NC}"
cat > appcast.xml << EOF
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
<channel>
<title>ShotScreen Updates</title>
<link>$GITEA_URL/$RELEASES_REPO/raw/branch/main/appcast.xml</link>
<description>ShotScreen Updates</description>
<language>en</language>
<item>
<title>ShotScreen $NEW_VERSION</title>
<description><![CDATA[
<h2>ShotScreen $NEW_VERSION</h2>
$HTML_NOTES
]]></description>
<pubDate>$(date -R)</pubDate>
<enclosure url="$GITEA_URL/$RELEASES_REPO/releases/download/v$NEW_VERSION/ShotScreen-$NEW_VERSION.dmg"
sparkle:version="$NEW_VERSION"
sparkle:shortVersionString="$NEW_VERSION"
length="$DMG_SIZE"
type="application/octet-stream"
sparkle:edSignature="$SIGNATURE" />
<sparkle:minimumSystemVersion>13.0</sparkle:minimumSystemVersion>
</item>
</channel>
</rss>
EOF
# Validate appcast
if ! xmllint --noout appcast.xml 2>/dev/null; then
echo -e "${RED}❌ Appcast validation failed${NC}"
exit 1
fi
echo -e "${GREEN}✅ Valid appcast created${NC}"
# Debug: Show what we're about to deploy
echo -e "${BLUE}🔍 DEBUG: Appcast content preview:${NC}"
echo "Version: $(grep 'sparkle:version=' appcast.xml | head -1)"
echo "URL: $(grep 'enclosure url=' appcast.xml | head -1 | cut -d'"' -f2)"
# Deploy appcast
echo -e "${BLUE}🚀 Deploying appcast...${NC}"
RELEASES_DIR="../shotscreen"
# Clean up any existing problematic repository
if [ -d "$RELEASES_DIR" ]; then
echo -e "${YELLOW}🧹 Cleaning up existing repository...${NC}"
rm -rf "$RELEASES_DIR"
fi
# Create fresh directory and clone
mkdir -p "$RELEASES_DIR"
cd "$RELEASES_DIR"
echo -e "${BLUE}📥 Fresh clone from remote...${NC}"
git clone "$GITEA_URL/$RELEASES_REPO.git" . || {
echo -e "${YELLOW}⚠️ Remote repository doesn't exist or clone failed, creating new repo...${NC}"
git init
git remote add origin "$GITEA_URL/$RELEASES_REPO.git"
}
# Configure git for this repository
git config pull.rebase false # Use merge instead of rebase
git config user.name "ShotScreen Release Bot" || true
git config user.email "releases@shotscreen.app" || true
# Backup old appcast for comparison
if [ -f "appcast.xml" ]; then
echo -e "${BLUE}📋 Backing up old appcast...${NC}"
cp "appcast.xml" "appcast.xml.backup"
echo "Old version: $(grep 'sparkle:version=' "appcast.xml" | head -1)"
fi
# Copy new appcast
cd - > /dev/null
cp appcast.xml "$RELEASES_DIR/"
cd "$RELEASES_DIR"
# Add and commit
git add appcast.xml
git commit -m "Deploy appcast for v$NEW_VERSION" || true
# Push with force if needed (since we're authoritative for appcast)
echo -e "${BLUE}📤 Pushing appcast to remote...${NC}"
if ! git push origin main; then
echo -e "${YELLOW}⚠️ Normal push failed, force pushing appcast update...${NC}"
git push origin main --force
fi
cd - > /dev/null
# Test everything
echo -e "${BLUE}🧪 Testing...${NC}"
sleep 2
# Test appcast download
if ! curl -s "$GITEA_URL/$RELEASES_REPO/raw/branch/main/appcast.xml" | grep -q "$NEW_VERSION"; then
echo -e "${RED}❌ Appcast test failed${NC}"
exit 1
fi
# Test DMG download
if ! curl -I -s "$GITEA_URL/$RELEASES_REPO/releases/download/v$NEW_VERSION/ShotScreen-$NEW_VERSION.dmg" | grep -q "200"; then
echo -e "${RED}❌ DMG test failed${NC}"
exit 1
fi
# Git operations
echo -e "${BLUE}📝 Git operations...${NC}"
git add .
git commit -m "Release v$NEW_VERSION" || true
git tag -a "v$NEW_VERSION" -m "Release v$NEW_VERSION" || true
git push -u origin main || git push origin main --force
git push origin "v$NEW_VERSION" || true
echo
echo -e "${GREEN}🎉 SUPER HUFTERPROOF RELEASE COMPLETE! 🎉${NC}"
echo -e "${GREEN}======================================${NC}"
echo -e "${GREEN}✅ Version: $NEW_VERSION${NC}"
echo -e "${GREEN}✅ DMG Size: $DMG_SIZE bytes${NC}"
echo -e "${GREEN}✅ All tests: PASSED${NC}"
echo
echo -e "${BLUE}🌐 Release: $GITEA_URL/$RELEASES_REPO/releases/tag/v$NEW_VERSION${NC}"
echo -e "${BLUE}📡 Appcast: $GITEA_URL/$RELEASES_REPO/raw/branch/main/appcast.xml${NC}"
echo
echo -e "${YELLOW}🎯 Ready to test updates on other computer!${NC}"