Automate Micronaut 2.1 Docker build with Azure DevOps / Pipelines
Tags: micronaut, azure, devops, docker
Published: 2020-10-26 20:16:14

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](https://github.com/bmuschko/gradle-docker-plugin). Micronaut has adopted this feature and Graeme (creator of Micronaut) posted [this YouTube video](https://www.youtube.com/watch?v=4QnuVlEDprs) 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
```gradle
dockerBuild {
    images = ["myproject:$project.version","myproject:latest"]
}
```
but _azure-pipelines.yaml_ will contain by default something like this as a task for deploying
```yaml
- task: Docker@2
    displayName: Build and push an image to container registry
      inputs:
        command: buildAndPush
        repository: $(imageRepository)
        dockerfile: $(dockerfilePath)
        containerRegistry: $(dockerRegistryServiceConnection)
        tags: |
          $(tag)
```
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
```yaml
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'
```
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:
```bash
#!/usr/bin/env bash
version=$(grep '^version[[:space:]]*"\(.\)\+"$' build.gradle | awk '{print $2}')
echo "##vso[task.setvariable variable=version]$version"
```
then your task may look like this:
```yaml
- 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"
```
###Windows pipelines?
I made a script which should give you same result when your running your pipelines on Windows platform
```powershell
$versionVal = Get-Content .\build.gradle | Select-String -Pattern '^version\s*\"(?<version>.+)\"$'  | % { $_.Matches[0].Groups["version"].Value }
Write-Host "##vso[task.setvariable variable=version]$version"
```
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](https://stackoverflow.com/a/56039297) 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.
```yaml
    - task: Bash@3
      inputs:
        targetType: 'inline'
        script: 'docker tag $(imageRepository):$(tag) $(containerRegistry)/$(imageRepository):$(tag)'
```

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'.
```yaml
    - task: Bash@3
      inputs:
        targetType: 'inline'
        script: 'docker tag $(imageRepository):$(tag) $(containerRegistry)/$(imageRepository):latest'
      condition: eq(variables['Build.SourceBranch'], 'development')
```
You can read more about it [here](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/expressions?view=azure-devops) if you need to configure it differently

##Push the image
```yaml
- task: Docker@2
    displayName: Build and push an image to container registry
      inputs:
        command: push
        repository: $(imageRepository)
        containerRegistry: $(dockerRegistryServiceConnection)
        buildContext: $(containerRegistry)
        tags: |
          $(tag)
          latest
```
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.
```yaml
${{ if eq(variables['Build.SourceBranchName'], 'development') }}:
  tags: |
    $(tag)
    latest
${{ if ne(variables['Build.SourceBranchName'], 'development') }}:
  tags: |
    $(tag)
```
So task could look like this replacing _branchNameGoesHeere_ with whatever your branch name should be:
```yaml
- 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)
```

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.