diff --git a/.github/autobuild/ios.sh b/.github/autobuild/ios.sh index 3d96df84d3..1c01e1c94d 100755 --- a/.github/autobuild/ios.sh +++ b/.github/autobuild/ios.sh @@ -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 + + # 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 + + # 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" 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}" + 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 diff --git a/.github/workflows/autobuild.yml b/.github/workflows/autobuild.yml index c88bd22812..66e863dfb4 100644 --- a/.github/workflows/autobuild.yml +++ b/.github/workflows/autobuild.yml @@ -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 # 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 @@ -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 }} diff --git a/ios/Info-make.plist b/ios/Info-make.plist new file mode 100644 index 0000000000..3b6a6c6f13 --- /dev/null +++ b/ios/Info-make.plist @@ -0,0 +1,57 @@ + + + + + CFBundleExecutable + @EXECUTABLE@ + CFBundleIdentifier + @BUNDLEIDENTIFIER@ + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + @EXECUTABLE@ + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSMicrophoneUsageDescription + We need access to your microphone to let others hear you. + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UIApplicationSupportsIndirectInputEvents + + UIBackgroundModes + + audio + + UILaunchScreen + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + + diff --git a/ios/Info-xcode.plist b/ios/Info-xcode.plist new file mode 100644 index 0000000000..fc10eac30e --- /dev/null +++ b/ios/Info-xcode.plist @@ -0,0 +1,59 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSMicrophoneUsageDescription + We need access to your microphone to let others hear you. + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UIApplicationSupportsIndirectInputEvents + + UIBackgroundModes + + audio + + UILaunchScreen + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + + diff --git a/ios/deploy_ios.sh b/ios/deploy_ios.sh index c5c150b17d..3e0afa489c 100755 --- a/ios/deploy_ios.sh +++ b/ios/deploy_ios.sh @@ -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 \"\"" + fi + ;; + k) + keychain_pass=$OPTARG + if [[ -z "$keychain_pass" ]]; then + echo "Please add keychain password to use: -k \"\"" + fi + ;; + h) + echo "Usage: -s 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 \ No newline at end of file diff --git a/ios/exportOptionsRelease.plist b/ios/exportOptionsRelease.plist new file mode 100644 index 0000000000..0cafc0a921 --- /dev/null +++ b/ios/exportOptionsRelease.plist @@ -0,0 +1,15 @@ + + + + + method + app-store + teamID + XXXXXXXXXX + provisioningProfiles + + io.jamulus.Jamulus + UUID_GOES_HERE + + + \ No newline at end of file