Skip to main content
Notice removed Authoritative reference needed by CommunityBot
Bounty Ended with no winning answer by CommunityBot
Notice added Authoritative reference needed by alcohol is evil
Bounty Started worth 50 reputation by alcohol is evil
fix version explanation
Source Link

Versions and short versions are the same for both platforms so that they follow the rules of both stores (with combined rules for both stores both versions must be numbers separated by dots and must be unique):

  • develop:
    • short version (version name) - must be numbers separated by dots and must be unique: {year}{month}{day}.{dayCounter}.0
    • version (version code): 2 * buildId
  • production (version code) - must be numbers separated by dots and must be unique:
    • short version (version name): {year}{month}{day}.{dayCounter}.1
    • version (version code): 2 * buildId + 1

Versions and short versions are the same for both platforms so that they follow the rules of both stores:

  • develop:
    • short version (version name) - must be numbers separated by dots and must be unique: {year}{month}{day}.{dayCounter}.0
    • version: 2 * buildId
  • production (version code) - must be numbers separated by dots and must be unique:
    • short version: {year}{month}{day}.{dayCounter}.1
    • version: 2 * buildId + 1

Versions and short versions are the same for both platforms so that they follow the rules of both stores (with combined rules for both stores both versions must be numbers separated by dots and must be unique):

  • develop:
    • short version (version name): {year}{month}{day}.{dayCounter}.0
    • version (version code): 2 * buildId
  • production:
    • short version (version name): {year}{month}{day}.{dayCounter}.1
    • version (version code): 2 * buildId + 1
version limitation for Android and iOS explained
Source Link
  • develop:
    • short version (version name) - must be numbers separated by dots and must be unique: {year}{month}{day}.{dayCounter}.0
    • version: 2 * buildId
  • production (version code) - must be numbers separated by dots and must be unique:
    • short version: {year}{month}{day}.{dayCounter}.1
    • version: 2 * buildId + 1

A tester is first testing a development version and when it works - they approve a production stage and then test it. Production stage does not have to be approved and run - it depends of changes on backend services and if they were already merged to main branches. When tests are passed - production version is promoted to production (development version is never promoted, it is only in intervalinternal tests phase).

  • develop:
    • short version: {year}{month}{day}.{dayCounter}.0
    • version: 2 * buildId
  • production:
    • short version: {year}{month}{day}.{dayCounter}.1
    • version: 2 * buildId + 1

A tester is first testing a development version and when it works - they approve a production stage and then test it. Production stage does not have to be approved and run - it depends of changes on backend services and if they were already merged to main branches. When tests are passed - production version is promoted to production (development version is never promoted, it is only in interval tests phase).

  • develop:
    • short version (version name) - must be numbers separated by dots and must be unique: {year}{month}{day}.{dayCounter}.0
    • version: 2 * buildId
  • production (version code) - must be numbers separated by dots and must be unique:
    • short version: {year}{month}{day}.{dayCounter}.1
    • version: 2 * buildId + 1

A tester is first testing a development version and when it works - they approve a production stage and then test it. Production stage does not have to be approved and run - it depends of changes on backend services and if they were already merged to main branches. When tests are passed - production version is promoted to production (development version is never promoted, it is only in internal tests phase).

Source Link

React Native (expo) mobile application publishing to stores in Azure DevOps pipeline using different backends for different app versions

Mobile development is quite new for me, and I would like to ask about a solution review. I'm not sure the way I publish the application is correct, I think good companies may do it differently.

I have two environments: development and production. For each environment there are different URLs for API services. I publish the application to App Store and to Google Play store using the Azure DevOps pipeline. Each environment version uses different .env.* file with configuration.

Versions and short versions are the same for both platforms so that they follow the rules of both stores:

  • develop:
    • short version: {year}{month}{day}.{dayCounter}.0
    • version: 2 * buildId
  • production:
    • short version: {year}{month}{day}.{dayCounter}.1
    • version: 2 * buildId + 1

The pipeline has two stages:

  • development build
  • production build - asking for a permission to run

A tester is first testing a development version and when it works - they approve a production stage and then test it. Production stage does not have to be approved and run - it depends of changes on backend services and if they were already merged to main branches. When tests are passed - production version is promoted to production (development version is never promoted, it is only in interval tests phase).

For each platform tester is using:

  • iOS: TestFlight to install a specific version of the app
  • Android: URL copied from Google Play Console for a specific version of the app

