From 2d0af7985d969f68131e5bd3a6726f2708b8c1b8 Mon Sep 17 00:00:00 2001 From: Mateusz Urbanek Date: Wed, 27 Mar 2024 12:58:04 +0100 Subject: [PATCH] ci: added release automation (#20) ## What this PR does / why we need it: This pull request adds release automation to the project by introducing a GitHub Action workflow named `00-release-please.yaml`. The workflow utilizes the `release-please` GitHub Action to automate the release process. It also includes a configuration file `release-please-config.json` that specifies the release types for different packages within the project, such as Go packages and Helm charts. ## Which issue(s) this PR resolves: Resolves #13 ## Special notes for your reviewer: The key changes include: - Addition of `00-release-please.yaml` GitHub Action workflow for release automation. - Introduction of `release-please-config.json` specifying release types for different packages. - Introduction of `.release-please-manifest.json` specifying the initial (and current, auto updated) version for each package. - Renaming of `.github/workflows/0-k8s-linters.yaml` to `.github/workflows/10-k8s.yaml` with adjustments in the workflow content: - The `package` job packages the Helm chart and pushes it to the registry. - The `package` job includes conditional steps based on the event type: - If it's a tag event (`tags: - 'linode-cosi-driver-v*'`), the workflow captures the latest tag, sets proper tags, and packages the Helm chart. - Steps are included to set up Helm, package artifacts. - The workflow uses the `softprops/action-gh-release` GitHub Action to upload release artifacts. - The workflow captures the latest tag if it exists and sets up proper tags using a script (`scripts/tags.sh`). - The version information is used for packaging and pushing the Helm chart. - Renaming of `.github/workflows/10-linters-tests.yaml` to `.github/workflows/10-linters-tests-image.yaml` with adjustments in the workflow content: - Now description explicitly mentions running linters and tests, and publishing a new image during releases or pushes to the main branch. - Triggers on each push to the main branch (`branches: - "main"`) and on each release (`tags: - 'v*'`). - Added `REGISTRY`, `REPOSITORY`, and `IMAGE` environment variables for Docker registry configuration. - Steps for building and pushing the Docker image based on release or main branch push events. - Introduction of `scripts/tags.sh` script for generating version and tags based on input parameters. - Utilization of the script to set up proper tags and versioning for the Docker image. - Definition of output variables (`grype` and `tags`) to capture image scan and Docker image tags information. ## Additional documentation e.g., enhancement proposals, usage docs, etc.: The changes aim to streamline the release process and enhance the project's automation. The `release-please` GitHub Action is configured to trigger on each push to the main branch, automating the creation of releases based on semantic versioning and updating the version information accordingly. --------- Signed-off-by: Mateusz Urbanek Release-As: 0.1.0 --- .github/release-please-config.json | 10 ++ .github/workflows/00-release-please.yaml | 23 +++ .github/workflows/10-k8s-linters.yaml | 24 ---- .github/workflows/10-k8s.yaml | 71 ++++++++++ .github/workflows/10-linters-tests-image.yaml | 131 ++++++++++++++++++ .github/workflows/10-linters-tests.yaml | 97 ------------- .github/workflows/99-release.yaml | 35 ----- .gitignore | 3 + .release-please-manifest.json | 4 + Dockerfile | 2 +- Makefile | 9 +- githooks/install-hooks.sh | 6 +- go.mod | 2 +- helm/linode-cosi-driver/Chart.yaml | 20 +-- helm/linode-cosi-driver/README.md | 2 +- .../linode-cosi-driver/templates/_helpers.tpl | 14 +- scripts/tags.sh | 53 +++++++ 17 files changed, 322 insertions(+), 184 deletions(-) create mode 100644 .github/release-please-config.json create mode 100644 .github/workflows/00-release-please.yaml delete mode 100644 .github/workflows/10-k8s-linters.yaml create mode 100644 .github/workflows/10-k8s.yaml create mode 100644 .github/workflows/10-linters-tests-image.yaml delete mode 100644 .github/workflows/10-linters-tests.yaml delete mode 100644 .github/workflows/99-release.yaml create mode 100644 .release-please-manifest.json create mode 100755 scripts/tags.sh diff --git a/.github/release-please-config.json b/.github/release-please-config.json new file mode 100644 index 0000000..7dabd42 --- /dev/null +++ b/.github/release-please-config.json @@ -0,0 +1,10 @@ +{ + "packages": { + ".": { + "release-type": "go" + }, + "helm/linode-cosi-driver": { + "release-type": "helm" + } + } +} diff --git a/.github/workflows/00-release-please.yaml b/.github/workflows/00-release-please.yaml new file mode 100644 index 0000000..43262cf --- /dev/null +++ b/.github/workflows/00-release-please.yaml @@ -0,0 +1,23 @@ +# This workflow runs release please GitHub action. It is supposed to be run on each +# push to main branch. + +on: + push: + branches: + - main + +permissions: + contents: write + pull-requests: write + +name: Release Please + +jobs: + release-please: + name: Release Please + runs-on: ubuntu-latest + steps: + - uses: google-github-actions/release-please-action@v4 + with: + token: ${{ secrets.PAT }} # need to use separate PAT from GITHUB_TOKEN, so new actions can be triggered + config-file: .github/release-please-config.json diff --git a/.github/workflows/10-k8s-linters.yaml b/.github/workflows/10-k8s-linters.yaml deleted file mode 100644 index f7019a3..0000000 --- a/.github/workflows/10-k8s-linters.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# This workflow run linters on kubernetes resources. -# It is supposed to be run on each push to main branch, as well as for each push in pull request. - -name: K8s Linters - -on: - pull_request: - branches: [ '*' ] - paths: - - 'helm/**' - -jobs: - linters: - name: Lint Kubernetes manifests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Scan repo with kube-linter - uses: stackrox/kube-linter-action@v1 - with: - directory: helm/ - config: helm/.kube-linter.yaml diff --git a/.github/workflows/10-k8s.yaml b/.github/workflows/10-k8s.yaml new file mode 100644 index 0000000..5ad9b0d --- /dev/null +++ b/.github/workflows/10-k8s.yaml @@ -0,0 +1,71 @@ +# This workflow runs linters on kubernetes resources. It is supposed to be for each push +# in pull request that modifies helm chart. + +name: Kubernetes + +on: + pull_request: + branches: [ '*' ] + paths: + - 'helm/**' + push: + tags: + - 'linode-cosi-driver-v*' + +env: + REGISTRY: docker.io + REPOSITORY: linode + +permissions: + contents: write + +jobs: + linters: + name: Lint Kubernetes manifests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Scan repo with kube-linter + uses: stackrox/kube-linter-action@v1 + with: + directory: helm/ + config: helm/.kube-linter.yaml + + package: + name: Package helm chart + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/linode-cosi-driver-v') + steps: + - uses: actions/checkout@v4 + - name: Capture latest tag if exists + uses: actions-ecosystem/action-get-latest-tag@v1 + id: get-latest-tag + with: + semver_only: true + with_initial_version: true + initial_version: v0.0.0 + - name: Set proper tags + id: tags + run: | + ./scripts/tags.sh \ + "${{ env.REGISTRY }}/${{ env.REPOSITORY }}" \ + "${{ github.sha }}" \ + "${{ github.ref_name }}" \ + "${{ steps.get-latest-tag.outputs.tag }}" \ + "${GITHUB_OUTPUT}" + - name: Setup helm + uses: azure/setup-helm@v4 + - name: Package artifacts + run: | + helm package \ + --destination=release \ + --version=${{ steps.tags.outputs.chart }} \ + helm/linode-cosi-driver + - name: Upload Release Artifacts + uses: softprops/action-gh-release@v2 + with: + name: ${{ github.ref_name }} + files: | + ./release/linode-cosi-driver-${{ steps.tags.outputs.chart }}.tgz diff --git a/.github/workflows/10-linters-tests-image.yaml b/.github/workflows/10-linters-tests-image.yaml new file mode 100644 index 0000000..6e485da --- /dev/null +++ b/.github/workflows/10-linters-tests-image.yaml @@ -0,0 +1,131 @@ +# This workflow run linters and tests, and publishes a new image if there is a release or +# push to main. It is supposed to be run on each push to main branch, on each release, as +# well as for each push in pull request. + +name: Code and Image workflow + +on: + pull_request: + branches: [ '*' ] + push: + branches: + - "main" + tags: + - 'v*' + +env: + REGISTRY: docker.io + REPOSITORY: linode + IMAGE: linode-cosi-driver + +permissions: + contents: read + +jobs: + commitlint: + name: Lint commit messages + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: wagoid/commitlint-github-action@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + golangci-lint: + name: Run golangci-lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./go.mod + cache: false + - uses: golangci/golangci-lint-action@v4 + with: + version: latest + skip-cache: true + + shell-linter: + name: Run Shellcheck, Checkmake + runs-on: ubuntu-latest + steps: + - name: Checkout the code + uses: actions/checkout@v4 + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + - uses: actions/setup-go@v5 + with: + go-version-file: ./go.mod + cache: false + - name: Install Checkmake + run: go install github.com/mrtazz/checkmake/cmd/checkmake@latest + - name: Run Checkmake + run: checkmake Makefile + - name: Run Checkmake on tests + run: checkmake test/Makefile + + tests: + name: Run unit tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./go.mod + cache: false + - run: | + make test + + docker: + name: Build dev image and run scans + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Login to registry + if: startsWith(github.ref, 'refs/tags/v') || (github.ref == 'refs/heads/main') + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_TOKEN }} + - name: Capture latest tag if exists + uses: actions-ecosystem/action-get-latest-tag@v1 + id: get-latest-tag + with: + semver_only: true + with_initial_version: true + initial_version: v0.0.0 + - name: Set proper tags + id: tags + run: | + ./scripts/tags.sh \ + "${{ env.REGISTRY }}/${{ env.REPOSITORY }}/${{ env.IMAGE }}" \ + "${{ github.sha }}" \ + "${{ github.ref_name }}" \ + "${{ steps.get-latest-tag.outputs.tag }}" \ + "${GITHUB_OUTPUT}" + - name: Build image + uses: docker/build-push-action@v5 + with: + push: ${{ startsWith(github.ref, 'refs/tags/v') || (github.ref == 'refs/heads/main') }} + load: ${{ !(startsWith(github.ref, 'refs/tags/v') || (github.ref == 'refs/heads/main')) }} + tags: ${{ steps.tags.outputs.all }} + build-args: | + VERSION=${{ steps.tags.outputs.version }} + target: runtime + - name: Scan image using Grype + id: grype + uses: anchore/scan-action@v3 + with: + image: ${{ steps.tags.outputs.full_version }} + output-format: table + - name: Scan image using Trivy + if: steps.grype.outcome == 'success' || steps.grype.outcome == 'failure' + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ steps.tags.outputs.full_version }} + format: table + exit-code: '1' + severity: 'CRITICAL,HIGH,MEDIUM' diff --git a/.github/workflows/10-linters-tests.yaml b/.github/workflows/10-linters-tests.yaml deleted file mode 100644 index d9aafc4..0000000 --- a/.github/workflows/10-linters-tests.yaml +++ /dev/null @@ -1,97 +0,0 @@ -# This workflow run linters and tests. -# It is supposed to be run on each push to main branch, as well as for each push in pull request. - -name: Linters - -on: - pull_request: - branches: [ '*' ] - -env: - GO_VERSION: "1.21" - IMAGE: linode/linode-cosi-driver - -permissions: - contents: read - -jobs: - commitlint: - name: Lint commit messages - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: wagoid/commitlint-github-action@v5 - - golangci-lint: - name: Run golangci-lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: ${{ env.GO_VERSION }} - cache: false - - uses: golangci/golangci-lint-action@v4 - with: - version: latest - skip-cache: true - - shell-linter: - name: Run Shellcheck, Checkmake - runs-on: ubuntu-latest - steps: - - name: Checkout the code - uses: actions/checkout@v4 - - name: Run ShellCheck - uses: ludeeus/action-shellcheck@master - - uses: actions/setup-go@v5 - with: - go-version: ${{ env.GO_VERSION }} - cache: false - - name: Install Checkmake - run: go install github.com/mrtazz/checkmake/cmd/checkmake@latest - - name: Run Checkmake - run: checkmake Makefile - - name: Run Checkmake on tests - run: checkmake test/Makefile - - tests: - name: Run unit tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: ${{ env.GO_VERSION }} - cache: false - - run: | - make test - - docker: - name: Build dev image and run scans - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Build image - uses: docker/build-push-action@v5 - with: - push: false - load: true - tags: ${{ env.IMAGE }}:${{ github.sha }} - build-args: | - VERSION=${{ github.sha }} - target: runtime - - name: Scan image using Grype - uses: anchore/scan-action@v3 - with: - image: ${{ env.IMAGE }}:${{ github.sha }} - output-format: table - - name: Scan image using Trivy - uses: aquasecurity/trivy-action@master - with: - image-ref: ${{ env.IMAGE }}:${{ github.sha }} - format: table - exit-code: '1' - severity: 'CRITICAL,HIGH,MEDIUM' diff --git a/.github/workflows/99-release.yaml b/.github/workflows/99-release.yaml deleted file mode 100644 index a4a7b25..0000000 --- a/.github/workflows/99-release.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# This workflow is used to run all necessary actions after the release. -# This should include building and pushing the image. -# It is supposed to be run on each new release. - -name: Release - -on: - push: - tags: - - 'v*' - -env: - REGISTRY: docker.io - IMAGE: linode/linode-cosi-driver - -jobs: - docker: - name: Build dev image and run scans - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Login to registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ secrets.REGISTRY_USERNAME }} - password: ${{ secrets.REGISTRY_TOKEN }} - - name: Build and push image - uses: docker/build-push-action@v5 - with: - push: true - tags: ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ github.ref_name }} - build-args: | - VERSION=${{ github.ref_name }} - target: runtime diff --git a/.gitignore b/.gitignore index 143220b..b191e41 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ go.work # MacOS attributes files .DS_Store + +# Release artifacts +release/ diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..707f4b6 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,4 @@ +{ + ".": "0.1.0", + "helm/linode-cosi-driver": "0.1.0" +} diff --git a/Dockerfile b/Dockerfile index b42ab19..aaf621e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -ARG TOOLCHAIN_VERSION=1.21 +ARG TOOLCHAIN_VERSION=1.22 ######################################################################################### # Build diff --git a/Makefile b/Makefile index 57d2005..b258ad2 100644 --- a/Makefile +++ b/Makefile @@ -22,11 +22,12 @@ VERSION ?= $(shell git tag | tail -n 1 | grep '' || echo 'v0.0.0')$(shell git di TOOLCHAIN_VERSION := $(shell sed -En 's/^go (.*)$$/\1/p' go.mod) MODULE_NAME := $(shell sed -En 's/^module (.*)$$/\1/p' go.mod) -REGISTRY := docker.io -IMAGE := linode/linode-cosi-driver +REGISTRY ?= docker.io +REPOSITORY ?= linode +IMAGE := linode-cosi-driver CONTAINERFILE ?= Dockerfile -OCI_TAGS += --tag=${REGISTRY}/${IMAGE}:${VERSION} +OCI_TAGS += --tag=${REGISTRY}/${REPOSITORY}/${IMAGE}:${VERSION} OCI_BUILDARGS += --build-arg=VERSION=${VERSION} GOFLAGS ?= @@ -75,7 +76,7 @@ clean: # Clean the previous build files. .PHONY: clean-image clean-image: # Attempt to remove the old container image builds. - @-${ENGINE} image rm -f $(shell ${ENGINE} image ls -aq ${REGISTRY}/${REPOSITORY}/${NAME}:${VERSION} | xargs -n1 | sort -u | xargs) + @-${ENGINE} image rm -f $(shell ${ENGINE} image ls -aq ${REGISTRY}/${REPOSITORY}/${IMAGE}:${VERSION} | xargs -n1 | sort -u | xargs) .PHONY: help help: # Show help for each of the Makefile recipes. diff --git a/githooks/install-hooks.sh b/githooks/install-hooks.sh index eff3436..e2d3225 100755 --- a/githooks/install-hooks.sh +++ b/githooks/install-hooks.sh @@ -1,4 +1,8 @@ -#!/bin/bash -aex +#!/usr/bin/env bash + +set -a +set -e +set -x if [[ "$(uname -s)" == "Darwin" ]]; then if command -v brew &> /dev/null; then diff --git a/go.mod b/go.mod index 0314bfb..49306d4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/linode/linode-cosi-driver -go 1.21 +go 1.22 require ( github.com/go-resty/resty/v2 v2.12.0 diff --git a/helm/linode-cosi-driver/Chart.yaml b/helm/linode-cosi-driver/Chart.yaml index 1a517cb..5bee013 100644 --- a/helm/linode-cosi-driver/Chart.yaml +++ b/helm/linode-cosi-driver/Chart.yaml @@ -1,24 +1,6 @@ apiVersion: v2 name: linode-cosi-driver description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) version: 0.1.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -appVersion: "v0.1.0" +appVersion: 0.1.0 diff --git a/helm/linode-cosi-driver/README.md b/helm/linode-cosi-driver/README.md index 919a7fc..9d8eabd 100644 --- a/helm/linode-cosi-driver/README.md +++ b/helm/linode-cosi-driver/README.md @@ -1,6 +1,6 @@ # linode-cosi-driver -![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat) ![AppVersion: v0.1.0](https://img.shields.io/badge/AppVersion-v0.1.0-informational?style=flat) +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat) ![AppVersion: 0.1.0](https://img.shields.io/badge/AppVersion-0.1.0-informational?style=flat) A Helm chart for Kubernetes diff --git a/helm/linode-cosi-driver/templates/_helpers.tpl b/helm/linode-cosi-driver/templates/_helpers.tpl index 99aa669..b4149f2 100644 --- a/helm/linode-cosi-driver/templates/_helpers.tpl +++ b/helm/linode-cosi-driver/templates/_helpers.tpl @@ -79,11 +79,23 @@ Create the name of the service account to use. {{- required "value 'otelExporter.configMap.ref' required" .Values.otelExporter.configMap.ref }} {{- end }} +{{/* +Create version of the driver image. +*/}} +{{- define "linode-cosi-driver.version" }} + {{- $version := default .Chart.AppVersion .Values.driver.image.tag }} + {{- if hasPrefix "v" $version }} + {{- print $version }} + {{- else }} + {{- printf "v%s" $version }} + {{- end }} +{{- end }} + {{/* Create the full name of driver image from repository and tag. */}} {{- define "linode-cosi-driver.driverImageName" }} - {{- .Values.driver.image.repository }}:{{ default .Chart.AppVersion .Values.driver.image.tag }} + {{- .Values.driver.image.repository }}:{{ include "linode-cosi-driver.version" . }} {{- end }} {{/* diff --git a/scripts/tags.sh b/scripts/tags.sh new file mode 100755 index 0000000..97740c7 --- /dev/null +++ b/scripts/tags.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +# ./scripts/tags.sh "docker.io/example/example" "$(git rev-parse HEAD)" 'main' 'v0.1.0' +# This script generates version information and tags for a Docker image based on input parameters. + +set -e +set -u +set -o pipefail + +IMAGE="${1}" +SHA="$(echo "${2}" | cut -c1-8)" +REF_NAME="${3}" +LATEST_REF="${4}" +GITHUB_OUTPUT="${5}" +DATE="v$(date +'%Y%m%d')" + +if [[ "${REF_NAME}" =~ ([v][0-9]+\.[0-9]+\.[0-9]+.*) ]]; then + REF_NAME="${BASH_REMATCH[1]}" +fi + +if [[ "${LATEST_REF}" =~ ([v][0-9]+\.[0-9]+\.[0-9]+.*) ]]; then + LATEST_REF="${BASH_REMATCH[1]}" +fi + +# Determine the version and tags based on the reference name +if [[ "${REF_NAME}" == v* ]]; then + VERSION="${DATE}-${REF_NAME}-${SHA}" + TAGS=("${VERSION}" "${REF_NAME}" "latest") + CHART="${REF_NAME}" +elif [[ "${REF_NAME}" == "main" ]]; then + VERSION="${DATE}-${LATEST_REF}-${SHA}" + TAGS=("${VERSION}" "nightly") + CHART="${LATEST_REF}-${SHA}" +else + VERSION="${DATE}-${SHA}" + TAGS=("${VERSION}") + CHART="${LATEST_REF}-${SHA}" +fi + +# Create a comma-separated list of tags +length="${#TAGS[@]}" +TAGS_CSV=() +for ((i=0; i> "${GITHUB_OUTPUT}"