With Azure Pipelines we can build applications with many different languages, one of which is Go.
If you’re using a Hosted Agent, which is the recommended way (since you don’t need to manage your own machines), you are at the mercy of what software is installed on the agent. But if you’re building an application to target a specific runtime, say Go 1.12.3 (the latest at the time of writing), you might be out of luck as the agent doesn’t have that installed on it.
So let’s take a look at how to setup Go as part of your pipeline.
Side note: You can also use Docker for your builds, but Docker isn’t for everyone, so I’m focusing on non-containerised agents here. Docker can also be a challenge if you need several runtimes, to say, build a web application with Go + WASM support.
We’re going to be modifying the standard Go Azure Pipeline, so start with that a template.
Setting up our variables
The first thing we’ll want to do is modify the variables that Go will be expecting, specifically
GOROOT. By default, these point to one of the versions of Go on the agent, but we’ll modify them to use our version of Go.
variables: GOPATH: '$(Agent.BuildDirectory)/gopath' # Go workspace path GOROOT: '$(Agent.BuildDirectory)/go' # Go installation path GOBIN: '$(GOPATH)/bin' # Go binaries path modulePath: '$(GOPATH)/src/github.com/$(build.repository.name)' # Path to the module's code
We’ll using one of the Agents pre-defined variables,
Agent.BuildDirectory, as it’s somewhere I know I can write to on the Agent, and it’s scoped to my build in particular.
GOPATH is going to be a new folder we’ll create and
GOROOT will be where Go is unpacked to. These will now be environment variables so when Go eventually executes, it’ll be the right version of Go.
Next, we’ll add a new step to download and unpack the version of Go we want to target:
steps: - script: | wget "https://storage.googleapis.com/golang/go1.12.3.linux-amd64.tar.gz" --output-document "$(Agent.BuildDirectory)/go1.12.3.tar.gz" tar -C '$(Agent.BuildDirectory)' -xzf "$(Agent.BuildDirectory)/go1.12.3.tar.gz" displayName: 'Install Go 1.12'
Since we know my agent is a Linux agent (from the
pool defined in the template) we’ll use
wget to download the Linux binaries and drop them into the
Agent.BuildDirectory. With that done we can unpack it with
tar, specifying that we want to unpack to
Agent.BuildDirectory, and given the structure of the
tar.gz contains a folder named
go we’ll end up with a path that matches
Lastly, we need to ensure that the
PATH of the agent knows about this version of Go, and we do that by setting output variables:
- script: | echo '##vso[task.prependpath]$(GOBIN)' echo '##vso[task.prependpath]$(GOROOT)/bin'
This is covered in the Set up a Go workspace step in the docs guide.
Now the rest of the pipeline can follow exactly as the template describes!
Version pinning of dependencies is really important to ensure that we have repeatable builds over time. Since we don’t want to end up in a situation where we manually manage our agent pool we want our build definition to prepare the agent to the environment we desire, including setting up the appropriate runtimes.
You can see a full pipeline definition on my GitHub that utilises this approach.
You could also use this approach to prepare multiple different versions of Go to create a build matrix against several runtime releases, but I’ll leave that as an exercise to you, dear reader 😉.