Automatically publishing new versions of my Graph PowerShell Docker image

A while ago I blogged about creating a Docker image with PowerShell and the latest version of Graph. Thing is, Microsoft keeps releasing new versions of the module pretty regularly and unless I remember to go and build a new version of the image each time, it is quickly outdated.

Then I came across this toot on Mastodon. He had setup something to update his Unbound DNS Docker image (interestingly, something I too had dabbled with a long time ago) each time NLnet Labs releases a new version of Unbound DNS. Here’s a link to his GitHub action which does this and the key thing is: 1) it runs on a schedule (I had been too lazy to figure out GitHub actions could do that!) and 2) it very smartly uses the APIs to check the version of his container vs the version of the Unbound DNS (which is released on GitHub, so he can query the releases) and only updates the image in case of changes. Nice!

I used his scheduler idea, but had to make some changes to the other bits to cater to my specific use case.

Getting the latest version of the PowerShell SDK from GitHub is easy. This command will give all the releases:

It’s in JSON format, so pipe it via jq. The JSON is an array of entries like this:

What we want is the name property. The following can extract that:

The '.[] | .name' bit is the script to jq. It tells it to read the array (.[]) and extract name. Output looks like this:

So I should sort it in reverse order, treating the text as numbers, then take the first element, and also remove the quotation marks.

The -Vr switches to sort tells it to treat them as numbers and do a reverse sort. The head -n 1 takes the first element. And tr -d removes the double quotes.

Next up, how do I get the latest version of my Docker image? I was hoping I could query GitHub to get the latest version of the package somehow, but that doesn’t work. For one, GitHub doesn’t seem to have a way of getting all packages of a repo – all it can do is get all packages for an organization, or all packages for a user, and some variants of these – but all of these require authentication. Not a problem in itself, I generated a token to test things out, and used the API to get all versions of the packaged owned by me.

(The token in the examples is not valid).

The result is again JSON that looks like this:

I can use the following to just extract version numbers. That’s the tags basically.

Output looks like this:

Of course I’d have to reverse sort just to be sure, take only the first element, and remove the double quotes.

The jq snippet here is a bit more involved. Read the array (.[]), extract the property with the tags (.metadata.container.tags) – which is itself an array, so read that (.[]).

While I could have gone with this, I didn’t. Instead I decided to query DockerHub as I could just do it without a personal token etc.

An example entry is as follows. All these are part of an array called results.

To extract the name (which has the version) I can do:

And the same drill as above to get just the latest version:

Boom! So now I have the latest version in DockerHub. And the latest version of the module from Microsoft (via GitHub).

Since I have a workflow that already builds the container, I wanted to keep things consistent with that as much as possible. So I added a new job to the existing one. My existing one looks like this (this is just a snippet):

I added a new one above that checks if there’s a version difference.

What this new job does is check whether there’s a difference in versions. If yes, it sets an output variable SHOULD_RUN to true. The second job now only runs if that variable is set to true (needs.update-check-job.outputs.SHOULD_RUN == 'true').

Since I know the version of the module to target, I output that as part of the first job, and the second job uses that when building the container.

I am pretty pleased with it overall! To give credit, it was this StackOverflow post that gave me the idea of using multiple jobs. That felt neater than the other solutions.

And that’s it! Now GitHub Actions will automatically publish new versions of this image whenever there’s a new Graph module. As luck would have it, today version 2.14.1 was released and the image was automatically built. Nice!