├── .editorconfig ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── CHANGELOG.md ├── README.md ├── build.sh ├── example ├── .gitignore ├── README.md ├── data │ └── .gitkeep ├── docker-compose.yml ├── gulpfile.js ├── package.json └── scripts │ └── init.sh ├── images ├── 7.2 │ └── Dockerfile ├── 7.3 │ └── Dockerfile ├── 7.4 │ └── Dockerfile └── 8.0 │ └── Dockerfile ├── package.json └── run.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | PLEASE READ THIS BEFORE POSTING AN ISSUE! 2 | 3 | This is a template to make sure you provide us with all the information we will be to help you out. Please take the time to fill detailed issues, it will be faster for us to provide solutions! In the following sections, you should replace the content with your own. The content that is there is just presented as an example. 4 | 5 | ## Overview 6 | 7 | - What's your problem about? 8 | - What is your operating system? 9 | - What is your docker version? `docker version` 10 | 11 | ## `docker-compose.yml` 12 | 13 | ```yml 14 | version: '2' 15 | services: 16 | wordpress: 17 | # Replace this with your own docker-compose.yml content 18 | ``` 19 | 20 | ## Project structure 21 | 22 | ``` 23 | /data 24 | /database.sql 25 | /wp-content 26 | /plugins 27 | /my-plugin 28 | /themes 29 | /my-theme 30 | ``` 31 | 32 | ## `docker-compose up` output 33 | 34 | ``` 35 | wordpress_1 | =============================================================================== 36 | wordpress_1 | Begin WordPress Installation 37 | wordpress_1 | =============================================================================== 38 | wordpress_1 | ==> Installing wordpress 39 | wordpress_1 | -> Downloading... ✘ 40 | wordpress_1 | ==> Waiting for MySQL to initialize... 41 | wordpress_1 | -> mysqld is alive 42 | wordpress_1 | ==> Configuring wordpress 43 | wordpress_1 | -> Generating wp-config.php file... ✘ 44 | wordpress_1 | ==> Checking database 45 | wordpress_1 | -> Creating database wordpress ✘ 46 | wordpress_1 | -> Loading data backup from /data/database.sql ✘ 47 | wordpress_1 | ==> Checking for multisite 48 | wordpress_1 | -> Multisite not found. SKIPPING... 49 | ``` 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # File system junk files 2 | Desktop.ini 3 | Thumbs.db 4 | .DS_Store 5 | ._* 6 | .Spotlight-V100 7 | .Trashes 8 | .apdisk 9 | .fseventsd 10 | 11 | # Editor files 12 | *.sublime-* 13 | *.swp 14 | .c9 15 | .sass-cache/ 16 | .vscode 17 | .history 18 | 19 | # Wordpress 20 | wp-content/* 21 | 22 | # WordPress Starter Dev Files 23 | /docker-compose.yml 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.25.4 - latest 4 | 5 | ### Patch 6 | 7 | - Fix bug that prevented url form of plugins/themes to fail due to nullglob interacting with the parsing. #176 8 | 9 | ## 0.25.3 10 | 11 | ### Patch 12 | 13 | - Fix bug preventing a default theme from being installed when no theme dependencies or volumes exist. #172 14 | - Fix small bug that occurred when no volumes were included in `/data` due to not having nullglob set. 15 | 16 | ## 0.25.2 17 | 18 | ### Patch 19 | 20 | - Fix bug in plugin/theme activation caused by attempting to activate using a URL rather than the slug in cases where plugins/themes are installed using zip URL. 21 | 22 | ## 0.25.1 23 | 24 | ### Minor 25 | 26 | - Drop PHP 7.0 and 7.1 images. 27 | - Add PHP 7.4 image. 28 | 29 | ### Patch 30 | 31 | - Fix bug in run.sh resulting from an invalid flag given to `find` 32 | 33 | ## 0.25.0 34 | 35 | ### Minor 36 | 37 | - Make runtime script much simpler and more reliable. #165 38 | - Increase verbosity of logging related to database interactions. #165 39 | 40 | ## 0.24.0 41 | 42 | ### Minor 43 | 44 | * feat: bump default theme install to "twentytwenty" 45 | * feat: increase `upload_max_filesize` to `50M` 46 | 47 | ### Patch 48 | 49 | * fix: include "must-use" plugins in plugin volume check 50 | * fix: add --create-dirs to curl in dockerfile 51 | * fix: improve plugin volumes check to also include plugins that are a single php file 52 | 53 | ## 0.23.0 54 | 55 | ### Minor 56 | 57 | - Add missing `bcmath` and `imagick` PHP extensions recommended by WordPress 5.2's Health Check. 58 | 59 | ## 0.22.0 60 | 61 | ### Minor 62 | 63 | - Add `php7.3` 64 | - Add `/docker-entrypoint-initwp.d/` directory check to allow users to mount and run arbitrary scripts on build. 65 | - Use `twentynineteen` theme by default. 66 | 67 | ## 0.21.0 68 | 69 | ### Minor 70 | 71 | - Automatically perform a WordPress database update after importing, if needed. 72 | 73 | ### Chore 74 | 75 | - Update example files and error messages related to bindfs. 76 | 77 | ## 0.20.0 78 | 79 | ### Minor 80 | 81 | - Add support for Redis object caching. 82 | - Allow user to specify a different database charset via environment variable (#137) 83 | 84 | ## 0.19.0 85 | 86 | **BREAKING CHANGE:** The WordPress service must now be configured in one of the following ways: 87 | 88 | (preferred) 89 | ```yml 90 | services: 91 | wordpress: 92 | cap_add: 93 | - SYS_ADMIN 94 | devices: 95 | - /dev/fuse 96 | ``` 97 | 98 | **OR** 99 | 100 | (try to avoid if possible) 101 | ```yml 102 | services: 103 | wordpress: 104 | privileged: true 105 | ``` 106 | 107 | This configuration allows the wordpress container to mount the WordPress 108 | directory to the web root with back and forth syncronization using bindfs. This 109 | configuration ensures that when editing the files/folders manually on your 110 | system, they have the proper `admin` permissions in the /app directory, and 111 | when the server edits files/folders from WordPress, the webroot always have the 112 | proper `www-data` permissions in the web root. 113 | 114 | ## 0.18.0 115 | 116 | **BREAKING CHANGE:** `URL_REPLACE` environment variable now only accepts `after_url`, rather than `before_url,after_url`. This will gracefully fix itself if it encounters the old format for this version only, but will break on subsequent versions. 117 | 118 | ## Minor 119 | - Create and use a non-root user (admin) with passwordless sudo priviledges, rather than using the root user. 120 | - Plugins and themes can now be space-separated (preferred) or comma-separated (legacy). 121 | - Readdress #110 to print out logs in a similar, but greatly simplified, format. 122 | - Install and remove themes and plugins in parallel. 123 | - Greatly simplify build pipeline. 124 | 125 | ## 0.17.0 126 | 127 | ### Minor 128 | - Add PHP 7.2 (`latest`, `latest-php7.2`, `0.17.0-php7.2`) 129 | - Add `DB_USER` config option to environment variables 130 | 131 | ## 0.16.0 132 | - **BREAKING CHANGE:** Builds will now exit if any plugin or theme installs fail. 133 | - Improve logging. 134 | 135 | ## 0.15.2 136 | 137 | ### Fixes 138 | - **REALLY** fix error causing volumed plugins and/or themes to be deleted. 139 | - Move `.dockercache` out of index directory so it can't be accessed directly. 140 | 141 | ## 0.15.1 142 | 143 | ### Fixes 144 | - Fix error causing volumed plugins to be deleted. 145 | - Always flush `.htaccess` to add rewrite rules back in the event a plugin modified it (e.g. `w3-total-cache`). 146 | 147 | ### Other 148 | - Add `wp-cli` bash completions to the wordpress container. 149 | 150 | ## 0.15.0 151 | 152 | ### Deprecations 153 | 154 | **Note:** Both deprecations will still work as usual until the next release cycle. 155 | 156 | #### `[local]plugin-name` & `[local]theme-name` syntax 157 | 158 | The build process is now aware of locally-volumed plugins and themes automatically. 159 | 160 | Additionally, listing locally-volumed plugins and themes in your `docker-compose.yml` file is optional; you may list them if you'd like to keep your compose file declarative, or you may skip listing them completely. The build will complete the same either way. 161 | 162 | #### `SEARCH_REPLACE` has been renamed to `URL_REPLACE` 163 | 164 | We chose to rename this because, although you may search and replace strings that are not URLs, the build process requires them to be. 165 | 166 | This name reflects that requirement better and will lead to less confusion down the road. 167 | 168 | ### `VERBOSE` environement variable 169 | 170 | Logging has been changed to show necessary information by default. 171 | 172 | ### Improvements 173 | - Add `php7.1` base image. 174 | - Widespread efficiency improvements to build process. 175 | - Reduce the number of Dockerfile layers. 176 | - If `SERVER_NAME` is specified (eg. `example.com`), create a `ServerAlias` in the apache configs for `www.example.com`. 177 | 178 | ### Fixes 179 | - Plugins and themes are only pruned if they meet the following criteria (Closes #51): 180 | - Not listed in `docker-compose.yml`. 181 | - Not added as a local volume. 182 | - Adjust permissions of volumed files and directories so that they remain editable outside the container, and remain secure. (Closes #12) 183 | - Fix critical security vulnerability. HT @joerybruijntjes. 184 | 185 | ## 0.14.0 186 | - Add `SERVER_NAME` variable to allow for quickly setting that directive in the apache config for users interested in running in production. 187 | 188 | ## 0.13.0 189 | - Add `certbot` into the image to allow for easily obtaining and renewing SSL certificates 190 | 191 | ## 0.12.2 192 | - Add missing `exif` php extension to handle WordPress media upload metadata parsing. 193 | - Bump default base theme to `twentyseventeen` 194 | 195 | ## 0.12.1 196 | - This project now maintains 2 separate `Dockerfile`, depending on the version of PHP you'd like to have installed. 197 | 198 | **Available Dockerfiles**: 199 | 200 | | PHP Version | Tags | 201 | | ----------- | ---- | 202 | | **7.0** | `latest` `latest-php7.0` `0.12.1` `0.12.1-php7.0` | 203 | | **5.6** | `latest-php5.6` `0.12.1-php5.6` | 204 | 205 | ## 0.11.0 206 | - Add `WP_VERSION` environment variable which allows users to specify which version of WordPress is installed. 207 | 208 | ## 0.10.1 209 | - fix: Add default permalink structure (`/%year%/%monthnum%/%postname%/`). Closes #42. 210 | 211 | ## 0.10.0 212 | 213 | - BREAKING CHANGE: Explicitly listing all plugin/theme requirements (including those located in volumed directories) required. 214 | - Refactor run.sh into functions 215 | - Add new URL plugin format to better check existence after initial build. 216 | - Add new URL theme format to better check existence after initial build. 217 | - Add `VERBOSE` environment variable option which allows the build to run verbosely if one chooses. 218 | - Prettier logging. 219 | - Add `--activate` after plugin installs so that all plugins start out activated. 220 | - Many other improvements 221 | 222 | ## 0.9.0 223 | 224 | - Add `DB_HOST` environment variable - Fix #46 225 | - Various bug fixes and improvements 226 | 227 | ## 0.8.0 228 | 229 | - Add `DB_PREFIX` optional param - Fix #37 230 | - Only create .htaccess for non-multisite installations - Fix #32 231 | - Various bug fixes and improvements 232 | 233 | ## 0.7.0 234 | 235 | - Support Wordpress Multisite - Fix #33 236 | - Fix a typo in the gulpfile 237 | 238 | ## 0.6.0 239 | 240 | - Update wp-cli 241 | - `gulp clean` now also clears generated templates 242 | - Support for `WP_DEBUG_LOG` and `WP_DEBUG_DISPLAY` options 243 | 244 | ## 0.5.0 245 | 246 | - Clear any apache pid file before starting apache 247 | 248 | ## 0.4.0 249 | 250 | - Add a script to run wp-cli via npm 251 | - Recreate the `wp-cli.yml` and `wp-config.php` every time to make sure they are up to date. 252 | 253 | ## 0.3.0 254 | 255 | - Remove all the scripts 256 | - Consolidate and improve the `run.sh` 257 | - Update the documentation 258 | 259 | ## 0.2.0 260 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Visible Wordpress Starter 2 | 3 | A Docker Wordpress development environment by the team at 4 | [Visible](https://visible.vc/) and some awesome 5 | [contributors](https://github.com/visiblevc/wordpress-starter/graphs/contributors). 6 | Our goal is to make Wordpress development slightly less frustrating. 7 | 8 | - [Introduction](#introduction) 9 | - [Example](./example/) 10 | - [Requirements](#requirements) 11 | - [Getting Started](#getting-started) 12 | - [Available Images](#available-images) 13 | - [Default Wordpress Admin Credentials](#default-wordpress-admin-credentials) 14 | - [Default Database Credentials](#default-database-credentials) 15 | - [Service Environment Variables](#service-environment-variables) 16 | - [`wordpress`](#wordpress) 17 | - [`db`](#db) 18 | - [Workflow Tips](#workflow-tips) 19 | - [Using `wp-cli`](#using-wp-cli) 20 | - [Working with Databases](#working-with-databases) 21 | - [Using in Production](#using-in-production) 22 | - [Contributing](#contributing) 23 | 24 | ### Introduction 25 | 26 | We wrote a series of articles explaining in depth the philosophy behind this 27 | project: 28 | 29 | - [Intro: A slightly less shitty WordPress developer workflow](https://visible.vc/engineering/wordpress-developer-workflow/) 30 | - [Part 1: Setup a local development environment for WordPress with Docker](https://visible.vc/engineering/docker-environment-for-wordpress/) 31 | - [Part 2: Setup an asset pipeline for WordPress theme development](https://visible.vc/engineering/asset-pipeline-for-wordpress-theme-development/) 32 | - [Part 3: Optimize your wordpress theme assets and deploy to S3](https://visible.vc/engineering/optimize-wordpress-theme-assets-and-deploy-to-s3-cloudfront/) 33 | 34 | ### Requirements 35 | 36 | Well, to run a Docker environment, you will need Docker. The Dockerfile is only 37 | for an Apache+PHP+Wordpress container, you will need a `MySQL` or `MariaDB` 38 | container to run a website. We use Docker Compose 1.6+ for the orchestration. 39 | 40 | ### Getting started 41 | 42 | This project has 2 parts: the Docker environment and a set of tools for theme 43 | development. To quickly get started, you can simply run the following: 44 | 45 | ``` 46 | # copy the files 47 | git clone https://github.com/visiblevc/wordpress-starter.git 48 | 49 | # navigate to example directory 50 | cd wordpress-starter/example 51 | 52 | # start the website at localhost:8080 53 | docker-compose up -d && docker-compose logs -f wordpress 54 | ``` 55 | 56 | **NOTE:** If you run on MacOS with Docker in VirtualBox, you will want to 57 | forward the port by running this 58 | `VBoxManage controlvm vm-name natpf1 "tcp8080,tcp,127.0.0.1,8080,,8080"`. If you 59 | use another port than `8080`, change it in the command. 60 | 61 | ### Available Images 62 | 63 | | PHP Version | Tags | 64 | | ----------- | ------------------------------------------- | 65 | | **7.4** | `latest` `latest-php7.4` `-php7.4` | 66 | | **7.3** | `latest` `latest-php7.3` `-php7.3` | 67 | | **7.2** | `latest-php7.2` `-php7.2` | 68 | 69 | If you need a specific version, look at the [Changelog](CHANGELOG.md) 70 | 71 | ### Default Wordpress Admin Credentials 72 | 73 | To access the Wordpress Admin at `/wp-admin`, the default values are as follows: 74 | 75 | | Credential | Value | Notes | 76 | | --------------------- | ------------------------------- | ---------------------------------------------------------- | 77 | | **Username or Email** | `root` or `admin@wordpress.com` | Can be changed with the `ADMIN_EMAIL` environment variable | 78 | | **Password** | `root` | Uses the same value as the `DB_PASS` environment variable | 79 | 80 | ### Default Database Credentials 81 | 82 | | Credential | Value | Notes | 83 | | ----------------- | ---------------------- | -------------------------------------------------------------------------------------------------- | 84 | | **Hostname** | `db` | Can be changed with the `DB_HOST` environment variable **NOTE:**: Must match database service name | 85 | | **Username** | `root` | | 86 | | **Password** | | Must be set using the `DB_PASS` environment variable | 87 | | **Database Name** | `wordpress` | Can be changed with the `DB_NAME` environment variable | 88 | | **Admin Email** | `admin@${DB_NAME}.com` | | 89 | 90 | ### Service Environment Variables 91 | 92 | **Notes:** 93 | 94 | - Variables marked with ✅ are required 95 | - Single quotes must surround `boolean` environment variables 96 | 97 | #### `wordpress` 98 | 99 | | Variable | Default Value | Description | 100 | | ------------------ | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 101 | | `DB_USER` | `root` | Username for both the database and the WordPress installation (if not importing existing) | 102 | | `DB_PASS`✅ | | Password for the database. Value must match `MYSQL_ROOT_PASSWORD` set in the `db` service | 103 | | `DB_HOST` | `db` | Hostname for the database | 104 | | `DB_NAME` | `wordpress` | Name of the database | 105 | | `DB_PREFIX` | `wp_` | Prefix for the database | 106 | | `DB_CHARSET` | `utf8` | Select a charset for the wordpress database (legacy versions might not be utf8) | 107 | | `SERVER_NAME` | `localhost` | Set this to `.` if you plan on obtaining SSL certificates | 108 | | `ADMIN_EMAIL` | `admin@${DB_NAME}.com` | Administrator email address | 109 | | `WP_LOCALE` | `en_US` | Set the site language | 110 | | `WP_DEBUG` | `'false'` | [Click here](https://codex.wordpress.org/WP_DEBUG) for more information | 111 | | `WP_DEBUG_DISPLAY` | `'false'` | [Click here](https://codex.wordpress.org/WP_DEBUG#WP_DEBUG_DISPLAY) for more information | 112 | | `WP_DEBUG_LOG` | `'false'` | [Click here](https://codex.wordpress.org/WP_DEBUG#WP_DEBUG_LOG) for more information | 113 | | `WP_VERSION` | `latest` | Specify the WordPress version to install. Accepts any valid semver number, `latest`, or `nightly` for beta builds. | 114 | | `THEMES` | | Space-separated list of themes you want to install in either of the following forms
  • `theme-slug`: Used when installing theme direct from WordPress.org
  • `[theme-slug]https://themesite.com/theme.zip`: Used when installing theme from URL
