Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 187 additions & 11 deletions .github/workflows/build-mac.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,61 @@ jobs:

- name: Create xcconfig files
run: |
cd MacOS/ProxyBridge
cp proxybridge-app.xcconfig Signing-Config-app.xcconfig
cp proxybridge-ext.xcconfig Signing-Config-ext.xcconfig
sed -i '' 's/DEVELOPMENT_TEAM = L.*/DEVELOPMENT_TEAM = /' Signing-Config-app.xcconfig
sed -i '' 's/DEVELOPMENT_TEAM = L.*/DEVELOPMENT_TEAM = /' Signing-Config-ext.xcconfig

echo "${{ secrets.MACOS_APP_XCCONFIG }}" > MacOS/ProxyBridge/Signing-Config-app.xcconfig
echo "${{ secrets.MACOS_EXT_XCCONFIG }}" > MacOS/ProxyBridge/Signing-Config-ext.xcconfig

- name: Install Provisioning Profiles
run: |
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles

echo "${{ secrets.MACOS_APP_PROVISION_PROFILE }}" | base64 --decode > /tmp/app.provisionprofile
echo "${{ secrets.MACOS_EXT_PROVISION_PROFILE }}" | base64 --decode > /tmp/ext.provisionprofile

APP_UUID=$(security cms -D -i /tmp/app.provisionprofile | plutil -extract UUID raw -)
EXT_UUID=$(security cms -D -i /tmp/ext.provisionprofile | plutil -extract UUID raw -)

cp /tmp/app.provisionprofile ~/Library/MobileDevice/Provisioning\ Profiles/${APP_UUID}.provisionprofile
cp /tmp/ext.provisionprofile ~/Library/MobileDevice/Provisioning\ Profiles/${EXT_UUID}.provisionprofile

echo "Installed app profile: ${APP_UUID}"
echo "Installed ext profile: ${EXT_UUID}"

- name: Import Certificate
run: |
# Decode certificate
echo "${{ secrets.MACOS_CERTIFICATE }}" | base64 --decode > /tmp/certificate.p12

# Create a temporary keychain
security create-keychain -p "${{ secrets.MACOS_KEYCHAIN_PASSWORD }}" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "${{ secrets.MACOS_KEYCHAIN_PASSWORD }}" build.keychain
security set-keychain-settings build.keychain

# Import Developer ID Application certificate
security import /tmp/certificate.p12 \
-k build.keychain \
-P "${{ secrets.MACOS_CERTIFICATE_PASSWORD }}" \
-T /usr/bin/codesign \
-A

# Import Developer ID Installer certificate
echo "${{ secrets.MACOS_INSTALLER_CERTIFICATE }}" | base64 --decode > /tmp/installer.p12
security import /tmp/installer.p12 \
-k build.keychain \
-P "${{ secrets.MACOS_INSTALLER_CERTIFICATE_PASSWORD }}" \
-T /usr/bin/codesign \
-T /usr/bin/productsign \
-A

# Allow codesign and productsign to access the keychain without prompting
security set-key-partition-list -S apple-tool:,apple: -s -k "${{ secrets.MACOS_KEYCHAIN_PASSWORD }}" build.keychain

# Add build.keychain to the keychain search list so productsign can find the cert
security list-keychains -d user -s build.keychain login.keychain-db

# Verify both certificates are available
security find-identity -v -p codesigning build.keychain

- name: Build Universal Binary
run: |
cd MacOS/ProxyBridge
Expand All @@ -40,14 +89,141 @@ jobs:
-derivedDataPath build/DerivedData \
ARCHS="arm64 x86_64" \
ONLY_ACTIVE_ARCH=NO \
CODE_SIGN_IDENTITY="-" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
OTHER_CODE_SIGN_FLAGS="--keychain build.keychain --timestamp" \
clean build

- name: Verify Build
run: |
cd MacOS/ProxyBridge
ls -la build/DerivedData/Build/Products/Release/
file build/DerivedData/Build/Products/Release/ProxyBridge.app/Contents/MacOS/ProxyBridge
lipo -archs build/DerivedData/Build/Products/Release/ProxyBridge.app/Contents/MacOS/ProxyBridge
lipo -archs build/DerivedData/Build/Products/Release/ProxyBridge.app/Contents/MacOS/ProxyBridge
# Verify code signature
codesign -dv --verbose=4 build/DerivedData/Build/Products/Release/ProxyBridge.app

- name: Notarize App
run: |
APP_PATH="MacOS/ProxyBridge/build/DerivedData/Build/Products/Release/ProxyBridge.app"

# Zip the app for submission
ditto -c -k --keepParent "$APP_PATH" /tmp/ProxyBridge.zip

# Submit for notarization and wait for result, capture submission ID
SUBMIT_OUTPUT=$(xcrun notarytool submit /tmp/ProxyBridge.zip \
--apple-id "${{ secrets.APPLE_ID }}" \
--password "${{ secrets.APPLE_APP_PASSWORD }}" \
--team-id "${{ secrets.APPLE_TEAM_ID }}" \
--wait 2>&1)

echo "$SUBMIT_OUTPUT"

