{"id":5770,"date":"2021-01-29T15:54:29","date_gmt":"2021-01-29T14:54:29","guid":{"rendered":"https:\/\/qdraw.nl\/blog\/?p=5770"},"modified":"2022-03-04T10:20:25","modified_gmt":"2022-03-04T09:20:25","slug":"hoe-gebruik-je-templates-met-azure-pipelines","status":"publish","type":"post","link":"https:\/\/qdraw.nl\/blog\/technologie\/hoe-gebruik-je-templates-met-azure-pipelines\/","title":{"rendered":"Hoe gebruik je templates met Azure Pipelines"},"content":{"rendered":"<p>Azure Devops is een Microsoft dienst waar mee code projecten beschikbaar kunnen gesteld worden voor anderen. In deze technische blog geef ik een introductie hoe ik templates hergebruik. De Azure Pipelines werken vrijwel met elke programmeertaal of projecttype. Deze tool combineert Continuous Integration (CI) and Continuous Delivery (CD) om continue het project te compileren en testen. Dit is een methode voor het frequent leveren van applicaties naar (eind)klanten waarbij vrijwel alle stappen geautomatiseerd zijn. <\/p>\n<p>Met Azure Pipelines is het gebruikelijk om de build pipelines in yaml te definieren. Dit zijn tekst bestanden waarin alle stappen worden uitgeschreven. Deze yaml bestanden worden ingecheckt in het versie beheer systeem. Het voordeel van deze werkwijze is dat deze per branch verschillend kunnen zijn, zodat je veranderingen niet op een centraal moment moet doorvoeren.<\/p>\n<p>Vooral bij een complexer project is handig om verschillende onderdelen her te gebruiken voor bijvoorbeeld verschillende onderdelen. In het onderstaande project zijn een aantal onderdelen. Het is namelijk niet altijd nodig dat alles wordt gebouwd, vooral als je er geen veranderingen in heb doorgevoerd.<\/p>\n<p>Een belangrijk voordeel is om het op te splitsen is dat je verschillende templates kunt hergebruiken, dit voorkomt dubbele code en de naam van de bestanden geven al een overzicht van de taken die hierin worden uitgevoerd. <\/p>\n<p>In het project dat ik hier als voorbeeld gebruik hou ik de volgende verdeling aan:<\/p>\n<p>1. Server. De backend van de website, in ons geval is dat is het Sitecore platform<br \/>\n2. Client. Losse front-end frameworks die los staan van de backend.<br \/>\n3. Services. Losse Windows services, die achtergrond taken uitvoeren.<br \/>\n4. Develop CI. Dat is een combinatie van alle bovenstaande pipelines<\/p>\n<p><em><a rel=\"nofollow noopener\" target=\"_blank\" href=\"https:\/\/qdraw.medium.com\/an-introduction-to-azure-pipelines-and-the-reuse-of-templates-88b04677cb69\">See English version of this article on Medium<\/a><\/em><\/p>\n<p>Hieronder een schermafbeelding van hoe de pipeline inrichting eruit ziet. <\/p>\n<p><a href=\"https:\/\/media.qdraw.nl\/log\/een-introductie-van-azure-pipelines-en-het-hergebruik-van-templat\/1000\/azure-pipelines-en-het-hergebruik-van-templates_01_kl1k.jpg\" class=\"lightbox[blog]\"\n       title=\"Een introductie van Azure Pipelines en het hergebruik van templates - Pipeline screen Azure Devops | foto 1\"\n       data-gps=\"0,0\" data-heightratio=\"0.522\"><br \/>\n        <noscript><img decoding=\"async\" src=\"https:\/\/media.qdraw.nl\/log\/een-introductie-van-azure-pipelines-en-het-hergebruik-van-templat\/500\/azure-pipelines-en-het-hergebruik-van-templates_01_kl.jpg\" alt=\" Pipeline screen Azure Devops\" \/><\/noscript><br \/>\n\t\t<img decoding=\"async\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAIAAADwyuo0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAI0lEQVR42mN59PiJsJAgMzPzt1\/\/WH79\/MHIxPjnzx9uNhYAoLgL14Ue9qsAAAAASUVORK5CYII=\" title=\"Pipeline screen Azure Devops | foto 1\" \n\t\t     alt=\"Een introductie van Azure Pipelines en het hergebruik van templates ; Pipeline screen Azure Devops ; azure, devops, vsts, pipelines, build\" \/><br \/>\n    <\/a><\/p>\n<h2>Develop CI<\/h2>\n<p>Ik begin hier bij de Develop CI omdat deze de verschillende onderdelen allemaal bevat. In Azure Devops klik in het pipeline venster op \u2018New pipeline\u2019. Vervolgens selecteer ik de locatie van de repository. Dit kan zowel in Azure Devops als op Github zijn. Dan worden er een aantal opties voorgesteld. Ik begin hier met de starter pipeline en kies een locatie. Voor nu ga ik voor de Develop CI uit startpunt: \/azure-pipeline\/develop-ci.yml Wanneer je op \u2018Save and Run\u2019 klikt dan wordt deze ingecheckt. Vervolgens wordt de pipeline uitgevoerd.<\/p>\n<p><a href=\"https:\/\/media.qdraw.nl\/log\/een-introductie-van-azure-pipelines-en-het-hergebruik-van-templat\/1000\/azure-pipelines-en-het-hergebruik-van-templates_02_kl1k.jpg\" class=\"lightbox[blog]\"\n       title=\"Een introductie van Azure Pipelines en het hergebruik van templates - Review your pipeline Azure Devops | foto 2\"\n       data-gps=\"0,0\" data-heightratio=\"0.647\"><br \/>\n        <noscript><img decoding=\"async\" src=\"https:\/\/media.qdraw.nl\/log\/een-introductie-van-azure-pipelines-en-het-hergebruik-van-templat\/500\/azure-pipelines-en-het-hergebruik-van-templates_02_kl.jpg\" alt=\" Review your pipeline Azure Devops\" \/><\/noscript><br \/>\n\t\t<img decoding=\"async\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAMElEQVR42mN5\/OIZCysHLxfn229\/WXj5&#x2B;NhZWFlZmOU42Fn&#x2B;\/2P&#x2B;9ZuRjZmJgZERAOwjCkRfMBPHAAAAAElFTkSuQmCC\" title=\"Review your pipeline Azure Devops | foto 2\" \n\t\t     alt=\"Een introductie van Azure Pipelines en het hergebruik van templates ; Review your pipeline Azure Devops ; azure, devops, vsts, pipelines, build, yaml, starter kit\" \/><br \/>\n    <\/a><\/p>\n<h2>Parallel lopende machines met Stages<\/h2>\n<p>In het (bovenstaande) starter voorbeeld wordt maar 1 machine tegelijk gebruikt. Voor een groter project is het handiger als onderdelen parallel lopen. Hier zijn wel kosten aan verbonden.  Als je pipelines afgeschermd zijn en je gebruikt de gratis variant dan heb je 1 machine per organisatie beschikbaar met een maximum van 1800 minuten per maand. Dit kan veranderen maar dit is de huidige situatie van januari 2021.<\/p>\n<p>De stages in de yaml pipeline zijn bijwijze van spreken de verschillende boeken en jobs de hoofdstukken. De jobs worden op verschillende machines uitgevoerd. De laatste stap \u2018Merge\u2019 wacht voordat de Client en Server stages zijn afgerond. <\/p>\n<p>Hieronder de yaml pipeline voor \/azure-pipeline\/develop-ci.yml<\/p>\n<pre><code>\r\ntrigger: none\r\n\r\npr:\r\n- development\r\n\r\nvariables:\r\n  buildPlatform: \"Any CPU\"\r\n  buildConfiguration: \"Release\"\r\n  webroot: \"$(build.artifactstagingdirectory)\/wwwroot\"\r\n  serverPool: \"windows-latest\"\r\n  clientPool: \"ubuntu-20.04\"\r\n  serverSitecoreArtifact: \"server_sitecore\"\r\n  serverUnicornDataArtifact: \"server_unicorn_sitecore_data\"\r\n  serverConfigCollectionArtifact: \"server_config_collection\"\r\n  clientArtifact: \"client\"\r\n  mergeArtifact: \"merge\"\r\n  npm_config_cache: $(Pipeline.Workspace)\/.npm\r\n\r\nstages:\r\n  - stage: Server\r\n    dependsOn: []\r\n    jobs:\r\n      - template: \/azure-pipeline\/jobs\/server_sitecore.yml\r\n        parameters:\r\n          pool: \"$(serverPool)\"\r\n          artifact: \"$(serverSitecoreArtifact)\"\r\n\r\n      - template: \/azure-pipeline\/jobs\/server_config_collection.yml\r\n        parameters:\r\n          pool: \"$(serverPool)\"\r\n          artifact: \"$(serverConfigCollectionArtifact)\"\r\n          serverUnicornDataArtifact: \"$(serverUnicornDataArtifact)\"\r\n\r\n  - stage: Client\r\n    dependsOn: []\r\n    jobs:\r\n      - template: \/azure-pipeline\/jobs\/client.yml\r\n        parameters:\r\n          pool: \"$(clientPool)\"\r\n          artifact: \"$(clientArtifact)\"\r\n\r\n  - stage: Services\r\n    dependsOn: []\r\n    jobs:\r\n      - template: \/azure-pipeline\/jobs\/services.yml\r\n        parameters:\r\n          pool: \"$(serverPool)\"\r\n          solutions:\r\n            - key: service1\r\n              value: \"\/src\/service1.csproj\" \r\n\r\n  - stage: Merge\r\n    dependsOn:\r\n      - Server\r\n      - Client\r\n    jobs:\r\n      - template: \/azure-pipeline\/jobs\/merge.yml\r\n        parameters:\r\n          pool: \"$(serverPool)\"\r\n          artifact: \"$(collectionArtifact)\"\r\n          serverSitecoreArtifact: \"$(serverSitecoreArtifact)\"\r\n\t   clientArtifact: \"$(clientArtifact)\"\r\n          mergeArtifact: \"$(mergeArtifact)\"\r\n<\/code><\/pre>\n<h2>Loop door een lijst om verschillende services te bouwen<\/h2>\n<p>Wanneer je verder inzoomt op de Services taak. Deze staat hieronder uitgeschreven. De services yaml wordt als template aangeroepen in de \u2018Develop CI\u2019 pipeline  onder het kopje templates. De uitkomst is dat deze job meerdere artifacts maakt. Een artifact is de uitkomst van een build taak. Dit kan een mapje met dll\u2019s zijn of een mapje met css en javascript. <\/p>\n<p>In de \u2018Develop CI\u2019 yaml defineer ik de parameters. In de services yaml staan ook parameters, maar deze worden overschreven. Deze staan er alleen maar voor het geval de parameters niet wordt meegegeven.<\/p>\n<p>Hieronder de yaml pipeline voor <code>\/azure-pipeline\/jobs\/services.yml<\/code><\/p>\n<pre><code>\r\nparameters:\r\n  pool:\r\n    vmImage: windows-2019\r\n  nuGetVersion: \"5.2.0\"\r\n  solutionDir: '$(Build.SourcesDirectory)\/src\/'\r\n  slnFile: '$(Build.SourcesDirectory)\/src\/Project.sln'\r\n  job: ServicesRabbit\r\n  solutions: \r\n    - key: service1\r\n      value: \"\/src\/service1.csproj\" \r\n  BuildArguments: \"\/p:DeployOnBuild=True \/p:DeployDefaultTarget=WebPublish \/p:WebPublishMethod=FileSystem\"\r\n\r\njobs:\r\n  - job: ${{ parameters.job }}\r\n    displayName: \"Services \"\r\n    workspace:\r\n      clean: all\r\n    pool:\r\n      vmImage: ${{ parameters.pool }}\r\n      demands:\r\n        - msbuild\r\n    variables:\r\n      outputPath: '$(build.artifactstagingdirectory)\\is_over_written_default'\r\n    steps:\r\n      - checkout: self\r\n        clean: true\r\n        fetchDepth: 1\r\n\r\n      - template: \/azure-pipeline\/steps\/server_net_restore.yml\r\n        parameters:\r\n          solution: ${{ parameters.slnFile }}\r\n\r\n      - ${{ each item in parameters.solutions }}:\r\n\r\n        - task: PowerShell@2\r\n          enabled: true\r\n          displayName: \"[net] update output path\"\r\n          inputs:\r\n            targetType: 'inline'\r\n            script: |\r\n              $updateOutputPath = \"$(build.artifactstagingdirectory)\/${{ item.key }}\"\r\n              Write-Output \"##vso[task.setvariable variable=outputPath]$updateOutputPath\"\r\n\r\n        - task: VSBuild@1\r\n          displayName: \"[net] Build ${{ item.key }}\"\r\n          inputs:\r\n            solution: \"$(Build.SourcesDirectory)${{ item.value }}\"\r\n            vsVersion: \"16.0\"\r\n            msbuildArgs: '${{ parameters.BuildArguments }} \/p:SolutionDir=\"${{ parameters.solutionDir }}\"'\r\n            platform: \"$(buildPlatform)\"\r\n            configuration: \"$(buildConfiguration)\"\r\n            maximumCpuCount: true\r\n\r\n        - task: PublishPipelineArtifact@1\r\n          enabled: true\r\n          displayName: \"[net] Publish Project ${{ item.key }}\"\r\n          inputs:\r\n            artifactName: ${{ item.key }}\r\n            targetPath: $(build.artifactstagingdirectory)\/${{ item.key }}\r\n            publishLocation: \"pipeline\"\r\n<\/code><\/pre>\n<h2>Losse stappen binnen de jobs<\/h2>\n<p>Om onderdelen her te gebruiken binnen de verschillende jobs, splits je de stappen uit. Dit is bijvoorbeeld handig voor het ophalen van de externe software bibliotheken. Deze stap wordt op meerdere plekken gebruikt. Om dubbele code te voorkomen gebruik deze stap als template.<\/p>\n<p>In het onderstaande voorbeeld restore ik de nuget packages van de solution.  Voor dit .NET Framework project wordt de packages.config style gebruikt. In de eerste taak wordt gezocht naar alle \u2018packages.config\u2019 bestanden in het project. Hier wordt een hash van gemaakt zodat als er iets in \u00e9\u00e9n van deze bestanden iets veranderd er geen gebruik wordt gemaakt van de oude cache map. Als de config bestanden niet veranderen dan blijft de key het zelfde. In Azure Devops is de cache key scoped tot de pipeline en als deze niet binnen 7 dagen wordt gebruikt dan wordt de inhoud verwijderd.<\/p>\n<p>De inhoud van: <code>\/azure-pipeline\/steps\/server_net_restore.yml<\/code><\/p>\n<p><code><\/p>\n<pre>\r\nparameters:\r\n  verbose: false\r\n  nuGetVersion: \"5.2.0\"\r\n  solution: '$(Build.SourcesDirectory)\\src\\Project.sln\u2019\r\n\r\nsteps:\r\n\r\n  - task: Cache@2\r\n    displayName: \"[net] Cache NuGet packages\"\r\n    inputs:\r\n      key: 'nuget | \"$(Agent.OS)\" | **\/packages.config,!**\/bin\/**'\r\n      restoreKeys: |\r\n        nuget | \"$(Agent.OS)\"\r\n      path: '$(Build.SourcesDirectory)\/src\/packages'\r\n\r\n  - task: NuGetToolInstaller@1\r\n    displayName: \"[net] Use NuGet ${{ parameters.nuGetVersion }}\"\r\n    inputs:\r\n      versionSpec: ${{ parameters.nuGetVersion }}\r\n      \r\n  - task: NuGetCommand@2\r\n    displayName: \"[net] NuGet Restore\"\r\n    inputs:\r\n      restoreSolution: \"${{ parameters.solution }}\"\r\n      feedsToUse: config\r\n<\/code><\/pre>\n<h2>Merge job<\/h2>\n<p>Als laatste stap in het build proces is het combineren van de artifacts voordat deze naar een omgeving worden gereleased. Het is aan jou of je dit in de build stap doet of dat je dit tijdens de release doet. In het onderstaande voorbeeld is het niet te zien maar in mijn geval waren verschillende front-end frameworks die op verschillende locaties staan dus leek het mij slim om dit al voordat de release start alles al bij elkaar te brengen.<\/p>\n<p>Dit is : <code>\/azure-pipeline\/jobs\/merge.yml<\/code><\/p>\n<p><code><\/p>\n<pre>\r\nparameters:\r\n  pool: \"ubuntu-latest\"\r\n  serverSitecoreArtifact: \"server_sitecore\"\r\n  clientArtifact: \"client\"\r\n  mergeArtifact: \"merge\"\r\n\r\njobs:\r\n  - job: Merge\r\n    displayName: Merge website artifacts\r\n    pool:\r\n      vmImage: ${{ parameters.pool }}\r\n    steps:\r\n      - checkout: none\r\n\r\n      - task: DownloadPipelineArtifact@2\r\n        displayName: \"[$(serverSitecoreArtifact)] DownloadPipelineArtifact\"\r\n        inputs:\r\n          artifact: $(serverSitecoreArtifact)\r\n          downloadPath: '$(pipeline.workspace)\/$(serverSitecoreArtifact)'\r\n\r\n      - task: DownloadPipelineArtifact@2\r\n        displayName: \"[$(clientArtifact)] DownloadPipelineArtifact\"\r\n        inputs:\r\n          artifact: $(clientArtifact)\r\n          downloadPath: '$(pipeline.workspace)\/$(clientArtifact)'\r\n\r\n      - task: PowerShell@2\r\n        enabled: true\r\n        displayName: rename $(serverSitecoreArtifact) to $(mergeArtifact)\r\n        inputs:\r\n          targetType: 'inline'\r\n          script: |\r\n            Move-Item -Path $(pipeline.workspace)\/$(serverSitecoreArtifact) -Destination $(pipeline.workspace)\/$(mergeArtifact)\r\n\r\n      - task: PowerShell@2\r\n        enabled: true\r\n        displayName: Merge $(clientArtifact) in $(mergeArtifact) (When \/assets\/client already exist)\r\n        inputs:\r\n          targetType: 'inline'\r\n          script: |\r\n            Get-ChildItem -Path $(pipeline.workspace)\/$(clientArtifact) | Copy-Item -Destination $(pipeline.workspace)\/$(mergeArtifact)\/assets\/client -Recurse -Force\r\n\r\n      - task: PublishPipelineArtifact@1\r\n        enabled: true\r\n        displayName: Publish $(mergeArtifact) Artifact\r\n        inputs:\r\n          targetPath: \"$(pipeline.workspace)\/$(mergeArtifact)\"\r\n          artifact: $(mergeArtifact)\r\n          publishLocation: \"pipeline\"\r\n<\/code><\/pre>\n<p>\nDe conclusie dat het goed mogelijk is om pipelines op te splitsen in losse bestanden waardoor het makkelijker wordt om het overzicht te houden. Op deze manier voorkom je dubbele code en automatiseer je de release van de applicatie. <\/p>\n<p>Deze blog verscheen voor het eerst op qdraw.nl<\/p>\n","protected":false},"excerpt":{"rendered":"Azure Devops is een Microsoft dienst waar mee code projecten beschikbaar kunnen gesteld worden voor anderen. In deze technische blog geef ik een introductie hoe ik templates hergebruik. De Azure Pipelines werken vrijwel met elke programmeertaal of projecttype. Deze tool combineert Continuous Integration (CI) and Continuous Delivery (CD) om continue het project te compileren en&#8230; <a class=\"view-article\" href=\"https:\/\/qdraw.nl\/blog\/technologie\/hoe-gebruik-je-templates-met-azure-pipelines\/\">Bekijk artikel<\/a>","protected":false},"author":2,"featured_media":5776,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[624],"tags":[2788,2789,2761,2790],"class_list":["post-5770","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-technologie","tag-azure","tag-azure-devops","tag-code","tag-continuous-integration"],"_links":{"self":[{"href":"https:\/\/qdraw.nl\/blog\/wp-json\/wp\/v2\/posts\/5770","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/qdraw.nl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/qdraw.nl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/qdraw.nl\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/qdraw.nl\/blog\/wp-json\/wp\/v2\/comments?post=5770"}],"version-history":[{"count":0,"href":"https:\/\/qdraw.nl\/blog\/wp-json\/wp\/v2\/posts\/5770\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/qdraw.nl\/blog\/wp-json\/wp\/v2\/media\/5776"}],"wp:attachment":[{"href":"https:\/\/qdraw.nl\/blog\/wp-json\/wp\/v2\/media?parent=5770"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/qdraw.nl\/blog\/wp-json\/wp\/v2\/categories?post=5770"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/qdraw.nl\/blog\/wp-json\/wp\/v2\/tags?post=5770"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}