From c2cd93b53e41d768566a5c30fccdf84ff16061a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20G=C3=BCrtler?= Date: Wed, 21 Jan 2026 13:51:16 +0100 Subject: [PATCH 1/9] #170 update makefiles --- Makefile | 2 +- build/make/bats.mk | 10 ++-- build/make/bats/Dockerfile | 2 +- build/make/bats/customBatsEntrypoint.sh | 9 +++- build/make/build.mk | 2 +- build/make/k8s-component.mk | 12 +++-- build/make/k8s-crd.mk | 9 ++-- build/make/k8s.mk | 13 ++++- build/make/release.sh | 8 +-- build/make/release_functions.sh | 71 ++++++++++++++++++++----- build/make/static-analysis.mk | 8 +-- build/make/test-unit.mk | 8 ++- 12 files changed, 117 insertions(+), 37 deletions(-) diff --git a/Makefile b/Makefile index 4de3ecb9..2cf565c6 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -MAKEFILES_VERSION=9.9.1 +MAKEFILES_VERSION=10.5.0 .DEFAULT_GOAL:=dogu-release diff --git a/build/make/bats.mk b/build/make/bats.mk index 7e73553d..23680444 100644 --- a/build/make/bats.mk +++ b/build/make/bats.mk @@ -9,7 +9,7 @@ BATS_SUPPORT=$(BATS_LIBRARY_DIR)/bats-support BATS_FILE=$(BATS_LIBRARY_DIR)/bats-file BATS_BASE_IMAGE?=bats/bats BATS_CUSTOM_IMAGE?=cloudogu/bats -BATS_TAG?=1.11.0 +BATS_TAG?=1.12.0 BATS_DIR=build/make/bats BATS_WORKDIR="${WORKDIR}"/"${BATS_DIR}" @@ -18,15 +18,19 @@ unit-test-shell: unit-test-shell-$(ENVIRONMENT) $(BATS_ASSERT): @git clone --depth 1 https://github.com/bats-core/bats-assert $@ + @rm -rf $@/.git $(BATS_MOCK): @git clone --depth 1 https://github.com/grayhemp/bats-mock $@ + @rm -rf $@/.git $(BATS_SUPPORT): @git clone --depth 1 https://github.com/bats-core/bats-support $@ + @rm -rf $@/.git $(BATS_FILE): @git clone --depth 1 https://github.com/bats-core/bats-file $@ + @rm -rf $@/.git $(BASH_SRC): BASH_SRC:=$(shell find "${WORKDIR}" -type f -name "*.sh") @@ -49,10 +53,10 @@ unit-test-shell-local: $(BASH_SRC) $(PASSWD) $(ETCGROUP) $(HOME_DIR) buildTestIm "${BATS_DIR}"/customBatsEntrypoint.sh make unit-test-shell-generic-no-junit unit-test-shell-generic: - @bats --formatter junit --output ${BASH_TEST_REPORT_DIR} ${TESTS_DIR} + @bats --report-formatter junit --formatter junit --output ${BASH_TEST_REPORT_DIR} ${TESTS_DIR} unit-test-shell-generic-no-junit: - @bats ${TESTS_DIR} + @bats --report-formatter junit --output ${BASH_TEST_REPORT_DIR} ${TESTS_DIR} .PHONY buildTestImage: buildTestImage: diff --git a/build/make/bats/Dockerfile b/build/make/bats/Dockerfile index 7167a941..95a27875 100644 --- a/build/make/bats/Dockerfile +++ b/build/make/bats/Dockerfile @@ -1,7 +1,7 @@ ARG BATS_BASE_IMAGE ARG BATS_TAG -FROM ${BATS_BASE_IMAGE:-bats/bats}:${BATS_TAG:-1.11.0} +FROM ${BATS_BASE_IMAGE:-bats/bats}:${BATS_TAG:-1.12.0} # Make bash more findable by scripts and tests RUN apk add make git bash diff --git a/build/make/bats/customBatsEntrypoint.sh b/build/make/bats/customBatsEntrypoint.sh index 58856fe3..bfc192ba 100755 --- a/build/make/bats/customBatsEntrypoint.sh +++ b/build/make/bats/customBatsEntrypoint.sh @@ -3,4 +3,11 @@ set -o errexit set -o nounset set -o pipefail -"$@" \ No newline at end of file +targetReportDir="${PWD}"/target/shell_test_reports +uidgid=1000:1000 +exitcode=0 +"$@" || exitcode=$? +echo "Resetting file ownership to ${uidgid} in ${targetReportDir}/" +chown -R ${uidgid} "${targetReportDir}"/* +echo "exiting with code ${exitcode}" +exit ${exitcode} \ No newline at end of file diff --git a/build/make/build.mk b/build/make/build.mk index a29f2c5b..c2f512ce 100644 --- a/build/make/build.mk +++ b/build/make/build.mk @@ -3,7 +3,7 @@ ADDITIONAL_LDFLAGS?=-extldflags -static LDFLAGS?=-ldflags "$(ADDITIONAL_LDFLAGS) -X main.Version=$(VERSION) -X main.CommitID=$(COMMIT_ID)" GOIMAGE?=golang -GOTAG?=1.24 +GOTAG?=1.25 GOOS?=linux GOARCH?=amd64 PRE_COMPILE?= diff --git a/build/make/k8s-component.mk b/build/make/k8s-component.mk index 6c1f6c41..4efe639e 100644 --- a/build/make/k8s-component.mk +++ b/build/make/k8s-component.mk @@ -1,4 +1,5 @@ -COMPONENT_DEV_VERSION?=${VERSION}-dev +COMPONENT_BUILD_VERSION := $(shell date +%s) +COMPONENT_DEV_VERSION?=${VERSION}-dev.${COMPONENT_BUILD_VERSION} include ${BUILD_DIR}/make/k8s.mk @@ -15,8 +16,9 @@ HELM_RELEASE_TGZ=${HELM_TARGET_DIR}/${ARTIFACT_ID}-${VERSION}.tgz HELM_DEV_RELEASE_TGZ=${HELM_TARGET_DIR}/${ARTIFACT_ID}-${COMPONENT_DEV_VERSION}.tgz HELM_ARTIFACT_NAMESPACE?=k8s ifeq (${RUNTIME_ENV}, remote) - HELM_ARTIFACT_NAMESPACE?=testing/k8s + HELM_ARTIFACT_NAMESPACE=testing/k8s endif +$(info HELM_ARTIFACT_NAMESPACE=$(HELM_ARTIFACT_NAMESPACE)) K8S_RESOURCE_COMPONENT ?= "${K8S_RESOURCE_TEMP_FOLDER}/component-${ARTIFACT_ID}-${VERSION}.yaml" K8S_RESOURCE_COMPONENT_CR_TEMPLATE_YAML ?= $(BUILD_DIR)/make/k8s-component.tpl @@ -93,10 +95,10 @@ helm-reinstall: helm-delete helm-apply ## Uninstalls the current helm chart and .PHONY: helm-chart-import helm-chart-import: ${CHECK_VAR_TARGETS} helm-generate helm-package ${IMAGE_IMPORT_TARGET} ## Imports the currently available chart into the cluster-local registry. @if [[ ${STAGE} == "development" ]]; then \ - echo "Import ${HELM_DEV_RELEASE_TGZ} into K8s cluster ${CES_REGISTRY_HOST}..."; \ + echo "Import ${HELM_DEV_RELEASE_TGZ} into K8s cluster ${CES_REGISTRY_HOST}/${HELM_ARTIFACT_NAMESPACE}..."; \ ${BINARY_HELM} push ${HELM_DEV_RELEASE_TGZ} oci://${CES_REGISTRY_HOST}/${HELM_ARTIFACT_NAMESPACE} ${BINARY_HELM_ADDITIONAL_PUSH_ARGS}; \ else \ - echo "Import ${HELM_RELEASE_TGZ} into K8s cluster ${CES_REGISTRY_HOST}..."; \ + echo "Import ${HELM_RELEASE_TGZ} into K8s cluster ${CES_REGISTRY_HOST}/${HELM_ARTIFACT_NAMESPACE}..."; \ ${BINARY_HELM} push ${HELM_RELEASE_TGZ} oci://${CES_REGISTRY_HOST}/${HELM_ARTIFACT_NAMESPACE} ${BINARY_HELM_ADDITIONAL_PUSH_ARGS}; \ fi @echo "Done." @@ -142,7 +144,7 @@ ${K8S_RESOURCE_COMPONENT_CR_TEMPLATE_YAML}: ${K8S_RESOURCE_TEMP_FOLDER} fi .PHONY: component-apply -component-apply: check-k8s-namespace-env-var ${COMPONENT_PRE_APPLY_TARGETS} ${IMAGE_IMPORT_TARGET} helm-generate helm-chart-import component-generate ## Applies the component yaml resource to the actual defined context. +component-apply: isProduction check-k8s-namespace-env-var ${COMPONENT_PRE_APPLY_TARGETS} ${IMAGE_IMPORT_TARGET} helm-generate helm-chart-import component-generate ## Applies the component yaml resource to the actual defined context. @kubectl apply -f "${K8S_RESOURCE_COMPONENT}" --namespace="${NAMESPACE}" --context="${KUBE_CONTEXT_NAME}" @echo "Done." diff --git a/build/make/k8s-crd.mk b/build/make/k8s-crd.mk index a16292dc..aca625da 100644 --- a/build/make/k8s-crd.mk +++ b/build/make/k8s-crd.mk @@ -5,7 +5,8 @@ ifeq ($(APPEND_CRD_SUFFIX), true) else ifeq ($(APPEND_CRD_SUFFIX), false) ARTIFACT_CRD_ID = $(ARTIFACT_ID) endif -DEV_CRD_VERSION ?= ${VERSION}-dev +CRD_BUILD_VERSION := $(shell date +%s).$(TIMESTAMP) +DEV_CRD_VERSION ?= ${VERSION}-dev.${COMPONENT_BUILD_VERSION} HELM_CRD_SOURCE_DIR ?= ${WORKDIR}/k8s/helm-crd HELM_CRD_TARGET_DIR ?= $(K8S_RESOURCE_TEMP_FOLDER)/helm-crd HELM_CRD_RELEASE_TGZ = ${HELM_CRD_TARGET_DIR}/${ARTIFACT_CRD_ID}-${VERSION}.tgz @@ -89,10 +90,10 @@ ${HELM_CRD_RELEASE_TGZ}: ${BINARY_HELM} crd-helm-generate ## Generates and packa .PHONY: crd-helm-chart-import crd-helm-chart-import: ${CHECK_VAR_TARGETS} check-k8s-artifact-id crd-helm-generate crd-helm-package ## Imports the currently available Helm CRD chart into the cluster-local registry. @if [[ ${STAGE} == "development" ]]; then \ - echo "Import ${HELM_CRD_DEV_RELEASE_TGZ} into K8s cluster ${CES_REGISTRY_HOST}..."; \ + echo "Import ${HELM_CRD_DEV_RELEASE_TGZ} into K8s cluster ${CES_REGISTRY_HOST}/${HELM_ARTIFACT_NAMESPACE}..."; \ ${BINARY_HELM} push ${HELM_CRD_DEV_RELEASE_TGZ} oci://${CES_REGISTRY_HOST}/${HELM_ARTIFACT_NAMESPACE} ${BINARY_HELM_ADDITIONAL_PUSH_ARGS}; \ else \ - echo "Import ${HELM_CRD_RELEASE_TGZ} into K8s cluster ${CES_REGISTRY_HOST}..."; \ + echo "Import ${HELM_CRD_RELEASE_TGZ} into K8s cluster ${CES_REGISTRY_HOST}/${HELM_ARTIFACT_NAMESPACE}..."; \ ${BINARY_HELM} push ${HELM_CRD_RELEASE_TGZ} oci://${CES_REGISTRY_HOST}/${HELM_ARTIFACT_NAMESPACE} ${BINARY_HELM_ADDITIONAL_PUSH_ARGS}; \ fi @echo "Done." @@ -111,7 +112,7 @@ crd-component-generate: ${K8S_RESOURCE_TEMP_FOLDER} ## Generate the CRD componen fi .PHONY: crd-component-apply -crd-component-apply: check-k8s-namespace-env-var crd-helm-chart-import crd-component-generate ## Applies the CRD component YAML resource to the actual defined context. +crd-component-apply: isProduction check-k8s-namespace-env-var crd-helm-chart-import crd-component-generate ## Applies the CRD component YAML resource to the actual defined context. @kubectl apply -f "${K8S_RESOURCE_CRD_COMPONENT}" --namespace="${NAMESPACE}" --context="${KUBE_CONTEXT_NAME}" @echo "Done." diff --git a/build/make/k8s.mk b/build/make/k8s.mk index d55141c0..162abb69 100644 --- a/build/make/k8s.mk +++ b/build/make/k8s.mk @@ -11,7 +11,7 @@ BINARY_YQ_4_VERSION?=v4.40.3 BINARY_HELM = $(UTILITY_BIN_PATH)/helm BINARY_HELM_VERSION?=v3.13.0 CONTROLLER_GEN = $(UTILITY_BIN_PATH)/controller-gen -CONTROLLER_GEN_VERSION?=v0.14.0 +CONTROLLER_GEN_VERSION?=v0.19.0 # Setting SHELL to bash allows bash commands to be executed by recipes. # Options are set to exit when a recipe line exits non-zero or a piped command fails. @@ -203,3 +203,14 @@ envtest: ${ENVTEST} ## Download envtest-setup locally if necessary. ${ENVTEST}: $(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) + +.PHONY: isProduction +isProduction: + @if [[ "${STAGE}" == "production" ]]; then \ + echo "Command executed in production stage. Aborting."; \ + exit 1; \ + else \ + echo "Command executed in development stage. Continuing."; \ + fi + + diff --git a/build/make/release.sh b/build/make/release.sh index 6d6388e4..ee706243 100755 --- a/build/make/release.sh +++ b/build/make/release.sh @@ -45,13 +45,15 @@ else CURRENT_TOOL_VERSION=$(get_current_version_by_makefile) fi +BASE_VERSION=$(get_base_version_by_makefile) + NEW_RELEASE_VERSION="$(read_new_version)" validate_new_version "${NEW_RELEASE_VERSION}" if [[ -n "${DRY_RUN}" ]]; then start_dry_run_release "${NEW_RELEASE_VERSION}" else - start_git_flow_release "${NEW_RELEASE_VERSION}" + start_git_flow_release "${NEW_RELEASE_VERSION}" "${BASE_VERSION}" fi update_versions "${NEW_RELEASE_VERSION}" @@ -60,9 +62,9 @@ update_releasenotes "${NEW_RELEASE_VERSION}" show_diff if [[ -n "${DRY_RUN}" ]]; then - abort_dry_run_release "${NEW_RELEASE_VERSION}" + abort_dry_run_release "${NEW_RELEASE_VERSION}" "${BASE_VERSION}" else - finish_release_and_push "${CURRENT_TOOL_VERSION}" "${NEW_RELEASE_VERSION}" + finish_release_and_push "${CURRENT_TOOL_VERSION}" "${NEW_RELEASE_VERSION}" "${BASE_VERSION}" fi echo "=====Finished Release process=====" diff --git a/build/make/release_functions.sh b/build/make/release_functions.sh index f3e1636f..0abe86ef 100755 --- a/build/make/release_functions.sh +++ b/build/make/release_functions.sh @@ -25,6 +25,11 @@ get_current_version_by_makefile() { grep '^VERSION=[0-9[:alpha:].-]*$' Makefile | sed s/VERSION=//g } +get_base_version_by_makefile() { + BASE_VERSION=$(grep '^BASE_VERSION=[0-9[:alpha:].-]*$' Makefile | sed s/BASE_VERSION=//g) + echo "${BASE_VERSION}" +} + get_current_version_by_dogu_json() { jq ".Version" --raw-output dogu.json } @@ -52,24 +57,47 @@ validate_new_version() { start_git_flow_release() { local NEW_RELEASE_VERSION="${1}" + local BASE_RELEASE_VERSION="${2}" + local BASE_DEV_BRANCH_NAME + # Do gitflow git flow init --defaults --force local mainBranchExists mainBranchExists="$(git show-ref refs/remotes/origin/main || echo "")" - if [ -n "$mainBranchExists" ]; then - echo 'Using "main" branch for production releases' - git flow config set master main - git checkout main - git pull origin main + if [[ -z "$BASE_RELEASE_VERSION" ]]; then + echo "BASE_RELEASE_VERSION variable is empty" + if [ -n "$mainBranchExists" ]; then + echo 'Using "main" branch for production releases' + git flow config set master main + git checkout main + git pull origin main + else + echo 'Using "master" branch for production releases' + git checkout master + git pull origin master + fi + BASE_DEV_BRANCH_NAME="develop" else - echo 'Using "master" branch for production releases' - git checkout master - git pull origin master + echo "BASE_RELEASE_VERSION variable is not empty" + if [[ ${NEW_RELEASE_VERSION} != ${BASE_RELEASE_VERSION}* ]]; then + echo "ERROR: Release version (${NEW_RELEASE_VERSION}) does not start with base version (${BASE_RELEASE_VERSION})" + exit 1 + fi + + BASE_MAIN_BRANCH_NAME="${BASE_RELEASE_VERSION}/main" + echo "Using ${BASE_MAIN_BRANCH_NAME} branch for production releases" + git flow config set master ${BASE_MAIN_BRANCH_NAME} + git checkout ${BASE_MAIN_BRANCH_NAME} + git pull origin ${BASE_MAIN_BRANCH_NAME} + BASE_DEV_BRANCH_NAME="${BASE_RELEASE_VERSION}/develop" fi - git checkout develop - git pull origin develop + git flow config set develop ${BASE_DEV_BRANCH_NAME} + + git checkout ${BASE_DEV_BRANCH_NAME} + git pull origin ${BASE_DEV_BRANCH_NAME} + git flow config git flow release start v"${NEW_RELEASE_VERSION}" } @@ -81,8 +109,17 @@ start_dry_run_release() { abort_dry_run_release() { local NEW_RELEASE_VERSION="${1}" + local BASE_RELEASE_VERSION="${2}" + + local BASE_DEV_BRANCH_NAME + + if [[ -z "$BASE_RELEASE_VERSION" ]]; then + BASE_DEV_BRANCH_NAME="develop" + else + BASE_DEV_BRANCH_NAME="${BASE_RELEASE_VERSION}/develop" + fi - git checkout develop + git checkout ${BASE_DEV_BRANCH_NAME} git branch -D dryrun/v"${NEW_RELEASE_VERSION}" } @@ -302,12 +339,22 @@ show_diff() { finish_release_and_push() { local CURRENT_VERSION="${1}" local NEW_RELEASE_VERSION="${2}" + local BASE_RELEASE_VERSION="${3}" # Push changes and delete release branch wait_for_ok "Upgrade from version v${CURRENT_VERSION} to version v${NEW_RELEASE_VERSION} finished. Should the changes be pushed?" git push origin release/v"${NEW_RELEASE_VERSION}" echo "Switching back to develop and deleting branch release/v${NEW_RELEASE_VERSION}..." - git checkout develop + + local BASE_DEV_BRANCH_NAME + + if [[ -z "$BASE_RELEASE_VERSION" ]]; then + BASE_DEV_BRANCH_NAME="develop" + else + BASE_DEV_BRANCH_NAME="${BASE_RELEASE_VERSION}/develop" + fi + + git checkout ${BASE_DEV_BRANCH_NAME} git branch -D release/v"${NEW_RELEASE_VERSION}" } diff --git a/build/make/static-analysis.mk b/build/make/static-analysis.mk index 9cbc39ef..262bc0d2 100644 --- a/build/make/static-analysis.mk +++ b/build/make/static-analysis.mk @@ -2,14 +2,14 @@ STATIC_ANALYSIS_DIR=$(TARGET_DIR)/static-analysis GOIMAGE?=golang -GOTAG?=1.24 +GOTAG?=1.25 CUSTOM_GO_MOUNT?=-v /tmp:/tmp REVIEW_DOG=$(TMP_DIR)/bin/reviewdog LINT=$(TMP_DIR)/bin/golangci-lint -LINT_VERSION?=v1.64.8 +LINT_VERSION?=v2.5.0 # ignore tests and mocks -LINTFLAGS=--tests=false --exclude-files="^.*_mock.go$$" --exclude-files="^.*/mock.*.go$$" --timeout 10m --issues-exit-code 0 +LINTFLAGS=--tests=false --timeout 10m --issues-exit-code 0 ADDITIONAL_LINTER=-E bodyclose -E containedctx -E contextcheck -E decorder -E dupl -E errname -E forcetypeassert -E funlen -E unparam .PHONY: static-analysis @@ -47,7 +47,7 @@ $(STATIC_ANALYSIS_DIR)/static-analysis.log: $(STATIC_ANALYSIS_DIR) $(STATIC_ANALYSIS_DIR)/static-analysis-cs.log: $(STATIC_ANALYSIS_DIR) @echo "run static analysis with export to checkstyle format" - @$(LINT) $(LINTFLAGS) run --out-format=checkstyle ./... $(ADDITIONAL_LINTER) > $@ + @$(LINT) $(LINTFLAGS) --output.checkstyle.path stdout run ./... $(ADDITIONAL_LINTER) > $@ $(STATIC_ANALYSIS_DIR): $(LINT) @mkdir -p $(STATIC_ANALYSIS_DIR) diff --git a/build/make/test-unit.mk b/build/make/test-unit.mk index 54a9448d..c6994a45 100644 --- a/build/make/test-unit.mk +++ b/build/make/test-unit.mk @@ -9,9 +9,15 @@ COVERAGE_REPORT=$(UNIT_TEST_DIR)/coverage.out PRE_UNITTESTS?= POST_UNITTESTS?= +ASJSON?= + .PHONY: unit-test unit-test: $(XUNIT_JSON) ## Start unit tests +ifeq ($(ENVIRONMENT),ci) +ASJSON='-json' +endif + $(XUNIT_JSON): $(SRC) $(GO_JUNIT_REPORT) ifneq ($(strip $(PRE_UNITTESTS)),) @make $(PRE_UNITTESTS) @@ -21,7 +27,7 @@ endif @echo 'mode: set' > ${COVERAGE_REPORT} @rm -f $(UNIT_TEST_LOG) || true @for PKG in $(PACKAGES) ; do \ - ${GO_CALL} test -v $$PKG -coverprofile=${COVERAGE_REPORT}.tmp -json 2>&1 | tee $(UNIT_TEST_LOG).tmp ; \ + ${GO_CALL} test -v $$PKG -coverprofile=${COVERAGE_REPORT}.tmp ${ASJSON} 2>&1 | tee $(UNIT_TEST_LOG).tmp ; \ cat ${COVERAGE_REPORT}.tmp | tail +2 >> ${COVERAGE_REPORT} ; \ rm -f ${COVERAGE_REPORT}.tmp ; \ cat $(UNIT_TEST_LOG).tmp >> $(UNIT_TEST_LOG) ; \ From a8c9ae43b6426f93e8aea87795e9e187dd9e6b16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20G=C3=BCrtler?= Date: Wed, 21 Jan 2026 14:04:58 +0100 Subject: [PATCH 2/9] #170 use 5.1.8/main and 5.1.8/develop for releases --- Jenkinsfile | 7 +++++-- Makefile | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index a4119150..72335a9f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,5 @@ #!groovy -@Library(['github.com/cloudogu/ces-build-lib@4.2.0', 'github.com/cloudogu/dogu-build-lib@v3.2.0']) +@Library(['github.com/cloudogu/ces-build-lib@5.1.0', 'github.com/cloudogu/dogu-build-lib@v3.2.0']) import com.cloudogu.ces.cesbuildlib.* import com.cloudogu.ces.dogubuildlib.* @@ -12,6 +12,7 @@ node('vagrant') { Git git = new Git(this, "cesmarvin") git.committerName = 'cesmarvin' git.committerEmail = 'cesmarvin@cloudogu.com' + Makefile makefile = new Makefile(this) GitFlow gitflow = new GitFlow(this, git) GitHub github = new GitHub(this, git) Changelog changelog = new Changelog(this) @@ -139,7 +140,9 @@ node('vagrant') { String releaseVersion = git.getSimpleBranchName(); stage('Finish Release') { - gitflow.finishRelease(releaseVersion) + productionReleaseBranch = makefile.determineGitFlowMainBranch() + developmentBranch = makefile.determineGitFlowDevelopBranch() + gitflow.finishRelease(releaseVersion, productionReleaseBranch, developmentBranch) } stage('Push Dogu to registry') { diff --git a/Makefile b/Makefile index 2cf565c6..913611cb 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ MAKEFILES_VERSION=10.5.0 +BASE_VERSION=5.1.8 .DEFAULT_GOAL:=dogu-release From 8b0ae6571ffa6b5ffb4ecea1646b3841ccced0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20G=C3=BCrtler?= Date: Wed, 21 Jan 2026 14:47:04 +0100 Subject: [PATCH 3/9] #170 update dogu-build-lib --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 72335a9f..27c60c13 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,5 @@ #!groovy -@Library(['github.com/cloudogu/ces-build-lib@5.1.0', 'github.com/cloudogu/dogu-build-lib@v3.2.0']) +@Library(['github.com/cloudogu/ces-build-lib@5.1.0', 'github.com/cloudogu/dogu-build-lib@v3.5.1']) import com.cloudogu.ces.cesbuildlib.* import com.cloudogu.ces.dogubuildlib.* From dffb1578a39cd9021323bed7ba3670bd9b1aabce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20G=C3=BCrtler?= Date: Thu, 22 Jan 2026 14:41:55 +0100 Subject: [PATCH 4/9] #170 add write permission for the redmine group to the working directory --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c46d9bd2..d9a9215e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -143,7 +143,9 @@ RUN set -eux -o pipefail \ && rm -rf /root/* /tmp/* $(gem env gemdir)/cache \ && apk --purge del /.build-deps \ && rm -rf /var/cache/apk/* \ - && apk add ruby-irb + && apk add ruby-irb \ + && chown -R "${USER}":"${USER}" "${WORKDIR}" \ + && chmod -R g+w "${WORKDIR}" WORKDIR ${WORKDIR} From 2a22045ca5893b1d96c4a63a37c652e4a69a06b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20G=C3=BCrtler?= Date: Thu, 22 Jan 2026 16:44:01 +0100 Subject: [PATCH 5/9] #170 add function to retry rails script --- resources/util.sh | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/resources/util.sh b/resources/util.sh index cdd68083..f13cbf6f 100644 --- a/resources/util.sh +++ b/resources/util.sh @@ -182,7 +182,7 @@ function create_temporary_admin() { # In case we are in restart loop to prevent infinite admin users... remove_last_temporary_admin - railsConsole "/rails_scripts/create_admin.rb" --username "${TMP_ADMIN_NAME}" --password "${TMP_ADMIN_PASSWORD}" || exit 1 + railsConsoleRetryOnce 60 "/rails_scripts/create_admin.rb" --username "${TMP_ADMIN_NAME}" --password "${TMP_ADMIN_PASSWORD}" || exit 1 doguctl config -e "last_tmp_admin" "${TMP_ADMIN_NAME}" } @@ -272,6 +272,26 @@ function railsConsole() { rails r -e production "$@" } +function railsConsoleRetryOnce() { + local RETRY_AFTER=${1} + local SCRIPT_ARGS=${@:2} + + echo "Run rails script ${SCRIPT_ARGS}" + exec rails r -e production "${SCRIPT_ARGS}" & + + local PID=$! + for i in $(seq 1 "${RETRY_AFTER}"); do + if [[ -d /proc/${PID} ]]; then + sleep 1 + else + exit 0 + fi + done + + echo "Run rails script again: ${SCRIPT_ARGS}" + exec rails r -e production "${SCRIPT_ARGS}" +} + # make the script only run when executed, not when sourced if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then fetchDatabaseConnection From 07f14f8d4ff5be10240ecc77972bffae20ba2134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20G=C3=BCrtler?= Date: Fri, 23 Jan 2026 14:30:49 +0100 Subject: [PATCH 6/9] #170 fix errors, kill hanging process and catch exit code from background process --- resources/util.sh | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/resources/util.sh b/resources/util.sh index f13cbf6f..b9bfd9ae 100644 --- a/resources/util.sh +++ b/resources/util.sh @@ -182,7 +182,7 @@ function create_temporary_admin() { # In case we are in restart loop to prevent infinite admin users... remove_last_temporary_admin - railsConsoleRetryOnce 60 "/rails_scripts/create_admin.rb" --username "${TMP_ADMIN_NAME}" --password "${TMP_ADMIN_PASSWORD}" || exit 1 + railsConsoleRetryOnce 300 "/rails_scripts/create_admin.rb" --username "${TMP_ADMIN_NAME}" --password "${TMP_ADMIN_PASSWORD}" || exit 1 doguctl config -e "last_tmp_admin" "${TMP_ADMIN_NAME}" } @@ -199,7 +199,7 @@ function remove_last_temporary_admin() { then echo "Removing last temporary admin..." # shellcheck disable=SC1091 - railsConsole "/rails_scripts/remove_user.rb" --username "${LAST_TMP_ADMIN}" || exit 1 + railsConsoleRetryOnce 300 "/rails_scripts/remove_user.rb" --username "${LAST_TMP_ADMIN}" || exit 1 doguctl config --rm last_tmp_admin fi } @@ -274,22 +274,28 @@ function railsConsole() { function railsConsoleRetryOnce() { local RETRY_AFTER=${1} - local SCRIPT_ARGS=${@:2} + local SCRIPT_NAME=${2} + local SCRIPT_ARGS=("${@:3}") - echo "Run rails script ${SCRIPT_ARGS}" - exec rails r -e production "${SCRIPT_ARGS}" & + echo "Run rails script ${SCRIPT_NAME}" + rails r -e production "${SCRIPT_NAME}" "${SCRIPT_ARGS[@]}" & + echo "Waiting up to ${RETRY_AFTER} seconds for script ${SCRIPT_NAME} to finish." local PID=$! - for i in $(seq 1 "${RETRY_AFTER}"); do + for _ in $(seq 1 "${RETRY_AFTER}"); do if [[ -d /proc/${PID} ]]; then sleep 1 else - exit 0 + # returns exit code of the background process + wait $PID + return $? fi done - echo "Run rails script again: ${SCRIPT_ARGS}" - exec rails r -e production "${SCRIPT_ARGS}" + echo "Script ${SCRIPT_NAME} did not finish after ${RETRY_AFTER} second. Kill it and run it again." + kill -9 ${PID} + rails r -e production "${SCRIPT_NAME}" "${SCRIPT_ARGS[@]}" + return $? } # make the script only run when executed, not when sourced From 48663c2c7ba6bec3a83816229dfcfd649ba37ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20G=C3=BCrtler?= Date: Fri, 23 Jan 2026 15:43:16 +0100 Subject: [PATCH 7/9] #170 add changelog and release notes; change time to retry to 180 seconds. --- CHANGELOG.md | 3 +++ docs/gui/release_notes_de.md | 3 +++ docs/gui/release_notes_en.md | 3 +++ resources/util.sh | 6 +++--- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d989fa6..5dcbf645 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Fixed +- [#170] Fix hanging scripts that create and remove temporary admins + - backport to redmine 5 ## [v5.1.8-4] - 2025-11-27 ### Fixed diff --git a/docs/gui/release_notes_de.md b/docs/gui/release_notes_de.md index 3428d252..0262f3b5 100644 --- a/docs/gui/release_notes_de.md +++ b/docs/gui/release_notes_de.md @@ -5,6 +5,9 @@ Im Folgenden finden Sie die Release Notes für das Redmine-Dogu. Technische Details zu einem Release finden Sie im zugehörigen [Changelog](https://docs.cloudogu.com/de/docs/dogus/redmine/CHANGELOG/). ## [Unreleased] +### Fixed +* Die Skripte, die beim Systemstart temporäre Administratoren erstellen und entfernen, werden manchmal nicht beendet. + Dieser Fehler wurde in dieser Version behoben. ## [v5.1.8-4] - 2025-11-27 ### Fixed diff --git a/docs/gui/release_notes_en.md b/docs/gui/release_notes_en.md index 3081fd21..4590e733 100644 --- a/docs/gui/release_notes_en.md +++ b/docs/gui/release_notes_en.md @@ -5,6 +5,9 @@ Below you will find the release notes for the Redmine Dogu. Technical details on a release can be found in the corresponding [Changelog](https://docs.cloudogu.com/en/docs/dogus/redmine/CHANGELOG/). ## [Unreleased] +### Fixed +* The scripts that create and remove temporary admins during startup sometimes don't exit. + This bug was fixed in this version. ## [v5.1.8-4] - 2025-11-27 ### Fixed diff --git a/resources/util.sh b/resources/util.sh index b9bfd9ae..309e0bd8 100644 --- a/resources/util.sh +++ b/resources/util.sh @@ -182,7 +182,7 @@ function create_temporary_admin() { # In case we are in restart loop to prevent infinite admin users... remove_last_temporary_admin - railsConsoleRetryOnce 300 "/rails_scripts/create_admin.rb" --username "${TMP_ADMIN_NAME}" --password "${TMP_ADMIN_PASSWORD}" || exit 1 + railsConsoleRetryOnce 180 "/rails_scripts/create_admin.rb" --username "${TMP_ADMIN_NAME}" --password "${TMP_ADMIN_PASSWORD}" || exit 1 doguctl config -e "last_tmp_admin" "${TMP_ADMIN_NAME}" } @@ -199,7 +199,7 @@ function remove_last_temporary_admin() { then echo "Removing last temporary admin..." # shellcheck disable=SC1091 - railsConsoleRetryOnce 300 "/rails_scripts/remove_user.rb" --username "${LAST_TMP_ADMIN}" || exit 1 + railsConsoleRetryOnce 180 "/rails_scripts/remove_user.rb" --username "${LAST_TMP_ADMIN}" || exit 1 doguctl config --rm last_tmp_admin fi } @@ -292,7 +292,7 @@ function railsConsoleRetryOnce() { fi done - echo "Script ${SCRIPT_NAME} did not finish after ${RETRY_AFTER} second. Kill it and run it again." + echo "Script ${SCRIPT_NAME} did not finish after ${RETRY_AFTER} seconds. Kill it and run it again." kill -9 ${PID} rails r -e production "${SCRIPT_NAME}" "${SCRIPT_ARGS[@]}" return $? From 2d3eae1a4abeacfd4789bff980153c6973f3c0cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20G=C3=BCrtler?= Date: Mon, 26 Jan 2026 15:53:23 +0100 Subject: [PATCH 8/9] #170 set owner only to the file that needed it; improved message --- Dockerfile | 3 +-- resources/util.sh | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index d9a9215e..1b1bfbc6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -144,8 +144,7 @@ RUN set -eux -o pipefail \ && apk --purge del /.build-deps \ && rm -rf /var/cache/apk/* \ && apk add ruby-irb \ - && chown -R "${USER}":"${USER}" "${WORKDIR}" \ - && chmod -R g+w "${WORKDIR}" + && chown "${USER}":"${USER}" "${WORKDIR}/Gemfile.lock" WORKDIR ${WORKDIR} diff --git a/resources/util.sh b/resources/util.sh index 309e0bd8..c974a081 100644 --- a/resources/util.sh +++ b/resources/util.sh @@ -292,7 +292,7 @@ function railsConsoleRetryOnce() { fi done - echo "Script ${SCRIPT_NAME} did not finish after ${RETRY_AFTER} seconds. Kill it and run it again." + echo "Script ${SCRIPT_NAME} did not finish after ${RETRY_AFTER} seconds. Killing it and running it again..." kill -9 ${PID} rails r -e production "${SCRIPT_NAME}" "${SCRIPT_ARGS[@]}" return $? From 1c977f5ffc0fd4af2ec43361038067861cfe882c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20G=C3=BCrtler?= Date: Thu, 29 Jan 2026 14:10:17 +0100 Subject: [PATCH 9/9] #176 made retry timeout for rails scripts configurable --- dogu.json | 6 ++++++ resources/util.sh | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/dogu.json b/dogu.json index 13bc5f1c..9939c4a4 100644 --- a/dogu.json +++ b/dogu.json @@ -105,6 +105,12 @@ "Name": "rack/bytesize_limit", "Description": "Configure the maximum byte size of query strings that the redmine web server (rack) allows. The default is 4194304 (4 MiB). Depending on the available processing power setting this parameter to high might cause redmine to soft lock.", "Optional": true + }, + { + "Name": "rails_script_timeout", + "Description": "Define after how many seconds the execution of critical rails scripts in the startup process is terminated and tried again", + "Optional": true, + "Default": "180" } ], "Volumes": [ diff --git a/resources/util.sh b/resources/util.sh index c974a081..7dece901 100644 --- a/resources/util.sh +++ b/resources/util.sh @@ -178,11 +178,12 @@ function create_temporary_admin() { local TMP_ADMIN_RANDOMIZED_STR TMP_ADMIN_RANDOMIZED_STR="$(doguctl random -l 60)" TMP_ADMIN_PASSWORD="${TMP_ADMIN_RANDOMIZED_STR}${TMP_ADMIN_PASSWORD_SUFFIX}" + RAILS_TIMEOUT="$(doguctl config rails_script_timeout)" # In case we are in restart loop to prevent infinite admin users... remove_last_temporary_admin - railsConsoleRetryOnce 180 "/rails_scripts/create_admin.rb" --username "${TMP_ADMIN_NAME}" --password "${TMP_ADMIN_PASSWORD}" || exit 1 + railsConsoleRetryOnce "${RAILS_TIMEOUT}" "/rails_scripts/create_admin.rb" --username "${TMP_ADMIN_NAME}" --password "${TMP_ADMIN_PASSWORD}" || exit 1 doguctl config -e "last_tmp_admin" "${TMP_ADMIN_NAME}" } @@ -194,12 +195,13 @@ function remove_last_temporary_admin() { local DEFAULT="" local LAST_TMP_ADMIN LAST_TMP_ADMIN="$(doguctl config -e --default "${DEFAULT}" last_tmp_admin)" + RAILS_TIMEOUT="$(doguctl config rails_script_timeout)" if [ "${LAST_TMP_ADMIN}" != "${DEFAULT}" ] then echo "Removing last temporary admin..." # shellcheck disable=SC1091 - railsConsoleRetryOnce 180 "/rails_scripts/remove_user.rb" --username "${LAST_TMP_ADMIN}" || exit 1 + railsConsoleRetryOnce "${RAILS_TIMEOUT}" "/rails_scripts/remove_user.rb" --username "${LAST_TMP_ADMIN}" || exit 1 doguctl config --rm last_tmp_admin fi }