wip #45
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build TablePro | |
| on: | |
| push: | |
| tags: ["v*"] | |
| paths-ignore: | |
| - "**.md" | |
| - "docs/**" | |
| - ".vscode/**" | |
| env: | |
| XCODE_PROJECT: TablePro.xcodeproj | |
| XCODE_SCHEME: TablePro | |
| BUILD_CONFIGURATION: Release | |
| XCODE_VERSION: "26.2" # Updated to support macOS 26 Tahoe liquid design | |
| jobs: | |
| lint: | |
| name: SwiftLint | |
| runs-on: macos-26 # Updated to macOS 26 (Tahoe) runner | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Cache SwiftLint | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/Library/Caches/Homebrew/swiftlint* | |
| key: swiftlint-${{ runner.os }}-${{ hashFiles('.swiftlint.yml') }} | |
| restore-keys: | | |
| swiftlint-${{ runner.os }}- | |
| - name: Install SwiftLint | |
| run: brew install swiftlint | |
| - name: Run SwiftLint | |
| run: swiftlint lint --strict | |
| prepare-libs: | |
| name: Prepare Universal Libraries | |
| runs-on: macos-26 # Updated to macOS 26 (Tahoe) runner | |
| needs: lint | |
| timeout-minutes: 25 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Cache Homebrew downloads | |
| uses: actions/cache@v4 | |
| continue-on-error: true | |
| with: | |
| path: ~/Library/Caches/Homebrew | |
| key: brew-downloads-${{ runner.os }}-${{ hashFiles('**/scripts/build-release.sh') }} | |
| restore-keys: | | |
| brew-downloads-${{ runner.os }}- | |
| - name: Install ARM64 Homebrew dependencies | |
| run: | | |
| echo "Installing ARM64 dependencies..." | |
| # Check and install only if needed | |
| if ! brew list mariadb-connector-c &>/dev/null; then | |
| echo "📦 Installing mariadb-connector-c..." | |
| brew install mariadb-connector-c | |
| else | |
| echo "✅ mariadb-connector-c already installed" | |
| fi | |
| if ! brew list libpq &>/dev/null; then | |
| echo "📦 Installing libpq..." | |
| brew install libpq | |
| else | |
| echo "✅ libpq already installed" | |
| fi | |
| # Link packages with --force (needed for keg-only formulas) | |
| brew link --force mariadb-connector-c | |
| brew link --force libpq | |
| # Verify installations | |
| if ! brew list mariadb-connector-c >/dev/null 2>&1; then | |
| echo "❌ ERROR: mariadb-connector-c installation failed or incomplete" | |
| exit 1 | |
| fi | |
| if ! brew list libpq >/dev/null 2>&1; then | |
| echo "❌ ERROR: libpq installation failed or incomplete" | |
| exit 1 | |
| fi | |
| # Get the actual prefix for mariadb-connector-c (handles keg-only formulas) | |
| MARIADB_PREFIX=$(brew --prefix mariadb-connector-c) | |
| echo "MariaDB Connector/C prefix: $MARIADB_PREFIX" | |
| # Find the library file (may be in lib/ or lib/mariadb/) | |
| MARIADB_LIB="" | |
| for path in "$MARIADB_PREFIX/lib/mariadb/libmariadb.a" "$MARIADB_PREFIX/lib/libmariadb.a"; do | |
| if [ -f "$path" ]; then | |
| MARIADB_LIB="$path" | |
| break | |
| fi | |
| done | |
| if [ -z "$MARIADB_LIB" ]; then | |
| echo "❌ ERROR: mariadb-connector-c installed but library file not found" | |
| echo "Searched in:" | |
| echo " - $MARIADB_PREFIX/lib/mariadb/libmariadb.a" | |
| echo " - $MARIADB_PREFIX/lib/libmariadb.a" | |
| echo "" | |
| echo "Contents of $MARIADB_PREFIX/lib/:" | |
| ls -la "$MARIADB_PREFIX/lib/" || echo "Directory not found" | |
| exit 1 | |
| fi | |
| echo "✅ Found ARM64 MariaDB library at: $MARIADB_LIB" | |
| echo "ARM64_MARIADB_LIB=$MARIADB_LIB" >> $GITHUB_ENV | |
| echo "✅ All ARM64 dependencies installed successfully" | |
| - name: Install x86_64 Homebrew | |
| run: | | |
| # Install Rosetta 2 if needed | |
| if ! arch -x86_64 /usr/bin/true 2>/dev/null; then | |
| echo "Installing Rosetta 2..." | |
| if ! softwareupdate --install-rosetta --agree-to-license; then | |
| echo "❌ ERROR: Failed to install Rosetta 2" | |
| echo "This is required to run x86_64 binaries on Apple Silicon" | |
| exit 1 | |
| fi | |
| # Verify Rosetta 2 is now working | |
| if ! arch -x86_64 /usr/bin/true 2>/dev/null; then | |
| echo "❌ ERROR: Rosetta 2 installed but not functional" | |
| exit 1 | |
| fi | |
| echo "✅ Rosetta 2 installed successfully" | |
| else | |
| echo "✅ Rosetta 2 already installed" | |
| fi | |
| # Check if x86_64 Homebrew is already installed | |
| if [ ! -f /usr/local/bin/brew ]; then | |
| echo "Installing x86_64 Homebrew..." | |
| # Using official Homebrew installer (trusted source) | |
| if ! arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"; then | |
| echo "❌ ERROR: Homebrew installation script failed" | |
| exit 1 | |
| fi | |
| # Verify installation | |
| if [ ! -f /usr/local/bin/brew ]; then | |
| echo "❌ ERROR: Homebrew installed but /usr/local/bin/brew not found" | |
| exit 1 | |
| fi | |
| # Verify it's executable | |
| if ! /usr/local/bin/brew --version; then | |
| echo "❌ ERROR: Homebrew binary is not functional" | |
| exit 1 | |
| fi | |
| echo "✅ x86_64 Homebrew installed successfully" | |
| else | |
| echo "x86_64 Homebrew already installed" | |
| # Still verify it works | |
| if ! /usr/local/bin/brew --version; then | |
| echo "❌ ERROR: x86_64 Homebrew exists but is not functional" | |
| exit 1 | |
| fi | |
| fi | |
| - name: Install x86_64 Homebrew dependencies | |
| run: | | |
| echo "Installing x86_64 dependencies..." | |
| # Check and install only if needed | |
| if ! arch -x86_64 /usr/local/bin/brew list mariadb-connector-c &>/dev/null; then | |
| echo "📦 Installing mariadb-connector-c (x86_64)..." | |
| arch -x86_64 /usr/local/bin/brew install mariadb-connector-c | |
| else | |
| echo "✅ mariadb-connector-c (x86_64) already installed" | |
| fi | |
| if ! arch -x86_64 /usr/local/bin/brew list libpq &>/dev/null; then | |
| echo "📦 Installing libpq (x86_64)..." | |
| arch -x86_64 /usr/local/bin/brew install libpq | |
| else | |
| echo "✅ libpq (x86_64) already installed" | |
| fi | |
| # Link packages with --force (needed for keg-only formulas) | |
| arch -x86_64 /usr/local/bin/brew link --force mariadb-connector-c | |
| arch -x86_64 /usr/local/bin/brew link --force libpq | |
| # Verify installations | |
| if ! arch -x86_64 /usr/local/bin/brew list mariadb-connector-c >/dev/null 2>&1; then | |
| echo "❌ ERROR: mariadb-connector-c installation failed or incomplete" | |
| exit 1 | |
| fi | |
| if ! arch -x86_64 /usr/local/bin/brew list libpq >/dev/null 2>&1; then | |
| echo "❌ ERROR: libpq installation failed or incomplete" | |
| exit 1 | |
| fi | |
| # Get the actual prefix for mariadb-connector-c (handles keg-only formulas) | |
| X86_MARIADB_PREFIX=$(arch -x86_64 /usr/local/bin/brew --prefix mariadb-connector-c) | |
| echo "x86_64 MariaDB Connector/C prefix: $X86_MARIADB_PREFIX" | |
| # Find the library file (may be in lib/ or lib/mariadb/) | |
| X86_MARIADB_LIB="" | |
| for path in "$X86_MARIADB_PREFIX/lib/mariadb/libmariadb.a" "$X86_MARIADB_PREFIX/lib/libmariadb.a"; do | |
| if [ -f "$path" ]; then | |
| X86_MARIADB_LIB="$path" | |
| break | |
| fi | |
| done | |
| if [ -z "$X86_MARIADB_LIB" ]; then | |
| echo "❌ ERROR: mariadb-connector-c installed but library file not found" | |
| echo "Searched in:" | |
| echo " - $X86_MARIADB_PREFIX/lib/mariadb/libmariadb.a" | |
| echo " - $X86_MARIADB_PREFIX/lib/libmariadb.a" | |
| echo "" | |
| echo "Contents of $X86_MARIADB_PREFIX/lib/:" | |
| ls -la "$X86_MARIADB_PREFIX/lib/" || echo "Directory not found" | |
| exit 1 | |
| fi | |
| echo "✅ Found x86_64 MariaDB library at: $X86_MARIADB_LIB" | |
| echo "X86_MARIADB_LIB=$X86_MARIADB_LIB" >> $GITHUB_ENV | |
| echo "✅ All x86_64 dependencies installed successfully" | |
| - name: Create universal libmariadb library | |
| run: | | |
| echo "Creating universal libmariadb.a..." | |
| # Use library paths from environment (set by previous steps) | |
| ARM64_LIB="${ARM64_MARIADB_LIB}" | |
| X86_LIB="${X86_MARIADB_LIB}" | |
| echo "ARM64 library path: $ARM64_LIB" | |
| echo "x86_64 library path: $X86_LIB" | |
| # Verify input files exist | |
| if [ -z "$ARM64_LIB" ] || [ ! -f "$ARM64_LIB" ]; then | |
| echo "❌ ERROR: ARM64 library not found at: $ARM64_LIB" | |
| echo "ARM64 Homebrew installation may have failed" | |
| exit 1 | |
| fi | |
| if [ -z "$X86_LIB" ] || [ ! -f "$X86_LIB" ]; then | |
| echo "❌ ERROR: x86_64 library not found at: $X86_LIB" | |
| echo "x86_64 Homebrew installation may have failed" | |
| exit 1 | |
| fi | |
| # Verify inputs are correct architectures | |
| echo "Verifying input architectures:" | |
| lipo -info "$ARM64_LIB" | |
| lipo -info "$X86_LIB" | |
| # Ensure output directory exists | |
| mkdir -p Libs | |
| # Create universal binary | |
| if ! lipo -create "$ARM64_LIB" "$X86_LIB" -output Libs/libmariadb_universal.a; then | |
| echo "❌ ERROR: Failed to create universal library" | |
| echo "This could mean:" | |
| echo " - Libraries are incompatible" | |
| echo " - Libraries are already universal" | |
| echo " - Output directory is not writable" | |
| exit 1 | |
| fi | |
| # Verify output | |
| if [ ! -f "Libs/libmariadb_universal.a" ]; then | |
| echo "❌ ERROR: Universal library was not created" | |
| exit 1 | |
| fi | |
| echo "✅ Universal library created successfully:" | |
| lipo -info Libs/libmariadb_universal.a | |
| ls -lh Libs/libmariadb_universal.a | |
| # Verify it's actually universal (contains both architectures) | |
| if ! lipo -info Libs/libmariadb_universal.a | grep -q "arm64"; then | |
| echo "❌ ERROR: Universal library missing arm64 architecture" | |
| exit 1 | |
| fi | |
| if ! lipo -info Libs/libmariadb_universal.a | grep -q "x86_64"; then | |
| echo "❌ ERROR: Universal library missing x86_64 architecture" | |
| exit 1 | |
| fi | |
| echo "✅ Verified: Library contains both arm64 and x86_64" | |
| - name: Upload library artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: libs-universal | |
| path: Libs/ | |
| retention-days: 1 | |
| build-arm64: | |
| name: Build ARM64 | |
| runs-on: macos-26 # Updated to macOS 26 (Tahoe) runner | |
| needs: prepare-libs | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download library artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: libs-universal | |
| path: Libs/ | |
| - name: Verify downloaded artifacts | |
| run: | | |
| echo "Verifying downloaded library artifacts..." | |
| if [ ! -f "Libs/libmariadb_universal.a" ]; then | |
| echo "❌ ERROR: Required library artifact not found: Libs/libmariadb_universal.a" | |
| echo "This means:" | |
| echo " - The prepare-libs job failed to create the library" | |
| echo " - The artifact upload failed" | |
| echo " - The artifact download was corrupted" | |
| echo "" | |
| echo "Contents of Libs directory:" | |
| ls -la Libs/ || echo "Libs directory is empty or doesn't exist" | |
| exit 1 | |
| fi | |
| echo "✅ Universal library found" | |
| lipo -info Libs/libmariadb_universal.a | |
| ls -lh Libs/libmariadb_universal.a | |
| - name: Cache Homebrew downloads | |
| uses: actions/cache@v4 | |
| continue-on-error: true | |
| with: | |
| path: ~/Library/Caches/Homebrew | |
| key: brew-arm64-downloads-${{ runner.os }}-${{ hashFiles('**/scripts/build-release.sh') }} | |
| restore-keys: | | |
| brew-arm64-downloads-${{ runner.os }}- | |
| - name: Install ARM64 dependencies | |
| run: | | |
| echo "Installing ARM64 dependencies..." | |
| # Check and install only if needed | |
| if ! brew list mariadb-connector-c &>/dev/null; then | |
| echo "📦 Installing mariadb-connector-c..." | |
| brew install mariadb-connector-c | |
| else | |
| echo "✅ mariadb-connector-c already installed" | |
| fi | |
| if ! brew list libpq &>/dev/null; then | |
| echo "📦 Installing libpq..." | |
| brew install libpq | |
| else | |
| echo "✅ libpq already installed" | |
| fi | |
| # Link packages with --force (needed for keg-only formulas) | |
| brew link --force mariadb-connector-c | |
| brew link --force libpq | |
| # Verify installations | |
| if ! brew list mariadb-connector-c >/dev/null 2>&1; then | |
| echo "❌ ERROR: mariadb-connector-c installation failed" | |
| exit 1 | |
| fi | |
| if ! brew list libpq >/dev/null 2>&1; then | |
| echo "❌ ERROR: libpq installation failed" | |
| exit 1 | |
| fi | |
| echo "✅ ARM64 dependencies installed" | |
| - name: Select Xcode version | |
| run: | | |
| sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer | |
| echo "Selected Xcode version:" | |
| xcodebuild -version | |
| - name: Build ARM64 | |
| run: | | |
| chmod +x scripts/build-release.sh | |
| scripts/build-release.sh arm64 | |
| - name: Verify build | |
| run: | | |
| echo "Verifying build output..." | |
| BINARY_PATH="build/Release/TablePro-arm64.app/Contents/MacOS/TablePro" | |
| # Check binary exists | |
| if [ ! -f "$BINARY_PATH" ]; then | |
| echo "❌ ERROR: Built binary not found at: $BINARY_PATH" | |
| echo "Build may have failed silently" | |
| exit 1 | |
| fi | |
| # Check it's not empty | |
| if [ ! -s "$BINARY_PATH" ]; then | |
| echo "❌ ERROR: Binary file is empty" | |
| exit 1 | |
| fi | |
| # Check architecture | |
| ARCH_INFO=$(lipo -info "$BINARY_PATH") | |
| echo "Architecture: $ARCH_INFO" | |
| if ! echo "$ARCH_INFO" | grep -q "arm64"; then | |
| echo "❌ ERROR: Binary does not contain arm64 architecture" | |
| echo "Expected: arm64 only" | |
| echo "Got: $ARCH_INFO" | |
| exit 1 | |
| fi | |
| if echo "$ARCH_INFO" | grep -q "x86_64"; then | |
| echo "❌ ERROR: Binary contains x86_64 but should be arm64 only" | |
| exit 1 | |
| fi | |
| # Check it's executable | |
| if [ ! -x "$BINARY_PATH" ]; then | |
| echo "❌ ERROR: Binary is not executable" | |
| exit 1 | |
| fi | |
| # Display info | |
| echo "✅ Build verified successfully" | |
| echo "Binary size: $(ls -lh "$BINARY_PATH" | awk '{print $5}')" | |
| echo "App bundle size: $(du -sh build/Release/TablePro-arm64.app | awk '{print $1}')" | |
| - name: Create DMG installer | |
| run: | | |
| echo "Creating DMG installer..." | |
| # Install create-dmg tool for proper icon handling in CI | |
| echo "📦 Installing create-dmg tool..." | |
| brew install create-dmg | |
| # Make DMG creation script executable | |
| chmod +x scripts/create-dmg.sh | |
| # Create DMG with version from git tag or use default | |
| # The script handles app renaming internally | |
| VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.1.13") | |
| echo "📌 Using version: $VERSION" | |
| scripts/create-dmg.sh "$VERSION" "arm64" "build/Release/TablePro-arm64.app" | |
| # Verify DMG was created - check for the specific file or any arm64 DMG | |
| DMG_FILE="build/Release/TablePro-${VERSION}-arm64.dmg" | |
| if [ -f "$DMG_FILE" ]; then | |
| echo "✅ DMG installer created successfully: $DMG_FILE" | |
| else | |
| echo "⚠️ Expected DMG not found at: $DMG_FILE" | |
| echo "📂 Checking for any DMG files in build/Release/:" | |
| ls -la build/Release/*.dmg 2>/dev/null || echo " No DMG files found" | |
| # Check if any arm64 DMG was created (version might differ) | |
| if ls build/Release/*-arm64.dmg 1>/dev/null 2>&1; then | |
| echo "✅ Found arm64 DMG file(s):" | |
| ls -lh build/Release/*-arm64.dmg | |
| else | |
| echo "❌ ERROR: No arm64 DMG file was created" | |
| exit 1 | |
| fi | |
| fi | |
| ls -lh build/Release/*.dmg | |
| - name: Create ZIP archive (fallback) | |
| run: | | |
| echo "Creating ZIP archive..." | |
| cd build/Release | |
| if ! zip -r TablePro-arm64.zip TablePro-arm64.app; then | |
| echo "❌ ERROR: Failed to create ZIP archive" | |
| exit 1 | |
| fi | |
| echo "✅ ZIP archive created" | |
| ls -lh TablePro-arm64.zip | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: TablePro-arm64-${{ github.sha }} | |
| path: | | |
| build/Release/*.dmg | |
| build/Release/TablePro-arm64.zip | |
| retention-days: ${{ startsWith(github.ref, 'refs/tags/v') && 90 || 7 }} | |
| build-x86_64: | |
| name: Build x86_64 | |
| runs-on: macos-26 # Updated to macOS 26 (Tahoe) runner | |
| needs: prepare-libs | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download library artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: libs-universal | |
| path: Libs/ | |
| - name: Verify downloaded artifacts | |
| run: | | |
| echo "Verifying downloaded library artifacts..." | |
| if [ ! -f "Libs/libmariadb_universal.a" ]; then | |
| echo "❌ ERROR: Required library artifact not found: Libs/libmariadb_universal.a" | |
| echo "" | |
| echo "Contents of Libs directory:" | |
| ls -la Libs/ || echo "Libs directory is empty or doesn't exist" | |
| exit 1 | |
| fi | |
| echo "✅ Universal library found" | |
| lipo -info Libs/libmariadb_universal.a | |
| - name: Cache Homebrew downloads | |
| uses: actions/cache@v4 | |
| continue-on-error: true | |
| with: | |
| path: ~/Library/Caches/Homebrew | |
| key: brew-x86_64-downloads-${{ runner.os }}-${{ hashFiles('**/scripts/build-release.sh') }} | |
| restore-keys: | | |
| brew-x86_64-downloads-${{ runner.os }}- | |
| - name: Install Rosetta 2 | |
| run: | | |
| if ! arch -x86_64 /usr/bin/true 2>/dev/null; then | |
| echo "Installing Rosetta 2..." | |
| if ! softwareupdate --install-rosetta --agree-to-license; then | |
| echo "❌ ERROR: Failed to install Rosetta 2" | |
| exit 1 | |
| fi | |
| # Verify Rosetta 2 works | |
| if ! arch -x86_64 /usr/bin/true 2>/dev/null; then | |
| echo "❌ ERROR: Rosetta 2 installed but not functional" | |
| exit 1 | |
| fi | |
| echo "✅ Rosetta 2 installed" | |
| else | |
| echo "✅ Rosetta 2 already installed" | |
| fi | |
| - name: Install x86_64 Homebrew | |
| run: | | |
| if [ ! -f /usr/local/bin/brew ]; then | |
| echo "Installing x86_64 Homebrew..." | |
| if ! arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"; then | |
| echo "❌ ERROR: Homebrew installation failed" | |
| exit 1 | |
| fi | |
| if [ ! -f /usr/local/bin/brew ]; then | |
| echo "❌ ERROR: Homebrew not found after installation" | |
| exit 1 | |
| fi | |
| if ! /usr/local/bin/brew --version; then | |
| echo "❌ ERROR: Homebrew not functional" | |
| exit 1 | |
| fi | |
| echo "✅ x86_64 Homebrew installed" | |
| else | |
| echo "x86_64 Homebrew already installed" | |
| if ! /usr/local/bin/brew --version; then | |
| echo "❌ ERROR: Homebrew not functional" | |
| exit 1 | |
| fi | |
| fi | |
| - name: Install x86_64 dependencies | |
| run: | | |
| echo "Installing x86_64 dependencies..." | |
| # Check and install only if needed | |
| if ! arch -x86_64 /usr/local/bin/brew list mariadb-connector-c &>/dev/null; then | |
| echo "📦 Installing mariadb-connector-c (x86_64)..." | |
| arch -x86_64 /usr/local/bin/brew install mariadb-connector-c | |
| else | |
| echo "✅ mariadb-connector-c (x86_64) already installed" | |
| fi | |
| if ! arch -x86_64 /usr/local/bin/brew list libpq &>/dev/null; then | |
| echo "📦 Installing libpq (x86_64)..." | |
| arch -x86_64 /usr/local/bin/brew install libpq | |
| else | |
| echo "✅ libpq (x86_64) already installed" | |
| fi | |
| # Link packages with --force (needed for keg-only formulas) | |
| arch -x86_64 /usr/local/bin/brew link --force mariadb-connector-c | |
| arch -x86_64 /usr/local/bin/brew link --force libpq | |
| # Verify installations | |
| if ! arch -x86_64 /usr/local/bin/brew list mariadb-connector-c >/dev/null 2>&1; then | |
| echo "❌ ERROR: mariadb-connector-c installation failed" | |
| exit 1 | |
| fi | |
| if ! arch -x86_64 /usr/local/bin/brew list libpq >/dev/null 2>&1; then | |
| echo "❌ ERROR: libpq installation failed" | |
| exit 1 | |
| fi | |
| echo "✅ x86_64 dependencies installed" | |
| - name: Select Xcode version | |
| run: | | |
| sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer | |
| echo "Selected Xcode version:" | |
| xcodebuild -version | |
| - name: Build x86_64 | |
| run: | | |
| chmod +x scripts/build-release.sh | |
| scripts/build-release.sh x86_64 | |
| - name: Verify build | |
| run: | | |
| echo "Verifying build output..." | |
| BINARY_PATH="build/Release/TablePro-x86_64.app/Contents/MacOS/TablePro" | |
| # Check binary exists | |
| if [ ! -f "$BINARY_PATH" ]; then | |
| echo "❌ ERROR: Built binary not found at: $BINARY_PATH" | |
| exit 1 | |
| fi | |
| # Check it's not empty | |
| if [ ! -s "$BINARY_PATH" ]; then | |
| echo "❌ ERROR: Binary file is empty" | |
| exit 1 | |
| fi | |
| # Check architecture | |
| ARCH_INFO=$(lipo -info "$BINARY_PATH") | |
| echo "Architecture: $ARCH_INFO" | |
| if ! echo "$ARCH_INFO" | grep -q "x86_64"; then | |
| echo "❌ ERROR: Binary does not contain x86_64 architecture" | |
| echo "Expected: x86_64 only" | |
| echo "Got: $ARCH_INFO" | |
| exit 1 | |
| fi | |
| if echo "$ARCH_INFO" | grep -q "arm64"; then | |
| echo "❌ ERROR: Binary contains arm64 but should be x86_64 only" | |
| exit 1 | |
| fi | |
| # Check it's executable | |
| if [ ! -x "$BINARY_PATH" ]; then | |
| echo "❌ ERROR: Binary is not executable" | |
| exit 1 | |
| fi | |
| # Display info | |
| echo "✅ Build verified successfully" | |
| echo "Binary size: $(ls -lh "$BINARY_PATH" | awk '{print $5}')" | |
| echo "App bundle size: $(du -sh build/Release/TablePro-x86_64.app | awk '{print $1}')" | |
| - name: Create DMG installer | |
| run: | | |
| echo "Creating DMG installer..." | |
| # Install create-dmg tool for proper icon handling in CI | |
| echo "📦 Installing create-dmg tool..." | |
| brew install create-dmg | |
| # Make DMG creation script executable | |
| chmod +x scripts/create-dmg.sh | |
| # Create DMG with version from git tag or use default | |
| # The script handles app renaming internally | |
| VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.1.13") | |
| echo "📌 Using version: $VERSION" | |
| scripts/create-dmg.sh "$VERSION" "x86_64" "build/Release/TablePro-x86_64.app" | |
| # Verify DMG was created - check for the specific file or any x86_64 DMG | |
| DMG_FILE="build/Release/TablePro-${VERSION}-x86_64.dmg" | |
| if [ -f "$DMG_FILE" ]; then | |
| echo "✅ DMG installer created successfully: $DMG_FILE" | |
| else | |
| echo "⚠️ Expected DMG not found at: $DMG_FILE" | |
| echo "📂 Checking for any DMG files in build/Release/:" | |
| ls -la build/Release/*.dmg 2>/dev/null || echo " No DMG files found" | |
| # Check if any x86_64 DMG was created (version might differ) | |
| if ls build/Release/*-x86_64.dmg 1>/dev/null 2>&1; then | |
| echo "✅ Found x86_64 DMG file(s):" | |
| ls -lh build/Release/*-x86_64.dmg | |
| else | |
| echo "❌ ERROR: No x86_64 DMG file was created" | |
| exit 1 | |
| fi | |
| fi | |
| ls -lh build/Release/*.dmg | |
| - name: Create ZIP archive (fallback) | |
| run: | | |
| echo "Creating ZIP archive..." | |
| cd build/Release | |
| if ! zip -r TablePro-x86_64.zip TablePro-x86_64.app; then | |
| echo "❌ ERROR: Failed to create ZIP archive" | |
| exit 1 | |
| fi | |
| echo "✅ ZIP archive created" | |
| ls -lh TablePro-x86_64.zip | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: TablePro-x86_64-${{ github.sha }} | |
| path: | | |
| build/Release/*.dmg | |
| build/Release/TablePro-x86_64.zip | |
| retention-days: ${{ startsWith(github.ref, 'refs/tags/v') && 90 || 7 }} | |
| release: | |
| name: Create GitHub Release | |
| runs-on: macos-26 # Updated to macOS 26 (Tahoe) runner | |
| needs: [build-arm64, build-x86_64] | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download ARM64 artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: TablePro-arm64-${{ github.sha }} | |
| path: artifacts/ | |
| - name: Download x86_64 artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: TablePro-x86_64-${{ github.sha }} | |
| path: artifacts/ | |
| - name: Verify and organize artifacts for release | |
| run: | | |
| VERSION=${GITHUB_REF#refs/tags/v} | |
| if [ -z "$VERSION" ]; then | |
| echo "❌ ERROR: Failed to extract version from ref: $GITHUB_REF" | |
| exit 1 | |
| fi | |
| echo "Preparing artifacts for version: $VERSION" | |
| echo "Contents of artifacts directory:" | |
| ls -la artifacts/ | |
| # Note: DMG files should already have correct names from build | |
| # ZIP files need to be renamed | |
| # Rename ZIP files if they exist | |
| if [ -f "artifacts/TablePro-arm64.zip" ]; then | |
| mv artifacts/TablePro-arm64.zip "artifacts/TablePro-${VERSION}-arm64.zip" | |
| fi | |
| if [ -f "artifacts/TablePro-x86_64.zip" ]; then | |
| mv artifacts/TablePro-x86_64.zip "artifacts/TablePro-${VERSION}-x86_64.zip" | |
| fi | |
| echo "✅ Artifacts organized successfully" | |
| echo "Final artifacts:" | |
| ls -lh artifacts/ | |
| - name: Generate release notes | |
| id: release_notes | |
| run: | | |
| VERSION=${GITHUB_REF#refs/tags/} | |
| # Try to get previous tag | |
| PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "") | |
| if [ -z "$PREVIOUS_TAG" ]; then | |
| echo "No previous tag found, showing recent commits" | |
| COMMIT_RANGE="HEAD~10..HEAD" | |
| else | |
| echo "Generating changes since $PREVIOUS_TAG" | |
| COMMIT_RANGE="$PREVIOUS_TAG..HEAD" | |
| fi | |
| # Extract raw commits | |
| RAW_COMMITS=$(git log --pretty=format:"%s" "$COMMIT_RANGE" 2>/dev/null || echo "") | |
| # Filter noise commits | |
| FILTERED_COMMITS=$(echo "$RAW_COMMITS" | awk ' | |
| tolower($0) ~ /^wip$/ { next } | |
| tolower($0) ~ /^wip:/ { next } | |
| /^Merge pull request/ { next } | |
| /^Merge branch/ { next } | |
| /^Bump [0-9]/ { next } | |
| tolower($0) ~ /^bump version/ { next } | |
| tolower($0) ~ /^bump [0-9]/ { next } | |
| /^\[.*[Bb]ot\]/ { next } | |
| /^$/ { next } | |
| { print } | |
| ') | |
| # Categorize commits by conventional commit type | |
| FEAT_COMMITS=$(echo "$FILTERED_COMMITS" | grep -i "^feat:" | sed 's/^feat: *//' || true) | |
| FIX_COMMITS=$(echo "$FILTERED_COMMITS" | grep -i "^fix:" | sed 's/^fix: *//' || true) | |
| PERF_COMMITS=$(echo "$FILTERED_COMMITS" | grep -i "^perf:" | sed 's/^perf: *//' || true) | |
| DOCS_COMMITS=$(echo "$FILTERED_COMMITS" | grep -i "^docs:" | sed 's/^docs: *//' || true) | |
| REFACTOR_COMMITS=$(echo "$FILTERED_COMMITS" | grep -i "^refactor:" | sed 's/^refactor: *//' || true) | |
| CHORE_COMMITS=$(echo "$FILTERED_COMMITS" | grep -iE "^(chore|style|test):" | sed 's/^[^:]*: *//' || true) | |
| OTHER_COMMITS=$(echo "$FILTERED_COMMITS" | grep -ivE "^(feat|fix|perf|docs|refactor|chore|style|test):" || true) | |
| # Build categorized output | |
| CHANGES="" | |
| if [ -n "$FEAT_COMMITS" ]; then | |
| CHANGES="${CHANGES}### New Features"$'\n\n' | |
| CHANGES="${CHANGES}$(echo "$FEAT_COMMITS" | sed 's/^/- /')"$'\n\n' | |
| fi | |
| if [ -n "$FIX_COMMITS" ]; then | |
| CHANGES="${CHANGES}### Bug Fixes"$'\n\n' | |
| CHANGES="${CHANGES}$(echo "$FIX_COMMITS" | sed 's/^/- /')"$'\n\n' | |
| fi | |
| if [ -n "$PERF_COMMITS" ]; then | |
| CHANGES="${CHANGES}### Performance Improvements"$'\n\n' | |
| CHANGES="${CHANGES}$(echo "$PERF_COMMITS" | sed 's/^/- /')"$'\n\n' | |
| fi | |
| if [ -n "$DOCS_COMMITS" ]; then | |
| CHANGES="${CHANGES}### Documentation"$'\n\n' | |
| CHANGES="${CHANGES}$(echo "$DOCS_COMMITS" | sed 's/^/- /')"$'\n\n' | |
| fi | |
| if [ -n "$REFACTOR_COMMITS" ]; then | |
| CHANGES="${CHANGES}### Refactoring"$'\n\n' | |
| CHANGES="${CHANGES}$(echo "$REFACTOR_COMMITS" | sed 's/^/- /')"$'\n\n' | |
| fi | |
| if [ -n "$CHORE_COMMITS" ]; then | |
| CHANGES="${CHANGES}### Maintenance"$'\n\n' | |
| CHANGES="${CHANGES}$(echo "$CHORE_COMMITS" | sed 's/^/- /')"$'\n\n' | |
| fi | |
| if [ -n "$OTHER_COMMITS" ]; then | |
| CHANGES="${CHANGES}### Other Changes"$'\n\n' | |
| CHANGES="${CHANGES}$(echo "$OTHER_COMMITS" | sed 's/^/- /')"$'\n\n' | |
| fi | |
| # Fallback if all commits were filtered out | |
| if [ -z "$CHANGES" ]; then | |
| CHANGES="### Changes"$'\n\n'"- Minor improvements and bug fixes"$'\n\n' | |
| fi | |
| cat > release_notes.md <<EOF | |
| $CHANGES | |
| EOF | |
| echo "✅ Release notes generated" | |
| cat release_notes.md | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| files: | | |
| artifacts/*.dmg | |
| artifacts/*.zip | |
| body_path: release_notes.md | |
| draft: false | |
| prerelease: ${{ contains(github.ref, '-beta') || contains(github.ref, '-alpha') || contains(github.ref, '-rc') }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |