Skip to content

Commit

Permalink
Automate signed pkg build for macOS App Store submission (#2624)
Browse files Browse the repository at this point in the history
  • Loading branch information
danryu committed Jul 21, 2024
1 parent 9f34684 commit dc29ab0
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 11 deletions.
67 changes: 62 additions & 5 deletions .github/autobuild/mac.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,18 @@ setup() {
}

prepare_signing() {
## Certificate types in use:
# - MACOS_CERTIFICATE - Developer ID Application - for codesigning for adhoc release
# - MAC_STORE_APP_CERT - Mac App Distribution - codesigning for App Store submission
# - MAC_STORE_INST_CERT - Mac Installer Distribution - for signing installer pkg file for App Store submission

[[ "${SIGN_IF_POSSIBLE:-0}" == "1" ]] || return 1

# Signing was requested, now check all prerequisites:
[[ -n "${MACOS_CERTIFICATE:-}" ]] || return 1
[[ -n "${MACOS_CERTIFICATE_ID:-}" ]] || return 1
[[ -n "${MACOS_CERTIFICATE_PWD:-}" ]] || return 1
[[ -n "${NOTARIZATION_PASSWORD:-}" ]] || return 1
[[ -n "${KEYCHAIN_PASSWORD:-}" ]] || return 1

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

echo "Signing was requested and all dependencies are satisfied"

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

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

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

# If distribution cert is present, set for store signing + submission
if [[ -n "${MAC_STORE_APP_CERT}" ]]; then

# Check all Github secrets are in place
# MAC_STORE_APP_CERT already checked
[[ -n "${MAC_STORE_APP_CERT_ID:-}" ]] || return 1
[[ -n "${MAC_STORE_APP_CERT_PWD:-}" ]] || return 1
[[ -n "${MAC_STORE_INST_CERT:-}" ]] || return 1
[[ -n "${MAC_STORE_INST_CERT_ID:-}" ]] || return 1
[[ -n "${MAC_STORE_INST_CERT_PWD:-}" ]] || return 1

# Put the certs to files
echo "${MAC_STORE_APP_CERT}" | base64 --decode > macapp_certificate.p12
echo "${MAC_STORE_INST_CERT}" | base64 --decode > macinst_certificate.p12

echo "App Store distribution dependencies are satisfied, proceeding..."

# Add additional certs to the keychain
security set-keychain-settings build.keychain
security unlock-keychain -p "${KEYCHAIN_PASSWORD}" build.keychain
security import macapp_certificate.p12 -k build.keychain -P "${MAC_STORE_APP_CERT_PWD}" -A -T /usr/bin/codesign
security import macinst_certificate.p12 -k build.keychain -P "${MAC_STORE_INST_CERT_PWD}" -A -T /usr/bin/productbuild
security set-key-partition-list -S apple-tool:,apple: -s -k "${KEYCHAIN_PASSWORD}" build.keychain

# Tell Github Workflow that we are building for store submission
echo "macos_store=true" >> "$GITHUB_OUTPUT"
fi

return 0
}

Expand All @@ -136,7 +170,7 @@ build_app_as_dmg_installer() {
# Mac's bash version considers BUILD_ARGS unset without at least one entry:
BUILD_ARGS=("")
if prepare_signing; then
BUILD_ARGS=("-s" "${MACOS_CERTIFICATE_ID}")
BUILD_ARGS=("-s" "${MACOS_CERTIFICATE_ID}" "-a" "${MAC_STORE_APP_CERT_ID}" "-i" "${MAC_STORE_INST_CERT_ID}" "-k" "${KEYCHAIN_PASSWORD}")
fi
TARGET_ARCHS="${TARGET_ARCHS}" ./mac/deploy_mac.sh "${BUILD_ARGS[@]}"
}
Expand All @@ -146,6 +180,26 @@ pass_artifact_to_job() {
echo "Moving build artifact to deploy/${artifact}"
mv ./deploy/Jamulus-*installer-mac.dmg "./deploy/${artifact}"
echo "artifact_1=${artifact}" >> "$GITHUB_OUTPUT"

artifact2="jamulus_${JAMULUS_BUILD_VERSION}_mac${ARTIFACT_SUFFIX:-}.pkg"
file=(./deploy/Jamulus_*.pkg)
if [ -f "${file[0]}" ]; then
echo "Moving build artifact2 to deploy/${artifact2}"
mv "${file[0]}" "./deploy/${artifact2}"
echo "artifact_2=${artifact2}" >> "$GITHUB_OUTPUT"
fi
}

appstore_submit() {
echo "Submitting package to AppStore Connect..."
# test the signature of package
pkgutil --check-signature "${ARTIFACT_PATH}"

xcrun notarytool submit "${ARTIFACT_PATH}" \
--apple-id "${NOTARIZATION_USERNAME}" \
--team-id "${APPLE_TEAM_ID}" \
--password "${NOTARIZATION_PASSWORD}" \
--wait
}

case "${1:-}" in
Expand All @@ -158,6 +212,9 @@ case "${1:-}" in
get-artifacts)
pass_artifact_to_job
;;
appstore-submit)
appstore_submit
;;
*)
echo "Unknown stage '${1:-}'"
exit 1
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/autobuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,12 @@ jobs:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERT}}
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERT_PWD }}
MACOS_CERTIFICATE_ID: ${{ secrets.MACOS_CERT_ID }}
MAC_STORE_APP_CERT: ${{ secrets.MACAPP_CERT}}
MAC_STORE_APP_CERT_PWD: ${{ secrets.MACAPP_CERT_PWD }}
MAC_STORE_APP_CERT_ID: ${{ secrets.MACAPP_CERT_ID }}
MAC_STORE_INST_CERT: ${{ secrets.MACAPP_INST_CERT}}
MAC_STORE_INST_CERT_PWD: ${{ secrets.MACAPP_INST_CERT_PWD }}
MAC_STORE_INST_CERT_ID: ${{ secrets.MACAPP_INST_CERT_ID }}
NOTARIZATION_PASSWORD: ${{ secrets.NOTARIZATION_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
MACOS_CA_PUBLICKEY: ${{ secrets.MACOS_CA_PUBKEY }}
Expand Down Expand Up @@ -430,6 +436,20 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

## RELEASE PROCEDURE FOR: macOS App Store - storesigned pkg
- name: Validate and Upload macOS Storesign Pkg
if: >-
steps.build.outputs.macos_store == 'true' &&
needs.create_release.outputs.publish_to_release == 'true'
id: macos_validate_upload
run: ${{ matrix.config.base_command }} appstore-submit
env:
ARTIFACT_PATH: deploy/${{ steps.get-artifacts.outputs.artifact_2 }}
NOTARIZATION_USERNAME: ${{ secrets.NOTARIZATION_USERNAME }}
NOTARIZATION_PASSWORD: ${{ secrets.NOTARIZATION_PASSWORD }}
JAMULUS_BUILD_VERSION: ${{ needs.create_release.outputs.build_version }}
APPLE_TEAM_ID: XXXXXXXXXXX

- name: Perform CodeQL Analysis
if: matrix.config.run_codeql
uses: github/codeql-action/analyze@v3
Expand Down
48 changes: 42 additions & 6 deletions mac/deploy_mac.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,36 @@ resources_path="${root_path}/src/res"
build_path="${root_path}/build"
deploy_path="${root_path}/deploy"
cert_name=""
macapp_cert_name=""
macinst_cert_name=""
keychain_pass=""

while getopts 'hs:' flag; do
while getopts 'hs:k:a:i:' flag; do
case "${flag}" in
s)
cert_name=$OPTARG
if [[ -z "$cert_name" ]]; then
echo "Please add the name of the certificate to use: -s \"<name>\""
fi
;;
a)
macapp_cert_name=$OPTARG
if [[ -z "$macapp_cert_name" ]]; then
echo "Please add the name of the codesigning certificate to use: -a \"<name>\""
fi
;;
i)
macinst_cert_name=$OPTARG
if [[ -z "$macinst_cert_name" ]]; then
echo "Please add the name of the installer signing certificate to use: -i \"<name>\""
fi
;;
k)
keychain_pass=$OPTARG
if [[ -z "$keychain_pass" ]]; then
echo "Please add keychain password to use: -k \"<name>\""
fi
;;
h)
echo "Usage: -s <cert name> for signing mac build"
exit 0
Expand Down Expand Up @@ -83,6 +104,25 @@ build_app() {
else
macdeployqt "${build_path}/${target_name}.app" -verbose=2 -always-overwrite -hardened-runtime -timestamp -appstore-compliant -sign-for-notarization="${cert_name}"
fi

## Build installer pkg file - for submission to App Store
if [[ -z "$macapp_cert_name" ]]; then
echo "No cert to sign for App Store, bypassing..."
else
# Clone the build directory to leave the adhoc signed app untouched
cp -a "${build_path}" "${build_path}_storesign"

# Add Qt deployment deps and codesign the app for App Store submission
macdeployqt "${build_path}_storesign/${target_name}.app" -verbose=2 -always-overwrite -hardened-runtime -timestamp -appstore-compliant -sign-for-notarization="${macapp_cert_name}"

# Create pkg installer and sign for App Store submission
productbuild --sign "${macinst_cert_name}" --keychain build.keychain --component "${build_path}_storesign/${target_name}.app" /Applications "${build_path}/Jamulus_${JAMULUS_BUILD_VERSION}.pkg"

# move created pkg file to prep for download
mv "${build_path}/Jamulus_${JAMULUS_BUILD_VERSION}.pkg" "${deploy_path}"
fi

# move app bundle to prep for dmg creation
mv "${build_path}/${target_name}.app" "${deploy_path}"

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

# Get Jamulus version
local app_version
app_version=$(sed -nE 's/^VERSION *= *(.*)$/\1/p' "${project_path}")

# Build installer image

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

Expand Down

0 comments on commit dc29ab0

Please sign in to comment.