├── pic ├── pic.png └── logo.jpg ├── README.md ├── CHANGELOG.md └── bdsm.sh /pic/pic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelradionov/bdsm/HEAD/pic/pic.png -------------------------------------------------------------------------------- /pic/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelradionov/bdsm/HEAD/pic/logo.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![usage](/pic/logo.jpg) 2 | ![usage](/pic/pic.png) 3 | 4 | # BDSM (Bash Database SQL Manager) 5 | 6 | Bash script to easily 7 | - get DB credentials from your CMS/framework config file 8 | - export DB, 9 | - import DB, 10 | - search in DB, 11 | - search/replace in DB, 12 | - pull database from remote server, 13 | - and lot more. Look at the screenshot above! 14 | 15 | # Supported engines 16 | - WordPress 17 | - Laravel 18 | - Prestashop 1.6 19 | - Prestashop 1.7 20 | - ... 21 | 22 | # Supported databases 23 | - MySQL 24 | - PostgreSQL 25 | 26 | Please request your engine by creating new issue on Github! Give me your engine's config file example and I'll add it to the script. 👍 27 | 28 | # Installation 29 | Fast way (in one step). Worked on Mac, Linux and Windows (terminals with bash support) 30 | 31 | Stable version 🌞 32 | 33 | ```shell 34 | eval "$(curl "https://raw.githubusercontent.com/michaelradionov/gg_installer/master/gg_installer.sh")" && gg_installer bdsm 35 | ``` 36 | 37 | 38 | # Other our scripts installation 39 | 40 | Since BDSM can install other usefull scripts, you can install them simply running 41 | ```shell 42 | bdsm --install-all 43 | ``` 44 | 45 | This command will install: 46 | - [Git aliases](https://github.com/michaelradionov/aliases/blob/master/aliases_git2.sh) 47 | - [HelloBash script](https://github.com/michaelradionov/helloBash) 48 | - My favorite [Micro Editor](https://micro-editor.github.io/) 49 | - [Laravel aliases](https://github.com/michaelradionov/aliases/blob/master/laravel_aliases.sh) 50 | - [Docker aliases](https://github.com/michaelradionov/aliases/blob/master/docker_aliases.sh) 51 | - [Jira aliases](https://github.com/michaelradionov/aliases/blob/master/jira_aliases.sh) 52 | - [Random aliases](https://github.com/michaelradionov/aliases/blob/master/random_aliases.sh) 53 | 54 | # Usage in interactive mode (for humans) 55 | 56 | 1. `cd` in website root directory 57 | 2. Execute `bdsm` 58 | 3. Follow instructions. Enjoy! See features description below. 59 | 4. ~~💰 Donate~~. Nah! Just give this repo a star and I will appreciate it! ⭐️ 60 | 61 | # Usage with flags (for automation) 62 | 63 | `--backup` option is made to simply create DB backup file with name like `my_database_mysql_2019-10-25.sql` and quit. `--backup` option will not open interactive mode. 64 | 65 | ```shell 66 | bdsm --backup [-d ] [-n ] [--zip] 67 | ``` 68 | 69 | Example: 70 | 71 | ```shell 72 | bdsm --backup -d /backups/ -n 14 --zip 73 | ``` 74 | 75 | ## Parameters 76 | - `-d` is optional parameter. Use it to specify backups directory. By default, script will use `bd_backups` folder in root of your project (website) 77 | - `-n` is optional parameter. Use it to limit total number of backups. By default, there is no limit. 78 | - `--zip` is optional parameter. Use it to automatically compress database backups with tar (tar.gz) 79 | 80 | 81 | You can use **BDSM** as a simple DB backuping tool by putting this to your cron job! I suggest you to use it like this 82 | 83 | ### Cron jobs 84 | 85 | Daily Cron job example: 86 | 87 | ```shell 88 | 0 0 * * * /bin/bash -c "source ~/.gg_tools/bdsm.sh && cd && bdsm --backup -d --zip " 89 | ``` 90 | 91 | For example, you can keep 7 daily backups, 4 weekly and 6 monthly backups in different folders like this 92 | 93 | ```shell 94 | WEBSITE_PATH= 95 | BACKUPS_PATH=/db_backups 96 | 97 | # 7 daily backups 98 | 0 0 * * * /bin/bash -c "source ~/.gg_tools/bdsm.sh && cd "$WEBSITE_PATH" && bdsm --backup -d "$BACKUPS_PATH"/daily -n 7 --zip" 99 | 100 | # 4 weekly backups 101 | 0 0 * * 0 /bin/bash -c "source ~/.gg_tools/bdsm.sh && cd "$WEBSITE_PATH" && bdsm --backup -d "$BACKUPS_PATH"/weekly -n 4 --zip" 102 | 103 | # 6 monthly backups 104 | 0 0 1 * * /bin/bash -c "source ~/.gg_tools/bdsm.sh && cd "$WEBSITE_PATH" && bdsm --backup -d "$BACKUPS_PATH"/monthly -n 6 --zip" 105 | ``` 106 | 107 | 108 | where `path/to/your/website` is obviously a path to your website! 109 | 110 | ## Features (options) 111 | 112 | 0. When you execute BDSM it will automatically detect you engine, check for config file and parses it to get DB credentials. Then all of that data will be stored in your current Bash session. 113 | 1. **Show credentials.** Pretty obvious, isn't it? 114 | 2. **Export DB locally.** This option will dump your database into file. If you are using Docker, then you should use option 12 before. See description below. 115 | 3. **Search in dump.** This option is responsible for searching in exported DB dump file. 116 | 4. **Search/Replace in dump.** Obviously, this option will search/replace any given data. First step will be search, then (optionally) replacement. Please keep in your mind that BDSM doesn't support RegExp for now, only simple string values. 117 | 5. **Import dump.** If BDSM successfully determined your DB credentials OR if your entered them manually (option 11), then "Import dump" option will... Guess what? It will import you dump file to database. What a surprise! 118 | 6. **Pull DB from remote server.** This is very cool feature that allows you do a bunch of stuff IN ONE RUN! What it will do is: 119 | 1. SSH to a given remote server 120 | 2. Check if there is a Docker container with "mysql" in its name 121 | 3. Depending on previous step it will export DB to dump file from localhost or from Docker container 122 | 4. SCP exported dump to you on youк local machine (depending where you launched BDSM) 123 | 5. Delete exported dump from the remote server 124 | 7. **Delete dump.** Deletes local dump. 125 | 8. **Self-update.** You can update BDSM to the last version by this option. 126 | 9. **Install other scripts.** Convinious way to install or update: 127 | 1. Micro Editor. GUI like editro in your terminal and alias it to "m" command. See more https://micro-editor.github.io/ 128 | 2. Hello Bash. My handy Bash prompt configurator. I use it everywhere. See more https://github.com/michaelradionov/helloBash 129 | 3. My awesome Git aliases. See more here https://github.com/michaelradionov/aliases 130 | 4. Other aliases for Jira, Laravel, Docker and other. 131 | 10. **Look for dump elsewhere locally.** This option will help you to point BDSM to already existing dump file. 132 | 11. **Enter credentials manually.** Manually input DB name, user and password. 133 | 12. **Choose/forget local Docker container.** In fact it means "enter Docker mode". Here you can: 134 | 1. Let BDSM to find container with "mysql" in its name 135 | 2. Explicitly input Docker container's name 136 | 3. Quit Docker mode 137 | 138 | 139 | # Requirements 140 | 141 | - **Bash support.** Check by executing `bash --version` 142 | - **Mysql CLI support.** Check by executing `mysql --version` and `mysqldump --version` 143 | - **cURL CLI support.** Check by executing `curl --version` 144 | - **Sourcing ~/.bashrc file** on session start. Check by: 145 | 1. executing `echo 'echo "It works"' >> ~/.bashrc` 146 | 2. then start new terminal session. If you see "It works!" then you good. 147 | 3. Then **delete** this line from your ~/.bashrc. 148 | - For the previous item. If you don't know where is ~/.bashrc, you should execute `cd` (without parameters), then `pwd`. Output will be your home path, it equals `~`. 149 | 150 | If you have literally ANY trouble with installing and using script, please, create an issue in Github repo https://github.com/michaelradionov/bdsm. 151 | 152 | # Changelog 153 | 154 | [Changelog](https://github.com/michaelradionov/bdsm/blob/master/CHANGELOG.md) 155 | 156 | # Contribution 157 | 158 | 1. Edit the variable `BDSM_VERSION` in **bdsm.sh** 159 | 2. Update **CHANGELOG.md** 160 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [0.3.10] - 2021-12-15 8 | ### Added 9 | - 'if-exists' option for dropdb 10 | 11 | ## [0.3.9] - 2021-08-05 12 | ### Added 13 | - Fixed dump generation 14 | - Added using of COMPOSE_PROJECT_NAME for docker 15 | 16 | ## [0.3.8] - 2020-05-14 17 | ### Added 18 | - Backuping without locking tables 19 | - Added script version in greetings 20 | 21 | ## [0.3.7] - 2020-04-11 22 | ### Added 23 | - "--zip" flag for cron backup mode which compresses backups with tar (tar.gz). 24 | 25 | ## [0.3.6] - 2019-12-26 26 | ### Added 27 | - Various fixes to easily make several cron jobs for different backuping periods 28 | 29 | ## [0.3.5] - 2019-12-25 30 | ### Added 31 | - Added ability to limit max backups quantity by flag with something like `bdsm --backup -n 10` 32 | 33 | ## [0.3.4] - 2019-12-25 34 | ### Added 35 | - Added ability to specify backups location for Cron tasks with something like `bdsm --backup -d /some/folder/` 36 | - Nightly installation now made with the flag `--nightly` 37 | 38 | ## [0.3.3] - 2019-12-23 39 | ### Added 40 | - Added brancher script for switching branches (experimental feature for managers) 41 | - Added my gist to install Docker and docker-compose on Ubuntu 16.04 42 | 43 | ## [0.3.2] - 2019-11-12 44 | ### Added 45 | - Added ability to choose DB dump file from list 46 | 47 | ## [0.3.1] - 2019-10-25 48 | ### Added 49 | - Automagically detect Docker 50 | 51 | ## [0.3.0] - 2019-10-25 52 | ### Added 53 | - **HOT!** Added `--backup` flag to backup database by automatically Cron job! 54 | - Placing DB dump file to folder 55 | 56 | ## [0.2.1] - 2019-10-24 57 | ### Added 58 | - Better dump file naming 59 | 60 | ## [0.2.0] - 2019-10-23 61 | ### Added 62 | - PostgresSQL support 63 | - Nightly builds to keep stable version stable all the time 64 | 65 | ## [0.0.1] - 2019-01-13 66 | ### Added 67 | - Changelog! 68 | 69 | 211 | -------------------------------------------------------------------------------- /bdsm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # BDSM – Bash Database SQL Manager 4 | # https://github.com/michaelradionov/bdsm 5 | 6 | 7 | 8 | getBackupsFolderName(){ 9 | echo "db_backups" 10 | } 11 | 12 | generateDumpName(){ 13 | dump_name="${DB_DATABASE}_${DB_CONNECTION}_$(date +%Y-%m-%d__%H-%M).sql" 14 | echo $dump_name 15 | } 16 | 17 | # Variables 18 | SCRIPT_NAME="bdsm" 19 | BACKUP_FOLDER=$(getBackupsFolderName) 20 | SCRIPTS_FOLDER=~/.gg_tools 21 | BDSM_VERSION="v0.3.10" 22 | 23 | # Colors 24 | L_RED='\033[1;31m' 25 | YELLOW='\033[1;33m' 26 | WHITE='\033[1;37m' 27 | D_GREY='\033[1;30m' 28 | D_VIOL='\033[1;34m' 29 | NC='\033[0m' 30 | 31 | # Check previous command error status 32 | check_command_exec_status () { 33 | if [[ $1 -eq 0 ]] 34 | then 35 | echo -e "${YELLOW}Success!${NC}" 36 | echo 37 | else 38 | echo -e "${L_RED}ERROR${NC}" 39 | echo 40 | fi 41 | } 42 | 43 | isDumpExists(){ 44 | # If dump doesn't exists 45 | if [ ! -f "$BACKUP_FOLDER/$dbfile" ]; then 46 | echo -e "${L_RED}No DB dump file found!${NC}" 47 | return 1 48 | fi 49 | } 50 | 51 | # Deletes dump 52 | deleteDump(){ 53 | echo 54 | isDumpExists || return 55 | echo "Deleting dump..."; 56 | rm -f "$BACKUP_FOLDER/$dbfile" 57 | check_command_exec_status $? 58 | } 59 | 60 | # Imports dump 61 | importDump(){ 62 | isDumpExists || return 63 | if [[ -z $container ]]; then 64 | # Not in Docker mode 65 | echo "Importing locally"; 66 | # MySQL 67 | if [[ $DB_CONNECTION == "mysql" ]]; then 68 | mysql -u$DB_USERNAME -p$DB_PASSWORD $DB_DATABASE --force < "$BACKUP_FOLDER/$dbfile" 69 | fi 70 | # PostgreSQL 71 | if [[ $DB_CONNECTION == "pgsql" ]]; then 72 | echo "Droping DB..."; 73 | PGPASSWORD=$DB_PASSWORD dropdb --if-exists -U $DB_USERNAME $DB_DATABASE 74 | 75 | if [[ $? -eq 0 ]]; then 76 | echo "Creating DB..."; 77 | PGPASSWORD=$DB_PASSWORD createdb -U $DB_USERNAME $DB_DATABASE 78 | if [[ $? -eq 0 ]]; then 79 | echo "Importing dump..."; 80 | PGPASSWORD=$DB_PASSWORD psql --quiet -U $DB_USERNAME $DB_DATABASE < "$BACKUP_FOLDER/$dbfile" 81 | check_command_exec_status $? 82 | fi 83 | else 84 | check_command_exec_status 1 85 | fi 86 | 87 | fi 88 | else 89 | # Docker mode 90 | echo "Importing in Docker Container"; 91 | # MySQL 92 | if [[ $DB_CONNECTION == "mysql" ]]; then 93 | cat "$BACKUP_FOLDER/$dbfile" | docker exec -i $container /usr/bin/mysql -u$DB_USERNAME -p$DB_PASSWORD $DB_DATABASE 94 | fi 95 | # PostgreSQL 96 | if [[ $DB_CONNECTION == "pgsql" ]]; then 97 | 98 | echo "Droping DB..."; 99 | docker exec -i $container /usr/local/bin/dropdb --if-exists -U $DB_USERNAME $DB_DATABASE 100 | 101 | if [[ $? -eq 0 ]]; then 102 | 103 | echo "Creating DB..."; 104 | docker exec -i $container /usr/local/bin/createdb -U $DB_USERNAME $DB_DATABASE 105 | 106 | if [[ $? -eq 0 ]]; then 107 | 108 | echo "Importing dump..."; 109 | cat "$BACKUP_FOLDER/$dbfile" | docker exec -i $container /usr/local/bin/psql --quiet -U $DB_USERNAME -d $DB_DATABASE 110 | check_command_exec_status $? 111 | fi 112 | else 113 | check_command_exec_status 1 114 | fi 115 | fi 116 | fi 117 | } 118 | 119 | 120 | # Enter credentials manually 121 | EnterCredentials(){ 122 | # read -p "Enter Mysql host: " DB_HOST 123 | read -p "Enter DB name: " DB_DATABASE 124 | read -p "Enter DB user: " DB_USERNAME 125 | read -p "Enter DB password: " DB_PASSWORD 126 | read -p "Enter DB connection (mysql or pgsql): " DB_CONNECTION 127 | } 128 | 129 | 130 | # Searches in DB dump 131 | SearchInDump(){ 132 | echo 133 | isDumpExists || return 134 | read -p 'Search string: ' old_domain 135 | echo 136 | echo -e "Searching for ${WHITE}${old_domain}${NC} in ${WHITE}${dbfile}${NC}"; 137 | find=`grep -o "$old_domain" "$BACKUP_FOLDER/$dbfile" | wc -l | tr -d " "`; 138 | check_command_exec_status $? 139 | echo -e "Found ${WHITE}$find${NC} occurrences of $old_domain"; 140 | echo 141 | } 142 | 143 | # Search and replace in dump 144 | searchReplaceInDump(){ 145 | SearchInDump 146 | echo 147 | isDumpExists || return 148 | read -p 'Replace string (q to exit): ' new_domain 149 | if [[ $new_domain == "q" ]] || [[ -z $new_domain ]]; then 150 | echo -e "${L_RED}Not doing search/replace!${NC}" 151 | return 0 152 | fi 153 | echo 154 | echo -e "Replacing ${WHITE}${old_domain}${NC} with ${WHITE}${new_domain}${NC} in ${WHITE}${dbfile}${NC}"; 155 | 156 | perl -pi -w -e "s|${old_domain}|${new_domain}|g;" "$BACKUP_FOLDER/$dbfile" 157 | check_command_exec_status $? 158 | } 159 | 160 | # get DB credentials from config file 161 | getCredentials(){ 162 | 163 | unset configFile 164 | # unset DB_DATABASE 165 | # unset DB_USERNAME 166 | # unset DB_PASSWORD 167 | 168 | # Looking for config file 169 | # WordPress 170 | if [ -f wp-config.php ]; then 171 | appName='WordPress' 172 | configFile=wp-config.php 173 | # DB_HOST=`cat "$configFile" | grep DB_HOST | cut -d \' -f 4` 174 | DB_DATABASE=`cat "$configFile" | grep DB_NAME | cut -d \' -f 4` 175 | DB_USERNAME=`cat "$configFile" | grep DB_USER | cut -d \' -f 4` 176 | DB_PASSWORD=`cat "$configFile" | grep DB_PASSWORD | cut -d \' -f 4` 177 | COMPOSE_PROJECT_NAME=`cat "$configFile" | grep COMPOSE_PROJECT_NAME | cut -d \' -f 4` 178 | DB_CONNECTION='mysql' 179 | 180 | # WordPress from wp-content. Long story. We have some oldest repos in wp-content folder 181 | elif [ -f ../wp-config.php ]; then 182 | appName='WordPress' 183 | configFile=../wp-config.php 184 | # DB_HOST=`cat "$configFile" | grep DB_HOST | cut -d \' -f 4` 185 | DB_DATABASE=`cat "$configFile" | grep DB_NAME | cut -d \' -f 4` 186 | DB_USERNAME=`cat "$configFile" | grep DB_USER | cut -d \' -f 4` 187 | DB_PASSWORD=`cat "$configFile" | grep DB_PASSWORD | cut -d \' -f 4` 188 | COMPOSE_PROJECT_NAME=`cat "$configFile" | grep COMPOSE_PROJECT_NAME | cut -d \' -f 4` 189 | DB_CONNECTION='mysql' 190 | 191 | # Laravel 192 | elif [[ -f .env ]]; then 193 | appName='Laravel' 194 | configFile=.env 195 | source .env 196 | 197 | # Prestashop 1.7 198 | elif [[ -f app/config/parameters.php ]]; then 199 | appName='Prestashop 1.7' 200 | configFile=app/config/parameters.php 201 | DB_DATABASE=`cat "$configFile" | grep database_name | cut -d \' -f 4` 202 | DB_USERNAME=`cat "$configFile" | grep database_user | cut -d \' -f 4` 203 | DB_PASSWORD=`cat "$configFile" | grep database_password | cut -d \' -f 4` 204 | DB_CONNECTION='mysql' 205 | 206 | # Prestashop 1.6 207 | elif [[ -f config/settings.inc.php ]]; then 208 | appName='Prestashop 1.6' 209 | configFile=config/settings.inc.php 210 | DB_DATABASE=`cat "$configFile" | grep DB_NAME | cut -d \' -f 4` 211 | DB_USERNAME=`cat "$configFile" | grep DB_USER | cut -d \' -f 4` 212 | DB_PASSWORD=`cat "$configFile" | grep DB_PASSWD | cut -d \' -f 4` 213 | DB_CONNECTION='mysql' 214 | 215 | # Not found 216 | # else 217 | # DB_DATABASE='' 218 | # DB_USERNAME='' 219 | # DB_PASSWORD='' 220 | fi 221 | 222 | } 223 | 224 | # Creates DB dump 225 | createDump(){ 226 | echo 227 | if [[ -z $DB_DATABASE ]] || [[ -z $DB_USERNAME ]]; then 228 | echo -e "${L_RED}Sorry, credentials is not set :(${NC}" 229 | return 230 | fi 231 | if [ -z $BACKUP_FOLDER ]; then 232 | BACKUP_FOLDER=$(getBackupsFolderName) 233 | fi 234 | checkAndCreateBackupFolder 235 | 236 | dbfile=$(generateDumpName) 237 | 238 | dumpFilePath="${BACKUP_FOLDER}/${dbfile}" 239 | 240 | if [[ -z $container ]]; then 241 | # Not in Docker mode 242 | echo -e "Making DB dump locally in ${WHITE}${dumpFilePath}${NC}"; 243 | 244 | # MySQL connection 245 | if [[ $DB_CONNECTION == "mysql" ]]; then 246 | mysqldump --insert-ignore --skip-lock-tables --single-transaction=TRUE -u$DB_USERNAME -p$DB_PASSWORD $DB_DATABASE > $dumpFilePath 247 | fi 248 | 249 | # PostgreSQL connection 250 | if [[ $DB_CONNECTION == "pgsql" ]]; then 251 | PGPASSWORD=$DB_PASSWORD pg_dump -U $DB_USERNAME $DB_DATABASE > $dumpFilePath 252 | fi 253 | 254 | check_command_exec_status $? 255 | # This is for dumpStats 256 | remote=1 257 | else 258 | # Docker mode 259 | echo "Making DB dump from Docker container"; 260 | 261 | if [[ $DB_CONNECTION == "mysql" ]]; then 262 | docker exec $container /usr/bin/mysqldump --insert-ignore --skip-lock-tables --single-transaction=TRUE -u$DB_USERNAME -p$DB_PASSWORD $DB_DATABASE > $dumpFilePath 263 | fi 264 | if [[ $DB_CONNECTION == "pgsql" ]]; then 265 | docker exec $container /usr/local/bin/pg_dump -U $DB_USERNAME $DB_DATABASE > $dumpFilePath 266 | fi 267 | 268 | check_command_exec_status $? 269 | # This is for dumpStats 270 | remote=3 271 | fi 272 | } 273 | 274 | showCredentials(){ 275 | echo 276 | # echo -e "DB host: ${WHITE}$DB_HOST${NC}" 277 | echo -e "DB connection: ${WHITE}$DB_CONNECTION${NC}" 278 | echo -e "DB name: ${WHITE}$DB_DATABASE${NC}" 279 | echo -e "DB user: ${WHITE}$DB_USERNAME${NC}" 280 | echo -e "DB password: ${WHITE}$DB_PASSWORD${NC}" 281 | echo 282 | # dumpStats 283 | } 284 | 285 | showdelimiter(){ 286 | echo 287 | echo '-------------------' 288 | echo 289 | } 290 | 291 | title(){ 292 | echo -e "${D_VIOL}$1${NC}" 293 | } 294 | 295 | dumpStats(){ 296 | echo 297 | echo -e "Current dir: ${WHITE}$(pwd)${NC}" 298 | # Config file 299 | if [[ ! -f $configFile ]]; then 300 | echo -e "${L_RED}Can't find config file!${NC}" 301 | else 302 | echo -e "App name: ${WHITE}$appName${NC}" 303 | echo -e "Config file: ${WHITE}$configFile${NC}" 304 | fi 305 | 306 | # DB dump 307 | if [ -f "$BACKUP_FOLDER/$dbfile" ]; then 308 | dumpSize=$(du -k -h "$BACKUP_FOLDER/$dbfile" | cut -f1 | tr -d ' ') 309 | dumpChangeDate=$(date -r "$BACKUP_FOLDER/$dbfile") 310 | echo -e "DB dump file: ${WHITE}$dbfile${NC}" 311 | echo -e "DB type: ${WHITE}$DB_CONNECTION${NC}" 312 | echo -e "Remote or local dump: $(remoteOrLocalDump)" 313 | echo -e "Dump size: ${WHITE}$dumpSize${NC}" 314 | echo -e "Dump last changed: ${WHITE}$dumpChangeDate${NC}" 315 | else 316 | echo -e "${L_RED}No DB dump found!${NC}" 317 | fi 318 | # Docker container 319 | if [[ ! -z $container ]]; then 320 | echo -e "Docker container: ${WHITE}$container${NC}" 321 | fi 322 | } 323 | 324 | # Determines if dump made from local or remote DB 325 | remoteOrLocalDump(){ 326 | if [[ $remote -eq 1 ]]; then 327 | # Local 328 | echo -e "${WHITE}Local${NC}" 329 | elif [[ $remote -eq 2 ]]; then 330 | # Remote 331 | echo -e "${YELLOW}Remote (${remotePath})${NC}" 332 | elif [[ $remote -eq 3 ]]; then 333 | # Remote 334 | echo -e "${D_GREY}Local from Docker container${NC}" 335 | else 336 | echo -e "Not sure ..." 337 | fi 338 | } 339 | 340 | surprise(){ 341 | curl parrot.live 342 | # curl http://artscene.textfiles.com/asciiart/angela.art 343 | # curl http://artscene.textfiles.com/asciiart/cow.txt 344 | } 345 | 346 | PullDumpFromRemote(){ 347 | echo 348 | echo -e "Remote host?" 349 | # Show previous host if it is not empty 350 | if [[ ! -z $host ]]; then 351 | oldhost=$host 352 | echo -e "Previous host: ${WHITE}${host}${NC}"; 353 | fi 354 | read -p "For example, root@123.45.12.23 or just hit 'enter' for previous host: " host 355 | echo 356 | 357 | # if user just pushed enter and previous host is empty 358 | if [[ -z $host && -z $oldhost ]]; then 359 | echo -e "${L_RED}No host!${NC}" 360 | return 361 | # if user just pushed enter and previous host is NOT empty 362 | elif [[ -z $host && ! -z $oldhost ]]; then 363 | host=$oldhost 364 | fi 365 | 366 | 367 | 368 | 369 | 370 | echo -e "Path on remote?" 371 | # Show previous path if it is not empty 372 | if [[ ! -z $path ]]; then 373 | echo -e "Previous path: ${WHITE}${path}${NC}"; 374 | oldpath=$path 375 | fi 376 | read -p "For example, /path/to/website or enter for previous path: " path 377 | echo 378 | 379 | # if user just pushed enter and previous path is empty 380 | if [[ -z $path && -z $oldpath ]]; then 381 | echo -e "${L_RED}No path!${NC}" 382 | return 383 | # if user just pushed enter and previous path is NOT empty 384 | elif [[ -z $path && ! -z $oldpath ]]; then 385 | path=$oldpath 386 | fi 387 | 388 | 389 | 390 | 391 | 392 | 393 | echo -e "Creating dump on remote server" 394 | echo 395 | # Triming trailing slash in path 396 | path=${path%%+(/)} 397 | # Creating dump on remote server and echoing only dump name 398 | remoteDump=$(ssh -t $host "cd $path && $(declare -f getCredentials createDump check_command_exec_status getFirstContainer generateDumpName checkAndCreateBackupFolder getBackupsFolderName); getCredentials; getFirstContainer > /dev/null 2>&1 ; createDump > /dev/null 2>&1 ; printf "'$dbfile') 399 | check_command_exec_status $? 400 | 401 | # Pulling dump from remote 402 | remotePath="${host}:${path}/${BACKUP_FOLDER}/${remoteDump}" 403 | dbfile=$remoteDump 404 | echo -e "Pulling dump from remote ${remotePath}" 405 | scp "${remotePath}" "${BACKUP_FOLDER}/${dbfile}" 406 | check_command_exec_status $? 407 | 408 | # Removing dump from remote 409 | echo -e "Removing dump from remote ${remotePath}" 410 | ssh -t $host "cd $path/$BACKUP_FOLDER && rm $remoteDump" 411 | check_command_exec_status $? 412 | 413 | # This is for dumpStats 414 | remote=2 415 | } 416 | 417 | getFirstContainer(){ 418 | if [[ $DB_CONNECTION == "mysql" ]]; then 419 | container=$(docker ps --format {{.Names}} | grep ${COMPOSE_PROJECT_NAME}_mysql) 420 | fi 421 | if [[ $DB_CONNECTION == "pgsql" ]]; then 422 | container=$(docker ps --format {{.Names}} | grep ${COMPOSE_PROJECT_NAME}_postgres) 423 | fi 424 | } 425 | 426 | selfUpdate(){ 427 | eval "$(curl "https://raw.githubusercontent.com/michaelradionov/gg_installer/master/gg_installer.sh")" && gg_installer bdsm 428 | check_command_exec_status $? 429 | } 430 | 431 | installOtherScripts(){ 432 | echo -e "What script do you want to install? 433 | ${WHITE}1.${NC} Go Git Aliases — ${YELLOW}https://github.com/michaelradionov/aliases${NC} 434 | ${WHITE}2.${NC} HelloBash — ${YELLOW}https://github.com/michaelradionov/helloBash${NC} 435 | ${WHITE}3.${NC} Install Micro Editor (Mac & Linux) — ${YELLOW}https://gist.github.com/michaelradionov/156daa2058d004f8bfe9356f7f2bf5de${NC} 436 | ${WHITE}4.${NC} Install Docker Aliases — ${YELLOW}https://github.com/michaelradionov/aliases${NC} 437 | ${WHITE}5.${NC} Install Laravel Aliases — ${YELLOW}https://github.com/michaelradionov/aliases${NC} 438 | ${WHITE}6.${NC} Install Jira Aliases — ${YELLOW}https://github.com/michaelradionov/aliases${NC} 439 | ${WHITE}7.${NC} Install Random Aliases — ${YELLOW}https://github.com/michaelradionov/aliases${NC} 440 | ${WHITE}8.${NC} Install Docker on Ubuntu 16.04 — ${YELLOW}https://gist.github.com/michaelradionov/84879dc686e7f9e43bc38ecbbd879af4${NC}" 441 | # ${WHITE}9.${NC} Install Brancher script for switching branches — ${YELLOW}https://github.com/Flagstudio/brancher${NC}" 442 | read -p "Type number: " script 443 | case $script in 444 | 1) 445 | InstallGoGitAliases 446 | ;; 447 | 2) 448 | InstallHelloBash 449 | ;; 450 | 3) 451 | InstallMicroEditor 452 | ;; 453 | 4) 454 | InstallDockerAliases 455 | ;; 456 | 5) 457 | InstallLaravelAliases 458 | ;; 459 | 6) 460 | InstallJiraAliases 461 | ;; 462 | 7) 463 | InstallRandomAliases 464 | ;; 465 | 8) 466 | InstallDockerOnUbuntu16 467 | ;; 468 | 9) 469 | InstallBrancher 470 | ;; 471 | esac 472 | } 473 | 474 | InstallGoGitAliases(){ 475 | title "Installing Go Git Aliases" 476 | echo -e "Check it out at https://github.com/michaelradionov/git-alias" 477 | eval "$(curl "https://raw.githubusercontent.com/michaelradionov/gg_installer/master/gg_installer.sh")" && gg_installer gg_aliases 478 | } 479 | InstallHelloBash(){ 480 | title "Installing Hello Bash" 481 | echo -e "Check it out at https://github.com/michaelradionov/helloBash" 482 | eval "$(curl "https://raw.githubusercontent.com/michaelradionov/gg_installer/master/gg_installer.sh")" && gg_installer hello_bash 483 | } 484 | InstallMicroEditor(){ 485 | title "Installing Micro Editor" 486 | echo -e "Check it out at https://gist.github.com/michaelradionov/156daa2058d004f8bfe9356f7f2bf5de" 487 | cd ; curl https://getmic.ro | bash; echo 'alias m="~/micro"' >> .bashrc; source ~/.bashrc; 488 | } 489 | InstallDockerAliases(){ 490 | title "Installing Docker Aliases" 491 | echo -e "Check it out at https://github.com/michaelradionov/aliases" 492 | eval "$(curl "https://raw.githubusercontent.com/michaelradionov/gg_installer/master/gg_installer.sh")" && gg_installer docker_aliases 493 | } 494 | InstallLaravelAliases(){ 495 | title "Installing Laravel Aliases" 496 | echo -e "Check it out at https://github.com/michaelradionov/aliases" 497 | eval "$(curl "https://raw.githubusercontent.com/michaelradionov/gg_installer/master/gg_installer.sh")" && gg_installer laravel_aliases 498 | } 499 | InstallJiraAliases(){ 500 | title "Installing Jira Aliases" 501 | echo -e "Check it out at https://github.com/michaelradionov/aliases" 502 | eval "$(curl "https://raw.githubusercontent.com/michaelradionov/gg_installer/master/gg_installer.sh")" && gg_installer jira_aliases 503 | 504 | } 505 | InstallRandomAliases(){ 506 | title "Installing Random Aliases" 507 | echo -e "Check it out at https://github.com/michaelradionov/aliases" 508 | eval "$(curl "https://raw.githubusercontent.com/michaelradionov/gg_installer/master/gg_installer.sh")" && gg_installer random_aliases 509 | 510 | } 511 | InstallDockerOnUbuntu16(){ 512 | title "Installing Docker on Ubuntu" 513 | echo -e "Check it out at https://gist.github.com/michaelradionov/84879dc686e7f9e43bc38ecbbd879af4" 514 | curl https://gist.githubusercontent.com/michaelradionov/84879dc686e7f9e43bc38ecbbd879af4/raw/e8027dd815106ad3b9351750beb3e09f4fffc7d8/Docker_Ubuntu_16.sh | sudo bash 515 | } 516 | InstallBrancher(){ 517 | title "Installing Brancher" 518 | echo -e "Check it out at https://github.com/Flagstudio/brancher" 519 | eval "$(curl "https://raw.githubusercontent.com/michaelradionov/gg_installer/master/gg_installer.sh")" && gg_installer brancher 520 | } 521 | 522 | 523 | ChooseDockerContainer(){ 524 | read -p "Enter container name, type 'forget' to forget OR leave empty to let BDSM find one: " container 525 | if [[ -z $container ]]; then 526 | getFirstContainer 527 | elif [[ $container == 'forget' ]]; then 528 | unset container 529 | fi 530 | } 531 | 532 | chooseDump(){ 533 | 534 | 535 | if [ -z "$(ls -A "$BACKUP_FOLDER")" ]; then 536 | echo -e "${L_RED}${BACKUP_FOLDER} is empty!${NC}" 537 | return 538 | fi 539 | 540 | files=( "${BACKUP_FOLDER}"/* ) 541 | 542 | echo "The following DB dump files were found; select one: " 543 | echo 544 | COLUMNS=12 545 | PS3="Use number to select a file or 'q' to cancel: " 546 | select filename in "${files[@]}" 547 | do 548 | if [[ "$REPLY" == q ]]; then break; fi 549 | if [[ "$filename" == "" ]] 550 | then 551 | echo "'$REPLY' is not a valid number" 552 | continue 553 | fi 554 | dbfile=$(basename "$filename") 555 | check_command_exec_status $? 556 | echo 557 | echo -e "${WHITE}$dbfile${NC} choosed" 558 | break 559 | done 560 | } 561 | 562 | checkAndCreateBackupFolder(){ 563 | 564 | if [ -n "$1" ]; then BACKUP_FOLDER=$1; fi 565 | 566 | if [ ! -d "$BACKUP_FOLDER" ]; then 567 | echo -e "Making ${WHITE}${BACKUP_FOLDER}${NC} directory for database backups..." 568 | mkdir -p "$BACKUP_FOLDER" 569 | check_command_exec_status $? 570 | fi 571 | } 572 | 573 | count_dumps_and_remove_old_dumps_by_count(){ 574 | BACKUPS_COUNT_LIMIT=$1 575 | BACKUPS_COUNT=$(ls -1q "$BACKUP_FOLDER" | wc -l) 576 | echo -e "There is $BACKUPS_COUNT files in $BACKUP_FOLDER" 577 | 578 | if [ "$BACKUPS_COUNT" -gt "$BACKUPS_COUNT_LIMIT" ]; then 579 | echo -e "You have ${WHITE}$BACKUPS_COUNT${NC} backups and backup limit is ${WHITE}$BACKUPS_COUNT_LIMIT${NC}" 580 | echo -e "${L_RED}Removing extras...${NC}" 581 | ls -tp "$BACKUP_FOLDER" | grep -v '/$' | tail -n +$((BACKUPS_COUNT_LIMIT + 1)) | awk "{print \"$BACKUP_FOLDER/\"\$1}" | xargs -I {} rm {} 582 | fi 583 | } 584 | 585 | 586 | 587 | askUserNoVariants(){ 588 | read -p "What do you want from me? (type number of action, 'q' or enter for help): " action 589 | } 590 | 591 | askUserWithVariants(){ 592 | echo -e "What do you want from me? 593 | ${WHITE}1.${NC} Show Credentials 594 | ${WHITE}2.${NC} Export DB locally 595 | ${WHITE}3.${NC} Search in dump 596 | ${WHITE}4.${NC} Search/Replace in dump 597 | ${WHITE}5.${NC} Import dump 598 | ${WHITE}6.${NC} Pull DB from remote server (with Docker support) ${L_RED}HOT!${NC} 599 | ${WHITE}7.${NC} Delete Dump 600 | ${WHITE}8.${NC} Self-update 601 | ${WHITE}9.${NC} Install other scripts ${L_RED}HOT!${NC} 602 | ${WHITE}10.${NC} Choose DB dump file from ${BACKUP_FOLDER} folder ${YELLOW}NEW!${NC} 603 | ${WHITE}11.${NC} Enter credentials manually 604 | ${WHITE}12.${NC} Choose/forget local Docker container 605 | 606 | ${WHITE}p.${NC} Party! Ctrl+C to exit the party 607 | ${WHITE}q.${NC} Exit" 608 | read -p "Type number (type number of action or 'q' for exit): " action 609 | } 610 | 611 | ################################################### 612 | # Routing 613 | ################################################### 614 | doStuff(){ 615 | case $action in 616 | 1) 617 | title 'showCredentials' 618 | # getCredentials 619 | showCredentials 620 | ;; 621 | 2) 622 | title 'createDump' 623 | createDump 624 | ;; 625 | 3) 626 | title 'SearchInDump' 627 | SearchInDump 628 | ;; 629 | 4) 630 | title 'searchReplaceInDump' 631 | searchReplaceInDump 632 | ;; 633 | 5) 634 | title 'importDump' 635 | importDump 636 | ;; 637 | 6) 638 | title 'PullDumpFromRemote' 639 | PullDumpFromRemote 640 | ;; 641 | 7) 642 | title 'deleteDump' 643 | deleteDump 644 | ;; 645 | 8) 646 | title 'selfUpdate' 647 | echo 648 | selfUpdate 649 | ;; 650 | 9) 651 | title 'installOtherScripts' 652 | echo 653 | installOtherScripts 654 | ;; 655 | 10) 656 | title 'chooseDump' 657 | echo 658 | chooseDump 659 | ;; 660 | 11) 661 | title 'EnterCredentials' 662 | echo 663 | EnterCredentials 664 | ;; 665 | 12) 666 | title 'ChooseDockerContainer' 667 | echo 668 | ChooseDockerContainer 669 | ;; 670 | 'p') 671 | surprise 672 | ;; 673 | 'q') 674 | title 'Bye!' 675 | return 1 676 | ;; 677 | *) 678 | # default 679 | title 'Need help?' 680 | # getCredentials 681 | dumpStats 682 | showdelimiter 683 | askUserWithVariants 684 | showdelimiter 685 | doStuff 686 | ;; 687 | esac 688 | } 689 | 690 | ########################### 691 | 692 | bdsm(){ 693 | 694 | # First we'll check input flags 695 | while [ -n "$1" ] 696 | do 697 | case "$1" in 698 | 699 | "--install-all") 700 | 701 | showdelimiter 702 | title "Installing ALL the stuff..." 703 | InstallGoGitAliases 704 | InstallHelloBash 705 | InstallMicroEditor 706 | InstallDockerAliases 707 | InstallLaravelAliases 708 | InstallJiraAliases 709 | InstallRandomAliases 710 | 711 | return 712 | ;; 713 | 714 | "--nightly") 715 | 716 | showdelimiter 717 | title "Installing BDSM in nightly mode..." 718 | eval "$(curl "https://raw.githubusercontent.com/michaelradionov/gg_installer/master/gg_installer.sh")" && gg_installer bdsm_nightly 719 | check_command_exec_status $? 720 | 721 | echo -e "Removing ${SCRIPTS_FOLDER}/${SCRIPT_NAME}.sh ..." 722 | rm ${SCRIPTS_FOLDER}/${SCRIPT_NAME}.sh 723 | check_command_exec_status $? 724 | 725 | echo -e "To switch back to stable mode please install BDSM as usual with default command. More info https://github.com/michaelradionov/bdsm" 726 | 727 | return 728 | ;; 729 | 730 | "--backup") 731 | 732 | showdelimiter 733 | title "Automatic (Cron) mode" 734 | showdelimiter 735 | getCredentials 736 | getFirstContainer 737 | shift # Getting rid of "--backup" parameter 738 | 739 | while [ -n "$1" ] 740 | do 741 | case "$1" in 742 | -d) 743 | 744 | if [ -z "$2" ]; then 745 | echo -e "${L_RED}You must specify backups path when using the \"-d\" flag!${NC}" 746 | return 747 | fi 748 | 749 | if [ ! -d "$2" ]; then 750 | echo -e "${L_RED}$2 folder doesn't exist!${NC}" 751 | checkAndCreateBackupFolder "$2" 752 | fi 753 | 754 | echo -e "Changing backup folder path to ${WHITE}$2${NC}" 755 | BACKUP_FOLDER=$2 756 | check_command_exec_status $? 757 | 758 | shift # Getting rid of parameter 759 | ;; 760 | 761 | -n) 762 | 763 | if [ -z "$2" ]; then 764 | echo -e "${L_RED}You must specify backups number using the \"-n\" flag!${NC}" 765 | return 766 | fi 767 | 768 | if ! [[ "$2" =~ ^[0-9]+$ ]]; then 769 | echo -e "${L_RED}$2 is not an integer!${NC}" 770 | return 771 | fi 772 | 773 | BACKUPS_COUNT_LIMIT=$2 774 | 775 | shift # Getting rid of parameter 776 | ;; 777 | --zip) 778 | ZIP_BACKUP=true 779 | ;; 780 | 781 | *) echo "$1 is not a valid option. More info https://github.com/michaelradionov/bdsm";; 782 | esac 783 | shift # Getting rid of '-d' or '-n' or '--zip' parameters 784 | done 785 | 786 | createDump 787 | 788 | # Clean up backups because CLI flag input 789 | if ! [ -z "$BACKUPS_COUNT_LIMIT" ]; then 790 | count_dumps_and_remove_old_dumps_by_count "$BACKUPS_COUNT_LIMIT" 791 | fi 792 | 793 | # ZIP backup because CLI flag input 794 | if [ -n "$ZIP_BACKUP" ]; then 795 | tar --remove-files -czf "$BACKUP_FOLDER/$dbfile.tar.gz" "$BACKUP_FOLDER/$dbfile" 796 | dbfile="$BACKUP_FOLDER/$dbfile.tar.gz" 797 | fi 798 | 799 | return 800 | ;; 801 | 802 | *) 803 | 804 | echo -e "${L_RED}$1 is not an option${NC}" 805 | return 806 | ;; 807 | 808 | esac 809 | shift 810 | done 811 | 812 | # If there is no inputs then we go interactive 813 | 814 | getCredentials 815 | getFirstContainer 816 | checkAndCreateBackupFolder 817 | showdelimiter 818 | title "Hello from ${YELLOW}BDSM${D_VIOL} script ${YELLOW}${BDSM_VERSION}${D_VIOL}!" 819 | 820 | while : 821 | do 822 | showdelimiter 823 | askUserNoVariants 824 | showdelimiter 825 | doStuff || break 826 | done 827 | return 828 | 829 | 830 | } 831 | --------------------------------------------------------------------------------