| 115 | | `PLUGINS` | | Space-separated list of plugins you want to install in either of the following forms:
  • `plugin-slug`: Used when installing plugin direct from WordPress.org.
  • `[plugin-slug]http://pluginsite.com/plugin.zip`: Used when installing plugin from URL.
| 116 | | `MULTISITE` | `'false'` | Set to `'true'` to enable multisite | 117 | | `PERMALINKS` | `/%year%/%monthnum%/%postname%/` | A valid WordPress permalink [structure tag](https://wordpress.org/support/article/using-permalinks/#structure-tags) | 118 | | `URL_REPLACE` | |

Value must be a full replacement URL to use in development environments if importing a database for development use.

Example: If your live site's URL is `https://www.example.com` and you intend to use the database on port 8080 of localhost, this value should be set to `http://localhost:8080`.

Note: If you are running Docker using Docker Machine, your replacement url MUST be the output of the following command: `echo $(docker-machine ip ):8080`

| 119 | | `EXTRA_PHP` | |

Extra PHP code to add to `wp-config.php`. [Click here](https://developer.wordpress.org/cli/commands/config/create/) for more information.

IMPORTANT NOTE: All `$` symbols must be escaped by prepending an extra `$`, otherwise it will be interpreted by docker as an environment variable. In other words, `$variable` must be `$$variable`.

| 120 | 121 | #### `db` 122 | 123 | | Variable | Default Value | Description | 124 | | ----------------------- | ------------- | ----------------------------------------------- | 125 | | `MYSQL_ROOT_PASSWORD`✅ | | Must match `DB_PASS` of the `wordpress` service | 126 | 127 | ## Workflow Tips 128 | 129 | ### Using `wp-cli` 130 | 131 | You can access wp-cli by running `npm run wp ...`. Here are some examples: 132 | 133 | ``` 134 | npm run wp plugin install 135 | npm run wp db import /data/database.sql 136 | ``` 137 | 138 | ### Working with Databases 139 | 140 | If you have an exported `.sql` file from an existing website, drop the file into 141 | the `data/` folder. The first time you run the container, it will detect the SQL 142 | dump and use it as a database. If it doesn't find one, it will create a fresh 143 | database. 144 | 145 | If the SQL dump changes for some reason, you can reload the database by running: 146 | 147 | ```sh 148 | docker-compose exec wordpress /bin/bash "wp db import $(find /data/*.sql | head -n 1) --allow-root" 149 | ``` 150 | 151 | If you want to create a dump of your development database, you can run: 152 | 153 | ```sh 154 | docker-compose exec wordpress /bin/bash -c 'wp db export /data/dump.sql --allow-root' 155 | ``` 156 | 157 | Finally, sometimes your development environment runs on a different domain than 158 | your live one. The live will be `example.com` and the development 159 | `localhost:8080`. This project does a search and replace for you. You can set 160 | the `URL_REPLACE: localhost:8080` environment variable in the 161 | `docker-compose.yml`. 162 | 163 | ## Using in Production 164 | 165 | ### Adjustments to `docker-compose.yml` 166 | 167 | ```yml 168 | # If something isn't shown, assume it's the same as the examples above 169 | version: "3" 170 | services: 171 | wordpress: 172 | ports: 173 | - 80:80 174 | - 443:443 175 | restart: always 176 | environment: 177 | SERVER_NAME: mysite.com 178 | DB_PASS: ${SECURE_PASSWORD} # Stored in .env file 179 | volumes: 180 | - ./letsencrypt:/etc/letsencrypt 181 | - ./data:/data 182 | # anything else you'd like to be able to back up 183 | db: 184 | restart: always 185 | environment: 186 | MYSQL_ROOT_PASSWORD: ${SECURE_PASSWORD} # Stored in .env file 187 | ``` 188 | 189 | ### SSL Certificates 190 | 191 | We highly recommend securing your site with SSL encryption. The Let's Encrypt 192 | and Certbot projects have made doing this both free (as in beer) and painless. 193 | We've incorporated these projects into this project. 194 | 195 | Assuming your site is running on your production host, follow the below steps to 196 | obtain and renew SSL certificates. 197 | 198 | #### Obtaining Certificates 199 | 200 | You should first [set `SERVER_NAME` to `.` in your `docker-compose.yml`](#wordpress) 201 | 202 | ```sh 203 | $ docker-compose ps 204 | Name Command State 205 | --------------------------------------------------------- 206 | project_db_1 docker-entrypoint.sh mysqld Up 207 | project_wordpress_1 docker-php-entrypoint /run.sh Up 208 | 209 | $ docker-compose exec wordpress /bin/bash 210 | root@4e16c7fe4a10:/app# certbot --apache 211 | ``` 212 | 213 | #### Renewing Certificates 214 | 215 | ```sh 216 | $ docker-compose ps 217 | Name Command State 218 | --------------------------------------------------------- 219 | project_db_1 docker-entrypoint.sh mysqld Up 220 | project_wordpress_1 docker-php-entrypoint /run.sh Up 221 | 222 | $ docker-compose exec wordpress /bin/bash 223 | root@4e16c7fe4a10:/app# certbot renew 224 | ``` 225 | 226 | ## Contributing 227 | 228 | You can find Development instructions in the 229 | [Wiki](https://github.com/visiblevc/wordpress-starter/wiki/Development). 230 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | : "${npm_package_version?Script must be ran using npm}" 5 | 6 | for dir in ./images/*; do 7 | docker build \ 8 | -f "$dir/Dockerfile" \ 9 | --build-arg VERSION="$npm_package_version" \ 10 | -t "visiblevc/wordpress:latest" \ 11 | -t "visiblevc/wordpress:latest-php${dir##*/}" \ 12 | -t "visiblevc/wordpress:$npm_package_version-php${dir##*/}" \ 13 | . 14 | done 15 | 16 | echo " 17 | 18 | Successfully built images with the following tags:" 19 | 20 | docker images visiblevc/wordpress --format "{{.Tag}}" | sort -r 21 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # File system junk files 2 | Desktop.ini 3 | Thumbs.db 4 | .DS_Store 5 | ._* 6 | .Spotlight-V100 7 | .Trashes 8 | .apdisk 9 | .fseventsd 10 | 11 | # Editor files 12 | *.sublime-* 13 | *.swp 14 | .c9 15 | .sass-cache/ 16 | 17 | # Wordpress 18 | wp-content/* 19 | !wp-content/themes 20 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | Download this example to your current working directory 4 | 5 | ```sh 6 | $ curl https://codeload.github.com/visiblevc/wordpress-starter/tar.gz/master | tar -xz --strip 1 wordpress-starter-master/example 7 | ``` 8 | 9 | The only thing you need to get started is a `docker-compose.yml` file: 10 | 11 | ```yml 12 | version: '3' 13 | services: 14 | wordpress: 15 | image: visiblevc/wordpress 16 | 17 | # required for mounting bindfs 18 | cap_add: 19 | - SYS_ADMIN 20 | devices: 21 | - /dev/fuse 22 | # required on certain cloud hosts 23 | security_opt: 24 | - apparmor:unconfined 25 | 26 | ports: 27 | - 8080:80 28 | - 443:443 29 | volumes: 30 | - ./data:/data 31 | - ./scripts:/docker-entrypoint-initwp.d 32 | environment: 33 | DB_NAME: wordpress 34 | DB_PASS: root 35 | PLUGINS: >- 36 | academic-bloggers-toolkit 37 | co-authors-plus 38 | [WP-API]https://github.com/WP-API/WP-API/archive/master.zip 39 | 40 | db: 41 | image: mariadb:10 # or mysql:5.7 42 | volumes: 43 | - data:/var/lib/mysql 44 | environment: 45 | MYSQL_ROOT_PASSWORD: root 46 | 47 | # if you want phpMyAdmin 48 | phpmyadmin: 49 | image: phpmyadmin/phpmyadmin 50 | ports: 51 | - 22222:80 52 | 53 | volumes: 54 | data: 55 | ``` 56 | 57 | ## Running the example 58 | 59 | 1. Run the following command in the root of the example directory. 60 | 61 | ```sh 62 | $ docker-compose up -d && docker-compose logs -f wordpress 63 | ``` 64 | 65 | 2. When the build is finished, hit ctrl-c to detach from the logs and visit `localhost:8080` in your browser. 66 | -------------------------------------------------------------------------------- /example/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/visiblevc/wordpress-starter/7b2d2510704a25ccaebeda12e799c59471838dd2/example/data/.gitkeep -------------------------------------------------------------------------------- /example/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | wordpress: 4 | image: visiblevc/wordpress 5 | 6 | # required for mounting bindfs 7 | cap_add: 8 | - SYS_ADMIN 9 | devices: 10 | - /dev/fuse 11 | # required on certain cloud hosts 12 | security_opt: 13 | - apparmor:unconfined 14 | 15 | ports: 16 | - 8080:80 17 | - 443:443 18 | volumes: 19 | - ./data:/data 20 | - ./scripts:/docker-entrypoint-initwp.d 21 | environment: 22 | DB_NAME: wordpress 23 | DB_PASS: root 24 | PLUGINS: >- 25 | academic-bloggers-toolkit 26 | co-authors-plus 27 | [WP-API]https://github.com/WP-API/WP-API/archive/master.zip 28 | 29 | db: 30 | image: mariadb:10 # or mysql:5.7 31 | volumes: 32 | - data:/var/lib/mysql 33 | environment: 34 | MYSQL_ROOT_PASSWORD: root 35 | 36 | phpmyadmin: 37 | image: phpmyadmin/phpmyadmin 38 | ports: 39 | - 22222:80 40 | 41 | volumes: 42 | data: 43 | -------------------------------------------------------------------------------- /example/gulpfile.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var gulp = require('gulp'); 3 | var runSequence = require('run-sequence'); 4 | 5 | // Project plugins 6 | var rev = require('gulp-rev'); 7 | var sass = require('gulp-sass'); 8 | var clean = require('gulp-clean'); 9 | var concat = require('gulp-concat'); 10 | var rename = require('gulp-rename'); 11 | var replace = require('gulp-replace-task'); 12 | var collect = require('gulp-rev-collector'); 13 | var imagemin = require('gulp-imagemin'); 14 | var pngquant = require('imagemin-pngquant'); 15 | var uglify = require('gulp-uglify'); 16 | var minifyCss = require('gulp-minify-css'); 17 | var awspublish = require('gulp-awspublish'); 18 | var autoprefixer = require('gulp-autoprefixer'); 19 | 20 | // Configuration 21 | var config = require('./package.json').gulp; 22 | var themePath = 'wp-content/themes/' + config.theme; 23 | var distPath = themePath + '/dist'; 24 | var distPathAbsolute = '/' + distPath; 25 | var assetPath = themePath + '/assets'; 26 | 27 | gulp.task( 28 | 'default', 29 | [ 30 | 'compile-styles', 31 | 'compile-scripts', 32 | 'compile-fonts', 33 | 'compile-images', 34 | 'compile-templates' 35 | ], 36 | function() { 37 | gulp.watch(assetPath + '/scss/**/*.scss', ['compile-styles']); 38 | gulp.watch(assetPath + '/js/**/*.js', ['compile-scripts']); 39 | gulp.watch(assetPath + '/fonts/**/*', ['compile-fonts']); 40 | gulp.watch(assetPath + '/images/**/*', ['compile-images']); 41 | gulp.watch(assetPath + '/templates/**/*', ['compile-templates']); 42 | } 43 | ); 44 | 45 | gulp.task('deploy-assets', function (callback) { 46 | runSequence( 47 | 'clean', 48 | ['compile-styles', 'compile-scripts', 'compile-images', 'compile-fonts', 'compile-templates'], 49 | ['optimize-styles', 'optimize-scripts', 'optimize-images'], 50 | 'version-assets', 51 | ['replace-versioned-assets-in-assets', 'replace-versioned-assets-in-templates'], 52 | 'gzip-assets', 53 | 'publish-to-s3', 54 | callback 55 | ); 56 | }); 57 | 58 | gulp.task('clean', function () { 59 | return gulp.src([ 60 | distPath, 61 | themePath + '/*.php' 62 | ], {read: false}) 63 | .pipe(clean({force: true})); 64 | }); 65 | 66 | // Styles 67 | // ------ 68 | 69 | gulp.task('compile-styles', function () { 70 | return ( 71 | gulp.src([ 72 | assetPath + '/scss/style.scss' 73 | ]) 74 | .pipe(sass().on('error', sass.logError)) 75 | .pipe(autoprefixer({ 76 | browsers: ['last 2 versions'], 77 | cascade: false 78 | })) 79 | .pipe(gulp.dest(distPath + '/css')) 80 | ); 81 | }); 82 | 83 | gulp.task('optimize-styles', function () { 84 | return gulp.src(distPath + '/css/style.css') 85 | .pipe(minifyCss()) 86 | .pipe(gulp.dest(distPath + '/css')); 87 | }); 88 | 89 | 90 | 91 | // Scripts 92 | // ------- 93 | 94 | gulp.task('compile-scripts', function () { 95 | return ( 96 | gulp.src([ 97 | assetPath + '/js/script.js' 98 | ]) 99 | .pipe(concat('script.js')) 100 | .pipe(gulp.dest(distPath + '/js')) 101 | ); 102 | }); 103 | 104 | gulp.task('optimize-scripts', function () { 105 | return gulp.src(distPath + '/js/script.js') 106 | .pipe(uglify()) 107 | .pipe(gulp.dest(distPath + '/js/')); 108 | }); 109 | 110 | 111 | 112 | // Images 113 | // ------- 114 | 115 | gulp.task('compile-images', function () { 116 | return gulp.src(assetPath + '/images/**/*') 117 | .pipe(gulp.dest(distPath + '/images')); 118 | }); 119 | 120 | gulp.task('optimize-images', function () { 121 | return gulp.src(distPath + '/images/**/*') 122 | .pipe(imagemin({ 123 | progressive: true, 124 | svgoPlugins: [{removeViewBox: false}], 125 | use: [pngquant()] 126 | })) 127 | .pipe(gulp.dest(distPath + '/images')); 128 | }); 129 | 130 | 131 | 132 | // Fonts 133 | // ----- 134 | 135 | gulp.task('compile-fonts', function () { 136 | return gulp.src(assetPath + '/fonts/**/*') 137 | .pipe(gulp.dest(distPath + '/fonts')); 138 | }); 139 | 140 | 141 | 142 | // Templates 143 | // --------- 144 | 145 | gulp.task('compile-templates', function () { 146 | return gulp.src(assetPath + '/templates/**/*') 147 | .pipe(gulp.dest(themePath)); 148 | }); 149 | 150 | 151 | // versioning 152 | // ----------- 153 | 154 | gulp.task('version-assets', function () { 155 | return gulp.src(distPath + '/**/*') 156 | .pipe(rev()) 157 | .pipe(gulp.dest(distPath)) 158 | .pipe(rev.manifest()) 159 | .pipe(gulp.dest(themePath)); 160 | }); 161 | 162 | gulp.task('replace-versioned-assets-in-assets', function () { 163 | var dirReplacements = {}; 164 | dirReplacements[distPathAbsolute] = config.productionAssetURL; 165 | 166 | return gulp.src([ 167 | themePath + '/**/*.json', 168 | distPath + '/**/*.css', 169 | distPath + '/**/*.js' 170 | ]) 171 | .pipe(collect({ replaceReved: true, dirReplacements: dirReplacements })) 172 | .pipe(gulp.dest(distPath)); 173 | }); 174 | 175 | gulp.task('replace-versioned-assets-in-templates', function () { 176 | var dirReplacements = {}; 177 | dirReplacements[distPathAbsolute] = config.productionAssetURL; 178 | 179 | return gulp.src([ 180 | themePath + '/**/*.json', 181 | themePath + '/*.php' 182 | ]) 183 | .pipe(collect({ replaceReved: true, dirReplacements: dirReplacements })) 184 | .pipe(gulp.dest(themePath)); 185 | }); 186 | 187 | 188 | 189 | // S3 190 | // -- 191 | 192 | gulp.task('gzip-assets', function () { 193 | return gulp.src([ 194 | '!' + distPath + '/**/*.gz', 195 | distPath + '/**/*' 196 | ]) 197 | .pipe(awspublish.gzip({ ext: '.gz' })) 198 | .pipe(gulp.dest(distPath)); 199 | }); 200 | 201 | gulp.task('publish-to-s3', function () { 202 | var publisher = awspublish.create(config.aws); 203 | var headers = { 204 | 'Cache-Control': 'max-age=31536000, no-transform, public' 205 | }; 206 | 207 | return gulp.src(distPath + '/**') 208 | .pipe(publisher.publish(headers)) 209 | .pipe(publisher.sync()) 210 | .pipe(awspublish.reporter()); 211 | }); 212 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "wp": "docker-compose exec wordpress bash" 6 | }, 7 | "dependencies": {}, 8 | "devDependencies": { 9 | "chalk": "^1.1.0", 10 | "dateformat": "^1.0.11", 11 | "gulp": "^3.9.0", 12 | "gulp-autoprefixer": "~2.3.1", 13 | "gulp-awspublish": "^2.0.2", 14 | "gulp-clean": "^0.3.1", 15 | "gulp-concat": "^2.6.0", 16 | "gulp-imagemin": "^2.3.0", 17 | "gulp-minify-css": "^1.2.0", 18 | "gulp-rename": "^1.2.2", 19 | "gulp-replace": "^0.5.4", 20 | "gulp-replace-task": "^0.2.3", 21 | "gulp-rev": "^5.1.0", 22 | "gulp-rev-collector": "^1.0.0", 23 | "gulp-sass": "^2.0.4", 24 | "gulp-uglify": "^1.2.0", 25 | "gulp-util": "^3.0.6", 26 | "imagemin-pngquant": "^4.1.2", 27 | "lodash.template": "^4.5.0", 28 | "path": "^0.11.14", 29 | "replace-ext": "0.0.1", 30 | "run-sequence": "^1.1.2", 31 | "through2": "^2.0.0", 32 | "vinyl": "^0.5.1" 33 | }, 34 | "gulp": { 35 | "theme": "", 36 | "productionAssetURL": "", 37 | "aws": { 38 | "params": { 39 | "Bucket": "" 40 | }, 41 | "accessKeyId": "", 42 | "secretAccessKey": "" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /example/scripts/init.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | wp plugin activate my-plugin 4 | -------------------------------------------------------------------------------- /images/7.2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.2-apache 2 | ARG VERSION=latest 3 | LABEL maintainer="Derek P Sifford " \ 4 | version="${VERSION}-php7.2" 5 | 6 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 7 | 8 | # Install base requirements & sensible defaults + required PHP extensions 9 | # hadolint ignore=DL3008 10 | RUN echo "deb http://ftp.debian.org/debian $(sed -n 's/^VERSION=.*(\(.*\)).*/\1/p' /etc/os-release)-backports main" >> /etc/apt/sources.list \ 11 | && apt-get update \ 12 | && apt-get install -y --no-install-recommends \ 13 | bash-completion \ 14 | bindfs \ 15 | ghostscript \ 16 | less \ 17 | libjpeg-dev \ 18 | libmagickwand-dev \ 19 | libpng-dev \ 20 | libxml2-dev \ 21 | libzip-dev \ 22 | mariadb-client \ 23 | sudo \ 24 | unzip \ 25 | vim \ 26 | zip \ 27 | && apt-get -t "$(sed -n 's/^VERSION=.*(\(.*\)).*/\1/p' /etc/os-release)-backports" install -y --no-install-recommends \ 28 | python-certbot-apache \ 29 | && rm -rf /var/lib/apt/lists/* \ 30 | && docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \ 31 | && docker-php-ext-configure zip --with-libzip \ 32 | && pecl install imagick \ 33 | && pecl install redis \ 34 | && docker-php-ext-install \ 35 | bcmath \ 36 | exif \ 37 | gd \ 38 | mysqli \ 39 | opcache \ 40 | soap \ 41 | zip \ 42 | && docker-php-ext-enable imagick \ 43 | && docker-php-ext-enable redis \ 44 | && { \ 45 | echo 'memory_limit = 512M'; \ 46 | # See https://github.com/visiblevc/wordpress-starter/issues/160#issuecomment-544561961 47 | echo 'upload_max_filesize = 50M'; \ 48 | } > /usr/local/etc/php/php.ini \ 49 | # See https://secure.php.net/manual/en/opcache.installation.php 50 | && { \ 51 | echo 'opcache.memory_consumption = 128'; \ 52 | echo 'opcache.interned_strings_buffer = 8'; \ 53 | echo 'opcache.max_accelerated_files = 4000'; \ 54 | echo 'opcache.revalidate_freq = 2'; \ 55 | echo 'opcache.fast_shutdown = 1'; \ 56 | echo 'opcache.enable_cli = 1'; \ 57 | } > /usr/local/etc/php/conf.d/opcache-recommended.ini \ 58 | && sed -i 's/AllowOverride None/AllowOverride All/g' /etc/apache2/apache2.conf \ 59 | # Fixes issue where error is logged stating apache could not resolve the 60 | # fully qualified domain name 61 | && echo 'ServerName localhost' > /etc/apache2/conf-available/fqdn.conf \ 62 | # Grab and install wp-cli from remote 63 | && curl --create-dirs \ 64 | -o /usr/local/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar \ 65 | -o /etc/bash_completion.d/wp-cli https://raw.githubusercontent.com/wp-cli/wp-cli/master/utils/wp-completion.bash \ 66 | && a2enconf fqdn \ 67 | && a2enmod rewrite expires 68 | 69 | # Add admin superuser, create install directory, adjust perms, & add symlink 70 | COPY run.sh /run.sh 71 | RUN useradd -ms /bin/bash -G sudo admin \ 72 | && echo "admin ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/admin \ 73 | && chmod 0440 /etc/sudoers.d/admin \ 74 | && chmod +x /usr/local/bin/wp /run.sh \ 75 | && mkdir -m 0700 /app \ 76 | && chown admin:admin /app \ 77 | && printf '%s\t%s\t%s\t%s%s%s%s%s%s%s%s\t%d\t%d\n' \ 78 | '/app' \ 79 | '/var/www/html' \ 80 | 'fuse.bindfs' \ 81 | 'force-user=www-data,' \ 82 | 'force-group=www-data,' \ 83 | 'create-for-user=admin,' \ 84 | 'create-for-group=admin,' \ 85 | 'create-with-perms=0644:a+X,' \ 86 | 'chgrp-ignore,' \ 87 | 'chown-ignore,' \ 88 | 'chmod-ignore' \ 89 | 0 \ 90 | 0 > /etc/fstab 91 | 92 | USER admin 93 | WORKDIR /app 94 | EXPOSE 80 443 95 | CMD ["/run.sh"] 96 | -------------------------------------------------------------------------------- /images/7.3/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.3-apache 2 | ARG VERSION=latest 3 | LABEL maintainer="Derek P Sifford " \ 4 | version="${VERSION}-php7.3" 5 | 6 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 7 | 8 | # Install base requirements & sensible defaults + required PHP extensions 9 | # hadolint ignore=DL3008 10 | RUN echo "deb http://ftp.debian.org/debian $(sed -n 's/^VERSION=.*(\(.*\)).*/\1/p' /etc/os-release)-backports main" >> /etc/apt/sources.list \ 11 | && apt-get update \ 12 | && apt-get install -y --no-install-recommends \ 13 | bash-completion \ 14 | bindfs \ 15 | ghostscript \ 16 | less \ 17 | libjpeg-dev \ 18 | libmagickwand-dev \ 19 | libpng-dev \ 20 | libxml2-dev \ 21 | libzip-dev \ 22 | mariadb-client \ 23 | sudo \ 24 | unzip \ 25 | vim \ 26 | zip \ 27 | && apt-get -t "$(sed -n 's/^VERSION=.*(\(.*\)).*/\1/p' /etc/os-release)-backports" install -y --no-install-recommends \ 28 | python-certbot-apache \ 29 | && rm -rf /var/lib/apt/lists/* \ 30 | && docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \ 31 | && docker-php-ext-configure zip --with-libzip \ 32 | && pecl install imagick \ 33 | && pecl install redis \ 34 | && docker-php-ext-install \ 35 | bcmath \ 36 | exif \ 37 | gd \ 38 | mysqli \ 39 | opcache \ 40 | soap \ 41 | zip \ 42 | && docker-php-ext-enable imagick \ 43 | && docker-php-ext-enable redis \ 44 | && { \ 45 | echo 'memory_limit = 512M'; \ 46 | # See https://github.com/visiblevc/wordpress-starter/issues/160#issuecomment-544561961 47 | echo 'upload_max_filesize = 50M'; \ 48 | } > /usr/local/etc/php/php.ini \ 49 | # See https://secure.php.net/manual/en/opcache.installation.php 50 | && { \ 51 | echo 'opcache.memory_consumption = 128'; \ 52 | echo 'opcache.interned_strings_buffer = 8'; \ 53 | echo 'opcache.max_accelerated_files = 4000'; \ 54 | echo 'opcache.revalidate_freq = 2'; \ 55 | echo 'opcache.fast_shutdown = 1'; \ 56 | echo 'opcache.enable_cli = 1'; \ 57 | } > /usr/local/etc/php/conf.d/opcache-recommended.ini \ 58 | && sed -i 's/AllowOverride None/AllowOverride All/g' /etc/apache2/apache2.conf \ 59 | # Fixes issue where error is logged stating apache could not resolve the 60 | # fully qualified domain name 61 | && echo 'ServerName localhost' > /etc/apache2/conf-available/fqdn.conf \ 62 | # Grab and install wp-cli from remote 63 | && curl --create-dirs \ 64 | -o /usr/local/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar \ 65 | -o /etc/bash_completion.d/wp-cli https://raw.githubusercontent.com/wp-cli/wp-cli/master/utils/wp-completion.bash \ 66 | && a2enconf fqdn \ 67 | && a2enmod rewrite expires 68 | 69 | # Add admin superuser, create install directory, adjust perms, & add symlink 70 | COPY run.sh /run.sh 71 | RUN useradd -ms /bin/bash -G sudo admin \ 72 | && echo "admin ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/admin \ 73 | && chmod 0440 /etc/sudoers.d/admin \ 74 | && chmod +x /usr/local/bin/wp /run.sh \ 75 | && mkdir -m 0700 /app \ 76 | && chown admin:admin /app \ 77 | && printf '%s\t%s\t%s\t%s%s%s%s%s%s%s%s\t%d\t%d\n' \ 78 | '/app' \ 79 | '/var/www/html' \ 80 | 'fuse.bindfs' \ 81 | 'force-user=www-data,' \ 82 | 'force-group=www-data,' \ 83 | 'create-for-user=admin,' \ 84 | 'create-for-group=admin,' \ 85 | 'create-with-perms=0644:a+X,' \ 86 | 'chgrp-ignore,' \ 87 | 'chown-ignore,' \ 88 | 'chmod-ignore' \ 89 | 0 \ 90 | 0 > /etc/fstab 91 | 92 | USER admin 93 | WORKDIR /app 94 | EXPOSE 80 443 95 | CMD ["/run.sh"] 96 | -------------------------------------------------------------------------------- /images/7.4/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.4-apache 2 | ARG VERSION=latest 3 | LABEL maintainer="Derek P Sifford " \ 4 | version="${VERSION}-php7.4" 5 | 6 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 7 | 8 | # Install base requirements & sensible defaults + required PHP extensions 9 | # hadolint ignore=DL3008 10 | RUN echo "deb http://ftp.debian.org/debian $(sed -n 's/^VERSION=.*(\(.*\)).*/\1/p' /etc/os-release)-backports main" >> /etc/apt/sources.list \ 11 | && apt-get update \ 12 | && apt-get install -y --no-install-recommends \ 13 | bash-completion \ 14 | bindfs \ 15 | ghostscript \ 16 | less \ 17 | libjpeg-dev \ 18 | libmagickwand-dev \ 19 | libpng-dev \ 20 | libxml2-dev \ 21 | libzip-dev \ 22 | mariadb-client \ 23 | sudo \ 24 | unzip \ 25 | vim \ 26 | zip \ 27 | && apt-get -t "$(sed -n 's/^VERSION=.*(\(.*\)).*/\1/p' /etc/os-release)-backports" install -y --no-install-recommends \ 28 | python-certbot-apache \ 29 | && rm -rf /var/lib/apt/lists/* \ 30 | && docker-php-ext-configure gd --with-freetype --with-jpeg \ 31 | && pecl install imagick \ 32 | && pecl install redis \ 33 | && docker-php-ext-install \ 34 | bcmath \ 35 | exif \ 36 | gd \ 37 | mysqli \ 38 | opcache \ 39 | soap \ 40 | zip \ 41 | && docker-php-ext-enable imagick \ 42 | && docker-php-ext-enable redis \ 43 | && { \ 44 | echo 'memory_limit = 512M'; \ 45 | # See https://github.com/visiblevc/wordpress-starter/issues/160#issuecomment-544561961 46 | echo 'upload_max_filesize = 50M'; \ 47 | } > /usr/local/etc/php/php.ini \ 48 | # See https://secure.php.net/manual/en/opcache.installation.php 49 | && { \ 50 | echo 'opcache.memory_consumption = 128'; \ 51 | echo 'opcache.interned_strings_buffer = 8'; \ 52 | echo 'opcache.max_accelerated_files = 4000'; \ 53 | echo 'opcache.revalidate_freq = 2'; \ 54 | echo 'opcache.fast_shutdown = 1'; \ 55 | echo 'opcache.enable_cli = 1'; \ 56 | } > /usr/local/etc/php/conf.d/opcache-recommended.ini \ 57 | && sed -i 's/AllowOverride None/AllowOverride All/g' /etc/apache2/apache2.conf \ 58 | # Fixes issue where error is logged stating apache could not resolve the 59 | # fully qualified domain name 60 | && echo 'ServerName localhost' > /etc/apache2/conf-available/fqdn.conf \ 61 | # Grab and install wp-cli from remote 62 | && curl --create-dirs \ 63 | -o /usr/local/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar \ 64 | -o /etc/bash_completion.d/wp-cli https://raw.githubusercontent.com/wp-cli/wp-cli/master/utils/wp-completion.bash \ 65 | && a2enconf fqdn \ 66 | && a2enmod rewrite expires 67 | 68 | # Add admin superuser, create install directory, adjust perms, & add symlink 69 | COPY run.sh /run.sh 70 | RUN useradd -ms /bin/bash -G sudo admin \ 71 | && echo "admin ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/admin \ 72 | && chmod 0440 /etc/sudoers.d/admin \ 73 | && chmod +x /usr/local/bin/wp /run.sh \ 74 | && mkdir -m 0700 /app \ 75 | && chown admin:admin /app \ 76 | && printf '%s\t%s\t%s\t%s%s%s%s%s%s%s%s\t%d\t%d\n' \ 77 | '/app' \ 78 | '/var/www/html' \ 79 | 'fuse.bindfs' \ 80 | 'force-user=www-data,' \ 81 | 'force-group=www-data,' \ 82 | 'create-for-user=admin,' \ 83 | 'create-for-group=admin,' \ 84 | 'create-with-perms=0644:a+X,' \ 85 | 'chgrp-ignore,' \ 86 | 'chown-ignore,' \ 87 | 'chmod-ignore' \ 88 | 0 \ 89 | 0 > /etc/fstab 90 | 91 | USER admin 92 | WORKDIR /app 93 | EXPOSE 80 443 94 | CMD ["/run.sh"] 95 | -------------------------------------------------------------------------------- /images/8.0/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0-apache 2 | ARG VERSION=latest 3 | LABEL maintainer="Derek P Sifford " \ 4 | version="${VERSION}-php8.0" 5 | 6 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 7 | 8 | # Install base requirements & sensible defaults + required PHP extensions 9 | # hadolint ignore=DL3008 10 | RUN echo "deb http://ftp.debian.org/debian $(sed -n 's/^VERSION=.*(\(.*\)).*/\1/p' /etc/os-release)-backports main" >> /etc/apt/sources.list \ 11 | && apt-get update \ 12 | && apt-get install -y --no-install-recommends \ 13 | bash-completion \ 14 | bindfs \ 15 | ghostscript \ 16 | less \ 17 | libjpeg-dev \ 18 | libmagickwand-dev \ 19 | libpng-dev \ 20 | libxml2-dev \ 21 | libzip-dev \ 22 | mariadb-client \ 23 | sudo \ 24 | unzip \ 25 | vim \ 26 | zip \ 27 | && apt-get -t "$(sed -n 's/^VERSION=.*(\(.*\)).*/\1/p' /etc/os-release)-backports" install -y --no-install-recommends \ 28 | python-certbot-apache \ 29 | && rm -rf /var/lib/apt/lists/* \ 30 | && docker-php-ext-configure gd --with-freetype --with-jpeg \ 31 | # TODO: uncomment this when imagick supports php8 32 | # && pecl install imagick \ 33 | && pecl install redis \ 34 | && docker-php-ext-install \ 35 | bcmath \ 36 | exif \ 37 | gd \ 38 | mysqli \ 39 | opcache \ 40 | soap \ 41 | zip \ 42 | # TODO: uncomment this when imagick supports php8 43 | # && docker-php-ext-enable imagick \ 44 | && docker-php-ext-enable redis \ 45 | && { \ 46 | echo 'memory_limit = 512M'; \ 47 | # See https://github.com/visiblevc/wordpress-starter/issues/160#issuecomment-544561961 48 | echo 'upload_max_filesize = 50M'; \ 49 | } > /usr/local/etc/php/php.ini \ 50 | # See https://secure.php.net/manual/en/opcache.installation.php 51 | && { \ 52 | echo 'opcache.memory_consumption = 128'; \ 53 | echo 'opcache.interned_strings_buffer = 8'; \ 54 | echo 'opcache.max_accelerated_files = 4000'; \ 55 | echo 'opcache.revalidate_freq = 2'; \ 56 | echo 'opcache.fast_shutdown = 1'; \ 57 | echo 'opcache.enable_cli = 1'; \ 58 | } > /usr/local/etc/php/conf.d/opcache-recommended.ini \ 59 | && sed -i 's/AllowOverride None/AllowOverride All/g' /etc/apache2/apache2.conf \ 60 | # Fixes issue where error is logged stating apache could not resolve the 61 | # fully qualified domain name 62 | && echo 'ServerName localhost' > /etc/apache2/conf-available/fqdn.conf \ 63 | # Grab and install wp-cli from remote 64 | && curl --create-dirs \ 65 | -o /usr/local/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar \ 66 | -o /etc/bash_completion.d/wp-cli https://raw.githubusercontent.com/wp-cli/wp-cli/master/utils/wp-completion.bash \ 67 | && a2enconf fqdn \ 68 | && a2enmod rewrite expires 69 | 70 | # Add admin superuser, create install directory, adjust perms, & add symlink 71 | COPY run.sh /run.sh 72 | RUN useradd -ms /bin/bash -G sudo admin \ 73 | && echo "admin ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/admin \ 74 | && chmod 0440 /etc/sudoers.d/admin \ 75 | && chmod +x /usr/local/bin/wp /run.sh \ 76 | && mkdir -m 0700 /app \ 77 | && chown admin:admin /app \ 78 | && printf '%s\t%s\t%s\t%s%s%s%s%s%s%s%s\t%d\t%d\n' \ 79 | '/app' \ 80 | '/var/www/html' \ 81 | 'fuse.bindfs' \ 82 | 'force-user=www-data,' \ 83 | 'force-group=www-data,' \ 84 | 'create-for-user=admin,' \ 85 | 'create-for-group=admin,' \ 86 | 'create-with-perms=0644:a+X,' \ 87 | 'chgrp-ignore,' \ 88 | 'chown-ignore,' \ 89 | 'chmod-ignore' \ 90 | 0 \ 91 | 0 > /etc/fstab 92 | 93 | USER admin 94 | WORKDIR /app 95 | EXPOSE 80 443 96 | CMD ["/run.sh"] 97 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "visiblevc-wordpress", 3 | "version": "0.26.0", 4 | "description": "A slightly less shitty wordpress development workflow", 5 | "main": "index.js", 6 | "repository": "https://github.com/visiblevc/wordpress-starter.git", 7 | "author": "Karel Ledru-Mathe ", 8 | "maintainers": [ 9 | "Derek P Sifford " 10 | ], 11 | "license": "MIT", 12 | "scripts": { 13 | "build": "./build.sh" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if ! sudo mount -a 2> /dev/null; then 4 | printf '\e[1;31mERROR:\e[0m %s' \ 5 | 'Container running with improper privileges. 6 | 7 | Be sure your service is configured with the following options: 8 | ___ 9 | services: 10 | wordpress: 11 | cap_add: 12 | - SYS_ADMIN 13 | devices: 14 | - /dev/fuse 15 | # needed on certain cloud hosts 16 | security_opt: 17 | - apparmor:unconfined 18 | ___ 19 | 20 | OR (use first option if possible) 21 | ___ 22 | services: 23 | wordpress: 24 | privileged: true 25 | ___ 26 | 27 | ' | sed 's/^ //' 28 | exit 1 29 | fi 30 | 31 | # Environment 32 | # ------------ 33 | declare -x TERM="${TERM:-xterm}" 34 | declare PLUGINS="${PLUGINS//,/}" 35 | declare THEMES="${THEMES//,/}" 36 | 37 | # Runtime 38 | # ------------ 39 | declare default_theme=twentytwenty 40 | declare -A plugin_deps 41 | declare -A theme_deps 42 | 43 | # Configuration 44 | # ------------- 45 | mkdir -p ~/.wp-cli 46 | echo -e " 47 | path: /app 48 | color: true 49 | apache_modules: 50 | - mod_rewrite 51 | 52 | config create: 53 | dbhost: ${DB_HOST:-db}:3306 54 | dbname: ${DB_NAME:-wordpress} 55 | dbpass: ${DB_PASS:-root} 56 | dbprefix: ${DB_PREFIX:-wp_} 57 | dbuser: ${DB_USER:-root} 58 | dbcharset: ${DB_CHARSET:-utf8} 59 | extra-php: | 60 | define('WP_DEBUG', ${WP_DEBUG:-false}); 61 | define('WP_DEBUG_LOG', ${WP_DEBUG_LOG:-false}); 62 | define('WP_DEBUG_DISPLAY', ${WP_DEBUG_DISPLAY:-true}); 63 | $(sed '1 ! s/.*/ \0/' < <(echo -e "${EXTRA_PHP:-}")) 64 | 65 | core download: 66 | locale: ${WP_LOCALE:-en_US} 67 | skip-content: true 68 | version: ${WP_VERSION:-latest} 69 | 70 | core install: 71 | admin_email: ${ADMIN_EMAIL:-admin@${SERVER_NAME:-localhost.com}} 72 | admin_password: ${DB_PASS:-root} 73 | admin_user: ${DB_USER:-root} 74 | skip-email: true 75 | title: ${DB_NAME:-wordpress} 76 | url: ${URL_REPLACE:-localhost:8080} 77 | 78 | rewrite structure: 79 | hard: true 80 | 81 | " > ~/.wp-cli/config.yml 82 | 83 | main() { 84 | h1 'Begin WordPress Installation' 85 | init 86 | 87 | h2 'Waiting for MySQL to initialize...' 88 | while ! mysqladmin ping \ 89 | --host="${DB_HOST:-db}" \ 90 | --user="${DB_USER:-root}" \ 91 | --password="${DB_PASS:-root}" \ 92 | --silent > /dev/null; do 93 | sleep 1 94 | done 95 | 96 | h2 'Configuring WordPress' 97 | wp config create --force |& logger 98 | 99 | h2 'Checking database' 100 | check_database 101 | 102 | if [[ "$MULTISITE" == 'true' ]]; then 103 | h2 'Enabling Multisite' 104 | wp core multisite-convert |& logger 105 | fi 106 | 107 | h2 'Checking themes' 108 | check_themes 109 | 110 | h2 'Checking plugins' 111 | check_plugins 112 | 113 | h2 'Finalizing' 114 | if [[ $MULTISITE != true ]]; then 115 | wp rewrite structure \ 116 | "${PERMALINKS:-/%year%/%monthnum%/%postname%/}" |& logger 117 | fi 118 | 119 | if [[ -e /docker-entrypoint-initwp.d ]]; then 120 | h2 'Executing user init scripts' 121 | for file in /docker-entrypoint-initwp.d/*; do 122 | [[ -x $file ]] && "$file" 123 | done 124 | fi 125 | 126 | h1 'WordPress Installation Complete!' 127 | 128 | sudo rm -f /var/run/apache2/apache2.pid 129 | sudo apache2-foreground 130 | } 131 | 132 | # Config Functions 133 | # --------------------- 134 | 135 | init() { 136 | declare raw_line 137 | declare -a keyvalue 138 | 139 | for raw_line in $PLUGINS; do 140 | mapfile -t keyvalue < <( 141 | sed -n ' 142 | s/.*\[\(.*\)\]\([^[:blank:]]*\).*/\1\n\2/p # Matches [key]value form 143 | t # If previous match succeeds, skip to end 144 | {p; p;} # Assumes normal form 145 | ' <<< "$raw_line" 146 | ) 147 | plugin_deps[${keyvalue[0]}]="${keyvalue[1]}" 148 | done 149 | 150 | for raw_line in $THEMES; do 151 | mapfile -t keyvalue < <( 152 | sed -n ' 153 | s/.*\[\(.*\)\]\([^[:blank:]]*\).*/\1\n\2/p # Matches [key]value form 154 | t # If previous match succeeds, skip to end 155 | {p; p;} # Assumes normal form 156 | ' <<< "$raw_line" 157 | ) 158 | theme_deps[${keyvalue[0]}]="${keyvalue[1]}" 159 | done 160 | 161 | # If no theme dependencies or volumes exist, fall back to default 162 | if [[ ${#theme_deps[@]} == 0 && ! -d /app/wp-content/themes ]]; then 163 | theme_deps["$default_theme"]="$default_theme" 164 | fi 165 | 166 | # Apache config adustments 167 | sudo sed -i \ 168 | -e "/^[[:blank:]]*.ServerName www.example.com/{c\\" \ 169 | -e "\\tServerName ${SERVER_NAME:-localhost} \\" \ 170 | -e "\\tServerAlias www.${SERVER_NAME:-localhost}" \ 171 | -e '}' /etc/apache2/sites-available/000-default.conf 172 | 173 | sudo chown -R admin:admin /app 174 | 175 | if [[ ! -f /app/wp-settings.php ]]; then 176 | h2 'Downloading WordPress' 177 | wp core download |& logger 178 | fi 179 | } 180 | 181 | # this is ran in a subshell to isolate nullglob as it for whatever reason 182 | # interacts with our $PLUGINS and $THEMES variables. 183 | # See: https://github.com/visiblevc/wordpress-starter/issues/176 184 | check_database() ( 185 | shopt -s nullglob 186 | declare file 187 | declare -i num_imported=0 188 | 189 | wp core is-installed 2> /dev/null && return 190 | 191 | wp db create |& logger 192 | 193 | wp core install |& logger 194 | 195 | for file in /data/*.sql; do 196 | h2 "Importing $file (this might take a while)..." 197 | wp db import "$file" |& logger 198 | ((num_imported++)) 199 | done 200 | 201 | if ((num_imported > 0)); then 202 | if [[ -n $URL_REPLACE ]]; then 203 | h2 "Replacing URLs in database (this might take a while)..." 204 | wp search-replace \ 205 | --skip-columns=guid \ 206 | --report-changed-only \ 207 | --no-report \ 208 | "$(wp option get siteurl)" \ 209 | "$URL_REPLACE" |& logger 210 | fi 211 | 212 | h2 'Updating database...' 213 | wp core update-db |& logger 214 | fi 215 | ) 216 | 217 | check_plugins() { 218 | declare key 219 | 220 | for key in "${!plugin_deps[@]}"; do 221 | wp plugin is-installed "$key" || wp plugin install "${plugin_deps[$key]}" |& logger 222 | wp plugin is-active "$key" || wp plugin activate "$key" |& logger 223 | done 224 | } 225 | 226 | check_themes() { 227 | declare key 228 | 229 | for key in "${!theme_deps[@]}"; do 230 | wp theme is-installed "$key" || wp theme install "${theme_deps[$key]}" |& logger 231 | wp theme is-active "$key" || wp theme activate "$key" |& logger 232 | done 233 | } 234 | 235 | # Helpers 236 | # --------------------- 237 | 238 | declare -i term_width=70 239 | 240 | h1() { 241 | declare border padding text 242 | border='\e[1;34m'"$(printf '=%.0s' $(seq 1 "$term_width"))"'\e[0m' 243 | padding="$(printf ' %.0s' $(seq 1 $(((term_width - $(wc -m <<< "$*")) / 2))))" 244 | text="\\e[1m$*\\e[0m" 245 | echo -e "$border" 246 | echo -e "${padding}${text}${padding}" 247 | echo -e "$border" 248 | } 249 | 250 | h2() { 251 | printf '\e[1;33m==>\e[37;1m %s\e[0m\n' "$*" 252 | } 253 | 254 | logger() { 255 | fold --width $((term_width - 9)) -s | sed -n ' 256 | /^\x1b\[[0-9;]*m/{ # match any line beginning with colorized text 257 | s/Error:/ \0/ # pads line so its length matches others 258 | p # any lines containing color 259 | b # branch to end 260 | } 261 | s/.*/ \0/p # pads all other lines with 9 spaces 262 | ' 263 | } 264 | 265 | main 266 | --------------------------------------------------------------------------------