github.com/goreleaser/goreleaser@v1.25.1/www/docs/blog/posts/2022-02-20-azure-devops.md (about) 1 --- 2 date: 2022-02-20 3 slug: azure-devops 4 categories: 5 - tutorials 6 authors: 7 - dirien 8 --- 9 10 # Use GoReleaser With Azure DevOps 11 12 In this blog article, I want to show how to use **GoReleaser** in **Azure DevOps**. 13 14 <!-- more --> 15 16  17 18 In this blog article, I want to show how to use **GoReleaser** in **Azure 19 DevOps**. 20 21 ### But why? Are not everyone using GitHub? 22 23 Exactly, not everyone is using GitHub. Actually, there are many companies who 24 use the Azure Cloud with **Azure DevOps**. 25 26 ## What is Azure DevOps? 27 28 **Azure DevOps** provides developer services for allowing teams to plan work, 29 collaborate on code development, and build and deploy applications. 30 31 **Azure DevOps** provides integrated features that you can access through your 32 web browser or IDE client. You can use one or more of the following standalone 33 services based on your business needs: 34 35 - **Azure Repos** provides Git repositories. 36 - **Azure Pipelines** provides build and release services to support continuous 37 integration and delivery of your applications. 38 - **Azure Boards** delivers a suite of Agile tools to support planning and 39 tracking work, code defects, and issues using Kanban and Scrum methods. 40 - **Azure Test Plans** provides several tools to test your apps, including 41 manual/exploratory testing and continuous testing. 42 - **Azure Artifacts** allows teams to share packages such as Maven, npm, NuGet, 43 and more from public and private sources and integrate package sharing into your 44 pipelines. 45 46 ## Install GoReleaser Via The Marketplace 47 48  49 50 **GoReleaser** offers a [Plugin via the 51 Marketplace](https://marketplace.visualstudio.com/items?itemName=GoReleaser.goreleaser). 52 The installation itself is done via some clicks in the UI and you are ready to 53 start! 54 55 In your pipeline editor you can lookup the task: 56 57  58 59 And quickly change the default settings to fit with your use case! 60 For example set a specific version or execute certain **GoReleaser** commands. 61 62 See the official docs for more details 63 [https://github.com/goreleaser/goreleaser-azure-devops-extension](https://github.com/goreleaser/goreleaser-azure-devops-extension) 64 65  66 67 ## Finally The Demo! 68 69 ```go 70 package main 71 72 import ( 73 "fmt" 74 ) 75 76 var ( 77 version = "0.0.1" 78 commit = "none" 79 date = "none" 80 builtBy = "none" 81 ) 82 83 func main() { 84 fmt.Println("Version:\t", version) 85 fmt.Println("Commit:\t\t", commit) 86 fmt.Println("Date:\t\t", date) 87 fmt.Println("Built by:\t", builtBy) 88 } 89 ``` 90 91 Before we head over to the configure the pipeline, let us create the 92 `.goreleaser.yaml` 93 94 ```yaml 95 # This is an example .goreleaser.yml file with some sensible defaults. 96 # Make sure to check the documentation at https://goreleaser.com 97 before: 98 hooks: 99 # You may remove this if you don't use go modules. 100 - go mod tidy 101 builds: 102 - env: 103 - CGO_ENABLED=0 104 goarch: 105 - amd64 106 - arm64 107 goos: 108 - linux 109 - darwin 110 111 project_name: goreleaser-ado 112 113 checksum: 114 name_template: "checksums.txt" 115 116 snapshot: 117 name_template: "{{ incpatch .Version }}-next" 118 119 source: 120 enabled: true 121 122 release: 123 disable: true 124 125 sboms: 126 - artifacts: archive 127 - id: source 128 artifacts: source 129 130 signs: 131 - cmd: cosign 132 certificate: "${artifact}.pem" 133 args: 134 - sign-blob 135 - "-key=cosign.key" 136 - "--output-certificate=${certificate}" 137 - "--output-signature=${signature}" 138 - "${artifact}" 139 artifacts: checksum 140 output: true 141 stdin: "{{ .Env.COSIGN_PASSWORD }}" 142 143 docker_signs: 144 - cmd: cosign 145 artifacts: images 146 output: true 147 args: 148 - "sign" 149 - "-key=cosign.key" 150 - "${artifact}" 151 stdin: "{{ .Env.COSIGN_PASSWORD }}" 152 153 dockers: 154 - image_templates: ["dirien/{{ .ProjectName }}:{{ .Version }}-amd64"] 155 goarch: amd64 156 dockerfile: Dockerfile 157 use: buildx 158 build_flag_templates: 159 - --platform=linux/amd64 160 - image_templates: ["dirien/{{ .ProjectName }}:{{ .Version }}-arm64"] 161 goarch: arm64 162 dockerfile: Dockerfile 163 use: buildx 164 build_flag_templates: 165 - --platform=linux/arm64/v8 166 167 docker_manifests: 168 - name_template: "dirien/{{ .ProjectName }}:{{ .Version }}" 169 image_templates: 170 - "dirien/{{ .ProjectName }}:{{ .Version }}-amd64" 171 - "dirien/{{ .ProjectName }}:{{ .Version }}-arm64" 172 - name_template: "dirien/{{ .ProjectName }}:latest" 173 image_templates: 174 - "dirien/{{ .ProjectName }}:{{ .Version }}-amd64" 175 - "dirien/{{ .ProjectName }}:{{ .Version }}-arm64" 176 ``` 177 178 Here, we going to create **linux** and **darwin** binary, the corresponding 179 container, create the SBoM with syft and sign everything via cosign. 180 181 > Here is one first important steps: you need to disable the **release** step in 182 > GoReleaser. 183 > Azure DevOps does not work the same way as GitHub what releases concerns. 184 > We handle the upload of the artefacts differently. 185 186 If you need more infos, for the different settings and possibilities inside 187 **GoReleaser**, head over to the official documentation 188 [https://goreleaser.com/intro/](https://goreleaser.com/intro/) 189 190 ### Service 191 192 Connection in Azure DevOps As we going to upload the image to Docker Hub, we 193 need to create in **Azure Devops** the Service Connection. 194 195 Go to **Project Settings** and click **Service connections**: 196  197  198 199 Choose **Docker Registry**: 200  201 202 In the detail view, select **Docker Hub **and then enter your details, like 203 **Docker ID**, **Docker Password** and the **Service Connection Name**: 204  205 206 Click **Verify and save**, we will come back to the service connection in our 207 Pipeline code. 208 209 ### The Azure Pipeline File 210 211 Now starts the fun part, the creation of the actual Azure Pipeline. 212 If you are completely new to **Azure DevOps** pipeline, I highly suggest to 213 checkout the 214 [docs](https://docs.microsoft.com/en-us/azure/devops/pipelines/create-first-pipeline?view=azure-devops&tabs=java%2Ctfs-2018-2%2Cbrowser) 215 from Microsoft. 216 217 In our example, we going to write the pipeline only as code (there is a 218 deprecated UI only option too! But meh!). 219 220 Azure Pipeline are written in **yaml.** 221 222 ```yaml 223 # Starter pipeline 224 # Start with a minimal pipeline that you can customize to build and deploy your code. 225 # Add steps that build, run tests, deploy, and more: 226 # https://aka.ms/yaml 227 228 pr: 229 branches: 230 include: 231 - main 232 233 trigger: 234 tags: 235 include: 236 - "*" 237 branches: 238 include: 239 - "*" 240 241 jobs: 242 - job: build 243 pool: 244 vmImage: ubuntu-latest 245 steps: 246 - task: GoTool@0 247 displayName: "Install Go" 248 inputs: 249 version: "1.17" 250 - task: CmdLine@2 251 displayName: "Build and Test" 252 inputs: 253 script: | 254 go mod tidy 255 go build . 256 - job: release 257 dependsOn: build 258 displayName: Release via GoReleaser 259 condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/')) 260 variables: 261 - group: cosign 262 pool: 263 vmImage: ubuntu-latest 264 steps: 265 - task: GoTool@0 266 displayName: "Install Go" 267 inputs: 268 version: "1.17" 269 - task: CmdLine@2 270 displayName: "Install Syft" 271 inputs: 272 script: | 273 curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin 274 - task: CmdLine@2 275 displayName: "Install cosign" 276 inputs: 277 script: | 278 curl -sLO https://github.com/sigstore/cosign/releases/download/v1.5.2/cosign-linux-amd64 279 chmod +x cosign-linux-amd64 280 mv cosign-linux-amd64 /usr/local/bin/cosign 281 - task: Docker@2 282 inputs: 283 containerRegistry: "dirien-docker-hub" 284 command: "login" 285 addPipelineData: false 286 addBaseImageData: false 287 - task: goreleaser@0 288 inputs: 289 version: "latest" 290 distribution: "goreleaser" 291 args: "release --rm-dist" 292 293 - task: CopyFiles@2 294 displayName: "Copy GoReleaser dist folder to ArtifactStagingDirectory" 295 inputs: 296 Contents: | 297 dist/*.tar.gz 298 dist/*.zip 299 dist/*.txt 300 dist/*.sbom 301 dist/*.sig 302 cosign.pub 303 TargetFolder: "$(Build.ArtifactStagingDirectory)" 304 CleanTargetFolder: true 305 OverWrite: true 306 flattenFolders: true 307 308 - task: PublishBuildArtifacts@1 309 displayName: "Publish GoReleaser release artifacts" 310 inputs: 311 ArtifactName: "GoReleaser release" 312 ``` 313 314 The pipeline consist of two different jobs parts: 315 316 - the **build** job, run every time something changes on any branch or when a 317 pull request gets created. Here we can run our tests, linting, SAST to get 318 quickly feedback. 319 - the **release** job, will run only when a git tag gets created (see the 320 condition tag under the job tag). Creating a git tag is part of the release 321 process. Similar as we do in the GitHub Flow. 322 323 During the release job, we download [Anchore 324 syft](https://github.com/anchore/syft) and 325 [cosign](https://github.com/sigstore/cosign) as we going to need them during the 326 **gorleaser** task. 327 Currently there is no native task for this in **Azure DevOps**. We just use the 328 **CmdLine** task and curl the binaries. 329 330  331 332 It is also important to log into your **Docker Hub** account, via the **Service 333 Connection** we created earlier. 334 The **Docker** task takes care of the actual login. 335 336  337 338 Now we can call our **GoReleaser** task. 339 340  341 342 ### Azure Pipeline Secret Library 343 344 For cosign, I use a password, I stored in the Azure Pipeline Library as secret 345 variable. 346 347  348 349 In my pipeline code, I will pass this value as environment variable via the 350 variables tag. 351 352  353 354 In this demo, I am going to publish the release artefacts as build artefacts. 355 356  357 358 The task **CopyFiles** collects some files from the **dist** folder and the 359 cosign public key and **PublishBuildArtifacts** publish them. 360 You will find the artefacts on the pipeline detail 361 362  363 364  365 366 Of course, you can use other targets too, like a cloud native storage. 367 368 You can check out the 369 [How to use GoReleaser with Cloud Native Storage](/blog/cloud-native-storage) 370 post for more details on this subject 371 372 ### Release the kraken äh app! 373 374 Head over to **tags** menu and create a new tag in the UI 375 376  377 378  379 380 Your pipeline should immediately start to run: 381 382  383 384 And both jobs should run: 385 386  387 388  389 390 And this is pretty much all of it! As I promised, very easy and straight forward 391 we can implement **GoReleaser** in **Azure DevOps**, similar we would use it in 392 GitHub 393 394  395 396 ### Caveat: 397 398 - I use in cosign not the _keyless_ approach, as 399 I am not sure that it will work for **Azure DevOps**. So I generated a keypair 400 and committed the public and private key into the repository.