My pipeline YAML:

trigger:
  - main

variables:
  - group: mobileApps
  - name: version
    value: $[format('{0:yyyyMMdd}', pipeline.startTime)]
  - name: revision
    value: $[counter(variables['version'], 1)]
  - name: versionNameTest
    value: $[format('{0}.{1}.0', variables['version'], variables['revision'])]
  - name: versionNameProd
    value: $[format('{0}.{1}.1', variables['version'], variables['revision'])]
  - name: buildId
    value: $[variables['Build.BuildId']]  

stages:
  - stage:
    displayName: Test version
    jobs:
    - job: DisplayVersions
      displayName: Display versions
      steps:
        - script: |            
            echo versionName=$(versionNameTest)
            echo versionCode=$((2 * $(buildId)))
          displayName: Display versions

    - job: IOS
      displayName: iOS bundle release
      dependsOn: DisplayVersions
      pool:
        vmImage: "macos-latest"
      steps:
        - script: |
            /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $(versionNameTest)" ios/SomeFolder/Info.plist
            /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $((2 * $(buildId)))" ios/SomeFolder/Info.plist
            cat ios/SomeFolder/Info.plist
          displayName: Set versions in Info.plist
          
        - script: |
            npm install
          displayName: Install node modules

        - task: CocoaPods@0
          inputs:
            workingDirectory: 'ios/'
            forceRepoUpdate: false
          displayName: Install pods

        - task: InstallAppleCertificate@2
          inputs:
            certSecureFile: 'someName.p12' 
            # kv password not working, reading stars?
            certPwd: 'some password'
        
        - task: InstallAppleProvisioningProfile@1
          inputs:
            provProfileSecureFile: 'someName.mobileprovision'

        - script: |
            rm .env.production
          displayName: Use .env config

        - task: Xcode@5
          inputs:
            actions: 'build'
            scheme: 'SomeScheme'
            sdk: 'iphoneos'
            configuration: 'Release'
            xcWorkspacePath: 'ios/someName.xcworkspace'
            packageApp: true
            exportPath: '.'
            signingOption: 'manual'
            signingIdentity: '$(APPLE_CERTIFICATE_SIGNING_IDENTITY)'
            provisioningProfileUuid: '$(APPLE_PROV_PROFILE_UUID)'

        - task: CopyFiles@2
          inputs:
            contents: "**/*.ipa"
            targetFolder: "$(build.artifactStagingDirectory)"
          displayName: Copy iOS bundle to artifacts

        - task: PublishBuildArtifacts@1
          displayName: Publish artifacts

        - task: AppStoreRelease@1
          displayName: 'Publish to the App Store TestFlight track'
          inputs:
            serviceEndpoint: 'Apple App Store service connection'
            appIdentifier: com.some-name.some-name
            ipaPath: '$(build.artifactstagingdirectory)/**/*.ipa'
            shouldSkipWaitingForProcessing: true
            shouldSkipSubmission: true

    - job: Android
      displayName: Android bundle release
      dependsOn: DisplayVersions #IOS
      pool:
        vmImage: "ubuntu-latest"
      steps:
        - script: |
            npm install
          displayName: Install node modules

        - script: |
            keystorePath=$(pwd)/infrastructure/keystores/upload-keystore.jks
            cd android
            chmod +x gradlew

            ENVFILE=.env ./gradlew \
              -Pandroid.injected.signing.store.file="$keystorePath" \
              -Pandroid.injected.signing.store.password=$(java-key-store-password) \
              -Pandroid.injected.signing.key.alias=$(java-key-store-alias) \
              -Pandroid.injected.signing.key.password=$(java-key-store-password) \
              -PversionName=$(versionNameTest) \
              -PversionCode=$((2 * $(buildId))) \
              bundleRelease
          displayName: Build Android bundle

        - task: CopyFiles@2
          inputs:
            contents: "android/app/build/outputs/bundle/release/*.aab"
            targetFolder: "$(build.artifactStagingDirectory)"
          displayName: Copy Android bundle to artifacts

        - task: PublishBuildArtifacts@1
          displayName: Publish artifacts

        - task: GooglePlayRelease@4
          inputs:
            serviceConnection: "Some Service Account"
            applicationId: "com.some-name.some-name"
            action: "SingleBundle"
            bundleFile: "android/app/build/outputs/bundle/release/*.aab"
            track: "internal"
            releaseName: "$(versionNameTest)"
            changesNotSentForReview: true
          displayName: Send Android bundle to Google Play for internal testing

  - stage:
    displayName: Prod version
    jobs:
    - job: DisplayVersions
      displayName: Display versions
      steps:
        - script: |
            echo versionName=$(versionNameProd)
            echo versionCode=$((2 * $(buildId) + 1))
          displayName: Display versions

    - deployment: IOS
      displayName: iOS bundle release
      dependsOn: DisplayVersions
      pool:
        vmImage: "macos-latest"
      environment: SomeEnvironment
      strategy:
        runOnce:
          deploy:
            steps:
            - checkout: self

            - script: |
                /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $(versionNameProd)" ios/SomeFolder/Info.plist
                /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $((2 * $(buildId) + 1))" ios/SomeName/Info.plist
                cat ios/SomeFolder/Info.plist
              displayName: Set versions in Info.plist
          
            - script: |
                npm install
              displayName: Install node modules

            - task: CocoaPods@0
              inputs:
                workingDirectory: 'ios/'
                forceRepoUpdate: false
              displayName: Install pods

            - task: InstallAppleCertificate@2
              inputs:
                certSecureFile: 'someName.p12' 
                # kv password not working, reading stars?
                certPwd: 'some-password'
            
            - task: InstallAppleProvisioningProfile@1
              inputs:
                provProfileSecureFile: 'someName.mobileprovision'

            - script: |
                mv -f .env.production .env
              displayName: Use .env.production config

            - task: Xcode@5
              inputs:
                actions: 'build'
                scheme: 'SomeScheme'
                sdk: 'iphoneos'
                configuration: 'Release'
                xcWorkspacePath: 'ios/someName.xcworkspace'
                packageApp: true
                exportPath: '.'
                signingOption: 'manual'
                signingIdentity: '$(APPLE_CERTIFICATE_SIGNING_IDENTITY)'
                provisioningProfileUuid: '$(APPLE_PROV_PROFILE_UUID)'

            - task: CopyFiles@2
              inputs:
                contents: "**/*.ipa"
                targetFolder: "$(build.artifactStagingDirectory)"
              displayName: Copy iOS bundle to artifacts

            - task: PublishBuildArtifacts@1
              displayName: Publish artifacts

            - task: AppStoreRelease@1
              displayName: 'Publish to the App Store TestFlight track'
              inputs:
                serviceEndpoint: 'Apple App Store service connection'
                appIdentifier: com.some-name.some-name
                ipaPath: '$(build.artifactstagingdirectory)/**/*.ipa'
                shouldSkipWaitingForProcessing: true
                shouldSkipSubmission: true

    - deployment: Android
      displayName: Android bundle release
      dependsOn: DisplayVersions #IOS
      pool:
        vmImage: "ubuntu-latest"
      environment: SomeEnvironment
      strategy:
        runOnce:
          deploy:
            steps:
            - checkout: self

            - script: |
                npm install
              displayName: Install node modules

            - script: |
                keystorePath=$(pwd)/infrastructure/keystores/upload-keystore.jks
                cd android
                chmod +x gradlew

                ENVFILE=.env.production ./gradlew \
                  -Pandroid.injected.signing.store.file="$keystorePath" \
                  -Pandroid.injected.signing.store.password=$(java-key-store-password) \
                  -Pandroid.injected.signing.key.alias=$(java-key-store-alias) \
                  -Pandroid.injected.signing.key.password=$(java-key-store-password) \
                  -PversionName=$(versionNameProd) \
                  -PversionCode=$((2 * $(buildId) + 1)) \
                  bundleRelease
              displayName: Build Android bundle

            - task: CopyFiles@2
              inputs:
                contents: "android/app/build/outputs/bundle/release/*.aab"
                targetFolder: "$(build.artifactStagingDirectory)"
              displayName: Copy Android bundle to artifacts

            - task: PublishBuildArtifacts@1
              displayName: Publish artifacts

            - task: GooglePlayRelease@4
              inputs:
                serviceConnection: "Some Service Account"
                applicationId: "com.some-name.some-name"
                action: "SingleBundle"
                bundleFile: "android/app/build/outputs/bundle/release/*.aab"
                track: "internal"
                releaseName: "$(versionNameProd)"
                changesNotSentForReview: true
              displayName: Send Android bundle to Google Play for internal testing