Простой деплой сайта на хостинг из Git-репозитория

Давным-давно я заливал сайты в интернет с помощью FTP-клиента. Потом интернет стал быстрее, и я стал прямо по FTP править странички, потому что каждый раз вручную копировать файлы было жутко неудобно.

Сейчас код всех моих сайтов находится в гит-репозиториях на Гитхабе или Битбакете, поэтому можно легко организовать выкладку сайтов оттуда: внесли изменения, закоммитили код, набрали команду в консоли, и через минуту пользователи уже видят новую версию сайта.

Для этого я использую свой шелл-скрипт shipit: он позволяет выполнять любые команды на сервере по SSH. До этого я долго использовал Fabric, но Шипит проще и удобнее.

Настройка авторизации

Для начала нужно настроить авторизацию с помощью SSH-ключей: без паролей и смс.

Создание SSH-ключа и добавление его на Гитхаб или Битбакет

Создание и использование ключей хорошо описано в документации Гитхаба и Битбакета. Это нужно сделать и на локальном компьютере, и на хостинге, чтобы он тоже мог ходить на Гитхаб или Битбакет.

Создание псевдонима соединения

Чтобы не набирать каждый раз имя хоста и логин, можно создать псевдоним. Для этого добавим в файл ~/.ssh/config:

Host myhost
  HostName 113.113.13.13
  User tema
  IdentityFile ~/.ssh/id_rsa

Замените данные на ваш хост, логин и ключ, созданный в предыдущем шаге.

Загрузка SSH-ключа на хостинг

Избавимся от необходимости вводить пароль при каждом заходе:

ssh myhost 'mkdir -p .ssh && cat >> ~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub

Теперь вы можете заходить на хостинг набирая:

ssh myhost

Подготовка проекта

Для сборки я использую Грант. Если вы не используете Грант, можете пропустить этот раздел.

Есть два способа сборки и деплоя сайта:

  1. Коммитить собранные файлы, деплоить простым git pull.
  2. Коммитить только исходный код, собирать при деплое.

Я обычно использую второй. Репозиторий получается аккуратнее, диффы чище, работать удобнее. Но приходится настраивать на сервере сборку. Однако с Грантом это не проблема.

Для деплоя я создаю специальную задачу deploy, где нет оптимизации картинок, тестирования и других долгих задач, не имеющих значения при сборке сайта.

grunt.registerTask('default', [
  'jshint',
  'concat',
  'stylus',
  'imagemin'
]);
grunt.registerTask('deploy', ['concat', 'stylus']);

Плагины для Гранта я всегда устанавливаю с ключом --save-dev, чтобы сохранять ссылки на их конкретные версии в package.json. Он должен выглядеть примерно так:

{
  "name": "example",
  "version": "0.0.0",
  "private": true,
  "devDependencies": {
    "grunt": "~0.4.2",
    "load-grunt-tasks": "~0.2.0",
    "grunt-contrib-jshint": "~0.7.2",
    "grunt-contrib-concat": "~0.3.0",
    "grunt-contrib-stylus": "~0.11.0",
    "grunt-contrib-imagemin": "~0.4.0"
  }
}

Так я могу быть уверен, что сборка не сломается из-за того, что на сервере будет стоять неправильная версия какого-то пакета.

Подготовка репозитория

Добавим в локальный репозиторий ссылку на удалённый (на Гитхабе или Битбакете) и запушим код:

git remote add origin git@github.com:sapegin/example.git
git push -u origin master

Клонирование репозитория на хостинг

Клонируем репозиторий сайта на хостинг:

ssh myhost
git clone git@github.com:sapegin/example.git ~/sites/example.com
logout

Никаких паролей запрашиваться уже не должно.

Установка shipit

Шипит устанавливается одной строкой:

pathtoshipit=/usr/local/bin/shipit; curl -o $pathtoshipit https://raw.github.com/sapegin/shipit/master/bin/shipit; chmod +x $pathtoshipit; unset pathtoshipit

Настройка деплоя

Типичный скрипт деплоя у меня выглядит так:

git checkout master
git pull
npm install
node -e "require('grunt').cli()" _ deploy

Переведу на русский язык:

  1. Переходим в ветку master (на всякий случай).
  2. Получаем свежий код с Гитхаба/Битбакета.
  3. Устанавливаем/обновляем npm-пакеты.
  4. Запускам сборку Грантом.

Последний пункт нужно пояснить: я запускаю задачу deploy локально установленным (npm install без ключа -g) Грантом. Так всё нужное для деплоя (кроме Гита и npm) устанавливается одной командой npm install и не требует sudo.

Напишем скрипт для Шипита:

host='myhost'
path='sites/example.com'

[deploy]
git checkout master
git pull
npm install
node -e "require('grunt').cli()" _ deploy

И сохраним его как .shipit в папке проекта.

Шипит работает очень просто: он подключается к серверу по SSH, переходит в папку сайта и выполняет там команды, указанные после метки [deploy]. (На самом деле возможностей у него больше.)

Всё, теперь можно разложить сайт одной командой, не считая коммита и пуша изменений:

git commit -a -m "Make ponies pink."
git push
shipit

Shipit