├── LICENSE.md ├── README.md ├── bin └── sync.sh ├── database.yml └── uploads.yml /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Creame - Paco Toledo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # trellis-simple-sync 2 | Ansible playbooks for Trellis database and uploads sync.\ 3 | Just copy and run without any extra configuration. 4 | 5 | ## Installation 6 | 1. Copy `database.yml` and `uploads.yml` files into Trellis root folder 7 | 2. Copy `bin/sync.sh` files into Trellis bin folder 8 | 3. (Optional) Add `db-backup-*.sql.gz` to your Bedrock `.gitignore` file 9 | 10 | ## Usage 11 | Run `./bin/sync.sh ` 12 | 13 | * Available `` options: `uploads`, `database`, `all` 14 | * Available `` options: `push`, `pull` 15 | * The `push` is for upload data from development and update selected environment, and the `pull` for download data from selected environment and update development. 16 | * `uploads` sync is not destructive, it only adds or update new files, don't delete missing files. 17 | * On every database sync a datetimed backup `db-backup-YYYYMMDDTHHMMSS.sql.gz` is saved before import data. 18 | 19 | ## Aliases 20 | You can use alias for a shorter or more intuitive commands 21 | 22 | * staging - stag - s 23 | * production - prod - p 24 | * database - db 25 | * uploads - media 26 | * push - up 27 | * pull - down 28 | 29 | Examples:\ 30 | `./bin/sync.sh stag example.com db up`\ 31 | `./bin/sync.sh prod example.com media down`\ 32 | `./bin/sync.sh prod example.com all up` 33 | 34 | ## Skipping GUIDs 35 | 36 | Skipping the GUID column is the default behaviour as it can have unwanted effects on production environments. If you are sure you want to replace GUID URLs, for example if you are deploying a site to production for the first time, you can prevent skipping GUIDs with `--extra-vars "skip_guids=false"`. 37 | 38 | Example:\ 39 | `./bin/sync.sh prod example.com db up --extra-vars "skip_guids=false"` 40 | 41 | ## Notes 42 | * Tested up to Ansible 2.6.1 43 | * Replace Elementor urls on database sync (if installed). 44 | * For database sync the development vagrant VM must be powered on every time you run a command 45 | * On every database sync a `db-backup-YYYYMMDDTHHMMSS.sql.gz` file is automatically created inside destination environment Bedrock folder. In development, if you don't want it to be saved in the repository: 46 | * You can add `db-backup-*.sql.gz` to your Bedrock `.gitignore` file 47 | * Or you can comment `PULL > Backup development database` task on `database.yml` 48 | 49 | ## Contribute 50 | * Anyone is welcome to contribute to the plugin. 51 | * Please merge (squash) all your changes into a single commit before you open a pull request. 52 | 53 | ## License 54 | MIT 55 | 56 | ## Credits 57 | © 2018 [Creame](https://crea.me). 58 | Heavily inspired by [trellis-database-uploads-migration](https://github.com/valentinocossar/trellis-database-uploads-migration) and [trellis-db-push-and-pull](https://github.com/hamedb89/trellis-db-push-and-pull). 59 | 60 | Special thanks to [the Roots team](https://roots.io/about/) whose [Trellis](https://github.com/roots/trellis) make this project possible. 61 | -------------------------------------------------------------------------------- /bin/sync.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | shopt -s nullglob 3 | 4 | ENVIRONMENTS=( hosts/* ) 5 | ENVIRONMENTS=( "${ENVIRONMENTS[@]##*/}" ) 6 | NUM_ARGS=4 7 | 8 | show_usage() { 9 | echo "Usage: sync 10 | 11 | is the environment to deploy to ("staging", "production", etc) 12 | is the WordPress site to deploy (name defined in "wordpress_sites") 13 | is what we go to sync ("uploads", "database" or "all") 14 | is the sync mode ("pull" or "push") 15 | 16 | Available environments: 17 | `( IFS=$'\n'; echo "${ENVIRONMENTS[*]}" )` 18 | 19 | Aliases: 20 | staging - stag - s 21 | production - prod - p 22 | uploads - media 23 | database - db 24 | pull - down 25 | push - up 26 | 27 | Examples: 28 | sync staging example.com database push 29 | sync production example.com db pull 30 | sync staging example.com uploads pull 31 | sync prod example.com all up 32 | " 33 | } 34 | 35 | [[ $# -lt NUM_ARGS ]] && { show_usage; exit 127; } 36 | 37 | for arg 38 | do 39 | [[ $arg = -h ]] && { show_usage; exit 0; } 40 | done 41 | 42 | ENV="$1"; shift 43 | SITE="$1"; shift 44 | TYPE="$1"; shift 45 | MODE="$1"; shift 46 | EXTRA_PARAMS=$@ 47 | 48 | # allow use of abbreviations of environments 49 | if [[ $ENV = p || $ENV = prod ]]; then 50 | ENV="production" 51 | elif [[ $ENV = s || $ENV = stag ]]; then 52 | ENV="staging" 53 | fi 54 | 55 | # allow use of alias of types 56 | if [[ $TYPE = db ]]; then 57 | TYPE="database" 58 | elif [[ $TYPE = media ]]; then 59 | TYPE="uploads" 60 | fi 61 | 62 | # allow use of alias of modes 63 | if [[ $MODE = down ]]; then 64 | MODE="pull" 65 | elif [[ $MODE = up ]]; then 66 | MODE="push" 67 | fi 68 | 69 | DATABASE_CMD="ansible-playbook database.yml -e env=$ENV -e site=$SITE -e mode=$MODE $EXTRA_PARAMS" 70 | UPLOADS_CMD="ansible-playbook uploads.yml -e env=$ENV -e site=$SITE -e mode=$MODE $EXTRA_PARAMS" 71 | 72 | HOSTS_FILE="hosts/$ENV" 73 | 74 | if [[ ! -e $HOSTS_FILE ]]; then 75 | echo "Error: '$ENV' is not a valid environment ($HOSTS_FILE does not exist)." 76 | echo 77 | echo "Available environments:" 78 | ( IFS=$'\n'; echo "${ENVIRONMENTS[*]}" ) 79 | exit 1 80 | fi 81 | 82 | if [[ $TYPE != "database" && $TYPE != "uploads" && $TYPE != "all" ]]; then 83 | echo "Error: '$TYPE' is not a valid type (uploads or media, database or db, all)." 84 | exit 1 85 | fi 86 | 87 | if [[ $MODE != "pull" && $MODE != "push" ]]; then 88 | echo "Error: '$MODE' is not a valid sync mode (pull or down, push or up)." 89 | exit 1 90 | fi 91 | 92 | if [[ $TYPE = database ]]; then 93 | $DATABASE_CMD 94 | elif [[ $TYPE = uploads ]]; then 95 | $UPLOADS_CMD 96 | else 97 | $UPLOADS_CMD 98 | $DATABASE_CMD 99 | fi 100 | -------------------------------------------------------------------------------- /database.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Sync {{ site }} DATABASE between development <-> {{ env }} environments 3 | hosts: web:&{{ env }} 4 | remote_user: "{{ web_user }}" 5 | 6 | vars: 7 | project: "{{ wordpress_sites[site] }}" 8 | project_current: "{{ www_root }}/{{ site }}/current" 9 | project_local_path: "{{ (lookup('env', 'USER') == 'vagrant') | ternary(project_current, project.local_path) }}" 10 | sync_file: "db-sync.sql" 11 | backup_file: "db-backup-{{ ansible_date_time.iso8601_basic_short }}.sql" 12 | dev_host: "{{ groups['development'] | first }}" 13 | domain_dev: "{{ hostvars[dev_host].wordpress_sites[site].site_hosts.0.canonical }}" 14 | domain_env: "{{ project.site_hosts.0.canonical }}" 15 | url_dev: "{{ hostvars[dev_host].wordpress_sites[site].ssl.enabled | ternary('https', 'http') }}://{{ domain_dev }}" 16 | url_env: "{{ project.ssl.enabled | ternary('https', 'http') }}://{{ domain_env }}" 17 | skip_guids_option: "--skip-columns=guid" 18 | skip_guids_column: "{{ skip_guids | default('false') }}" 19 | 20 | tasks: 21 | - name: Abort if environment variable is equal to development 22 | fail: 23 | msg: "ERROR: development is not a valid environment for this mode (you can't push/pull from development to development)." 24 | when: env == "development" 25 | 26 | - name: Modify GUID columns 27 | set_fact: 28 | skip_guids_option: "" 29 | when: "skip_guids_column == 'false'" 30 | 31 | - name: PULL database 32 | block: 33 | - name: PULL > Export & gzip {{ env }} database 34 | shell: 'wp db export {{ sync_file }} --skip-plugins --skip-themes && gzip -q {{ sync_file }}' 35 | args: 36 | chdir: "{{ project_current }}" 37 | 38 | - name: PULL > Pull dump file from {{ env }} to development 39 | fetch: 40 | src: "{{ project_current }}/{{ sync_file }}.gz" 41 | dest: "{{ project_local_path }}/" 42 | flat: yes 43 | 44 | - name: PULL > Delete dump file from {{ env }} 45 | file: 46 | state: absent 47 | path: "{{ project_current }}/{{ sync_file }}.gz" 48 | 49 | - name: PULL > Backup development database 50 | connection: local 51 | shell: vagrant ssh -- 'cd {{ project_current }} && wp db export {{ backup_file }} --skip-plugins --skip-themes && gzip -q {{ backup_file }}' 52 | 53 | - name: PULL > Reset development database 54 | connection: local 55 | shell: vagrant ssh -- 'cd {{ project_current }} && wp db reset --yes --skip-plugins --skip-themes' 56 | 57 | - name: PULL > Gunzip & import database dump on development 58 | connection: local 59 | shell: vagrant ssh -- 'cd {{ project_current }} && gunzip -q {{ sync_file }}.gz && wp db import {{ sync_file }} --skip-plugins --skip-themes' 60 | 61 | - name: PULL > Delete dump file on development 62 | connection: local 63 | file: 64 | state: absent 65 | path: "{{ project_local_path }}/{{ sync_file }}" 66 | 67 | - name: PULL > Replace urls "{{ url_env }}" => "{{ url_dev }}" on development 68 | connection: local 69 | shell: vagrant ssh -- "cd {{ project_current }} && wp search-replace '{{ url_env }}' '{{ url_dev }}' {{ skip_guids_option }} --all-tables --skip-plugins --skip-themes" 70 | 71 | - name: PULL > Replace protocol-relative urls "//{{ domain_env }}" => "//{{ domain_dev }}" on development 72 | connection: local 73 | shell: vagrant ssh -- "cd {{ project_current }} && wp search-replace '//{{ domain_env }}' '//{{ domain_dev }}' {{ skip_guids_option }} --all-tables --skip-plugins --skip-themes" 74 | 75 | - name: PULL > Replace emails "@{{ domain_env }}" => "@{{ domain_dev }}" on development 76 | connection: local 77 | shell: vagrant ssh -- "cd {{ project_current }} && wp search-replace '@{{ domain_env }}' '@{{ domain_dev }}' --all-tables --skip-plugins --skip-themes" 78 | 79 | - name: PULL > Replace Elementor urls "{{ url_env }}" => "{{ url_dev }}" on development 80 | connection: local 81 | shell: vagrant ssh -- "cd {{ project_current }} && wp cli has-command 'elementor replace_urls' && [ $? -eq 0 ] && wp elementor replace_urls '{{ url_env }}' '{{ url_dev }}' --skip-themes || cd ." 82 | 83 | - name: PULL > Cache flush on development 84 | connection: local 85 | shell: vagrant ssh -- "cd {{ project_current }} && wp cache flush" 86 | 87 | - name: PULL > Elementor flush CSS on development 88 | connection: local 89 | shell: vagrant ssh -- "cd {{ project_current }} && wp cli has-command 'elementor flush_css' && [ $? -eq 0 ] && wp elementor flush_css --skip-themes || cd ." 90 | when: mode is not defined or mode == "pull" 91 | 92 | - name: PUSH database 93 | block: 94 | - name: PUSH > Export & gzip development database 95 | connection: local 96 | shell: vagrant ssh -- 'cd {{ project_current }} && wp db export {{ sync_file }} --skip-plugins --skip-themes && gzip -q {{ sync_file }}' 97 | 98 | - name: PUSH > Push dump file from development to {{ env }} 99 | copy: 100 | src: "{{ project_local_path }}/{{ sync_file }}.gz" 101 | dest: "{{ project_current }}/" 102 | 103 | - name: PUSH > Delete dump file from development 104 | connection: local 105 | file: 106 | state: absent 107 | path: "{{ project_local_path }}/{{ sync_file }}.gz" 108 | 109 | - name: PUSH > Backup {{ env }} database 110 | shell: 'wp db export {{ backup_file }} --skip-plugins --skip-themes && gzip -q {{ backup_file }}' 111 | args: 112 | chdir: "{{ project_current }}" 113 | 114 | - name: PUSH > Reset {{ env }} database 115 | command: wp db reset --yes --skip-plugins --skip-themes 116 | args: 117 | chdir: "{{ project_current }}" 118 | 119 | - name: PUSH > Gunzip & import database dump on {{ env }} 120 | shell: 'gunzip -q {{ sync_file }}.gz && wp db import {{ sync_file }} --skip-plugins --skip-themes' 121 | args: 122 | chdir: "{{ project_current }}" 123 | 124 | - name: PUSH > Delete dump file on {{ env }} 125 | file: 126 | state: absent 127 | path: "{{ project_current }}/{{ sync_file }}" 128 | 129 | - name: PUSH > Replace urls "{{ url_dev }}" => "{{ url_env }}" on {{ env }} 130 | command: wp search-replace '{{ url_dev }}' '{{ url_env }}' {{ skip_guids_option }} --all-tables --skip-plugins --skip-themes 131 | args: 132 | chdir: "{{ project_current }}" 133 | 134 | - name: PUSH > Replace protocol-relative urls "//{{ domain_dev }}" => "//{{ domain_env }}" on {{ env }} 135 | command: wp search-replace '//{{ domain_dev }}' '//{{ domain_env }}' {{ skip_guids_option }} --all-tables --skip-plugins --skip-themes 136 | args: 137 | chdir: "{{ project_current }}" 138 | 139 | - name: PUSH > Replace emails "@{{ domain_dev }}" => "@{{ domain_env }}" on {{ env }} 140 | command: wp search-replace '@{{ domain_dev }}' '@{{ domain_env }}' --all-tables --skip-plugins --skip-themes 141 | args: 142 | chdir: "{{ project_current }}" 143 | 144 | - name: PUSH > Replace Elementor urls "{{ url_dev }}" => "{{ url_env }}" on {{ env }} 145 | shell: wp cli has-command 'elementor replace_urls' && [ $? -eq 0 ] && wp elementor replace_urls '{{ url_dev }}' '{{ url_env }}' --skip-themes || cd . 146 | args: 147 | chdir: "{{ project_current }}" 148 | 149 | - name: PUSH > Cache flush on {{ env }} 150 | command: wp cache flush 151 | args: 152 | chdir: "{{ project_current }}" 153 | 154 | - name: PUSH > Elementor flush CSS on {{ env }} 155 | shell: wp cli has-command 'elementor flush_css' && [ $? -eq 0 ] && wp elementor flush_css --skip-themes || cd . 156 | args: 157 | chdir: "{{ project_current }}" 158 | when: mode is defined and mode == "push" 159 | -------------------------------------------------------------------------------- /uploads.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Sync {{ site }} UPLOADS between development <-> {{ env }} environments 3 | hosts: web:&{{ env }} 4 | remote_user: "{{ web_user }}" 5 | 6 | vars: 7 | project: "{{ wordpress_sites[site] }}" 8 | project_current: "{{ www_root }}/{{ site }}/current" 9 | project_local_path: "{{ (lookup('env', 'USER') == 'vagrant') | ternary(project_current, project.local_path) }}" 10 | 11 | tasks: 12 | - name: Abort if environment variable is equal to development 13 | fail: 14 | msg: "ERROR: development is not a valid environment for this mode (you can't push/pull from development to development)." 15 | when: env == "development" 16 | 17 | - name: PULL uploads 18 | block: 19 | - name: Pull uploads from {{ env }} 20 | synchronize: 21 | src: "{{ project_current }}/web/app/uploads/" 22 | dest: "{{ project_local_path }}/web/app/uploads/" 23 | mode: pull 24 | recursive: yes 25 | rsync_opts: --exclude=.DS_Store 26 | 27 | - name: Elementor flush CSS on development 28 | connection: local 29 | shell: vagrant ssh -- "cd {{ project_current }} && wp cli has-command 'elementor flush_css' && [ $? -eq 0 ] && wp elementor flush_css --skip-themes || cd ." 30 | when: mode is not defined or mode == "pull" 31 | 32 | - name: PUSH uploads 33 | block: 34 | - name: Push uploads to {{ env }} 35 | synchronize: 36 | src: "{{ project_local_path }}/web/app/uploads/" 37 | dest: "{{ project_current }}/web/app/uploads/" 38 | mode: push 39 | recursive: yes 40 | rsync_opts: --exclude=.DS_Store 41 | 42 | - name: Elementor flush CSS on {{ env }} 43 | shell: wp cli has-command 'elementor flush_css' && [ $? -eq 0 ] && wp elementor flush_css --skip-themes || cd . 44 | args: 45 | chdir: "{{ project_current }}" 46 | when: mode is defined and mode == "push" 47 | --------------------------------------------------------------------------------