diff --git a/.github/scripts/create_changelog.sh b/.github/scripts/create_changelog.sh new file mode 100755 index 0000000000..1dd2e0e1f8 --- /dev/null +++ b/.github/scripts/create_changelog.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +PREVIOUS_RELEASE=$2 # for testability + +# standard bash error handling +set -o nounset # treat unset variables as an error and exit immediately. +set -o errexit # exit immediately when a command fails. +set -E # needs to be set if we want the ERR trap +set -o pipefail # prevents errors in a pipeline from being masked +RELEASE_TAG=$1 + +REPOSITORY=${REPOSITORY:-kyma-project/busola} +GITHUB_URL=https://api.github.com/repos/${REPOSITORY} +GITHUB_AUTH_HEADER="Authorization: token ${GITHUB_TOKEN}" +CHANGELOG_FILE="CHANGELOG.md" + +if [ "${PREVIOUS_RELEASE}" == "" ] +then + PREVIOUS_RELEASE=$(git describe --tags --abbrev=0) +fi +echo "Previous release: ${PREVIOUS_RELEASE}" + +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=$(mktemp --suffix=.new XXXXX) + +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}" ] #reference found + 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_TAG}" >> ${CHANGELOG_FILE} + +# cleanup +rm "${NEW_CONTRIB}" || echo "cleaned up" diff --git a/.github/scripts/create_draft_release.sh b/.github/scripts/create_draft_release.sh new file mode 100755 index 0000000000..a74bb5f243 --- /dev/null +++ b/.github/scripts/create_draft_release.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# This script returns the id of the draft release + +# standard bash error handling +set -o nounset # treat unset variables as an error and exit immediately. +set -o errexit # exit immediately when a command fails. +set -E # needs to be set if we want the ERR trap +set -o pipefail # prevents errors in a pipeline from being masked + +RELEASE_TAG=$1 +>&2 echo "Creating draft release: ${RELEASE_TAG}" + +REPOSITORY=${REPOSITORY:-kyma-project/busola} +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") + +echo "$(echo $CURL_RESPONSE | jq -r ".id")" diff --git a/.github/scripts/publish_release.sh b/.github/scripts/publish_release.sh new file mode 100755 index 0000000000..322c6dd48f --- /dev/null +++ b/.github/scripts/publish_release.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +# This script publishes a draft release + +# standard bash error handling +set -o nounset # treat unset variables as an error and exit immediately. +set -o errexit # exit immediately when a command fails. +set -E # needs to be set if we want the ERR trap +set -o pipefail # prevents errors in a pipeline from being masked + +RELEASE_ID=${RELEASE_ID?"Release id is not defined"} +IS_LATEST_RELEASE=${IS_LATEST_RELEASE?"latest release not defined"} + +REPOSITORY=${REPOSITORY:-kyma-project/busola} +GITHUB_URL=https://api.github.com/repos/${REPOSITORY} +GITHUB_AUTH_HEADER="Authorization: Bearer ${GITHUB_TOKEN}" + +CURL_RESPONSE=$(curl -w "%{http_code}" -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_ID} \ + -d '{"draft": false, "make_latest": '"$IS_LATEST_RELEASE"'}') + +HTTP_CODE=$(tail -n1 <<< "${CURL_RESPONSE}") +if [[ "${HTTP_CODE}" != "200" ]]; then + echo "${CURL_RESPONSE}" + exit 1 +fi diff --git a/.github/scripts/upload_assets.sh b/.github/scripts/upload_assets.sh new file mode 100755 index 0000000000..6b3c392759 --- /dev/null +++ b/.github/scripts/upload_assets.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# standard bash error handling +set -o nounset # treat unset variables as an error and exit immediately. +set -o errexit # exit immediately when a command fails. +set -E # needs to be set if we want the ERR trap +set -o pipefail # prevents errors in a pipeline from being masked + +REPOSITORY=${REPOSITORY:-kyma-project/busola} +RELEASE_TAG=${RELEASE_TAG?"Release tag is not defined"} +RELEASE_ID=${RELEASE_ID?"Release id is not defined"} + + +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 $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 +} + +BUSOLA_K8S="busola.yaml" +generate_k8s() { + set -x + cd resources + (cd base/web && kustomize edit set image busola-web=europe-docker.pkg.dev/kyma-project/prod/busola-web:"${RELEASE_TAG}") + (cd base/backend && kustomize edit set image busola-backend=europe-docker.pkg.dev/kyma-project/prod/busola-backend:"${RELEASE_TAG}") + kustomize build base/ > ../"${BUSOLA_K8S}" + cd - +} + +echo "Updating github release with assets" +UPLOAD_URL="https://uploads.github.com/repos/${REPOSITORY}/releases/${RELEASE_ID}/assets" + +generate_k8s +uploadFile ${BUSOLA_K8S} "${UPLOAD_URL}?name=${BUSOLA_K8S}" diff --git a/.github/workflows/busola-backend-build.yml b/.github/workflows/busola-backend-build.yml index 35186b5bae..a6a74816d3 100644 --- a/.github/workflows/busola-backend-build.yml +++ b/.github/workflows/busola-backend-build.yml @@ -1,6 +1,12 @@ name: Busola Backend Build on: + workflow_call: + inputs: + tag: + description: "Additional tag for built images" + required: true + type: string push: branches: - main @@ -26,5 +32,4 @@ jobs: name: busola-backend dockerfile: Dockerfile context: backend - export-tags: true - tags: latest + tags: ${{ inputs.tag != '' && inputs.tag || 'latest' }} diff --git a/.github/workflows/busola-build.yml b/.github/workflows/busola-build.yml index 4e490b0b11..0347f12d7a 100644 --- a/.github/workflows/busola-build.yml +++ b/.github/workflows/busola-build.yml @@ -1,6 +1,12 @@ name: Busola Build on: + workflow_call: + inputs: + tag: + description: "Additional tag for built images" + required: true + type: string push: branches: - main @@ -37,6 +43,5 @@ jobs: with: name: busola dockerfile: Dockerfile - context: . - export-tags: true - tags: latest + tags: ${{ inputs.tag != '' && inputs.tag || 'latest' }} + build-args: ${{ inputs.tag != '' && format('tag={0}', inputs.tag) || '' }} diff --git a/.github/workflows/busola-web-build.yml b/.github/workflows/busola-web-build.yml index c3f60c28f3..ef40dbd8da 100644 --- a/.github/workflows/busola-web-build.yml +++ b/.github/workflows/busola-web-build.yml @@ -1,10 +1,15 @@ name: Busola Web Build on: + workflow_call: + inputs: + tag: + description: "Additional tag for built images" + required: true + type: string push: branches: - main - - release paths: - ".github/workflows/busola-web-build.yml" - "kyma/**" @@ -36,6 +41,5 @@ jobs: with: name: busola-web dockerfile: Dockerfile.web - context: . - export-tags: true - tags: latest + tags: ${{ inputs.tag != '' && inputs.tag || 'latest' }} + build-args: ${{ inputs.tag != '' && format('tag={0}', inputs.tag) || '' }} diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 93d4af18a1..6a7bf6a951 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -1,4 +1,4 @@ -name: create release +name: Create release on: workflow_dispatch: @@ -17,143 +17,65 @@ permissions: # used by build images steps contents: write # This is required for actions/checkout and builds jobs: - # TODO: Do we need this check? Our tests on main depends on what have changed. - # verify-head-status: - # name: Verify HEAD - # runs-on: ubuntu-latest - # - # steps: - # - name: Checkout code - # uses: actions/checkout@v4 - # with: - # fetch-depth: 0 - # - # - name: Verify github actions - # run: ./.github/scripts/verify-actions-status.sh ${{ github.ref_name }} - - # TODO: This steps cannot be executed on fork, so I need to adjust them later - # upgrade-images: - # name: Upgrade main images - # needs: verify-head-status - # runs-on: ubuntu-latest - # - # steps: - # - name: Checkout code - # uses: actions/checkout@v4 - # with: - # token: ${{ secrets.BOT_TOKEN }} - # fetch-depth: 0 - - # - name: Bump values.yaml - # run: | - # ./hack/replace_serverless_chart_images.sh all . - # env: - # IMG_DIRECTORY: "prod" - # IMG_VERSION: ${{ github.event.inputs.name }} - # PROJECT_ROOT: "." - # - # - name: Bump sec-scanners-config.yaml based on values.yaml - # run: ./.github/scripts/upgrade-sec-scanners-config.sh - # env: - # IMG_VERSION: ${{ github.event.inputs.name }} - # - # - name: Commit&Push - # run: | - # git config --local user.email "team-otters@sap.com" - # git config --local user.name "ottersbot" - # - # git add . - # git commit --allow-empty -m "upgrade dependencies" - # git push origin ${{ github.ref_name }} - - create-tag: - name: Create tag - # needs: upgrade-images + build-busola-web: + name: Build Busola web + uses: ./.github/workflows/busola-web-build.yml + with: + tag: ${{ github.event.inputs.name }} + build-busola: + name: Build Busola + uses: ./.github/workflows/busola-build.yml + with: + tag: ${{ github.event.inputs.name }} + build-busola-backend: + name: Build busola backend + uses: ./.github/workflows/busola-backend-build.yml + with: + tag: ${{ github.event.inputs.name }} + + create-release: + name: Create release + needs: [ build-busola-web, build-busola-backend, build-busola ] runs-on: ubuntu-latest - steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - ref: ${{ github.ref_name }} # Checkout to latest branch changes + ref: ${{ github.ref_name }} # checkout to latest branch changes ( by default this action checkouts to the SHA that triggers action ) + - name: Create changelog + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PULL_BASE_REF: ${{ github.event.inputs.name }} + run: ./.github/scripts/create_changelog.sh ${{ github.event.inputs.name }} - name: Create tag run: | git tag ${{ github.event.inputs.name }} git push origin ${{ github.event.inputs.name }} - builds: - name: Build images - runs-on: ubuntu-lates - steps: - - name: Build busola web - uses: ./.github/workflows/busola-web-build.yml - with: - tag: '${{ github.event.inputs.name }}' - - name: Build busola backend - uses: ./.github/workflows/busola-backend-build.yml - with: - tag: '${{ github.event.inputs.name }}' - needs: create-tag - - - create-draft: - name: Create draft release - needs: builds - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.ref_name }} # checkout to latest branch changes ( by default this action checkouts to the SHA that triggers action ) - - name: Create draft release id: create-draft env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# REPOSITORY: dbadura/busola #TODO: Remove it run: | RELEASE_ID=$(./.github/scripts/create_draft_release.sh ${{ github.event.inputs.name }}) echo $RELEASE_ID echo "release_id=$RELEASE_ID" >> $GITHUB_OUTPUT - - name: Create release assets - id: create-assets + - name: Upload release assets + id: upload-assets env: - PULL_BASE_REF: ${{ github.event.inputs.name }} + RELEASE_TAG: ${{ github.event.inputs.name }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# REPOSITORY: dbadura/busola #TODO: Remove it RELEASE_ID: ${{ steps.create-draft.outputs.release_id }} run: ./.github/scripts/upload_assets.sh - outputs: - release_id: ${{ steps.create-draft.outputs.release_id }} - - # TODO: Run integration tests? - # integrations: - # needs: create-draft - # secrets: inherit - # uses: ./.github/workflows/_integration-tests.yaml - # with: - # image: europe-docker.pkg.dev/kyma-project/prod/serverless-operator:${{ github.event.inputs.name }} - - publish-release: - name: Publish release - needs: [create-draft] #integrations, create-draft] - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.event.inputs.name }} # checkout to latest branch changes ( by default this action checkouts to the SHA that triggers action ) - - name: Publish release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# REPOSITORY: dbadura/busola #TODO: Remove it - run: ./.github/scripts/publish_release.sh ${{ needs.create-draft.outputs.release_id }} ${{ github.event.inputs.latest_release }} + IS_LATEST_RELEASE: ${{ github.event.inputs.latest_release }} + RELEASE_ID: ${{ steps.create-draft.outputs.release_id }} + run: ./.github/scripts/publish_release.sh + outputs: + release_id: ${{ steps.create-draft.outputs.release_id }} diff --git a/Dockerfile b/Dockerfile index ea3c90f7d7..50a9e04a30 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,7 @@ # ---- Base Alpine with Node ---- FROM node:20.17-alpine3.20 AS builder ARG default_tag +ARG tag RUN apk update && \ apk upgrade && \ @@ -18,7 +19,8 @@ ENV CI true COPY . /app -RUN yq -i '.version = "'${default_tag}'"' public/version.yaml && \ +# build arg `tag` is used because `default_tag` is used by image builder and it cannot be overwritten. +RUN export TAG=${tag:-$default_tag} && yq -i '.version = "'${TAG}'"' public/version.yaml && \ make resolve validate RUN npm run build:docker diff --git a/Dockerfile.web b/Dockerfile.web index 0ab91e31ef..cd14dd0fd7 100644 --- a/Dockerfile.web +++ b/Dockerfile.web @@ -1,6 +1,7 @@ # ---- Base Alpine with Node ---- FROM node:20.17-alpine3.20 AS builder ARG default_tag +ARG tag # Install global dependencies RUN apk update && \ @@ -14,8 +15,8 @@ ENV PRODUCTION true ENV CI true COPY . /app - -RUN yq -i '.version = "'${default_tag}'"' public/version.yaml && \ +# build arg `tag` is used because `default_tag` is used by image builder and it cannot be overwritten. +RUN export TAG=${tag:-$default_tag} && yq -i '.version = "'${TAG}'"' public/version.yaml && \ make resolve validate RUN npm run build:docker diff --git a/resources/apply-resources.sh b/resources/apply-resources.sh index f624eec7ef..14739fa646 100755 --- a/resources/apply-resources.sh +++ b/resources/apply-resources.sh @@ -15,7 +15,8 @@ TMP_DIR="../temp/resources" mkdir -p "${TMP_DIR}" cp -rf . "${TMP_DIR}" -envsubst < "${TMP_DIR}"/base/ingress.tpl.yaml > "${TMP_DIR}"/base/ingress.yaml - kubectl create namespace "${NAMESPACE}" || true kubectl apply -k "${TMP_DIR}/environments/${ENVIRONMENT}" --namespace="${NAMESPACE}" + +envsubst < "${TMP_DIR}"/ingress/ingress.tpl.yaml > "${TMP_DIR}"/ingress/ingress.yaml +kubectl apply -k "${TMP_DIR}"/ingress --namespace=$NAMESPACE diff --git a/resources/base/backend/deployment.yaml b/resources/base/backend/deployment.yaml index aae5a96941..a017c42574 100644 --- a/resources/base/backend/deployment.yaml +++ b/resources/base/backend/deployment.yaml @@ -20,7 +20,7 @@ spec: spec: containers: - name: backend - image: europe-docker.pkg.dev/kyma-project/prod/busola-backend:latest + image: busola-backend imagePullPolicy: Always resources: limits: diff --git a/resources/base/kustomization.yaml b/resources/base/kustomization.yaml index 79ab6dae88..446976a60d 100644 --- a/resources/base/kustomization.yaml +++ b/resources/base/kustomization.yaml @@ -1,7 +1,12 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - - ingress.yaml - - ingressClass.yaml - backend - web +images: + - name: busola-web + newName: europe-docker.pkg.dev/kyma-project/prod/busola-web + newTag: latest + - name: busola-backend + newName: europe-docker.pkg.dev/kyma-project/prod/busola-backend + newTag: latest diff --git a/resources/base/web/deployment.yaml b/resources/base/web/deployment.yaml index 1916164582..a8a8b1eb5e 100644 --- a/resources/base/web/deployment.yaml +++ b/resources/base/web/deployment.yaml @@ -16,12 +16,13 @@ spec: spec: containers: - name: busola - image: europe-docker.pkg.dev/kyma-project/prod/busola-web:latest + image: busola-web imagePullPolicy: Always env: - name: ENVIRONMENT valueFrom: configMapKeyRef: + optional: true key: ENVIRONMENT name: environment resources: diff --git a/resources/base/ingress.tpl.yaml b/resources/ingress/ingress.tpl.yaml similarity index 100% rename from resources/base/ingress.tpl.yaml rename to resources/ingress/ingress.tpl.yaml diff --git a/resources/base/ingressClass.yaml b/resources/ingress/ingressClass.yaml similarity index 100% rename from resources/base/ingressClass.yaml rename to resources/ingress/ingressClass.yaml