Skip to content

Commit dc29ab0

Browse files
authored
Automate signed pkg build for macOS App Store submission (#2624)
1 parent 9f34684 commit dc29ab0

File tree

3 files changed

+124
-11
lines changed

3 files changed

+124
-11
lines changed

.github/autobuild/mac.sh

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,18 @@ setup() {
7171
}
7272

7373
prepare_signing() {
74+
## Certificate types in use:
75+
# - MACOS_CERTIFICATE - Developer ID Application - for codesigning for adhoc release
76+
# - MAC_STORE_APP_CERT - Mac App Distribution - codesigning for App Store submission
77+
# - MAC_STORE_INST_CERT - Mac Installer Distribution - for signing installer pkg file for App Store submission
78+
7479
[[ "${SIGN_IF_POSSIBLE:-0}" == "1" ]] || return 1
7580

7681
# Signing was requested, now check all prerequisites:
7782
[[ -n "${MACOS_CERTIFICATE:-}" ]] || return 1
7883
[[ -n "${MACOS_CERTIFICATE_ID:-}" ]] || return 1
7984
[[ -n "${MACOS_CERTIFICATE_PWD:-}" ]] || return 1
85+
[[ -n "${NOTARIZATION_PASSWORD:-}" ]] || return 1
8086
[[ -n "${KEYCHAIN_PASSWORD:-}" ]] || return 1
8187

8288
# Check for notarization (not wanted on self signed build)
@@ -90,8 +96,8 @@ prepare_signing() {
9096

9197
echo "Signing was requested and all dependencies are satisfied"
9298

93-
# Put the cert to a file
94-
echo "${MACOS_CERTIFICATE}" | base64 --decode > certificate.p12
99+
## Put the certs to files
100+
echo "${MACOS_CERTIFICATE}" | base64 --decode > macos_certificate.p12
95101

96102
# If set, put the CA public key into a file
97103
if [[ -n "${MACOS_CA_PUBLICKEY}" ]]; then
@@ -104,8 +110,8 @@ prepare_signing() {
104110
# Remove default re-lock timeout to avoid codesign hangs:
105111
security set-keychain-settings build.keychain
106112
security unlock-keychain -p "${KEYCHAIN_PASSWORD}" build.keychain
107-
security import certificate.p12 -k build.keychain -P "${MACOS_CERTIFICATE_PWD}" -T /usr/bin/codesign
108-
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "${KEYCHAIN_PASSWORD}" build.keychain
113+
security import macos_certificate.p12 -k build.keychain -P "${MACOS_CERTIFICATE_PWD}" -A -T /usr/bin/codesign
114+
security set-key-partition-list -S apple-tool:,apple: -s -k "${KEYCHAIN_PASSWORD}" build.keychain
109115

110116
# Tell Github Workflow that we want signing
111117
echo "macos_signed=true" >> "$GITHUB_OUTPUT"
@@ -125,6 +131,34 @@ prepare_signing() {
125131
echo "macos_notarize=true" >> "$GITHUB_OUTPUT"
126132
fi
127133

134+
# If distribution cert is present, set for store signing + submission
135+
if [[ -n "${MAC_STORE_APP_CERT}" ]]; then
136+
137+
# Check all Github secrets are in place
138+
# MAC_STORE_APP_CERT already checked
139+
[[ -n "${MAC_STORE_APP_CERT_ID:-}" ]] || return 1
140+
[[ -n "${MAC_STORE_APP_CERT_PWD:-}" ]] || return 1
141+
[[ -n "${MAC_STORE_INST_CERT:-}" ]] || return 1
142+
[[ -n "${MAC_STORE_INST_CERT_ID:-}" ]] || return 1
143+
[[ -n "${MAC_STORE_INST_CERT_PWD:-}" ]] || return 1
144+
145+
# Put the certs to files
146+
echo "${MAC_STORE_APP_CERT}" | base64 --decode > macapp_certificate.p12
147+
echo "${MAC_STORE_INST_CERT}" | base64 --decode > macinst_certificate.p12
148+
149+
echo "App Store distribution dependencies are satisfied, proceeding..."
150+
151+
# Add additional certs to the keychain
152+
security set-keychain-settings build.keychain
153+
security unlock-keychain -p "${KEYCHAIN_PASSWORD}" build.keychain
154+
security import macapp_certificate.p12 -k build.keychain -P "${MAC_STORE_APP_CERT_PWD}" -A -T /usr/bin/codesign
155+
security import macinst_certificate.p12 -k build.keychain -P "${MAC_STORE_INST_CERT_PWD}" -A -T /usr/bin/productbuild
156+
security set-key-partition-list -S apple-tool:,apple: -s -k "${KEYCHAIN_PASSWORD}" build.keychain
157+
158+
# Tell Github Workflow that we are building for store submission
159+
echo "macos_store=true" >> "$GITHUB_OUTPUT"
160+
fi
161+
128162
return 0
129163
}
130164

@@ -136,7 +170,7 @@ build_app_as_dmg_installer() {
136170
# Mac's bash version considers BUILD_ARGS unset without at least one entry:
137171
BUILD_ARGS=("")
138172
if prepare_signing; then
139-
BUILD_ARGS=("-s" "${MACOS_CERTIFICATE_ID}")
173+
BUILD_ARGS=("-s" "${MACOS_CERTIFICATE_ID}" "-a" "${MAC_STORE_APP_CERT_ID}" "-i" "${MAC_STORE_INST_CERT_ID}" "-k" "${KEYCHAIN_PASSWORD}")
140174
fi
141175
TARGET_ARCHS="${TARGET_ARCHS}" ./mac/deploy_mac.sh "${BUILD_ARGS[@]}"
142176
}
@@ -146,6 +180,26 @@ pass_artifact_to_job() {
146180
echo "Moving build artifact to deploy/${artifact}"
147181
mv ./deploy/Jamulus-*installer-mac.dmg "./deploy/${artifact}"
148182
echo "artifact_1=${artifact}" >> "$GITHUB_OUTPUT"
183+
184+
artifact2="jamulus_${JAMULUS_BUILD_VERSION}_mac${ARTIFACT_SUFFIX:-}.pkg"
185+
file=(./deploy/Jamulus_*.pkg)
186+
if [ -f "${file[0]}" ]; then
187+
echo "Moving build artifact2 to deploy/${artifact2}"
188+
mv "${file[0]}" "./deploy/${artifact2}"
189+
echo "artifact_2=${artifact2}" >> "$GITHUB_OUTPUT"
190+
fi
191+
}
192+
193+
appstore_submit() {
194+
echo "Submitting package to AppStore Connect..."
195+
# test the signature of package
196+
pkgutil --check-signature "${ARTIFACT_PATH}"
197+
198+
xcrun notarytool submit "${ARTIFACT_PATH}" \
199+
--apple-id "${NOTARIZATION_USERNAME}" \
200+
--team-id "${APPLE_TEAM_ID}" \
201+
--password "${NOTARIZATION_PASSWORD}" \
202+
--wait
149203
}
150204

151205
case "${1:-}" in
@@ -158,6 +212,9 @@ case "${1:-}" in
158212
get-artifacts)
159213
pass_artifact_to_job
160214
;;
215+
appstore-submit)
216+
appstore_submit
217+
;;
161218
*)
162219
echo "Unknown stage '${1:-}'"
163220
exit 1

.github/workflows/autobuild.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,12 @@ jobs:
368368
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERT}}
369369
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERT_PWD }}
370370
MACOS_CERTIFICATE_ID: ${{ secrets.MACOS_CERT_ID }}
371+
MAC_STORE_APP_CERT: ${{ secrets.MACAPP_CERT}}
372+
MAC_STORE_APP_CERT_PWD: ${{ secrets.MACAPP_CERT_PWD }}
373+
MAC_STORE_APP_CERT_ID: ${{ secrets.MACAPP_CERT_ID }}
374+
MAC_STORE_INST_CERT: ${{ secrets.MACAPP_INST_CERT}}
375+
MAC_STORE_INST_CERT_PWD: ${{ secrets.MACAPP_INST_CERT_PWD }}
376+
MAC_STORE_INST_CERT_ID: ${{ secrets.MACAPP_INST_CERT_ID }}
371377
NOTARIZATION_PASSWORD: ${{ secrets.NOTARIZATION_PASSWORD }}
372378
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
373379
MACOS_CA_PUBLICKEY: ${{ secrets.MACOS_CA_PUBKEY }}
@@ -430,6 +436,20 @@ jobs:
430436
env:
431437
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
432438

