Усі нотатки

Що їде на сервер
а що лишається вдома

Репозиторій зазвичай містить більше, ніж сайт: ескізи, промпти, фото-референси й інструменти. Сервер має отримати сайт і нічого більше, а деплой має бути настільки нудним, щоб його не страшно було запускати в понеділок зранку.

Одразу до коду

Репозиторій — ще не сайт

Поруч зі сторінками в репозиторії легко накопичуються дизайнерські джерела, промпти для картинок, фото-референси, нотатки й інструменти. Залийте всю теку — і все це стане публічним разом із сайтом.

  • Їде на сервер: готові сторінки, стилі, скрипти, шрифти й картинки, якими вони справді користуються, і серверні файли на кшталт robots.txt, карти сайту та правил переадресації.
  • Лишається вдома: дизайнерські джерела й промпти, особисті фото, нотатки, тести й інструменти збирання, резервні копії та все, де є пароль чи ключ.

Тому наш сайт збирається в окрему теку. Збирач робить кожну сторінку кожною мовою за чистою адресою на кшталт /pl/notes/, а потім копіює лише ті файли, на які посилаються сторінки, стилі й скрипти. Те, на що ніхто не посилається, нікуди не їде.

Імена, які перемагають кеш

Одна несподіванка дорогою: CDN перед сайтом ігнорував звичний трюк із ?v=, і після оновлення знову й знову приходили старі стилі. Тож збирач перейменовує самі файли. Кожне нове ім’я містить перші вісім символів хешу SHA-256 від вмісту файлу.

JavaScript · tools/build-site.cjs

for (const file of HASHED) {
  const text = readText(file);
  const hash = crypto.createHash('sha256').update(text).digest('hex').slice(0, 8);
  renamed[file] = file.replace(/(\.[a-z]+)$/, `.${hash}$1`);
  write(renamed[file], text);
}

readText() заодно перетворює віндовсні закінчення рядків на звичайні, тож однаковий вміст отримує однакове ім’я на будь-якому комп’ютері. Далі сторінки переписуються так, щоб вести на style.1a2b3c4d.css замість style.css; вихідні файли не змінюються.

Тепер правила кешування можуть бути суворими. Сторінки щоразу перевіряються наново, тож деплой видно вже під час наступного відвідування. Стилі й скрипти з хешем кешуються на рік як незмінні: змінений файл отримує нове ім’я, і старий ніколи не застаріває. Картинки автоматично не перейменовуються, тож замінена картинка отримує нове ім’я вручну, наприклад -v2.

Деплой, який уміє сказати «ні»

Скрипт деплою запускає збирач і відмовляється йти далі, якщо результат виглядає неповним. Це важливо, коли заливка дзеркалить теку з видаленням, як rsync --delete: віддзеркалений із порожньої теки, він спорожнив би живий сайт.

Bash · tools/deploy.sh

test -f dist/site/index.html || { echo "deploy: no index.html in dist/site"; exit 1; }
test -f dist/site/.htaccess || { echo "deploy: no .htaccess in dist/site"; exit 1; }
pages=$(find dist/site -name '*.html' | wc -l)

Наступний рядок порівнює це число з очікуваною кількістю сторінок: кожна сторінка кожною мовою плюс 404. Лише тоді щось вирушає з комп’ютера.

  1. Спершу резервна копія
    Перш ніж щось зміниться, поточна версія сайту пакується в резервну копію. Кількох останніх копій досить.
  2. Розпакувати поруч із живим сайтом
    Нова збірка спершу потрапляє в тимчасову теку, тож зламана заливка ніколи не замінить робочий сайт наполовину.
  3. Чіпати лише свою теку
    Синхронізація оновлює теку самого сайту й нічого поруч.

Ключі — осторонь. Ключу чи паролю для деплою не місце ні в репозиторії, ні в збірці: вони живуть лише там, звідки деплой запускають.

Питати живий сайт, а не збірку

Після заливки скрипт ставить справжньому домену кілька запитань і чекає точних відповідей.

  • Головна й сторінка кожною мовою відповідають 200: усі версії на місці.
  • Обробник форми зв’язку відповідає 405 на звичайний візит: він приймає лише POST.
  • Стара адреса відповідає 301: збережені людьми посилання й далі кудись ведуть.
  • Вигадана адреса відповідає 404: відсутню сторінку чесно позначено, і показується власна сторінка 404 сайту.

Ще одна несподіванка: CDN може відповісти 403 на голий запит зі скрипта, хоча всім іншим сторінку віддає. Тож кожне запитання має п’ять спроб, перш ніж зарахується як провал.

Bash · tools/deploy.sh

check() {
  for attempt in 1 2 3 4 5; do
    code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 20 "$1")
    if [ "$code" = "$2" ]; then echo "$1 -> $code"; return 0; fi
    sleep 6
  done
  echo "deploy: $1 answered $code, expected $2"; return 1
}

Нудно — і так задумано

Добрий деплой минає без пригод: та сама збірка щоразу, свіжа резервна копія й кілька точних відповідей від живого сайту. Якщо щось не так, скрипт зупиняється раніше, ніж це помітять відвідувачі. Котка схвалює все, що дає їй проспати день релізу.

Усі нотаткиТакож: обкладинка показує справжній проєкт