Skip to content
This repository was archived by the owner on Aug 12, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: goreleaser

on:
push:
branches-ignore:
- "**"
tags:
- "v*.*.*"

jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- uses: azure/docker-login@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
email: ${{ secrets.DOCKER_USERNAME }}
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.13.x
- name: install kustomize
uses: imranismail/setup-kustomize@v1
with:
kustomize-version: "3.5.4"
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2.0.2
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ config/default/manager_image_patch.yaml-e

# test results
_artifacts

# Goreleaser
dist
61 changes: 61 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
before:
hooks:
- make manifest
builds:
-
id: capp
env:
- CGO_ENABLED=0
binary: manager
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this create the artifacts in the repo root directory? Or in dist/? I don't see anything in the config that pushes it into dist, but maybe that is the default?

Also, how do we distinguish between one build and another, so we don't accidentally put the darwin-amd64 binary in the linux-arm64 image? The current make build target creates bin/manager-<GOOS>-<GOARCH>. Can we reuse that structure? Or is goreleaser limited from doing that? I imagine not, with those go text templates I see below, but I don't know.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything that gets created by goreleaser is in dist

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And when it builds, how does it differentiate between the multiple binaries? It looks like it names them all manager, but there are multiple? Does it use the manager tag just as a tag, and automatically append the right arch/etc.? If so, how does Dockerfile.goreleaser get the right file? Or does it actually manipulate the dockerfile before running it?

goos:
- darwin
- linux
goarch:
- amd64
- arm64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this try to build also for darwin/arm64? Will that even work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it is smart enough to build only what Go can build apparently 👍 in my test locally it built:

capp_darwin_amd64
capp_linux_amd64
capp_linux_arm64

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

release:
extra_files:
- glob: "./out/release/infrastructure-packet/{{ .Tag }}/*"
dockers:
- image_templates:
- "packethost/cluster-api-provider-packet:latest-arm64"
- "packethost/cluster-api-provider-packet:{{ .Tag }}-arm64"
binaries:
- manager
builds:
- capp
dockerfile: Dockerfile.goreleaser
goos: linux
goarch: arm64
# The idea here is to add a post hook that will use the manifest tool to
# merge all the images and push them properly
skip_push: true
- image_templates:
- "packethost/cluster-api-provider-packet:latest-amd64"
- "packethost/cluster-api-provider-packet:{{ .Tag }}-amd64"
dockerfile: Dockerfile.goreleaser
binaries:
- manager
builds:
- capp
goos: linux
goarch: amd64
# The idea here is to add a post hook that will use the manifest tool to
# merge all the images and push them properly
skip_push: true
archives:
- replacements:
darwin: Darwin
linux: Linux
386: i386
amd64: x86_64
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
4 changes: 4 additions & 0 deletions Dockerfile.goreleaser
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM scratch
COPY manager /
ENTRYPOINT ["/manager"]

16 changes: 11 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.PHONY: vendor test manager clusterctl run install deploy manifests generate fmt vet run kubebuilder ci cd

GIT_VERSION?=$(shell git log -1 --format="%h")
RELEASE_TAG ?= $(shell git tag --points-at HEAD)
KUBEBUILDER_VERSION ?= 2.3.1
# default install location for kubebuilder; can be placed elsewhere
KUBEBUILDER_DIR ?= /usr/local/kubebuilder
Expand All @@ -9,9 +11,6 @@ CONTROLLER_GEN=$(GOBIN)/controller-gen

CERTMANAGER_URL ?= https://github.com/jetstack/cert-manager/releases/download/v0.14.1/cert-manager.yaml

GIT_VERSION?=$(shell git log -1 --format="%h")
RELEASE_TAG ?= $(shell git tag --points-at HEAD)

REPO_URL ?= https://github.com/packethost/cluster-api-provider-packet

# BUILDARCH is the host architecture
Expand Down Expand Up @@ -76,7 +75,7 @@ CORE_URL ?= https://github.com/kubernetes-sigs/cluster-api/releases/download/$(C
# useful function
word-dot = $(word $2,$(subst ., ,$1))

VERSION ?= 0.3.0
VERSION ?= $(RELEASE_TAG)
VERSION_CONTRACT ?= v1alpha3
VERSION_MAJOR ?= $(call word-dot,$(VERSION),1)
VERSION_MINOR ?= $(call word-dot,$(VERSION),2)
Expand Down Expand Up @@ -215,7 +214,14 @@ $(MANAGERLESS_DIR) $(MANAGERLESS_BASE):
mkdir -p $@

