Photo par Ryan Hutton

Ajoutez des nightly builds à vos applications Xamarin avec Azure DevOps

Builder votre code régulièrement

Posted by Damien Aicheh on March 05, 2020 · 16 mins

Lorsque vous développez une application mobile, vous travaillez souvent en équipe; chaque membre de l’équipe code une fonctionnalité et la propose par le système de Pull Request aux autres développeurs. Lorsqu’un ou deux développeurs de votre équipe la valideront, votre code sera fusionné avec le reste du code.

Pour être sûr que votre projet compile toujours, il est recommandé de le builder régulièrement dans un environnement de référence, ce qui, dans notre cas est Azure DevOps. Pour ce faire, une façon consiste à builder votre projet chaque jour ouvrable à minuit.

Dans ce tutoriel, je vais vous montrer comment configurer une nightly builds en utilisant une application Xamarin.Forms.

Dans mon précédent tutoriel nous avons vu comment exécuter et publier des tests unitaires dans Azure DevOps. Ce tutoriel fait partie d’une série complète de tutoriels Azure DevOps utilisant une application Xamarin.Forms en tant qu’exemple. Ce tutoriel peut être lu indépendamment et les concepts peuvent être appliqués à n’importe quelle technologie mobile que vous utilisez.

Configurez votre pipeline

Passons à Azure DevOps et créons un nouveau pipeline.

Dans l’onglet Pipelines, créez un New pipeline et suivez les 4 étapes pour obtenir le code source à partir du repository approprié. Azure DevOps créera automatiquement un nouveau fichier azure-pipelines.yml à la racine du dossier de votre projet. C’est à cet endroit que la définition du job sera définie et ensuite interprétée par Azure DevOps.

Si vous avez fait le tutoriel précédent, vous disposez déjà d’un modèle appelé init_restore.yml pour restaurer les packages Nuget de votre projet dans un dossier templates à la racine de votre projet de solution qui contient ces lignes:


parameters:
  solutionPath: ''

steps:
- task: NuGetToolInstaller@1
- task: NuGetCommand@2
  inputs:
    restoreSolution: '${{ parameters.solutionPath }}'

Structure

Nous utiliserons quelque chose appelé stages qui nous permet d’exécuter différents travaux sur des agents distincts. Ci-dessous, nous avons défini une liste de stages, un pour iOS et un pour Android. Dans chacun, nous restaurons nos packages Nuges en utilisant le modèle que nous avons créé précédemment.

trigger:
  - master

pool:
  vmImage: 'macOS-10.14'

stages:
  - stage: Build_Xamarin_Android
    dependsOn: []
    jobs:
      - job:
        displayName: 'Build Xamarin.Android'
        workspace:
          clean: all
        steps:
          - template: templates/init_restore.yml
            parameters:
              solutionPath: '$(solutionPath)'
          
          ## Steps to build your Xamarin Android application

  - stage: Build_Xamarin_iOS
    dependsOn: []
    jobs:
      - job:
        displayName: 'Build Xamarin.iOS'
        workspace:
          clean: all
        steps:
          - template: templates/init_restore.yml
            parameters:
              solutionPath: '$(solutionPath)'

          ## Steps to build your Xamarin Android application

Par défaut, chaque stage dépend de la précédente, ils démarrent donc un par un. Pour éviter que vos stage ne dépendent les uns des autres, il vous suffit de définir la propriété dependsOn à un tableau vide comme ci-dessus. Si vous avez upgradé votre abonnement Azure DevOps, vous pourrez exécuter plusieurs travaux en parallèle. Il créera donc votre application iOS et Android en même temps. Pour vérifier cela, allez dans Project settings > Parallel jobs et vous verrez le nombre de travaux en parallèles dont vous disposez.

Steps de builds

Il est temps d’ajouter les tasks de builds et de signature en fonction de ce que je vous ai montré dans les tutoriels précédents.

Configuration des variables

Créons un nouveau fichier appelé variables.yml dans notre dossier templates. Il contiendra toutes les variables du pipeline. En les séparant dans un fichier spécifique, il sera plus facile à maintenir.

Voici les variables dont nous avons besoin à ce stade pour pouvoir builder notre projet:

variables:
  - name: xamarinSdkVersion
    value: '6_6_0'
  - name: solutionPath
    value: '**/*.sln'
  - name: buildConfiguration
    value: 'Release'

