Photo par Ant Rozetsky

Comment réutiliser votre pipeline Azure DevOps entre vos projets

Arrêtez de dupliquer vos configurations de builds, réutilisez-les!

Créé par Damien Aicheh le 21/11/2019 · 14 mins

Lorsque vous réalisez un projet pour une entreprise, vous travaillez toujours avec plusieurs environnements, par exemple: intégration, pré-production et production. Le nombre d’environnements dépend souvent des besoins de votre client et de vos habitudes, chacun ayant un grand nombreuses de propriétés.

Lorsque vous configurez l’intégration continue et le déploiement continue, vous le faites pour chaque environnement. Les jobs et les tâches que vous avez utilisées seront exactement les mêmes à chaque fois, les seules choses qui changent sont les variables spécifiques à chaque environnement.

En tant que bon développeur, vous ne voudriez probablement pas dupliquer vos configurations et ensuite devoir les maintenir. Il sera répétitif et source d’erreurs d’ajouter ou de supprimer une configuration pour chaque environnement. La question qui se pose maintenant est la suivante: pouvez-vous réutiliser votre pipeline Azure pour vos projets? La réponse est OUI!

Dans ce tutoriel, je vais vous montrer un exemple simple basé sur la configuration de l’un de mes précédents tutoriels: Comment builder, signer et déployer votre application Xamarin.Android avec Azure DevOps et App Center . Ce sera un bon point de départ pour vous montrer ce qui peut être fait avec Azure DevOps.

Vue d’ensemble

Ici nous avons le précédent azure-pipeline.yml que nous allons utiliser comme base pour ce tutoriel:

trigger:
- master

pool:
  vmImage: 'macos-latest'

variables:
  - group: android-pipeline

steps:

- task: NuGetToolInstaller@1

- task: NuGetCommand@2
  inputs:
    restoreSolution: '**/*.sln'

- task: XamarinAndroid@1
  inputs:
    projectFile: '**/**.csproj'
    outputDirectory: '$(outputDirectory)'
    configuration: '$(buildConfiguration)'

- task: AndroidSigning@3
  inputs:
    apkFiles: '**/*.apk' 
    apksign: true
    apksignerKeystoreFile: 'production.jks'
    apksignerKeystorePassword: $(keystore.password)
    apksignerKeystoreAlias: $(key.alias)
    apksignerKeyPassword: $(key.password)
    apksignerArguments: --out $(outputDirectory)/app.release.apk
    zipalign: true

- task: AppCenterDistribute@3
  inputs:
    serverEndpoint: 'VSAC'
    appSlug: '$(appSlug)'
    appFile: '$(outputDirectory)/app.release.apk'
    releaseNotesOption: 'input'
    releaseNotesInput: 'New version'
    destinationType: 'groups'
    distributionGroupId: '$(distributionGroupId)'

Dans cette définition de pipeline, nous pouvons voir que, selon l’environnement que vous avez décidé d’utiliser, la signature de l’apk sera effectuée avec un certificat différent et votre application sera déployé à un groupe de distribution différent sur [App Center].

Nous pouvons scinder ce script en deux parties: une pour le build et une pour la distribution de votre application. Nous pouvons également imaginer une troisième partie pouvant être les tests exécutés avec les tests unitaires et les tests d’interface utilisateur.

Donc, vous auriez 3 étapes (stages):

  • Lancer les tests
  • Lancer le build
  • Distribuez votre application

Pour les besoins de ce tutoriel, je vais uniquement vous montrer les étapes de build et de distribution. Nous aurons deux environnements à gérer: la pré-production et la production.

Divisé en étapes

Chaque étape que nous avons définie précédemment est appelé stage dans Azure DevOps. Créons donc un nouveau fichier azure-pipeline.yml et définissons nos étapes comme suit:

trigger:
- master

pool:
  vmImage: 'macos-latest'

stages:
  - stage: Build
    displayName: Build stage

  - stage: Distribute
    displayName: Distribute stage

Définir nos jobs réutilisables

Maintenant, créons un nouveau dossier à côté de votre azure-pipeline.yml et appelons-le templates. Ce dossier contiendra tous les templates réutilisables que vous pourrez ensuite partager entre vos projets.

Dans ce dossier, créez deux fichiers:

  • xamarin-android-build.yml
  • xamarin-android-distribute.yml