.PHONY: release-clusterctl release-manifests release $(RELEASE_CLUSTERCTLYAML) $(RELEASE_MANIFEST) $(RELEASE_METADATA) $(RELEASE_CLUSTER_TEMPLATE)
release: release-manifests release-clusterctl release-cluster-template
manifest: release-manifests release-clusterctl release-cluster-template

release:
goreleaser release --rm-dist --snapshot --skip-publish

release/publish:
goreleaser release --rm-dist

release-manifests: $(RELEASE_MANIFEST) $(RELEASE_METADATA) $(RELEASE_CLUSTER_TEMPLATE)
$(RELEASE_MANIFEST): $(RELEASE_DIR) ## Builds the manifests to publish with a release
kustomize build config/default > $@
Expand Down
200 changes: 200 additions & 0 deletions docs/contribution/release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
# how release works

Prerequisite:

* docker installed
* How drone works
* How [goreleaser](https://goreleaser.com/intro/) works and you have to
[install](https://goreleaser.com/install/) it to try a release locally.
* multi arch TODO, don't know yet

## Continuous Delivery

We use GitHub Action as continuous integration and continuous delivery pipeline.
Integration means: running tests, checking code quality.
Delivery means: make a release when a new tag get pushed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to add something here about delivery vs release. Whenever something gets merged into master, we push a new image tagged with that hash, e.g. docker.io/packethost/cluster-api-provider-packet:abcde566. When we cut a release, i.e. a git tag, we also push an image tagged with that tag, e.g. docker.io/packethost/cluster-api-provider-packet:v0.3.1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do! Thanks, I didn't notice that


GoReleaser is used only when a new tag gets pushed. We also push a new image
every time master changes. I will document this workflow in its own chapter:
"push from master" at the end of this document but at the moment it does not use
GoReleaser.

## goreleaser
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we use goreleaser only when cutting an actual tagged release, e.g. v0.3.2? What do we use when cutting builds on master directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, we use what we already have in place

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So there are two different flows, then? One for cutting tagged releases (goreleaser) and one for non-tagged merges (existing)? And I assume the difference is handled in drone.yml where "if release then run goreleaser"?

We should document that.


GoReleaser is a popular tool to release Go applications and library.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is goreleaser expected to be installed? What installs it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So how does it get installed? If I run this, then I will have a failure? I think if this is used for releases only (not normal builds) then it is OK not to have it verified and installed automatically (although it would be nice, but not necessary), but we need to make it clear that: it is a prerequisite for releases; here is the link to install it; it isn't needed except for releases, which are normally handled by drone.

And, of course, we need to add it to drone.yml, but that can come later.


We use GoReleaser for the following features:

* Multi arch build. It uses Go cross compilation feature to build for Darwin and
Linux (arm and amd)
* Docker to build docker images for Linux container arm and amd.
* Changelog to generate a changelog that will be pushed in the GitHub release
page with the list of PR part of the release itself.
* Artifact (binaries) will be pushed as part of GitHub release page as well as
the other YAML file required by cluster-api like: metadata.yaml,
infrastructure-provider.yaml and so on.

First things you should check if the file `./goreleaser.yaml` in the project
root because it gives you an idea about what it does.

## Directories and general workflow

The release life cycle touches two directories:

1. `./dist` it is a temporary directory used by GoReleaser to bundle all the
required files and, binaries, changelog and so on. It is in gitignore.
2. `./out` is ignored by git and it gets generated via `make release`. It is a
bundle that contains generated Kubernetes manifest required by the
cluster-api such as: metadata.yaml, infrastructure-provider.yaml,
cluster-template.yaml and so on.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are they bundled together? Does goreleaser create them all and move them all into dist/ and then create an actual release? Or does it use both directories in parallel?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can mention looking at the .goreleaser.yaml file in the root of the project because it gives a good context of what goreleaser does even if you don't know it

Goreleaser builds binary by its own in dist and it gets files from the out directory (generated via the command you did make release)

if you look at the .goreleaser.yaml file you will see how goreleaser includes them from the out directory generated with the make releaser command

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Can we add that to the doc? Something as simple as: the details are in goreleaser.yaml, but the process goreleaser does is:

  1. build binaries in dist
  2. make output artifacts in out/ (we had better make sure those exist first)
  3. combine them all in dist
  4. create a release


GoReleaser works in this way:

1. it builds binaries in `./dist`
2. moves all the file that has to be added in the release archive and release
page such as: LICENSE, README, the kubernetes manifests in `./dist`
3. Build docker images
4. Push all the archives and the images to GitHub and to Docker Hub.

## Local workflow

GoReleaser can be used locally, to visualize how a release will look like for
example:

```
$ goreleaser release --rm-dir --skip-publish
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have been pretty good about simple make targets that wrap complex commands. As it is, much of our generation (e.g. out/release/ and bin/manager-*) are created via makefile targets, especially because of more complex dependencies, like manifests and generate. We should have a simple single make target for this.


You can run this command if your git HEAD has a tag. You can make a temporary
one:

```
git tag v0.10.0
```

Or you can decided to run GoRelease in `--snapshot` mode to avoid a fake tag
(but changelog won't work):

```
$ goreleaser release --rm-dir --snapshot --skip-publish
```

*Removing --skip-publish goreleaser will attempt a push to GitHub and to your
docker image repository (docker.io). In this way you can cut a release from your
laptop if needed. But it is not a good idea and we use drone for that. Normally
at Packet only drone.io is capable of pushing to Docker Hub and GitHub.*

We have an utility target that helps you do try a release:

```
make release
```

If you know what you are doing and you have the right access to GitHub and
Docker Hub you can use:

```
make release/publish
```
**THIS IS NOT USUALLY REQUIRED AND YOU SHOULD NOT DO IT!**

## Drone workflow

Drone is capable of triggering a command only when a new tag is pushed.
The command it runs is: `goreleaser release`.

In order to push artifacts and a release page, drone needs to have access to
docker image repository and GitHub via access token.

## push from master

We push images to Docker Hub tagged as the git commit sha and latest every time
master changes (usually when a PR gets merged).

This process at the moment does not use GoReleaser (it will may use it in the
future) and you can check how it works look at the `./.github/workflows/ci.yaml`
file. In practice when master changes an action will build and push the new
images to Docker Hub.

## example of goreleaser output

This is an example of a valid goreleaser output that I ran locally via `make release`

```
goreleaser release --rm-dist --snapshot --skip-publish

• releasing...
• loading config file file=.goreleaser.yml
• running before hooks
• loading environment variables
• getting and validating git state
• releasing v0.1.0, commit 1f8e0e31d10a3f4f909fbcd9249fb12b14bf0010
• pipe skipped error=disabled during snapshot mode
• parsing tag
• setting defaults
• snapshotting
• github/gitlab/gitea releases
• project name
• building binaries
• creating source archive
• archives
• linux packages
• snapcraft packages
• calculating checksums
• signing artifacts
• docker images
• artifactory
• blobs
• homebrew tap formula
• scoop manifests
• snapshotting
• checking ./dist
• --rm-dist is set, cleaning it up
• writing effective config file
• writing config=dist/config.yaml
• generating changelog
• pipe skipped error=not available for snapshots
• building binaries
• building binary=/Users/gianarb/git/cluster-api-provider-packet/dist/capp_l
inux_arm64/manager
• building binary=/Users/gianarb/git/cluster-api-provider-packet/dist/capp_d
arwin_amd64/manager
• building binary=/Users/gianarb/git/cluster-api-provider-packet/dist/capp_l
inux_amd64/manager
• archives
• creating archive=dist/cluster-api-provider-packet_v0.1.0-next_Linux_x86_64
.tar.gz
• creating archive=dist/cluster-api-provider-packet_v0.1.0-next_Linux_arm64.
tar.gz
• creating archive=dist/cluster-api-provider-packet_v0.1.0-next_Darwin_x86_6
4.tar.gz
• creating source archive
• pipe skipped error=source pipe is disabled
• linux packages
• snapcraft packages
• calculating checksums
• checksumming file=cluster-api-provider-packet_v0.1.0-next_Darwin_x86_64.tar.gz
• checksumming file=cluster-api-provider-packet_v0.1.0-next_Linux_arm64.tar.gz
• checksumming file=cluster-api-provider-packet_v0.1.0-next_Linux_x86_64.tar.gz
• signing artifacts
• docker images
• building docker image image=packethost/cluster-api-provider-packet:latest-amd64
• building docker image image=packethost/cluster-api-provider-packet:latest-arm64
• pipe skipped error=docker.skip_push is set
• publishing
• blobs
• pipe skipped error=blobs section is not configured
• http upload
• pipe skipped error=uploads section is not configured
• docker images
• pipe skipped error=publishing is disabled
• snapcraft packages
• pipe skipped error=publishing is disabled
• github/gitlab/gitea releases
• pipe skipped error=publishing is disabled
• homebrew tap formula
• token type type=
• scoop manifests
• pipe skipped error=publishing is disabled
• release succeeded after 8.29s
```