Using –build-args with docker in Azure DevOps Pipelines

This should be simple right? Even with the new yaml pipeline editor, you should be able to do the old ‘build and push’ and fire a few arguments in. Nope. Doesn’t work. Here’s how you can do it…

The background around this problem is that I want to add in some custom logic around Assembly Info in our build pipeline, so we have a .sh file that will edit the .csproj file and update the AssemblyVersion elements. (I know there are possibilities for doing this with wildcards etc… but I want to do it this way, for reasons)

The script is very simple

#!/bin/sh

echo "Running version updater..."

while getopts 'm:n:b:r:' flag
do
    case ${flag} in
        m) MAJOR=${OPTARG};;
        n) MINOR=${OPTARG};;
        b) BUILD=${OPTARG};;
        r) REVISION=${OPTARG};;
    esac
done

PROJECTPATH="./API/my-api-project.csproj"
TOREPLACE="0.0.0.0"
REPLACEMENT="${MAJOR}.${MINOR}.${BUILD}.${REVISION}"

echo "Major: ${MAJOR}"
echo "Minor: ${MINOR}"
echo "Build: ${BUILD}"
echo "Revision: ${REVISION}"
echo "Replacement: ${REPLACEMENT}"

sed -i "s/$TOREPLACE/$REPLACEMENT/" $PROJECTPATH

echo "Completed version updater..."

It will take 4 arguments, and use that to create our AssemblyVersion attribute. This attribute is processed when you build a dotnet project to create an auto generated AssemblyInfo.cs file. It just lives in a PropertyGroup of our project file (I have it set to all zeros to make easy to replace with sed)

<PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
    <LanguageVersion>9.0</LanguageVersion>    
    <!-- This is updated as part of dev ops pipelines -->
    <AssemblyVersion>0.0.0.0</AssemblyVersion>
</PropertyGroup>

As we host our webapps in containers, this is run from within our dockerfile, so we push those in as dockerfile ARGS then we run our script before we do build/publish etc. The changes happen to the source in the intermediate build environment container that is disposed, so our source in git is never changed.

...
ARG major
ARG minor
ARG build
ARG revision

RUN sh ./updateVersion.sh -m $major -n $minor -b $build -r $revision
...

So, I used the autocomplete in azure’s yaml editor to add the args, as you would expect (and totally didn’t go through about 20 iterations of how to correctly format these arguments).

Seriously…. got it first time, no swearing either.

This is how the azure pipeline yaml looks for that step. The values exist as variables in the Pipeline so they are accessed with the standard $(VariableName) format.

- task: Docker@2
  displayName: 'Build and Push Docker Image'
  inputs:
    containerRegistry: 'MY Container Registry'
    repository: 'my-repo/my-image'
    Dockerfile: dockerfile.api
    tags: |
     latest
     $(Build.BuildId)
    arguments: '--build-arg major=$(BuildMajor) --build-arg minor=$(BuildMinor) --build-arg build=$(Build.BuildId) --build-arg revision=$(BuildRevision)'

Awesome right, I’ll tag it with the build id, latest, build it and push it up. Autocomplete gave me the various options, so it should just work!?

Jokes it doesn’t work.

After many many builds I finally logged out what I was actually getting, and the .sh file is showing that it’s not getting any arguments passed in? How can this be? It works on my machine at the command line!

Well.. the crux of it is, that Azure DevOps’ “Build and Push” task doesn’t accept arguments, even though it will happily suggest it, and won’t throw any errors. The solution is to split out the Build and Push steps into separate tasks, as only the build task will accept arguments.

Now the yaml looks like this:

- task: Docker@2
  displayName: 'Build'
  inputs:
    command: build
    containerRegistry: 'MY Container Registry'
    repository: 'my-repo/my-image'
    Dockerfile: dockerfile.api
    tags: |
     latest
     $(Build.BuildId)
    arguments: '--build-arg major=$(BuildMajor) --build-arg minor=$(BuildMinor) --build-arg build=$(Build.BuildId) --build-arg revision=$(BuildRevision)'

- task: Docker@2
  displayName: Push
  inputs:
    command: push
    containerRegistry: 'MY Container Registry'
    repository: 'my-repo/my-image'
    tags: |
     latest
     $(Build.BuildId)

Boom! The API now has all the details that I pass in baked into the assembly info of the API.

Incase you didn’t believe me for some reason

I am aware this is a long way to write “Split it out into separate tasks” but I figured it wouldn’t hurt to have a few bits and bobs to copy/paste.

All the best.

2 thoughts on “Using –build-args with docker in Azure DevOps Pipelines

Add yours

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

Up ↑

%d bloggers like this: