From 3d211d76d69c36b6ed7f7443cae6d57da98487e9 Mon Sep 17 00:00:00 2001 From: Christophe Jauffret Date: Fri, 29 Apr 2022 09:50:41 +0200 Subject: [PATCH] Improve actions and build process (#25) Improve Build and release actions: build and publish container only for release set controller image version in infrastructure-components.yaml auto update metadata.yaml change release action and define release notes based on PR labels test build on PR --- .github/release.yaml | 24 +++++ .github/workflows/build-dev.yaml | 46 ++------ .github/workflows/release.yaml | 102 ++++++++++++------ Makefile | 179 ++++++++++++++++--------------- 4 files changed, 187 insertions(+), 164 deletions(-) create mode 100644 .github/release.yaml diff --git a/.github/release.yaml b/.github/release.yaml new file mode 100644 index 0000000000..d3b94dc9db --- /dev/null +++ b/.github/release.yaml @@ -0,0 +1,24 @@ +# .github/release.yml + +changelog: + exclude: + labels: + - ignore-for-release + categories: + - title: Breaking Changes 🛠 + labels: + - Semver-Major + - breaking-change + - title: Exciting New Features 🎉 + labels: + - Semver-Minor + - enhancement + - title: Bug Fixes 🐛 + labels: + - bug + - title: Documentation 📖 + labels: + - documentation + - title: Other Changes + labels: + - "*" \ No newline at end of file diff --git a/.github/workflows/build-dev.yaml b/.github/workflows/build-dev.yaml index c9e9dd079c..2962c9429d 100644 --- a/.github/workflows/build-dev.yaml +++ b/.github/workflows/build-dev.yaml @@ -1,8 +1,11 @@ -name: build and publish container +name: Test Build on: push: + pull_request: + + jobs: build-container: runs-on: ubuntu-latest @@ -13,48 +16,11 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Login to GHCR - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Setup Go uses: actions/setup-go@v2 with: go-version: '^1.17' - - name: Docker meta - id: meta - uses: docker/metadata-action@v3 - with: - images: capi-nutanix - sep-tags: "," - sep-labels: "," - tags: | - type=semver,pattern=v{{version}} - type=semver,pattern=v{{major}}.{{minor}} - type=semver,pattern=v{{major}} - type=sha - - - name: Install tools - uses: redhat-actions/openshift-tools-installer@v1 - with: - source: "github" - ko: "latest" - - - name: Prepare build - run: make test - - - name: Build container - env: - KO_DOCKER_REPO: ghcr.io/nutanix-cloud-native/cluster-api-provider-nutanix/controller - TAGS: ${{ steps.meta.outputs.tags }} - LABELS: ${{ steps.meta.outputs.labels }} - PLATFORMS: linux/amd64,linux/arm64,linux/arm - run: | - PTAGS=`echo $TAGS | sed 's/capi-nutanix://g'` - export SOURCE_DATE_EPOCH=$(date +%s) - ko build --bare --image-label "$LABELS" -t "$PTAGS" --platform=$PLATFORMS . + - name: Test build + run: make test build diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index eaac65c12d..1bc2d3f6b5 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -6,59 +6,91 @@ on: - 'v*.*.*' jobs: - release_content: - name: Prepare Release release_content + build_release: + name: Build Release runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 + - name: Setup Go + uses: actions/setup-go@v2 + with: + go-version: '^1.17' + - name: Install tools uses: redhat-actions/openshift-tools-installer@v1 with: source: "github" kustomize: "latest" + ko: "latest" - - name: build template - run: kustomize build config/default > infrastructure-components.yaml + - name: Login to GHCR + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - - name: release - uses: actions/create-release@v1 - id: create_release + - name: Docker meta + id: meta + uses: docker/metadata-action@v3 with: - draft: false - prerelease: true - release_name: ${{ github.ref }} - tag_name: ${{ github.ref }} + images: capi-nutanix + sep-tags: "," + sep-labels: "," + tags: | + type=semver,pattern=v{{version}} + type=semver,pattern=v{{major}}.{{minor}} + type=semver,pattern=v{{major}} + type=sha + + - name: Prepare build + run: make manifests generate + + - name: Build container env: - GITHUB_TOKEN: ${{ github.token }} + KO_DOCKER_REPO: ghcr.io/${{ github.repository }}/controller + TAGS: ${{ steps.meta.outputs.tags }} + LABELS: ${{ steps.meta.outputs.labels }} + PLATFORMS: linux/amd64,linux/arm64,linux/arm + run: | + PTAGS=`echo $TAGS | sed 's/capi-nutanix://g'` + export SOURCE_DATE_EPOCH=$(date +%s) + ko build --bare --image-label "$LABELS" -t "$PTAGS" --platform=$PLATFORMS . - - name: upload infrastructure-components.yaml - uses: actions/upload-release-asset@v1 + - name: parse semver + id: semver env: - GITHUB_TOKEN: ${{ github.token }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./infrastructure-components.yaml - asset_name: infrastructure-components.yaml - asset_content_type: application/yaml + SEMVER: ${{ steps.meta.outputs.version }} + run: | + n=${SEMVER//[!0-9]/ } + a=(${n//\./ }) + echo "::set-output name=major::${a[0]}" + echo "::set-output name=minor::${a[1]}" - - name: upload metadata.yaml - uses: actions/upload-release-asset@v1 + - name: build template env: - GITHUB_TOKEN: ${{ github.token }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./metadata.yaml - asset_name: metadata.yaml - asset_content_type: application/yaml + NEW_IMG: ghcr.io/${{ github.repository }}/controller:${{ steps.meta.outputs.version }} + run: | + (cd config/manager && kustomize edit set image controller=$NEW_IMG) + kustomize build config/default > infrastructure-components.yaml - - name: upload cluster-template.yaml - uses: actions/upload-release-asset@v1 + - name: update metadata.yaml env: - GITHUB_TOKEN: ${{ github.token }} + MINOR: ${{ steps.semver.outputs.minor }} + MAJOR: ${{ steps.semver.outputs.major }} + run: | + yq -i '(.releaseSeries[] | select(.contract == "v1beta1")).major |= env(MAJOR)' metadata.yaml + yq -i '(.releaseSeries[] | select(.contract == "v1beta1")).minor |= env(MINOR)' metadata.yaml + + - name: create release + uses: softprops/action-gh-release@v1 with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./cluster-template.yaml - asset_name: cluster-template.yaml - asset_content_type: application/yaml \ No newline at end of file + draft: false + prerelease: true + generate_release_notes: true + files: | + infrastructure-components.yaml + metadata.yaml + cluster-template.yaml diff --git a/Makefile b/Makefile index eeb93c5bc6..a95411b258 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,26 @@ # Image URL to use all building/pushing image targets -#IMG ?= controller:latest -CONTROLLER_IMG_VER ?= latest -IMG ?= ghcr.io/nutanix-cloud-native/cluster-api-provider-nutanix/controller:${CONTROLLER_IMG_VER} +IMG ?= ghcr.io/nutanix/cloud-native/cluster-api-provider-nutanix/controller:latest + + +# Extract base and tag from IMG +IMG_REPO ?= $(word 1,$(subst :, ,${IMG})) +IMG_TAG ?= $(word 2,$(subst :, ,${IMG})) +ifeq (${IMG_TAG},) +IMG_TAG := latest +endif + +# PLATFORMS is a list of platforms to build for. +PLATFORMS ?= linux/amd64,linux/arm64,linux/arm + +# KIND_CLUSTER_NAME is the name of the kind cluster to use. +KIND_CLUSTER_NAME ?= capi-test + +# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. +ENVTEST_K8S_VERSION = 1.23 + +# CRD_OPTIONS define options to add to the CONTROLLER_GEN CRD_OPTIONS ?= "crd:crdVersions=v1" -export GOOS:=$(shell go env GOOS) -export GOARCH:=$(shell go env GOARCH) # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -20,7 +35,8 @@ endif SHELL = /usr/bin/env bash -o pipefail .SHELLFLAGS = -ec -all: init build clusterctl_install +.PHONY: all +all: build ##@ General @@ -35,151 +51,136 @@ all: init build clusterctl_install # More info on the awk command: # http://linuxcommand.org/lc3_adv_awk.php +.PHONY: help help: ## Display this help. @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) ##@ Development -setup: - # install kustomize - curl -L -O "https://github.com/kubernetes-sigs/kustomize/releases/download/v3.1.0/kustomize_3.1.0_$${GOOS}_$${GOARCH}" - sudo chmod +x kustomize_3.1.0_$${GOOS}_$${GOARCH} - sudo mv kustomize_3.1.0_$${GOOS}_$${GOARCH} /usr/local/bin/kustomize - - # install kubebuilder - curl -L -O "https://github.com/kubernetes-sigs/kubebuilder/releases/download/v1.0.8/kubebuilder_1.0.8_$${GOOS}_$${GOARCH}.tar.gz" - tar -zxvf kubebuilder_1.0.8_$${GOOS}_$${GOARCH}.tar.gz - mv kubebuilder_1.0.8_$${GOOS}_$${GOARCH} kubebuilder - sudo mv kubebuilder /usr/local/ - rm kubebuilder_1.0.8_$${GOOS}_$${GOARCH}.tar.gz - - # install clusterctl - curl -L https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.1.3/clusterctl-${GOOS}-${GOARCH} -o clusterctl - chmod +x ./clusterctl - sudo mv ./clusterctl /usr/local/bin/clusterctl - -init: kind-create setup - -CLUSTER_NAME = capi-test1 -kind-create: ##Get the relevant clustercrl for the latest supported cluster API version - # create kind cluster - kind create cluster --name=${CLUSTER_NAME} - clusterctl init --core cluster-api:v1.0.0 --bootstrap kubeadm:v1.0.0 --control-plane kubeadm:v1.0.0 -v 9 - -kind-delete: - # delete kind cluster - kind delete cluster --name=${CLUSTER_NAME} +.PHONY: manifests manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. - $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases + $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases -# Set --output-base for conversion-gen if we are not within GOPATH -#ifneq ($(abspath $(PROJECT_DIR)),$(shell go env GOPATH)/src/github.com/nutanix-cloud-native/cluster-api-provider-nutanix) -# OUTPUT_BASE := --output-base=$(PROJECT_DIR) -#endif - -generate: controller-gen conversion-gen## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations, and API conversion implementations. +.PHONY: generate +generate: controller-gen conversion-gen ## Generate code containing DeepCopy, DeepCopyInto, DeepCopyObject method implementations and API conversion implementations. $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." $(CONVERSION_GEN) \ - --input-dirs=./api/v1alpha4 \ - --input-dirs=./api/v1beta1 \ - --build-tag=ignore_autogenerated_core \ - --output-file-base=zz_generated.conversion \ - --go-header-file=./hack/boilerplate.go.txt + --input-dirs=./api/v1alpha4 \ + --input-dirs=./api/v1beta1 \ + --build-tag=ignore_autogenerated_core \ + --output-file-base=zz_generated.conversion \ + --go-header-file=./hack/boilerplate.go.txt +.PHONY: fmt fmt: ## Run go fmt against code. go fmt ./... +.PHONY: vet vet: ## Run go vet against code. go vet ./... -ENVTEST_ASSETS_DIR=$(shell pwd)/testbin -test: manifests generate fmt vet ## Run tests. - mkdir -p ${ENVTEST_ASSETS_DIR} - test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.8.3/hack/setup-envtest.sh - source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... -coverprofile cover.out +.PHONY: test +test: manifests generate fmt vet envtest ## Run tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --arch=amd64 -p path)" go test ./... -coverprofile cover.out + + +kind-create: ## Create a kind cluster and deploy the latest supported cluster API version + kind create cluster --name=${KIND_CLUSTER_NAME} + clusterctl init -v 9 + +kind-delete: ## Delete the kind cluster + kind delete cluster --name=${KIND_CLUSTER_NAME} ##@ Build +.PHONY: build build: generate fmt vet ## Build manager binary. go build -o bin/manager main.go +.PHONY: run run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go +.PHONY: docker-build docker-build: test ## Build docker image with the manager. - docker build -f package/docker/Dockerfile -t ${IMG} . + KO_DOCKER_REPO=ko.local ko build -B --platform=${PLATFORMS} -t ${IMG_TAG} -L . -docker-push: ## Push docker image with the manager. - docker push ${IMG} +.PHONY: docker-push +docker-push: test ## Push docker image with the manager. + KO_DOCKER_REPO=${IMG_REPO} ko build --bare --platform=${PLATFORMS} -t ${IMG_TAG} . -docker-push-kind: ## Make docker image available to kind cluster. - kind load docker-image --name ${CLUSTER_NAME} ${IMG} +.PHONY: docker-push-kind +docker-push-kind: test ## Make docker image available to kind cluster. + GOOS=linux GOARCH=${shell go env GOARCH} KO_DOCKER_REPO=ko.local ko build -B -t ${IMG_TAG} -L . + docker tag ko.local/cluster-api-provider-nutanix:${IMG_TAG} ${IMG} + kind load docker-image --name ${KIND_CLUSTER_NAME} ${IMG} ##@ Deployment +ifndef ignore-not-found + ignore-not-found = false +endif + +.PHONY: install install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. $(KUSTOMIZE) build config/crd | kubectl apply -f - -uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | kubectl delete -f - +.PHONY: uninstall +uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f - -TEST_NAMESPACE = test-ns-nutanix-capi -deploy: prepare-local-clusterctl docker-push-kind ## Deploy controller to the K8s cluster specified in ~/.kube/config. +.PHONY: deploy +deploy: manifests kustomize prepare-local-clusterctl docker-push-kind ## Deploy controller to the K8s cluster specified in ~/.kube/config. clusterctl init --infrastructure nutanix -v 9 - # TODO verify if we need to restart the controller pod to use latest uploaded image in kind cluster # cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} # $(KUSTOMIZE) build config/default | kubectl apply -f - -TEST_CLUSTER_NAME = test-nutanix-cluster-1 -deploy-test: ## Deploy controller to the K8s cluster specified in ~/.kube/config. - rm ./cluster.yaml || true - clusterctl generate cluster ${TEST_CLUSTER_NAME} --infrastructure nutanix:v0.2.0 --target-namespace ${TEST_NAMESPACE} --list-variables - clusterctl generate cluster ${TEST_CLUSTER_NAME} --infrastructure nutanix:v0.2.0 --target-namespace ${TEST_NAMESPACE} > ./cluster.yaml - kubectl create ns $(TEST_NAMESPACE) || true - kubectl apply -f ./cluster.yaml -n $(TEST_NAMESPACE) - -undeploy-test: - kubectl delete -f ./cluster.yaml -n $(TEST_NAMESPACE) - kubectl delete ns $(TEST_NAMESPACE) +.PHONY: undeploy +undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f - -undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. - # $(KUSTOMIZE) build config/default | kubectl delete -f - - clusterctl delete --include-crd --infrastructure nutanix:v0.2.0 - -release-manifests: - mkdir -p release-manifests - $(KUSTOMIZE) build config/default > release-manifests/infrastructure-components.yaml - -prepare-local-clusterctl: +.PHONY: prepare-local-clusterctl +prepare-local-clusterctl: manifests kustomize ## Prepare overide file for local clusterctl. mkdir -p ~/.cluster-api/overrides/infrastructure-nutanix/v0.2.0 cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} $(KUSTOMIZE) build config/default > ~/.cluster-api/overrides/infrastructure-nutanix/v0.2.0/infrastructure-components.yaml cp ./metadata.yaml ~/.cluster-api/overrides/infrastructure-nutanix/v0.2.0 cp ./cluster-template.yaml ~/.cluster-api/overrides/infrastructure-nutanix/v0.2.0 - cp ./clusterctl.yaml ~/.cluster-api/ + cp ./clusterctl.yaml ~/.cluster-api/clusterctl.yaml.template CONTROLLER_GEN = $(shell pwd)/bin/controller-gen +.PHONY: controller-gen controller-gen: ## Download controller-gen locally if necessary. - rm -f $(CONTROLLER_GEN) - $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.7.0) + $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0) CONVERSION_GEN = $(shell pwd)/bin/conversion-gen +.PHONY: conversion-gen conversion-gen: ## Download conversion-gen locally if necessary. rm -f $(CONVERSION_GEN) - $(call go-get-tool,$(CONVERSION_GEN),k8s.io/code-generator/cmd/conversion-gen@v0.22.2) + $(call go-get-tool,$(CONVERSION_GEN),k8s.io/code-generator/cmd/conversion-gen@v0.23.6) KUSTOMIZE = $(shell pwd)/bin/kustomize +.PHONY: kustomize kustomize: ## Download kustomize locally if necessary. - rm -f $(KUSTOMIZE) - $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v4@v4.5.2) + $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v4@v4.5.4) + +ENVTEST = $(shell pwd)/bin/setup-envtest +.PHONY: envtest +envtest: ## Download envtest-setup locally if necessary. + $(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) + +KO = $(shell pwd)/bin/ko +.PHONY: ko +ko: ## Download ko locally if necessary. + $(call go-get-tool,$(KO),github.com/google/ko@latest) # go-get-tool will 'go get' any package $2 and install it to $1. PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) define go-get-tool @[ -f $(1) ] || { \ set -e ;\ -echo "Downloading $(2) in $(PROJECT_DIR)/bin" ;\ +echo "Downloading $(2)" ;\ GOBIN=$(PROJECT_DIR)/bin go install $(2) ;\ } endef