Contents

We can find a lot of tutorials about how to setup Android CI/CD. But it’s hard for us to setup the iOS CI/CD. Also It’s expensive to buy a Mac computer to run and build iOS app. As we can find Azure Devops provide MacOS build agent, we can build and publish the iOS app on Azure devops for a lot cheaper prices.

Here is a Pipeline example of building iOS app on Azure Devops:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
pool:
vmImage: 'macos-latest'

steps:
- task: NodeTool@0
inputs:
versionSpec: '10.16.0'
displayName: 'Install Node.js 10.16.0'
- script: npm install
displayName: 'npm install'
- script: npm run lint
displayName: 'Run lint'
# iOS build
- task: InstallAppleCertificate@2
displayName: 'Install Apple distribution certificate'
inputs:
certSecureFile: 'IOS_RELEASE_CERTIFICATE.p12'
certPwd: $(IOS_RELEASE_P12_PASSWORD)
- task: InstallAppleProvisioningProfile@1
displayName: 'Install distribution provisional profile'
inputs:
provProfileSecureFile: 'IOS_RELEASE_PROVISION_PROFILE.mobileprovision'
- script: |
cd ios
pod install
cd ..
displayName: 'Install pod'
- script: |
npm install -g react-native-cli
react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ios/main.jsbundle --assets-dest ios
displayName: 'Bundle ios'
- task: Xcode@5
displayName: 'Build IPA for Release'
inputs:
actions: 'clean archive'
scheme: 'IOS_SCHEME_FOUND_IN_XCODE'
sdk: 'iphoneos'
configuration: 'Release'
xcWorkspacePath: 'IOS_WORKSPACE_FILE.xcworkspace'
xcodeVersion: 'default' # Options: 8, 9, 10, default, specifyPath
packageApp: true
signingOption: 'manual' # Options: nosign, default, manual, auto
useXcpretty: true # Makes it easier to diagnose build failures
teamId: $(IOS_TEAM_ID)
provisioningProfileUuid: $(IOS_RELEASE_PROVISIONING_PROFILE_UUID)
provisioningProfileName: 'THE_PROVISIONING_PROFILE_NAME_IN_APP_STORE'
archivePath: ios/build/Products/$(IOS_PROJECT_NAME).xcarchive
exportMethod: app-store
exportPath: ios/IPA/distribution
exportOptions: 'plist'
exportOptionsPlist: scripts/ios/exportOptions/exportOptions-dist.plist
- task: PublishPipelineArtifact@1
inputs:
path: $(System.DefaultWorkingDirectory)/ios/IPA
artifact: YOUR_APP_IOS

Here is a Pipeline example of building Android app on Azure Devops:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
pool:
vmImage: 'macos-latest'

steps:
- task: NodeTool@0
inputs:
versionSpec: '10.16.0'
displayName: 'Install Node.js 10.16.0'
- script: npm install
displayName: 'npm install'
- script: npm run lint
displayName: 'Run lint'
# - script: npm run test
# displayName: 'Run unit tests'
# Android build
- task: DownloadSecureFile@1
name: android_keystore
displayName: 'Download Android keystore'
inputs:
secureFile: 'KEYSTORE_FILE.jks'
- script: |
cd android
./gradlew clean assembleRelease -PBUILD_NAME=$BUILD_NAME -PBUILD_NUMBER=$ANDROID_BUILD_NUMBER -PANDROID_APP_ID=$ANDROID_APP_ID -PMYAPP_RELEASE_STORE_FILE=$(android_keystore.secureFilePath) -PMYAPP_RELEASE_KEY_ALIAS=$ANDROID_KEY_ALIAS -PMYAPP_RELEASE_STORE_PASSWORD=$ANDROID_KEYSTORE_PASSWORD -PMYAPP_RELEASE_KEY_PASSWORD=$ANDROID_KEY_PASSWORD
./gradlew bundleRelease -PBUILD_NAME=$BUILD_NAME -PBUILD_NUMBER=$ANDROID_BUILD_NUMBER -PANDROID_APP_ID=$ANDROID_APP_ID -PMYAPP_RELEASE_STORE_FILE=$(android_keystore.secureFilePath) -PMYAPP_RELEASE_KEY_ALIAS=$ANDROID_KEY_ALIAS -PMYAPP_RELEASE_STORE_PASSWORD=$ANDROID_KEYSTORE_PASSWORD -PMYAPP_RELEASE_KEY_PASSWORD=$ANDROID_KEY_PASSWORD
cd ..
mkdir android_outputs
cp -R android/app/build/outputs ./android_outputs
sed 's/enableSeparateBuildPerCPUArchitecture\ =\ true/enableSeparateBuildPerCPUArchitecture\ =\ false/g' android/app/build.gradle > ./build.gradle
rm android/app/build.gradle
cp ./build.gradle android/app/build.gradle
cd android
./gradlew clean assembleRelease -PBUILD_NAME=$BUILD_NAME -PBUILD_NUMBER=$ANDROID_BUILD_NUMBER -PANDROID_APP_ID=$ANDROID_APP_ID -PMYAPP_RELEASE_STORE_FILE=$(android_keystore.secureFilePath) -PMYAPP_RELEASE_KEY_ALIAS=$ANDROID_KEY_ALIAS -PMYAPP_RELEASE_STORE_PASSWORD=$ANDROID_KEYSTORE_PASSWORD -PMYAPP_RELEASE_KEY_PASSWORD=$ANDROID_KEY_PASSWORD
cd ..
cp android/app/build/outputs/apk/release/app-release.apk ./android_outputs/outputs/apk/release/app-release.apk
displayName: 'Building Android APK and packing distribution bundle'
- task: PublishPipelineArtifact@1
inputs:
path: $(System.DefaultWorkingDirectory)/android_outputs/outputs
artifact: ANDROID_ARTIFACT
Contents