Build and Sign DMG #6
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 and Sign DMG | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| branches: | |
| - 'copilot/fix-1' # Enable testing on current working branch | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| create_release: | |
| description: 'Create a GitHub release' | |
| required: false | |
| default: false | |
| type: boolean | |
| env: | |
| APP_NAME: TrackWeight | |
| SCHEME: TrackWeight | |
| CONFIGURATION: Release | |
| jobs: | |
| build-and-sign: | |
| runs-on: macos-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Setup Xcode | |
| uses: maxim-lobanov/setup-xcode@v1 | |
| with: | |
| xcode-version: latest-stable | |
| - name: Import Code-Signing Certificates | |
| if: ${{ env.BUILD_CERTIFICATE_BASE64 != '' }} | |
| env: | |
| BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} | |
| uses: Apple-Actions/import-codesign-certs@v2 | |
| with: | |
| p12-file-base64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} | |
| p12-password: ${{ secrets.P12_PASSWORD }} | |
| - name: Install provisioning profile | |
| if: ${{ env.BUILD_PROVISION_PROFILE_BASE64 != '' }} | |
| env: | |
| BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }} | |
| run: | | |
| PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision | |
| echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH | |
| mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles | |
| cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles | |
| - name: Build and Archive App | |
| run: | | |
| xcodebuild \ | |
| -project TrackWeight.xcodeproj \ | |
| -scheme ${{ env.SCHEME }} \ | |
| -configuration ${{ env.CONFIGURATION }} \ | |
| -archivePath "$RUNNER_TEMP/${{ env.APP_NAME }}.xcarchive" \ | |
| -destination 'generic/platform=macOS' \ | |
| ARCHS="arm64 x86_64" \ | |
| ONLY_ACTIVE_ARCH=NO \ | |
| archive | |
| - name: Export App | |
| run: | | |
| # Use development export method if no signing certificates are available | |
| if [ -z "${{ secrets.BUILD_CERTIFICATE_BASE64 }}" ]; then | |
| # Create export options for unsigned build | |
| cat > "$RUNNER_TEMP/ExportOptions.plist" << EOF | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| <plist version="1.0"> | |
| <dict> | |
| <key>method</key> | |
| <string>debugging</string> | |
| <key>signingStyle</key> | |
| <string>manual</string> | |
| <key>stripSwiftSymbols</key> | |
| <true/> | |
| <key>destination</key> | |
| <string>export</string> | |
| <key>signingCertificate</key> | |
| <string>-</string> | |
| <key>teamID</key> | |
| <string>-</string> | |
| <key>uploadBitcode</key> | |
| <false/> | |
| <key>uploadSymbols</key> | |
| <false/> | |
| </dict> | |
| </plist> | |
| EOF | |
| else | |
| # Use the existing ExportOptions.plist for signed builds | |
| cp ExportOptions.plist "$RUNNER_TEMP/ExportOptions.plist" | |
| fi | |
| xcodebuild \ | |
| -archivePath "$RUNNER_TEMP/${{ env.APP_NAME }}.xcarchive" \ | |
| -exportPath "$RUNNER_TEMP/export" \ | |
| -exportOptionsPlist "$RUNNER_TEMP/ExportOptions.plist" \ | |
| -exportArchive | |
| - name: Re-sign App Components | |
| if: ${{ env.BUILD_CERTIFICATE_BASE64 != '' }} | |
| env: | |
| BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| run: | | |
| echo "π Re-signing framework with Developer ID certificate..." | |
| FRAMEWORK_PATH="$RUNNER_TEMP/export/${{ env.APP_NAME }}.app/Contents/Frameworks/OpenMultitouchSupportXCF.framework" | |
| if [[ -d "$FRAMEWORK_PATH" ]]; then | |
| codesign --force --sign "Developer ID Application" \ | |
| --options runtime \ | |
| --timestamp \ | |
| "$FRAMEWORK_PATH/Versions/A/OpenMultitouchSupportXCF" | |
| codesign --force --sign "Developer ID Application" \ | |
| --options runtime \ | |
| --timestamp \ | |
| "$FRAMEWORK_PATH" | |
| echo "β Framework re-signed successfully" | |
| fi | |
| # Re-sign the main app to ensure everything is consistent | |
| echo "π Re-signing main application..." | |
| codesign --force --sign "Developer ID Application" \ | |
| --options runtime \ | |
| --entitlements "TrackWeight/TrackWeight.entitlements" \ | |
| --timestamp \ | |
| --deep \ | |
| --strict \ | |
| "$RUNNER_TEMP/export/${{ env.APP_NAME }}.app" | |
| echo "β Application re-signed successfully" | |
| - name: Verify Universal Binary and Code Signatures | |
| run: | | |
| echo "ποΈ Verifying Universal Binary Architecture..." | |
| APP_BINARY="$RUNNER_TEMP/export/${{ env.APP_NAME }}.app/Contents/MacOS/${{ env.APP_NAME }}" | |
| if [[ -f "$APP_BINARY" ]]; then | |
| echo "π Binary architectures:" | |
| lipo -archs "$APP_BINARY" | |
| if lipo -archs "$APP_BINARY" | grep -q "arm64" && lipo -archs "$APP_BINARY" | grep -q "x86_64"; then | |
| echo "β Universal binary confirmed: Contains both ARM64 and x86_64" | |
| else | |
| echo "β Warning: Binary may not be universal" | |
| lipo -detailed_info "$APP_BINARY" | |
| fi | |
| fi | |
| # Check framework architecture if it exists | |
| FRAMEWORK_PATH="$RUNNER_TEMP/export/${{ env.APP_NAME }}.app/Contents/Frameworks/OpenMultitouchSupportXCF.framework" | |
| if [[ -d "$FRAMEWORK_PATH" ]]; then | |
| FRAMEWORK_BINARY="$FRAMEWORK_PATH/Versions/A/OpenMultitouchSupportXCF" | |
| if [[ -f "$FRAMEWORK_BINARY" ]]; then | |
| echo "π Framework architectures:" | |
| lipo -archs "$FRAMEWORK_BINARY" | |
| fi | |
| fi | |
| # Only verify signatures if certificates are available | |
| if [[ -n "${{ secrets.BUILD_CERTIFICATE_BASE64 }}" ]]; then | |
| echo "π Verifying main application signature..." | |
| codesign --verify --verbose "$RUNNER_TEMP/export/${{ env.APP_NAME }}.app" | |
| echo "π Verifying framework signature..." | |
| if [[ -d "$FRAMEWORK_PATH" ]]; then | |
| codesign --verify --verbose "$FRAMEWORK_PATH" | |
| fi | |
| echo "π Deep verification with online validation..." | |
| codesign --verify --deep --strict --verbose=2 "$RUNNER_TEMP/export/${{ env.APP_NAME }}.app" | |
| echo "β All signature verifications passed" | |
| else | |
| echo "βΉοΈ Skipping signature verification (no certificates provided)" | |
| fi | |
| - name: Notarize App | |
| if: ${{ env.BUILD_CERTIFICATE_BASE64 != '' && env.APPLE_ID != '' }} | |
| env: | |
| BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| run: | | |
| echo "π Starting notarization process..." | |
| # Create a zip file for notarization (notarytool prefers zip over raw .app) | |
| cd "$RUNNER_TEMP/export" | |
| # Use ditto for creating zip compatible with Apple's notarization service | |
| ditto -c -k --keepParent "${{ env.APP_NAME }}.app" "${{ env.APP_NAME }}.zip" | |
| # Submit for notarization | |
| echo "π€ Submitting app for notarization..." | |
| xcrun notarytool submit "${{ env.APP_NAME }}.zip" \ | |
| --apple-id "$APPLE_ID" \ | |
| --password "$APPLE_ID_PASSWORD" \ | |
| --team-id "$APPLE_TEAM_ID" \ | |
| --wait \ | |
| --timeout 20m | |
| # Staple the notarization ticket to the app | |
| echo "π Stapling notarization ticket..." | |
| xcrun stapler staple "${{ env.APP_NAME }}.app" | |
| # Verify the stapling worked | |
| echo "β Verifying notarization..." | |
| xcrun stapler validate "${{ env.APP_NAME }}.app" | |
| echo "π Notarization complete!" | |
| - name: Install create-dmg | |
| run: | | |
| brew install create-dmg | |
| - name: Create DMG | |
| run: | | |
| # Create a clean directory with only the app for DMG creation | |
| echo "π Preparing clean DMG contents..." | |
| DMG_STAGING="$RUNNER_TEMP/dmg_staging" | |
| rm -rf "$DMG_STAGING" | |
| mkdir -p "$DMG_STAGING" | |
| # Copy only the app to staging directory | |
| cp -R "$RUNNER_TEMP/export/${{ env.APP_NAME }}.app" "$DMG_STAGING/" | |
| # Create clean professional DMG | |
| create-dmg \ | |
| --volname "${{ env.APP_NAME }}" \ | |
| --volicon "$DMG_STAGING/${{ env.APP_NAME }}.app/Contents/Resources/AppIcon.icns" \ | |
| --window-pos 200 120 \ | |
| --window-size 600 300 \ | |
| --icon-size 100 \ | |
| --icon "${{ env.APP_NAME }}.app" 175 120 \ | |
| --hide-extension "${{ env.APP_NAME }}.app" \ | |
| --app-drop-link 425 120 \ | |
| --hdiutil-quiet \ | |
| "$RUNNER_TEMP/${{ env.APP_NAME }}.dmg" \ | |
| "$DMG_STAGING/" | |
| - name: Get version info | |
| id: version_info | |
| run: | | |
| if [[ "${{ github.ref }}" == refs/tags/* ]]; then | |
| VERSION=${GITHUB_REF#refs/tags/} | |
| else | |
| VERSION=$(date +%Y%m%d-%H%M%S) | |
| fi | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "dmg_name=${{ env.APP_NAME }}.dmg" >> $GITHUB_OUTPUT | |
| - name: Rename DMG with version | |
| run: | | |
| mv "$RUNNER_TEMP/${{ env.APP_NAME }}.dmg" "$RUNNER_TEMP/${{ steps.version_info.outputs.dmg_name }}" | |
| - name: Upload DMG as artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ steps.version_info.outputs.dmg_name }} | |
| path: ${{ runner.temp }}/${{ steps.version_info.outputs.dmg_name }} | |
| retention-days: 30 | |
| - name: Create Release | |
| if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.create_release == 'true') | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| tag_name: ${{ steps.version_info.outputs.version }} | |
| name: ${{ env.APP_NAME }} ${{ steps.version_info.outputs.version }} | |
| body: | | |
| # TrackWeight ${{ steps.version_info.outputs.version }} | |
| Transform your MacBook's trackpad into a precise digital weighing scale! | |
| ## Installation | |
| 1. Download the DMG file below | |
| 2. Open the DMG and drag TrackWeight.app to your Applications folder | |
| 3. Run the app and follow the setup instructions | |
| ## Requirements | |
| - macOS 13.0 or later (Ventura or newer) | |
| - MacBook with Force Touch trackpad (2015 or newer MacBook Pro, 2016 or newer MacBook) | |
| ## Usage | |
| 1. Open the scale | |
| 2. Rest your finger on the trackpad | |
| 3. While maintaining finger contact, put your object on the trackpad | |
| 4. Apply minimal pressure while maintaining contact to get the weight | |
| files: | | |
| ${{ runner.temp }}/${{ steps.version_info.outputs.dmg_name }} | |
| draft: false | |
| prerelease: false | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Summary | |
| run: | | |
| echo "## Build Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "β Successfully built and packaged TrackWeight DMG" >> $GITHUB_STEP_SUMMARY | |
| echo "π¦ DMG file: ${{ steps.version_info.outputs.dmg_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "π Original repository: https://github.com/KrishKrosh/TrackWeight" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "The DMG includes:" >> $GITHUB_STEP_SUMMARY | |
| echo "- TrackWeight.app (signed and notarized if certificates provided)" >> $GITHUB_STEP_SUMMARY | |
| echo "- Clean professional DMG with just the app and Applications link" >> $GITHUB_STEP_SUMMARY | |
| echo "- Proper code signing with hardened runtime enabled" >> $GITHUB_STEP_SUMMARY |