La dernière version actuelle du SDK Xamarin est la version 6.6.0, c’est pourquoi nous avons défini la valeur du xamarinSdkVersion à 6_6_0. Cela nous permet également d’avoir une nouvelle fonctionnalité pour Xamarin.Android: les Android App Bundle !

Pour des raisons de sécurité, nous devrons créer un groupe de variables pour stocker l’alias et les mots de passe associés au keystore et au provisioning profile pour signer notre application. Si vous n’êtes pas familier avec cela, vous pouvez consulter mon précédent tutoriel à ce sujet.

Pour ce tutoriel, les groupes de variables seront appelés xamarin-full-pipeline, ils contiennent tous les mots de passe du keystore et du provisioning profile. Il ressemblera à ceci:

Variables group

N’oubliez pas d’uploader votre provisioning profile et votre keystore dans les fichiers sécurisés pour pouvoir signer vos applications.

Avant vos steps, chargeons le groupe de variables et le variables.yml comme ceci:

variables:
  - group: xamarin-full-pipeline
  - template: templates/variables.yml

Xamarin.Android

Pour Android, créons un nouveau modèle appelé build_xamarin_android.yml dans le dossier templates. Ce template définira une version personnalisée pour le SDK Xamarin, buildera l’application Android et la signera:


parameters:
  xamarinSdkVersion: ''
  packageFormat: 'apk'
  projectFile: ''
  buildConfiguration: ''
  apksignerKeystoreFile: ''
  apksignerKeystorePassword: ''
  apksignerKeyPassword: ''

steps:
- script: sudo $AGENT_HOMEDIRECTORY/scripts/select-xamarin-sdk.sh ${{ parameters.xamarinSdkVersion }}
  displayName: 'Select the Xamarin SDK version'
  enabled: true

- task: DownloadSecureFile@1
  name: keyStore
  displayName: "Download keystore from secure files"
  inputs:
    secureFile: '${{ parameters.apksignerKeystoreFile }}'

- task: Bash@3
  displayName: "Download keystore from secure files"
  inputs:
    targetType: "inline"
    script: |
      msbuild -restore ${{ parameters.projectFile }} -t:SignAndroidPackage -p:AndroidPackageFormat=${{ parameters.packageFormat }} -p:Configuration=${{ parameters.buildConfiguration }} -p:AndroidKeyStore=True -p:AndroidSigningKeyStore=$(keyStore.secureFilePath) -p:AndroidSigningStorePass=${{ parameters.apksignerKeystorePassword }} -p:AndroidSigningKeyAlias=${{ parameters.apksignerKeystoreAlias }} -p:AndroidSigningKeyPass=${{ parameters.apksignerKeyPassword }}

Comme vous pouvez le voir, ce template nous permet de créer et de signer un apk ou aab (Android App Bundle), selon la valeur de la propriété packageFormat que vous choisissez: apk ou aab.

Si vous souhaitez comprendre en détail comment une application Xamarin.Android peut être créée à l’aide d’Azure DevOps, il est recommandé de lire les tutoriels précédents à ce sujet:

Maintenant, dans notre azure-pipelines.yml, nous pouvons utiliser ce modèle juste après la restauration des packages Nugets pour l’application Xamarin.Android:

- template: templates/build_xamarin_android.yml
  parameters:
    xamarinSdkVersion: '$(xamarinSdkVersion)'
    packageFormat: 'aab' # Choose apk or aab depending on your needs
    projectFile: '$(Build.SourcesDirectory)/XamarinDevOps.Android/*.csproj'
    buildConfiguration: '$(buildConfiguration)'
    apksignerKeystoreFile: 'production.jks'
    apksignerKeystorePassword: $(keystore.password)
    apksignerKeystoreAlias: $(key.alias)
    apksignerKeyPassword: $(key.password)

Xamarin.iOS

Pour iOS, créons un nouveau template appelé build_xamarin_ios_ipa.yml dans le dossier templates. Ce template ressemblera au précédent; définissez une version personnalisée pour le SDK Xamarin, buildez l’application iOS et la signer:


parameters:
  xamarinSdkVersion: ''
  p12FileName: ''
  p12Password: ''
  provisioningProfile: ''
  solutionPath: ''
  buildConfiguration: ''
  signingIdentity: ''
  signingProvisioningProfileID: ''

