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  ![](https://cdn-images-1.medium.com/max/2000/1*hAK9NMa-YbSnTQdSEoX7Gw.png)
    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  ![](https://cdn-images-1.medium.com/max/4144/1*NCY5i6iCEPW5ZpNhYeB6Jg.png)
    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  ![](https://cdn-images-1.medium.com/max/2000/1*U-zVyao5qwgjfzTcGHgSJA.png)
    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  ![](https://cdn-images-1.medium.com/max/2000/1*Aqtx-0KvADotNmeX7X1HTQ.png)
    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  ![](https://cdn-images-1.medium.com/max/2000/1*QDD9dVR5ZYUtm4qh5Izy2w.png)
   197  ![](https://cdn-images-1.medium.com/max/2000/1*PMGoYUHoda1bVumVGW1Fow.png)
   198  
   199  Choose **Docker Registry**:
   200  ![](https://cdn-images-1.medium.com/max/2000/1*JmkHy2p5pjhc7llWCHjChw.png)
   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  ![](https://cdn-images-1.medium.com/max/2000/1*WCT6xaaNtoT6Y-Ws7-0qOQ.png)
   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  ![](https://cdn-images-1.medium.com/max/2496/1*O01UvqQCv8255Kj9JEvxiw.png)
   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  ![](https://cdn-images-1.medium.com/max/2000/1*_uMeZymqEJ0Xqq64OP_UXA.png)
   337  
   338  Now we can call our **GoReleaser** task.
   339  
   340  ![](https://cdn-images-1.medium.com/max/2000/1*blEbdGwd8PXZ9ExCScfYtw.png)
   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  ![](https://cdn-images-1.medium.com/max/4028/1*ioSS8X6yT4qFNQrCrQbAYA.png)
   348  
   349  In my pipeline code, I will pass this value as environment variable via the
   350  variables tag.
   351  
   352  ![](https://cdn-images-1.medium.com/max/2000/1*CTox6hgrOCgaTs9AFJWUhQ.png)
   353  
   354  In this demo, I am going to publish the release artefacts as build artefacts.
   355  
   356  ![](https://cdn-images-1.medium.com/max/2244/1*nwH4Ej9GbQ6RdE_MfYMelw.png)
   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  ![](https://cdn-images-1.medium.com/max/2000/1*GWCiLGBnHOnCjyRuqrPEEw.png)
   363  
   364  ![](https://cdn-images-1.medium.com/max/4220/1*MLeku1FpKCFMv7bfNvu_eA.png)
   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  ![](https://cdn-images-1.medium.com/max/2000/1*zIYaYhDk1rSpd5_si5mHIA.png)
   377  
   378  ![](https://cdn-images-1.medium.com/max/4472/1*Zaq4EUjn3egC5_3VLVQA5Q.png)
   379  
   380  Your pipeline should immediately start to run:
   381  
   382  ![](https://cdn-images-1.medium.com/max/5528/1*h5YYGAPjIXrcympI01H9yA.png)
   383  
   384  And both jobs should run:
   385  
   386  ![](https://cdn-images-1.medium.com/max/5592/1*TawbQohuoGdG4sO9xo0YLw.png)
   387  
   388  ![](https://cdn-images-1.medium.com/max/2000/0*GY5jcM6UErG39iF2.jpg)
   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  ![](https://cdn-images-1.medium.com/max/2560/0*1ZhRSdH-Se88gOdj.jpg)
   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.