diff --git a/docs/README.md b/docs/README.md index e5314ce..2b34d12 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,6 +14,7 @@ This package contains [Deployer](https://deployer.org/) recipes used to help dep ## Recipes +* [WordPress](recipes/wordpress.md) * [Static site](recipes/static.md) - * [Slack](recipes/slack.md) - send a notification to Slack when a deployment is complete diff --git a/docs/installation.md b/docs/installation.md index 81709e7..f487d34 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -53,6 +53,8 @@ For deploying WordPress websites. cp vendor/studio24/deployer-recipes/examples/wordpress.php ./deploy.php ``` +See [WordPress docs](recipes/wordpress.md). + [Source](../recipe/wordpress.php) ### Laravel: diff --git a/docs/recipes/wordpress.md b/docs/recipes/wordpress.md new file mode 100644 index 0000000..066f065 --- /dev/null +++ b/docs/recipes/wordpress.md @@ -0,0 +1,172 @@ +# WordPress + +Deploy a WordPress site. + +[Source](../../recipe/wordpress.php) + +## Background + +We manage our WordPress sites using the following directory structure: + +``` +├── docs +└── web +| ├── content +| | ├── cache +| | ├── mu-plugins +| | ├── plugins +| | ├── themes +| | └── uploads +| ├── wordpress +| └── wp-config.php +├── composer.json +├── .env +└── README.md +``` + +WordPress is not in source control and is installed to `web/wordpress`. This folder is set to be writable to support auto-updates to WordPress core. + +Source controlled plugins and themes are in the `web/content` folder. + +Configuration is stored in `web/wp-config.php` + +Sensitive or environment configuration values are stored in a `.env` file in the project root. + +Documentation is stored in the `docs` folder. + +## Installation + +### Requirements + +This recipe requires [WP CLI](https://wp-cli.org/) to exist on the remote server. + +### Recipe + +You can copy an example deployment file: + +``` +cp vendor/studio24/deployer-recipes/examples/wordpress.php ./deploy.php +``` + +### Loading environment variables in wp-config + +Local environment variables set in `.env` can be loaded in your wp-config file. + +First install [vlucas/phpdotenv](https://packagist.org/packages/vlucas/phpdotenv) and include the Composer autoloader. + +Set your local environment variables in `.env` via name and value pairs: + +``` +DB_NAME="wp_database" +S3_BUCKET="devbucket" +``` + +This can be loaded in your wp-config file via: + +```php +$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../'); +$dotenv->load(); +``` + +You can then reference the environment values in wp-config via `$_ENV`. E.g. + +```php +define('DB_NAME', $_ENV['DB_NAME']); +``` + +You can also refer to other environment variables in your `.env` file via `${NAME}`. E.g. + +``` +BASE_DIR="/var/webroot/project-root" +CACHE_DIR="${BASE_DIR}/cache" +``` + + +## Tasks + +The static site recipe has 2 new tasks it runs: + +### WordPress + +WordPress core is not in source control and should have auto-update enabled on the server. + +You can change the WordPress path via: + +``` +set('wordpress_path', 'web/wordpress'); +``` + +If you change it from the default, you will need to add this path to `shared_dirs` and `writable_dirs`. + +#### WordPress update strategy + +To let WordPress run updates automatically the `WP_AUTO_UPDATE_CORE` setting must exist in your config: + +``` +// Minor updates are enabled, development, and major updates are disabled (recommended for production) +define('WP_AUTO_UPDATE_CORE', 'minor'); + +// Development, minor, and major updates are all enabled +define('WP_AUTO_UPDATE_CORE', true); +``` + +It is also possible to configure this via [filters](https://developer.wordpress.org/advanced-administration/upgrade/upgrading/#configuration-via-filters). + +#### On deployment + +On deployment, if WordPress does not exist in this path, it installs it. If WordPress does exist, no core updates are made on deployment. + +#### Manually running WordPress updates + +If you need to manually update WordPress run: + +```bash +dep wp core update staging +``` + +You can update to a specific version via: + +```bash +dep wp core update --version= staging +``` + +### Composer + +If `composer.json` exists in the project root, the `deploy:vendors` task is automatically run. + +You can check this by running `dep tree deploy` which will show you the tasks to be run. + +#### Multiple Composer files in a WordPress project + +It is not recommended to have more than one composer.json file in a project. If you do have one, you should move the +dependencies into the root composer file. + +## WP CLI +If you need to call any [WP CLI](https://wp-cli.org/) commands this recipe includes the `wp()` function to allow you to do this. + +This requires WP CLI to exist on the server. + +Usage: + +```php +wp('command'); +``` + +E.g. to run `wp core version` + +```php +wp('core version'); +``` + +This automatically sets the current environment using the `stage` variable. You can pass this manually as the second param: + +```php +wp('core version', 'staging'); +``` + +## Configuration + +### Required configuration + +* `wordpress_path`: directory to install WordPress to relative to the release_path + diff --git a/recipe/wordpress.php b/recipe/wordpress.php index 6d12fa4..d0107cf 100644 --- a/recipe/wordpress.php +++ b/recipe/wordpress.php @@ -2,25 +2,29 @@ namespace Deployer; -require_once 'recipe/wordpress.php'; +require_once 'recipe/common.php'; require_once __DIR__ . '/common.php'; +// Path to WordPress core +set('wordpress_path', 'web/wordpress'); + // Shared files that need to persist between deployments set('shared_files', [ - 'config/wp-config.local.php' + '.env' ]); // Shared directories that need to persist between deployments set('shared_dirs', [ '.well-known', - 'web/wp-content/uploads', - 'web/wp-content/cache', + 'web/content/uploads', + 'web/content/cache', ]); // Writable directories set('writable_dirs', [ - 'web/wp-content/uploads', - 'web/wp-content/cache' + 'web/content/uploads', + 'web/content/cache', + '{{wordpress_path}}', ]); // Deployment and HTTP users @@ -33,6 +37,64 @@ // Array of remote => local file locations to sync to your local dev environment set('sync', [ 'images' => [ - 'shared/web/wp-content/uploads/' => 'web/wp-content/uploads' + 'shared/web/content/uploads/' => 'web/content/uploads' ], ]); + +// Install WordPress +task('deploy:wordpress_install', function() { + $wordPressPath = get('wordpress_path', false); + + cd('{{release_path}}'); + run(sprintf('mkdir -p %s', $wordPressPath)); + $stage = get('stage'); + + // Is WP already installed? + if (test(sprintf('[ -f %s/wp-blog-header.php ]', $wordPressPath))) { + writeln('Skipping WordPress download, current installed version: '); + } else { + // Install WP + // @see https://developer.wordpress.org/cli/commands/core/download/ + wp(sprintf('core download --skip-content --path=%s', $wordPressPath), $stage); + writeln('Downloaded WordPress version: '); + } + wp(sprintf('core version --path=%s', $wordPressPath), $stage); +}); + +// Optionally install Composer vendors if composer.json exists in project root +task('deploy:vendors_if_exists', function() { + if (test('[ -f {{release_path}}/composer.json ]')) { + writeln('Install vendors, composer.json found'); + invoke('deploy:vendors'); + } else { + writeln('Skipping, no composer.json found'); + } +}); + +/** + * Run WP CLI + * + * @param string $command wp command, e.g. core download + * @param ?string $stage environment, e.g. production. Defaults to the current stage name + * @return void + * @throws Exception\Exception + * @throws Exception\RunException + * @throws Exception\TimeoutException + */ +function wp(string $command, ?string $stage = null) +{ + if (null === $stage) { + $stage = get('stage', 'production'); + } + run(sprintf('WP_ENV=%s wp %s', $stage, $command), real_time_output: true); +} + + +// Deployment tasks +desc('Deploys your project'); +task('deploy', [ + 'deploy:prepare', + 'deploy:vendors_if_exists', + 'deploy:wordpress_install', + 'deploy:publish', +]);