Wszystkie notatki

Co jedzie na serwer
a co zostaje w domu

Repozytorium zwykle zawiera więcej niż stronę: szkice, prompty, zdjęcia referencyjne i narzędzia. Serwer ma dostać stronę i nic więcej, a wdrożenie ma być na tyle nudne, by można je było uruchomić w poniedziałek rano.

Od razu do kodu

Repozytorium to jeszcze nie strona

Obok stron w repozytorium łatwo gromadzą się materiały projektowe, prompty do obrazów, zdjęcia referencyjne, notatki i narzędzia. Wyślij cały folder, a wszystko to stanie się publiczne razem ze stroną.

  • Jedzie na serwer: gotowe strony, style, skrypty, czcionki i obrazy, których naprawdę używają, oraz pliki serwera, takie jak robots.txt, mapa strony i reguły przekierowań.
  • Zostaje w domu: materiały projektowe i prompty, prywatne zdjęcia, notatki, testy i narzędzia do budowania, kopie zapasowe oraz wszystko, w czym jest hasło lub klucz.

Dlatego nasza strona jest budowana do osobnego folderu. Skrypt tworzy każdą stronę w każdym języku pod czystym adresem w rodzaju /pl/notes/, a potem kopiuje tylko pliki, do których odwołują się strony, style i skrypty. Co nie jest podlinkowane, nigdzie nie jedzie.

Nazwy, które wygrywają z cache

Jedna niespodzianka po drodze: CDN przed stroną ignorował znany trik z ?v= i po aktualizacji wciąż wracały stare style. Dlatego budowanie zmienia nazwy samych plików. Każda nowa nazwa zawiera pierwsze osiem znaków hasha SHA-256 z zawartości pliku.

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() przy okazji zamienia windowsowe końce linii na zwykłe, więc ta sama zawartość dostaje tę samą nazwę na każdym komputerze. Potem strony są przepisywane tak, by wskazywały style.1a2b3c4d.css zamiast style.css; pliki źródłowe się nie zmieniają.

Teraz reguły cache mogą być surowe. Strony są za każdym razem sprawdzane od nowa, więc wdrożenie widać już przy następnej wizycie. Style i skrypty z hashem są cache’owane na rok jako niezmienne: zmieniony plik dostaje nową nazwę, więc stary nigdy się nie zestarzeje. Obrazy nie zmieniają nazw automatycznie, dlatego podmieniony obraz dostaje nową nazwę ręcznie, na przykład -v2.

Wdrożenie, które umie powiedzieć „nie”

Skrypt wdrożenia uruchamia budowanie i odmawia dalszej pracy, jeśli wynik wygląda na niepełny. To ważne, gdy wysyłka odzwierciedla folder z usuwaniem, jak robi to rsync --delete: odzwierciedlony z pustego folderu, opróżniłby działającą stronę.

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)

Kolejna linia porównuje tę liczbę z oczekiwaną liczbą stron: każda strona w każdym języku plus 404. Dopiero wtedy cokolwiek opuszcza komputer.

  1. Najpierw kopia zapasowa
    Zanim cokolwiek się zmieni, obecna wersja strony jest pakowana do kopii zapasowej. Wystarczy kilka ostatnich kopii.
  2. Rozpakuj obok działającej strony
    Nowa budowa trafia najpierw do folderu tymczasowego, więc zepsuta wysyłka nigdy nie podmieni działającej strony w połowie.
  3. Dotykaj tylko swojego folderu
    Synchronizacja aktualizuje folder samej strony i nic obok.

Klucze trzymamy z daleka. Klucz lub hasło do wdrożenia nie należą ani do repozytorium, ani do budowy: żyją tylko tam, skąd uruchamia się wdrożenie.

Pytaj działającą stronę, a nie budowę

Po wysyłce skrypt zadaje prawdziwej domenie kilka pytań i oczekuje dokładnych odpowiedzi.

  • Strona główna i strona w każdym języku odpowiadają 200: wszystkie wersje są na miejscu.
  • Obsługa formularza kontaktowego odpowiada 405 na zwykłą wizytę: przyjmuje tylko POST.
  • Stary adres odpowiada 301: zapisane przez ludzi linki nadal dokądś prowadzą.
  • Wymyślony adres odpowiada 404: brakująca strona jest uczciwie zgłaszana, z własną stroną 404.

Kolejna niespodzianka: CDN może odpowiedzieć 403 na gołe zapytanie ze skryptu, choć wszystkim innym stronę serwuje. Dlatego każde pytanie ma pięć prób, zanim zostanie uznane za porażkę.

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
}

Nudne — i tak ma być

Dobre wdrożenie mija bez przygód: za każdym razem ta sama budowa, świeża kopia zapasowa i kilka dokładnych odpowiedzi od działającej strony. Jeśli coś jest nie tak, skrypt zatrzymuje się, zanim zauważą to odwiedzający. Kotka popiera wszystko, co pozwala jej przespać dzień wydania.

Wszystkie notatkiZobacz też: okładka pokazuje prawdziwy projekt