[Из песочницы] Запускаем Gulp с вотчерами на обычном хостинге через админпанель
У тебя есть сайт с админпанелью и ты используешь или только собираешься использовать Gulp в этом проекте? Хочешь максимально работать с сайтом через админпанель, включая контроль над генератором ресурсов Gulp? Тогда под катом я покажу тебе простой способ управления Gulp’ом с вотчерами на сервере прямо из админпанели.
Это небольшой туториал по настройке и запуску Gulp’а на сервере средствами любой админпанели на PHP. Подразумевается, что запускается Gulp не для единовременной сборки ресурсов, а в режиме с вотчерами. Отлично подходит, если ты работаешь с проектом через Deployment Tools и локально он у тебя не запущен. При этом каждый раз билдить, например, SCSS локально и заливать результат уже надоело.
1. Тонкости хостинга
Так уж получилось, на моём хостинге не было поддержки Node. Пришлось поставить её отдельно в свою папку рядом с сайтами. Какую версию ставить, не принципиально, я использовал Node.js v5.12.0. Для установки просто распакуй архив куда-нибудь к себе на хостинг и добавь NodeJS bin
в Path (подробнее ниже).
Обрати внимание, если на твоём хостинге включено изолирование сайтов, то папку с Node нужно добавить в общедоступные. В противном случае, процессу gulp будет отказано в доступе к Node.
2. Настройка Node и Gulp
Тут ты подготовишь всё необходимое для запуска Gulp на сервере. В принципе, им уже можно будет отлично пользоваться через SSH, но основная цель: Настроить управление через админпанель.
2.1 Настраиваем Node
Если ты уже пользовался npm’ом на хостинге и у тебя там уже стоит Node, то пол дела сделано и можешь сразу переходить к пункту 2.2.
Для установки на сервере Gulp и всего необходимого для его запуска тебе нужно будет в корне проекта создать файл package.json
. Искренне надеюсь, что корень проекта у тебя лежит чуть выше папки public_html
.
Примерное содержимое файла package.json
:
{
"name": "Project Namet",
"version": "1.0.0",
"devDependencies": {
"browser-sync": "^2.14.0",
"gulp": "~3.9.0",
"gulp-autoprefixer": "^3.1.0",
"gulp-browserify": "~0.5.1",
"gulp-clean-css": "^2.0.12",
"gulp-concat": "~2.6.0",
"gulp-notify": "^2.2.0",
"gulp-plumber": "^1.1.0",
"gulp-sass": "~2.1.0",
"gulp-sourcemaps": "^1.6.0",
"gulp-uglify": "^1.5.1",
"gulp-util": "^3.0.7"
},
}
Набор плагинов для Gulp может быть любой, нужно смотреть, что именно ты используешь и зачем тебе это нужно. Что касается BrowserSync, то его запустить так и не удалось. Почему? Читай в самом конце в списке возможных проблем.
Далее, заблаговременно подключившись к серверу по SSH, ты должен настроить Path, чтобы установленная вручную Node попала в директории запуска (это, кстати, вовсе не гарантирует, что процесс сайта будет видеть эту Node или вообще хоть что-то. Там своё окружение со своим Path. Его настроишь уже через PHP.).
Установи Gulp и его компоненты из директории, где лежит package.json
:
npm imstall
У тебя появится в этой директории папка node_modules
, в которой будет всё что нужно для запуска Gulp.
Теперь нам нужно сконфигурировать Gulp.
2.2 Настраиваем Gulp под проект
Вообще было бы здорово, если бы ты умел это делать, но в любом случае по настройке конфигурационного файла Gulp есть много статей, я лишь приведу пример из своего проекта.
Для этого создай файл gulpfile.js
рядом с файлом package.json
с примерным наполнением:
'use strict';
var gulp = require('gulp');
var plumber = require('gulp-plumber');
var notify = require("gulp-notify");
var sass = require('gulp-sass');
var minifyCss = require('gulp-clean-css');
var sourcemaps = require('gulp-sourcemaps');
var autoprefixer = require('gulp-autoprefixer');
var browserSync = require('browser-sync').create();
var errorHandler = {
errorHandler: notify.onError({
title: 'Ошибка в плагине <%= error.plugin %>',
message: "Ошибка: <%= error.message %>"
})
};
gulp.task('scss', function(){
gulp.src('./scss/**/*.scss')
.pipe(plumber(errorHandler))
.pipe(sourcemaps.init())
.pipe(sass())
.pipe(autoprefixer())
.pipe(minifyCss({compatibility: 'ie8'}))
.pipe(sourcemaps.write({
includeContent: false
}))
.pipe(gulp.dest('./public_html/css/user'));
});
gulp.task('compiler', [
'scss'
]);
gulp.task('watch', ['compiler'], function(){
/*
browserSync.init({
host: 'http://mysite.ru',
online: false,
files: [
'./public_html/css/user/app.css'
]
});
*/
gulp.watch('./scss/**/*.scss', ['scss']);
});
gulp.task('default', ['watch']);
Я покажу на примере генерации только CSS, ты же можешь настроить Gulp как хочешь.
Подразумевается, что рядом с gulpfile.js
лежит папка scss
, в которой полная структура всех SCSS файлов. Если что, то найти и поменять путь к SCSS файлам в gulpfile.js
не сложно, их всего два:
- Место, куда смотрит таск генерации CSS
gulp.src('./scss/**/*.scss')
- Место, куда смотрит Watcher
gulp.watch('./scss/**/*.scss', ['scss']);
2.3 Проверка готовности инструментов
Если всё сделано правильно, то уже сейчас можно, подключившись по SSH, перейти в папку с gulpfile.js
и выполнить там:
gulp
При этом увидим какой-то дебаг от Gulp, либо предупреждения, которые тебе предстоит разрулить до перехода к следующему пункту. Сразу скажу, что проблема может быть в конфигурировании переменной Path для текущего подключенного пользователя, в ней обязательно должна быть папка bin
от Node и папка /node_modules/.bin
, которую должен устанавливать в Path сам NPM.
Другого рода проблемы, скорее будут относиться к неверной настройке gulpfile.js
.
3 Подготовка PHP для работы с Gulp
Для этого тебе сначала нужно понять, как это вообще будет работать, не опираясь на конкретный язык программирования, и лишь потом решить, что же из PHP нужно использовать. Благо вариантов не очень много, да и продумывать почти нечего.
Алгоритм следующий:
- Нужно при запросе на включение из админпанели запустить процесс Gulp в фоне и сохранить его PID где-нибудь.
- При запросе на выключение нужно проверить PID и убить работающий под ним процесс Gulp.
- При запросе статистики можно проверить существование работающего по текущему PID процесса Gulp и вернуть результат.
- Весь output фонового процесса Gulp стоит сохранять в файл и при желании можно также настроить отправку содержимого этого файла в админпанель по запросу (В статье не рассматривается этот пункт, можешь реализовать его в виде домашнего задания).
Вроде просто, теперь, какие средства PHP тебе понадобятся.
Самое первое и простое, это класс ExecProcess
где-то из обсуждения exec
на сайте PHP. Я его немного модифицировал и использую вот такой:
envList)){
$command .= 'export PATH=$PATH:'.implode(":", $this->envList).'; ';
}
if(!empty($this->root)){
$command .= 'cd '.$this->root.'; ';
}
if(!empty($this->command)){
$command .= 'nohup ' . $this->command . ' > /dev/null 2>&1 & echo $!;';
}
if(!empty($this->additional)){
$command .= implode("; ", $this->additional);
}
exec($command, $op);
$this->pid = intval($op[0]);
}
public function setPid($pid)
{
$this->pid = $pid;
}
public function setRoot($root)
{
$this->root = $root;
}
public function setEnv($envList)
{
$this->envList = $envList;
}
public function setCommand($commandLine)
{
$this->command = $commandLine;
}
public function setAdditional($additionalCommands)
{
$this->additional = $additionalCommands;
}
public function getPid()
{
return $this->pid;
}
public function status()
{
if(empty($this->pid)) return false;
$command = 'ps -p ' . $this->pid;
exec($command, $op);
return isset($op[1]);
}
public function start()
{
$this->runCom();
}
public function stop()
{
if(empty($this->pid)) return true;
$command = 'kill ' . $this->pid;
exec($command);
return !$this->status();
}
}
И класс, который будет запускать именно Gulp и контролировать его состояние, GulpProcess
:
execProcess = new ExecProcess();
// Начальная директория для запускаемого нами в итоге скрипта (gulp). У меня так сложилось, что она находится выше на один уровень скрипта index.php. Простыми словами, директория, в которой лежит gulpfile.js
$this->execProcess->setRoot("../");
// Те самые директории (абсолютные пути) к Node и node_modules для Path
$this->execProcess->setEnv([
NODE_MODULES_BIN,
NODE_BIN,
]);
/* Тестировал. Может пригодиться)
$this->execProcess->setAdditional([
"echo \$PATH",
"pwd",
"whoami",
"ps -ela",
"id",
]);
*/
$this->execProcess->setCommand('gulp');
}
public function start()
{
if(!$this->isActive()){
$this->execProcess->start();
$this->setPid();
return $this->checkStatus();
}
return true;
}
public function stop()
{
if($this->isActive()){
$this->execProcess->stop();
$this->clearPid();
}
return true;
}
public function isActive()
{
return $this->checkStatus();
}
private function getPid()
{
if(is_file($this->tempPidFile)){
$pid = intval(file_get_contents($this->tempPidFile));
$this->execProcess->setPid($pid);
return $pid;
}
return null;
}
private function setPid()
{
file_put_contents($this->tempPidFile, $this->execProcess->getPid());
}
private function clearPid()
{
if(is_file($this->tempPidFile)){
unlink($this->tempPidFile);
}
$this->execProcess->setPid(null);
}
private function checkStatus()
{
$pid = $this->getPid();
if(!empty($pid)){
if($this->execProcess->status()){
return true;
}
$this->clearPid();
return false;
}
return false;
}
}
Вроде всё лаконично и понятно. Осталось привязать это всё к админпанели.
4. Настройка экшенов админпанели
Какую систему ты используешь и как работаешь с экшенами известно лишь тебе одному. Я приведу пример своих экшенов, но уже всё должно быть очевидно:
start();
}
public function stopGulpProcess()
{
$gulpProcess = new GulpProcess();
return $gulpProcess->stop();
}
public function getGulpStatus()
{
$gulpProcess = new GulpProcess();
$this->jsonData["is_active"] = $gulpProcess->isActive();
return true;
}
}
Как было сказано в пункте 3, можно также получить текущий output от процесса Gulp, но для этого сначала надо модифицировать класс ExecProcess
, чтобы писал он весь output Не в /dev/null
, а в конкретный файл.
Итог
Теперь ты имеешь в руках довольно простой и приятный инструмент для управления процессом Gulp на сервере (даже на обычном хостинге) и не нужно заниматься компиляцией ресурсов на локальной машине, работая через Deployment Tools.
Все эти наработки можно красиво обернуть в Composer плагин и подшлифовать для конкретной системы (например, Yii2). Этим я обязательно займусь, когда переведу CMS на неё.
В купе с возможностью править файлы ресурсов на сервере через админпанель ты получаешь мощный инструмент, не требующий от тебя настроенной рабочей машины. Можно срочно поправить мелкие баги по требованию заказчика из любого места, с любого компьютера и т.д.
Возможные проблемы и попытки решения
Всё просто, качаем NodeJS в виде архива и заливаем на хостинг. Далее обязательно прописываем его в Path (Это поможет корректно работать NPM и Gulp при запуске через SSH).
После запуска через админпанель может так получиться, что процесс запустится и сразу же выключится. Это связано с какими-то проблемами запуска Gulp, которые, к сожалению, ты не увидишь. Всё ведь посылается в /dev/null
. Надо настроить класс ExecProcess
так, чтобы output сохранялся в файл, и уже в нём искать проблему.
Это ты сможешь узнать только из файла с output’ом процесса Gulp
. Это скорее всего означает, что на хостинге включено изолирование сайтов и твой текущий сайт не имеет доступа к папке, где установлен Node. Это решается средствами управления твоего хостинга. Нужно сделать папку с Node общедоступной.
Ну тут ты уже накосячил с gulpfile.js
, в теории, пример из статьи должен работать (я его, конечно, немного модифицировал, и он отличается от настоящего, рабочего в проекте, но правки были незначительные), попробуй оставить в нём минимум тасков и логики.
Отличная это штука, скажу я тебе, но мне так и не удалось её запустить. Всё дело в том, что она работает на определённых портах, используя Node, но на многих хостингах доступ к портам заблокирован. Если хочешь пользоваться BrowserSync’ом, придётся раскошелиться на VDS.
Вообще это контролирует класс GulpProvess
, он настраивает класс ExecProcess
таким образом, что контроль текущего процесса идёт по его PID. Как только процесс был создан, система получает его PID и дальше всегда работает с ним, при попытке создать новый процесс, при рабочем старом, система этого не позволит.
На крайний случай ты всегда можешь подключиться по SSH и проверить список активных запущенных процессов.
Ищи тот, что называется gulp, это и будет твой фоновый процесс.
Комментарии (3)
13 декабря 2016 в 13:10
0↑
↓
У вас ошибка в примере с установкой зависимостей: «npm imstall»13 декабря 2016 в 14:54
+1↑
↓
И самый главный вопрос — зачем? Gulp, Webpack, etc — это те штуки, которые нафиг не упали на проде висеть. Компиляция должна происходить локально и в прод деплоиться уже собранные сырцы.
А ли я не прав?
13 декабря 2016 в 16:08
+1↑
↓
Не ожидал в 2016 году прочесть статью о новом остроумном способе редактирования файлов на боевом сервере