Сегодня я хочу поделиться с достопочтенной аудиторией Хабра своим подходом к организации автоматической сборки проекта на WordPress, который значительно экономит время при создании новых сайтов.
Предпосылки
И так, вы делаете сайты на WordPress, и с каждым новым проектом вам приходится идти на wordpress.org, качать от туда, собственно, сам WordPress + набор плагинов, которые вы используете постоянно. Или же установкой плагинов вы занимаетесь прямо из админ-панели или того хуже — копируете их из директории предыдущего сайта. Мне это всегда не нравилось, как-то не элегантно что ли, не удовлетворяет эстетическим потребностям. К тому же занимает хоть немного, но все же время. Поэтому я задумался, как бы этот процесс улучшить. Скачал все что нужно, сложил аккуратненько в папочку и выполнил «git init» и «git push». Что ж, теперь у меня есть репозиторий на Bitbucket, где хранится моя сборка WP со всем необходимым. С этого момента в начале процесса разработки можно выполнить «git clone» и получить нечто готовое к работе. Способ радовал меня не долго — обнаружились «недостатки». А именно:
- избыточное использование репозитория (хранятся все иcходники плагинов и самой CMS);
- всегда хранится старая версия всего (можно конечно периодически обновлять, но лень);
- хочется хранить в том же репозитории исходники SCSS/SASS/LESS, не минифицированный JS-код и прочие важные компоненты, которые по идее не должны пересекаться с production-версией проекта;
Тут я и моя лень посовещались и пришли к выводу, что при начале работы над новым сайтом мы готовы тратить энергии не более чем на ввод одной (максимум двух) консольных команд для организации всего и вся и переходу непосредственно к процессу разработки. Потом лень отдельно от меня подумала и продолжила: «и что бы в Git все хранилось сразу, и что бы не надо было новые версии накатывать (изначально мол новые должны быть), и что бы можно было на сервере pull корректно выполнять (тебе ж потом это обслуживать все), и что бы вообще все само работало, и приступай побыстрее, а я пока отдохну».
Удовлетворяем хотелки лени
Первоначально я формализовал задачи в небольшой список:
- автоматизировать установку ядра WordPress и актуальных версий плагинов кочующих из проекта в проект;
- реализовать зависимость настроек проекта от серверного окружения;
- отделить исходники клиентской части от проекта;
- автоматизировать сборку клиентской части;
- организовать не избыточное хранение в Git-репозитории.
И приступил к реализации. Для начала я отправился читать документацию WP и нашел там прекрасную вещь, которая позволяет отделить ядро CMS от того, что изменяет разработчик. Набросал по этому случаю следующую структуру проекта:
content/
wp/
index.php
wp-config.php
В директории «wp» хранятся файлы ядра WordPress, «content» — папка для тем, плагинов, языковых версий и т.д., «wp-config.php» — стандартный файл настроек WP, а в «index.php», руководствуясь документацией я поместил следующее:
define('WP_USE_THEMES', true);
require( dirname( __FILE__ ) . '/wp/wp-blog-header.php' );
Запустил на сервере, проверил, ок, работает. Теперь нужно сделать так, что бы скачивалась самая последняя версия WP. Для этого я использовал Composer (как его установить, можно почитать тут). Все файлы, которые я создал ранее я поместил в папку «app», для того, что бы вынести все служебные файлы на уровень выше от исполняемого «index.php». В дальнейшем мой сайт будет запускаться из этой директории (не забудьте поправить настройки хоста для вашего сервера). А папка «wp» была вычищена от всего содержимого. В корень проекта я поместил файл «composer.json» со следующим содержимым:
{
"require": {
"php": ">=5.4",
"johnpbloch/wordpress": "*",
},
"extra": {
"wordpress-install-dir": "app/wp",
}
}
«johnpbloch/wordpress» — форк WP, пригодный для установки через Composer, а «wordpress-install-dir» указывает на директорию установки ядра CMS. Написав в консоли:
composer install
я убедился, что все работает. Свежий WordPress скачался в «app/wp». Что же с плагинами? С ними все хорошо, благодаря проекту wpackagist.org их так же можно подтягивать через Composer. Для этого нужно лишь немного модифицировать «composer.json»:
{
"repositories":[
{
"type":"composer",
"url":"https://wpackagist.org"
}
],
"require": {
"php": ">=5.4",
"johnpbloch/wordpress": "*",
"wpackagist-plugin/rus-to-lat-advanced": "*",
"wpackagist-plugin/advanced-custom-fields": "*",
"wpackagist-plugin/all-in-one-seo-pack": "*",
"wpackagist-plugin/google-sitemap-generator": "*",
"wpackagist-plugin/contact-form-7": "*",
"wpackagist-plugin/woocommerce": "*",
"wpackagist-plugin/saphali-woocommerce-lite": "*"
},
"extra": {
"wordpress-install-dir": "app/wp",
"installer-paths": {
"app/content/plugins/{$name}/": ["vendor:wpackagist-plugin"],
"app/content/themes/{$name}/": ["vendor:wpackagist-theme"]
}
}
}
В секции «repositories» указывается адрес «wpackagist», в секции «installer-paths» указываются пути, куда будут устанавливаться плагины и темы, а в секции «require» добавляются названия WP-плагинов в виде «wpackagist-plugin/{{plugin_name}}». В «wpackagist» доступны почти все плагины с wordpress.org, доступность плагинов можно смотреть в поиске на сайте wpackagist.org.
Выполнив:
composer update
увидел, как в директории «app/content/plugins» появились все нужные плагины. Теперь нужно разобраться с настройками, напомню, что стоит задача сделать настройки БД и дебага зависимыми от среды разработки, на локальном сервере свои, на боевом свои. Для этого выдавим их в отдельный файл «local-config.php»:
define( 'DB_NAME', '%%DB_NAME%%' );
define( 'DB_USER', '%%DB_USER%%' );
define( 'DB_PASSWORD', '%%DB_PASSWORD%%' );
define( 'DB_HOST', '%%DB_HOST%%' ); // Probably 'localhost'
ini_set( 'display_errors', true );
define( 'WP_DEBUG_DISPLAY', true );
define( 'AUTH_KEY', 'put your unique phrase here' );
define( 'SECURE_AUTH_KEY', 'put your unique phrase here' );
define( 'LOGGED_IN_KEY', 'put your unique phrase here' );
define( 'NONCE_KEY', 'put your unique phrase here' );
define( 'AUTH_SALT', 'put your unique phrase here' );
define( 'SECURE_AUTH_SALT', 'put your unique phrase here' );
define( 'LOGGED_IN_SALT', 'put your unique phrase here' );
define( 'NONCE_SALT', 'put your unique phrase here' );
и изменим «wp-config.php» следующим образом:
if ( file_exists( dirname( __FILE__ ) . '/local-config.php' ) ) {
define( 'WP_LOCAL_DEV', true );
include( dirname( __FILE__ ) . '/local-config.php' );
} else {
define( 'WP_LOCAL_DEV', false );
define( 'DB_NAME', '%%DB_NAME%%' );
define( 'DB_USER', '%%DB_USER%%' );
define( 'DB_PASSWORD', '%%DB_PASSWORD%%' );
define( 'DB_HOST', '%%DB_HOST%%' ); // Probably 'localhost'
ini_set( 'display_errors', 0 );
define( 'WP_DEBUG_DISPLAY', false );
define( 'AUTH_KEY', 'put your unique phrase here' );
define( 'SECURE_AUTH_KEY', 'put your unique phrase here' );
define( 'LOGGED_IN_KEY', 'put your unique phrase here' );
define( 'NONCE_KEY', 'put your unique phrase here' );
define( 'AUTH_SALT', 'put your unique phrase here' );
define( 'SECURE_AUTH_SALT', 'put your unique phrase here' );
define( 'LOGGED_IN_SALT', 'put your unique phrase here' );
define( 'NONCE_SALT', 'put your unique phrase here' );
}
Теперь, если существует файл «local-config.php», настройки будут подхватываться из него. Этот файл нужно добавить в ".gitignor" (зачем нам пароли от БД в репозитории?). Самое время внести данные для доступа к базе данных в «local-config.php», запустить процедуру установки WordPress и посетить админку.
В админке нужно посетить раздел «Настройки -> Общие» и там поправить адреса, следующим образом:
Адрес WordPress c "/wp" на конце, адрес сайта без "/wp".
Здорово, сайтом можно пользоваться. Следующим этап я посвятил пользовательским стилям и скриптам (а то как-то не логично, на сервере все само собирается, а всякие jquery вручную качать?). В качестве подготовки я отредактировал структуру проекта:
app/
content/
theme/
mytheme/
build/
index.php
style.css
wp/
index.php
local-config.php
wp-config.php
src/
fonts/
js/
main.js
scss/
style.ccss
composer.json
В папке «src/» хранятся исходные файлы шрифтов, скриптов и стилей. Далее они собираются с помощью gulp, минифицируются и складываются в папку «app/content/theme/mytheme/build». В качестве препроцессора для CSS я использую SCSS (как установить, думаю всем известно, но если нет, то вот инструкция), для сборки JS — browserify. Посчитал логичным, что зависимости клиентской части нужно подтягивать при помощи nmp. Файл «package.json» у меня получился такой:
{
"devDependencies": {
"bourbon": "*",
"bourbon-neat": "*",
"browserify": "*",
"fullpage.js": "*",
"gulp": "*",
"gulp-clean-css": "*",
"gulp-concat": "*",
"gulp-sass": "*",
"gulp-sourcemaps": "*",
"gulp-uglify": "*",
"jquery": "*",
"normalize-scss": "*",
"vinyl-source-stream": "*"
}
}
Секции кроме «devDependencies», заполнять не стал, поскольку публиковать это в npm я явно не планирую. Пишу в консоли:
npm install
Жду пару минут и вижу, что все указанные зависимости аккуратно оказались в «node_modules». Вишенкой на торте послужил файл «gulpfile.js» с таким содержимым:
'use strict';
var browserify = require('browserify'),
source = require('vinyl-source-stream'),
gulp = require('gulp'),
sass = require('gulp-sass'),
uglify = require('gulp-uglify'),
cleanCSS = require('gulp-clean-css'),
sourcemaps = require('gulp-sourcemaps'),
sourcePath = './src/',
buildPath = './app/content/themes/mytheme/build/';
//scss
gulp.task('scss', function () {
return gulp.src('./src/scss/style.scss')
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest(buildPath + 'css'));
});
gulp.task('scss:watch', function () {
return gulp.watch(sourcePath + 'scss/**/*.scss', ['scss']);
});
//js
gulp.task('browserify', function() {
return browserify(sourcePath + 'js/main.js')
.bundle()
.pipe(source('main.js'))
.pipe(gulp.dest(buildPath + 'js'));
});
gulp.task('browserify:watch', function () {
return gulp.watch(sourcePath + 'js/**/*.js', ['browserify']);
});
//fonts
gulp.task('copy:fonts', function () {
gulp.src(sourcePath + 'fonts/**/*', {base: sourcePath + 'fonts'})
.pipe(gulp.dest(buildPath + 'fonts'));
});
//minify
gulp.task('minify:js', ['browserify'], function(){
return gulp.src(buildPath + 'js/*.js')
.pipe(sourcemaps.init())
.pipe(uglify())
.pipe(sourcemaps.write())
.pipe(gulp.dest(buildPath + 'js'))
});
gulp.task('minify:css', ['scss'], function(){
return gulp.src(buildPath + 'css/*.css')
.pipe(cleanCSS({compatibility: 'ie9'}))
.pipe(gulp.dest(buildPath + 'css'));
});
//task groups
gulp.task('default', ['copy:fonts', 'scss', 'browserify']);
gulp.task('watch', ['copy:fonts', 'scss:watch', 'browserify:watch']);
gulp.task('production', ['copy:fonts', 'scss', 'browserify', 'minify:js', 'minify:css']);
Команда «gulp» скопирует шрифты, скомпилирует SCSS, склеит JS и сложит это все аккуратненько в папку билда. «gulp watch» делает тоже самое, но при каждом изменении файла. «gulp production» дополнительно почистит файлы от комментариев и минифицирует.
Что в итоге?
В итоге вышеописанное вам повторять совсем не обязательно. Я все удобненько залил на GitHub: https://github.com/IvanZhuck/kosher_wp_seeder.
Для запуска необходимо клонировать репозиторий и выполнить следующие команды (предварительно поправив список плагинов и зависимостей, если это требуется):
composer install
npm install
Я и моя лень довольны, проекты стали стартовать быстрее, а работа приятнее. Ваши вопросы и предложения жду в комментариях.
Комментарии (13)
Magi
05.09.2016 18:37+1По моему с помощью wp-cli.org аналогичные задачи решаются значительно проще. Одним скриптом с командами я могу получить полностью настроенный WP с нужными плагинами, темами и опциями, а затем обновлять неограниченное кол-во сайтов. Я сейчас как раз с ним с ним разбираюсь. Там можно выгрузить все опции WP и плагинов в файл. Выгружаем из свежеустановленного и настроенного, сравниваем и дорабатываем скрипт, добавив команды установки нужных опций.
Если вдруг кто-то разобрался с wp-cli подробнее, подскажите как можно обновлять WP при доступе только через http и ftp. Ключ такой есть, но примеров работы нет.
arku
06.09.2016 03:13У этого всего есть очень неявное поведение. Допустим вам необходимо поместить некий файл (например для яндекс верификации) в корень проекта. Затем у вас разряжается ваш любимый ноутбук и вы идете за компьютер, благо вы успели поместить этот файл в репозиторий. На компьютере вы стягиваете изменения и пишите composer install. Вуаля, файл пропал. Аналогично могут пропасть файлы загруженные через админку, что куда менее приятно. Для проверки — просто удалите vendor и запустите composer install заново :)
ivan_zhuck
06.09.2016 03:15Корень проекта к «composer install» не имеет отношения как бы совсем, поэтому все что вы туда сложите останется там. Это же относится и к файлам загруженным через админку (мы их специально отделили от ядра системы и вынесли в отдельную директорию).
arku
06.09.2016 14:40Под корнем проекта я имел ввиду корень папки app, где размещен WP. Рекомендую проверить, дело нехитрое, займет меньше минуты, что куда меньше потенциальных проблем.
ivan_zhuck
06.09.2016 14:45Я тоже имел ввиду эту папку. Вы можете развернуть проект локально и убедится, что все работает.
arku
06.09.2016 15:33+1Не заметил пункт, который гласит о размещении таких файлов в app и запуске веб-сервера из него. В таком случае все верно и этой ситуации не произойдет. Спасибо за разъяснение!
Makaruga
06.09.2016 05:32+3Ну дела…
Знакомьтесь:
https://roots.io/bedrock/
https://github.com/roots/bedrock
Использую уже пару лет, брат жив.ivan_zhuck
06.09.2016 05:38+3Что ж, спасибо за информацию, не знал о существовании этого проекта. Значит мое решение будет альтернативным, к тому же я описал детально как это работает, думаю, это будет интересно многим читателям.
Al-Vas
06.09.2016 13:28как вариант, ставим вордпрес, ставим данный плагин Installation Profiles, загружаем с его помощью список плагинов
peter41
07.09.2016 00:44Как вариант ставим плагин Duplicator — он собирает полностью установленный Wordpress со всеми плагинами для другого домена и делает скрипт для преноса.
Alabastr
12.09.2016 11:47+1Спасибо, про плагины ранее не задумывался.
Сам захожу издалека — с создания изолированного пользователя на хост-машине, а потому просто скрипт спрашивает, нужен ли свежий WP. Скрипт на баш, кому интресно.
gavriloff
Всегда думал, как же ещё можно поизвращаться с WP. Спасибо за статью xD