Нед��вно мы сделали то, о чём грезят все программисты и дизайнеры — переписали всё с нуля: полный редизайн нашего сайта и написание «движка» с чистого листа. Ниже поведаем о мотивации и процессе миграции с онлайн-CMS на статику.

Что за сайт-то?
Сайт-визитка компании. Состоит из ряда информационных страниц (о нас, контакты и т.п.), вакансий и портфолио — большого числа кейсов по нашим проектам, выполненных в едином стиле, но с уникальными особенностями. Никакого интерактива с пользователями. Полная двуязычность (за исключением некоторых страниц).
С контентом работают разные люди — маркетологи, HR-менеджеры, разработчики. У разных людей (и для разных задач) разные предпочтения в инструментах. Если фронтэндщику, верстающему новый кейс в портфолио, удобнее сделать git pull и работать в своём привычном окружении (vim, конечно), то HR'у, исправляющему опечатку в тексте вакансии, проще через веб-морду поменять одну букву, нажать кнопку «сохранить» и заниматься дальше своими делами.
Как было
Исторически сложилось так, что в предыдущей реинкарнации сайт был построен на самодельном движке, основанном на фреймворке Ruby on Rails. В качестве CMS использовался rails_admin. Для редактирования части контента был подключен WYSIWYG-редактор (CKEditor); остальное — в виде голого HTML (CodeMirror).
Мотивация
Зачем трогать, если работает?
- Проблема синхронизации данных в репозитории и данны�� в БД.
Банальная CMS на RoR использовала банальную СУБДsqlite(да, это один из тех случаев, когдаsqliteидеально годится для продакшена). Соответственно, тот контент, который лежит в БД, не лежит в репозитории вgit. Это неудобно для фронтенд-разработчиков, которым необходимо внести серьёзные изменения, например, в кейс портфолио. Дело в том, что первичная версия кейса, создаваемая разработчиком, лежит в виде шаблона в репозитории, а в момент деплоймента шаблон компилируется и пишется в БД, дабы иметь возможность его редактировать в CMS. Обратный процесс нетривиален (а в общем случае хоть и возможен, будет давать не исходный результат, что в итоге создаёт проблемы с патчами, например). - Операционная стоимость.
Сайт не требует интерактива от пользователей, но при этом всё равно необходима относительно сложная инфраструктура — как минимум одна машина с реверс-прокси и app-сервер. Как известно, RoR любит покушать RAM, а это стоит денег. - Производительность.
Значительно затрудняется оптимизация производительности. Необходимо кэшировать страницы / фрагменты. Кэш надо инвалидировать. Интеграция с CDN требует когнитивных усилий. - Удобство работы.
Известный факт: WYSIWYG работает не очень. Часто приходится в нём прибегать к кнопке «показать код» и редактировать HTML. А если этот HTML скомпилирован из шаблона, он бывает не очень красивым, и в окошке браузера работать с ним не комильфо.
Первые три пункта полностью решаются движком, построенным по принципу так называемого генератора статических сайтов. Данные синхронизировать никуда не надо, потому что они лежат в едином хранилище, мощности для хостинга статических страничек не нужны, кэширование и CDN становятся тривиальны.
Другие бонусы решения на статике:
- Мы в итоге захостились на GitHub Pages, который самостоятельно менеджит SSL-сертификаты от Let's Encrypt, интегрируется с Akamai CDN и не просит за это ни цента. Стоит отметить, что для интеграции с CDN необходимо использовать домен третьего уровня, так что требуется редирект с
e-legion.comнаwww.e-legion.com. Подробнее об этом в документации GitHub'а. И немного на тему: www.yes-www.org. - Все изменения сохраняются в истории репо. Ранее часть изменений была в
git log'е, а часть — в истории в Rails Admin. - Все изменения можно сперва проверить на тестовом инстансе, куда они автоматом попадают по пушу в репо. Если всё хорошо, то одной кнопкой всё деплоится в прод. Ранее постоянно синхронизировать весь контент между контурами и/или вносить все правки сперва на тест, потом на прод, было настолько неудобно (и необязательно), что никто этого, конечно, не делал. В какой-то момент тест вообще отключили, потому что, опять же, лишние бесполезные ресурсы.

Вы перешли только в 2019?
Почему же с такими очевидными плюсами мы осуществили переход только сейчас? Не потому что программисты только из пещеры вылезли. Они уже на протяжении нескольких лет пытались продать начальству своё реш��ние. Но помимо внепланового выделения бюджета, главным препятствием стоял вопрос удобства работы с контентом: как научить главных контентщиков (маркетологов) работать со всей этой историей? Если с редактированием «кода» они более-менее знакомы и привыкли, то обучить работать их с git'ом не всем представлялось целесообразным.
Какое-то время назад была идея написать свой фронт с интуитивным интерфейсом, но реализация упиралась в трудоёмкость, то есть в ресурсы разработчиков.
Шло время, и появились такие решения, как GitLab и встроенный в него Web IDE. И когда подошло время редизайна сайта, мы наконец создали небольшой прототип, представили его отделу маркетинга на ознакомление, проведя небольшой урок по работе с GitLab. На объяснение потребовалось менее часа. ��аркетологам реализация понравилась, и процесс был запущен.

