From f92ddd86c543d115aab78bf5b3fe7e6414136a52 Mon Sep 17 00:00:00 2001 From: DerLinkman Date: Wed, 29 Jan 2025 09:49:04 +0100 Subject: [PATCH 1/5] clamd: update to 1.4.2 + build from source instead using alpine packages (#6273) * clamd: update to 1.4.2 + build from source instead using alpine packages * clamd: remove exposed ports from buildfile * clamd: cleanup dockerfile --- data/Dockerfiles/clamd/Dockerfile | 97 +++++++++++++++++++++++++++++-- data/Dockerfiles/clamd/clamd.sh | 1 + docker-compose.yml | 2 +- 3 files changed, 93 insertions(+), 7 deletions(-) diff --git a/data/Dockerfiles/clamd/Dockerfile b/data/Dockerfiles/clamd/Dockerfile index 1850d4bed..e60e7eef1 100644 --- a/data/Dockerfiles/clamd/Dockerfile +++ b/data/Dockerfiles/clamd/Dockerfile @@ -1,14 +1,99 @@ -FROM alpine:3.20 +FROM alpine:3.21 AS builder + +WORKDIR /src +ENV CLAMD_VERSION=1.4.2 + +RUN apk upgrade --no-cache \ + && apk add --update --no-cache \ + g++ \ + gcc \ + gdb \ + make \ + cmake \ + py3-pytest \ + python3 \ + valgrind \ + bzip2-dev \ + check-dev \ + curl-dev \ + json-c-dev \ + libmilter-dev \ + libxml2-dev \ + linux-headers \ + ncurses-dev \ + openssl-dev \ + pcre2-dev \ + zlib-dev \ + cargo \ + rust + +RUN wget -P /src https://www.clamav.net/downloads/production/clamav-${CLAMD_VERSION}.tar.gz \ + && tar xzfv /src/clamav-${CLAMD_VERSION}.tar.gz \ + && cd /src/clamav-${CLAMD_VERSION} \ + && cmake . \ + -D CMAKE_BUILD_TYPE="Release" \ + -D CMAKE_INSTALL_PREFIX="/usr" \ + -D CMAKE_INSTALL_LIBDIR="/usr/lib" \ + -D APP_CONFIG_DIRECTORY="/etc/clamav" \ + -D DATABASE_DIRECTORY="/var/lib/clamav" \ + -D ENABLE_CLAMONACC=OFF \ + -D ENABLE_EXAMPLES=OFF \ + -D ENABLE_MILTER=ON \ + -D ENABLE_MAN_PAGES=OFF \ + -D ENABLE_STATIC_LIB=OFF \ + -D ENABLE_JSON_SHARED=ON \ + && cmake --build . \ + && make DESTDIR="/clamav" -j$(($(nproc) - 1)) install \ + && rm -r "/clamav/usr/lib/pkgconfig/" \ + && sed -e "s|^\(Example\)|\# \1|" \ + -e "s|.*\(LocalSocket\) .*|\1 /tmp/clamd.sock|" \ + -e "s|.*\(TCPSocket\) .*|\1 3310|" \ + -e "s|.*\(TCPAddr\) .*|#\1 0.0.0.0|" \ + -e "s|.*\(User\) .*|\1 clamav|" \ + -e "s|^\#\(LogFile\) .*|\1 /var/log/clamav/clamd.log|" \ + -e "s|^\#\(LogTime\).*|\1 yes|" \ + "/clamav/etc/clamav/clamd.conf.sample" > "/clamav/etc/clamav/clamd.conf" \ + && sed -e "s|^\(Example\)|\# \1|" \ + -e "s|.*\(DatabaseOwner\) .*|\1 clamav|" \ + -e "s|^\#\(UpdateLogFile\) .*|\1 /var/log/clamav/freshclam.log|" \ + -e "s|^\#\(NotifyClamd\).*|\1 /etc/clamav/clamd.conf|" \ + -e "s|^\#\(ScriptedUpdates\).*|\1 yes|" \ + "/clamav/etc/clamav/freshclam.conf.sample" > "/clamav/etc/clamav/freshclam.conf" \ + && sed -e "s|^\(Example\)|\# \1|" \ + -e "s|.*\(MilterSocket\) .*|\1 inet:7357|" \ + -e "s|.*\(User\) .*|\1 clamav|" \ + -e "s|^\#\(LogFile\) .*|\1 /var/log/clamav/milter.log|" \ + -e "s|^\#\(LogTime\).*|\1 yes|" \ + -e "s|.*\(\ClamdSocket\) .*|\1 unix:/tmp/clamd.sock|" \ + "/clamav/etc/clamav/clamav-milter.conf.sample" > "/clamav/etc/clamav/clamav-milter.conf" || exit 1 + + +FROM alpine:3.21 LABEL maintainer = "The Infrastructure Company GmbH " RUN apk upgrade --no-cache \ && apk add --update --no-cache \ - rsync \ - clamav \ - bind-tools \ - bash \ - tini + tzdata \ + rsync \ + bind-tools \ + bash \ + tini \ + json-c \ + libbz2 \ + libcurl \ + libmilter \ + libxml2 \ + ncurses-libs \ + pcre2 \ + zlib \ + libgcc \ + && addgroup -S "clamav" && \ + adduser -D -G "clamav" -h "/var/lib/clamav" -s "/bin/false" -S "clamav" && \ + install -d -m 755 -g "clamav" -o "clamav" "/var/log/clamav" && \ + chown -R clamav:clamav /var/lib/clamav + +COPY --from=builder "/clamav" "/" # init COPY clamd.sh /clamd.sh diff --git a/data/Dockerfiles/clamd/clamd.sh b/data/Dockerfiles/clamd/clamd.sh index 10df8072b..2c6e75dc6 100755 --- a/data/Dockerfiles/clamd/clamd.sh +++ b/data/Dockerfiles/clamd/clamd.sh @@ -91,6 +91,7 @@ done ) & BACKGROUND_TASKS+=($!) +echo "$(clamd -V) is starting... please wait a moment." nice -n10 clamd & BACKGROUND_TASKS+=($!) diff --git a/docker-compose.yml b/docker-compose.yml index cd85304b4..706a02309 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -64,7 +64,7 @@ services: - redis clamd-mailcow: - image: mailcow/clamd:1.66 + image: mailcow/clamd:1.70 restart: always depends_on: unbound-mailcow: From 244d4b8c4c3973c4c5a88b269aff1a230c160468 Mon Sep 17 00:00:00 2001 From: DerLinkman Date: Wed, 29 Jan 2025 13:46:53 +0100 Subject: [PATCH 2/5] compose: rollback clamd version until next major... accidentally pushed --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 706a02309..cd85304b4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -64,7 +64,7 @@ services: - redis clamd-mailcow: - image: mailcow/clamd:1.70 + image: mailcow/clamd:1.66 restart: always depends_on: unbound-mailcow: From 4db1569c93ce41467f32a34007b081c179dc9645 Mon Sep 17 00:00:00 2001 From: DerLinkman Date: Wed, 10 Sep 2025 16:22:19 +0200 Subject: [PATCH 3/5] Squashed commit of the following: commit 94c1a6c4e1f8901e38e4562dd208cc026277fab8 Author: DerLinkman Date: Wed Sep 10 16:20:58 2025 +0200 scripts: ipv6_controller improvement + fix modules handling (#6722) * Fix subscript handling for modules * ipv6: detect case when link local is present * v6-controller: removed fixed-cidr for docker 28+ --- _modules/scripts/ipv6_controller.sh | 148 ++++++++++++++++++++-------- generate_config.sh | 21 ++++ helper-scripts/_cold-standby.sh | 2 +- update.sh | 16 ++- 4 files changed, 145 insertions(+), 42 deletions(-) diff --git a/_modules/scripts/ipv6_controller.sh b/_modules/scripts/ipv6_controller.sh index de5272048..1ff5eb198 100644 --- a/_modules/scripts/ipv6_controller.sh +++ b/_modules/scripts/ipv6_controller.sh @@ -5,14 +5,65 @@ # 1) Check if the host supports IPv6 get_ipv6_support() { - if grep -qs '^1' /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null \ - || ! ip -6 route show default &>/dev/null; then + # ---- helper: probe external IPv6 connectivity without DNS ---- + _probe_ipv6_connectivity() { + # Use literal, always-on IPv6 echo responders (no DNS required) + local PROBE_IPS=("2001:4860:4860::8888" "2606:4700:4700::1111") + local ip rc=1 + + for ip in "${PROBE_IPS[@]}"; do + if command -v ping6 &>/dev/null; then + ping6 -c1 -W2 "$ip" &>/dev/null || ping6 -c1 -w2 "$ip" &>/dev/null + rc=$? + elif command -v ping &>/dev/null; then + ping -6 -c1 -W2 "$ip" &>/dev/null || ping -6 -c1 -w2 "$ip" &>/dev/null + rc=$? + else + rc=1 + fi + [[ $rc -eq 0 ]] && return 0 + done + return 1 + } + + if [[ ! -f /proc/net/if_inet6 ]] || grep -qs '^1' /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null; then DETECTED_IPV6=false - echo -e "${YELLOW}IPv6 not detected on host – ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}" - else - DETECTED_IPV6=true - echo -e "IPv6 detected on host – ${LIGHT_GREEN}leaving IPv6 support enabled${YELLOW}.${NC}" + echo -e "${YELLOW}IPv6 not detected on host – ${LIGHT_RED}IPv6 is administratively disabled${YELLOW}.${NC}" + return fi + + if ip -6 route show default 2>/dev/null | grep -qE '^default'; then + echo -e "${YELLOW}Default IPv6 route found – testing external IPv6 connectivity...${NC}" + if _probe_ipv6_connectivity; then + DETECTED_IPV6=true + echo -e "IPv6 detected on host – ${LIGHT_GREEN}leaving IPv6 support enabled${YELLOW}.${NC}" + else + DETECTED_IPV6=false + echo -e "${YELLOW}Default IPv6 route present but external IPv6 connectivity failed – ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}" + fi + return + fi + + if ip -6 addr show scope global 2>/dev/null | grep -q 'inet6'; then + DETECTED_IPV6=false + echo -e "${YELLOW}Global IPv6 address present but no default route – ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}" + return + fi + + if ip -6 addr show scope link 2>/dev/null | grep -q 'inet6'; then + echo -e "${YELLOW}Only link-local IPv6 addresses found – testing external IPv6 connectivity...${NC}" + if _probe_ipv6_connectivity; then + DETECTED_IPV6=true + echo -e "External IPv6 connectivity available – ${LIGHT_GREEN}leaving IPv6 support enabled${YELLOW}.${NC}" + else + DETECTED_IPV6=false + echo -e "${YELLOW}Only link-local IPv6 present and no external connectivity – ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}" + fi + return + fi + + DETECTED_IPV6=false + echo -e "${YELLOW}IPv6 not detected on host – ${LIGHT_RED}disabling IPv6 support${YELLOW}.${NC}" } # 2) Ensure Docker daemon.json has (or create) the required IPv6 settings @@ -21,7 +72,7 @@ docker_daemon_edit(){ DOCKER_MAJOR=$(docker version --format '{{.Server.Version}}' 2>/dev/null | cut -d. -f1) MISSING=() - _has_kv() { grep -Eq "\"$1\"\s*:\s*$2" "$DOCKER_DAEMON_CONFIG" 2>/dev/null; } + _has_kv() { grep -Eq "\"$1\"[[:space:]]*:[[:space:]]*$2" "$DOCKER_DAEMON_CONFIG" 2>/dev/null; } if [[ -f "$DOCKER_DAEMON_CONFIG" ]]; then @@ -38,12 +89,18 @@ docker_daemon_edit(){ fi # Gather missing keys - ! _has_kv ipv6 true && MISSING+=("ipv6: true") - ! grep -Eq '"fixed-cidr-v6"\s*:\s*".+"' "$DOCKER_DAEMON_CONFIG" \ - && MISSING+=('fixed-cidr-v6: "fd00:dead:beef:c0::/80"') - if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -le 27 ]]; then + ! _has_kv ipv6 true && MISSING+=("ipv6: true") + + # For Docker < 28, keep requiring fixed-cidr-v6 (default bridge needs it on old engines) + if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 28 ]]; then + ! grep -Eq '"fixed-cidr-v6"[[:space:]]*:[[:space:]]*".+"' "$DOCKER_DAEMON_CONFIG" \ + && MISSING+=('fixed-cidr-v6: "fd00:dead:beef:c0::/80"') + fi + + # For Docker < 27, ip6tables needed and was tied to experimental in older releases + if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 27 ]]; then _has_kv ipv6 true && ! _has_kv ip6tables true && MISSING+=("ip6tables: true") - ! _has_kv experimental true && MISSING+=("experimental: true") + ! _has_kv experimental true && MISSING+=("experimental: true") fi # Fix if needed @@ -60,9 +117,19 @@ docker_daemon_edit(){ cp "$DOCKER_DAEMON_CONFIG" "${DOCKER_DAEMON_CONFIG}.bak" if command -v jq &>/dev/null; then TMP=$(mktemp) - JQ_FILTER='.ipv6 = true | .["fixed-cidr-v6"] = "fd00:dead:beef:c0::/80"' - [[ "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 27 ]] \ - && JQ_FILTER+=' | .ip6tables = true | .experimental = true' + # Base filter: ensure ipv6 = true + JQ_FILTER='.ipv6 = true' + + # Add fixed-cidr-v6 only for Docker < 28 + if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 28 ]]; then + JQ_FILTER+=' | .["fixed-cidr-v6"] = (.["fixed-cidr-v6"] // "fd00:dead:beef:c0::/80")' + fi + + # Add ip6tables/experimental only for Docker < 27 + if [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 27 ]]; then + JQ_FILTER+=' | .ip6tables = true | .experimental = true' + fi + jq "$JQ_FILTER" "$DOCKER_DAEMON_CONFIG" >"$TMP" && mv "$TMP" "$DOCKER_DAEMON_CONFIG" echo -e "${LIGHT_GREEN}daemon.json updated. Restarting Docker...${NC}" (command -v systemctl &>/dev/null && systemctl restart docker) || service docker restart @@ -97,12 +164,19 @@ docker_daemon_edit(){ "experimental": true } EOF - else + elif [[ -n "$DOCKER_MAJOR" && "$DOCKER_MAJOR" -lt 28 ]]; then cat > "$DOCKER_DAEMON_CONFIG" < "$DOCKER_DAEMON_CONFIG" <> "$MAILCOW_CONF" + fi else - return + export IPV6_BOOL=false fi - fi - - # no manual override: proceed to set or export - if [[ "$DETECTED_IPV6" == "true" ]]; then - docker_daemon_edit - else echo "Skipping Docker IPv6 configuration because host does not support IPv6." + echo "Make sure to check if your docker daemon.json does not include \"enable_ipv6\": true if you do not want IPv6." + echo "IPv6 configuration complete: ENABLE_IPV6=false" + sleep 2 + return fi - # now write into mailcow.conf or export + docker_daemon_edit + if [[ -n "$MAILCOW_CONF" && -f "$MAILCOW_CONF" ]]; then - LINE="ENABLE_IPV6=$DETECTED_IPV6" if grep -q '^ENABLE_IPV6=' "$MAILCOW_CONF"; then - sed -i "s/^ENABLE_IPV6=.*/$LINE/" "$MAILCOW_CONF" + sed -i 's/^ENABLE_IPV6=.*/ENABLE_IPV6=true/' "$MAILCOW_CONF" else - echo "$LINE" >> "$MAILCOW_CONF" + echo "ENABLE_IPV6=true" >> "$MAILCOW_CONF" fi else - export IPV6_BOOL="$DETECTED_IPV6" + export IPV6_BOOL=true fi - echo "IPv6 configuration complete: ENABLE_IPV6=$DETECTED_IPV6" + echo "IPv6 configuration complete: ENABLE_IPV6=true" } \ No newline at end of file diff --git a/generate_config.sh b/generate_config.sh index 9610bf18d..2dba91d51 100755 --- a/generate_config.sh +++ b/generate_config.sh @@ -1,5 +1,26 @@ #!/usr/bin/env bash +# Ensure the script is run from the directory that contains a link of .env +# Resolve the directory this script lives in for consistent behavior when invoked from elsewhere +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" >/dev/null 2>&1 && pwd)" + +# Ensure script is executed in the mailcow installation directory by checking for a .env symlink that points to mailcow.conf +if [ ! -L "${PWD}/.env" ]; then + echo -e "\e[33mPlease run this script from the mailcow installation directory.\e[0m" + echo -e " \e[36mcd /path/to/mailcow && ./generate_config.sh\e[0m" + exit 1 +fi + +# Verify the .env symlink points to a mailcow.conf file +env_target="$(readlink -f "${PWD}/.env" 2>/dev/null || true)" +if [ -z "$env_target" ] || [ "$(basename "$env_target")" != "mailcow.conf" ]; then + echo -e "\e[31mThe found .env symlink does not point to a mailcow.conf file.\e[0m" + echo -e "\e[33mPlease create a symbolic link .env -> mailcow.conf inside the mailcow directory and run this script there.\e[0m" + echo -e "\e[33mNote: 'ln -s mailcow.conf .env' will create the symlink even if mailcow.conf does not yet exist.\e[0m" + echo -e " \e[36mcd /path/to/mailcow && ln -s mailcow.conf .env && ./generate_config.sh\e[0m" + exit 1 +fi + # Load mailcow Generic Scripts source _modules/scripts/core.sh source _modules/scripts/ipv6_controller.sh diff --git a/helper-scripts/_cold-standby.sh b/helper-scripts/_cold-standby.sh index bfda3ba94..f02436a95 100755 --- a/helper-scripts/_cold-standby.sh +++ b/helper-scripts/_cold-standby.sh @@ -293,7 +293,7 @@ if ! ssh -o StrictHostKeyChecking=no \ -i "${REMOTE_SSH_KEY}" \ ${REMOTE_SSH_HOST} \ -p ${REMOTE_SSH_PORT} \ - ${SCRIPT_DIR}/../update.sh -f --gc ; then + "cd \"${SCRIPT_DIR}/../\" && ./update.sh -f --gc" ; then >&2 echo -e "\e[31m[ERR]\e[0m - Could not cleanup old images on remote" fi diff --git a/update.sh b/update.sh index 89dec3e66..4ec0e7e40 100755 --- a/update.sh +++ b/update.sh @@ -3,6 +3,20 @@ ############## Begin Function Section ############## SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +MAILCOW_CONF="${SCRIPT_DIR}/mailcow.conf" + +# Ensure the script is run from the directory that contains mailcow.conf +if [ ! -f "${PWD}/mailcow.conf" ]; then + if [ -f "${SCRIPT_DIR}/mailcow.conf" ]; then + echo -e "\e[33mPlease run this script directly from the mailcow installation directory:\e[0m" + echo -e " \e[36mcd ${SCRIPT_DIR} && ./update.sh\e[0m" + exit 1 + else + echo -e "\e[31mmailcow.conf not found in current directory or script directory (\e[36m${SCRIPT_DIR}\e[31m).\e[0m" + echo -e "\e[33mRun this script directly from your mailcow installation directory.\e[0m" + exit 1 + fi +fi BRANCH="$(cd "${SCRIPT_DIR}" && git rev-parse --abbrev-ref HEAD)" MODULE_DIR="${SCRIPT_DIR}/_modules" @@ -27,8 +41,6 @@ if [ "$(id -u)" -ne "0" ]; then exit 1 fi -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - # Run pre-update-hook if [ -f "${SCRIPT_DIR}/pre_update_hook.sh" ]; then bash "${SCRIPT_DIR}/pre_update_hook.sh" From 9a2887cf46074d1cb9ad17be5a3bbcdf662fd1cf Mon Sep 17 00:00:00 2001 From: DerLinkman Date: Wed, 3 Dec 2025 16:26:22 +0100 Subject: [PATCH 4/5] core: improved docker compose version check --- _modules/scripts/core.sh | 74 ++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/_modules/scripts/core.sh b/_modules/scripts/core.sh index f776c6212..576967a48 100644 --- a/_modules/scripts/core.sh +++ b/_modules/scripts/core.sh @@ -38,45 +38,45 @@ get_docker_version(){ } get_compose_type(){ - if docker compose > /dev/null 2>&1; then - if docker compose version --short | grep -e "^2." -e "^v2." > /dev/null 2>&1; then - COMPOSE_VERSION=native - COMPOSE_COMMAND="docker compose" - if [[ "$caller" == "update.sh" ]]; then - sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=native/' "$SCRIPT_DIR/mailcow.conf" - fi - echo -e "\e[33mFound Docker Compose Plugin (native).\e[0m" - echo -e "\e[33mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m" - sleep 2 - echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m" - else - echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" - echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m" - exit 1 - fi - elif docker-compose > /dev/null 2>&1; then - if ! [[ $(alias docker-compose 2> /dev/null) ]] ; then - if docker-compose version --short | grep "^2." > /dev/null 2>&1; then - COMPOSE_VERSION=standalone - COMPOSE_COMMAND="docker-compose" - if [[ "$caller" == "update.sh" ]]; then - sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=standalone/' "$SCRIPT_DIR/mailcow.conf" - fi - echo -e "\e[33mFound Docker Compose Standalone.\e[0m" - echo -e "\e[33mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m" - sleep 2 - echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m" - else - echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" - echo -e "\e[31mPlease update/install manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m" - exit 1 - fi - fi + if docker compose > /dev/null 2>&1; then + if docker compose version --short | grep -e "^[2-9]\." -e "^v[2-9]\." -e "^[1-9][0-9]\." -e "^v[1-9][0-9]\." > /dev/null 2>&1; then + COMPOSE_VERSION=native + COMPOSE_COMMAND="docker compose" + if [[ "$caller" == "update.sh" ]]; then + sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=native/' "$SCRIPT_DIR/mailcow.conf" + fi + echo -e "\e[33mFound Docker Compose Plugin (native).\e[0m" + echo -e "\e[33mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m" + sleep 2 + echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m" else - echo -e "\e[31mCannot find Docker Compose.\e[0m" - echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/install/\e[0m" - exit 1 + echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" + echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m" + exit 1 fi + elif docker-compose > /dev/null 2>&1; then + if ! [[ $(alias docker-compose 2> /dev/null) ]] ; then + if docker-compose version --short | grep -e "^[2-9]\." -e "^[1-9][0-9]\." > /dev/null 2>&1; then + COMPOSE_VERSION=standalone + COMPOSE_COMMAND="docker-compose" + if [[ "$caller" == "update.sh" ]]; then + sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=standalone/' "$SCRIPT_DIR/mailcow.conf" + fi + echo -e "\e[33mFound Docker Compose Standalone.\e[0m" + echo -e "\e[33mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m" + sleep 2 + echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m" + else + echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" + echo -e "\e[31mPlease update/install manually regarding to this doc site: https://docs.mailcow.email/install/\e[0m" + exit 1 + fi + fi + else + echo -e "\e[31mCannot find Docker Compose.\e[0m" + echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/install/\e[0m" + exit 1 + fi } detect_bad_asn() { From 930ca76ea768f6205c5041905afbc9e226cb3105 Mon Sep 17 00:00:00 2001 From: DerLinkman Date: Wed, 3 Dec 2025 16:54:26 +0100 Subject: [PATCH 5/5] update: moved _modules initialization and update at the beginning of update script --- update.sh | 47 +++++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/update.sh b/update.sh index 4ec0e7e40..74eb8ff2c 100755 --- a/update.sh +++ b/update.sh @@ -20,11 +20,28 @@ fi BRANCH="$(cd "${SCRIPT_DIR}" && git rev-parse --abbrev-ref HEAD)" MODULE_DIR="${SCRIPT_DIR}/_modules" +# Calculate hash before fetch +if [[ -d "${MODULE_DIR}" && -n "$(ls -A "${MODULE_DIR}" 2>/dev/null)" ]]; then + MODULES_HASH_BEFORE=$(find "${MODULE_DIR}" -type f -exec sha256sum {} \; 2>/dev/null | sort | sha256sum | awk '{print $1}') +else + MODULES_HASH_BEFORE="EMPTY" +fi + +echo -e "\e[33mFetching latest _modules from origin/${BRANCH}…\e[0m" +git fetch origin "${BRANCH}" +git checkout "origin/${BRANCH}" -- _modules + if [[ ! -d "${MODULE_DIR}" || -z "$(ls -A "${MODULE_DIR}")" ]]; then - echo -e "\e[33m_modules is missing or empty – fetching all Modules from origin/${BRANCH}…\e[0m" - git fetch origin "${BRANCH}" - git checkout "origin/${BRANCH}" -- _modules - echo -e "\e[33mDone. Please restart the script...\e[0m" + echo -e "\e[31mError: _modules is still missing or empty after fetch!\e[0m" + exit 2 +fi + +# Calculate hash after fetch +MODULES_HASH_AFTER=$(find "${MODULE_DIR}" -type f -exec sha256sum {} \; 2>/dev/null | sort | sha256sum | awk '{print $1}') + +# Check if modules changed +if [[ "${MODULES_HASH_BEFORE}" != "${MODULES_HASH_AFTER}" ]]; then + echo -e "\e[33m_modules have been updated. Please restart the update script.\e[0m" exit 2 fi @@ -320,28 +337,6 @@ if [ ! "$DEV" ]; then chmod +x update.sh EXIT_COUNT+=1 fi - - MODULE_DIR="$(dirname "$0")/_modules" - echo -e "\e[32mChecking for updates in _modules...\e[0m" - if [ ! -d "${MODULE_DIR}" ] || [ -z "$(ls -A "${MODULE_DIR}")" ]; then - echo -e "\e[33m_modules missing or empty — fetching from origin...\e[0m" - git checkout "origin/${BRANCH}" -- _modules - else - OLD_SUM="$(find "${MODULE_DIR}" -type f -exec sha1sum {} \; | sort | sha1sum)" - git fetch origin - git checkout "origin/${BRANCH}" -- _modules - NEW_SUM="$(find "${MODULE_DIR}" -type f -exec sha1sum {} \; | sort | sha1sum)" - - if [[ "${OLD_SUM}" != "${NEW_SUM}" ]]; then - EXIT_COUNT+=1 - fi - fi - - if [ ${EXIT_COUNT} -ge 1 ]; then - echo "Changes for the update Script, please run this script again, exiting!" - exit 2 - fi - fi if [ ! "$FORCE" ]; then