Es gibt verschiedene Möglichkeiten eine Golang Binary (Programm) zu compilieren und anderen zur Verfügung zu stellen. Wir wollen uns hier einmal anschauen wie man das ganze macht mit hilfe des build Tools goreleaser
.
Goreleaser ist ein Tool um Golang Programme zu bauen. Der Fokus dabei liegt auf einfachheit, so kann man mittels goreleaser in einem Durchlauf für mehrere Plattformen und Architekturen bauen. Goreleaser unterstützt auch das Veröffentlichen auf den gängigsten Code Plattformen wie Gitlab und Github und ermöglicht es uns auch Docker-Images und Pakete für Debian oder RedHat zur Verfügung zu stellen. Die Seite von dem tool lautet wie folgt: https://goreleaser.com/ Für mehr Informationen rund um das Tool gibt es auch hier einen weiteren Artikel wo es nur um Goreleaser geht:
Natürlich brauchen wir für einen Lauf innerhalb einer Pipeline auch eine Konfigurationsdatei. Wir benutzen hier eine sehr einfache Datei und gehen kurz durch unsere Datei durch:
1version: 1
2before:
3 hooks:
4 - go mod tidy
5builds:
6 - env:
7 - CGO_ENABLED=0
8 goos:
9 - linux
10 - windows
11 - darwin
12 goarch:
13 - amd64
14 - arm64
Hier haben wi den ersten Abschnitt unserer Datei
Abschhnitt | Sub | Erklärung |
---|---|---|
version | - | Versioniert die eigentliche Konfigurationsdatei |
before | - | Hier wird alles rein geschrieben, was vor dem Starten des Build Prozesses ablaufen soll |
before | hooks | hier kann man hooks anlegen die vor dem starten des bau Prozesses ablaufen. Oft wird hier einmal das build enviroment aufgeräumt |
builds | env | Welche Umgebungsvariablen möchte man hinzufügen |
builds | goos | Für welche Betriebssysteme gebaut werden soll |
builds | goarch | In welche Architekturen gebaut werden soll |
gitlab_urls | use_package_registry | Hier wird die Registry für Releases direkt von gitlab genutzt. Wir wollen ja auf gitlab releasen |
gitlab_urls | use_job_token | Der Job Token wird für die authentifizierung mit der registry genutzt |
Ein weiterer Abschnitt wäre das changelog
und das archives
.
1archives:
2 - format: tar.gz
3 # this name template makes the OS and Arch compatible with the results of `uname`.
4 name_template: >-
5 {{ .ProjectName }}_
6 {{- title .Os }}_
7 {{- if eq .Arch "amd64" }}x86_64
8 {{- else if eq .Arch "386" }}i386
9 {{- else }}{{ .Arch }}{{ end }}
10 {{- if .Arm }}v{{ .Arm }}{{ end }}
11 # use zip for windows archives
12 format_overrides:
13 - goos: windows
14 format: zip
Hier werden die Endungen für das gebaute binary festglegt und kann auch bei bestimmten Systemen überschrieben werden
1changelog:
2 sort: asc
3 filters:
4 exclude:
5 - "^docs:"
6 - "^test:"
7
Es wird automatisch ein changelog angelegt und es kann hier definiert werden wie genau das geschieht.
Wenn unsere Konfiguration fertig ist sieht unsere Beispiel datei genau so aus:
1version: 1
2
3before:
4 hooks:
5 - go mod tidy
6
7builds:
8 - env:
9 - CGO_ENABLED=0
10 goos:
11 - linux
12 - windows
13 - darwin
14 goarch:
15 - amd64
16 - arm64
17gitlab_urls:
18 use_package_registry: true
19 use_job_token: true
20
21archives:
22 - format: tar.gz
23 # this name template makes the OS and Arch compatible with the results of `uname`.
24 name_template: >-
25 {{ .ProjectName }}_
26 {{- title .Os }}_
27 {{- if eq .Arch "amd64" }}x86_64
28 {{- else if eq .Arch "386" }}i386
29 {{- else }}{{ .Arch }}{{ end }}
30 {{- if .Arm }}v{{ .Arm }}{{ end }}
31 # use zip for windows archives
32 format_overrides:
33 - goos: windows
34 format: zip
35
36changelog:
37 sort: asc
38 filters:
39 exclude:
40 - "^docs:"
41 - "^test:"
.goreleaser.yaml
Damit wäre auch die Konfiguration von goreleaser abgeschlossen
Wir benutzen hier als Beispiel ein gannz kleines und einfaches Programm, da es nur dazu dient den Build-Prozess zu zeigen.
1package main
2
3import "fmt"
4
5func main() {
6 fmt.Println("Hello World")
7}
8
main.go
Unsere Pipeline halten wir auch kurz und konzentrieren uns lediglich auf das bauen und veröffentlichen des Programms. Wir definieren uns also lediglich eine Stufe in der Pipeline
1stages:
2 - release
Und hier verwenden wir das docker-image von goreleaser (wenn kein docker vorhanden ist, muss die CLI aufgerufen werden) und definieren den entrypoint als leer.
1release:
2 stage: release
3 image:
4 name: goreleaser/goreleaser
5 entrypoint: [""]
Wir wollen das die Pipeline lediglich läuft, wenn wir auch was zu releasen haben. Das ganze können wir gut durch Tags managen. Ein Git Tag ist eine Möglichkeit einem bestimmten Stand des Codes einem Namen zu geben, der normalerweise wie eine Versionsnummer gehandhabt wird. Damit würde unsere Pipeline immer laufen, wenn wir vorher folgendes gemacht haben als Beispiel
1git tag v1.0.0
2git push origin v1.0.0
macht einen neuen tag und schickt diesen tag an gitlab
Als Variablen nehmen wir den gitlab job token, der von goreleaser ja benutzt wird zur authentifizierung. Zum Schluss müssen wir nur noch beim script unser goreleaser CLI Tool anweisen, dass es was releasen soll
1 only:
2 - tags
3 variables:
4 # Disable shallow cloning so that goreleaser can diff between tags to
5 # generate a changelog.
6 GIT_DEPTH: 0
7 GITLAB_TOKEN: $CI_JOB_TOKEN
8 script:
9 - goreleaser release --clean
Damit sieht die komplette .gitlab-ci.yml
Datei wie folgt aus.
1stages:
2 - release
3
4release:
5 stage: release
6 image:
7 name: goreleaser/goreleaser
8 entrypoint: [""]
9 only:
10 - tags
11 variables:
12 # Disable shallow cloning so that goreleaser can diff between tags to
13 # generate a changelog.
14 GIT_DEPTH: 0
15 GITLAB_TOKEN: $CI_JOB_TOKEN
16 script:
17 - goreleaser release --clean
die .gitlab-ci.yml Datei
Die Pipeline läuft nun immer wenn wir ein Tag pushen. Das ganze kann sich angeguckt werden unter der Gitlab Instanz > Build > Pipelines
und die Ausgabe der Releases sollte nun unter Deploy > releases
zu finden sein. Hier wird immer die Versionsnummer (unser Tag), alle Dateien die wir gebaut haben und zusätzlich auch unser kleines Changelog zu sehen sein mit den Commit Nachrichten die in dem Tag bzw. Release dazu gekommen sind.