loading...

Automate Micronaut 2.1 Docker build with Azure DevOps / Pipelines

greenroommate profile image Haris Secic ・4 min read

Recently Micronaut 2.1 was published with yet another feature that makes us "end-developers" work even less. It's called "Gradle Docker Plugin" and you can check it out here. Micronaut has adopted this feature and Graeme (creator of Micronaut) posted this YouTube video explaining some features including how to use this Gradle plugin with Micronaut.

Prerequisites

I assume that you've setup Container Registry in Azure and have all connections set up properly to communicate between Azure DevOps and registry.
Also I hope you have a project with Micronaut 2.1 or greater version because this feature is only available starting from 2.1.0

Pipeline setup

My first reaction to this was "nice" and then I thought "Well how can use this to tag images with other than latest default tag while using Azure Pipelines". Thing is that in order to make a custom tag I did something like this

dockerBuild {
    images = ["myproject:$project.version","myproject:latest"]
}
Enter fullscreen mode Exit fullscreen mode

but azure-pipelines.yaml will contain by default something like this as a task for deploying

- task: Docker@2
    displayName: Build and push an image to container registry
      inputs:
        command: buildAndPush
        repository: $(imageRepository)
        dockerfile: $(dockerfilePath)
        containerRegistry: $(dockerRegistryServiceConnection)
        tags: |
          $(tag)
Enter fullscreen mode Exit fullscreen mode

but that $(tag) variable is hard-coded to read from something like '$(Build.BuildId)' which is not something very useful to tag your own images with because you probably want to track your own versions. Top of your pipeline yaml should have something like below config

variables:
  # Container registry service connection established during pipeline creation
  dockerRegistryServiceConnection: 'some-uuid-some-uuid'
  imageRepository: 'yourAzureImageRepository'
  containerRegistry: 'yourAzureIMageRepoistory.azurecr.io'
  dockerfilePath: '$(Build.SourcesDirectory)'
  tag: '$(Build.BuildId)'
  # Agent VM image name
  vmImageName: 'ubuntu-latest'
Enter fullscreen mode Exit fullscreen mode

You can remove that tag: ... part, as we are trying to put our own info in there, and the dockerfilePath as we will build image with gradle so we don't need the Dockerfile.

Instead let's make a script that will extract version from our build.gradle file. Add a bash task before task buildDocker task. Something like this should give you version:

#!/usr/bin/env bash
version=$(grep '^version[[:space:]]*"\(.\)\+"$' build.gradle | awk '{print $2}')
echo "##vso[task.setvariable variable=version]$version"
Enter fullscreen mode Exit fullscreen mode

then your task may look like this:

- task: Bash@3
    inputs:
      targetType: 'inline'
      script: |
        #!/usr/bin/env bash
        version=$(grep '^version[[:space:]]*"\(.\)\+"$' build.gradle | awk '{print $2} | sed 's/"//g'')
        echo "##vso[task.setvariable variable=version]$version"
Enter fullscreen mode Exit fullscreen mode

Windows pipelines?

I made a script which should give you same result when your running your pipelines on Windows platform

$versionVal = Get-Content .\build.gradle | Select-String -Pattern '^version\s*\"(?<version>.+)\"$'  | % { $_.Matches[0].Groups["version"].Value }
Write-Host "##vso[task.setvariable variable=version]$version"
Enter fullscreen mode Exit fullscreen mode

I'm not 100% sure that it works I just tested it in PowerShell and I got the version number printed out so I guess it works :D.

Maven?

Well just check this link and adjust it accordingly. This is also a way to get some credits to the person who posted that answer as I saw this answer while looking out how to set up variable in Azure Pipeline from bash.

Tag the image

Because image was built inside Azure pipelines we need to tag it to make push command know where to push the image. Easy way to do it on Linux pipeline is using bash.

    - task: Bash@3
      inputs:
        targetType: 'inline'
        script: 'docker tag $(imageRepository):$(tag) $(containerRegistry)/$(imageRepository):$(tag)'
Enter fullscreen mode Exit fullscreen mode

Sometimes you want your image to be tagged with latest because a lot people will configure (or leave by default) triggers that get activated only on this tag. So let's say you want your development to be the latest version but main or master to be specific version. Below task will activate only if branch name was 'development'.

    - task: Bash@3
      inputs:
        targetType: 'inline'
        script: 'docker tag $(imageRepository):$(tag) $(containerRegistry)/$(imageRepository):latest'
      condition: eq(variables['Build.SourceBranch'], 'development')
Enter fullscreen mode Exit fullscreen mode

You can read more about it here if you need to configure it differently

Push the image

- task: Docker@2
    displayName: Build and push an image to container registry
      inputs:
        command: push
        repository: $(imageRepository)
        containerRegistry: $(dockerRegistryServiceConnection)
        buildContext: $(containerRegistry)
        tags: |
          $(tag)
          latest
Enter fullscreen mode Exit fullscreen mode

If you didn't tag the image with the latest tag the whole pipeline will fail as there is no conditional statement. If you don't need it remove it completely.

Latest tag (optional)

If you need latest tag conditional this line should help.

${{ if eq(variables['Build.SourceBranchName'], 'development') }}:
  tags: |
    $(tag)
    latest
${{ if ne(variables['Build.SourceBranchName'], 'development') }}:
  tags: |
    $(tag)
Enter fullscreen mode Exit fullscreen mode

So task could look like this replacing branchNameGoesHeere with whatever your branch name should be:

- task: Docker@2
    displayName: Build and push an image to container registry
      inputs:
        command: push
        repository: $(imageRepository)
        containerRegistry: $(dockerRegistryServiceConnection)
        buildContext: $(containerRegistry)
        ${{ if eq(variables['Build.SourceBranchName'], 'development') }}:
          tags: |
            $(tag)
            latest
        ${{ if ne(variables['Build.SourceBranchName'], 'development') }}:
          tags: |
            $(tag)
Enter fullscreen mode Exit fullscreen mode

This should be it hope it work :D

Updates:
I've switched the logic for tagging images with latest as in most cases "latest" version is not the one to deploy nor keep on master/main branches. Obvious reason should be that deploying new code on push should only happen in development/testing environments while there must be a "manual" trigger to fetch the latest TESTED version that works properly and deploy it. Also prior example didn't actually work when having multiple tags and was missing tagging part so it's added now.

Discussion

pic
Editor guide