Skip to content

Commit 39e0c71

Browse files
committed
Add professional DMG installer with drag-and-drop interface
Created DMG installer feature that provides a better user experience without requiring an Apple Developer account: Features: - Professional drag-and-drop installation window - Applications folder symlink for easy installation - Custom background with visual instructions (optional, requires ImageMagick) - Compressed DMG format for smaller download size - Automatic window layout and icon positioning Scripts added: - create-dmg.sh: Main DMG creation script using hdiutil (macOS built-in) - create-dmg-background.sh: Optional custom background image generator CI/CD Changes: - Build workflow now creates DMG files for both ARM64 and x86_64 - DMG files are uploaded as artifacts alongside ZIP files - GitHub releases include both DMG (recommended) and ZIP (alternative) - Updated release notes with clear installation instructions Benefits: - Users get a familiar macOS installation experience - No need to manually move .app to Applications - Professional presentation - Works without Apple Developer certificate Usage: ./create-dmg.sh [version] [architecture] [source-app-path] Example: ./create-dmg.sh 0.1.13 arm64 build/Release/TablePro.app
1 parent 0c35965 commit 39e0c71

3 files changed

Lines changed: 303 additions & 75 deletions

File tree

.github/workflows/build.yml

Lines changed: 95 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -416,41 +416,55 @@ jobs:
416416
echo "Binary size: $(ls -lh "$BINARY_PATH" | awk '{print $5}')"
417417
echo "App bundle size: $(du -sh build/Release/TablePro-arm64.app | awk '{print $1}')"
418418
419-
- name: Create ZIP archive
419+
- name: Create DMG installer
420420
run: |
421-
echo "Creating ZIP archive..."
421+
echo "Creating DMG installer..."
422422
423-
if [ ! -d "build/Release/TablePro-arm64.app" ]; then
424-
echo "❌ ERROR: App bundle not found for archiving"
423+
# Rename app to remove architecture suffix for better UX
424+
if [ -d "build/Release/TablePro-arm64.app" ]; then
425+
cp -R "build/Release/TablePro-arm64.app" "build/Release/TablePro.app"
426+
else
427+
echo "❌ ERROR: App bundle not found"
425428
exit 1
426429
fi
427430
428-
cd build/Release
431+
# Make DMG creation script executable
432+
chmod +x create-dmg.sh
429433
430-
if ! zip -r TablePro-arm64.zip TablePro-arm64.app; then
431-
echo "❌ ERROR: Failed to create ZIP archive"
432-
exit 1
433-
fi
434+
# Create DMG with version from git tag or use default
435+
VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.1.13")
436+
./create-dmg.sh "$VERSION" "arm64" "build/Release/TablePro.app"
434437
435-
if [ ! -f "TablePro-arm64.zip" ]; then
436-
echo "❌ ERROR: ZIP file was not created"
438+
# Verify DMG was created
439+
if [ ! -f "build/Release/TablePro-${VERSION}-arm64.dmg" ]; then
440+
echo "❌ ERROR: DMG file was not created"
437441
exit 1
438442
fi
439443
440-
# Verify it's a valid ZIP
441-
if ! unzip -t TablePro-arm64.zip > /dev/null; then
442-
echo "❌ ERROR: Created ZIP is corrupted or invalid"
444+
echo "✅ DMG installer created successfully"
445+
ls -lh build/Release/*.dmg
446+
447+
- name: Create ZIP archive (fallback)
448+
run: |
449+
echo "Creating ZIP archive..."
450+
451+
cd build/Release
452+
453+
if ! zip -r TablePro-arm64.zip TablePro-arm64.app; then
454+
echo "❌ ERROR: Failed to create ZIP archive"
443455
exit 1
444456
fi
445457
446-
echo "✅ ZIP archive created and verified"
458+
echo "✅ ZIP archive created"
447459
ls -lh TablePro-arm64.zip
448460
449-
- name: Upload artifact
461+
- name: Upload artifacts
450462
uses: actions/upload-artifact@v4
451463
with:
452464
name: TablePro-arm64-${{ github.sha }}
453-
path: build/Release/TablePro-arm64.zip
465+
path: |
466+
build/Release/*.dmg
467+
build/Release/TablePro-arm64.zip
454468
retention-days: ${{ startsWith(github.ref, 'refs/tags/v') && 90 || 7 }}
455469

456470
build-x86_64:
@@ -623,41 +637,55 @@ jobs:
623637
echo "Binary size: $(ls -lh "$BINARY_PATH" | awk '{print $5}')"
624638
echo "App bundle size: $(du -sh build/Release/TablePro-x86_64.app | awk '{print $1}')"
625639
626-
- name: Create ZIP archive
640+
- name: Create DMG installer
627641
run: |
628-
echo "Creating ZIP archive..."
642+
echo "Creating DMG installer..."
629643
630-
if [ ! -d "build/Release/TablePro-x86_64.app" ]; then
631-
echo "❌ ERROR: App bundle not found for archiving"
644+
# Rename app to remove architecture suffix for better UX
645+
if [ -d "build/Release/TablePro-x86_64.app" ]; then
646+
cp -R "build/Release/TablePro-x86_64.app" "build/Release/TablePro.app"
647+
else
648+
echo "❌ ERROR: App bundle not found"
632649
exit 1
633650
fi
634651
635-
cd build/Release
652+
# Make DMG creation script executable
653+
chmod +x create-dmg.sh
636654
637-
if ! zip -r TablePro-x86_64.zip TablePro-x86_64.app; then
638-
echo "❌ ERROR: Failed to create ZIP archive"
639-
exit 1
640-
fi
655+
# Create DMG with version from git tag or use default
656+
VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.1.13")
657+
./create-dmg.sh "$VERSION" "x86_64" "build/Release/TablePro.app"
641658
642-
if [ ! -f "TablePro-x86_64.zip" ]; then
643-
echo "❌ ERROR: ZIP file was not created"
659+
# Verify DMG was created
660+
if [ ! -f "build/Release/TablePro-${VERSION}-x86_64.dmg" ]; then
661+
echo "❌ ERROR: DMG file was not created"
644662
exit 1
645663
fi
646664
647-
# Verify it's a valid ZIP
648-
if ! unzip -t TablePro-x86_64.zip > /dev/null; then
649-
echo "❌ ERROR: Created ZIP is corrupted or invalid"
665+
echo "✅ DMG installer created successfully"
666+
ls -lh build/Release/*.dmg
667+
668+
- name: Create ZIP archive (fallback)
669+
run: |
670+
echo "Creating ZIP archive..."
671+
672+
cd build/Release
673+
674+
if ! zip -r TablePro-x86_64.zip TablePro-x86_64.app; then
675+
echo "❌ ERROR: Failed to create ZIP archive"
650676
exit 1
651677
fi
652678
653-
echo "✅ ZIP archive created and verified"
679+
echo "✅ ZIP archive created"
654680
ls -lh TablePro-x86_64.zip
655681
656-
- name: Upload artifact
682+
- name: Upload artifacts
657683
uses: actions/upload-artifact@v4
658684
with:
659685
name: TablePro-x86_64-${{ github.sha }}
660-
path: build/Release/TablePro-x86_64.zip
686+
path: |
687+
build/Release/*.dmg
688+
build/Release/TablePro-x86_64.zip
661689
retention-days: ${{ startsWith(github.ref, 'refs/tags/v') && 90 || 7 }}
662690

663691
release:
@@ -683,55 +711,33 @@ jobs:
683711
name: TablePro-x86_64-${{ github.sha }}
684712
path: artifacts/
685713

686-
- name: Verify and rename artifacts for release
714+
- name: Verify and organize artifacts for release
687715
run: |
688-
VERSION=${GITHUB_REF#refs/tags/}
716+
VERSION=${GITHUB_REF#refs/tags/v}
689717
690718
if [ -z "$VERSION" ]; then
691719
echo "❌ ERROR: Failed to extract version from ref: $GITHUB_REF"
692720
exit 1
693721
fi
694722
695723
echo "Preparing artifacts for version: $VERSION"
724+
echo "Contents of artifacts directory:"
725+
ls -la artifacts/
696726
697-
# Check source files exist
698-
if [ ! -f "artifacts/TablePro-arm64.zip" ]; then
699-
echo "❌ ERROR: ARM64 artifact not found"
700-
echo "Contents of artifacts directory:"
701-
ls -la artifacts/
702-
exit 1
703-
fi
704-
705-
if [ ! -f "artifacts/TablePro-x86_64.zip" ]; then
706-
echo "❌ ERROR: x86_64 artifact not found"
707-
echo "Contents of artifacts directory:"
708-
ls -la artifacts/
709-
exit 1
710-
fi
711-
712-
# Rename with verification
713-
if ! mv artifacts/TablePro-arm64.zip "artifacts/TablePro-${VERSION}-arm64.zip"; then
714-
echo "❌ ERROR: Failed to rename ARM64 artifact"
715-
exit 1
716-
fi
717-
718-
if ! mv artifacts/TablePro-x86_64.zip "artifacts/TablePro-${VERSION}-x86_64.zip"; then
719-
echo "❌ ERROR: Failed to rename x86_64 artifact"
720-
exit 1
721-
fi
727+
# Note: DMG files should already have correct names from build
728+
# ZIP files need to be renamed
722729
723-
# Verify renamed files exist
724-
if [ ! -f "artifacts/TablePro-${VERSION}-arm64.zip" ]; then
725-
echo "❌ ERROR: Renamed ARM64 file not found"
726-
exit 1
730+
# Rename ZIP files if they exist
731+
if [ -f "artifacts/TablePro-arm64.zip" ]; then
732+
mv artifacts/TablePro-arm64.zip "artifacts/TablePro-${VERSION}-arm64.zip"
727733
fi
728734
729-
if [ ! -f "artifacts/TablePro-${VERSION}-x86_64.zip" ]; then
730-
echo "❌ ERROR: Renamed x86_64 file not found"
731-
exit 1
735+
if [ -f "artifacts/TablePro-x86_64.zip" ]; then
736+
mv artifacts/TablePro-x86_64.zip "artifacts/TablePro-${VERSION}-x86_64.zip"
732737
fi
733738
734-
echo "✅ Artifacts renamed successfully"
739+
echo "✅ Artifacts organized successfully"
740+
echo "Final artifacts:"
735741
ls -lh artifacts/
736742
737743
- name: Generate release notes
@@ -761,15 +767,28 @@ jobs:
761767
762768
Choose the appropriate version for your Mac:
763769
764-
- **Apple Silicon (M1/M2/M3)**: Download \`TablePro-${VERSION}-arm64.zip\`
765-
- **Intel**: Download \`TablePro-${VERSION}-x86_64.zip\`
770+
**Recommended (DMG Installer):**
771+
- 🍎 **Apple Silicon (M1/M2/M3/M4)**: \`TablePro-${VERSION}-arm64.dmg\`
772+
- 💻 **Intel**: \`TablePro-${VERSION}-x86_64.dmg\`
773+
774+
**Alternative (ZIP Archive):**
775+
- \`TablePro-${VERSION}-arm64.zip\` (Apple Silicon)
776+
- \`TablePro-${VERSION}-x86_64.zip\` (Intel)
766777
767778
### Installation
768779
769-
1. Download the appropriate ZIP file for your Mac
770-
2. Unzip the file
771-
3. Move TablePro.app to your Applications folder
772-
4. Right-click and select "Open" on first launch (unsigned app)
780+
**DMG Installer (Recommended):**
781+
1. Download and open the DMG file
782+
2. Drag TablePro to the Applications folder
783+
3. Eject the DMG
784+
4. Right-click TablePro in Applications and select "Open" on first launch
785+
786+
**ZIP Archive:**
787+
1. Download and unzip the file
788+
2. Move TablePro.app to your Applications folder
789+
3. Right-click and select "Open" on first launch
790+
791+
> **Note:** This app is not signed with an Apple Developer certificate. You may need to allow it in System Settings > Privacy & Security on first launch.
773792
774793
### Changes
775794
@@ -783,6 +802,7 @@ jobs:
783802
uses: softprops/action-gh-release@v1
784803
with:
785804
files: |
805+
artifacts/*.dmg
786806
artifacts/*.zip
787807
body_path: release_notes.md
788808
draft: false

create-dmg-background.sh

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/bin/bash
2+
# Create a professional DMG background image
3+
4+
set -e
5+
6+
OUTPUT_DIR="${1:-.dmg-assets}"
7+
OUTPUT_FILE="$OUTPUT_DIR/dmg-background.png"
8+
9+
mkdir -p "$OUTPUT_DIR"
10+
11+
echo "🎨 Creating DMG background image..."
12+
13+
if ! command -v convert &> /dev/null; then
14+
echo "❌ ERROR: ImageMagick not found"
15+
echo " Install with: brew install imagemagick"
16+
exit 1
17+
fi
18+
19+
# DMG window size: 600x400
20+
WIDTH=600
21+
HEIGHT=400
22+
23+
# Create background with gradient
24+
convert -size ${WIDTH}x${HEIGHT} \
25+
gradient:'#f5f5f7-#ffffff' \
26+
"$OUTPUT_FILE"
27+
28+
# Add arrow pointing from left to right
29+
convert "$OUTPUT_FILE" \
30+
-stroke '#007AFF' \
31+
-strokewidth 3 \
32+
-fill none \
33+
-draw "path 'M 250,200 L 350,200'" \
34+
-draw "path 'M 340,190 L 350,200 L 340,210'" \
35+
"$OUTPUT_FILE"
36+
37+
# Add subtle text hint at bottom
38+
convert "$OUTPUT_FILE" \
39+
-font "Helvetica" \
40+
-pointsize 13 \
41+
-fill '#86868b' \
42+
-gravity South \
43+
-annotate +0+30 'Drag the app icon to the Applications folder to install' \
44+
"$OUTPUT_FILE"
45+
46+
# Add subtle shadow effect
47+
convert "$OUTPUT_FILE" \
48+
\( +clone -background black -shadow 60x3+0+0 \) \
49+
+swap -background none -layers merge +repage \
50+
"$OUTPUT_FILE"
51+
52+
echo "✅ Background image created: $OUTPUT_FILE"
53+
echo " Size: ${WIDTH}x${HEIGHT}"

0 commit comments

Comments
 (0)