@@ -65,25 +65,108 @@ jobs:
6565 node-version : " 22"
6666 cache : npm
6767 cache-dependency-path : apps/microbridge-ui/package-lock.json
68- - name : Build Microbridge.app
68+
69+ - name : Import Developer ID certificate
70+ env :
71+ APPLE_CERTIFICATE : ${{ secrets.APPLE_CERTIFICATE }}
72+ APPLE_CERTIFICATE_PASSWORD : ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
73+ KEYCHAIN_PASSWORD : ${{ secrets.KEYCHAIN_PASSWORD }}
74+ run : |
75+ if [[ -z "${APPLE_CERTIFICATE:-}" ]]; then
76+ echo "APPLE_CERTIFICATE secret missing — UI build will be unsigned"
77+ echo "SIGNED=0" >> "$GITHUB_ENV"
78+ exit 0
79+ fi
80+ echo "SIGNED=1" >> "$GITHUB_ENV"
81+ CERT_PATH="$RUNNER_TEMP/certificate.p12"
82+ echo "$APPLE_CERTIFICATE" | base64 --decode > "$CERT_PATH"
83+ security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
84+ security default-keychain -s build.keychain
85+ security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
86+ security set-keychain-settings -t 3600 -u build.keychain
87+ security import "$CERT_PATH" -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" \
88+ -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild
89+ security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
90+ security list-keychains -d user -s build.keychain $(security list-keychains -d user | sed -e 's/"//g')
91+ IDENTITY="${{ secrets.APPLE_SIGNING_IDENTITY }}"
92+ if [[ -z "$IDENTITY" ]]; then
93+ IDENTITY="$(security find-identity -v -p codesigning build.keychain \
94+ | awk -F'"' '/Developer ID Application/{print $2; exit}')"
95+ fi
96+ test -n "$IDENTITY"
97+ echo "APPLE_SIGNING_IDENTITY=$IDENTITY" >> "$GITHUB_ENV"
98+ security find-identity -v -p codesigning build.keychain
99+
100+ - name : Prepare App Store Connect API key for notarization
101+ if : env.SIGNED == '1'
102+ env :
103+ APPLE_API_KEY : ${{ secrets.APPLE_API_KEY }}
104+ APPLE_API_ISSUER : ${{ secrets.APPLE_API_ISSUER }}
105+ APPLE_API_KEY_P8 : ${{ secrets.APPLE_API_KEY_P8 }}
106+ APPLE_TEAM_ID : ${{ secrets.APPLE_TEAM_ID }}
107+ run : |
108+ test -n "${APPLE_API_KEY:-}"
109+ test -n "${APPLE_API_ISSUER:-}"
110+ test -n "${APPLE_API_KEY_P8:-}"
111+ KEY_PATH="$RUNNER_TEMP/AuthKey_${APPLE_API_KEY}.p8"
112+ printf '%s\n' "$APPLE_API_KEY_P8" > "$KEY_PATH"
113+ chmod 600 "$KEY_PATH"
114+ {
115+ echo "APPLE_API_KEY=$APPLE_API_KEY"
116+ echo "APPLE_API_ISSUER=$APPLE_API_ISSUER"
117+ echo "APPLE_API_KEY_PATH=$KEY_PATH"
118+ echo "APPLE_TEAM_ID=${APPLE_TEAM_ID:-3NQG568C4Q}"
119+ } >> "$GITHUB_ENV"
120+
121+ - name : Build signed Microbridge.app + DMG
69122 working-directory : apps/microbridge-ui
123+ env :
124+ APPLE_CERTIFICATE : ${{ secrets.APPLE_CERTIFICATE }}
125+ APPLE_CERTIFICATE_PASSWORD : ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
126+ APPLE_SIGNING_IDENTITY : ${{ env.APPLE_SIGNING_IDENTITY }}
127+ APPLE_API_KEY : ${{ env.APPLE_API_KEY }}
128+ APPLE_API_ISSUER : ${{ env.APPLE_API_ISSUER }}
129+ APPLE_API_KEY_PATH : ${{ env.APPLE_API_KEY_PATH }}
130+ APPLE_TEAM_ID : ${{ env.APPLE_TEAM_ID }}
70131 run : |
71132 npm ci
72- npm run tauri build -- --bundles app --target ${{ matrix.target }}
73- - name : Package .app
133+ if [[ "${SIGNED:-0}" == "1" ]]; then
134+ npm run tauri build -- --bundles app,dmg --target ${{ matrix.target }}
135+ else
136+ npm run tauri build -- --bundles app --target ${{ matrix.target }}
137+ fi
138+
139+ - name : Package .app and DMG
74140 run : |
75- APP="$(find apps/microbridge-ui/src-tauri/target/${{ matrix.target }}/release/bundle/macos apps/microbridge-ui/src-tauri/target/release/bundle/macos -name 'Microbridge.app' -type d 2>/dev/null | head -n1)"
141+ set -euo pipefail
142+ APP="$(find apps/microbridge-ui/src-tauri/target/${{ matrix.target }}/release/bundle/macos \
143+ apps/microbridge-ui/src-tauri/target/release/bundle/macos \
144+ -name 'Microbridge.app' -type d 2>/dev/null | head -n1)"
76145 test -n "$APP"
77146 STAGE="microbridge-ui-${GITHUB_REF_NAME}-${{ matrix.target }}"
78- mkdir -p "staging/${STAGE}"
147+ mkdir -p "staging/${STAGE}" "ui-out"
79148 cp -R "$APP" "staging/${STAGE}/"
80149 cp README.md LICENSE-MIT LICENSE-APACHE INSTALL.md "staging/${STAGE}/"
81- tar -C staging -czf "${STAGE}.tar.gz" "${STAGE}"
82- echo "ASSET=${STAGE}.tar.gz" >> "$GITHUB_ENV"
150+ tar -C staging -czf "ui-out/${STAGE}.tar.gz" "${STAGE}"
151+
152+ DMG="$(find apps/microbridge-ui/src-tauri/target/${{ matrix.target }}/release/bundle/dmg \
153+ apps/microbridge-ui/src-tauri/target/release/bundle/dmg \
154+ -name '*.dmg' -type f 2>/dev/null | head -n1 || true)"
155+ if [[ -n "${DMG:-}" ]]; then
156+ DMG_OUT="ui-out/microbridge-ui-${GITHUB_REF_NAME}-${{ matrix.target }}.dmg"
157+ cp "$DMG" "$DMG_OUT"
158+ echo "Packed signed DMG: $DMG_OUT"
159+ spctl --assess --type open --context context:primary-signature "$DMG_OUT" || true
160+ codesign -dv --verbose=2 "$APP" || true
161+ else
162+ echo "No DMG produced (unsigned build or bundle skipped)"
163+ fi
164+ ls -la ui-out
165+
83166 - uses : actions/upload-artifact@v7
84167 with :
85168 name : ui-${{ matrix.target }}
86- path : ${{ env.ASSET }}
169+ path : ui-out/*
87170
88171 publish :
89172 name : publish release
97180 - name : Collect assets
98181 run : |
99182 mkdir -p release-assets
100- find artifacts -name '*.tar.gz' -exec cp {} release-assets/ \;
183+ find artifacts -type f \( - name '*.tar.gz' -o -name '*.dmg' \) -exec cp {} release-assets/ \;
101184 ls -la release-assets
102185 - uses : softprops/action-gh-release@v3
103186 with :
@@ -117,6 +200,12 @@ jobs:
117200
118201 Upgrade later: `brew update && brew upgrade microbridge`
119202
203+ ### Direct download (signed + notarized DMG)
204+
205+ Grab `microbridge-ui-${{ github.ref_name }}-aarch64-apple-darwin.dmg` (Apple Silicon)
206+ or `…-x86_64-apple-darwin.dmg` (Intel) from the assets below, open it, and drag
207+ Microbridge into Applications. Pair with the daemon archive or Homebrew formula.
208+
120209 Binary + app archive: `./scripts/install-from-release.sh ${{ github.ref_name }}`
121210
122211 Full guide: [INSTALL.md](INSTALL.md).
0 commit comments