From 5481bcef885f0eccc362687805569bdf9ac3bca4 Mon Sep 17 00:00:00 2001 From: Barys Barysenka Date: Wed, 12 Nov 2025 19:27:57 -0500 Subject: [PATCH 01/28] feat(rear): blkid * added blkid to the list of must have programs Jira-Ref: BCF-5671: [BM] Remove blkid and lsblk binaries from installation package --- usr/share/rear/conf/GNU/Linux.conf | 1 - usr/share/rear/conf/default.conf | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/share/rear/conf/GNU/Linux.conf b/usr/share/rear/conf/GNU/Linux.conf index 9ebc3e70a4..75cf0e818d 100644 --- a/usr/share/rear/conf/GNU/Linux.conf +++ b/usr/share/rear/conf/GNU/Linux.conf @@ -22,7 +22,6 @@ PROGS+=( rpc.statd rpcbind mknod -blkid vol_id udev_volume_id portmap diff --git a/usr/share/rear/conf/default.conf b/usr/share/rear/conf/default.conf index 7fff90a9ee..9f7f5cbb96 100644 --- a/usr/share/rear/conf/default.conf +++ b/usr/share/rear/conf/default.conf @@ -1759,6 +1759,7 @@ REQUIRED_PROGS=( awk bash bc +blkid cat cmp cp From 1cf494a708975b499d3fddd9f582bad421ed6f01 Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Tue, 18 Nov 2025 09:25:24 -0500 Subject: [PATCH 02/28] feat(bm): Changed naming of rear tarball Jira-Ref: BCF-5414: Adapt build scripts for cove --- Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Makefile b/Makefile index 4097de5f6f..68807fbb13 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,14 @@ else git_ref := $(shell git rev-parse HEAD | cut -c 1-8) git_count := $(shell git rev-list HEAD --no-merges | wc -l) git_branch_suffix = $(shell git symbolic-ref HEAD | sed -e 's,^.*/,,' -e "s/[^A-Za-z0-9]//g") + release_sufix = $(shell git symbolic-ref HEAD | grep -oP "(?<=release/)[\d\-\.]*-cove") + ifneq ($(release_sufix),) + git_branch_suffix = $(release_sufix) + endif + feature_sufix = $(shell git symbolic-ref HEAD | grep -oP "(?<=feature/)\w{1,}-\d{1,}|(?<=hotfix/)\w{1,}-\d{1,}") + ifneq ($(feature_sufix),) + git_branch_suffix = $(feature_sufix) + endif git_status := $(shell git status --porcelain) git_stamp := $(git_count).$(git_ref).$(git_branch_suffix) ifneq ($(git_status),) From bec0c4a8c2739e0358d2528803745edfc423d384 Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Tue, 18 Nov 2025 09:38:19 -0500 Subject: [PATCH 03/28] feat(bm): Review fixes: fixed typo Jira-Ref: BCF-5414: Adapt build scripts for cove --- Makefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 68807fbb13..d06312a4ca 100644 --- a/Makefile +++ b/Makefile @@ -35,13 +35,13 @@ else git_ref := $(shell git rev-parse HEAD | cut -c 1-8) git_count := $(shell git rev-list HEAD --no-merges | wc -l) git_branch_suffix = $(shell git symbolic-ref HEAD | sed -e 's,^.*/,,' -e "s/[^A-Za-z0-9]//g") - release_sufix = $(shell git symbolic-ref HEAD | grep -oP "(?<=release/)[\d\-\.]*-cove") - ifneq ($(release_sufix),) - git_branch_suffix = $(release_sufix) + release_suffix = $(shell git symbolic-ref HEAD | grep -oP "(?<=release/)[\d\-\.]*-cove") + ifneq ($(release_suffix),) + git_branch_suffix = $(release_suffix) endif - feature_sufix = $(shell git symbolic-ref HEAD | grep -oP "(?<=feature/)\w{1,}-\d{1,}|(?<=hotfix/)\w{1,}-\d{1,}") - ifneq ($(feature_sufix),) - git_branch_suffix = $(feature_sufix) + feature_suffix = $(shell git symbolic-ref HEAD | grep -oP "(?<=feature/)\w{1,}-\d{1,}|(?<=hotfix/)\w{1,}-\d{1,}") + ifneq ($(feature_suffix),) + git_branch_suffix = $(feature_suffix) endif git_status := $(shell git status --porcelain) git_stamp := $(git_count).$(git_ref).$(git_branch_suffix) From bf7fb8815e9238f8a7f092ad1056adf747db33ba Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Wed, 19 Nov 2025 03:51:11 -0500 Subject: [PATCH 04/28] feat(bm): Fixed CI build: changed 'grep -P' to 'sed' Jira-Ref: BCF-5414: Adapt build scripts for cove --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d06312a4ca..5c53db9e2f 100644 --- a/Makefile +++ b/Makefile @@ -35,11 +35,11 @@ else git_ref := $(shell git rev-parse HEAD | cut -c 1-8) git_count := $(shell git rev-list HEAD --no-merges | wc -l) git_branch_suffix = $(shell git symbolic-ref HEAD | sed -e 's,^.*/,,' -e "s/[^A-Za-z0-9]//g") - release_suffix = $(shell git symbolic-ref HEAD | grep -oP "(?<=release/)[\d\-\.]*-cove") + release_suffix = $(shell git symbolic-ref HEAD | sed -nE 's/^.*?release\/([0-9.-]*-cove).*/\1/p') ifneq ($(release_suffix),) git_branch_suffix = $(release_suffix) endif - feature_suffix = $(shell git symbolic-ref HEAD | grep -oP "(?<=feature/)\w{1,}-\d{1,}|(?<=hotfix/)\w{1,}-\d{1,}") + feature_suffix = $(shell git symbolic-ref HEAD | sed -nE 's/^.*?(feature|hotfix)\/([A-Za-z0-9_-]+-[0-9]+).*/\2/p') ifneq ($(feature_suffix),) git_branch_suffix = $(feature_suffix) endif From 4ddfce93d44e18a29c8e8a1310e2301d8a1a6382 Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Mon, 24 Nov 2025 05:33:39 -0500 Subject: [PATCH 05/28] feat(bm): Fixed output tarball names according to acceptance criteria Jira-Ref: BCF-5414: Adapt build scripts for cove --- Makefile | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 5c53db9e2f..8ba64650bd 100644 --- a/Makefile +++ b/Makefile @@ -37,17 +37,22 @@ else git_branch_suffix = $(shell git symbolic-ref HEAD | sed -e 's,^.*/,,' -e "s/[^A-Za-z0-9]//g") release_suffix = $(shell git symbolic-ref HEAD | sed -nE 's/^.*?release\/([0-9.-]*-cove).*/\1/p') ifneq ($(release_suffix),) - git_branch_suffix = $(release_suffix) + release_tag = $(shell git tag --points-at HEAD) + ifneq ($(release_tag),) + release_suffix = $(release_tag) + else + git_branch_suffix = $(release_suffix) + endif endif feature_suffix = $(shell git symbolic-ref HEAD | sed -nE 's/^.*?(feature|hotfix)\/([A-Za-z0-9_-]+-[0-9]+).*/\2/p') ifneq ($(feature_suffix),) git_branch_suffix = $(feature_suffix) endif git_status := $(shell git status --porcelain) - git_stamp := $(git_count).$(git_ref).$(git_branch_suffix) ifneq ($(git_status),) - git_stamp := $(git_stamp).changed - endif # git_status + changed_suffix = .changed + endif + git_stamp := $(git_count).$(git_ref).$(git_branch_suffix) else # no git git_date := now git_ref := 0 @@ -58,9 +63,13 @@ else endif # has .git git_stamp ?= 0.0.unknown - distversion = $(version)-git.$(git_stamp) - debrelease = 0git.$(git_stamp) - rpmrelease = .git.$(git_stamp) + ifneq ($(release_suffix),) + distversion = $(release_suffix)$(changed_suffix) + else # is not release version + distversion = $(version).$(git_stamp)$(changed_suffix) + endif # is release version + debrelease = 0git.$(git_stamp)$(changed_suffix) + rpmrelease = .git.$(git_stamp)$(changed_suffix) obsproject = Archiving:Backup:Rear:Snapshot obspackage = $(name) From 21b3f681b9cf266c8de92d6f43df2eb4532bd52c Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Wed, 3 Dec 2025 13:52:25 +0100 Subject: [PATCH 06/28] feat(cove): upgrade Shim and GRUB on Debian 10 Jira-Ref: BCF-6007: [ReaR] Debian 10 fails to boot after recovery with Secure Boot enabled --- .../COVE/Debian/620_upgrade_bootloders.sh | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloders.sh diff --git a/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloders.sh b/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloders.sh new file mode 100644 index 0000000000..519e1454b9 --- /dev/null +++ b/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloders.sh @@ -0,0 +1,47 @@ +# +# Upgrade Shim and GRUB bootloaders on Debian 10 +# +# Shim and GRUB are upgraded because Shim from the rescue system +# which is Debian12-based adds new entries to SBAT that leads to having +# non-SecureBoot compatible device after BMR. +# + +if [ "$OS_VERSION" != "10" ]; then + return 0 +fi + +if ! is_true "$USING_UEFI_BOOTLOADER"; then + return 0 +fi + +if is_cove_in_azure; then + return 0 +fi + +if is_true "$EFI_STUB"; then + return 0 +fi + +if [ "${UEFI_BOOTLOADER##*/}" != "shimx64.efi" ]; then + return 0 +fi + +function upgrade_bootloaders() { + local target_bootloder_dir="${UEFI_BOOTLOADER%/*}" + target_bootloder_dir="$TARGET_FS_ROOT$target_bootloder_dir" + + local shim="$target_bootloder_dir/shimx64.efi" + local grub="$target_bootloder_dir/grubx64.efi" + + cp -b /usr/lib/shim/shimx64.efi.signed "$shim" || return 1 + cp -b /usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed "$grub" || \ + { mv "$shim~" "$shim"; return 1; } + + rm "$shim~" "$grub~" +} + +if upgrade_bootloaders; then + LogPrint "Upgraded signed Shim and GRUB bootloaders for this system." +else + LogPrint "Failed to upgrade signed Shim and GRUB bootloaders for this system. UEFI Secure Boot might not available." +fi From baf922778b79b0f23eeef132e18458bc689b805b Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Wed, 3 Dec 2025 14:06:28 +0100 Subject: [PATCH 07/28] feat(cove): correct grammar errors Jira-Ref: BCF-6007: [ReaR] Debian 10 fails to boot after recovery with Secure Boot enabled --- .../finalize/COVE/Debian/620_upgrade_bootloders.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloders.sh b/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloders.sh index 519e1454b9..e15a28e539 100644 --- a/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloders.sh +++ b/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloders.sh @@ -27,11 +27,11 @@ if [ "${UEFI_BOOTLOADER##*/}" != "shimx64.efi" ]; then fi function upgrade_bootloaders() { - local target_bootloder_dir="${UEFI_BOOTLOADER%/*}" - target_bootloder_dir="$TARGET_FS_ROOT$target_bootloder_dir" + local target_bootloader_dir="${UEFI_BOOTLOADER%/*}" + target_bootloader_dir="$TARGET_FS_ROOT$target_bootloader_dir" - local shim="$target_bootloder_dir/shimx64.efi" - local grub="$target_bootloder_dir/grubx64.efi" + local shim="$target_bootloader_dir/shimx64.efi" + local grub="$target_bootloader_dir/grubx64.efi" cp -b /usr/lib/shim/shimx64.efi.signed "$shim" || return 1 cp -b /usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed "$grub" || \ @@ -43,5 +43,5 @@ function upgrade_bootloaders() { if upgrade_bootloaders; then LogPrint "Upgraded signed Shim and GRUB bootloaders for this system." else - LogPrint "Failed to upgrade signed Shim and GRUB bootloaders for this system. UEFI Secure Boot might not available." + LogPrint "Failed to upgrade signed Shim and GRUB bootloaders for this system. UEFI Secure Boot might not be available." fi From d24e91e5f56ea00c2b7905ffbcc92172f8e924dd Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Wed, 3 Dec 2025 14:23:31 +0100 Subject: [PATCH 08/28] feat(cove): correct file name Jira-Ref: BCF-6007: [ReaR] Debian 10 fails to boot after recovery with Secure Boot enabled --- .../{620_upgrade_bootloders.sh => 620_upgrade_bootloaders.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename usr/share/rear/finalize/COVE/Debian/{620_upgrade_bootloders.sh => 620_upgrade_bootloaders.sh} (100%) diff --git a/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloders.sh b/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh similarity index 100% rename from usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloders.sh rename to usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh From 0e4f1560ee4bd2635fcfa19740df30a5ca319079 Mon Sep 17 00:00:00 2001 From: Barys Barysenka Date: Wed, 3 Dec 2025 08:43:12 -0500 Subject: [PATCH 09/28] feat(rear): unit tests * create pipeline to run unit tests Jira-Ref: BCF-6186: [ReaR] Run unit tests during CI --- Jenkinsfile | 109 ++++++++++++++++++++++++++++++++++ Makefile | 2 +- containers/builder/Dockerfile | 24 ++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 Jenkinsfile create mode 100644 containers/builder/Dockerfile diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000000..3bc49c544b --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,109 @@ +@Library('cove') +import nable.cove.helpers.ShellHelper +import nable.cove.SecretManager + +final String jobName = env.JOB_NAME.split('/')[-2] +final boolean isProd = jobName.endsWith('-prd') +final String envType = isProd ? 'prd' : 'dev' + +def String repositoryName = 'rear' + +def config = [ + cloud: "backup-${envType}", + serviceAccount: "backup", + buildImage: "${nsbuild.ecrHost()}/cove/onprem/develop/rear-builder:v1.7" +] + +def secrets = [ + jenkins: [ + 'github-app': [ + usernamePassword: [ + usernameVariable: 'GITHUB_USERNAME', + passwordVariable: 'GITHUB_PASSWORD' + ] + ] + ], + kubernetes: [ + 'artifactory': [ + 'JFROG_USERNAME': 'ARTIFACTORY_USERNAME_COVE', + 'JFROG_ACCESS_TOKEN': 'ARTIFACTORY_TOKEN_COVE' + ] + ] +] + +def secretManager +def shellHelper + +pipeline { + agent { + kubernetes { + cloud "${config.cloud}" + yaml nsbuild.agentYaml(config) + defaultContainer nsbuild.defaultContainer(config) + } + } + + options { + ansiColor('xterm') + } + + stages { + stage('Prepare') { + steps { + script { + secretManager = new SecretManager( + this, + envType, + jenkinsWhitelist: [ + 'github-app' + ], + k8sWhitelist: [ + 'artifactory' + ] + ) + shellHelper = new ShellHelper(this, isUnix: true) + } + } + } + stage('Load secrets') { + agent { + kubernetes { + cloud "${config.cloud}" + yaml secretManager.getK8sPodYaml(secrets) + defaultContainer 'secrets' + customWorkspace 'w' + } + } + steps { + script { + secretManager.loadJenkinsSecrets(secrets.jenkins) + secretManager.loadK8sSecrets(secrets.kubernetes, shellHelper) + } + } + } + stage('Build') { + environment { + ARTIFACTORY_URL = 'https://mspsolarwinds.jfrog.io/artifactory' + } + steps { + script { + secretManager.withSecrets { + shellHelper.exec('Validate', """ + make validate + """) + shellHelper.exec('Build', """ + make dist + """) + def repository = (envType == 'dev') ? 'cove-generic-develop-local' : 'cove-generic-release-local' + shellHelper.exec('Upload', """ + PACKAGE="rear-\$(make version).tar.gz" + curl -sSf -X PUT -T dist/\${PACKAGE} \ + -u \${ARTIFACTORY_USERNAME_COVE}:\${ARTIFACTORY_TOKEN_COVE} \ + \${ARTIFACTORY_URL}/${repository}/rear/\${PACKAGE} + """) + } + } + } + } + } +} diff --git a/Makefile b/Makefile index 4097de5f6f..bde0448d88 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ else git_date := $(shell git log -n 1 --format="%ai") git_ref := $(shell git rev-parse HEAD | cut -c 1-8) git_count := $(shell git rev-list HEAD --no-merges | wc -l) - git_branch_suffix = $(shell git symbolic-ref HEAD | sed -e 's,^.*/,,' -e "s/[^A-Za-z0-9]//g") + git_branch_suffix = $(shell echo "$${CHANGE_BRANCH:-$${BRANCH_NAME:-$$(git symbolic-ref HEAD || echo unknown)}}" | sed -e 's,^.*/,,' -e "s/[^A-Za-z0-9]//g") git_status := $(shell git status --porcelain) git_stamp := $(git_count).$(git_ref).$(git_branch_suffix) ifneq ($(git_status),) diff --git a/containers/builder/Dockerfile b/containers/builder/Dockerfile new file mode 100644 index 0000000000..aafd2cb0c3 --- /dev/null +++ b/containers/builder/Dockerfile @@ -0,0 +1,24 @@ +ARG CONTAINER_REPO=263262308774.dkr.ecr.eu-west-1.amazonaws.com + +FROM ${CONTAINER_REPO}/ns/mirror/prd/library/debian:12.11-slim + +RUN apt-get update && \ + apt-get install -y \ + git \ + bats \ + bash \ + coreutils \ + curl \ + make \ + && rm -rf /var/lib/apt/lists/* + +ARG user=build +ARG group=build +ARG uid=1000 +ARG gid=1000 + +RUN groupadd -g "${gid}" "${group}" \ + && useradd -l -c "Build user" -d "/${user}" -u "${uid}" -g "${gid}" \ + -s /bin/bash -m "${user}" + +USER build From 0824d2b88456868880b5380747c7ed434488be47 Mon Sep 17 00:00:00 2001 From: Barys Barysenka Date: Thu, 27 Nov 2025 05:07:22 -0500 Subject: [PATCH 10/28] feat(rear): unit tests * added docker file to give the possibility to execute unit tests Jira-Ref: BCF-6186: [ReaR] Run unit tests during CI --- Jenkinsfile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 3bc49c544b..276c181382 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -94,6 +94,16 @@ pipeline { shellHelper.exec('Build', """ make dist """) + shellHelper.exec('Run unit tests', """ + cd tests/COVE && \ + for test_file in *.sh; do \ + echo "Running \$test_file..." && \ + ./"\$test_file" && \ + echo "✓ \$test_file passed" && \ + echo ""; \ + done && \ + echo "All COVE tests passed!" + """) def repository = (envType == 'dev') ? 'cove-generic-develop-local' : 'cove-generic-release-local' shellHelper.exec('Upload', """ PACKAGE="rear-\$(make version).tar.gz" From 8c81006c3892de9293e719c11b059a02162e185a Mon Sep 17 00:00:00 2001 From: Barys Barysenka Date: Wed, 3 Dec 2025 07:10:04 -0500 Subject: [PATCH 11/28] feat(rear): unit tests * create pipeline to run unit tests Jira-Ref: BCF-6186: [ReaR] Run unit tests during CI --- Jenkinsfile | 13 +- Makefile | 11 ++ ...000_save_future_dangling_efi_entries.bats} | 125 ++++++------------ .../COVE/001_remove_dangling_efi_entries.bats | 87 ++++++++++++ tests/COVE/001_remove_dangling_efi_entries.sh | 116 ---------------- 5 files changed, 141 insertions(+), 211 deletions(-) rename tests/COVE/{000_save_future_dangling_efi_entries.sh => 000_save_future_dangling_efi_entries.bats} (64%) create mode 100755 tests/COVE/001_remove_dangling_efi_entries.bats delete mode 100755 tests/COVE/001_remove_dangling_efi_entries.sh diff --git a/Jenkinsfile b/Jenkinsfile index 276c181382..5c7c324bb5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -91,19 +91,12 @@ pipeline { shellHelper.exec('Validate', """ make validate """) + shellHelper.exec('Run unit tests', """ + make test-cove + """) shellHelper.exec('Build', """ make dist """) - shellHelper.exec('Run unit tests', """ - cd tests/COVE && \ - for test_file in *.sh; do \ - echo "Running \$test_file..." && \ - ./"\$test_file" && \ - echo "✓ \$test_file passed" && \ - echo ""; \ - done && \ - echo "All COVE tests passed!" - """) def repository = (envType == 'dev') ? 'cove-generic-develop-local' : 'cove-generic-release-local' shellHelper.exec('Upload', """ PACKAGE="rear-\$(make version).tar.gz" diff --git a/Makefile b/Makefile index bde0448d88..2e77d2959d 100644 --- a/Makefile +++ b/Makefile @@ -101,6 +101,7 @@ help: \n\ version - Show ReaR version used\n\ validate - Check source code\n\ + test-cove - Run COVE unit tests with bats\n\ install - Install Relax-and-Recover (may replace files)\n\ uninstall - Uninstall Relax-and-Recover (may remove files)\n\ dist - Create tar file in dist/\n\ @@ -153,6 +154,16 @@ validate: fi; \ done +test-cove: + @echo "== Running COVE unit tests ==" + @for test_file in tests/COVE/*.bats; do \ + echo ""; \ + echo "========================================"; \ + echo "Executing: $${test_file}"; \ + echo "========================================"; \ + bats "$${test_file}"; \ + done + man: @echo -e "\033[1m== Prepare manual ==\033[0;0m" $(MAKE) -C doc man diff --git a/tests/COVE/000_save_future_dangling_efi_entries.sh b/tests/COVE/000_save_future_dangling_efi_entries.bats similarity index 64% rename from tests/COVE/000_save_future_dangling_efi_entries.sh rename to tests/COVE/000_save_future_dangling_efi_entries.bats index f2fe9f071f..41a62b5fbd 100755 --- a/tests/COVE/000_save_future_dangling_efi_entries.sh +++ b/tests/COVE/000_save_future_dangling_efi_entries.bats @@ -1,26 +1,27 @@ -#!/usr/bin/env bash - -SCRIPT_DIR="$(dirname "${0}")" -SCRIPT_DIR="$(realpath "${SCRIPT_DIR}")" -readonly SCRIPT_DIR - -REAR_SHARE_DIR=$(realpath "$SCRIPT_DIR/../../usr/share/rear") -readonly REAR_SHARE_DIR - -# shellcheck disable=SC1091 -source "$REAR_SHARE_DIR/lib/global-functions.sh" +setup_file() { + export REAR_SHARE_DIR="$(realpath "$BATS_TEST_DIRNAME/../../usr/share/rear")" + export USING_UEFI_BOOTLOADER="yes" + export COVE_TESTS="yes" +} -# shellcheck disable=SC2034 -readonly USING_UEFI_BOOTLOADER="yes" +setup() { + function is_cove_in_azure() { + false + } -# shellcheck disable=SC2034 -readonly COVE_TESTS="yes" + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/lib/global-functions.sh" + + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh" +} -function is_cove_in_azure() { - false +teardown() { + # Clean up any test-specific state if needed + unset DISKS_TO_BE_OVERWRITTEN } -function find_future_dangling_entry() { +@test "find single future dangling EFI entry" { function get_partuuids_of_disks_to_be_overwritten() { echo "368a5c5b-26bf-4a63-b9ae-64a92d79d085" echo "9bf08aed-f779-49fe-b310-9c21983289c1" @@ -41,16 +42,12 @@ function find_future_dangling_entry() { } local boot_number - boot_number="$(get_future_dangling_efi_entries)" || return 1 - - if [ "$boot_number" != "0003" ]; then - return 1 - fi - - return 0 + boot_number="$(get_future_dangling_efi_entries)" + + [ "$boot_number" = "0003" ] } -function find_future_dangling_entries() { +@test "find multiple future dangling EFI entries" { function get_partuuids_of_disks_to_be_overwritten() { echo "bc6f95e7-893a-434b-8b76-f3d52e6ad28d" } @@ -63,16 +60,12 @@ function find_future_dangling_entries() { } local boot_number - boot_number="$(get_future_dangling_efi_entries)" || return 1 - - if [ "$boot_number" != "0003 0004" ]; then - return 1 - fi - - return 0 + boot_number="$(get_future_dangling_efi_entries)" + + [ "$boot_number" = "0003 0004" ] } -function unexpected_efi_entry() { +@test "handle unexpected EFI entry format" { function get_partuuids_of_disks_to_be_overwritten() { echo "bc6f95e7-893a-434b-8b76-f3d52e6ad28d" } @@ -83,16 +76,12 @@ function unexpected_efi_entry() { } local boot_number - boot_number="$(get_future_dangling_efi_entries)" || return 1 - - if [ -n "$boot_number" ]; then - return 1 - fi - - return 0 + boot_number="$(get_future_dangling_efi_entries)" + + [ -z "$boot_number" ] } -function empty_functions() { +@test "handle empty functions" { function get_partuuids_of_disks_to_be_overwritten() { : } @@ -102,16 +91,12 @@ function empty_functions() { } local boot_number - boot_number="$(get_future_dangling_efi_entries)" || return 1 - - if [ -n "$boot_number" ]; then - return 1 - fi - - return 0 + boot_number="$(get_future_dangling_efi_entries)" + + [ -z "$boot_number" ] } -function get_partuuids_of_disks_to_be_overwritten_exits_with_error() { +@test "handle get_partuuids_of_disks_to_be_overwritten error" { function get_partuuids_of_disks_to_be_overwritten() { return 1 } @@ -120,18 +105,12 @@ function get_partuuids_of_disks_to_be_overwritten_exits_with_error() { : } - local boot_number - if boot_number="$(get_future_dangling_efi_entries)"; then - return 1 - fi - - return 0 + run get_future_dangling_efi_entries + [ "$status" -ne 0 ] } -function one_disk_to_be_overwritten() { - # shellcheck disable=SC2034 +@test "get partuuids for one disk to be overwritten" { DISKS_TO_BE_OVERWRITTEN="/dev/sda" - local expected_partuuids="368a5c5b-26bf-4a63-b9ae-64a92d79d085" function get_disk_partuuids() { @@ -150,8 +129,7 @@ function one_disk_to_be_overwritten() { [ "$expected_partuuids" = "$actual_partuuids" ] } -function two_disks_to_be_overwritten() { - # shellcheck disable=SC2034 +@test "get partuuids for two disks to be overwritten" { DISKS_TO_BE_OVERWRITTEN="/dev/sda /dev/sdb" function get_disk_partuuids() { @@ -174,8 +152,7 @@ function two_disks_to_be_overwritten() { [ "$expected_partuuids" = "$actual_partuuids" ] } -function no_disks_to_be_overwritten() { - # shellcheck disable=SC2034 +@test "handle no disks to be overwritten" { DISKS_TO_BE_OVERWRITTEN="" function get_disk_partuuids() { @@ -187,25 +164,3 @@ function no_disks_to_be_overwritten() { [ -z "$partuuids" ] } - -set -e - -TESTS=( - find_future_dangling_entry - find_future_dangling_entries - unexpected_efi_entry - empty_functions - get_partuuids_of_disks_to_be_overwritten_exits_with_error - one_disk_to_be_overwritten - two_disks_to_be_overwritten - no_disks_to_be_overwritten -) - -for test in "${TESTS[@]}"; do - # shellcheck disable=SC1091 - source "$REAR_SHARE_DIR/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh" - if ! "$test"; then - echo "$test failed" - exit 1 - fi -done diff --git a/tests/COVE/001_remove_dangling_efi_entries.bats b/tests/COVE/001_remove_dangling_efi_entries.bats new file mode 100755 index 0000000000..026a2769d0 --- /dev/null +++ b/tests/COVE/001_remove_dangling_efi_entries.bats @@ -0,0 +1,87 @@ +setup_file() { + export REAR_SHARE_DIR="$(realpath "$BATS_TEST_DIRNAME/../../usr/share/rear")" + export USING_UEFI_BOOTLOADER="yes" + export COVE_TESTS="yes" +} + +setup() { + function is_cove_in_azure() { + false + } + + function LogPrint() { + echo "$@" + } + + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/lib/global-functions.sh" + + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/finalize/COVE/default/665_remove_dangling_efi_entries.sh" +} + +@test "no future dangling efi entries" { + FUTURE_DANGLING_EFI_ENTRIES="" + + run remove_dangling_efi_entries + + [ "$status" -eq 0 ] +} + +@test "one entry to remove" { + FUTURE_DANGLING_EFI_ENTRIES="0001" + + function remove_efi_entry() { + : + } + + local expected_output="Removing EFI Boot Manager entry with '0001' entry ID" + + run remove_dangling_efi_entries + + [ "$status" -eq 0 ] + [ "$output" = "$expected_output" ] +} + +@test "two entries to remove" { + FUTURE_DANGLING_EFI_ENTRIES="0001 0003" + + function remove_efi_entry() { + : + } + + local expected_output + expected_output="$(printf "%s\n%s" \ + "Removing EFI Boot Manager entry with '0001' entry ID" \ + "Removing EFI Boot Manager entry with '0003' entry ID")" + + run remove_dangling_efi_entries + + [ "$status" -eq 0 ] + [ "$output" = "$expected_output" ] +} + +@test "fail to remove efi entry" { + FUTURE_DANGLING_EFI_ENTRIES="0001 0003" + + function remove_efi_entry() { + local id="$1" + if [ "$id" = "0001" ]; then + return 0 + elif [ "$id" = "0003" ]; then + return 1 + fi + } + + local expected_output + expected_output="$(printf "%s\n%s\n%s" \ + "Removing EFI Boot Manager entry with '0001' entry ID" \ + "Removing EFI Boot Manager entry with '0003' entry ID" \ + "Failed to remove EFI Boot Manager entry with '0003' entry ID" + )" + + run remove_dangling_efi_entries + + [ "$status" -eq 0 ] + [ "$output" = "$expected_output" ] +} diff --git a/tests/COVE/001_remove_dangling_efi_entries.sh b/tests/COVE/001_remove_dangling_efi_entries.sh deleted file mode 100755 index 163d7b088b..0000000000 --- a/tests/COVE/001_remove_dangling_efi_entries.sh +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env bash - -SCRIPT_DIR="$(dirname "${0}")" -SCRIPT_DIR="$(realpath "${SCRIPT_DIR}")" -readonly SCRIPT_DIR - -REAR_SHARE_DIR=$(realpath "$SCRIPT_DIR/../../usr/share/rear") -readonly REAR_SHARE_DIR - -# shellcheck disable=SC1091 -source "$REAR_SHARE_DIR/lib/global-functions.sh" - -# shellcheck disable=SC2034 -readonly USING_UEFI_BOOTLOADER="yes" - -# shellcheck disable=SC2034 -readonly COVE_TESTS="yes" - -function is_cove_in_azure() { - false -} - -function no_future_dangling_efi_entries() { - FUTURE_DANGLING_EFI_ENTRIES="" - remove_dangling_efi_entries -} - -function one_entry_to_remove() { - FUTURE_DANGLING_EFI_ENTRIES="0001" - - function LogPrint() { - echo "$@" - } - - function remove_efi_entry() { - : - } - - local expected_output="Removing EFI Boot Manager entry with '0001' entry ID" - - local actual_output - actual_output="$(remove_dangling_efi_entries)" - - [ "$expected_output" = "$actual_output" ] -} - -function two_entries_to_remove() { - # shellcheck disable=SC2034 - FUTURE_DANGLING_EFI_ENTRIES="0001 0003" - - function LogPrint() { - echo "$@" - } - - function remove_efi_entry() { - : - } - - local expected_output - expected_output="$(printf "%s\n%s" \ - "Removing EFI Boot Manager entry with '0001' entry ID" \ - "Removing EFI Boot Manager entry with '0003' entry ID")" - - local actual_output - actual_output="$(remove_dangling_efi_entries)" - - [ "$expected_output" = "$actual_output" ] -} - -function fail_to_remove_efi_entry() { - # shellcheck disable=SC2034 - FUTURE_DANGLING_EFI_ENTRIES="0001 0003" - - function LogPrint() { - echo "$@" - } - - function remove_efi_entry() { - local id="$1" - if [ "$id" = "0001" ]; then - return 0 - elif [ "$id" = "0003" ]; then - return 1 - fi - } - - local expected_output - expected_output="$(printf "%s\n%s\n%s" \ - "Removing EFI Boot Manager entry with '0001' entry ID" \ - "Removing EFI Boot Manager entry with '0003' entry ID" \ - "Failed to remove EFI Boot Manager entry with '0003' entry ID" - )" - - local actual_output - actual_output="$(remove_dangling_efi_entries)" - - [ "$expected_output" = "$actual_output" ] -} - -set -e - -TESTS=( - no_future_dangling_efi_entries - one_entry_to_remove - two_entries_to_remove - fail_to_remove_efi_entry -) - -for test in "${TESTS[@]}"; do - # shellcheck disable=SC1091 - source "$REAR_SHARE_DIR/finalize/COVE/default/665_remove_dangling_efi_entries.sh" - if ! "$test"; then - echo "$test failed" - exit 1 - fi -done From 4070ad243a19143c44c74f40493572d300ef6298 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Thu, 4 Dec 2025 13:50:48 +0100 Subject: [PATCH 12/28] feat(cove): skip upgrading Shim and GRUB if SB disabled - Add unit tests Jira-Ref: BCF-6007: [ReaR] Debian 10 fails to boot after recovery with Secure Boot enabled --- .../002_upgrade_bootloaders_on_debian10.bats | 121 ++++++++++++++++++ .../COVE/Debian/620_upgrade_bootloaders.sh | 18 ++- 2 files changed, 138 insertions(+), 1 deletion(-) create mode 100755 tests/COVE/002_upgrade_bootloaders_on_debian10.bats diff --git a/tests/COVE/002_upgrade_bootloaders_on_debian10.bats b/tests/COVE/002_upgrade_bootloaders_on_debian10.bats new file mode 100755 index 0000000000..c24ae60b27 --- /dev/null +++ b/tests/COVE/002_upgrade_bootloaders_on_debian10.bats @@ -0,0 +1,121 @@ +# +# Unit tests for upgrading Shim and GRUB bootloaders on Debian 10 +# + +function setup_file() { + REAR_SHARE_DIR="$(realpath "$BATS_TEST_DIRNAME/../../usr/share/rear")" + export REAR_SHARE_DIR +} + +function setup() { + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/lib/global-functions.sh" + + function LogPrint() { + echo "$@" + } + + OS_VERSION=10 + USING_UEFI_BOOTLOADER=yes + function is_cove_in_azure() { + false + } + EFI_STUB=no + UEFI_BOOTLOADER=/boot/efi/EFI/debian/shimx64.efi + + function sb_enabled() { + true + } +} + +@test "Shim and GRUB are upgraded successfully" { + function upgrade_bootloaders() { + return 0 + } + + run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" + [ "$status" -eq 0 ] + + local expected_output="Upgraded signed Shim and GRUB bootloaders for this system." + [ "$output" = "$expected_output" ] +} + +@test "Failed to upgrade Shim and GRUB" { + function upgrade_bootloaders() { + return 1 + } + + run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" + [ "$status" -eq 0 ] + + local expected_output="Failed to upgrade signed Shim and GRUB bootloaders for this system. UEFI Secure Boot might not be available." + [ "$output" = "$expected_output" ] +} + +@test "Skip upgrading Shim and GRUB if the system is not Debian 10" { + # shellcheck disable=SC2034 + OS_VERSION=11 + + run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" + [ "$status" -eq 0 ] + + local expected_output="" + [ "$output" = "$expected_output" ] +} + +@test "Skip upgrading Shim and GRUB if the system is not EFI" { + # shellcheck disable=SC2034 + USING_UEFI_BOOTLOADER=no + + run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" + [ "$status" -eq 0 ] + + local expected_output="" + [ "$output" = "$expected_output" ] +} + +@test "Skip upgrading Shim and GRUB on Azure" { + function is_cove_in_azure() { + true + } + + run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" + [ "$status" -eq 0 ] + + local expected_output="" + [ "$output" = "$expected_output" ] +} + +@test "Skip upgrading Shim and GRUB if the system is EFI stub" { + # shellcheck disable=SC2034 + EFI_STUB=yes + + run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" + [ "$status" -eq 0 ] + + local expected_output="" + [ "$output" = "$expected_output" ] +} + +@test "Skip upgrading Shim and GRUB if the bootloader is not shim" { + # shellcheck disable=SC2034 + UEFI_BOOTLOADER=/boot/efi/EFI/debian/grubx64.efi + + run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" + [ "$status" -eq 0 ] + + local expected_output="" + [ "$output" = "$expected_output" ] +} + +@test "Skip upgrading Shim and GRUB if SB disabled" { + function sb_enabled() { + false + } + + run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" + [ "$status" -eq 0 ] + + local expected_output="" + [ "$output" = "$expected_output" ] +} diff --git a/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh b/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh index e15a28e539..db12f8290f 100644 --- a/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh +++ b/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh @@ -26,7 +26,23 @@ if [ "${UEFI_BOOTLOADER##*/}" != "shimx64.efi" ]; then return 0 fi -function upgrade_bootloaders() { +declare -F sb_enabled >/dev/null || function sb_enabled() { + local sb_var=/sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c + if [ ! -f "$sb_var" ]; then + return 1 + fi + + local sb_var_data=0 + sb_var_data=$(dd if=$sb_var bs=1 skip=4 count=1 2>/dev/null | hexdump -e '1/1 "%X"') + + [ "$sb_var_data" -eq 1 ] +} + +if ! sb_enabled; then + return 0 +fi + +declare -F upgrade_bootloaders >/dev/null || function upgrade_bootloaders() { local target_bootloader_dir="${UEFI_BOOTLOADER%/*}" target_bootloader_dir="$TARGET_FS_ROOT$target_bootloader_dir" From 8673edd0744d8186eb9cca1d80db7cd4754f13f8 Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Fri, 5 Dec 2025 02:47:20 -0500 Subject: [PATCH 13/28] feat(bm): Fixed build for rpm package Jira-Ref: BCF-5414: Adapt build scripts for cove --- Makefile | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 8ba64650bd..69a3c9e193 100644 --- a/Makefile +++ b/Makefile @@ -35,18 +35,14 @@ else git_ref := $(shell git rev-parse HEAD | cut -c 1-8) git_count := $(shell git rev-list HEAD --no-merges | wc -l) git_branch_suffix = $(shell git symbolic-ref HEAD | sed -e 's,^.*/,,' -e "s/[^A-Za-z0-9]//g") + cove_suffix = $(git_branch_suffix) release_suffix = $(shell git symbolic-ref HEAD | sed -nE 's/^.*?release\/([0-9.-]*-cove).*/\1/p') ifneq ($(release_suffix),) - release_tag = $(shell git tag --points-at HEAD) - ifneq ($(release_tag),) - release_suffix = $(release_tag) - else - git_branch_suffix = $(release_suffix) - endif + cove_suffix = $(release_suffix) endif feature_suffix = $(shell git symbolic-ref HEAD | sed -nE 's/^.*?(feature|hotfix)\/([A-Za-z0-9_-]+-[0-9]+).*/\2/p') ifneq ($(feature_suffix),) - git_branch_suffix = $(feature_suffix) + cove_suffix = $(feature_suffix) endif git_status := $(shell git status --porcelain) ifneq ($(git_status),) @@ -64,9 +60,13 @@ else git_stamp ?= 0.0.unknown ifneq ($(release_suffix),) - distversion = $(release_suffix)$(changed_suffix) - else # is not release version - distversion = $(version).$(git_stamp)$(changed_suffix) + distversion = $(cove_suffix)$(changed_suffix) + else + ifneq ($(feature_suffix),) + distversion = $(version)-$(git_count).$(git_ref).$(cove_suffix)$(changed_suffix) + else # is not release version + distversion = $(version)-$(git_stamp)$(changed_suffix) + endif endif # is release version debrelease = 0git.$(git_stamp)$(changed_suffix) rpmrelease = .git.$(git_stamp)$(changed_suffix) From 96b3f252f30da12f3e8a7695f492960cac2ad698 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 5 Dec 2025 08:48:54 +0100 Subject: [PATCH 14/28] feat(cove): roll back logic related to SB status Jira-Ref: BCF-6007: [ReaR] Debian 10 fails to boot after recovery with Secure Boot enabled --- .../002_upgrade_bootloaders_on_debian10.bats | 16 ---------------- .../COVE/Debian/620_upgrade_bootloaders.sh | 18 ------------------ 2 files changed, 34 deletions(-) diff --git a/tests/COVE/002_upgrade_bootloaders_on_debian10.bats b/tests/COVE/002_upgrade_bootloaders_on_debian10.bats index c24ae60b27..a100f6fc9a 100755 --- a/tests/COVE/002_upgrade_bootloaders_on_debian10.bats +++ b/tests/COVE/002_upgrade_bootloaders_on_debian10.bats @@ -22,10 +22,6 @@ function setup() { } EFI_STUB=no UEFI_BOOTLOADER=/boot/efi/EFI/debian/shimx64.efi - - function sb_enabled() { - true - } } @test "Shim and GRUB are upgraded successfully" { @@ -107,15 +103,3 @@ function setup() { local expected_output="" [ "$output" = "$expected_output" ] } - -@test "Skip upgrading Shim and GRUB if SB disabled" { - function sb_enabled() { - false - } - - run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" - [ "$status" -eq 0 ] - - local expected_output="" - [ "$output" = "$expected_output" ] -} diff --git a/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh b/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh index db12f8290f..5932519f55 100644 --- a/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh +++ b/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh @@ -38,24 +38,6 @@ declare -F sb_enabled >/dev/null || function sb_enabled() { [ "$sb_var_data" -eq 1 ] } -if ! sb_enabled; then - return 0 -fi - -declare -F upgrade_bootloaders >/dev/null || function upgrade_bootloaders() { - local target_bootloader_dir="${UEFI_BOOTLOADER%/*}" - target_bootloader_dir="$TARGET_FS_ROOT$target_bootloader_dir" - - local shim="$target_bootloader_dir/shimx64.efi" - local grub="$target_bootloader_dir/grubx64.efi" - - cp -b /usr/lib/shim/shimx64.efi.signed "$shim" || return 1 - cp -b /usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed "$grub" || \ - { mv "$shim~" "$shim"; return 1; } - - rm "$shim~" "$grub~" -} - if upgrade_bootloaders; then LogPrint "Upgraded signed Shim and GRUB bootloaders for this system." else From 3012d90147b9922df0deb03714f9b3281bd53feb Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Fri, 5 Dec 2025 04:10:45 -0500 Subject: [PATCH 15/28] feat(bm): Fixed issues after merge with develop Jira-Ref: BCF-5414: Adapt build scripts for cove --- Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index f87c7575d9..c03cffb901 100644 --- a/Makefile +++ b/Makefile @@ -34,13 +34,14 @@ else git_date := $(shell git log -n 1 --format="%ai") git_ref := $(shell git rev-parse HEAD | cut -c 1-8) git_count := $(shell git rev-list HEAD --no-merges | wc -l) - git_branch_suffix = $(shell echo "$${CHANGE_BRANCH:-$${BRANCH_NAME:-$$(git symbolic-ref HEAD || echo unknown)}}" | sed -e 's,^.*/,,' -e "s/[^A-Za-z0-9]//g") + git_branch_name = $(shell echo "$${CHANGE_BRANCH:-$${BRANCH_NAME:-$$(git symbolic-ref HEAD || echo unknown)}}") + git_branch_suffix = $(shell echo "$git_branch_name" | sed -e 's,^.*/,,' -e "s/[^A-Za-z0-9]//g") cove_suffix = $(git_branch_suffix) - release_suffix = $(shell git symbolic-ref HEAD | sed -nE 's/^.*?release\/([0-9.-]*-cove).*/\1/p') + release_suffix = $(shell echo "$git_branch_name" | sed -nE 's/^.*?release\/([0-9.-]*-cove).*/\1/p') ifneq ($(release_suffix),) cove_suffix = $(release_suffix) endif - feature_suffix = $(shell git symbolic-ref HEAD | sed -nE 's/^.*?(feature|hotfix)\/([A-Za-z0-9_-]+-[0-9]+).*/\2/p') + feature_suffix = $(shell echo "$git_branch_name" | sed -nE 's/^.*?(feature|hotfix)\/([A-Za-z0-9_-]+-[0-9]+).*/\2/p') ifneq ($(feature_suffix),) cove_suffix = $(feature_suffix) endif From 47b41ef7afea44407a869f2e2141c070c1f86b7c Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Fri, 5 Dec 2025 04:23:32 -0500 Subject: [PATCH 16/28] feat(bm): Fixed issues after merge with develop Jira-Ref: BCF-5414: Adapt build scripts for cove --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index c03cffb901..795600e063 100644 --- a/Makefile +++ b/Makefile @@ -35,13 +35,13 @@ else git_ref := $(shell git rev-parse HEAD | cut -c 1-8) git_count := $(shell git rev-list HEAD --no-merges | wc -l) git_branch_name = $(shell echo "$${CHANGE_BRANCH:-$${BRANCH_NAME:-$$(git symbolic-ref HEAD || echo unknown)}}") - git_branch_suffix = $(shell echo "$git_branch_name" | sed -e 's,^.*/,,' -e "s/[^A-Za-z0-9]//g") + git_branch_suffix = $(shell echo "$(git_branch_name)" | sed -e 's,^.*/,,' -e "s/[^A-Za-z0-9]//g") cove_suffix = $(git_branch_suffix) - release_suffix = $(shell echo "$git_branch_name" | sed -nE 's/^.*?release\/([0-9.-]*-cove).*/\1/p') + release_suffix = $(shell echo "$(git_branch_name)" | sed -nE 's/^.*?release\/([0-9.-]*-cove).*/\1/p') ifneq ($(release_suffix),) cove_suffix = $(release_suffix) endif - feature_suffix = $(shell echo "$git_branch_name" | sed -nE 's/^.*?(feature|hotfix)\/([A-Za-z0-9_-]+-[0-9]+).*/\2/p') + feature_suffix = $(shell echo "$(git_branch_name)" | sed -nE 's/^.*?(feature|hotfix)\/([A-Za-z0-9_-]+-[0-9]+).*/\2/p') ifneq ($(feature_suffix),) cove_suffix = $(feature_suffix) endif From 343b05f52c723084ad9955d6649ee7a25faf4a47 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 5 Dec 2025 14:15:46 +0100 Subject: [PATCH 17/28] feat(cove): fix roll back Jira-Ref: BCF-6007: [ReaR] Debian 10 fails to boot after recovery with Secure Boot enabledase enter the commit message for your changes. Lines starting --- .../COVE/Debian/620_upgrade_bootloaders.sh | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh b/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh index 5932519f55..5b4815f2c5 100644 --- a/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh +++ b/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh @@ -26,16 +26,18 @@ if [ "${UEFI_BOOTLOADER##*/}" != "shimx64.efi" ]; then return 0 fi -declare -F sb_enabled >/dev/null || function sb_enabled() { - local sb_var=/sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c - if [ ! -f "$sb_var" ]; then - return 1 - fi +declare -F upgrade_bootloaders >/dev/null || function upgrade_bootloaders() { + local target_bootloader_dir="${UEFI_BOOTLOADER%/*}" + target_bootloader_dir="$TARGET_FS_ROOT$target_bootloader_dir" - local sb_var_data=0 - sb_var_data=$(dd if=$sb_var bs=1 skip=4 count=1 2>/dev/null | hexdump -e '1/1 "%X"') + local shim="$target_bootloader_dir/shimx64.efi" + local grub="$target_bootloader_dir/grubx64.efi" - [ "$sb_var_data" -eq 1 ] + cp -b /usr/lib/shim/shimx64.efi.signed "$shim" || return 1 + cp -b /usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed "$grub" || \ + { mv "$shim~" "$shim"; return 1; } + + rm "$shim~" "$grub~" } if upgrade_bootloaders; then From 425a505d3522422b2e174112e48a8b71b97bc12f Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 5 Dec 2025 15:25:56 +0100 Subject: [PATCH 18/28] feat(cove): cover case when grub or shim are missing Jira-Ref: BCF-6007: [ReaR] Debian 10 fails to boot after recovery with Secure Boot enabled --- .../rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh b/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh index 5b4815f2c5..d7f8db0838 100644 --- a/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh +++ b/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh @@ -33,11 +33,17 @@ declare -F upgrade_bootloaders >/dev/null || function upgrade_bootloaders() { local shim="$target_bootloader_dir/shimx64.efi" local grub="$target_bootloader_dir/grubx64.efi" + if [ ! -e "$shim" ] || [ ! -e "$grub" ]; then + return 1 + fi + cp -b /usr/lib/shim/shimx64.efi.signed "$shim" || return 1 cp -b /usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed "$grub" || \ { mv "$shim~" "$shim"; return 1; } rm "$shim~" "$grub~" + + return 0 } if upgrade_bootloaders; then From d748c2038f18b61c754b32f0f023f0d09a0eb078 Mon Sep 17 00:00:00 2001 From: Barys Barysenka Date: Fri, 5 Dec 2025 08:26:38 -0500 Subject: [PATCH 19/28] feat(rear): unit tests * added env validation Jira-Ref: BCF-6186: [ReaR] Run unit tests during CI --- Jenkinsfile | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 5c7c324bb5..7fc08b0684 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -3,7 +3,9 @@ import nable.cove.helpers.ShellHelper import nable.cove.SecretManager final String jobName = env.JOB_NAME.split('/')[-2] +final String branchName = env.CHANGE_BRANCH ?: env.BRANCH_NAME final boolean isProd = jobName.endsWith('-prd') +final boolean isProdBranch = branchName == 'master' || branchName.startsWith('release') final String envType = isProd ? 'prd' : 'dev' def String repositoryName = 'rear' @@ -33,6 +35,7 @@ def secrets = [ def secretManager def shellHelper +def shouldBuild = true pipeline { agent { @@ -51,6 +54,11 @@ pipeline { stage('Prepare') { steps { script { + if (isProd != isProdBranch) { + echo "Environment mismatch: isProd=${isProd}, isProdBranch=${isProdBranch}. Skipping build." + shouldBuild = false + } + secretManager = new SecretManager( this, envType, @@ -66,6 +74,9 @@ pipeline { } } stage('Load secrets') { + when { + expression { shouldBuild } + } agent { kubernetes { cloud "${config.cloud}" @@ -82,6 +93,9 @@ pipeline { } } stage('Build') { + when { + expression { shouldBuild } + } environment { ARTIFACTORY_URL = 'https://mspsolarwinds.jfrog.io/artifactory' } From a678ac96b2febbdec27d0ea1d6c065c316259048 Mon Sep 17 00:00:00 2001 From: Barys Barysenka Date: Mon, 8 Dec 2025 08:39:48 -0500 Subject: [PATCH 20/28] feat(rear): unit tests * job validation Jira-Ref: BCF-6186: [ReaR] Run unit tests during CI --- Jenkinsfile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 7fc08b0684..017fc85797 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -3,9 +3,9 @@ import nable.cove.helpers.ShellHelper import nable.cove.SecretManager final String jobName = env.JOB_NAME.split('/')[-2] -final String branchName = env.CHANGE_BRANCH ?: env.BRANCH_NAME +final String branchName = env.CHANGE_TARGET ?: env.BRANCH_NAME final boolean isProd = jobName.endsWith('-prd') -final boolean isProdBranch = branchName == 'master' || branchName.startsWith('release') +final boolean isProdBranch = branchName == 'master' final String envType = isProd ? 'prd' : 'dev' def String repositoryName = 'rear' @@ -55,8 +55,10 @@ pipeline { steps { script { if (isProd != isProdBranch) { - echo "Environment mismatch: isProd=${isProd}, isProdBranch=${isProdBranch}. Skipping build." + echo "Environment mismatch: target branch is ${branchName}, job is ${jobName}. Skipping build." shouldBuild = false + } else { + echo "Environment match: target branch is ${branchName}, job is ${jobName}. Proceeding with build." } secretManager = new SecretManager( From d4bb4ee43e4719bb5cf0b97c9e9c1dce5b7c5fad Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Mon, 15 Dec 2025 17:01:39 +0100 Subject: [PATCH 21/28] feat(cove): determine EFI bootloader Jira-Ref: BCF-6232: After BMR EFI systems lose "secure boot" compatibility --- .../000_save_future_dangling_efi_entries.bats | 2 + .../COVE/001_remove_dangling_efi_entries.bats | 2 + .../002_upgrade_bootloaders_on_debian10.bats | 2 + tests/COVE/003_determine_efi_bootloader.bats | 257 ++++++++++++++++++ usr/share/rear/lib/uefi-functions.sh | 126 +++++++++ .../default/840_determine_efi_bootloader.sh | 21 ++ 6 files changed, 410 insertions(+) create mode 100755 tests/COVE/003_determine_efi_bootloader.bats create mode 100644 usr/share/rear/rescue/systemstate/default/840_determine_efi_bootloader.sh diff --git a/tests/COVE/000_save_future_dangling_efi_entries.bats b/tests/COVE/000_save_future_dangling_efi_entries.bats index 41a62b5fbd..83fa98841f 100755 --- a/tests/COVE/000_save_future_dangling_efi_entries.bats +++ b/tests/COVE/000_save_future_dangling_efi_entries.bats @@ -1,3 +1,5 @@ +#!/bin/env bats + setup_file() { export REAR_SHARE_DIR="$(realpath "$BATS_TEST_DIRNAME/../../usr/share/rear")" export USING_UEFI_BOOTLOADER="yes" diff --git a/tests/COVE/001_remove_dangling_efi_entries.bats b/tests/COVE/001_remove_dangling_efi_entries.bats index 026a2769d0..f214dd1023 100755 --- a/tests/COVE/001_remove_dangling_efi_entries.bats +++ b/tests/COVE/001_remove_dangling_efi_entries.bats @@ -1,3 +1,5 @@ +#!/bin/env bats + setup_file() { export REAR_SHARE_DIR="$(realpath "$BATS_TEST_DIRNAME/../../usr/share/rear")" export USING_UEFI_BOOTLOADER="yes" diff --git a/tests/COVE/002_upgrade_bootloaders_on_debian10.bats b/tests/COVE/002_upgrade_bootloaders_on_debian10.bats index a100f6fc9a..393589f072 100755 --- a/tests/COVE/002_upgrade_bootloaders_on_debian10.bats +++ b/tests/COVE/002_upgrade_bootloaders_on_debian10.bats @@ -1,3 +1,5 @@ +#!/bin/env bats + # # Unit tests for upgrading Shim and GRUB bootloaders on Debian 10 # diff --git a/tests/COVE/003_determine_efi_bootloader.bats b/tests/COVE/003_determine_efi_bootloader.bats new file mode 100755 index 0000000000..dcddd01b2a --- /dev/null +++ b/tests/COVE/003_determine_efi_bootloader.bats @@ -0,0 +1,257 @@ +#!/bin/env bats + +# +# Unit tests for determining EFI bootloaders +# + +function setup_file() { + REAR_SHARE_DIR="$(realpath "$BATS_TEST_DIRNAME/../../usr/share/rear")" + export REAR_SHARE_DIR +} + +function setup() { + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/lib/global-functions.sh" + + function Log() { + echo "$@" + } + + OS_VERSION=10 + USING_UEFI_BOOTLOADER=yes + function is_cove_in_azure() { + false + } + EFI_STUB=no + UEFI_BOOTLOADER=/boot/efi/EFI/debian/shimx64.efi +} + +function source_efi_functions { + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/lib/uefi-functions.sh" +} + +@test "Get current boot entry" { + function efi_run_efibootmgr { + echo "BootCurrent: 0003" + echo "BootOrder: 0003,0000,0001,0002" + echo "Boot0000* EFI Virtual disk (0.0) PcieRoot(0x8)/Pci(0x0,0x0)/SCSI(0,0)" + echo "Boot0001* EFI VMware Virtual SATA CDROM Drive (0.0) PcieRoot(0x8)/Pci(0x2,0x0)/Sata(0,0,0)" + echo "Boot0002* EFI Network PcieRoot(0x8)/Pci(0x1,0x0)/MAC(00505698f752,1)" + # shellcheck disable=SC2028 + echo "Boot0003* Red Hat Enterprise Linux HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\shimx64.efi" + } + + source_efi_functions + + local current_boot + current_boot=$(efi_get_current_boot) + + [ "$current_boot" = "0003" ] +} + +@test "Get EFI device path" { + function efi_run_efibootmgr { + echo "BootCurrent: 0003" + echo "BootOrder: 0003,0000,0001,0002" + echo "Boot0000* EFI Virtual disk (0.0) PcieRoot(0x8)/Pci(0x0,0x0)/SCSI(0,0)" + echo "Boot0001* EFI VMware Virtual SATA CDROM Drive (0.0) PcieRoot(0x8)/Pci(0x2,0x0)/Sata(0,0,0)" + echo "Boot0002* EFI Network PcieRoot(0x8)/Pci(0x1,0x0)/MAC(00505698f752,1)" + # shellcheck disable=SC2028 + echo "Boot0003* Red Hat Enterprise Linux HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\shimx64.efi" + # shellcheck disable=SC2028 + echo "Boot0004* My GRUB HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\grubx64.efi" + } + + source_efi_functions + + local dp + dp=$(efi_get_device_path "0003") + [ "$dp" = "HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\EFI\redhat\shimx64.efi" ] + + dp=$(efi_get_device_path "0004") + [ "$dp" = "HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\EFI\redhat\grubx64.efi" ] +} + +@test "Failed get EFI device path" { + function efi_run_efibootmgr { + return 1 + } + + source_efi_functions + + run efi_get_device_path "0003" + + [ $status -eq 1 ] + [ -z "$output" ] + + function efi_run_efibootmgr { + echo "Boot0000* EFI Virtual disk (0.0) PcieRoot(0x8)/Pci(0x0,0x0)/SCSI(0,0)" + } + + run efi_get_device_path "0001" + + [ $status -eq 1 ] + [ -z "$output" ] +} + +@test "Get bootloader path" { + local num="0003" + function efi_get_device_path { + if [ "$1" != "$num" ]; then + return 1 + fi + # shellcheck disable=SC2028 + echo "HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\grubx64.efi" + } + + source_efi_functions + + local bootloader_path + bootloader_path=$(efi_get_bootloader_path "$num") + + [ "$bootloader_path" = "/EFI/redhat/grubx64.efi" ] +} + +@test "Failed to get bootloader path" { + function efi_get_device_path { + return 1 + } + + source_efi_functions + + run efi_get_bootloader_path "0003" + + [ $status -eq 1 ] + [ -z "$output" ] + + function efi_get_device_path { + echo "HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)" + } + + [ $status -eq 1 ] + [ -z "$output" ] +} + +@test "Get EFI boot partuuid for GPT" { + local num="0003" + function efi_get_device_path { + if [ "$1" != "$num" ]; then + return 1 + fi + # shellcheck disable=SC2028 + echo "HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\grubx64.efi" + } + + source_efi_functions + + local partuuid + partuuid=$(efi_get_boot_partuuid "$num") + + [ "$partuuid" = "bc6f95e7-893a-434b-8b76-f3d52e6ad28d" ] +} + +@test "Get current full EFI bootloader path" { + function efi_run_efibootmgr { + echo "BootCurrent: 0003" + echo "BootOrder: 0003,0000,0001,0002" + echo "Boot0000* EFI Virtual disk (0.0) PcieRoot(0x8)/Pci(0x0,0x0)/SCSI(0,0)" + echo "Boot0001* EFI VMware Virtual SATA CDROM Drive (0.0) PcieRoot(0x8)/Pci(0x2,0x0)/Sata(0,0,0)" + echo "Boot0002* EFI Network PcieRoot(0x8)/Pci(0x1,0x0)/MAC(00505698f752,1)" + # shellcheck disable=SC2028 + echo "Boot0003* Red Hat Enterprise Linux HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\shimx64.efi" + } + + function efi_get_mountpoint { + if [ "$1" != "bc6f95e7-893a-434b-8b76-f3d52e6ad28d" ]; then + return 1 + fi + + echo "/boot/efi" + } + + source_efi_functions + + local full_path + full_path="$(efi_get_current_full_bootloader_path)" + + [ "$full_path" = "/boot/efi/EFI/redhat/shimx64.efi" ] +} + +@test "Failed to get current full EFI bootloader path: cannot get current boot" { + function efi_get_current_boot { + return 1 + } + + source_efi_functions + + run efi_get_current_full_bootloader_path + + [ $status -eq 1 ] + [ "$output" = "EFI: Failed to get current boot" ] +} + +@test "Failed to get current full EFI bootloader path: cannot get partuuid" { + function efi_get_current_boot { + echo "0000" + } + + function efi_get_boot_partuuid { + return 1 + } + + source_efi_functions + + run efi_get_current_full_bootloader_path + + [ $status -eq 1 ] + [ "$output" = "EFI: Failed to get partuuid for the current boot '0000'" ] +} + +@test "Failed to get current full EFI bootloader path: cannot get mountpoint" { + function efi_get_current_boot { + echo "0000" + } + + local uuid="bc6f95e7-893a-434b-8b76-f3d52e6ad28d" + function efi_get_boot_partuuid { + echo "$uuid" + } + + function efi_get_mountpoint { + return 1 + } + + source_efi_functions + + run efi_get_current_full_bootloader_path + + [ $status -eq 1 ] + [ "$output" = "EFI: Failed to get mountpoint for partuuid '$uuid'" ] +} + +@test "Failed to get current full EFI bootloader path: cannot get bootloader path" { + function efi_get_current_boot { + echo "0000" + } + + local uuid="bc6f95e7-893a-434b-8b76-f3d52e6ad28d" + function efi_get_boot_partuuid { + echo "$uuid" + } + + function efi_get_mountpoint { + echo "/boot/efi" + } + + function efi_get_bootloader_path { + return 1 + } + + source_efi_functions + + run efi_get_current_full_bootloader_path + + [ $status -eq 1 ] + [ "$output" = "Failed to get bootloader path the current boot '0000'" ] +} diff --git a/usr/share/rear/lib/uefi-functions.sh b/usr/share/rear/lib/uefi-functions.sh index ee52fb02de..e32cbeb8de 100644 --- a/usr/share/rear/lib/uefi-functions.sh +++ b/usr/share/rear/lib/uefi-functions.sh @@ -125,3 +125,129 @@ function build_boot_efi { fi } +declare -F efi_run_efibootmgr >/dev/null || function efi_run_efibootmgr() { + if ! has_binary efibootmgr; then + return 1 + fi + + local efibootmgr_output=$TMP_DIR/efibootmgr_v_output + + if [ ! -s "$efibootmgr_output" ]; then + if ! efibootmgr -v 2>/dev/null > "$efibootmgr_output"; then + return 1 + fi + fi + + cat "$efibootmgr_output" +} + +declare -F efi_get_current_boot >/dev/null || function efi_get_current_boot() { + local boot_current + boot_current=$(efi_run_efibootmgr | sed -n 's|^BootCurrent: ||p') || return 1 + echo "$boot_current" +} + +declare -F efi_get_device_path >/dev/null || function efi_get_device_path() { + local bootnum="Boot$1" + + local dp + dp="$(efi_run_efibootmgr | sed -n "s|^${bootnum}\* .*\\t||p ")" || return 1 + + if [ -z "$dp" ]; then + return 1 + fi + + echo "$dp" +} + +declare -F efi_get_bootloader_path >/dev/null || function efi_get_bootloader_path { + local bootnum="$1" + + local dp + dp=$(efi_get_device_path "$bootnum") || return 1 + + local path + path=$(echo "$dp" | sed -n 's|HD(.*)/||; s|\\|/|g; p') || return 1 + + if [ -z "$path" ]; then + return 1 + fi + + echo "$path" +} + +declare -F efi_get_boot_partuuid >/dev/null || function efi_get_boot_partuuid { + local bootnum="$1" + + local dp + dp=$(efi_get_device_path "$bootnum") || return 1 + + local partuuid + partuuid=$(echo "$dp" | sed -n 's/^HD([0-9]\+,GPT,\([xX0-9a-fA-F-]\+\),.*/\1/p' ) || return 1 + + if [ -z "$partuuid" ]; then + return 1 + fi + + echo "$partuuid" +} + +declare -F efi_get_mountpoint || function efi_get_mountpoint { + if ! has_binary findmnt; then + return 1 + fi + + local partuuid=$1 + + local mnt + mnt=$(findmnt -S PARTUUID="$partuuid" -n -o TARGET) || return 2 + + if [ -z "$mnt" ]; then + return 1 + fi + + echo "$mnt" +} + +declare -F efi_get_current_full_bootloader_path >/dev/null || function efi_get_current_full_bootloader_path { + local current_boot + if ! current_boot=$(efi_get_current_boot); then + Log "EFI: Failed to get current boot" + return 1 + fi + + local partuuid + if ! partuuid=$(efi_get_boot_partuuid "$current_boot"); then + Log "EFI: Failed to get partuuid for the current boot '$current_boot'" + return 1 + fi + + local mnt + if ! mnt=$(efi_get_mountpoint "$partuuid"); then + Log "EFI: Failed to get mountpoint for partuuid '$partuuid'" + return 1 + fi + + local path + if ! path=$(efi_get_bootloader_path "$current_boot"); then + Log "Failed to get bootloader path the current boot '$current_boot'" + return 1 + fi + + if [ "${path::1}" != '/' ]; then + path="/$path" + fi + + echo "$mnt$path" +} + +declare -F efi_sb_enabled >/dev/null || function efi_sb_enabled { + if ! has_binary mokutil; then + return 1 + fi + + local sb_state + sb_state=$(mokutil --sb-state 2>&1) || return 1 + + grep -q "SecureBoot enabled" <<<"$sb_state" +} diff --git a/usr/share/rear/rescue/systemstate/default/840_determine_efi_bootloader.sh b/usr/share/rear/rescue/systemstate/default/840_determine_efi_bootloader.sh new file mode 100644 index 0000000000..668f0ba2a7 --- /dev/null +++ b/usr/share/rear/rescue/systemstate/default/840_determine_efi_bootloader.sh @@ -0,0 +1,21 @@ +# +# Determine current EFI bootloader +# + +if ! is_true "$USING_UEFI_BOOTLOADER"; then + return 0 +fi + +if is_true "$EFI_STUB"; then + return 0 +fi + +if [ -f "$UEFI_BOOTLOADER" ] && ! efi_sb_enabled; then + return 0 +fi + +if ! EFI_BOOTLOADER_PATH=$(efi_get_current_full_bootloader_path); then + return 0 +fi + +SECURE_BOOT_BOOTLOADER="$EFI_BOOTLOADER_PATH" From 92947b59985cd1eb707b3a806a09f69ad6173e21 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Wed, 17 Dec 2025 15:34:10 +0100 Subject: [PATCH 22/28] feat(cove): fix review comments from Copilot Jira-Ref: BCF-6232: After BMR EFI systems lose "secure boot" compatibility --- .../000_save_future_dangling_efi_entries.bats | 2 +- tests/COVE/003_determine_efi_bootloader.bats | 18 ++++-------------- usr/share/rear/lib/uefi-functions.sh | 12 ++++++------ 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/tests/COVE/000_save_future_dangling_efi_entries.bats b/tests/COVE/000_save_future_dangling_efi_entries.bats index 83fa98841f..8f4479f472 100755 --- a/tests/COVE/000_save_future_dangling_efi_entries.bats +++ b/tests/COVE/000_save_future_dangling_efi_entries.bats @@ -1,4 +1,4 @@ -#!/bin/env bats +#!/usr/bin/env bats setup_file() { export REAR_SHARE_DIR="$(realpath "$BATS_TEST_DIRNAME/../../usr/share/rear")" diff --git a/tests/COVE/003_determine_efi_bootloader.bats b/tests/COVE/003_determine_efi_bootloader.bats index dcddd01b2a..53e3b87cc7 100755 --- a/tests/COVE/003_determine_efi_bootloader.bats +++ b/tests/COVE/003_determine_efi_bootloader.bats @@ -1,4 +1,4 @@ -#!/bin/env bats +#!/usr/bin/env bats # # Unit tests for determining EFI bootloaders @@ -16,14 +16,6 @@ function setup() { function Log() { echo "$@" } - - OS_VERSION=10 - USING_UEFI_BOOTLOADER=yes - function is_cove_in_azure() { - false - } - EFI_STUB=no - UEFI_BOOTLOADER=/boot/efi/EFI/debian/shimx64.efi } function source_efi_functions { @@ -33,13 +25,11 @@ function source_efi_functions { @test "Get current boot entry" { function efi_run_efibootmgr { - echo "BootCurrent: 0003" + echo "BootCurrent: 0001" echo "BootOrder: 0003,0000,0001,0002" echo "Boot0000* EFI Virtual disk (0.0) PcieRoot(0x8)/Pci(0x0,0x0)/SCSI(0,0)" echo "Boot0001* EFI VMware Virtual SATA CDROM Drive (0.0) PcieRoot(0x8)/Pci(0x2,0x0)/Sata(0,0,0)" echo "Boot0002* EFI Network PcieRoot(0x8)/Pci(0x1,0x0)/MAC(00505698f752,1)" - # shellcheck disable=SC2028 - echo "Boot0003* Red Hat Enterprise Linux HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\shimx64.efi" } source_efi_functions @@ -47,7 +37,7 @@ function source_efi_functions { local current_boot current_boot=$(efi_get_current_boot) - [ "$current_boot" = "0003" ] + [ "$current_boot" = "0001" ] } @test "Get EFI device path" { @@ -253,5 +243,5 @@ function source_efi_functions { run efi_get_current_full_bootloader_path [ $status -eq 1 ] - [ "$output" = "Failed to get bootloader path the current boot '0000'" ] + [ "$output" = "Failed to get bootloader path for the current boot '0000'" ] } diff --git a/usr/share/rear/lib/uefi-functions.sh b/usr/share/rear/lib/uefi-functions.sh index e32cbeb8de..54f5c51d8c 100644 --- a/usr/share/rear/lib/uefi-functions.sh +++ b/usr/share/rear/lib/uefi-functions.sh @@ -130,7 +130,7 @@ declare -F efi_run_efibootmgr >/dev/null || function efi_run_efibootmgr() { return 1 fi - local efibootmgr_output=$TMP_DIR/efibootmgr_v_output + local efibootmgr_output="$TMP_DIR/efibootmgr_v_output" if [ ! -s "$efibootmgr_output" ]; then if ! efibootmgr -v 2>/dev/null > "$efibootmgr_output"; then @@ -151,7 +151,7 @@ declare -F efi_get_device_path >/dev/null || function efi_get_device_path() { local bootnum="Boot$1" local dp - dp="$(efi_run_efibootmgr | sed -n "s|^${bootnum}\* .*\\t||p ")" || return 1 + dp="$(efi_run_efibootmgr | sed -n "s|^${bootnum}\* .*\\t||p")" || return 1 if [ -z "$dp" ]; then return 1 @@ -183,7 +183,7 @@ declare -F efi_get_boot_partuuid >/dev/null || function efi_get_boot_partuuid { dp=$(efi_get_device_path "$bootnum") || return 1 local partuuid - partuuid=$(echo "$dp" | sed -n 's/^HD([0-9]\+,GPT,\([xX0-9a-fA-F-]\+\),.*/\1/p' ) || return 1 + partuuid=$(echo "$dp" | sed -n 's/^HD([0-9]\+,GPT,\([0-9a-fA-F-]\+\),.*/\1/p') || return 1 if [ -z "$partuuid" ]; then return 1 @@ -192,7 +192,7 @@ declare -F efi_get_boot_partuuid >/dev/null || function efi_get_boot_partuuid { echo "$partuuid" } -declare -F efi_get_mountpoint || function efi_get_mountpoint { +declare -F efi_get_mountpoint >/dev/null || function efi_get_mountpoint { if ! has_binary findmnt; then return 1 fi @@ -200,7 +200,7 @@ declare -F efi_get_mountpoint || function efi_get_mountpoint { local partuuid=$1 local mnt - mnt=$(findmnt -S PARTUUID="$partuuid" -n -o TARGET) || return 2 + mnt=$(findmnt -S PARTUUID="$partuuid" -n -o TARGET) || return 1 if [ -z "$mnt" ]; then return 1 @@ -230,7 +230,7 @@ declare -F efi_get_current_full_bootloader_path >/dev/null || function efi_get_c local path if ! path=$(efi_get_bootloader_path "$current_boot"); then - Log "Failed to get bootloader path the current boot '$current_boot'" + Log "Failed to get bootloader path for the current boot '$current_boot'" return 1 fi From de95aae21832d5ddcf9e944b54d891839284513c Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Wed, 17 Dec 2025 15:37:49 +0100 Subject: [PATCH 23/28] feat(cove): fix review comments from Copilot Jira-Ref: BCF-6232: After BMR EFI systems lose "secure boot" compatibility --- tests/COVE/001_remove_dangling_efi_entries.bats | 2 +- tests/COVE/002_upgrade_bootloaders_on_debian10.bats | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/COVE/001_remove_dangling_efi_entries.bats b/tests/COVE/001_remove_dangling_efi_entries.bats index f214dd1023..1e00669441 100755 --- a/tests/COVE/001_remove_dangling_efi_entries.bats +++ b/tests/COVE/001_remove_dangling_efi_entries.bats @@ -1,4 +1,4 @@ -#!/bin/env bats +#!/usr/bin/env bats setup_file() { export REAR_SHARE_DIR="$(realpath "$BATS_TEST_DIRNAME/../../usr/share/rear")" diff --git a/tests/COVE/002_upgrade_bootloaders_on_debian10.bats b/tests/COVE/002_upgrade_bootloaders_on_debian10.bats index 393589f072..7941b6eae9 100755 --- a/tests/COVE/002_upgrade_bootloaders_on_debian10.bats +++ b/tests/COVE/002_upgrade_bootloaders_on_debian10.bats @@ -1,4 +1,4 @@ -#!/bin/env bats +#!/usr/bin/env bats # # Unit tests for upgrading Shim and GRUB bootloaders on Debian 10 From 652dbf3523d542de7974e5b77962273b9b8aa72c Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Thu, 18 Dec 2025 17:15:23 +0100 Subject: [PATCH 24/28] feat(cove): check bootloader path Jira-Ref: BCF-6232: After BMR EFI systems lose "secure boot" compatibility --- tests/COVE/003_determine_efi_bootloader.bats | 44 +++++++++++++++++--- usr/share/rear/lib/uefi-functions.sh | 21 +++++++--- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/tests/COVE/003_determine_efi_bootloader.bats b/tests/COVE/003_determine_efi_bootloader.bats index 53e3b87cc7..8f09700607 100755 --- a/tests/COVE/003_determine_efi_bootloader.bats +++ b/tests/COVE/003_determine_efi_bootloader.bats @@ -13,7 +13,7 @@ function setup() { # shellcheck disable=SC1091 source "$REAR_SHARE_DIR/lib/global-functions.sh" - function Log() { + function LogPrint() { echo "$@" } } @@ -160,6 +160,10 @@ function source_efi_functions { echo "/boot/efi" } + function efi_check_bootloader_path { + return 0 + } + source_efi_functions local full_path @@ -178,7 +182,7 @@ function source_efi_functions { run efi_get_current_full_bootloader_path [ $status -eq 1 ] - [ "$output" = "EFI: Failed to get current boot" ] + [ "$output" = "WARN: EFI: Failed to get current boot" ] } @test "Failed to get current full EFI bootloader path: cannot get partuuid" { @@ -195,7 +199,7 @@ function source_efi_functions { run efi_get_current_full_bootloader_path [ $status -eq 1 ] - [ "$output" = "EFI: Failed to get partuuid for the current boot '0000'" ] + [ "$output" = "WARN: EFI: Failed to get partuuid for the current boot '0000'" ] } @test "Failed to get current full EFI bootloader path: cannot get mountpoint" { @@ -217,7 +221,7 @@ function source_efi_functions { run efi_get_current_full_bootloader_path [ $status -eq 1 ] - [ "$output" = "EFI: Failed to get mountpoint for partuuid '$uuid'" ] + [ "$output" = "WARN: EFI: Failed to get mountpoint for partuuid '$uuid'" ] } @test "Failed to get current full EFI bootloader path: cannot get bootloader path" { @@ -243,5 +247,35 @@ function source_efi_functions { run efi_get_current_full_bootloader_path [ $status -eq 1 ] - [ "$output" = "Failed to get bootloader path for the current boot '0000'" ] + [ "$output" = "WARN: EFI: Failed to get bootloader path for the current boot '0000'" ] +} + +@test "Failed to get current full EFI bootloader path: bootloader path does not exist" { + function efi_get_current_boot { + echo "0000" + } + + local uuid="bc6f95e7-893a-434b-8b76-f3d52e6ad28d" + function efi_get_boot_partuuid { + echo "$uuid" + } + + function efi_get_mountpoint { + echo "/boot/efi" + } + + function efi_get_bootloader_path { + echo "/EFI/redhat/shimx64.efi" + } + + function efi_check_bootloader_path { + return 1 + } + + source_efi_functions + + run efi_get_current_full_bootloader_path + + [ $status -eq 1 ] + [ "$output" = "WARN: EFI: Bootloader path '/boot/efi/EFI/redhat/shimx64.efi' does not exist" ] } diff --git a/usr/share/rear/lib/uefi-functions.sh b/usr/share/rear/lib/uefi-functions.sh index 54f5c51d8c..e94b963eba 100644 --- a/usr/share/rear/lib/uefi-functions.sh +++ b/usr/share/rear/lib/uefi-functions.sh @@ -209,28 +209,32 @@ declare -F efi_get_mountpoint >/dev/null || function efi_get_mountpoint { echo "$mnt" } +declare -F efi_check_bootloader_path >/dev/null || function efi_check_bootloader_path { + [ -f "$1" ] +} + declare -F efi_get_current_full_bootloader_path >/dev/null || function efi_get_current_full_bootloader_path { local current_boot if ! current_boot=$(efi_get_current_boot); then - Log "EFI: Failed to get current boot" + LogPrint "WARN: EFI: Failed to get current boot" return 1 fi local partuuid if ! partuuid=$(efi_get_boot_partuuid "$current_boot"); then - Log "EFI: Failed to get partuuid for the current boot '$current_boot'" + LogPrint "WARN: EFI: Failed to get partuuid for the current boot '$current_boot'" return 1 fi local mnt if ! mnt=$(efi_get_mountpoint "$partuuid"); then - Log "EFI: Failed to get mountpoint for partuuid '$partuuid'" + LogPrint "WARN: EFI: Failed to get mountpoint for partuuid '$partuuid'" return 1 fi local path if ! path=$(efi_get_bootloader_path "$current_boot"); then - Log "Failed to get bootloader path for the current boot '$current_boot'" + LogPrint "WARN: EFI: Failed to get bootloader path for the current boot '$current_boot'" return 1 fi @@ -238,7 +242,14 @@ declare -F efi_get_current_full_bootloader_path >/dev/null || function efi_get_c path="/$path" fi - echo "$mnt$path" + local full_path="$mnt$path" + + if ! efi_check_bootloader_path "$full_path"; then + LogPrint "WARN: EFI: Bootloader path '$full_path' does not exist" + return 1 + fi + + echo "$full_path" } declare -F efi_sb_enabled >/dev/null || function efi_sb_enabled { From 1c9af050fc630c05854b101826e3c8a582ea7e28 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 19 Dec 2025 11:21:58 +0100 Subject: [PATCH 25/28] feat(cove): support File(...) format in bootloader path Jira-Ref: BCF-6232: After BMR EFI systems lose "secure boot" compatibility --- tests/COVE/003_determine_efi_bootloader.bats | 18 ++++++++++++++++++ usr/share/rear/lib/uefi-functions.sh | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/tests/COVE/003_determine_efi_bootloader.bats b/tests/COVE/003_determine_efi_bootloader.bats index 8f09700607..4c23f176eb 100755 --- a/tests/COVE/003_determine_efi_bootloader.bats +++ b/tests/COVE/003_determine_efi_bootloader.bats @@ -103,6 +103,24 @@ function source_efi_functions { [ "$bootloader_path" = "/EFI/redhat/grubx64.efi" ] } +@test "Get bootloader path with File(...)" { + local num="0003" + function efi_get_device_path { + if [ "$1" != "$num" ]; then + return 1 + fi + # shellcheck disable=SC2028 + echo "HD(1,GPT,16f5dcec-b713-4d7b-83a3-824c82d95c99,0x800,0x100000)/File(\EFI\debian\shimx64.efi)" + } + + source_efi_functions + + local bootloader_path + bootloader_path=$(efi_get_bootloader_path "$num") + + [ "$bootloader_path" = "/EFI/debian/shimx64.efi" ] +} + @test "Failed to get bootloader path" { function efi_get_device_path { return 1 diff --git a/usr/share/rear/lib/uefi-functions.sh b/usr/share/rear/lib/uefi-functions.sh index e94b963eba..bebc9e9616 100644 --- a/usr/share/rear/lib/uefi-functions.sh +++ b/usr/share/rear/lib/uefi-functions.sh @@ -169,6 +169,10 @@ declare -F efi_get_bootloader_path >/dev/null || function efi_get_bootloader_pat local path path=$(echo "$dp" | sed -n 's|HD(.*)/||; s|\\|/|g; p') || return 1 + if [[ $path =~ ^File\((.*)\)$ ]]; then + path="${BASH_REMATCH[1]}" + fi + if [ -z "$path" ]; then return 1 fi From 48443f5adee332d266f7af2964f9abfad2cdd9d8 Mon Sep 17 00:00:00 2001 From: Barys Barysenka Date: Thu, 18 Dec 2025 07:12:05 -0500 Subject: [PATCH 26/28] feat(rear): files hash * added files from /usr/bin to check the hash after bmr Jira-Ref: BCF-6278: Investigate corrupted FF restores during BMR --- usr/share/rear/conf/default.conf | 7 +++++++ .../finalize/default/060_compare_files.sh | 11 ++++++++++ .../layout/save/default/600_snapshot_files.sh | 21 +++++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/usr/share/rear/conf/default.conf b/usr/share/rear/conf/default.conf index 945f8e8b38..7ad09d5401 100644 --- a/usr/share/rear/conf/default.conf +++ b/usr/share/rear/conf/default.conf @@ -3344,6 +3344,13 @@ COVE_INSTALL_DIR= # The timestamp value is passed by the Backup Manager via an environment variable. COVE_TIMESTAMP="${COVE_TIMESTAMP:-}" +# Verify binaries hash sum after recovery +# Set to 1 to enable verification, 0 to disable +COVE_VERIFY_BINARIES=0 + +# Locations to verify during recovery when COVE_VERIFY_BINARIES is enabled +COVE_VERIFY_PATHS=(/boot/efi /usr/bin /usr/sbin /usr/lib) + ## # End of BACKUP=COVE default settings. #### diff --git a/usr/share/rear/finalize/default/060_compare_files.sh b/usr/share/rear/finalize/default/060_compare_files.sh index 638c22fa70..412e56e1e0 100644 --- a/usr/share/rear/finalize/default/060_compare_files.sh +++ b/usr/share/rear/finalize/default/060_compare_files.sh @@ -37,3 +37,14 @@ if ! md5sum_stdout="$( chroot $TARGET_FS_ROOT md5sum -c --quiet < $VAR_DIR/layou LogPrintError "Manually check if those changed files cause issues in your recreated system" return 1 fi + +# For COVE backup with binary verification enabled, treat checksum mismatch as fatal error +if is_true "$COVE_VERIFY_BINARIES" ; then + LogPrint "Checking if COVE files are consistent" + if ! md5sum_stdout="$( chroot $TARGET_FS_ROOT md5sum -c --quiet < $VAR_DIR/layout/config/cove-files.md5sum )" ; then + LogPrintError "Restored files in $TARGET_FS_ROOT do not fully match the recreated system" + LogPrintError "$( sed -e "s|^/|$TARGET_FS_ROOT/|" -e 's/^/ /' <<< "$md5sum_stdout" )" + + Error "COVE binary verification failed: checksums do not match" + fi +fi diff --git a/usr/share/rear/layout/save/default/600_snapshot_files.sh b/usr/share/rear/layout/save/default/600_snapshot_files.sh index 3ac6b07ef4..27b9bd2027 100644 --- a/usr/share/rear/layout/save/default/600_snapshot_files.sh +++ b/usr/share/rear/layout/save/default/600_snapshot_files.sh @@ -13,3 +13,24 @@ for obj in "${CHECK_CONFIG_FILES[@]}" ; do fi done md5sum "${config_files[@]}" > $VAR_DIR/layout/config/files.md5sum + +# For COVE backup, additionally verify binaries if enabled +local item +local cove_files=() +if [ "$WORKFLOW" = "mksystemstate" ] && is_true "$COVE_VERIFY_BINARIES" ; then + for item in "${COVE_VERIFY_PATHS[@]}" ; do + if [ -d "$item" ] ; then + while IFS= read -r -d '' file ; do + cove_files+=( "$file" ) + done < <(find "$item" -type f -print0) + elif [ -f "$item" ] ; then + cove_files+=( "$item" ) + fi + done +fi + +cove_md5sum_file="$VAR_DIR/layout/config/cove-files.md5sum" +: > "$cove_md5sum_file" +for filepath in "${cove_files[@]}"; do + md5sum "$filepath" >> "$cove_md5sum_file" +done From 6909e848314e4bedd1c33ed504405ea11bc37bdf Mon Sep 17 00:00:00 2001 From: Barys Barysenka Date: Tue, 13 Jan 2026 10:08:34 +0100 Subject: [PATCH 27/28] feat(rear): compare hash hash * added files from /usr/bin to check the hash after bmr * added new workflow checkintegrity Jira-Ref: BCF-6278: Investigate corrupted FF restores during BMR --- .../COVE/default/001_compare_binaries.sh | 16 ++++++++++++++++ .../rear/finalize/default/060_compare_files.sh | 11 +++++++++++ usr/share/rear/lib/checkintegrity-workflow.sh | 12 ++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 usr/share/rear/checkintegrity/COVE/default/001_compare_binaries.sh create mode 100644 usr/share/rear/lib/checkintegrity-workflow.sh diff --git a/usr/share/rear/checkintegrity/COVE/default/001_compare_binaries.sh b/usr/share/rear/checkintegrity/COVE/default/001_compare_binaries.sh new file mode 100644 index 0000000000..ee38b70224 --- /dev/null +++ b/usr/share/rear/checkintegrity/COVE/default/001_compare_binaries.sh @@ -0,0 +1,16 @@ +# Check md5sum files +LogPrint "Checking if certain restored files are consistent with the recreated system" +local md5sum_stdout +for md5sum_file in files.md5sum cove-files.md5sum ; do + # Skip when there are no checksums for this file: + test -s "$VAR_DIR/layout/config/$md5sum_file" || continue + + DebugPrint "See $VAR_DIR/layout/config/$md5sum_file what files are checked" + if ! md5sum_stdout="$( md5sum -c --quiet < $VAR_DIR/layout/config/$md5sum_file )" ; then + LogPrintError "Restored files do not fully match the recreated system" + LogPrintError "$( sed -e 's/^/ /' <<< "$md5sum_stdout" )" + Error "Binary verification failed: checksums do not match for $md5sum_file" + fi +done + +LogPrint "Binary verification passed: all checksums match successfully" diff --git a/usr/share/rear/finalize/default/060_compare_files.sh b/usr/share/rear/finalize/default/060_compare_files.sh index 412e56e1e0..716690a89a 100644 --- a/usr/share/rear/finalize/default/060_compare_files.sh +++ b/usr/share/rear/finalize/default/060_compare_files.sh @@ -1,3 +1,14 @@ +# Copy md5sum files to COVE_INSTALL_DIR if they exist +if is_true "$COVE_VERIFY_BINARIES" ; then + local rear_dir="$VAR_DIR/layout/config" + local cove_dir="$COVE_INSTALL_DIR/rear/var/lib/rear/layout/config" + for md5sum_file in files.md5sum cove-files.md5sum ; do + if test -f "$rear_dir/$md5sum_file" ; then + mkdir -p "$cove_dir" + cp "$rear_dir/$md5sum_file" "$cove_dir/$md5sum_file" + fi + done +fi # Skip when there are no checksums: test -s $VAR_DIR/layout/config/files.md5sum || return 0 diff --git a/usr/share/rear/lib/checkintegrity-workflow.sh b/usr/share/rear/lib/checkintegrity-workflow.sh new file mode 100644 index 0000000000..8c105a0e70 --- /dev/null +++ b/usr/share/rear/lib/checkintegrity-workflow.sh @@ -0,0 +1,12 @@ +# checkintegrity-workflow.sh +# +# checkintegrity workflow for Relax-and-Recover +# +# This file is part of Relax-and-Recover, licensed under the GNU General +# Public License. Refer to the included COPYING for full text of license. + +WORKFLOW_checkintegrity_DESCRIPTION="check files consistency" +WORKFLOWS+=( checkintegrity ) +WORKFLOW_checkintegrity () { + SourceStage "checkintegrity" +} From 6f52993091033f007fad64b608d3d269354f9ade Mon Sep 17 00:00:00 2001 From: Barys Barysenka Date: Mon, 19 Jan 2026 13:12:51 +0100 Subject: [PATCH 28/28] feat(rear): compare hash hash * added files from /usr/bin to check the hash after bmr * added new workflow checkintegrity Jira-Ref: BCF-6278: Investigate corrupted FF restores during BMR --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 795600e063..5b7bb5a30d 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ # cp -a etc/rear/{mappings,templates} ... # assumes bash. So its better to set SHELL to bash explicitly, using the one from PATH # to also work on MacOS with Homebrew-installed bash +# test SHELL = bash # disable parallel execution of make