Сегодня я хочу поделиться с достопочтенной аудиторией Хабра своим подходом к организации автоматической сборки проекта на WordPress, который значительно экономит время при создании новых сайтов.

Предпосылки


И так, вы делаете сайты на WordPress, и с каждым новым проектом вам приходится идти на wordpress.org, качать от туда, собственно, сам WordPress + набор плагинов, которые вы используете постоянно. Или же установкой плагинов вы занимаетесь прямо из админ-панели или того хуже — копируете их из директории предыдущего сайта. Мне это всегда не нравилось, как-то не элегантно что ли, не удовлетворяет эстетическим потребностям. К тому же занимает хоть немного, но все же время. Поэтому я задумался, как бы этот процесс улучшить. Скачал все что нужно, сложил аккуратненько в папочку и выполнил «git init» и «git push». Что ж, теперь у меня есть репозиторий на Bitbucket, где хранится моя сборка WP со всем необходимым. С этого момента в начале процесса разработки можно выполнить «git clone» и получить нечто готовое к работе. Способ радовал меня не долго — обнаружились «недостатки». А именно:

  • избыточное использование репозитория (хранятся все иcходники плагинов и самой CMS);
  • всегда хранится старая версия всего (можно конечно периодически обновлять, но лень);
  • хочется хранить в том же репозитории исходники SCSS/SASS/LESS, не минифицированный JS-код и прочие важные компоненты, которые по идее не должны пересекаться с production-версией проекта;

Тут я и моя лень посовещались и пришли к выводу, что при начале работы над новым сайтом мы готовы тратить энергии не более чем на ввод одной (максимум двух) консольных команд для организации всего и вся и переходу непосредственно к процессу разработки. Потом лень отдельно от меня подумала и продолжила: «и что бы в Git все хранилось сразу, и что бы не надо было новые версии накатывать (изначально мол новые должны быть), и что бы можно было на сервере pull корректно выполнять (тебе ж потом это обслуживать все), и что бы вообще все само работало, и приступай побыстрее, а я пока отдохну».

Удовлетворяем хотелки лени


Первоначально я формализовал задачи в небольшой список:

  1. автоматизировать установку ядра WordPress и актуальных версий плагинов кочующих из проекта в проект;
  2. реализовать зависимость настроек проекта от серверного окружения;
  3. отделить исходники клиентской части от проекта;
  4. автоматизировать сборку клиентской части;
  5. организовать не избыточное хранение в 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)


  1. gavriloff
    05.09.2016 17:59
    +2

    Всегда думал, как же ещё можно поизвращаться с WP. Спасибо за статью xD


  1. Magi
    05.09.2016 18:37
    +1

    По моему с помощью wp-cli.org аналогичные задачи решаются значительно проще. Одним скриптом с командами я могу получить полностью настроенный WP с нужными плагинами, темами и опциями, а затем обновлять неограниченное кол-во сайтов. Я сейчас как раз с ним с ним разбираюсь. Там можно выгрузить все опции WP и плагинов в файл. Выгружаем из свежеустановленного и настроенного, сравниваем и дорабатываем скрипт, добавив команды установки нужных опций.
    Если вдруг кто-то разобрался с wp-cli подробнее, подскажите как можно обновлять WP при доступе только через http и ftp. Ключ такой есть, но примеров работы нет.


  1. craft37
    06.09.2016 03:12

    То что нужно, спасибо.


  1. arku
    06.09.2016 03:13

    У этого всего есть очень неявное поведение. Допустим вам необходимо поместить некий файл (например для яндекс верификации) в корень проекта. Затем у вас разряжается ваш любимый ноутбук и вы идете за компьютер, благо вы успели поместить этот файл в репозиторий. На компьютере вы стягиваете изменения и пишите composer install. Вуаля, файл пропал. Аналогично могут пропасть файлы загруженные через админку, что куда менее приятно. Для проверки — просто удалите vendor и запустите composer install заново :)


    1. ivan_zhuck
      06.09.2016 03:15

      Корень проекта к «composer install» не имеет отношения как бы совсем, поэтому все что вы туда сложите останется там. Это же относится и к файлам загруженным через админку (мы их специально отделили от ядра системы и вынесли в отдельную директорию).


      1. arku
        06.09.2016 14:40

        Под корнем проекта я имел ввиду корень папки app, где размещен WP. Рекомендую проверить, дело нехитрое, займет меньше минуты, что куда меньше потенциальных проблем.


        1. ivan_zhuck
          06.09.2016 14:45

          Я тоже имел ввиду эту папку. Вы можете развернуть проект локально и убедится, что все работает.


          1. arku
            06.09.2016 15:33
            +1

            Не заметил пункт, который гласит о размещении таких файлов в app и запуске веб-сервера из него. В таком случае все верно и этой ситуации не произойдет. Спасибо за разъяснение!


  1. Makaruga
    06.09.2016 05:32
    +3

    Ну дела…

    Знакомьтесь:
    https://roots.io/bedrock/
    https://github.com/roots/bedrock

    Использую уже пару лет, брат жив.


    1. ivan_zhuck
      06.09.2016 05:38
      +3

      Что ж, спасибо за информацию, не знал о существовании этого проекта. Значит мое решение будет альтернативным, к тому же я описал детально как это работает, думаю, это будет интересно многим читателям.


  1. Al-Vas
    06.09.2016 13:28

    как вариант, ставим вордпрес, ставим данный плагин Installation Profiles, загружаем с его помощью список плагинов


  1. peter41
    07.09.2016 00:44

    Как вариант ставим плагин Duplicator — он собирает полностью установленный Wordpress со всеми плагинами для другого домена и делает скрипт для преноса.


  1. Alabastr
    12.09.2016 11:47
    +1

    Спасибо, про плагины ранее не задумывался.
    Сам захожу издалека — с создания изолированного пользователя на хост-машине, а потому просто скрипт спрашивает, нужен ли свежий WP. Скрипт на баш, кому интресно.