439+
## RELEASE PROCEDURE FOR: macOS App Store - storesigned pkg
440+
- name: Validate and Upload macOS Storesign Pkg
441+
if: >-
442+
steps.build.outputs.macos_store == 'true' &&
443+
needs.create_release.outputs.publish_to_release == 'true'
444+
id: macos_validate_upload
445+
run: ${{ matrix.config.base_command }} appstore-submit
446+
env:
447+
ARTIFACT_PATH: deploy/${{ steps.get-artifacts.outputs.artifact_2 }}
448+
NOTARIZATION_USERNAME: ${{ secrets.NOTARIZATION_USERNAME }}
449+
NOTARIZATION_PASSWORD: ${{ secrets.NOTARIZATION_PASSWORD }}
450+
JAMULUS_BUILD_VERSION: ${{ needs.create_release.outputs.build_version }}
451+
APPLE_TEAM_ID: XXXXXXXXXXX
452+
433453
- name: Perform CodeQL Analysis
434454
if: matrix.config.run_codeql
435455
uses: github/codeql-action/analyze@v3

mac/deploy_mac.sh

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,36 @@ resources_path="${root_path}/src/res"
77
build_path="${root_path}/build"
88
deploy_path="${root_path}/deploy"
99
cert_name=""
10+
macapp_cert_name=""
11+
macinst_cert_name=""
12+
keychain_pass=""
1013