steps:
  - script: sudo $AGENT_HOMEDIRECTORY/scripts/select-xamarin-sdk.sh ${{ parameters.xamarinSdkVersion }}
    displayName: 'Select the Xamarin SDK version'
    enabled: true

  - task: InstallAppleCertificate@2
    inputs:
      certSecureFile: '${{ parameters.p12FileName }}'
      certPwd: '${{ parameters.p12Password }}'
      keychain: 'temp'
      deleteCert: true

  - task: InstallAppleProvisioningProfile@1
    inputs:
      provisioningProfileLocation: 'secureFiles'
      provProfileSecureFile: '${{ parameters.provisioningProfile }}'
      removeProfile: true
      
  - task: XamariniOS@2
    inputs:
      solutionFile: '${{ parameters.solutionPath }}'
      configuration: '${{ parameters.buildConfiguration }}'
      packageApp: true
      buildForSimulator: false
      runNugetRestore: false
      signingIdentity: '${{ parameters.signingIdentity }}'
      signingProvisioningProfileID: '${{ parameters.signingProvisioningProfileID }}'

Si vous voulez comprendre en détail ce que fait chaque task dans ce template, il est recommandé de lire mon précédent tutoriel à ce sujet.

Revenez à notre azure-pipelines.yml et appelez ce modèle juste après la restauration des packages Nugets pour l’application Xamarin.iOS:

- template: templates/build_xamarin_ios_ipa.yml
  parameters:
    xamarinSdkVersion: '$(xamarinSdkVersion)'
    p12FileName: '$(p12FileName)'
    p12Password: '$(p12Password)'
    provisioningProfile: '$(provisioningProfile)'
    solutionPath: '$(solutionPath)'
    buildConfiguration: '$(buildConfiguration)'
    signingIdentity: '$(APPLE_CERTIFICATE_SIGNING_IDENTITY)'
    signingProvisioningProfileID: '$(APPLE_PROV_PROFILE_UUID)'

Ajouter un cron

Prochaine étape, nous devons ajouter la possibilité à notre pipeline d’être démarré automatiquement chaque nuit pendant la semaine. Cela buildera notre application et lorsque vous retournerez au bureau le lendemain, il vous suffira de vérifier si tout c’est bien passé. Cela garantira que votre code se compile également en dehors de votre machine personnelle.

Pour ce faire, avant de définir les stages à l’intérieur de votre azure-pipeline.yml, ajoutez ces lignes:

schedules:
- cron: "0 0 * * 1-5"
  displayName: Daily midnight build
  branches:
    include:
    - master

La task cron fait fonctionner le pipeline du lundi au vendredi à minuit.

  • Le premier 0 correspond aux minutes de 0 à 59
  • Le second 0 correspond aux heures de 0 à 23
  • Le premier * correspond aux jours de 1 à 31
  • Le second * correspond au mois de 1 à 12
  • Le 1-5 correspond aux jours, 0 est le dimanche

Ajouter les Tests unitaires

Maintenant, basé sur le précédent tutoriel nous ajouterons un étape pour exécuter nos tests unitaires avant les étapes de builds de notre application iOS et Android:

- stage: Run_Unit_Tests
  jobs:
    - job:
      displayName: 'Run Unit Tests'
      steps:
        - template: templates/run_unit_tests.yml
          parameters:
            solutionPath: '$(solutionPath)'
            projects: '$(Build.SourcesDirectory)/XamarinDevOps.Tests/*.csproj'
            buildConfiguration: '$(buildConfiguration)'

Ensuite, pour exécuter les tests avant de builder votre application, définissez la valeur dependsOn du stage iOS et Android comme ceci:

dependsOn: Run_Unit_Tests

Si vous le faites correctement et exécutez votre pipeline, vous verrez quelque chose comme ceci:

Final stages

Touche finale

Vous avez maintenant une configuration de nightly build, qui garantit la réussite du build de votre projet et des templates réutilisables dans vos projets.

Et après ?

Dans le prochain tutoriel de cette série, nous nous concentrerons sur la gestion de votre application par environnement !

Sources:

Vous trouverez tout le code source sur ce répertoire Github sur la branche nightly_builds.

Happy coding!

Vous avez aimé ce tutoriel ? Laissez une étoile sur le répertoire Github associé !

N'hésitez pas à me suivre sur pour ne pas rater mon prochain tutoriel !