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 koduRepozytorium 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.
- Najpierw kopia zapasowa
Zanim cokolwiek się zmieni, obecna wersja strony jest pakowana do kopii zapasowej. Wystarczy kilka ostatnich kopii. - 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. - 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.