Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automate build of iOS signed ipa file for App Store submission #2625

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
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
66 changes: 62 additions & 4 deletions .github/autobuild/ios.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,75 @@ setup() {
fi
}


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

# Signing was requested, now check all prerequisites:
[[ -n "${IOSDIST_CERTIFICATE:-}" ]] || return 1
[[ -n "${IOSDIST_CERTIFICATE_ID:-}" ]] || return 1
[[ -n "${IOSDIST_CERTIFICATE_PWD:-}" ]] || return 1
[[ -n "${NOTARIZATION_PASSWORD:-}" ]] || return 1
[[ -n "${IOS_PROV_PROFILE_B64:-}" ]] || return 1
[[ -n "${KEYCHAIN_PASSWORD:-}" ]] || return 1

echo "Signing was requested and all dependencies are satisfied"

# use this as filename for Provisioning Profile
IOS_PP_PATH="embedded.mobileprovision"

## Put the cert to a file
# IOSDIST_CERTIFICATE - iOS Distribution
echo "${IOSDIST_CERTIFICATE}" | base64 --decode > iosdist_certificate.p12

## Echo Provisioning Profile to file
echo -n "${IOS_PROV_PROFILE_B64}" | base64 --decode > $IOS_PP_PATH
Comment on lines +46 to +64
Copy link
Member

@ann0see ann0see Jul 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in the macOS case, probably worth putting distribution related stuff in an if.


# Set up a keychain for the build:
security create-keychain -p "${KEYCHAIN_PASSWORD}" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "${KEYCHAIN_PASSWORD}" build.keychain
security import iosdist_certificate.p12 -k build.keychain -P "${IOSDIST_CERTIFICATE_PWD}" -A -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "${KEYCHAIN_PASSWORD}" build.keychain
# add notarization/validation/upload password to keychain
xcrun altool --store-password-in-keychain-item --keychain build.keychain APPCONNAUTH -u $NOTARIZATION_USER -p $NOTARIZATION_PASSWORD
# set lock timeout on keychain to 6 hours
security set-keychain-settings -lut 21600
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in the macOS case - maybe this is no longer needed.


# apply provisioning profile
#FIXME - maybe redundant?
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $IOS_PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles

# Tell Github Workflow that we need notarization & stapling:
echo "::set-output name=ios_signed::true"
return 0
}

build_app_as_ipa() {
# Add the Qt binaries to the PATH:
export PATH="${QT_DIR}/${QT_VERSION}/ios/bin:${PATH}"
./ios/deploy_ios.sh

# Mac's bash version considers BUILD_ARGS unset without at least one entry:
BUILD_ARGS=("")
if prepare_signing; then
BUILD_ARGS=("-s" "${IOSDIST_CERTIFICATE_ID}" "-k" "${KEYCHAIN_PASSWORD}")
fi
./ios/deploy_ios.sh "${BUILD_ARGS[@]}"
}

pass_artifact_to_job() {
local artifact="jamulus_${JAMULUS_BUILD_VERSION}_iOSUnsigned${ARTIFACT_SUFFIX:-}.ipa"
local artifact="jamulus_${JAMULUS_BUILD_VERSION}_iOS_unsigned${ARTIFACT_SUFFIX:-}.ipa"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how much a signed IPA on the web without the app store helps?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's any point to that, no - the signed IPA is purely for App Store submission. So it's really for @emlynmac or whoever is managing the Jamulus App Store account to have a ready-built file for submission.

I assume the unsigned IPA is useful for general testing purposes so obviously I've kept that in.

echo "Moving build artifact to deploy/${artifact}"
mv ./deploy/Jamulus.ipa "./deploy/${artifact}"
echo "artifact_1=${artifact}" >> "$GITHUB_OUTPUT"
mv ./deploy/Jamulus_unsigned.ipa "./deploy/${artifact}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not worth keeping this around, only signed version would be useful past making sure the build works.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I left a note above, I wasn't sure if people were maybe using the unsigned IPA for local testing or somesuch.
No problem to remove it if you prefer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not be removed IMO.

echo "::set-output name=artifact_1::${artifact}"

local artifact2="jamulus_${JAMULUS_BUILD_VERSION}_iOS_signed${ARTIFACT_SUFFIX:-}.ipa"
if [ -f ./deploy/Jamulus_signed.ipa ]; then
echo "Moving build artifact to deploy/${artifact2}"
mv ./deploy/Jamulus_signed.ipa "./deploy/${artifact2}"
echo "::set-output name=artifact_2::${artifact2}"
fi
}

