From 34feda67428c559836d28929c400cc7f0d1065a9 Mon Sep 17 00:00:00 2001 From: Carlo Lobrano Date: Mon, 29 Jul 2024 13:03:43 +0200 Subject: [PATCH] Automate upstream bundle creation Add CI automation to create bundles for K8S and OKD Removed "replaces" field from config as the field is only required in OKD. --- .github/workflows/post-submit.yaml | 48 ++------ .github/workflows/release.yaml | 107 ++++++++++++++++++ Makefile | 60 ++++++++-- ...enance-operator.clusterserviceversion.yaml | 1 - hack/semver_cmp.sh | 28 +++++ 5 files changed, 197 insertions(+), 47 deletions(-) create mode 100644 .github/workflows/release.yaml create mode 100755 hack/semver_cmp.sh diff --git a/.github/workflows/post-submit.yaml b/.github/workflows/post-submit.yaml index 170507424..5b3fcb374 100644 --- a/.github/workflows/post-submit.yaml +++ b/.github/workflows/post-submit.yaml @@ -11,54 +11,26 @@ permissions: pull-requests: read jobs: - push-images: - name: Build and push images to quay.io/medik8s + push_to_registry: + name: Build and push unversioned images to quay.io/medik8s runs-on: ubuntu-22.04 steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + - name: Log in to Quay.io - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.QUAY_USERNAME }} password: ${{ secrets.QUAY_PASSWORD }} registry: quay.io - - name: Build and push CSV 0.0.1 + latest images for PR merges to main - if: ${{ github.ref_type != 'tag' }} + - name: Build and push CSV version 0.0.1 with latest images run: export IMAGE_REGISTRY=quay.io/medik8s && make container-build-and-push-community - - - name: Build and push versioned CSV and images for tags - if: ${{ github.ref_type == 'tag' }} - # remove leading 'v' from tag! - run: export VERSION=$(echo $GITHUB_REF_NAME | sed 's/v//') && make container-build-and-push-community - - - name: Create release with manifests - if: ${{ github.ref_type == 'tag' }} - # https://github.com/marketplace/actions/github-release-create-update-and-upload-assets - uses: meeDamian/github-release@2.0 - with: - token: ${{ secrets.GITHUB_TOKEN }} - draft: true - body: | - # Node Maintenance Operator ${{ github.ref_name }} - - ## Notable Changes - - * TODO - - ## Release Artifacts - - ### Images - * Operator: quay.io/medik8s/node-maintenance-operator:${{ github.ref_name }} - * Bundle: quay.io/medik8s/node-maintenance-operator-bundle:${{ github.ref_name }} - * Catalog aka Index: quay.io/medik8s/node-maintenance-operator-catalog:${{ github.ref_name }} - - ### Source code and OLM manifests - Please find the source code and the OLM manifests in the `Assets` section below. - gzip: folders - files: > - Manifests:bundle/ diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 000000000..463a53e81 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,107 @@ +name: Release +on: + workflow_dispatch: + inputs: + operation: + description: "The operation to perform." + required: true + type: choice + default: "build_and_push_images" + options: + - build_and_push_images + - create_okd_release_pr + - create_k8s_release_pr + version: + description: "The version to release, without the leading `v`" + required: true + previous_version: + description: "The previous version, used for the CVS's `replaces` field, without the leading `v`" + required: true + ocp_version: + description: "The target OCP version for the release (mandatory for create_okd_release_pr option)" + required: false + +permissions: + contents: write + +jobs: + push_to_registry: + if: ${{ inputs.operation == 'build_and_push_images' }} + name: Build and push versioned images to quay.io/medik8s + runs-on: ubuntu-22.04 + env: + VERSION: ${{ inputs.version }} + PREVIOUS_VERSION: ${{ inputs.previous_version }} + OCP_VERSION: ${{ inputs.ocp_version }} + steps: + - name: Log inputs + run: | + echo "Building version: ${VERSION}," + echo "which replaces version: ${PREVIOUS_VERSION}." + + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Log in to Quay.io + uses: docker/login-action@v3 + with: + username: ${{ secrets.QUAY_USERNAME }} + password: ${{ secrets.QUAY_PASSWORD }} + registry: quay.io + + - name: Build and push versioned CSV and images + run: VERSION=$VERSION PREVIOUS_VERSION=$PREVIOUS_VERSION make container-build-community container-push + + - name: Create release with manifests, for tags + # https://github.com/marketplace/actions/github-release-create-update-and-upload-assets + uses: meeDamian/github-release@2.0 + with: + tag: v${{ inputs.version }} + token: ${{ secrets.GITHUB_TOKEN }} + draft: true + body: | + # Node Maintenance Operator ${{ inputs.version }} + + ## Notable Changes + + * TODO + + ## Release Artifacts + + ### Images + * Operator: quay.io/medik8s/node-maintenance-operator:v${{ inputs.version }} + * Bundle: quay.io/medik8s/node-maintenance-operator-bundle:v${{ inputs.version }} + * Catalog aka Index: quay.io/medik8s/node-maintenance-operator-catalog:v${{ inputs.version }} + + ### Source code and OLM manifests + Please find the source code and the OLM manifests in the `Assets` section below. + gzip: folders + files: > + Manifests:bundle/ + + create_k8s_release_pr: + if: inputs.operation == 'create_k8s_release_pr' + uses: medik8s/.github/.github/workflows/release_community_bundle_parametric.yaml@main + secrets: inherit + with: + version: ${{ inputs.version }} + previous_version: ${{ inputs.previous_version }} + community: 'K8S' + make_targets: "bundle-community-k8s" + create_okd_release_pr: + if: inputs.operation == 'create_okd_release_pr' + uses: medik8s/.github/.github/workflows/release_community_bundle_parametric.yaml@main + secrets: inherit + with: + version: ${{ inputs.version }} + previous_version: ${{ inputs.previous_version }} + ocp_version: ${{ inputs.ocp_version }} + community: 'OKD' + make_targets: "bundle-community-okd" diff --git a/Makefile b/Makefile index 809e39847..a795e5507 100755 --- a/Makefile +++ b/Makefile @@ -20,6 +20,12 @@ GO_VERSION = 1.20 ENVTEST_K8S_VERSION = 1.28 # See https://github.com/slintes/sort-imports/releases for the last version SORT_IMPORTS_VERSION = v0.2.1 +# OCP Version: for OKD bundle community +OCP_VERSION ?= 4.12 +# update for major version updates to YQ_VERSION! see https://github.com/mikefarah/yq +# NOTE: v4.42.1 is the latest supporting go 1.20 +YQ_API_VERSION = v4 +YQ_VERSION = v4.42.1 # IMAGE_REGISTRY used to indicate the registery/group for the operator, bundle and catalog IMAGE_REGISTRY ?= quay.io/medik8s @@ -212,18 +218,26 @@ export ICON_BASE64 ?= ${DEFAULT_ICON_BASE64} export CSV ?= "./bundle/manifests/$(OPERATOR_NAME).clusterserviceversion.yaml" .PHONY: bundle-update -bundle-update: verify-previous-version ## Update CSV fields and validate the bundle directory +bundle-update: ## Update CSV fields and validate the bundle directory sed -r -i "s|containerImage: .*|containerImage: $(IMG)|;" ${CSV} sed -r -i "s|createdAt: .*|createdAt: `date '+%Y-%m-%d %T'`|;" ${CSV} - sed -r -i "s|replaces: .*|replaces: $(OPERATOR_NAME).v${PREVIOUS_VERSION}|;" ${CSV} sed -r -i "s|base64data:.*|base64data: ${ICON_BASE64}|;" ${CSV} $(MAKE) bundle-validate -.PHONY: verify-previous-version -verify-previous-version: ## Verifies that PREVIOUS_VERSION variable is set - @if [ $(VERSION) != $(DEFAULT_VERSION) ] && [ $(VERSION) != $(CI_VERSION) ] && [ $(PREVIOUS_VERSION) = $(DEFAULT_VERSION) ]; then \ - echo "Error: PREVIOUS_VERSION must be set for the selected VERSION"; \ - exit 1; \ +.PHONY: add-replaces-field +add-replaces-field: ## Add replaces field to the CSV + # add replaces field when building versioned bundle + @if [ $(VERSION) != $(DEFAULT_VERSION) ]; then \ + if [ $(PREVIOUS_VERSION) == $(DEFAULT_VERSION) ]; then \ + echo "Error: PREVIOUS_VERSION must be set for versioned builds"; \ + exit 1; \ + elif [ $(shell ./hack/semver_cmp.sh $(VERSION) $(PREVIOUS_VERSION)) != 1 ]; then \ + echo "Error: VERSION ($(VERSION)) must be greater than PREVIOUS_VERSION ($(PREVIOUS_VERSION))"; \ + exit 1; \ + else \ + # preferring sed here, in order to have "replaces" near "version" \ + sed -r -i "/ version: $(VERSION)/ a\ replaces: $(OPERATOR_NAME).v$(PREVIOUS_VERSION)" ${CSV}; \ + fi \ fi .PHONY: bundle-reset-date @@ -231,10 +245,34 @@ bundle-reset-date: ## Reset bundle's createdAt sed -r -i "s|createdAt: .*|createdAt: \"\"|;" ${CSV} .PHONY: bundle-community -bundle-community: bundle-k8s ## Update displayName, and description fields in the bundle's CSV +bundle-community: ## Update displayName, and description fields in the bundle's CSV sed -r -i "s|displayName: Node Maintenance Operator|displayName: Node Maintenance Operator - Community Edition |;" ${CSV} $(MAKE) bundle-update + +.PHONY: bundle-community-k8s +bundle-community-k8s: bundle-k8s bundle-community ## Generate bundle manifests and metadata customized to Red Hat community release + +.PHONY: bundle-community-okd +bundle-community-okd: bundle bundle-community ## Generate bundle manifests and metadata customized to Red Hat community release + $(MAKE) add-replaces-field + $(MAKE) add-ocp-annotations + echo -e "\n # Annotations for OCP\n com.redhat.openshift.versions: \"v${OCP_VERSION}\"" >> bundle/metadata/annotations.yaml + + +.PHONY: add-ocp-annotations +add-ocp-annotations: yq ## Add OCP annotations + $(YQ) -i '.metadata.annotations."operators.openshift.io/valid-subscription" = "[\"OpenShift Kubernetes Engine\", \"OpenShift Container Platform\", \"OpenShift Platform Plus\"]"' ${CSV} + # new infrastructure annotations see https://docs.engineering.redhat.com/display/CFC/Best_Practices#Best_Practices-(New)RequiredInfrastructureAnnotations + $(YQ) -i '.metadata.annotations."features.operators.openshift.io/disconnected" = "true"' ${CSV} + $(YQ) -i '.metadata.annotations."features.operators.openshift.io/fips-compliant" = "false"' ${CSV} + $(YQ) -i '.metadata.annotations."features.operators.openshift.io/proxy-aware" = "false"' ${CSV} + $(YQ) -i '.metadata.annotations."features.operators.openshift.io/tls-profiles" = "false"' ${CSV} + $(YQ) -i '.metadata.annotations."features.operators.openshift.io/token-auth-aws" = "false"' ${CSV} + $(YQ) -i '.metadata.annotations."features.operators.openshift.io/token-auth-azure" = "false"' ${CSV} + $(YQ) -i '.metadata.annotations."features.operators.openshift.io/token-auth-gcp" = "false"' ${CSV} + + ##@ Build .PHONY: build @@ -291,6 +329,7 @@ GINKGO_DIR ?= $(LOCALBIN)/ginkgo OPM_DIR = $(LOCALBIN)/opm OPERATOR_SDK_DIR ?= $(LOCALBIN)/operator-sdk SORT_IMPORTS_DIR ?= $(LOCALBIN)/sort-imports +YQ_DIR ?= $(LOCALBIN)/yq ## Specific Tool Binaries KUSTOMIZE = $(KUSTOMIZE_DIR)/$(KUSTOMIZE_VERSION)/kustomize @@ -301,6 +340,7 @@ GINKGO = $(GINKGO_DIR)/$(GINKGO_VERSION)/ginkgo OPM = $(OPM_DIR)/$(OPM_VERSION)/opm OPERATOR_SDK = $(OPERATOR_SDK_DIR)/$(OPERATOR_SDK_VERSION)/operator-sdk SORT_IMPORTS = $(SORT_IMPORTS_DIR)/$(SORT_IMPORTS_VERSION)/sort-imports +YQ = $(YQ_DIR)/$(YQ_API_VERSION)-$(YQ_VERSION)/yq .PHONY: kustomize kustomize: ## Download kustomize locally if necessary. @@ -326,6 +366,10 @@ ginkgo: ## Download ginkgo locally if necessary. sort-imports: ## Download sort-imports locally if necessary. $(call go-install-tool,$(SORT_IMPORTS),$(SORT_IMPORTS_DIR),github.com/slintes/sort-imports@$(SORT_IMPORTS_VERSION)) +.PHONY: yq +yq: ## Download yq locally if necessary. + $(call go-install-tool,$(YQ),$(YQ_DIR), github.com/mikefarah/yq/$(YQ_API_VERSION)@$(YQ_VERSION)) + # go-install-tool will delete old package $2, then 'go install' any package $3 to $1. define go-install-tool @[ -f $(1) ]|| { \ diff --git a/config/manifests/bases/node-maintenance-operator.clusterserviceversion.yaml b/config/manifests/bases/node-maintenance-operator.clusterserviceversion.yaml index a3775f617..ea601ea7f 100644 --- a/config/manifests/bases/node-maintenance-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/node-maintenance-operator.clusterserviceversion.yaml @@ -101,5 +101,4 @@ spec: provider: name: Medik8s url: https://github.com/medik8s - replaces: node-maintenance-operator.v0.0.1 version: 0.0.0 diff --git a/hack/semver_cmp.sh b/hack/semver_cmp.sh new file mode 100755 index 000000000..3a8628fca --- /dev/null +++ b/hack/semver_cmp.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# -*- coding: UTF-8 -*- +## Compare two semantic version numbers. +## semver_cmp.sh VER1 VER2 is equivalent to test if "VER1 >= VER2" +## Usage: +## - semver_cmp.sh 1.2.3 1.2.4 ## -1 VER1 is < than VER2 +## - semver_cmp.sh 1.2.4 1.2.4 ## 0 VER1 is == to VER2 +## - semver_cmp.sh 1.2.5 1.2.4 ## 1 VER1 is > than VER2 +## other tests +## - semver_cmp.sh 1.2 1.2.4 ## -1 +## - semver_cmp.sh 1.2 1.2 ## 0 +## - semver_cmp.sh 1.3 1.2.9 ## 1 + +if [ "$#" -ne 2 ]; then + echo "Illegal number of parameters" + exit 1 +fi + +if [ "$1" = "$2" ]; then + echo 0 +else + # sort the input and check if it is sorted (quietly). + # `sort` will exit successfully if the given file is already sorted, and exit with status 1 otherwise. + # Since we already excluded that the two versions are equal, if the input is sorted, + # it means the first argument is less than the second one. + # https://www.gnu.org/software/coreutils/manual/html_node/sort-invocation.html#sort-invocation + printf "%s\n%s\n" "$1" "$2" | sort --version-sort --check=quiet && echo -1 || echo 1 +fi