diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml new file mode 100644 index 0000000..a4a66c5 --- /dev/null +++ b/.github/workflows/create-release.yml @@ -0,0 +1,72 @@ +name: "Create release" + +on: + workflow_dispatch: + inputs: + name: + description: "Release name" + default: "" + required: true + since: + description: "Changelog since" + default: "" + required: false + +jobs: + validate-head-status: + name: Validate HEAD + runs-on: ubuntu-latest + defaults: + run: + shell: bash + working-directory: ./scripts/release/ + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Validate set versions + run: ./validate_versions.sh ${{ github.event.inputs.name }} + - name: Validate pipeline status + run: ./validate_pipeline_status.sh ${{ github.ref_name }} + + draft-release: + name: Draft release + needs: validate-head-status + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Create changelog + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ./scripts/release/create_changelog.sh ${{ github.event.inputs.name }} ${{ github.event.inputs.since }} + - name: Draft release + id: draft-release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + RELEASE_ID=$(./scripts/release/draft_release.sh ${{ github.event.inputs.name }}) + echo "release_id=$RELEASE_ID" >> $GITHUB_OUTPUT + - name: Create lightweight tag + run: | + git tag ${{ github.event.inputs.name }} + git push origin ${{ github.event.inputs.name }} + outputs: + release_id: ${{ steps.draft-release.outputs.release_id }} + + publish-release: + name: Publish release + needs: [validate-head-status, draft-release] + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Publish release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ./scripts/release/publish_release.sh ${{ needs.draft-release.outputs.release_id }} diff --git a/.version b/.version new file mode 100644 index 0000000..a960ffb --- /dev/null +++ b/.version @@ -0,0 +1 @@ +MODULE_VERSION=0.1.0 diff --git a/Dockerfile b/Dockerfile index 30b576b..90fbf5f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,12 +16,14 @@ COPY controllers controllers/ # and so that source changes don't invalidate our downloaded layer RUN go mod download +ARG TAG_default_tag=from_dockerfile + # Build # the GOARCH has not a default value to allow the binary be built according to the host where the command # was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO # the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. -RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager main.go +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -ldflags="-X 'main.buildVersion=${TAG_default_tag}'" -a -o manager main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details FROM gcr.io/distroless/static:nonroot diff --git a/README.md b/README.md index 2c4d2b6..9545401 100644 --- a/README.md +++ b/README.md @@ -433,7 +433,7 @@ kyma alpha deploy Now run the command for creating the `ModuleTemplate` in the cluster. After this the module will be available for consumption based on the module name configured with the label `operator.kyma-project.io/module-name` on the `ModuleTemplate`. -_WARNING: Depending on your setup against either a k3d cluster/registry, you will need to run the script in `hack/local-template.sh` before pushing the ModuleTemplate to have proper registry setup. +_WARNING: Depending on your setup against either a k3d cluster/registry, you will need to run the script `/scripts/patch_local_template.sh` before pushing the ModuleTemplate to have proper registry setup. (This is necessary for k3d clusters due to port-mapping issues in the cluster that the operators cannot reuse, please take a look at the [relevant issue for more details](https://github.com/kyma-project/module-manager/issues/136#issuecomment-1279542587))_ ```sh diff --git a/config/samples/operator.kyma-project.io_v1alpha1_sample.yaml b/config/samples/default-sample-cr.yaml similarity index 100% rename from config/samples/operator.kyma-project.io_v1alpha1_sample.yaml rename to config/samples/default-sample-cr.yaml diff --git a/main.go b/main.go index b02dd7d..66f5f56 100644 --- a/main.go +++ b/main.go @@ -18,6 +18,7 @@ package main import ( "flag" + "fmt" "os" "time" @@ -60,6 +61,7 @@ type FlagVar struct { rateLimiterFrequency int rateLimiterBurst int finalState string + printVersion bool } func init() { //nolint:gochecknoinits @@ -68,6 +70,9 @@ func init() { //nolint:gochecknoinits //+kubebuilder:scaffold:scheme } +//nolint:gochecknoglobals +var buildVersion = "not_provided" + func main() { flagVar := defineFlagVar() opts := zap.Options{ @@ -76,6 +81,15 @@ func main() { opts.BindFlags(flag.CommandLine) flag.Parse() + if flagVar.printVersion { + msg := fmt.Sprintf("Template Operator version: %s\n", buildVersion) + _, err := os.Stdout.WriteString(msg) + if err != nil { + os.Exit(1) + } + os.Exit(0) + } + rateLimiter := controllers.RateLimiter{ Burst: flagVar.rateLimiterBurst, Frequency: flagVar.rateLimiterFrequency, @@ -145,5 +159,6 @@ func defineFlagVar() *FlagVar { "Indicates the failure max delay in seconds") flag.StringVar(&flagVar.finalState, "final-state", string(v1alpha1.StateReady), "Customize final state, to mimic state behaviour like Ready, Warning") + flag.BoolVar(&flagVar.printVersion, "version", false, "Prints the operator version and exits") return flagVar } diff --git a/module-config.yaml b/module-config.yaml index e3c7e72..a2d0bc5 100644 --- a/module-config.yaml +++ b/module-config.yaml @@ -3,6 +3,6 @@ channel: regular version: v1.0.0 manifest: template-operator.yaml security: sec-scanners-config.yaml -defaultCR: ./config/samples/operator.kyma-project.io_v1alpha1_sample.yaml +defaultCR: ./config/samples/default-sample-cr.yaml annotations: - operator.kyma-project.io/doc-url: https://kyma-project.io \ No newline at end of file + operator.kyma-project.io/doc-url: https://kyma-project.io diff --git a/hack/local-template.sh b/scripts/local/patch_template_file.sh similarity index 100% rename from hack/local-template.sh rename to scripts/local/patch_template_file.sh diff --git a/scripts/release/create_changelog.sh b/scripts/release/create_changelog.sh new file mode 100755 index 0000000..e2dbb85 --- /dev/null +++ b/scripts/release/create_changelog.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +PREVIOUS_RELEASE=$2 + +set -o nounset +set -o errexit +set -E +set -o pipefail + +RELEASE_VERSION=$1 + +if [ "${PREVIOUS_RELEASE}" == "" ] +then + PREVIOUS_RELEASE=$(git describe --tags --abbrev=0) +fi + +REPOSITORY=${REPOSITORY:-kyma-project/template-operator} +GITHUB_URL=https://api.github.com/repos/${REPOSITORY} +GITHUB_AUTH_HEADER="Authorization: token ${GITHUB_TOKEN}" +CHANGELOG_FILE="CHANGELOG.md" + +echo "## What has changed" >> ${CHANGELOG_FILE} +git log "${PREVIOUS_RELEASE}"..HEAD --pretty=tformat:"%h" --reverse | while read -r commit +do + COMMIT_AUTHOR=$(curl -H "${GITHUB_AUTH_HEADER}" -sS "${GITHUB_URL}/commits/${commit}" | jq -r '.author.login') + if [ "${COMMIT_AUTHOR}" != "kyma-bot" ]; then + git show -s "${commit}" --format="* %s by @${COMMIT_AUTHOR}" >> ${CHANGELOG_FILE} + fi +done + +NEW_CONTRIB=$$.new + +join -v2 \ +<(curl -H "${GITHUB_AUTH_HEADER}" -sS "${GITHUB_URL}/compare/$(git rev-list --max-parents=0 HEAD)...${PREVIOUS_RELEASE}" | jq -r '.commits[].author.login' | sort -u) \ +<(curl -H "${GITHUB_AUTH_HEADER}" -sS "${GITHUB_URL}/compare/${PREVIOUS_RELEASE}...HEAD" | jq -r '.commits[].author.login' | sort -u) >${NEW_CONTRIB} + +if [ -s ${NEW_CONTRIB} ] +then + echo -e "\n## New contributors" >> ${CHANGELOG_FILE} + while read -r user + do + REF_PR=$(grep "@${user}" ${CHANGELOG_FILE} | head -1 | grep -o " (#[0-9]\+)" || true) + if [ -n "${REF_PR}" ] + then + REF_PR=" in ${REF_PR}" + fi + echo "* @${user} made first contribution${REF_PR}" >> ${CHANGELOG_FILE} + done <${NEW_CONTRIB} +fi + +echo -e "\n**Full changelog**: https://github.com/$REPOSITORY/compare/${PREVIOUS_RELEASE}...${RELEASE_VERSION}" >> ${CHANGELOG_FILE} + +rm ${NEW_CONTRIB} || echo "cleaned up" diff --git a/scripts/release/draft_release.sh b/scripts/release/draft_release.sh new file mode 100755 index 0000000..bc7cc7b --- /dev/null +++ b/scripts/release/draft_release.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +set -o nounset +set -o errexit +set -E +set -o pipefail + +RELEASE_TAG=$1 + +REPOSITORY=${REPOSITORY:-kyma-project/template-operator} +GITHUB_URL=https://api.github.com/repos/${REPOSITORY} +GITHUB_AUTH_HEADER="Authorization: Bearer ${GITHUB_TOKEN}" +CHANGELOG_FILE=$(cat CHANGELOG.md) + +JSON_PAYLOAD=$(jq -n \ + --arg tag_name "$RELEASE_TAG" \ + --arg name "$RELEASE_TAG" \ + --arg body "$CHANGELOG_FILE" \ + '{ + "tag_name": $tag_name, + "name": $name, + "body": $body, + "draft": true + }') + +CURL_RESPONSE=$(curl -L \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "${GITHUB_AUTH_HEADER}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${GITHUB_URL}"/releases \ + -d "$JSON_PAYLOAD") + +# return the id of the release draft +echo "$CURL_RESPONSE" | jq -r ".id" diff --git a/scripts/release/publish_release.sh b/scripts/release/publish_release.sh new file mode 100755 index 0000000..9faa6a9 --- /dev/null +++ b/scripts/release/publish_release.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +set -o nounset +set -o errexit +set -E +set -o pipefail + +RELEASE_VERSION=$1 + +REPOSITORY=${REPOSITORY:-kyma-project/template-operator} +GITHUB_URL=https://api.github.com/repos/${REPOSITORY} +GITHUB_AUTH_HEADER="Authorization: Bearer ${GITHUB_TOKEN}" + +CURL_RESPONSE=$(curl -L \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "${GITHUB_AUTH_HEADER}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${GITHUB_URL}"/releases/"${RELEASE_VERSION}" \ + -d '{"draft":false}') +echo "$CURL_RESPONSE" diff --git a/scripts/release/upload_assets.sh b/scripts/release/upload_assets.sh new file mode 100755 index 0000000..5c07331 --- /dev/null +++ b/scripts/release/upload_assets.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash + +set -o nounset +set -o errexit +set -E +set -o pipefail + +uploadFile() { + filePath=${1} + ghAsset=${2} + + echo "Uploading ${filePath} as ${ghAsset}" + response=$(curl -s -o output.txt -w "%{http_code}" \ + --request POST --data-binary @"$filePath" \ + -H "Authorization: token $BOT_GITHUB_TOKEN" \ + -H "Content-Type: text/yaml" \ + "$ghAsset") + if [[ "$response" != "201" ]]; then + echo "Unable to upload the asset ($filePath): " + echo "HTTP Status: $response" + cat output.txt + exit 1 + else + echo "$filePath uploaded" + fi +} + +echo "PULL_BASE_REF ${PULL_BASE_REF}" + +MODULE_VERSION=${PULL_BASE_REF} make render-manifest + +echo "Generated template-operator.yaml:" +cat template-operator.yaml + +MODULE_VERSION=${PULL_BASE_REF} make module-build + +echo "Generated moduletemplate.yaml:" +cat moduletemplate.yaml + +echo "Fetching releases" +CURL_RESPONSE=$(curl -w "%{http_code}" -sL \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $BOT_GITHUB_TOKEN"\ + https://api.github.com/repos/kyma-project/template-operator/releases) +JSON_RESPONSE=$(sed '$ d' <<< "${CURL_RESPONSE}") +HTTP_CODE=$(tail -n1 <<< "${CURL_RESPONSE}") +if [[ "${HTTP_CODE}" != "200" ]]; then + echo "${CURL_RESPONSE}" + exit 1 +fi + +echo "Finding release id for: ${PULL_BASE_REF}" +RELEASE_ID=$(jq <<< "${JSON_RESPONSE}" --arg tag "${PULL_BASE_REF}" '.[] | select(.tag_name == $ARGS.named.tag) | .id') + +echo "Got '${RELEASE_ID}' release id" +if [ -z "${RELEASE_ID}" ] +then + echo "No release with tag = ${PULL_BASE_REF}" + exit 1 +fi + +echo "Adding assets to Github release" +UPLOAD_URL="https://uploads.github.com/repos/kyma-project/template-operator/releases/${RELEASE_ID}/assets" + +uploadFile "template-operator.yaml" "${UPLOAD_URL}?name=template-operator.yaml" +uploadFile "moduletemplate.yaml" "${UPLOAD_URL}?name=moduletemplate.yaml" +uploadFile "config/samples/default-sample-cr.yaml" "${UPLOAD_URL}?name=default-sample-cr.yaml" +uploadFile "module-config.yaml" "${UPLOAD_URL}?name=module-config.yaml" diff --git a/scripts/release/validate_pipeline_status.sh b/scripts/release/validate_pipeline_status.sh new file mode 100755 index 0000000..f6b00ef --- /dev/null +++ b/scripts/release/validate_pipeline_status.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +echo "Checking status of 'post-*' pipelines for template-operator" +REF_NAME="${1:-"main"}" +STATUS_URL="https://api.github.com/repos/kyma-project/template-operator/commits/${REF_NAME}/status" +STATUS=$(curl -L -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" "${STATUS_URL}" | head -n 2 ) +if [[ "$STATUS" == *"success"* ]]; then + echo "All recent jobs succeeded, post-pipelines are green." +else + echo "Latest post-pipelines are failing or pending! Reason:" + echo "$STATUS" + exit 1 +fi diff --git a/scripts/release/validate_versions.sh b/scripts/release/validate_versions.sh new file mode 100755 index 0000000..867a83b --- /dev/null +++ b/scripts/release/validate_versions.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -ue +source ./../../.version + +DESIRED_VERSION=$1 +if [[ "$DESIRED_VERSION" != "$MODULE_VERSION" ]]; then + echo "Versions don't match! Expected ${MODULE_VERSION} but got $DESIRED_VERSION." + echo "Please update .version file or change desired version!" + exit 1 +fi +echo "Versions match." + +IMAGE_TO_CHECK="${2:-europe-docker.pkg.dev/kyma-project/prod/template-operator}" +BUMPED_IMAGE_TAG=$(grep "${IMAGE_TO_CHECK}" ../../sec-scanners-config.yaml | cut -d : -f 2) +if [[ "$BUMPED_IMAGE_TAG" != "$DESIRED_VERSION" ]]; then + echo "Version tag in sec-scanners-config.yaml file is incorrect!" + echo "Could not find $DESIRED_VERSION." + exit 1 +fi +echo "Image version tag in sec-scanners-config.yaml does match with release tag." +exit 0 diff --git a/sec-scanners-config.yaml b/sec-scanners-config.yaml index f55ebee..0642fa9 100644 --- a/sec-scanners-config.yaml +++ b/sec-scanners-config.yaml @@ -1,8 +1,9 @@ module-name: template-operator -rc-tag: 0.5.0 -dev-branch: main +protecode: + - europe-docker.pkg.dev/kyma-project/prod/template-operator:0.1.0 whitesource: language: golang-mod + subprojects: false exclude: - "**/test/**" - - "**/*_test.go" \ No newline at end of file + - "**/*_test.go"