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







Что за сайт-то?


Сайт-визитка компании. Состоит из ряда информационных страниц (о нас, контакты и т.п.), вакансий и портфолио — большого числа кейсов по нашим проектам, выполненных в едином стиле, но с уникальными особенностями. Никакого интерактива с пользователями. Полная двуязычность (за исключением некоторых страниц).


С контентом работают разные люди — маркетологи, HR-менеджеры, разработчики. У разных людей (и для разных задач) разные предпочтения в инструментах. Если фронтэндщику, верстающему новый кейс в портфолио, удобнее сделать git pull и работать в своём привычном окружении (vim, конечно), то HR'у, исправляющему опечатку в тексте вакансии, проще через веб-морду поменять одну букву, нажать кнопку «сохранить» и заниматься дальше своими делами.



Как было


Исторически сложилось так, что в предыдущей реинкарнации сайт был построен на самодельном движке, основанном на фреймворке Ruby on Rails. В качестве CMS использовался rails_admin. Для редактирования части контента был подключен WYSIWYG-редактор (CKEditor); остальное — в виде голого HTML (CodeMirror).



Мотивация


Зачем трогать, если работает?


  1. Проблема синхронизации данных в репозитории и данны�� в БД.

    Банальная CMS на RoR использовала банальную СУБД sqlite (да, это один из тех случаев, когда sqlite идеально годится для продакшена). Соответственно, тот контент, который лежит в БД, не лежит в репозитории в git. Это неудобно для фронтенд-разработчиков, которым необходимо внести серьёзные изменения, например, в кейс портфолио. Дело в том, что первичная версия кейса, создаваемая разработчиком, лежит в виде шаблона в репозитории, а в момент деплоймента шаблон компилируется и пишется в БД, дабы иметь возможность его редактировать в CMS. Обратный процесс нетривиален (а в общем случае хоть и возможен, будет давать не исходный результат, что в итоге создаёт проблемы с патчами, например).
  2. Операционная стоимость.

    Сайт не требует интерактива от пользователей, но при этом всё равно необходима относительно сложная инфраструктура — как минимум одна машина с реверс-прокси и app-сервер. Как известно, RoR любит покушать RAM, а это стоит денег.
  3. Производительность.

    Значительно затрудняется оптимизация производительности. Необходимо кэшировать страницы / фрагменты. Кэш надо инвалидировать. Интеграция с CDN требует когнитивных усилий.
  4. Удобство работы.

    Известный факт: 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.
  • Все изменения можно сперва проверить на тестовом инстансе, куда они автоматом попадают по пушу в репо. Если всё хорошо, то одной кнопкой всё деплоится в прод. Ранее постоянно синхронизировать весь контент между контурами и/или вносить все правки сперва на тест, потом на прод, было настолько неудобно (и необязательно), что никто этого, конечно, не делал. В какой-то момент тест вообще отключили, потому что, опять же, лишние бесполезные ресурсы.

GitHub Pages

Вы перешли только в 2019?


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


Какое-то время назад была идея написать свой фронт с интуитивным интерфейсом, но реализация упиралась в трудоёмкость, то есть в ресурсы разработчиков.


Шло время, и появились такие решения, как GitLab и встроенный в него Web IDE. И когда подошло время редизайна сайта, мы наконец создали небольшой прототип, представили его отделу маркетинга на ознакомление, проведя небольшой урок по работе с GitLab. На объяснение потребовалось менее часа. ��аркетологам реализация понравилась, и процесс был запущен.



GitLab IDE Interface

Технические подробности


Итак, пара слов о том, как в итоге получилось.


> 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.
    GitLab CI 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, потому что всё максимально просто и понятно.