11-
while getopts 'hs:' flag; do
14+
while getopts 'hs:k:a:i:' flag; do
1215
case "${flag}" in
1316
s)
1417
cert_name=$OPTARG
1518
if [[ -z "$cert_name" ]]; then
1619
echo "Please add the name of the certificate to use: -s \"<name>\""
1720
fi
1821
;;
22+
a)
23+
macapp_cert_name=$OPTARG
24+
if [[ -z "$macapp_cert_name" ]]; then
25+
echo "Please add the name of the codesigning certificate to use: -a \"<name>\""
26+
fi
27+
;;
28+
i)
29+
macinst_cert_name=$OPTARG
30+
if [[ -z "$macinst_cert_name" ]]; then
31+
echo "Please add the name of the installer signing certificate to use: -i \"<name>\""
32+
fi
33+
;;
34+
k)
35+
keychain_pass=$OPTARG
36+
if [[ -z "$keychain_pass" ]]; then
37+
echo "Please add keychain password to use: -k \"<name>\""
38+
fi
39+
;;
1940
h)
2041
echo "Usage: -s <cert name> for signing mac build"
2142
exit 0
@@ -83,6 +104,25 @@ build_app() {
83104
else
84105
macdeployqt "${build_path}/${target_name}.app" -verbose=2 -always-overwrite -hardened-runtime -timestamp -appstore-compliant -sign-for-notarization="${cert_name}"
85106
fi
107+
108+
## Build installer pkg file - for submission to App Store
109+
if [[ -z "$macapp_cert_name" ]]; then
110+
echo "No cert to sign for App Store, bypassing..."
111+
else
112+
# Clone the build directory to leave the adhoc signed app untouched
113+
cp -a "${build_path}" "${build_path}_storesign"
114+
115+
# Add Qt deployment deps and codesign the app for App Store submission
116+
macdeployqt "${build_path}_storesign/${target_name}.app" -verbose=2 -always-overwrite -hardened-runtime -timestamp -appstore-compliant -sign-for-notarization="${macapp_cert_name}"
117+
118+
# Create pkg installer and sign for App Store submission
119+
productbuild --sign "${macinst_cert_name}" --keychain build.keychain --component "${build_path}_storesign/${target_name}.app" /Applications "${build_path}/Jamulus_${JAMULUS_BUILD_VERSION}.pkg"
120+
121+
# move created pkg file to prep for download
122+
mv "${build_path}/Jamulus_${JAMULUS_BUILD_VERSION}.pkg" "${deploy_path}"
123+
fi
124+
125+
# move app bundle to prep for dmg creation
86126
mv "${build_path}/${target_name}.app" "${deploy_path}"
87127

88128
# Cleanup
@@ -114,10 +154,6 @@ build_installer_image() {
114154
# FIXME: Currently caching is disabled due to an error in the extract step
115155
brew install create-dmg
116156

117-
# Get Jamulus version
118-
local app_version
119-
app_version=$(sed -nE 's/^VERSION *= *(.*)$/\1/p' "${project_path}")
120-
121157
# Build installer image
122158

123159
# When this script is run on Github's CI with CodeQL enabled, CodeQL adds dynamic library
@@ -141,7 +177,7 @@ build_installer_image() {
141177
--icon "${client_target_name}.app" 630 210 \
142178
--icon "${server_target_name}.app" 530 210 \
143179
--eula "${root_path}/COPYING" \
144-
"${deploy_path}/${client_target_name}-${app_version}-installer-mac.dmg" \
180+
"${deploy_path}/${client_target_name}-${JAMULUS_BUILD_VERSION}-installer-mac.dmg" \
145181
"${deploy_path}/"
146182
}
147183

0 commit comments

Comments
 (0)