diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..17d725e --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Creame - Paco Toledo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..254967c --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# trellis-simple-sync +Ansible playbooks for Trellis database and uploads sync.\ +Just copy and run without any extra configuration. + +## Installation +1. Copy `database.yml` and `uploads.yml` files into Trellis root folder +2. Copy `bin/sync.sh` files into Trellis bin folder +3. (Optional) Add `*_db_backup.sql.gz` to your Bedrock `.gitignore` file + +## Usage +Run `./bin/sync.sh ` + +* Available `` options: `uploads`, `database` +* Available `` options: `push`, `pull` +* The `push` is for upload data from development and update selected environment, and the `pull` for download data from selected environment and update development. +* `uploads` sync is not destructive, it only adds or update new files, don't delete missing files. + +## Aliases +You can use alias for a shorter or more intuitive commands + +* staging - stag - s +* production - prod - p +* database - db +* uploads - media +* push - up +* pull - down + +Examples:\ +`./bin/sync.sh stag example.com db up`\ +`./bin/sync.sh prod example.com media down` + +## Notes +* Tested up to Ansible 2.6.1 +* For database sync the development vagrant VM must be powered on every time you run a command +* On every database command a `sitename_db_backup.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: + * You can add `*_db_backup.sql.gz` to your Bedrock `.gitignore` file + * Or you can comment `PULL > Backup development database` task on `database.yml` + +## Contribute +* Anyone is welcome to contribute to the plugin. +* Please merge (squash) all your changes into a single commit before you open a pull request. + +## License +MIT + +## Credits +© 2018 [Creame](https://crea.me). +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). + +Special thanks to [the Roots team](https://roots.io/about/) whose [Trellis](https://github.com/roots/trellis) make this project possible. diff --git a/bin/sync.sh b/bin/sync.sh new file mode 100755 index 0000000..7ee9ab5 --- /dev/null +++ b/bin/sync.sh @@ -0,0 +1,95 @@ +#!/bin/bash +shopt -s nullglob + +ENVIRONMENTS=( hosts/* ) +ENVIRONMENTS=( "${ENVIRONMENTS[@]##*/}" ) +NUM_ARGS=4 + +show_usage() { + echo "Usage: sync + + is the environment to deploy to ("staging", "production", etc) + is the WordPress site to deploy (name defined in "wordpress_sites") + is what we go to sync ("uploads" or "database") + is the sync mode ("pull" or "push") + +Available environments: +`( IFS=$'\n'; echo "${ENVIRONMENTS[*]}" )` + +Aliases: + staging - stag - s + production - prod - p + uploads - media + database - db + pull - down + push - up + +Examples: + sync staging example.com database push + sync production example.com db pull + sync staging example.com uploads pull + sync prod example.com media up +" +} + +[[ $# -lt NUM_ARGS ]] && { show_usage; exit 127; } + +for arg +do + [[ $arg = -h ]] && { show_usage; exit 0; } +done + +ENV="$1"; +SITE="$2"; +TYPE="$3"; +MODE="$4"; + +# allow use of abbreviations of environments +if [[ $ENV = p || $ENV = prod ]]; then + ENV="production" +elif [[ $ENV = s || $ENV = stag ]]; then + ENV="staging" +fi + +# allow use of alias of types +if [[ $TYPE = db ]]; then + TYPE="database" +elif [[ $TYPE = media ]]; then + TYPE="uploads" +fi + +# allow use of alias of modes +if [[ $MODE = down ]]; then + MODE="pull" +elif [[ $MODE = up ]]; then + MODE="push" +fi + +DATABASE_CMD="ansible-playbook database.yml -e env=$ENV -e site=$SITE -e mode=$MODE" +UPLOADS_CMD="ansible-playbook uploads.yml -e env=$ENV -e site=$SITE -e mode=$MODE" + +HOSTS_FILE="hosts/$ENV" + +if [[ ! -e $HOSTS_FILE ]]; then + echo "Error: '$ENV' is not a valid environment ($HOSTS_FILE does not exist)." + echo + echo "Available environments:" + ( IFS=$'\n'; echo "${ENVIRONMENTS[*]}" ) + exit 1 +fi + +if [[ $TYPE != "database" && $TYPE != "uploads" ]]; then + echo "Error: '$TYPE' is not a valid type (uploads or media, database or db)." + exit 1 +fi + +if [[ $MODE != "pull" && $MODE != "push" ]]; then + echo "Error: '$MODE' is not a valid sync mode (pull or down, push or up)." + exit 1 +fi + +if [[ $TYPE = database ]]; then + $DATABASE_CMD +else + $UPLOADS_CMD +fi diff --git a/database.yml b/database.yml new file mode 100644 index 0000000..28ef4dc --- /dev/null +++ b/database.yml @@ -0,0 +1,106 @@ +--- +- name: Sync {{ site }} DATABASE between development <-> {{ env }} environments + hosts: web:&{{ env }} + remote_user: "{{ web_user }}" + + vars: + project: "{{ wordpress_sites[site] }}" + project_current: "{{ www_root }}/{{ site }}/current" + project_local_path: "{{ (lookup('env', 'USER') == 'vagrant') | ternary(project_current, project.local_path) }}" + sync_file: "{{ site | regex_replace('\\.+', '_') }}_db_sync.sql.gz" + backup_file: "{{ site | regex_replace('\\.+', '_') }}_db_backup.sql.gz" + dev_host: "{{ groups['development'] | first }}" + url_dev: "{{ hostvars[dev_host].wordpress_sites[site].ssl.enabled | ternary('https', 'http') }}://{{ hostvars[dev_host].wordpress_sites[site].site_hosts.0.canonical }}" + url_env: "{{ project.ssl.enabled | ternary('https', 'http') }}://{{ project.site_hosts.0.canonical }}" + + tasks: + - name: Abort if environment variable is equal to development + fail: + msg: "ERROR: development is not a valid environment for this mode (you can't push/pull from development to development)." + when: env == "development" + + # PULL database + - block: + - name: PULL > Export {{ env }} database + shell: wp db export - | gzip > {{ sync_file }} + args: + chdir: "{{ project_current }}" + + - name: PULL > Pull dump file from {{ env }} to development + fetch: + src: "{{ project_current }}/{{ sync_file }}" + dest: "{{ project_local_path }}/" + flat: yes + + - name: PULL > Delete dump file from {{ env }} + file: + state: absent + path: "{{ project_current }}/{{ sync_file }}" + + - name: PULL > Backup development database + connection: local + shell: vagrant ssh -- 'cd {{ project_current }} && wp db export - | gzip > {{ backup_file }}' + + - name: PULL > Reset development database + connection: local + shell: vagrant ssh -- 'cd {{ project_current }} && wp db reset --yes' + + - name: PULL > Import database dump on development + connection: local + shell: vagrant ssh -- 'cd {{ project_current }} && gzip -c -d {{ sync_file }} | wp db import -' + + - name: PULL > Delete dump file from development + connection: local + file: + state: absent + path: "{{ project_local_path }}/{{ sync_file }}" + + - name: PULL > Search for {{ url_env }} and replace with {{ url_dev }} on development + connection: local + shell: vagrant ssh -- "cd {{ project_current }} && wp search-replace '{{ url_env }}' '{{ url_dev }}' --all-tables" + + when: mode is not defined or mode == "pull" + + # PUSH database + - block: + - name: PUSH > Export development database + connection: local + shell: vagrant ssh -- 'cd {{ project_current }} && wp db export - | gzip > {{ sync_file }}' + + - name: PUSH > Push dump file from development to {{ env }} + copy: + src: "{{ project_local_path }}/{{ sync_file }}" + dest: "{{ project_current }}/" + + - name: PUSH > Delete dump file from development + connection: local + file: + state: absent + path: "{{ project_local_path }}/{{ sync_file }}" + + - name: PUSH > Backup {{ env }} database + shell: wp db export - | gzip > {{ backup_file }} + args: + chdir: "{{ project_current }}" + + - name: PUSH > Reset {{ env }} database + command: wp db reset --yes + args: + chdir: "{{ project_current }}" + + - name: PUSH > Import database dump on {{ env }} + shell: gzip -c -d {{ sync_file }} | wp db import - + args: + chdir: "{{ project_current }}" + + - name: PUSH > Delete dump file from {{ env }} + file: + state: absent + path: "{{ project_current }}/{{ sync_file }}" + + - name: PUSH > Search for {{ url_dev }} and replace with {{ url_env }} on {{ env }} + command: wp search-replace '{{ url_dev }}' '{{ url_env }}' --all-tables + args: + chdir: "{{ project_current }}" + + when: mode is defined and mode == "push" diff --git a/uploads.yml b/uploads.yml new file mode 100644 index 0000000..1084bf5 --- /dev/null +++ b/uploads.yml @@ -0,0 +1,33 @@ +--- +- name: Sync {{ site }} UPLOADS between development <-> {{ env }} environments + hosts: web:&{{ env }} + remote_user: "{{ web_user }}" + + vars: + project: "{{ wordpress_sites[site] }}" + project_current: "{{ www_root }}/{{ site }}/current" + project_local_path: "{{ (lookup('env', 'USER') == 'vagrant') | ternary(project_current, project.local_path) }}" + + tasks: + - name: Abort if environment variable is equal to development + fail: + msg: "ERROR: development is not a valid environment for this mode (you can't push/pull from development to development)." + when: env == "development" + + - name: Pull uploads from {{ env }} + synchronize: + src: "{{ project_current }}/web/app/uploads/" + dest: "{{ project_local_path }}/web/app/uploads/" + mode: pull + recursive: yes + rsync_opts: --exclude=.DS_Store + when: mode is not defined or mode == "pull" + + - name: Push uploads to {{ env }} + synchronize: + src: "{{ project_local_path }}/web/app/uploads/" + dest: "{{ project_current }}/web/app/uploads/" + mode: push + recursive: yes + rsync_opts: --exclude=.DS_Store + when: mode is defined and mode == "push"