#!/bin/bash PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH CONTAINER_PROJECT_NAME=billionmail PGSQL_CONTAINER_NAME="${CONTAINER_PROJECT_NAME}-pgsql-billionmail-1" DOVECOT_CONTAINER_NAME="${CONTAINER_PROJECT_NAME}-dovecot-billionmail-1" POSTFIX_CONTAINER_NAME="${CONTAINER_PROJECT_NAME}-postfix-billionmail-1" RSPAMD_CONTAINER_NAME="${CONTAINER_PROJECT_NAME}-rspamd-billionmail-1" TIME=$(date +%Y_%m_%d_%H_%M_%S) PWD_d=`pwd` if [ $(whoami) != "root" ];then echo -e "Non-root install, please try the following solutions: \n 1.Please switch to root user install \n 2.Try executing the following install commands: \n sudo bash $0 $@" exit 1 fi if [ ! -d ".git" ]; then ls -al echo "The .git does not exist, Cannot continue operation, Please operate in the project directory." exit 1 fi if [ ! -s ".env" ]; then ls -al echo " The .env file does not exist. Please check whether BillionMail is installed? Please operate in the project directory." exit 1 fi . .env if [ -z "${DBUSER}" ] || [ -z "${DBNAME}" ] || [ -z "${DBPASS}" ]; then echo "The obtained .env configuration exception is empty." exit 1 fi while [ ${#} -gt 0 ]; do case $1 in -h|--help) echo "Usage: [options]" echo "Options:" echo " --ours Use merge strategy option "ours" to resolve conflicts in favor of non-BillionMail code (local changes override remote changes)." exit 0 ;; --ours) GIT_MERGE_STRATEGY=ours shift 1 ;; esac shift 1 done Command_Exists() { command -v "$@" >/dev/null 2>&1 } GetSysInfo(){ if [ -s "/etc/redhat-release" ];then SYS_VERSION=$(cat /etc/redhat-release) elif [ -s "/etc/issue" ]; then SYS_VERSION=$(cat /etc/issue) fi SYS_INFO=$(uname -a) SYS_BIT=$(getconf LONG_BIT) MEM_TOTAL=$(free -m|grep Mem|awk '{print $2}') CPU_INFO=$(getconf _NPROCESSORS_ONLN) echo -e ${SYS_VERSION} echo -e Bit:${SYS_BIT} Mem:${MEM_TOTAL}M Core:${CPU_INFO} echo -e ${SYS_INFO} echo -e "Please screenshot the above error message and post to the https://github.com/aaPanel/BillionMail/issues for help" } Red_Error(){ echo '================================================='; printf '\033[1;31;40m%b\033[0m\n' "$@"; GetSysInfo exit 1; } Bored_waiting(){ w_time="$wait_time" msg="$wait_msg" progress="." for ((i=0; i<${w_time}; i++)) do printf "$msg %-10s %d\r" "$progress" "$((i+1))" sleep 1 if [ "$progress" == ".........." ]; then progress="." else progress+="." fi done printf "$msg %-10s %d\r" ".........." "$w_time" #echo "" } Docker_Start() { if Command_Exists docker || [ -f /lib/systemd/system/docker.service ]; then if Command_Exists systemctl; then # systemctl mask getty@tty1.service systemctl enable docker systemctl reset-failed docker.service systemctl start docker.service if [ "$?" != "0" ];then systemctl reset-failed docker.service systemctl restart docker.socket systemctl restart docker.service if [ "$?" != "0" ];then docker_status=$(systemctl status docker.service) echo "$docker_status" if [ -f /etc/docker/daemon.json ]; then echo "/etc/docker/daemon.json file content:" cat /etc/docker/daemon.json echo -e "\033[33mRecommended check the above /etc/docker/daemon.json Are the file contents correct?\033[0m" fi # Wait for 100 seconds of animation echo -e "\033[31m start docker service failed, maybe too fast to start\033[0m" wait_msg="waiting 100 seconds to start docker service" wait_time="100" Bored_waiting systemctl reset-failed docker.service systemctl restart docker.service if [ "$?" != "0" ];then docker_status=$(systemctl status docker.service) echo "$docker_status" Red_Error "docker start failed" "Please try again later execute the restart docker command: systemctl restart docker.service (no content returns normal)" "Finally, re-execute install BillionMail" fi fi fi else chkconfig --add docker chkconfig --level 2345 docker on service docker start if [ "$?" != "0" ];then service docker.service status cat /etc/docker/daemon.json Red_Error "docker start failed" fi fi else Red_Error "Docker is not installed" fi } Docker_Compose_Check(){ if Command_Exists docker-compose; then DOCKER_COMPOSE="docker-compose" else if Command_Exists docker; then Docker_compose="docker compose version" if $Docker_compose; then DOCKER_COMPOSE="docker compose" fi else Red_Error "ERROR: Docker Compose does not exist" fi fi } Docker_Status_Check(){ if Command_Exists docker ; then if Command_Exists systemctl; then docker_status=$(systemctl is-active docker) # Determine the running status of docker if [ "$docker_status" = "active" ]; then echo -e "\033[32m Docker is running \033[0m" else echo -e "\033[31m Docker Not running, starting... \033[0m" Docker_Start fi fi else Red_Error "The docker command does not exist, please check whether docker is installed." fi } Update_BillionMail(){ if ! Command_Exists git ; then Red_Error "ERROR: git Command does not exist" fi BRANCH="main" if [ -f "update.sh" ]; then echo -e "Checking for update.sh script..." MD5_1="$(md5sum update.sh)" git fetch origin git checkout "origin/${BRANCH}" update.sh MD5_2=$(md5sum update.sh) if [[ "${MD5_1}" != "${MD5_2}" ]]; then echo -e "\033[33m update.sh is changed, please run update.sh script again.\033[0m" chmod +x update.sh exit 1 fi fi echo -e "\033[34m🚀 Committing current changes...\033[0m" # Set user.name and user.email if they are not set if [[ -z "$(git config user.name)" ]]; then git config user.name BillionMail fi if [[ -z "$(git config user.email)" ]]; then git config user.email BillionMail@BillionMail.com fi # Commit current changes git add -u git commit -am "Before update on ${TIME}" > /dev/null # Fetch remote updates echo -e "\033[34m🔍 Fetching remote updates...\033[0m" git fetch origin # Merge changes echo -e "\033[34m🔄 Merging changes (strategy: ${GIT_MERGE_STRATEGY:-theirs}, options: patience)...\033[0m" git config merge.defaultToUpstream true git merge -X"${GIT_MERGE_STRATEGY:-theirs}" -Xpatience -m "After update on ${TIME}" GIT_MERGE_RETURN=$? case ${GIT_MERGE_RETURN} in 128) echo -e "\033[31m❌ Critical Error: Detected local files conflicting with upstream repository\033[0m" echo -e "Please move these files to another location before updating:" git status exit 1 ;; 1) echo -e "\033[33m⚠️ Detected file conflicts, attempting automatic resolution...\033[0m" git status --porcelain | grep -E "UD|DU" | awk '{print $2}' | xargs rm -v git add -A git commit -m "After update on ${TIME}" > /dev/null git checkout . echo -e "\033[33m✅ Conflicts resolved automatically, If necessary, delete and recreate the file.\033[0m" ;; 0) echo -e "\033[32m✅ Code update completed successfully.\033[0m" ;; *) echo -e "\033[31m❌ Unknown merge error encountered (code: ${GIT_MERGE_RETURN})\033[0m" echo -e "Please check the error message above and try again after resolving issues." exit 1 ;; esac if [ ! -s "docker-compose.yml" ]; then ls -al Red_Error "docker-compose.yml not found." fi echo -e "Stop BillionMail, please wait..." sleep 3 ${DOCKER_COMPOSE} down echo -e "Getting the latest image, please wait..." ${DOCKER_COMPOSE} pull echo -e "Starting BillionMail, please wait..." ${DOCKER_COMPOSE} up -d if [ $? -eq 0 ]; then echo -e "\033[32m✅ Started successfully, update completed.\033[0m" else echo -e "\033[33m⚠️ Update completed, please check container status. If startup failed, check container logs.\033[0m" fi [ ! -d "/opt" ] && mkdir /opt echo "${PWD_d}" > /opt/PWD-Billion-Mail.txt ln -sf ${PWD_d}/bm.sh /usr/bin/bm chmod +x ${PWD_d}/bm.sh } Update_config() { if ! grep -q "^RETENTION_DAYS=" .env; then echo "" >> .env echo "# Number of days to keep log backup" >> .env echo "RETENTION_DAYS=7" >> .env fi } echo " +----------------------------------------------------------------------------- | You are updating BillionMail. This operation will pull the latest code from | GitHub's main branch and rebuild the containers. | | If you have made any modifications, please back them up first. | This operation may overwrite your changes. +----------------------------------------------------------------------------- " while [ "$go" != 'y' ] && [ "$go" != 'n' ]; do read -p "Are you sure want to update BillionMail. All containers will be stopped? (y/n): " go; done if [ "$go" == 'n' ];then exit; fi Docker_Compose_Check Docker_Status_Check Update_BillionMail Update_config