├── README.md ├── docs ├── mage2-backup-script-help-0.1.0.png ├── mage2-backup-script-help-0.2.0.png └── mage2-backup-script-in-action-0.2.0.gif └── src └── mage2-db-code-backup.sh /README.md: -------------------------------------------------------------------------------- 1 | # Bash Script: Backup Magento2 Code + Database 2 | 3 | This utility script helps you to backup Magento2 code and database. 4 | You can either run the command manually or can automate it via cronjob. 5 | 6 | 7 | ## INSTALL 8 | You can simply download the script file and give the executable permission. 9 | ``` 10 | curl -0 https://raw.githubusercontent.com/MagePsycho/magento2-db-code-backup-bash-script/master/src/mage2-db-code-backup.sh -o mage2-backup.sh 11 | chmod +x mage2-backup.sh 12 | ``` 13 | 14 | To make it system wide command 15 | ``` 16 | sudo mv mage2-backup.sh /usr/local/bin/mage2-backup.sh 17 | ``` 18 | 19 | ## USAGE 20 | ### To display help 21 | ``` 22 | ./mage2-backup.sh --help 23 | ``` 24 | 25 | ### To backup database only 26 | ``` 27 | ./mage2-backup.sh --backup-db --src-dir=/path/to/magento2/root --dest-dir=/path/to/destination 28 | ``` 29 | 30 | If you want to get rid of this message 31 | > Using a password on the command line interface can be insecure. 32 | 33 | You can create a `.my.cnf` file in home directory with the following config 34 | ``` 35 | [client] 36 | host=localhost 37 | user=[your-db-user] 38 | password=[your-db-pass] 39 | ``` 40 | And use option `--use-mysql-config` as 41 | ``` 42 | ./mage2-backup.sh --backup-db --use-mysql-config --src-dir=/path/to/magento2/root --dest-dir=/path/to/destination 43 | ``` 44 | 45 | ### To backup code only 46 | ``` 47 | ./mage2-backup.sh --backup-code --skip-media --src-dir=/path/to/magento2/root --dest-dir=/path/to/destination 48 | ``` 49 | 50 | ### To backup code + database 51 | ``` 52 | ./mage2-backup.sh --backup-db --backup-code --skip-media --src-dir=/path/to/magento2/root --dest-dir=/path/to/destination 53 | ``` 54 | 55 | **Notes** 56 | If you want to use custom backup name, you can pass `--backup-name=...` option. 57 | If this option is not used, the `mage2-backup.$DATETIME` will be used as a default basename for code & db backups. 58 | *You can utilize this option if you want to automate the backup and download process* 59 | 60 | 61 | ### To schedule backup via Cron 62 | If you want to schedule via Cron, just add the following line in your Crontab entry `crontab -e` 63 | ``` 64 | 0 0 * * * /path/to/mage2-backup.sh --backup-db --backup-code --skip-media --src-dir=/path/to/magento2/root --dest-dir=/path/to/destination > /dev/null 2>&1 65 | ``` 66 | `0 0 * * *` expression means the command will run run at every midnight. 67 | 68 | ## Screenshots 69 | ![Mage2Backup Help](https://github.com/MagePsycho/magento2-db-code-backup-bash-script/raw/master/docs/mage2-backup-script-help-0.2.0.png "Mage2Backup Help") 70 | 1. Mage2Backup Help 71 | 72 | ![Mage2Backup in Action](https://github.com/MagePsycho/magento2-db-code-backup-bash-script/raw/master/docs/mage2-backup-script-in-action-0.2.0.gif "Mage2Backup in Action") 73 | 2. Mage2Backup in Action 74 | 75 | ## TO-DOS 76 | - S3 support 77 | - Google Drive support 78 | - Option to exclude log tables 79 | -------------------------------------------------------------------------------- /docs/mage2-backup-script-help-0.1.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MagePsycho/magento2-db-code-backup-bash-script/b8957b8af172e430aba5ab3478938e2e36de8faa/docs/mage2-backup-script-help-0.1.0.png -------------------------------------------------------------------------------- /docs/mage2-backup-script-help-0.2.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MagePsycho/magento2-db-code-backup-bash-script/b8957b8af172e430aba5ab3478938e2e36de8faa/docs/mage2-backup-script-help-0.2.0.png -------------------------------------------------------------------------------- /docs/mage2-backup-script-in-action-0.2.0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MagePsycho/magento2-db-code-backup-bash-script/b8957b8af172e430aba5ab3478938e2e36de8faa/docs/mage2-backup-script-in-action-0.2.0.gif -------------------------------------------------------------------------------- /src/mage2-db-code-backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Script to backup Magento2 Codebase + Database 5 | # 6 | # @author Raj KB 7 | # @website http://www.magepsycho.com 8 | # @version 0.2.2 9 | 10 | # UnComment it if bash is lower than 4.x version 11 | shopt -s extglob 12 | 13 | ################################################################################ 14 | # CORE FUNCTIONS - Do not edit 15 | ################################################################################ 16 | # 17 | # VARIABLES 18 | # 19 | _bold=$(tput bold) 20 | _underline=$(tput sgr 0 1) 21 | _reset=$(tput sgr0) 22 | 23 | _purple=$(tput setaf 171) 24 | _red=$(tput setaf 1) 25 | _green=$(tput setaf 76) 26 | _tan=$(tput setaf 3) 27 | _blue=$(tput setaf 38) 28 | 29 | # 30 | # HEADERS & LOGGING 31 | # 32 | function _debug() 33 | { 34 | if [[ "$DEBUG" = 1 ]]; then 35 | "$@" 36 | fi 37 | } 38 | 39 | function _header() 40 | { 41 | printf '\n%s%s========== %s ==========%s\n' "$_bold" "$_purple" "$@" "$_reset" 42 | } 43 | 44 | function _arrow() 45 | { 46 | printf '➜ %s\n' "$@" 47 | } 48 | 49 | function _success() 50 | { 51 | printf '%s✔ %s%s\n' "$_green" "$@" "$_reset" 52 | } 53 | 54 | function _error() { 55 | printf '%s✖ %s%s\n' "$_red" "$@" "$_reset" 56 | } 57 | 58 | function _warning() 59 | { 60 | printf '%s➜ %s%s\n' "$_tan" "$@" "$_reset" 61 | } 62 | 63 | function _underline() 64 | { 65 | printf '%s%s%s%s\n' "$_underline" "$_bold" "$@" "$_reset" 66 | } 67 | 68 | function _bold() 69 | { 70 | printf '%s%s%s\n' "$_bold" "$@" "$_reset" 71 | } 72 | 73 | function _note() 74 | { 75 | printf '%s%s%sNote:%s %s%s%s\n' "$_underline" "$_bold" "$_blue" "$_reset" "$_blue" "$@" "$_reset" 76 | } 77 | 78 | function _die() 79 | { 80 | _error "$@" 81 | exit 1 82 | } 83 | 84 | function _safeExit() 85 | { 86 | exit 0 87 | } 88 | 89 | # 90 | # UTILITY HELPER 91 | # 92 | function _seekConfirmation() 93 | { 94 | printf '\n%s%s%s' "$_bold" "$@" "$_reset" 95 | read -p " (y/n) " -n 1 96 | printf '\n' 97 | } 98 | 99 | # Test whether the result of an 'ask' is a confirmation 100 | function _isConfirmed() 101 | { 102 | if [[ "$REPLY" =~ ^[Yy]$ ]]; then 103 | return 0 104 | fi 105 | return 1 106 | } 107 | 108 | 109 | function _typeExists() 110 | { 111 | if type "$1" >/dev/null; then 112 | return 0 113 | fi 114 | return 1 115 | } 116 | 117 | function _isOs() 118 | { 119 | if [[ "${OSTYPE}" == $1* ]]; then 120 | return 0 121 | fi 122 | return 1 123 | } 124 | 125 | function _checkRootUser() 126 | { 127 | #if [ "$(id -u)" != "0" ]; then 128 | if [ "$(whoami)" != 'root' ]; then 129 | echo "You have no permission to run $0 as non-root user. Use sudo" 130 | exit 1; 131 | fi 132 | 133 | } 134 | 135 | function _printPoweredBy() 136 | { 137 | local mp_ascii 138 | mp_ascii=' 139 | __ ___ ___ __ 140 | / |/ /__ ____ ____ / _ \___ __ ______/ / ___ 141 | / /|_/ / _ `/ _ `/ -_) ___(_-> Store: ${_reset}${_underline}${_blue}https://www.magepsycho.com${_reset}${_reset}${_green} 151 | >> Blog: ${_reset}${_underline}${_blue}https://blog.magepsycho.com${_reset}${_reset}${_green} 152 | 153 | ################################################################ 154 | ${_reset} 155 | EOF 156 | } 157 | 158 | ################################################################################ 159 | # SCRIPT FUNCTIONS 160 | ################################################################################ 161 | function _printUsage() 162 | { 163 | echo -n "$(basename "$0") [OPTION]... 164 | 165 | Backup Magento2 Codebase + Database. 166 | Version $VERSION 167 | 168 | Options: 169 | -sd, --src-dir Source directory (from where backup file will be created) 170 | -dd, --dest-dir Destination directory (to where the backup file will be moved) 171 | -bd, --backup-db Backup DB 172 | -bc, --backup-code Backup Code 173 | -bn --backup-name Backup Name (Default: mage2-backup.$DATETIME) 174 | -uc, --use-mysql-config Use MySQL config file (~/.my.cnf) 175 | -sm, --skip-media Skip media folder from code backup 176 | -h, --help Display this help and exit 177 | -v, --version Output version information and exit 178 | 179 | Examples: 180 | $(basename "$0") --backup-db --backup-code --skip-media --src-dir=... --dest-dir=... 181 | 182 | " 183 | _printPoweredBy 184 | exit 1 185 | } 186 | 187 | function processArgs() 188 | { 189 | # Parse Arguments 190 | for arg in "$@" 191 | do 192 | case $arg in 193 | -bd=*|--backup-db) 194 | M2_BACKUP_DB=1 195 | ;; 196 | -bc=*|--backup-code) 197 | M2_BACKUP_CODE=1 198 | ;; 199 | -sd=*|--src-dir=*) 200 | M2_SRC_DIR="${arg#*=}" 201 | ;; 202 | -dd=*|--dest-dir=*) 203 | M2_DEST_DIR="${arg#*=}" 204 | ;; 205 | -uc|--use-mysql-config) 206 | M2_USE_MYSQL_CONFIG=1 207 | ;; 208 | -sm|--skip-media) 209 | M2_SKIP_MEDIA=1 210 | ;; 211 | -bn=*|--backup-name=*) 212 | M2_BACKUP_NAME="${arg#*=}" 213 | ;; 214 | --debug) 215 | DEBUG=1 216 | ;; 217 | -h|--help) 218 | _printUsage 219 | ;; 220 | *) 221 | _printUsage 222 | ;; 223 | esac 224 | done 225 | 226 | validateArgs 227 | sanitizeArgs 228 | } 229 | 230 | function validateArgs() 231 | { 232 | ERROR_COUNT=0 233 | if [[ -z "$M2_BACKUP_DB" && -z "$M2_BACKUP_CODE" ]]; then 234 | _error "You should mention at least one of the backup types: --backup-db or --backup-code" 235 | ERROR_COUNT=$((ERROR_COUNT + 1)) 236 | fi 237 | 238 | if [[ -z "$M2_SRC_DIR" ]]; then 239 | _error "Source directory parameter missing." 240 | ERROR_COUNT=$((ERROR_COUNT + 1)) 241 | fi 242 | 243 | if [[ ! -z "$M2_SRC_DIR" && ! -f "$M2_SRC_DIR/app/etc/env.php" ]]; then 244 | _error "Source directory must be Magento 2 root folder." 245 | ERROR_COUNT=$((ERROR_COUNT + 1)) 246 | fi 247 | 248 | if [[ -z "$M2_DEST_DIR" ]]; then 249 | _error "Destination directory parameter missing." 250 | ERROR_COUNT=$((ERROR_COUNT + 1)) 251 | fi 252 | 253 | if [[ ! -z "$M2_DEST_DIR" ]] && ! mkdir -p "$M2_DEST_DIR"; then 254 | _error "Unable to create destination directory." 255 | ERROR_COUNT=$((ERROR_COUNT + 1)) 256 | fi 257 | 258 | #echo "$ERROR_COUNT" 259 | [[ "$ERROR_COUNT" -gt 0 ]] && exit 1 260 | } 261 | 262 | function sanitizeArgs() 263 | { 264 | # remove trailing / 265 | if [[ ! -z "$M2_SRC_DIR" ]]; then 266 | M2_SRC_DIR="${M2_SRC_DIR%/}" 267 | fi 268 | 269 | if [[ ! -z "$M2_DEST_DIR" ]]; then 270 | M2_DEST_DIR="${M2_DEST_DIR%/}" 271 | fi 272 | } 273 | 274 | function prepareBackupName() 275 | { 276 | if [[ -z "$M2_BACKUP_NAME" ]]; then 277 | #MD5=`echo \`date\` $RANDOM | md5sum | cut -d ' ' -f 1` 278 | DATETIME=$(date +"%Y-%m-%d-%H-%M-%S") 279 | M2_BACKUP_NAME="mage2-backup.$DATETIME" 280 | fi 281 | } 282 | 283 | function prepareCodebaseFilename() 284 | { 285 | M2_CODE_BACKUP_FILE="${M2_DEST_DIR}/${M2_BACKUP_NAME}.tar.gz" 286 | } 287 | 288 | function prepareDatabaseFilename() 289 | { 290 | M2_DB_BACKUP_FILE="${M2_DEST_DIR}/${M2_BACKUP_NAME}.sql.gz" 291 | } 292 | 293 | function createDbBackup() 294 | { 295 | _success "Dumping MySQL..." 296 | local host username password dbName 297 | # TODO FIX if there are multiple occurences 298 | host=$(grep -m 1 host "${M2_SRC_DIR}/app/etc/env.php" | cut -d "'" -f 4) 299 | username=$(grep -m 1 username "${M2_SRC_DIR}/app/etc/env.php" | cut -d "'" -f 4) 300 | password=$(grep -m 1 password "${M2_SRC_DIR}/app/etc/env.php" | cut -d "'" -f 4) 301 | dbName=$(grep -m 1 dbname "${M2_SRC_DIR}/app/etc/env.php" |cut -d "'" -f 4) 302 | 303 | # @todo option to skip log tables 304 | if [[ "$M2_USE_MYSQL_CONFIG" -eq 1 ]]; then 305 | mysqldump "$dbName" | gzip > "$M2_DB_BACKUP_FILE" 306 | else 307 | mysqldump -h "$host" -u "$username" -p"$password" "$dbName" | gzip > "$M2_DB_BACKUP_FILE" 308 | fi 309 | _success "Done!" 310 | } 311 | 312 | function createCodeBackup() 313 | { 314 | _success "Archiving Codebase..." 315 | 316 | declare -a EXC_PATH 317 | EXC_PATH[1]=./.git 318 | EXC_PATH[2]=./var 319 | EXC_PATH[3]=./pub/static 320 | EXC_PATH[4]=./app/etc/env.php 321 | EXC_PATH[5]=./generated 322 | 323 | if [[ "$M2_SKIP_MEDIA" == 1 ]]; then 324 | EXC_PATH[6]=./pub/media 325 | fi 326 | 327 | EXCLUDES='' 328 | for i in "${!EXC_PATH[@]}" ; do 329 | CURRENT_EXC_PATH=${EXC_PATH[$i]} 330 | # note the trailing space 331 | EXCLUDES="${EXCLUDES}--exclude=${CURRENT_EXC_PATH} " 332 | done 333 | 334 | tar -zcf "$M2_CODE_BACKUP_FILE" ${EXCLUDES} -C "${M2_SRC_DIR}" . 335 | _success "Done!" 336 | } 337 | 338 | function printSuccessMessage() 339 | { 340 | _success "Magento2 Backup Completed!" 341 | 342 | echo "################################################################" 343 | echo "" 344 | BACKUP_TYPES=() 345 | if [[ "$M2_BACKUP_DB" -eq 1 ]]; then 346 | BACKUP_TYPES+=('DB') 347 | fi 348 | if [[ "$M2_BACKUP_CODE" -eq 1 ]]; then 349 | BACKUP_TYPES+=('Code') 350 | fi 351 | 352 | backupTypes=$( IFS=$','; echo "${BACKUP_TYPES[*]}" ) 353 | echo " >> Backup Type : ${backupTypes}" 354 | echo " >> Backup Source : ${M2_SRC_DIR}" 355 | 356 | if [[ "$M2_BACKUP_DB" -eq 1 ]]; then 357 | echo " >> Database Dump File : ${M2_DB_BACKUP_FILE}" 358 | fi 359 | 360 | if [[ "$M2_BACKUP_CODE" -eq 1 ]]; then 361 | echo " >> Codebase Archive File : ${M2_CODE_BACKUP_FILE}" 362 | fi 363 | 364 | echo "" 365 | echo "################################################################" 366 | _printPoweredBy 367 | 368 | } 369 | 370 | ################################################################################ 371 | # Main 372 | ################################################################################ 373 | export LC_CTYPE=C 374 | export LANG=C 375 | 376 | DEBUG=0 377 | _debug set -x 378 | VERSION="0.2.2" 379 | 380 | M2_SRC_DIR= 381 | M2_DEST_DIR= 382 | M2_BACKUP_DB=0 383 | M2_BACKUP_CODE=0 384 | M2_USE_MYSQL_CONFIG=0 385 | M2_SKIP_MEDIA=0 386 | M2_BACKUP_NAME= 387 | M2_DB_BACKUP_FILE= 388 | M2_CODE_BACKUP_FILE= 389 | 390 | function main() 391 | { 392 | [[ $# -lt 1 ]] && _printUsage 393 | 394 | processArgs "$@" 395 | 396 | prepareBackupName 397 | prepareCodebaseFilename 398 | prepareDatabaseFilename 399 | 400 | if [[ "$M2_BACKUP_DB" -eq 1 ]]; then 401 | createDbBackup 402 | fi 403 | 404 | if [[ "$M2_BACKUP_CODE" -eq 1 ]]; then 405 | createCodeBackup 406 | fi 407 | 408 | printSuccessMessage 409 | 410 | exit 0 411 | } 412 | 413 | main "$@" 414 | 415 | _debug set +x 416 | --------------------------------------------------------------------------------