case "${1:-}" in
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/autobuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,12 @@ jobs:
- config_name: iOS (artifacts)
target_os: ios
building_on_os: macos-11
base_command: QT_VERSION=5.15.2 ./.github/autobuild/ios.sh
base_command: QT_VERSION=5.15.2 SIGN_IF_POSSIBLE=1 ./.github/autobuild/ios.sh
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use the presence of a certificate in the environment to determine whether to sign, so not needing another parameter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather follow the lead as being done for macOS, which uses that flag. Or we can remove from both macOS and iOS but to have in just one feels weird.

# Build failed with CodeQL enabled when last tested 03/2022 (#2490).
# There are no hints that iOS is supposed to be supported by CodeQL.
# Therefore, disable it:
run_codeql: false
xcode_version: 12.5.1
xcode_version: 13.2.1

- config_name: Windows (artifact+codeQL)
target_os: windows
Expand Down Expand Up @@ -363,6 +363,10 @@ jobs:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERT}}
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERT_PWD }}
MACOS_CERTIFICATE_ID: ${{ secrets.MACOS_CERT_ID }}
IOSDIST_CERTIFICATE: ${{ secrets.IOSDIST_CERT}}
IOSDIST_CERTIFICATE_PWD: ${{ secrets.IOSDIST_CERT_PWD }}
IOSDIST_CERTIFICATE_ID: ${{ secrets.IOSDIST_CERT_ID }}
IOS_PROV_PROFILE_B64: ${{ secrets.IOS_PROVISIONING_PROFILE }}
NOTARIZATION_PASSWORD: ${{ secrets.NOTARIZATION_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
MACOS_CA_PUBLICKEY: ${{ secrets.MACOS_CA_PUBKEY }}
Expand Down
57 changes: 57 additions & 0 deletions ios/Info-make.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?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>CFBundleExecutable</key>
<string>@EXECUTABLE@</string>
<key>CFBundleIdentifier</key>
<string>@BUNDLEIDENTIFIER@</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>@EXECUTABLE@</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSMicrophoneUsageDescription</key>
<string>We need access to your microphone to let others hear you.</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
<key>UILaunchScreen</key>
<dict/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>

</dict>
</plist>
59 changes: 59 additions & 0 deletions ios/Info-xcode.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSMicrophoneUsageDescription</key>
<string>We need access to your microphone to let others hear you.</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
<key>UILaunchScreen</key>
<dict/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>

</dict>
</plist>
125 changes: 113 additions & 12 deletions ios/deploy_ios.sh
Original file line number Diff line number Diff line change
@@ -1,18 +1,119 @@
#!/bin/bash
set -eu -o pipefail

## Builds an ipa file for iOS. Should be run from the repo-root
root_path="$(pwd)"
project_path="${root_path}/Jamulus.pro"
iosdeploy_path="${root_path}/ios"
resources_path="${root_path}/src/res"
build_path="${root_path}/build"
deploy_path="${root_path}/deploy"
iosdist_cert_name=""
keychain_pass=""

# Create Xcode file and build
qmake -spec macx-xcode Jamulus.pro
/usr/bin/xcodebuild -project Jamulus.xcodeproj -scheme Jamulus -configuration Release clean archive -archivePath "build/Jamulus.xcarchive" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO CODE_SIGN_ENTITLEMENTS=""
while getopts 'hs:k:' flag; do
case "${flag}" in
s)
iosdist_cert_name=$OPTARG
if [[ -z "$iosdist_cert_name" ]]; then
echo "Please add the name of the certificate to use: -s \"<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 ios build"
exit 0
;;
*)
exit 1
;;
esac
done