Технические подробности
Итак, пара слов о том, как в итоге получилось.
> tree -aL 1 --dirsfirst -C
.
├── .git
├── app
├── images
├── node_modules
├── pages
├── public
├── .gitignore
├── .gitlab-ci.yml
├── .jshintrc
├── README.markdown
├── gulpfile.js
├── makefile
├── package-lock.json
└── package.json
Что видно сразу:
- генератор написан на Node.js;
- используется таск-раннер gulp;
- на верхнем уровне есть директории
app(файлы «приложения», то есть шаблоны и исходники js и css),images(очевидно, картинки),pages(контент),public(директория, которая будет сервиться по http).
> tree -aL 3 --dirsfirst -C pages/
pages/
├── en
...
└── ru
├── portfolio
│ ├── projects
│ └── index.pug
├── vacancies
│ ├── klgd
│ ├── msk
│ ├── spb
│ └── index.pug
├── 404.pug
├── about.pug
├── contacts.pug
├── education.pug
├── events.pug
├── faq.pug
├── index.pug
└── process.pug
Контент структурирован интуитивно понятным образом, так что (даже без знания английского языка) можно найти нужный файл, исходя из URL страницы. Используется шаблонизатор Pug (синтаксис проще и чище, чем HTML).
Со сбором js, css и картинок всё банально. Посмотрим на недословную выжимку кода из gulpfile'а, собирающего странички:
gulp.task('pug', () => {
// Preload shared data only once.
let sharedData = {};
for (let lang of ["en", "ru"]) {
sharedData[lang] = loadSharedData(lang);
}
// Load necessary data from other files for each file render.
let dataGetter = (file) => {
let content = frontmatter(String(file.contents));
file.contents = new Buffer(content.body);
let data = content.attributes;
data.lang = file.relative.split(path.sep)[0];
data.env = process.env.NODE_ENV;
loadPageData(data, sharedData);
return data;
};
// Remove html file extensions in URLs.
let renamer = (filepath) => {
if (filepath.basename === '404') {
return; // special case for Github Pages
}
if (filepath.basename !== 'index') {
filepath.dirname = path.join(filepath.dirname, filepath.basename);
filepath.basename = 'index';
}
};
return gulp
.src('./pages/**/*.pug')
.pipe(data(dataGetter))
.pipe(pug(pugOptions))
.pipe(rename(renamer))
.pipe(gulp.dest('./public'));
});
Для подгрузки данных при рендеринге шаблонов используется gulp-data. Метаданные файлов лежат в самих шаблонах в формате front-matter, откуда соответствующим пакетом подгружаются. «Смежные» данные, например, список кейсов для индексной страницы портфолио или список вакансий, подгружаются специальным дата-геттером, который собирает необходимый набор данных для каждой отдельной страницы.
Помимо этого используется gulp-rename для облагораживания URL'ов — все странички помещаются в одноимённые директории под названием index.html. Таким образом, исходная страничка faq.pug доступна по URL /faq/, а не /faq.html.

Второй интересный момент, который стоит рассмотреть, это конфигурация GitLab CI/CD:
stages:
- build
- deploy
build_sites:
stage: build
tags:
- npm
before_script:
- make deps
script:
- make build
variables:
NODE_ENV: production
artifacts:
when: on_success
expire_in: 7 days
paths:
- public
deploy_staging:
stage: deploy
tags:
- npm
only:
- master
environment: staging
dependencies:
- build_sites
script:
- make deploy_server
variables:
SSH_USER: elegion
deploy_production:
stage: deploy
when: manual
tags:
- npm
only:
- master
environment: production
dependencies:
- build_sites
script:
- make -j2 deploy_ghpages
Тут стоит обратить внимание на следующие вещи:
- Сборка происходит при пуше в любую ветку. Так, при работе в feature-бренчах люди получают фидбек, если где-то накосячили на столько, что сломали билд.
- При пуше в
master, автоматом происходит деплой на тестовое окружение. Для деплоя используется примитивныйrsync --archive --compress --delete --copy-links ./public ${SSH_USER}@${SSH_HOST}:(да, ещё один бонус сайта на статике — сверхшустрые и беспроблемные деплои). - Джоба на деплой в прод становится доступна после успешной сборки и деплоймента в тест и запускается по нажатию одной кпопки в UI.

- Благодаря возможностям
make, деплой сразу двух сайтов на двух языках (www.e-legion.com и www.e-legion.ru) делается параллельно.
Заключение
На разработку прототипа ушло 2 дня. Доведение движка до ума заняло ещё 3 дня. Настройка CI/CD заняла менее 1 дня. Остальное потраченное время было необходимо в любом случае — создание дизайна, рерайт контента, вёрстка. В итоге довольны все: разработчики, потому что simple is better than complex и поддержка стала в разы проще; админы, потому что они перестали быть нужны вообще, контентщики, потому что стало удобнее. Цитата маркетолога: «Знаю, что мне теперь не хочется закрыть глаза или убежать, когда нужно что-то на сайте поправить». Заодно и на хостинг теперь уходит 0 ₽ в месяц, примерно на 750 ₽ меньше, чем раньше.

Если вы в своей IT-компании до сих пор не используете статику для сайтов-визиток, лендингов и подобных штук по причине того, что переживаете за способности ваших контентщиков, спешим переубедить вас и ваше начальство по опыту нашей истории успеха. Современный UI GitLab и подобных ему git-хостингов при должной конфигурации проекта более удобен для работы наших маркетологов и HR'ов, чем старая CMS на rails_admin. Даже если первые пару раз у людей будут возникать вопросы, помочь с ответом сможет любой подручный разработчик, знакомый с git, потому что всё максимально просто и понятно.