Comme leur nom l’indique, le premier aura les tâches de build et le second les tâches de distribution.

Dans le fichier xamarin-android-build.yml, ajoutons la section de build de notre précédent azure-pipeline.yml comme ceci:


parameters:
  env: 'NOT_DEFINE'
  buildConfiguration: 'NOT_DEFINE'
  outputDirectory: 'NOT_DEFINE'
  keystoreFile: 'NOT_DEFINE'
  keystorePassword: 'NOT_DEFINE'
  keystoreAlias: 'NOT_DEFINE'
  keyPassword: 'NOT_DEFINE'

jobs:
  - job:
    displayName: Build ${{ parameters.env }}.
    steps:
        - task: NuGetToolInstaller@1

        - task: NuGetCommand@2
          inputs:
            restoreSolution: '**/*.sln'

        - task: XamarinAndroid@1
          inputs:
            projectFile: '**/**.csproj'
            outputDirectory: ${{ parameters.outputDirectory }}
            configuration: ${{ parameters.buildConfiguration }}
        
        - task: AndroidSigning@3
          inputs:
            apkFiles: '**/*.apk' 
            apksign: true
            apksignerKeystoreFile: ${{ parameters.keystoreFile }}
            apksignerKeystorePassword: ${{ parameters.keystorePassword }}
            apksignerKeystoreAlias: ${{ parameters.keystoreAlias }}
            apksignerKeyPassword: ${{ parameters.keyPassword }}
            apksignerArguments: --out ${{ parameters.outputDirectory }}/app.${{ parameters.env }}.release.apk
            zipalign: true
        
        - task: PublishPipelineArtifact@1
          inputs:
            targetPath: '${{ parameters.outputDirectory}}/app.${{ parameters.env }}.release.apk'
            artifactName: 'build_${{ parameters.env}}'

Comme vous pouvez le voir en haut de ce fichier, nous définissons tous les paramètres qui dépendent de l’environnement. Puis nous les utilisons directement avec la syntaxe suivante:


key: ${{parameters.YOUR_PARAMETER_HERE}}

Chaque paramètre est défini avec une valeur par défaut égale à NOT_DEFINE par conséquent, si votre build échoue et que cette valeur apparaît dans les journaux, vous saurez que vous avez oublié de spécifier une valeur. Vous pouvez aussi faire quelque chose de plus précis comme KEY_PASSWORD_NOT_DEFINE comme valeur par défaut pour keyPassword par exemple.

À la fin de ce fichier xamarin-android-build.yml, vous pouvez voir une nouvelle tâche appelée PublishPipelineArtifact@1. Cette tâche nous permet de sauvegarder le package généré entre les étapes. Nous allons donc garder notre apk généré entre l’étape de build et l’étape de distribution pour chaque environnement. Ceci est utile car chaque étape est exécutée une par une, vous devez donc conserver le résultat entre chaqu’une.

Ceci fait, passons au fichier xamarin-android-distributions.yml et ajoutons cette configuration:


parameters:
  env: 'NOT_DEFINE'
  outputDirectory: 'NOT_DEFINE'
  appSlug: 'NOT_DEFINE'
  distributionGroupId: 'NOT_DEFINE'
  serverEndpoint: 'VSAC'

jobs:
  - job:
    displayName: Distribute ${{ parameters.env }}.
    steps:
    - task: DownloadPipelineArtifact@2
      inputs:
        artifactName: 'build_${{ parameters.env}}'
        targetPath: ${{ parameters.outputDirectory }} 

    - task: CopyFiles@2
      inputs:
        sourceFolder: ${{ parameters.outputDirectory }} 
        contents: '**/app.${{ parameters.env }}.release.apk'
        TargetFolder: '$(Build.ArtifactStagingDirectory)/${{ parameters.env }}'

    - task: PublishBuildArtifacts@1
      inputs:
        pathtoPublish: '$(Build.ArtifactStagingDirectory)'
        artifactName: 'apks'
        publishLocation: 'container'

    - task: AppCenterDistribute@3
      inputs:
        serverEndpoint: ${{ parameters.serverEndpoint }}
        appSlug: ${{ parameters.appSlug }}
        appFile: '${{ parameters.outputDirectory }}/app.${{ parameters.env }}.release.apk'
        releaseNotesOption: 'input'
        releaseNotesInput: 'New version'
        destinationType: 'groups'
        distributionGroupId: ${{ parameters.distributionGroupId }}