# Generate ipa by copying the .app file from the xcarchive directory
mkdir build/Payload
cp -r build/Jamulus.xcarchive/Products/Applications/Jamulus.app build/Payload/
cd build
zip -0 -y -r Jamulus.ipa Payload/
cleanup()
{
# Clean up previous deployments
rm -rf "${build_path}"
rm -rf "${deploy_path}"
mkdir -p "${build_path}"
mkdir -p "${build_path}/Exports"
mkdir -p "${deploy_path}"
}

# Make a deploy folder and copy file
mkdir ../deploy
mv Jamulus.ipa ../deploy
build_ipa()
{
## Builds an ipa file for iOS. Should be run from the repo-root

# Create Xcode project file
qmake -spec macx-xcode Jamulus.pro

# disable deprecation warnings re legacy build system - XCode 13 errors on this
/usr/libexec/PlistBuddy -c "Add :DisableBuildSystemDeprecationDiagnostic bool" Jamulus.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
/usr/libexec/PlistBuddy -c "Set :DisableBuildSystemDeprecationDiagnostic true" Jamulus.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

# Build
if [[ -z "$iosdist_cert_name" ]]; then
# Build unsigned
/usr/bin/xcodebuild -project Jamulus.xcodeproj -scheme Jamulus -configuration Release clean archive \
-archivePath "build/Jamulus.xcarchive" \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
CODE_SIGN_ENTITLEMENTS=""
else
# Build signed ipa file
# Ref: https://developer.apple.com/forums/thread/70326
# // Builds the app into an archive
/usr/bin/xcodebuild -project Jamulus.xcodeproj -scheme Jamulus -configuration Release clean archive \
-archivePath "build/Jamulus.xcarchive" \
DEVELOPMENT_TEAM="XXXXXXXXXX" \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO

#FIXME this may be redundant - since provisioning profile is specified in exportOptionsRelease.plist
cp ~/Library/MobileDevice/Provisioning\ Profiles/embedded.mobileprovision build/Jamulus.xcarchive/Products/Applications/Jamulus.app/

# // Exports the archive according to the export options specified by the plist
# export signed installer to build/Exports/Jamulus.ipa
/usr/bin/xcodebuild -exportArchive \
-archivePath "build/Jamulus.xcarchive" \
-exportPath "build/Exports/" \
-exportOptionsPlist "ios/exportOptionsRelease.plist" \
DEVELOPMENT_TEAM="XXXXXXXXXX" \
CODE_SIGN_IDENTITY="${iosdist_cert_name}" \
CODE_SIGNING_REQUIRED=YES \
CODE_SIGNING_ALLOWED=YES \
CODE_SIGN_STYLE="Manual"

# if validate/upload
xcrun altool --validate-app -f "${build_path}/Exports/Jamulus.ipa" -t ios -p @keychain:APPCONNAUTH
xcrun altool --upload-app -f "${build_path}/Exports/Jamulus.ipa" -t ios -p @keychain:APPCONNAUTH
fi

# Generate unsigned ipa by copying the .app structure from the xcarchive directory
cd ${root_path}
mkdir -p build/unsigned/Payload
cp -r build/Jamulus.xcarchive/Products/Applications/Jamulus.app build/unsigned/Payload/
cd build/unsigned
zip -0 -y -r Jamulus.ipa Payload/

# copy files
cd ${root_path}
# unsigned IPA
mv build/unsigned/Jamulus.ipa deploy/Jamulus_unsigned.ipa
# signed IPA
if [[ ! -z "$iosdist_cert_name" ]]; then
mv build/Exports/Jamulus.ipa deploy/Jamulus_signed.ipa
fi
}

# Cleanup previous deployments
cleanup

# Build ipa file for App Store submission (eg via Transporter etc)
build_ipa
15 changes: 15 additions & 0 deletions ios/exportOptionsRelease.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?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>app-store</string>
<key>teamID</key>
<string>XXXXXXXXXX</string>
<key>provisioningProfiles</key>
<dict>
<key>io.jamulus.Jamulus</key>
<string>UUID_GOES_HERE</string>
</dict>
</dict>
</plist>
Loading