├── logs ├── .gitignore └── README.md ├── database ├── .gitignore └── README.md ├── projects ├── .gitignore └── README.md ├── core ├── images │ ├── mysql │ │ ├── Dockerfile │ │ └── configs │ │ │ └── my-001.cnf │ ├── proxy │ │ ├── Dockerfile │ │ └── configs │ │ │ └── nginx.conf │ └── base │ │ ├── configs │ │ ├── php.ini │ │ ├── www.conf │ │ └── supervisord.conf │ │ └── Dockerfile ├── configs │ └── default │ │ ├── configs │ │ ├── xdebug.ini │ │ ├── wp-config.php │ │ └── nginx.conf.template │ │ ├── Dockerfile │ │ └── docker-compose.yml ├── scripts │ ├── down │ ├── up │ ├── rebuild │ ├── destroy │ ├── helpers │ ├── db │ ├── wp │ ├── build │ └── docker └── docker-compose.yml ├── .gitignore ├── setups.template.yml ├── app_env ├── wpdev ├── LICENSE └── README.md /logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !README.md 4 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !README.md 4 | -------------------------------------------------------------------------------- /projects/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !README.md 4 | -------------------------------------------------------------------------------- /core/images/mysql/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mysql 2 | 3 | EXPOSE 3306 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | 4 | .data 5 | setups.yml 6 | mysql-secret.cnf 7 | core/configs/*/configs/nginx.conf 8 | -------------------------------------------------------------------------------- /projects/README.md: -------------------------------------------------------------------------------- 1 | ### WP Dev Projects 2 | 3 | This folder contain all of your actual project files listed in `setups.yml`. It may be empty until the build script is run. 4 | -------------------------------------------------------------------------------- /logs/README.md: -------------------------------------------------------------------------------- 1 | ### WP Dev Log Files 2 | 3 | This folder contain error logs for all projects setup in `setups.yml`. A separate folder is created for each project. 4 | It may be empty until the logs are created. 5 | -------------------------------------------------------------------------------- /database/README.md: -------------------------------------------------------------------------------- 1 | ### WP Dev Database Files 2 | 3 | This folder contain sql files for database provisioning. This is also the place where backup files are saved by default. 4 | It may be empty until some files are added or backups are created. 5 | -------------------------------------------------------------------------------- /core/configs/default/configs/xdebug.ini: -------------------------------------------------------------------------------- 1 | xdebug.remote_enable = on 2 | xdebug.remote_autostart = on 3 | xdebug.remote_connect_back = off 4 | xdebug.remote_handler = dbgp 5 | xdebug.profiler_enable = 0 6 | xdebug.profiler_output_dir = "/var/www" 7 | xdebug.remote_port = 9000 8 | -------------------------------------------------------------------------------- /core/images/proxy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM wpdev-base 2 | 3 | WORKDIR /var/www 4 | 5 | ADD configs/nginx.conf /etc/nginx/nginx.conf 6 | 7 | RUN chown -R www-data:www-data /var/www 8 | 9 | EXPOSE 80 10 | 11 | ENTRYPOINT ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf"] 12 | -------------------------------------------------------------------------------- /core/images/base/configs/php.ini: -------------------------------------------------------------------------------- 1 | expose_php=off 2 | display_errors=on 3 | log_errors=on 4 | allow_url_fopen=off 5 | allow_url_include=off 6 | max_execution_time=300 7 | max_input_time=30 8 | memory_limit=512M 9 | post_max_size=256M 10 | upload_max_filesize=256M 11 | disable_functions=exec,passthru,shell_exec,system,proc_open,popen,parse_ini_file,show_source 12 | cgi.force_redirect=on 13 | open_basedir="/var/www:/tmp" 14 | cgi.fix_pathinfo=0 15 | daemonize=yes 16 | catch_workers_output=yes 17 | -------------------------------------------------------------------------------- /core/scripts/down: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Stop all related containers. 4 | 5 | source core/scripts/helpers 6 | 7 | set_project_env 8 | 9 | info "Stopping containers for ${APP_NAME}" 10 | if [[ $(docker ps -aq --filter "label=group=${APP_NAME}") ]]; then 11 | docker stop $(docker ps -aq --filter "label=group=${APP_NAME}") 2>>${LOG_FILE} \ 12 | || error "Unable to stop containers." 13 | 14 | success "All containers for ${APP_NAME} were stopped successfully." "\n" 15 | else 16 | error "No containers to stop for ${APP_NAME}." 17 | fi 18 | -------------------------------------------------------------------------------- /core/scripts/up: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Start all related containers. 4 | 5 | source core/scripts/helpers 6 | 7 | set_project_env 8 | 9 | info "Starting containers for ${APP_NAME}" 10 | if [[ $(docker ps -aq --filter "label=group=${APP_NAME}") ]]; then 11 | docker start $(docker ps -aq --filter "label=group=${APP_NAME}") 2>>${LOG_FILE} || \ 12 | error "Unable to start core containers." 13 | 14 | success "All containers are up and running." "\n" 15 | else 16 | error "No containers to start for ${APP_NAME}. Try './wpdev build'" 17 | fi 18 | -------------------------------------------------------------------------------- /core/configs/default/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM wpdev-base 2 | 3 | WORKDIR /var/www 4 | 5 | ADD ./configs/nginx.conf /etc/nginx/nginx.conf 6 | 7 | RUN chown -R www-data:www-data /var/www 8 | 9 | # Setup xdebug 10 | ENV XDEBUGINI_PATH=/etc/php/7.0/fpm/conf.d/20-xdebug.ini 11 | COPY configs/xdebug.ini /tmp/xdebug.ini 12 | RUN cat /tmp/xdebug.ini >> $XDEBUGINI_PATH 13 | RUN echo "xdebug.remote_host="`/sbin/ip route|awk '/default/ { print $3 }'` >> $XDEBUGINI_PATH 14 | 15 | EXPOSE 80 16 | 17 | ENTRYPOINT ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf"] 18 | -------------------------------------------------------------------------------- /setups.template.yml: -------------------------------------------------------------------------------- 1 | --- 2 | website1: 3 | host: website1.dev 4 | source: git@git.org:user/project.git 5 | title: "Website 1 with all settings" 6 | admin_user: "admin" 7 | admin_password: "admin" 8 | admin_email: "admin@website1.com" 9 | plugins: 10 | - "plugin-slug-1 --activate" 11 | - "plugin-slug-2" 12 | themes: 13 | - "theme-slug-1 --activate" 14 | - "theme-slug-2" 15 | env: 16 | DB_NAME: website1_db 17 | 18 | website2: 19 | host: website2.dev 20 | title: "Website 2 with minimal settings." 21 | version: "4.5" 22 | env: 23 | DB_NAME: website2_db 24 | -------------------------------------------------------------------------------- /core/images/base/configs/www.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | pid = /run/php/php7.0-fpm.pid 3 | error_log = /var/log/php7.0-fpm.log 4 | syslog.facility = daemon 5 | syslog.ident = php-fpm 6 | log_level = notice 7 | daemonize = yes 8 | 9 | [www] 10 | user = www-data 11 | group = www-data 12 | listen = /var/run/php/php7.0-fpm.sock 13 | listen.owner = www-data 14 | listen.group = www-data 15 | pm = dynamic 16 | pm.max_children = 500 17 | pm.start_servers = 2 18 | pm.min_spare_servers = 1 19 | pm.max_spare_servers = 3 20 | pm.process_idle_timeout = 10s; 21 | pm.max_requests = 500 22 | pm.status_path = /php-fpm/status 23 | slowlog = syslog 24 | request_slowlog_timeout = 5 25 | clear_env = no 26 | request_terminate_timeout = 30 27 | -------------------------------------------------------------------------------- /core/images/mysql/configs/my-001.cnf: -------------------------------------------------------------------------------- 1 | # MariaDB database server configuration file. 2 | 3 | [mysqld] 4 | port = 3306 5 | basedir = /usr 6 | tmpdir = /tmp 7 | 8 | max_connections = 100 9 | connect_timeout = 5 10 | wait_timeout = 600 11 | max_allowed_packet = 16M 12 | thread_cache_size = 128 13 | sort_buffer_size = 4M 14 | bulk_insert_buffer_size = 16M 15 | tmp_table_size = 32M 16 | max_heap_table_size = 32M 17 | 18 | query_cache_limit = 128K 19 | query_cache_size = 64M 20 | 21 | # Enable the slow query log to see queries with especially long duration 22 | slow_query_log_file = /var/log/mysql/mariadb-slow.log 23 | long_query_time = 10 24 | 25 | # Allow server to accept connections on all interfaces. 26 | bind-address = 0.0.0.0 27 | 28 | [mysqldump] 29 | max_allowed_packet = 256M 30 | -------------------------------------------------------------------------------- /app_env: -------------------------------------------------------------------------------- 1 | #!/usr/bin 2 | 3 | # WP Dev 4 | export APP_NAME="wpdev" 5 | export APP_PORT="80" 6 | export NETWORK_NAME="wpdev_network" 7 | export COMPOSE_FILE="docker-compose.yml" 8 | export LOG_FILE="${PWD}/logs/${APP_NAME}.logs" 9 | 10 | # Default WordPress Settings 11 | export WP_DEFAULT_TITLE="WordPress" # Site title 12 | export WP_ADMIN_USER="admin" 13 | export WP_ADMIN_PASSWORD="admin" 14 | export WP_ADMIN_EMAIL="admin@local.dev" 15 | 16 | # MySQL Database 17 | export MYSQL_PORT="3306" 18 | export MYSQL_ROOT_PASSWORD="wpdev" 19 | export DB_HOST="wpdev-mysql" 20 | export DB_USER="wpdev" 21 | export DB_PASSWORD="wpdev" 22 | 23 | # Redis 24 | export REDIS_PORT="6379" 25 | 26 | # phpmyadmin or adminer 27 | export MYSQL_CLIENT="phpmyadmin" 28 | export MYSQL_CLIENT_PORT="8080" 29 | 30 | # xdebug 31 | export HOST_IP=192.168.1.2 # Your machine ip address for xdebug connection 32 | -------------------------------------------------------------------------------- /core/configs/default/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | www: 5 | build: . 6 | container_name: ${VIRTUAL_HOST} 7 | volumes: 8 | - ../../../projects/${PROJECT_NAME}:/var/www 9 | - ../../../.data/composer:/root/.composer 10 | - ../../../.data/npm:/root/.npm 11 | - ../../../.data/wp-cli:/root/.wp-cli 12 | - ../../../logs/${VIRTUAL_HOST}:/var/log/nginx 13 | external_links: 14 | - "${APP_NAME}-mysql" 15 | - "${APP_NAME}-redis" 16 | environment: 17 | - DB_HOST=${DB_HOST} 18 | - DB_USER=${DB_USER} 19 | - DB_PASSWORD=${DB_PASSWORD} 20 | - DB_NAME=${DB_NAME} 21 | - XDEBUG_CONFIG=remote_host=${HOST_IP} 22 | - PHP_IDE_CONFIG=serverName=${VIRTUAL_HOST} 23 | labels: 24 | group: ${APP_NAME} 25 | type: project 26 | 27 | networks: 28 | default: 29 | external: 30 | name: ${NETWORK_NAME} 31 | -------------------------------------------------------------------------------- /wpdev: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | readonly CMD_PATH="./core/scripts/" 4 | 5 | function usage 6 | { 7 | echo " 8 | Usage: wpdev 9 | 10 | Build all images and projects by default. 11 | 12 | Commands: 13 | up Start all related containers 14 | down Stop all related containers 15 | db Perform database related operations 16 | build [--no-cache] [-h] Build the infra 17 | destroy [--force] [-f] Destroy the infra 18 | rebuild [--no-cache] [-h] Rebuild the infra 19 | " 20 | } 21 | 22 | case $1 in 23 | up ) 24 | ${CMD_PATH}up 25 | ;; 26 | 27 | down ) 28 | ${CMD_PATH}down 29 | ;; 30 | 31 | build ) 32 | ${CMD_PATH}build "${@:2}" 33 | ;; 34 | 35 | destroy ) 36 | ${CMD_PATH}destroy "${@:2}" 37 | ;; 38 | 39 | rebuild ) 40 | ${CMD_PATH}rebuild "${@:2}" 41 | ;; 42 | 43 | db ) 44 | ${CMD_PATH}db "${@:2}" 45 | ;; 46 | 47 | * ) 48 | usage 49 | ;; 50 | esac 51 | -------------------------------------------------------------------------------- /core/scripts/rebuild: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Rebuild script. 4 | 5 | source core/scripts/helpers 6 | source core/scripts/docker 7 | 8 | set_project_env 9 | 10 | function usage 11 | { 12 | echo " 13 | Usage: rebuild 14 | 15 | Remove and rebuild all core images and projects by default. 16 | 17 | Options: 18 | -h | --help Display this help text 19 | --no-cache Rebuild all skipping cache 20 | 21 | Commands: 22 | projects \"\" Rebuild given projects. Use \"all\" to rebuild all projects 23 | " 24 | } 25 | 26 | case $1 in 27 | -h | --help ) usage 28 | exit 0 29 | ;; 30 | 31 | --no-cache ) info "Starting rebuild process using --no-cache option" 32 | rebuild_without_cache 33 | ;; 34 | 35 | projects ) info "Rebuilding projects" 36 | rebuild_projects "${@:2}" 37 | ;; 38 | 39 | * ) info "Starting rebuild process" 40 | rebuild_with_cache 41 | ;; 42 | esac 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017, Ankit Pokhrel 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /core/images/base/configs/supervisord.conf: -------------------------------------------------------------------------------- 1 | [unix_http_server] 2 | file=/dev/shm/supervisor.sock 3 | 4 | [supervisord] 5 | logfile=/tmp/supervisord.log 6 | logfile_maxbytes=50MB 7 | logfile_backups=10 8 | loglevel=info 9 | pidfile=/tmp/supervisord.pid 10 | nodaemon=false 11 | minfds=1024 12 | minprocs=200 13 | user=root 14 | 15 | [rpcinterface:supervisor] 16 | supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface 17 | 18 | [supervisorctl] 19 | serverurl=unix:///dev/shm/supervisor.sock ; use a unix:// URL for a unix socket 20 | 21 | [program:php-fpm] 22 | command=/usr/sbin/php-fpm7.0 -F 23 | autostart=true 24 | autorestart=true 25 | priority=5 26 | stdout_events_enabled=true 27 | stderr_events_enabled=true 28 | stdout_logfile=/dev/stdout 29 | stdout_logfile_maxbytes=0 30 | stderr_logfile=/dev/stderr 31 | stderr_logfile_maxbytes=0 32 | 33 | [program:nginx] 34 | command=/usr/sbin/nginx 35 | autostart=true 36 | autorestart=true 37 | priority=10 38 | stdout_events_enabled=true 39 | stderr_events_enabled=true 40 | stdout_logfile=/dev/stdout 41 | stdout_logfile_maxbytes=0 42 | stderr_logfile=/dev/stderr 43 | stderr_logfile_maxbytes=0 44 | -------------------------------------------------------------------------------- /core/scripts/destroy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Remove all containers and networks. 4 | 5 | source core/scripts/helpers 6 | 7 | set_project_env 8 | 9 | function destroy 10 | { 11 | info "Removing containers for ${APP_NAME}" 12 | if [[ $(docker ps -aq --filter "label=group=${APP_NAME}") ]]; then 13 | docker rm -f $(docker ps -aq --filter "label=group=${APP_NAME}") 2>>${LOG_FILE} \ 14 | || error "Unable to remove containers." 15 | else 16 | error "No containers to remove for ${APP_NAME}." 17 | fi 18 | 19 | info "Removing network ${NETWORK_NAME}" "\n" 20 | (docker network ls -f name=${NETWORK_NAME} | grep ${NETWORK_NAME}) > /dev/null \ 21 | && docker network rm ${NETWORK_NAME} > /dev/null 22 | 23 | success "All containers and network for ${APP_NAME} were removed successfully." 24 | } 25 | 26 | function confirm_destroy_action 27 | { 28 | confirm_action "Are you sure you want to destroy ${APP_NAME} setup? [y/n] " 29 | } 30 | 31 | if [[ $# == 0 ]]; then 32 | confirm_destroy_action 33 | destroy 34 | else 35 | if [[ "$1" == "-f" || "$1" == "--force" ]]; then 36 | destroy 37 | else 38 | confirm_destroy_action 39 | destroy 40 | fi 41 | fi 42 | -------------------------------------------------------------------------------- /core/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | www: 5 | image: wpdev-base 6 | container_name: wpdev-base 7 | labels: 8 | tag: wpdev-base 9 | group: ${APP_NAME} 10 | type: image 11 | 12 | proxy: 13 | image: wpdev-proxy 14 | container_name: wpdev-proxy 15 | ports: 16 | - "${APP_PORT}:80" 17 | labels: 18 | tag: ${APP_NAME}-proxy 19 | group: ${APP_NAME} 20 | type: image 21 | 22 | mysqldb: 23 | image: wpdev-mysql 24 | container_name: wpdev-mysql 25 | restart: always 26 | environment: 27 | MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} 28 | MYSQL_USER: ${DB_USER} 29 | MYSQL_PWD: ${DB_PASSWORD} 30 | ports: 31 | - "${MYSQL_PORT}:3306" 32 | volumes: 33 | - ../.data/mysql:/var/lib/mysql 34 | - ./images/mysql/configs:/etc/mysql/conf.d 35 | - ../database:/tmp/migrations 36 | labels: 37 | tag: ${APP_NAME}-mysql 38 | group: ${APP_NAME} 39 | type: image 40 | 41 | redis: 42 | image: redis 43 | container_name: wpdev-redis 44 | restart: always 45 | ports: 46 | - "${REDIS_PORT}:6379" 47 | volumes: 48 | - ../.data/redis:/data 49 | labels: 50 | tag: ${APP_NAME}-redis 51 | group: ${APP_NAME} 52 | type: image 53 | 54 | networks: 55 | default: 56 | external: 57 | name: ${NETWORK_NAME} 58 | -------------------------------------------------------------------------------- /core/scripts/helpers: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Helper functions. 4 | 5 | function get_yaml_value 6 | { 7 | cat setups.yml | shyaml get-value $1 8 | } 9 | 10 | function get_yaml_values 11 | { 12 | cat setups.yml | shyaml get-values $1 13 | } 14 | 15 | function set_project_env 16 | { 17 | set -a 18 | . "app_env" 19 | set +a 20 | } 21 | 22 | function confirm_action 23 | { 24 | read -p "$1" -n 1 -r 25 | 26 | echo 27 | if [[ ! $REPLY =~ ^[y]$ ]]; then 28 | warning "Operation aborted." 29 | exit 0 30 | fi 31 | } 32 | 33 | function get_value 34 | { 35 | local key=$1 36 | local default=$2 37 | 38 | local value=$(get_yaml_value ${key} 2> /dev/null) 39 | 40 | if [[ -z "${value}" ]]; then 41 | value="${default}" 42 | fi 43 | 44 | echo ${value} 45 | } 46 | 47 | function log 48 | { 49 | local level=$1 50 | local msg=$2 51 | local lf=$3 52 | local bold=$(tput bold) 53 | local normal=$(tput sgr0) 54 | 55 | case "$level" in 56 | error) color_code='91'; ;; 57 | info) color_code='31'; ;; 58 | success) color_code='32'; ;; 59 | warning) color_code='36'; ;; 60 | *) color_code='39'; ;; 61 | esac 62 | 63 | local reset="\e[0m" 64 | local color="\e[0;${color_code}m" 65 | 66 | if [[ "$OSTYPE" == "darwin"* ]]; then 67 | reset="\x1B[0m" 68 | color="\x1B[0;${color_code}m" 69 | fi 70 | 71 | local level="$(tr '[:lower:]' '[:upper:]' <<< ${level:0:1})${level:1}" 72 | 73 | echo -e "${lf}${color}${bold}${level}${normal}: ${msg}${reset}" 74 | } 75 | 76 | function info 77 | { 78 | log info "$1" "$2" 79 | } 80 | 81 | function warning 82 | { 83 | log warning "$1" "$2" 84 | } 85 | 86 | function success 87 | { 88 | log success "$1" "$2" 89 | } 90 | 91 | function error 92 | { 93 | log error "[$(date +'%Y-%m-%dT%H:%M:%S%z')] $1" "$2" 1>&2 94 | 95 | exit 1; 96 | } 97 | -------------------------------------------------------------------------------- /core/scripts/db: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Database based operations. 4 | 5 | source core/scripts/helpers 6 | 7 | set_project_env 8 | 9 | function usage 10 | { 11 | echo " 12 | Usage: db 13 | 14 | Perform database related operations. 15 | 16 | Commands: 17 | backup -d \"\" [-p ] Backup specific database in given path 18 | backup_all [-p ] Backup all database 19 | " 20 | } 21 | 22 | function backup_all 23 | { 24 | local now=$(date +"%Y-%m-%d-%H:%M:%S") 25 | local download_path="./database/bkp-${now}.sql" 26 | 27 | while getopts "p:" arg; do 28 | case ${arg} in 29 | p ) download_path=${OPTARG} 30 | ;; 31 | esac 32 | done 33 | 34 | info "Creating backup for all databases" 35 | docker exec wpdev-mysql bash -c "exec mysqldump --all-databases" > ${download_path} || \ 36 | error "Oops! Something went wrong." 37 | 38 | success "Database dump saved: ${download_path}" 39 | } 40 | 41 | function backup 42 | { 43 | local database="" 44 | local now=$(date +"%Y-%m-%d-%H:%M:%S") 45 | local download_path="./database/bkp-${now}.sql" 46 | 47 | while getopts "d:p:" arg; do 48 | case ${arg} in 49 | d ) database=${OPTARG} 50 | ;; 51 | p ) download_path=${OPTARG} 52 | ;; 53 | esac 54 | done 55 | 56 | if [[ -z "${database}" ]]; then 57 | usage 58 | error "Database name list is required" 59 | exit 1 60 | fi 61 | 62 | info "Creating backup for ${database}" 63 | docker exec wpdev-mysql bash -c "exec mysqldump --databases ${database}" \ 64 | > ${download_path} || \ 65 | error "Oops! Looks like only partial backup was created. Please correct the errors." 66 | 67 | success "Database dump for ${database} saved: ${download_path}" 68 | } 69 | 70 | case $1 in 71 | backup ) backup "${@:2}" 72 | ;; 73 | 74 | backup_all ) backup_all "${@:2}" 75 | ;; 76 | 77 | * ) usage 78 | exit 0 79 | ;; 80 | esac 81 | -------------------------------------------------------------------------------- /core/images/proxy/configs/nginx.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | worker_processes 2; 3 | daemon off; 4 | 5 | events { 6 | worker_connections 5000; 7 | multi_accept on; 8 | } 9 | 10 | worker_rlimit_nofile 20000; 11 | 12 | http { 13 | sendfile on; 14 | tcp_nopush on; 15 | tcp_nodelay on; 16 | keepalive_requests 100; 17 | keepalive_timeout 30; 18 | types_hash_max_size 2048; 19 | server_tokens off; 20 | client_max_body_size 128M; 21 | 22 | include /etc/nginx/mime.types; 23 | default_type text/plain; 24 | 25 | log_format nginx '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" - $request_time X-Forwarded-For=$http_x_forwarded_for Host=$host'; 26 | 27 | access_log /dev/stdout nginx; 28 | error_log /dev/stderr warn; 29 | 30 | gzip on; 31 | gzip_disable "msie6"; 32 | 33 | gzip_vary on; 34 | gzip_proxied any; 35 | gzip_comp_level 6; 36 | gzip_buffers 16 8k; 37 | gzip_http_version 1.1; 38 | gzip_types 39 | application/atom+xml 40 | application/javascript 41 | application/json 42 | application/rss+xml 43 | application/vnd.ms-fontobject 44 | application/x-font-ttf 45 | application/x-web-app-manifest+json 46 | application/xhtml+xml 47 | application/xml 48 | font/opentype 49 | image/svg+xml 50 | image/x-icon 51 | image/png 52 | image/gif 53 | image/jpeg 54 | image/webp 55 | text/css 56 | text/plain 57 | text/x-component; 58 | 59 | server { 60 | listen 80; 61 | server_name _; 62 | root /dev/null; 63 | 64 | proxy_http_version 1.1; 65 | proxy_set_header Host $host; 66 | proxy_set_header Referer $http_referer; 67 | proxy_set_header X-Real-IP $remote_addr; 68 | proxy_set_header X-Forwarded-Host $server_name; 69 | proxy_set_header X-Forwarded-Proto http; 70 | proxy_set_header X-Forwarded-Port 80; 71 | proxy_read_timeout 30; 72 | 73 | location ~ /(robots.txt|favicon.ico) { 74 | access_log off; 75 | log_not_found off; 76 | } 77 | 78 | location / { 79 | resolver 127.0.0.11; 80 | proxy_pass http://$host$uri$is_args$args; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /core/scripts/wp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Handle all WordPress related commands. 4 | 5 | function continue_wordpress_setup 6 | { 7 | local project_name=$1 8 | local virtual_host=$2 9 | local project_path="projects/${project_name}" 10 | local host_sql_path="database/${DB_NAME}.sql" 11 | local sql_path="/tmp/migrations/${DB_NAME}.sql" 12 | 13 | cp core/configs/default/configs/wp-config.php ${project_path}/wp-config.php 14 | 15 | if [[ -r "${host_sql_path}" ]]; then 16 | info "Importing ${host_sql_path}" 17 | 18 | docker exec -it wpdev-mysql bash -c "mysql ${DB_NAME} < ${sql_path}" || \ 19 | error "Unable to import ${DB_NAME}.sql" 20 | else 21 | install_core ${project_name} ${virtual_host} 22 | fi 23 | 24 | install_plugins ${project_name} ${virtual_host} 25 | install_themes ${project_name} ${virtual_host} 26 | } 27 | 28 | function install_core 29 | { 30 | local project_name=$1 31 | local virtual_host=$2 32 | 33 | local url=$(get_yaml_value ${project_name}.host) 34 | local title=$(get_value ${project_name}.title ${WP_DEFAULT_TITLE}) 35 | local admin_user=$(get_value ${project_name}.admin_user ${WP_ADMIN_USER}) 36 | local admin_password=$(get_value ${project_name}.admin_password ${WP_ADMIN_PASSWORD}) 37 | local admin_email=$(get_value ${project_name}.admin_email ${WP_ADMIN_EMAIL}) 38 | 39 | local install_cmd="wp core install --url=\"${url}\" --title=\"${title}\" --admin_user=\"${admin_user}\" --admin_password=\"${admin_password}\" --admin_email=\"${admin_email}\" --skip-email --allow-root"; 40 | 41 | info "Installing core" 42 | docker exec -it ${virtual_host} bash -c "${install_cmd}" 43 | } 44 | 45 | function install_plugins 46 | { 47 | local project_name=$1 48 | local virtual_host=$2 49 | local plugins=$(get_yaml_values ${project_name}.plugins 2> /dev/null) 50 | 51 | if [[ -n "${plugins}" ]]; then 52 | info "Installing plugins" 53 | 54 | IFS=$'\n' 55 | for plugin in ${plugins}; do 56 | docker exec -it ${virtual_host} bash -c "wp plugin install ${plugin} --allow-root" 57 | done 58 | unset IFS 59 | fi 60 | } 61 | 62 | function install_themes 63 | { 64 | local project_name=$1 65 | local virtual_host=$2 66 | local themes=$(get_yaml_values ${project_name}.themes 2> /dev/null) 67 | 68 | if [[ -n "${themes}" ]]; then 69 | info "Installing themes" 70 | 71 | IFS=$'\n' 72 | for theme in ${themes}; do 73 | docker exec -it ${virtual_host} bash -c "wp theme install ${theme} --allow-root" 74 | done 75 | unset IFS 76 | fi 77 | } 78 | -------------------------------------------------------------------------------- /core/scripts/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Build dev environment. 4 | 5 | source core/scripts/helpers 6 | source core/scripts/wp 7 | source core/scripts/docker 8 | 9 | set_project_env 10 | 11 | function usage 12 | { 13 | echo " 14 | Usage: build 15 | 16 | Build all images and projects by default. 17 | 18 | Options: 19 | -h | --help Display this help text 20 | 21 | Commands: 22 | projects \"\" Build given projects. Use \"all\" to build all projects. 23 | core [--no-cache] Build core images 24 | " 25 | } 26 | 27 | function build_core_images 28 | { 29 | info "Checking for network" 30 | create_network 31 | 32 | local cache=$1 33 | 34 | if [[ "${cache}" == "--no-cache" ]]; then 35 | info "Building base image with --no-cache option" 36 | build_base_image ${cache} 37 | 38 | info "Building proxy with --no-cache option" 39 | build_proxy ${cache} 40 | 41 | info "Building mysql with --no-cache option" 42 | build_mysql ${cache} 43 | else 44 | info "Building base image" 45 | build_base_image 46 | 47 | info "Building proxy" 48 | build_proxy 49 | 50 | info "Building mysql" 51 | build_mysql 52 | fi 53 | 54 | info "Shutting down old reference (if any)" 55 | docker-compose -p ${APP_NAME} -f core/${COMPOSE_FILE} down 2>>${LOG_FILE} || \ 56 | error "Unable to shutdown containers." 57 | 58 | info "Booting WP Dev" 59 | docker-compose -p ${APP_NAME} -f core/${COMPOSE_FILE} up -d --build --remove-orphans 2>>${LOG_FILE} || \ 60 | error "Problem in running main docker-compose file." 61 | 62 | success "Successfully built core images." 63 | } 64 | 65 | function build_projects 66 | { 67 | local projects=$1 68 | 69 | for project in ${projects}; do 70 | info "Setting up ${project}" 71 | 72 | virtual_host=$(get_yaml_value ${project}.host) 73 | 74 | boot_project ${project} ${virtual_host} 75 | provision_database ${DB_NAME} 76 | setup_project ${project} ${virtual_host} 77 | done 78 | 79 | success "Successfully built projects." 80 | } 81 | 82 | function build_all 83 | { 84 | build_core_images $1 85 | 86 | local projects=$(cat setups.yml | shyaml keys) 87 | build_projects "${projects}" 88 | 89 | run_mysql_client 90 | } 91 | 92 | case $1 in 93 | -h | --help ) 94 | usage 95 | ;; 96 | 97 | projects ) 98 | if [[ "$2" == "all" ]]; then 99 | projects=$(cat setups.yml | shyaml keys) 100 | else 101 | projects="${@:2}" 102 | fi 103 | 104 | build_projects "${projects}" 105 | ;; 106 | 107 | core ) 108 | build_core_images $2 109 | ;; 110 | 111 | * ) 112 | build_all $1 113 | ;; 114 | esac 115 | -------------------------------------------------------------------------------- /core/images/base/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | MAINTAINER Ankit Pokhrel 3 | 4 | ENV LANG C.UTF-8 5 | ENV DEBIAN_FRONTEND noninteractive 6 | 7 | RUN ln -sf /usr/share/zoneinfo/Asia/Kathmandu /etc/localtime 8 | 9 | RUN rm -rf /var/lib/apt/lists/* && apt-get clean 10 | RUN apt-get update --fix-missing && apt-get -y upgrade 11 | 12 | # Install php, nginx and other dependencies 13 | RUN apt-get -y install rsyslog \ 14 | rsyslog-gnutls \ 15 | supervisor \ 16 | nginx \ 17 | curl \ 18 | git \ 19 | php7.0-fpm \ 20 | php7.0-cli \ 21 | php7.0-gd \ 22 | php7.0-imap \ 23 | php7.0-intl \ 24 | php7.0-json \ 25 | php7.0-mcrypt \ 26 | php7.0-mbstring \ 27 | php7.0-ldap \ 28 | php7.0-zip \ 29 | php7.0-xml \ 30 | php-xdebug \ 31 | php7.0-mysql \ 32 | php7.0-soap \ 33 | php7.0-curl && \ 34 | apt-get clean && \ 35 | rm -rf /var/lib/apt/lists/* 36 | 37 | # Install composer 38 | RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && chmod +x /usr/local/bin/composer 39 | 40 | # Install node 41 | RUN curl -sL https://deb.nodesource.com/setup_6.x -o /tmp/nodesource_setup.sh 42 | RUN bash /tmp/nodesource_setup.sh 43 | RUN apt-get -y install nodejs build-essential 44 | RUN node --version && npm --version 45 | 46 | # Install wp-cli 47 | RUN curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && chmod +x wp-cli.phar && mv wp-cli.phar /usr/local/bin/wp 48 | RUN wp --info --allow-root 49 | 50 | # Install PHP_Codesniffer 51 | RUN curl -OL https://github.com/squizlabs/PHP_CodeSniffer/releases/download/2.9.1/phpcs.phar && chmod +x phpcs.phar && mv phpcs.phar /usr/local/bin/phpcs 52 | RUN curl -OL https://github.com/squizlabs/PHP_CodeSniffer/releases/download/2.9.1/phpcbf.phar && chmod +x phpcbf.phar && mv phpcbf.phar /usr/local/bin/phpcbf 53 | RUN phpcs --version 54 | 55 | # Install WP Codesniffer 56 | RUN git clone -b master https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards.git /tmp/wpcs 57 | RUN phpcs --config-set installed_paths /tmp/wpcs 58 | 59 | # Add configs 60 | ADD ./configs/www.conf /etc/php/7.0/fpm/pool.d/www.conf 61 | ADD ./configs/php.ini /etc/php/7.0/fpm/conf.d/99-custom.ini 62 | ADD ./configs/supervisord.conf /etc/supervisord.conf 63 | 64 | WORKDIR /var/www 65 | 66 | RUN mkdir /var/run/php/ 67 | 68 | EXPOSE 80 69 | -------------------------------------------------------------------------------- /core/configs/default/configs/wp-config.php: -------------------------------------------------------------------------------- 1 | )_KmvaEskeTMM67wm)AV'); 50 | define('SECURE_AUTH_KEY', '7h5Wj^Nafo7GU2C$pEf4|+*OfZ05}Bw;`RZbR`#k:ERUeBRkAqD{O@Wk/7jcyn~Z'); 51 | define('LOGGED_IN_KEY', 'cbpaj/X^?^7jeUR8,gF]R(E$XfItH>|Fxi*Nk@}rX+N-:pn5QIw'); 52 | define('NONCE_KEY', 'EFGI 1?|NYgT9yeSdiX^IVGkc&#g;7wkTJ&=tj1)3^cnDkc;yfoQXo]fOUJRol9w'); 53 | define('AUTH_SALT', 'fl|1{,K_Q27XOGJtM.V^}MF67E4Tj*C_2DI,#rQIO@mz8o][T?!VgL$tow*@apV2'); 54 | define('SECURE_AUTH_SALT', 'vDZeGy?/^E~ggx7YDOF-A)U o%zG]9`X|,Z??A{E< UEf~opx9f0mKE tB h*w%)'); 55 | define('LOGGED_IN_SALT', 'M>zoa/Anz/R>BGOl|pi6-7]+@gT[;cm1OaJ ,`l&}; 62 | root /var/www; 63 | index index.php index.html; 64 | 65 | location = /favicon.ico { 66 | log_not_found off; 67 | access_log off; 68 | } 69 | 70 | location = /robots.txt { 71 | allow all; 72 | log_not_found off; 73 | access_log off; 74 | } 75 | 76 | # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac). 77 | # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban) 78 | location ~ /\. { 79 | return 404; 80 | } 81 | 82 | # Deny access to any files with a .php extension in the uploads directory 83 | # Works in sub-directory installs and also in multisite network 84 | # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban) 85 | location ~* /(?:uploads|files)/.*\.php$ { 86 | deny all; 87 | } 88 | 89 | location / { 90 | try_files $uri $uri/ /index.php?q=$uri&$args; 91 | } 92 | 93 | location ~ \.php$ { 94 | try_files $uri =404; 95 | 96 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 97 | fastcgi_index index.php; 98 | 99 | include fastcgi_params; 100 | 101 | fastcgi_intercept_errors on; 102 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 103 | fastcgi_pass unix:/var/run/php/php7.0-fpm.sock; 104 | fastcgi_read_timeout 30; 105 | } 106 | 107 | location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { 108 | expires max; 109 | log_not_found off; 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /core/scripts/docker: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Handle all docker related commands. 4 | 5 | function create_network 6 | { 7 | (docker network ls -f name=${NETWORK_NAME} | grep ${NETWORK_NAME}) > /dev/null || \ 8 | docker network create ${NETWORK_NAME} 2>>${LOG_FILE} || \ 9 | error "Failed to create network" 10 | } 11 | 12 | function build_base_image 13 | { 14 | local base_path="core/images/base/"; 15 | 16 | docker build $1 -t wpdev-base \ 17 | -f ${base_path}Dockerfile ${base_path} 2>>${LOG_FILE} || \ 18 | error "Failed to build docker image for base" 19 | } 20 | 21 | function build_proxy 22 | { 23 | local base_path="core/images/proxy/"; 24 | 25 | docker build $1 -t wpdev-proxy \ 26 | -f ${base_path}Dockerfile ${base_path} 2>>${LOG_FILE} || \ 27 | error "Failed to build docker image for proxy" 28 | } 29 | 30 | function build_mysql 31 | { 32 | local base_path="core/images/mysql/"; 33 | 34 | docker build $1 -t wpdev-mysql \ 35 | -f ${base_path}Dockerfile ${base_path} 2>>${LOG_FILE} || \ 36 | error "Failed to build docker image for mysql" 37 | } 38 | 39 | function rebuild_with_cache 40 | { 41 | confirm_action "Are you sure you want to rebuild ${APP_NAME} setup? [y/n] " 42 | 43 | core/scripts/destroy -f 44 | core/scripts/build 45 | } 46 | 47 | function rebuild_without_cache 48 | { 49 | confirm_action "Are you sure you want to rebuild ${APP_NAME} setup? [y/n] " 50 | 51 | core/scripts/destroy -f 52 | core/scripts/build --no-cache 53 | } 54 | 55 | function rebuild_projects 56 | { 57 | local projects=$1 58 | 59 | if [[ -z "${projects}" ]]; then 60 | error "Project list required" 61 | fi 62 | 63 | confirm_action "Are you sure you want to rebuild ${APP_NAME} setup? [y/n] " 64 | 65 | docker rm -f $(docker ps -aq --filter "label=type=project") 2>>${LOG_FILE} || \ 66 | error "Unable to remove project containers." 67 | 68 | core/scripts/build projects "${projects}" 69 | } 70 | 71 | function boot_project 72 | { 73 | local virtual_host=$2 74 | 75 | export PROJECT_NAME=$1 76 | 77 | local project_config_path="core/configs/${PROJECT_NAME}/" 78 | 79 | if [[ ! -f ${project_config_path}${COMPOSE_FILE} ]]; then 80 | warning "docker-compose.yml file not found for ${PROJECT_NAME}." 81 | 82 | project_config_path="core/configs/default/" 83 | fi 84 | 85 | info "Running compose file ${project_config_path}${COMPOSE_FILE}" 86 | 87 | export DB_NAME=$(get_value ${PROJECT_NAME}.env.DB_NAME "${PROJECT_NAME}_db") 88 | export VIRTUAL_HOST=${virtual_host} 89 | 90 | cp "${project_config_path}configs/nginx.conf.template" "${project_config_path}configs/nginx.conf" 91 | sed -i '' -e "s//${VIRTUAL_HOST}/g" "${project_config_path}configs/nginx.conf" 92 | 93 | (cd ${project_config_path}; \ 94 | docker-compose -p "${virtual_host}" -f ${COMPOSE_FILE} down 2>>${LOG_FILE} && \ 95 | docker-compose -p "${virtual_host}" -f ${COMPOSE_FILE} up -d --build --remove-orphans 2>>${LOG_FILE} || \ 96 | error "Problem in setting up ${virtual_host}.") 97 | 98 | rm -f "${project_config_path}configs/nginx.conf" 99 | } 100 | 101 | function setup_project 102 | { 103 | local project_name=$1 104 | local virtual_host=$2 105 | local project_dir="./projects/" 106 | local project_path="${project_dir}${project_name}/" 107 | 108 | if [[ -f ${project_path}/wp-config.php ]]; then 109 | info "Skipping ${project_path} setup" 110 | return 111 | fi 112 | 113 | local source="$(get_yaml_value ${project_name}.source 2> /dev/null)" 114 | local clone="$(get_yaml_value ${project_name}.clone 2> /dev/null)" 115 | 116 | if [[ -n "${clone}" ]]; then 117 | source_project="${project_dir}${clone}/" 118 | source_db="$(get_yaml_value ${clone}.env.DB_NAME 2> /dev/null)" 119 | source_host="$(get_yaml_value ${clone}.host 2> /dev/null)" 120 | 121 | destination_db="$(get_yaml_value ${project_name}.env.DB_NAME 2> /dev/null)" 122 | destination_host="$(get_yaml_value ${project_name}.host 2> /dev/null)" 123 | 124 | echo "Copying ${source_project} to ${project_path}" 125 | 126 | # clone project from local source 127 | cp -R ${source_project} ${project_path} 128 | 129 | # clone database 130 | docker exec wpdev-mysql bash -c "exec mysqldump ${source_db} | mysql ${destination_db}" || \ 131 | error "Couldn't clone database." 132 | 133 | # replace source host with destination host 134 | docker exec ${virtual_host} bash -c "wp search-replace ${source_host} ${destination_host} --allow-root" || \ 135 | warning "Unable to update url." 136 | elif [[ -n "${source}" ]]; then 137 | # clone project from git 138 | git clone --recursive ${source} ${project_path} 139 | else 140 | # download core 141 | local version="$(get_yaml_value ${project_name}.version 2> /dev/null)" 142 | if [[ -z "${version}" ]]; then 143 | version="latest" 144 | fi 145 | 146 | docker exec -it ${virtual_host} bash -c "wp core download --version=${version} --allow-root" 147 | fi 148 | 149 | # continue with the setup 150 | continue_wordpress_setup ${project_name} ${virtual_host} 151 | } 152 | 153 | function provision_database 154 | { 155 | # Wait for mysql to accept connections 156 | mysql_health_check 157 | 158 | local db_name=$1 159 | 160 | info "Creating database ${db_name}" 161 | 162 | local connection_string="mysql" 163 | local create="CREATE DATABASE IF NOT EXISTS ${db_name};" 164 | local grant="GRANT ALL PRIVILEGES ON ${db_name}.* TO \"${DB_USER}\"@'%' WITH GRANT OPTION;FLUSH PRIVILEGES;" 165 | 166 | docker exec -it wpdev-mysql bash -c "echo \"${create}${grant}\" | ${connection_string}" || \ 167 | error "Couldn't create database ${db_name}" 168 | } 169 | 170 | function mysql_health_check 171 | { 172 | info "Performing health check for mysql" 173 | 174 | local mysql_health_check="mysqladmin ping" 175 | local cmd=$(docker exec -it wpdev-mysql bash -c "${mysql_health_check}") 176 | 177 | while [[ "${cmd}" != *"mysqld is alive"* ]]; do 178 | info "Waiting for mysql to accept connections" 179 | sleep 5 180 | cmd=$(docker exec -it wpdev-mysql bash -c "${mysql_health_check}") 181 | done 182 | } 183 | 184 | function run_mysql_client 185 | { 186 | info "Starting ${MYSQL_CLIENT}" 187 | 188 | local container_name="wpdev-${MYSQL_CLIENT}" 189 | 190 | docker rm -f ${container_name} &> /dev/null 191 | 192 | if [[ ${MYSQL_CLIENT} == "adminer" ]]; then 193 | docker rm -f wpdev-phpmyadmin &> /dev/null 194 | docker run -d -p ${MYSQL_CLIENT_PORT}:80 --network ${NETWORK_NAME} --name ${container_name} \ 195 | --label group=${APP_NAME} clue/adminer 196 | else 197 | docker rm -f wpdev-adminer &> /dev/null 198 | docker run -d -p ${MYSQL_CLIENT_PORT}:80 --network ${NETWORK_NAME} -e PMA_HOSTS=wpdev-mysql \ 199 | --label group=${APP_NAME} --name ${container_name} phpmyadmin/phpmyadmin 200 | fi 201 | } 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## WP Dev 2 | Rapid dev environment using docker for modern WordPress development. 3 | 4 | > Please feel free to try and report any bug found. 5 | > Pull requests, issues, and project recommendations are more than welcome! 6 | 7 | ## Overview 8 | WP Dev aims to setup dev environment for modern WordPress development using simple yaml configuration inside `setups.yml`. 9 | A basic setup will only have website name, host and database name. The build command will fetch latest WordPress, configure 10 | and make it ready for you to work further. If git `source` is provided, it will clone the project from given repo. The system 11 | is also capable of provisioning database, installing plugins and themes using `wp-cli` during project setup. 12 | ```yaml 13 | # Sample configuration 14 | website1: 15 | host: website1.dev 16 | env: 17 | DB_NAME: website1_db 18 | ``` 19 | 20 | ### What's included? 21 | 1. [PHP-fpm 7.0.x](https://php-fpm.org/) 22 | 2. [Nginx](http://nginx.org/) ([stable](https://www.nginx.com/blog/nginx-1-10-1-11-released/) version) 23 | 3. [Supervisor](http://supervisord.org/) 24 | 4. [MySQL 5.7.*](https://www.mysql.com/) 25 | 5. [Redis](https://redis.io/) 26 | 6. [WP-CLI](http://wp-cli.org/) 27 | 7. [Composer](https://getcomposer.org/) 28 | 8. [NodeJs 6.x](https://nodejs.org) 29 | 9. [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) & [WP Coding Standards](https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards) 30 | 10. [phpMyAdmin](https://www.phpmyadmin.net/) & [Adminer](https://www.adminer.org/) 31 | 32 | ### Prerequisites 33 | 1. Install docker: https://docs.docker.com/engine/installation/ 34 | 2. Make sure that you have `docker-compose` installed 35 | 36 | ```shell 37 | $ docker-compose --version 38 | docker-compose version: 1.11.2 39 | ``` 40 | 41 | If not, install `docker-compose`: https://docs.docker.com/v1.11/compose/install/ 42 | 43 | 3. Install shyaml: https://github.com/0k/shyaml#installation 44 | 4. Install `git` if you haven't already: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git 45 | 46 | ### Application level environments 47 | Application level environment variables are located inside `app_env`. Following values are configurable. 48 | ```shell 49 | # WP Dev 50 | export APP_NAME="wpdev" 51 | export APP_PORT="80" 52 | export NETWORK_NAME="wpdev_network" 53 | export LOG_FILE="${PWD}/logs/${APP_NAME}.logs" 54 | 55 | # Default WordPress Settings 56 | export WP_DEFAULT_TITLE="WordPress" # Site title 57 | export WP_ADMIN_USER="admin" 58 | export WP_ADMIN_PASSWORD="admin" 59 | export WP_ADMIN_EMAIL="admin@local.dev" 60 | 61 | # MySQL Database 62 | export MYSQL_PORT="3306" 63 | export MYSQL_ROOT_PASSWORD="wpdev" 64 | export DB_HOST="wpdev-mysql" # Name of your mysql container 65 | export DB_USER="wpdev" 66 | export DB_PASSWORD="wpdev" 67 | export MYSQL_CLIENT_PORT="8080" 68 | 69 | # Redis 70 | export REDIS_PORT="6379" 71 | 72 | # phpmyadmin or adminer 73 | export MYSQL_CLIENT="phpmyadmin" 74 | 75 | # xdebug 76 | export HOST_IP=192.168.1.2 # Your machine ip address for xdebug connection 77 | ``` 78 | 79 | ### Configure websites 80 | Copy or rename `setups.template.yml` to `setups.yml` to get started with the website setup. Website name, host and DB_NAME 81 | inside env are the only values required. Website name can only be alphanumeric and can contain hyphen/dash (-). 82 | ```yaml 83 | # Basic setup 84 | website1: 85 | host: website1.dev 86 | env: 87 | DB_NAME: website1_db 88 | 89 | # Complete entry with source as a git repo 90 | website2: 91 | host: website2.dev 92 | source: git@git.org:user/project.git 93 | title: "Website 2 with all settings" 94 | admin_user: "admin" 95 | admin_password: "admin" 96 | admin_email: "admin@website1.com" 97 | plugins: 98 | - "plugin-slug-1 --activate" 99 | - "plugin-slug-2" 100 | themes: 101 | - "theme-slug-1 --activate" 102 | - "theme-slug-2" 103 | env: 104 | DB_NAME: website2_db 105 | 106 | # WordPress 4.3 107 | website3: 108 | host: website3.dev 109 | version: 4.3 110 | env: 111 | DB_NAME: website3_db 112 | 113 | # Cloning from existing project 114 | website2_clone: 115 | clone: website2 116 | host: website2clone.local 117 | env: 118 | DB_NAME: website2_clone_db 119 | 120 | ``` 121 | 122 | ## Available options 123 | Option | Description | Default 124 | ---------------|------------------------------------------------------------------------------------------------------------------------------|----------------------- 125 | host | Host name. This entry should also be made inside `/etc/hosts` | 126 | source | Git source for the project to clone from | 127 | clone | Existing project to clone from | 128 | version | WordPress version | latest 129 | title | WordPress site title | defaults from `app_env` 130 | admin_user | WordPress admin user | defaults from `app_env` 131 | admin_password | WordPress admin password | defaults from `app_env` 132 | admin_email | WordPress admin email | defaults from `app_env` 133 | plugins | List of plugins to install during setup. You can provide all parameters that `wp-cli` accepts, eg: `--activate` or `--debug` | 134 | themes | List of themes to install during setup. You can provide all parameters that `wp-cli` accepts, eg: `--activate` or `--debug` | 135 | env | Environment variables like `DB_NAME`. `DB_NAME` is required. | 136 | 137 | ### Build 138 | You only need to run build script if you have made any changes in `setups.yml` file. 139 | ```shell 140 | $ ./wpdev build 141 | ``` 142 | 143 | To build core images only, use 144 | ```shell 145 | $ ./wpdev build core 146 | 147 | # To skip cache, use --no-cache option 148 | $ ./wpdev build core --no-cache 149 | ``` 150 | 151 | To build projects only, use 152 | ```shell 153 | $ ./wpdev build projects "" 154 | ``` 155 | 156 | You can access help file using `./wpdev build -h` 157 | 158 | ### Run project containers 159 | You can skip this step if you just build the projects using above step. The projects should already be up and running. 160 | From next time, you can just boot the containers using following command as the related project containers are already built. 161 | ```shell 162 | $ ./wpdev up 163 | ``` 164 | 165 | ### Update /etc/hosts 166 | Update `/etc/hosts` by adding all hosts listed in `setups.yml` 167 | ```shell 168 | 0.0.0.0 your-project.host 169 | ``` 170 | 171 | If you followed above steps correctly then, at this point, you should be able access project in your browser. Project files are 172 | located inside `projects` folder. 173 | 174 | ### Accessing database configs 175 | Database configs are stored in environment variables. In order to use it in say your project, use `getenv`. 176 | ``` 177 | /** The name of the database for WordPress */ 178 | define('DB_NAME', getenv('DB_NAME')); 179 | 180 | /** MySQL database username */ 181 | define('DB_USER', getenv('DB_USER')); 182 | 183 | /** MySQL database password */ 184 | define('DB_PASSWORD', getenv('DB_PASSWORD')); 185 | 186 | /** MySQL hostname */ 187 | define('DB_HOST', getenv('DB_HOST')); 188 | ``` 189 | 190 | ### Shutdown project containers 191 | You can shutdown running containers using following script. 192 | ```shell 193 | $ ./wpdev down 194 | ``` 195 | 196 | ### Clean or destroy 197 | You can delete all related containers using destroy script. 198 | ```shell 199 | $ ./wpdev destroy 200 | ``` 201 | 202 | ### Rebuild 203 | Rebuild will first delete all related containers and builds it again. It is same as running `./wpdev destroy && ./wpdev build`. 204 | ```shell 205 | $ ./wpdev rebuild -h 206 | usage: rebuild 207 | --no-cache Skip cache 208 | -h | --help Display this help text 209 | ``` 210 | 211 | ### Override project configuration 212 | To override configurations for a project, copy `core/configs/default` folder and name it to your website name you are 213 | using in `setups.yml`. You can now change required configurations for the project. The system will first check if the 214 | configuration for the website is available and builds it, if not, it will use default configuration. 215 | 216 | ### Volumes 217 | All volumes are mounted inside `.data` folder except the logs. Logs are mounted inside `logs` folder. 218 | 219 | - Composer: .data/composer 220 | - NPM: .data/npm 221 | - MySQL: .data/mysql 222 | - Redis: .data/redis 223 | - WP-CLI: .data/wp-cli 224 | 225 | ### Database provisioning and backup 226 | If you want to import database for a project during setup, just add a sql file with same name as your database name and the system 227 | will automatically import it during the build process. So for the basic setup above in [Configure websites](#configure-websites) 228 | section, you need to add a file called `website1_db` inside database folder. 229 | 230 | #### Backup all databases 231 | To backup all databases, use 232 | ```shell 233 | $ ./wpdev db backup_all 234 | ``` 235 | 236 | By default it will save backup to the `database` folder. You can provide custom path using `-p` option. 237 | ```shell 238 | $ ./wpdev db backup_all -p /path/to/downloads/bkp.sql 239 | ``` 240 | 241 | #### Backup specific databases 242 | To only backup some databases, use 243 | ```shell 244 | $ ./wpdev db backup -d "db1 db2" 245 | ``` 246 | 247 | Same as above it will save backup to the `database` folder. You can provide custom path using `-p` option. 248 | ```shell 249 | $ ./wpdev db backup -d "db1 db2" -p /path/to/downloads/bkp.sql 250 | ``` 251 | 252 | ### MySQL Client 253 | You can choose between `phpmyadmin` or `adminer` as your mysql client. By default the client runs on port `8080`. 254 | These values are configurable in `app_env`. So with default configuration, you can access the client by visiting 255 | http://localhost:8080. You can use the values `DB_USER` and `DB_PASSWORD` from `app_env` to login. 256 | 257 | > To connect database using third party clients like sequel pro, use `0.0.0.0` as a host. 258 | > `DB_HOST` is the ip of your mysql container which is `wpdev-mysql` by default. 259 | 260 | ### Running WP CodeSniffer 261 | You can enter into container and run: 262 | ```shell 263 | $ phpcs --standard=WordPress /path/to/file/or/folder 264 | 265 | # to fix 266 | $ phpcbf --standard=WordPress /path/to/file/or/folder 267 | ``` 268 | 269 | Or, execute it from outside 270 | ```shell 271 | $ docker exec -it bash -c "phpcs --standard=WordPress /path/to/file/or/folder" 272 | 273 | # to fix 274 | $ docker exec -it bash -c "phpcbf --standard=WordPress /path/to/file/or/folder" 275 | ``` 276 | 277 | ### Xdebug Remote Connection 278 | To debug with xdebug using IDE like PHPStorm, follow following steps. 279 | 1. Update your machine ip (`HOST_IP`) in `app_env`. 280 | 2. Use your host name (container name) as a server name. 281 | 3. Use `localhost` as `host` and `APP_PORT` as port. 282 | 4. Start listening to PHP Debug Connections from your IDE. 283 | 284 | ### Todo 285 | - [x] Add NodeJs and npm support. 286 | - [x] Add PHP_CodeSniffer for [WordPress](https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards). 287 | - [x] Setup [Xdebug](https://xdebug.org/) remote connection. 288 | - [ ] Ability to sync/update plugins, themes, core etc for a project. 289 | - [x] Ability to create new project from existing one (variation). 290 | --------------------------------------------------------------------------------