Using Azure pipelines, I am trying to build/deploy a Xamarin iOS app that depends on a push notification iOS service extension developed by Airship.
To build an iOS app and a related iOS service extension requires multiple provisioning profiles, one for the app and another for the service extension.
The Azure pipeline build fails, complaining that the service extension provisioning profile does not match the apps provisioning profile.
How do I get an Azure pipeline build to succeed when multiple provisioning profiles are required?
In case it matters, the app is developed using Visual Studio for Mac. I am able to build and run it on a physical test device.
Update #1:
This is the error message we get in the Azure pipeline console. This message is output when trying to build the service extension.
Info.plist : error : Project bundle identifier 'com.orgname.ServiceExtension' does not match specified provisioning profile 'xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx'
(The xxxxx's represent our apps provisioning profile uuid)
Reviewing the Azure code, it looks like there is no way to tell the build step to use multiple provisioning profiles. If I am understanding the code correctly, it looks like the Xamarin iOS Azure pipeline task can only handle a single provProfileUuid
value.
Update #2 - The .yml files:
azure-pipelines-prod.yml
variables:
productName: 'OurAppName'
projectBuildNumber: $[counter('', 800)]
versionName: $[format('{0:yyyy}.{0:MM}.{0:dd}', pipeline.startTime)]
name: $(versionName) ($(projectBuildNumber))
jobs:
- template: pipelines/build-ios.yml
parameters:
productionBuild: true
provisioningProfile: 'match_AppStore_com_orgname_appname.mobileprovision'
serviceExtensionProvisioningProfile: 'orgname_appname_service_ext.mobileprovision'
azure-pipeline-build-ios.yml
parameters:
- name: productionBuild
type: boolean
default: false
- name: provisioningProfile
displayName: 'Provisioning Profile'
type: string
- name: serviceExtensionProvisioningProfile
displayName: 'Provisioning Profile for App Extension'
type: string
jobs:
- job: iOS
pool:
vmImage: 'macOS-latest'
variables:
iosRoot: '$(system.defaultworkingdirectory)/$(productName).iOS'
iosProjectPath: '$(iosRoot)/*iOS.csproj'
NUGET_PACKAGES: $(Pipeline.Workspace)/.nuget/packages
buildConfiguration: 'Release'
outputDirectory: '$(build.binariesDirectory)/$(buildConfiguration)'
infoPlist: '$(iosRoot)/Info.plist'
# if not a production build, add the QA build symbol for compiler switches
${{ if eq(parameters.productionBuild, false) }}:
buildArgs: '/p:DefineConstants="QA"'
${{ if eq(parameters.productionBuild, true) }}:
buildArgs: ''
steps:
# restore and run unit tests
- template: unit-tests.yml
# Set build number and version Name
- task: ios-bundle-version@1
displayName: 'Set build number and version Name'
inputs:
sourcePath: '$(infoPlist)'
versionCodeOption: 'buildid'
versionCode: '$(projectBuildNumber)'
versionName: '$(versionName)'
printFile: true
# Install signing certificate
- task: InstallAppleCertificate@2
displayName: 'Install signing certificate'
inputs:
certSecureFile: 'AppleSigningCertificate.p12'
certPwd: '$(ios-cert-key)'
# Install Provisioning Profile
- task: InstallAppleProvisioningProfile@1
displayName: 'Install Provisioning Profile'
inputs:
provProfileSecureFile: ${{ parameters.provisioningProfile }}
# Install App Extension Provisioning Profile
- task: InstallAppleProvisioningProfile@1
displayName: 'Install Provisioning Profile for App Extension'
inputs:
provProfileSecureFile: ${{ parameters.serviceExtensionProvisioningProfile }}
# Build iOS ipa package
- task: XamariniOS@2
displayName: Build iOS ipa package
inputs:
solutionFile: '$(iosProjectPath)'
configuration: '$(buildConfiguration)'
buildForSimulator: false
packageApp: true
signingIdentity: '$(APPLE_CERTIFICATE_SIGNING_IDENTITY)'
signingProvisioningProfileID: '$(APPLE_PROV_PROFILE_UUID)'
args: /p:IpaPackageDir="$(Build.ArtifactStagingDirectory)" /p:OutputPath="$(outputDirectory)/iPhone/" /p:RestorePackages=false $(buildArgs)"
# Archive dSYM
- task: ArchiveFiles@2
displayName: 'Archive dSYM'
inputs:
rootFolderOrFile: '$(outputDirectory)/iPhone/$(productName).iOS.app.dSYM'
includeRootFolder: false
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/$(productName).iOS.app.dSYM.zip'
replaceExistingArchive: true
# on non-prod builds, make a simulator build
- ${{ if eq(parameters.productionBuild, false) }}:
# Create simulator build
- task: XamariniOS@2
displayName: 'Create simulator build'
inputs:
solutionFile: '$(iosProjectPath)'
configuration: '$(buildConfiguration)'
buildForSimulator: true
packageApp: false
args: /p:OutputPath="$(outputDirectory)/iPhoneSimulator/" /p:RestorePackages=false $(buildArgs)
# Archive Simulator Build
- task: ArchiveFiles@2
displayName: 'Archive Simulator Build'
inputs:
rootFolderOrFile: '$(outputDirectory)/iPhoneSimulator/$(productName).iOS.app'
includeRootFolder: false
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/$(productName).iOS.app.zip'
replaceExistingArchive: true
# Archive Simulator dSYM
- task: ArchiveFiles@2
displayName: 'Archive Simulator dSYM'
inputs:
rootFolderOrFile: '$(outputDirectory)/iPhoneSimulator/$(productName).iOS.app.dSYM'
includeRootFolder: false
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/$(productName).iOS.app.Simulator.dSYM.zip'
replaceExistingArchive: true
# publish artifacts to pipeline
- task: PublishBuildArtifacts@1
displayName: 'publish artifacts to pipeline'
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'drop'
publishLocation: 'Container'