# Extract submission ID
SUBMISSION_ID=$(echo "$SUBMIT_OUTPUT" | grep "^ id:" | head -1 | awk '{print $2}')
echo "Submission ID: $SUBMISSION_ID"

# Always fetch the detailed log from Apple
if [ -n "$SUBMISSION_ID" ]; then
echo "--- Notarization Log ---"
xcrun notarytool log "$SUBMISSION_ID" \
--apple-id "${{ secrets.APPLE_ID }}" \
--password "${{ secrets.APPLE_APP_PASSWORD }}" \
--team-id "${{ secrets.APPLE_TEAM_ID }}" 2>&1 || true
fi

# Fail if notarization was not accepted
if ! echo "$SUBMIT_OUTPUT" | grep -q "status: Accepted"; then
echo "Notarization failed!"
exit 1
fi

# Staple the notarization ticket to the app
xcrun stapler staple "$APP_PATH"

# Verify notarization
spctl -a -vvv -t install "$APP_PATH"

- name: Build PKG Installer
env:
PKG_ENV_CONTENT: ${{ secrets.PKG_ENV }}
run: |
# Write PKG_ENV to .env but strip SIGNING_IDENTITY so build.sh skips signing
# (productsign inside build.sh cannot access build.keychain — we do it below)
printf '%s' "$PKG_ENV_CONTENT" | grep -v "^SIGNING_IDENTITY" > MacOS/ProxyBridge/.env

# Copy notarized .app into output/ where build.sh expects it
mkdir -p MacOS/ProxyBridge/output
cp -R MacOS/ProxyBridge/build/DerivedData/Build/Products/Release/ProxyBridge.app \
MacOS/ProxyBridge/output/ProxyBridge.app

# Run build script — creates the unsigned .pkg only
cd MacOS/ProxyBridge
chmod +x build.sh
bash build.sh

PKG_VERSION="3.2.0"
PKG_UNSIGNED="output/ProxyBridge-v${PKG_VERSION}-Universal-Installer.pkg"
PKG_SIGNED="output/ProxyBridge-v${PKG_VERSION}-Universal-Installer-signed.pkg"

# Sign pkg with explicit keychain — no prompting
echo "Signing installer..."
KEYCHAIN_PATH=$(security list-keychains -d user | grep build | tr -d ' "')
echo "Using keychain: $KEYCHAIN_PATH"
security unlock-keychain -p "${{ secrets.MACOS_KEYCHAIN_PASSWORD }}" "$KEYCHAIN_PATH"
productsign \
--sign "${{ secrets.MACOS_SIGNING_IDENTITY }}" \
--keychain "$KEYCHAIN_PATH" \
"$PKG_UNSIGNED" "$PKG_SIGNED"
mv "$PKG_SIGNED" "$PKG_UNSIGNED"

# Notarize pkg
echo "Notarizing installer..."
PKG_SUBMIT_OUTPUT=$(xcrun notarytool submit "$PKG_UNSIGNED" \
--apple-id "${{ secrets.APPLE_ID }}" \
--password "${{ secrets.APPLE_APP_PASSWORD }}" \
--team-id "${{ secrets.APPLE_TEAM_ID }}" \
--wait 2>&1)

echo "$PKG_SUBMIT_OUTPUT"

PKG_SUBMISSION_ID=$(echo "$PKG_SUBMIT_OUTPUT" | grep "^ id:" | head -1 | awk '{print $2}')
if [ -n "$PKG_SUBMISSION_ID" ]; then
echo "--- PKG Notarization Log ---"
xcrun notarytool log "$PKG_SUBMISSION_ID" \
--apple-id "${{ secrets.APPLE_ID }}" \
--password "${{ secrets.APPLE_APP_PASSWORD }}" \
--team-id "${{ secrets.APPLE_TEAM_ID }}" 2>&1 || true
fi

if ! echo "$PKG_SUBMIT_OUTPUT" | grep -q "status: Accepted"; then
echo "PKG notarization failed!"
exit 1
fi

xcrun stapler staple "$PKG_UNSIGNED"
echo "PKG signed, notarized and stapled successfully"

- name: Upload PKG Artifact
uses: actions/upload-artifact@v4
with:
name: ProxyBridge-macOS-Installer
path: MacOS/ProxyBridge/output/ProxyBridge-*.pkg
retention-days: 30

- name: Cleanup
if: always()
run: |
# Delete temporary keychain
security delete-keychain build.keychain || true

# Remove provisioning profiles
rm -f ~/Library/MobileDevice/Provisioning\ Profiles/*.provisionprofile || true

# Remove temp cert and profile files
rm -f /tmp/certificate.p12 /tmp/installer.p12 /tmp/app.provisionprofile /tmp/ext.provisionprofile /tmp/ProxyBridge.zip || true

# Remove .env with sensitive data
rm -f MacOS/ProxyBridge/.env || true

# Remove xcconfig files with sensitive data
rm -f MacOS/ProxyBridge/Signing-Config-app.xcconfig || true
rm -f MacOS/ProxyBridge/Signing-Config-ext.xcconfig || true
3 changes: 3 additions & 0 deletions .github/workflows/release-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ on:
types: [published, created]
workflow_dispatch:

permissions:
contents: write

jobs:
build-and-release:
runs-on: ubuntu-latest
Expand Down
Loading
Loading