Многие сталкивались с проблемой Automatic Upload в PhpStorm (сюда можно вписать другую IDE с подобной проблемой) при работе на удаленном сервере. Называть ее «проблемой» тоже можно с натяжкой, но тема жива: PhpStorm при заливке измененных файлов каждый раз поднимает новое подключение, что занимает достаточно ощутимое время (у меня уж точно:
Под катом я расскажу как на коленке набросал скрипт keep-alive подключения с Automatic Upload проекта.
В тексте будет много кода и мало комметариев. Связано это с тем что комментировать там и нечего: для новичков разобраться в NodeJS помогут гайды и статьи (но в это в любом случае JavaScript), а для бывалых код-ревью скажет больше чем я.
Сам скрипт написан на NodeJS — он достаточно прост и всегда находится под рукой, поэтому выбор был очевиден. Подключение я использую SFTP. Итак что нам нужно:
- Мониторинг файлов на изменение
- Upload измененных файлов на удаленный сервер
Я решил не углубляться в идеальные для этого библиотеки — не хотелось тратить на эту задачу достаточно много времени. Беглый просмотр интернетов навел меня на 2 простеньких модуля:
Описывать как их устанавливать и как работает NodeJS не буду — гайдов полно на том же хабре.
Файлик я назвал незамысловато deploy.js. Первые строки выглядят просто:
var fs = require('fs');
var Connection = require('ssh2');
var Сhokidar = require('chokidar');
var c = new Connection();
Итак, библиотеки подключили. Первым делом надо подключиться к серверу:
c.connect({
host: 'habrahabr.ru',
port: 1500,
username: 'habr',
password: 'habrapassword'
});
Как у большинства модулей у ssh2 есть события. Выведем их в лог:
c.on('connect', function() {
console.log('Connection :: connect');
});
c.on('error', function(err) {
console.log('Connection :: error :: ' + err);
});
c.on('end', function() {
console.log('Connection :: end');
});
c.on('close', function(had_error) {
console.log('Connection :: close');
});
Ну и конечно то с чем мы и будем работать:
c.on('ready', function() {
....
})
В этом модуле есть достаточно полезная фича — sftp: достаточно удобный и простой менеджер для работы с файлами:
c.sftp(function(err,sftp){
if(!err){
console.log('sftp start');
}else{
console.log('sftp err',err);
}
});
Когда подключение поднято — пришло время внедрить мониторинг файлов:
var watcher = chokidar.watch(local_path,
{ignored: // функция отвечающая за игнорирование файлов. я добавил сюда пути папки SVN и самого PhpStorm
function(path){
if(path.indexOf(".idea") >= 0 || path.indexOf(".svn") >= 0 || path == (local_path+'/dpl')){
return true;
}
},
ignoreInitial:true // параметр отчающий за игнорирование файлов при инициализации скрипта. Если false - будет литься весь проект при каждом запуске
});
У вотчера есть события add, change, unlink , которые и отрабатывают при манипуляции с файлами. Дело осталось за малым: заставить модуль ssh2 дублировать изменения проекта по sftp:
watcher
.on('add', function(path,meta) {
console.log('upload added',path.replace(local_path,deploy_path));
sftp.fastPut(path,path.replace(local_path,deploy_path),{},function(err){
if(err){
console.log('sftp upload err',err);
}
});
})
.on('change', function(path) {
console.log('upload changed',path.replace(local_path,deploy_path));
sftp.fastPut(path,path.replace(local_path,deploy_path),{},function(err){
if(err){
console.log('sftp change err',err);
}
});
})
.on('unlink', function(path) {
console.log('remove file',path.replace(local_path,deploy_path));
sftp.unlink(path.replace(local_path,deploy_path),function(err){
if(err){
console.log('sftp remove err',err);
}
});
})
.on('error', function(error) {console.error('Error happened', error);})
Готово! Файлы заливаются быстрее чем мы табаемся в браузер! По поводу нагрузки — скрипт кушает ЦП примерно как скайп (MacBook Air mid 2012), так что вполне терпимо.
Чтобы не дублировать на каждый проект по деплоеру я набросал легкий Bash скрипт, который бросаю в корень проекта, он уже и запускает наш скрипт.
#!/bin/bash
DEPLOY_PATH="/deploy/path"
DEPLOYER_PATH="/path/to/deploy.js"
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do
TARGET="$(readlink "$SOURCE")"
if [[ $SOURCE == /* ]]; then
echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
SOURCE="$TARGET"
else
DIR="$( dirname "$SOURCE" )"
echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
SOURCE="$DIR/$TARGET"
fi
done
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
if [ "$DIR" != "$RDIR" ]; then
echo "local_path is '$DIR'"
fi
node $DEPLOYER_PATH local_path=$DIR deploy_path=$DEPLOY_PATH
Ну и в этом случае конечно надо получить параметры в deploy.js
var local_path;
var deploy_path;
process.argv.forEach(function (val, index, array) {
var item_arr = val.split('=');
if(item_arr.length > 1){
switch(item_arr[0]){
case 'local_path':
local_path = item_arr[1];
break;
case 'deploy_path':
deploy_path = item_arr[1];
break;
}
}
});
Скрипт прост и подходит для многих других задач. Например можно перезапускать NodeJS приложение на удаленном сервера после заливки файла, или дублировать картинки с окружения разработчика на общий dev сервер.
Прикладываю файл скрипта.
Скрипт был написан примерно за час, поэтому в нем могут быть глупости и банальные ошибки, поэтому прошу не судить строго. Смысл поста — не предоставить идеально работающий код а навести на мысли о возможном решении. Долгие часы я шарился в интернетах в поисках плагинов или хаков, но ничего не нашел. Идея пришла сама собой, с выходом нового PhpStorm, когда увидел поддержку консоли в IDE.
Спасибо за внимание и конструктивную критику!
Автор: Pasa