Tout d’abord, nous téléchargeons notre artéfact (ce que nous avions précédemment publié dans le fichier xamarin-android-build.yml) afin que nous puissions commencer à le distribuer. Ensuite, pour vous donner un exemple plus intéressant en plus de la tâche AppCenterDistribute@3, j’ai ajouté les tâches CopyFiles@2 et PublishBuildArtifacts@1 pour vous montrer que nous pouvons publier nos artéfacts sur un serveur spécifique, par exemple. Dans cet exemple, il s’agit simplement de regrouper tous les fichiers apk dans un dossier d’artéfacts appelé apks dans le pipeline Azure DevOps, comme suit:

Artifacts

Pour envoyer les artefacts générés à un serveur spécifique, vous devez spécifier les propriétés publishLocation et targetPath de la tâche PublishBuildArtifacts@1.

Puis, en utilisant AppCenterDistribute@3, votre application sera automatiquement publiée dans App Center. Par défaut, mon serverEndpoint est toujours appelé VSAC, n’hésitez pas à l’écraser à votre guise.

Lorsque vous êtes satisfait de vos différents templates, je vous recommande de les placer tous dans un référentiel spécifique afin qu’il soit réutilisable depuis n’importe lequel de vos projets.

Utilisez les templates

Avec nos templates prêts, il est temps de les utiliser en passant nos paramètres en fonction de l’environnement. Avant tout, n’oubliez pas de télécharger vos certificats et de charger votre groupe de variables. Si vous n’êtes pas familier avec cela, vous pouvez consulter mon précédent tutoriel à ce sujet.

Pour ce tutoriel, le groupe de variables s’appellera android-pipeline et ressemblera à ceci:

Variables group

Chargez-le avant vos stages:

variables:
  - group: android-pipeline

Définissons nos deux environnements pour les étapes de build et de distribution comme ceci:

stages:
  - stage: Build
    displayName: Build stage
    jobs:
      - template: templates/xamarin-android-build.yml
        parameters:
          env: 'preproduction'
          buildConfiguration: '$(buildConfiguration)'
          outputDirectory: '$(outputDirectory)'
          keystoreFile: 'pre-production.jks'
          keystorePassword: $(keystore.preproduction.password)
          keystoreAlias: $(key.preproduction.alias)
          keyPassword: $(key.preproduction.password)

      - template: templates/xamarin-android-build.yml
        parameters:
          env: 'production'
          buildConfiguration: '$(buildConfiguration)'
          outputDirectory: '$(outputDirectory)'
          keystoreFile: 'production.jks'
          keystorePassword: $(keystore.password)
          keystoreAlias: $(key.alias)
          keyPassword: $(key.password)

  - stage: Distribute
    displayName: Distribute stage
    jobs:
      - template: templates/xamarin-android-distribute.yml
        parameters:
          env: 'preproduction'
          outputDirectory: '$(outputDirectory)'
          appSlug: '$(appSlug)'
          distributionGroupId: '$(distributionGroupId.PreProduction)'
          
      - template: templates/xamarin-android-distribute.yml
        parameters:
          env: 'production'
          outputDirectory: '$(outputDirectory)'
          appSlug: '$(appSlug)'
          distributionGroupId: '$(distributionGroupId.Production)'

Comme vous pouvez le constater, nous spécifions le template dont nous avons besoin et lui transmettons les paramètres.

Si vous exécutez ceci à l’intérieur de Azure DevOps, vous verrez quelque chose comme ceci:

Pipeline result

Votre application sera également déployée dans App Center.

Touche finale

Ceci est une introduction simple avec un exemple concret pour vous montrer tous les avantages de cette méthode:

  • Réutilisez votre pipeline Azure entre vos projets
  • Facile à maintenir
  • Facile à mettre à jour
  • Capitaliser entre chaque projet

Nous pouvons faire beaucoup plus d’optimisations avec toutes les possibilités offertes par Azure DevOps. Je vous encourage à essayer par vous-même!

Sources:

Vous trouverez un exemple de code sur ce répertoire Github.

Happy codding !

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 !