Skip to content

Build and Sign DMG

Build and Sign DMG #6

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