GitHub Pages to darmowy hosting pozwalający na publikowanie statycznych stron HTML. Nie wspiera on jednak uruchamiania skryptów po stronie serwera, co oznacza brak wsparcia dla języków backendowych, takich jak PHP, Python czy Ruby. W tym poradniku pokażę potencjalny sposób na obejście tego ograniczenia – proceduralne generowanie HTML przy użyciu Node.js poprzez GitHub Actions.
Node.js to środowisko uruchomieniowe JavaScript po stronie serwera, które pozwala na tworzenie aplikacji webowych, generowanie plików i automatyzację różnych procesów. Dzięki Node.js możemy np. napisać skrypt, który dynamicznie tworzy statyczne pliki HTML, które następnie można opublikować na GitHub Pages. W ten sposób możemy w pewnym stopniu "zasymulować" backend na statycznym hostingu, a dokładniej wygenerować "na przyszłość" wszystkie oczekiwane strony.
GitHub Actions to narzędzie do automatyzacji procesów w repozytorium GitHub. Pozwala na tworzenie tzw. workflow, które mogą uruchamiać się automatycznie np. przy każdym pushu do repozytorium.
Celem projektu jest automatyczne tworzenie stron urządzeń dla mojej listy programowalnych sprzętów IoT. Mam już jedną zbiorczą stronę-wyszukiwarkę która pobiera dane z pliku JSON i działa w przeglądarce, ale dodatkowo chcę by każde urządzenie miało osobno swój link z własną stroną.
Tutaj pojawia się pierwsza wątpliwość - ale jak Node.js, skoro nie można uruchamiać skryptów po stronie serwera? Rzeczywiście nie można uruchamiać skryptów po stronie serwera, ale w trakcie zmian na repozytorium można uruchomić jednokrotnie GitHub Workflow który wygeneruje statyczny HTML i zapisze go do gałęzi gh-pages.
Zatem organizacja repozytorium wyglądać będzie następująco:
- branch main zawiera główne informacje które my podajemy, dane źródłowe, w moim przykładzie to będzie plik JSON z informacjami o urządzeniach
- branch gh-pages będzie domyślnie pusty
- dodatkowo w branch main będzie workflow GitHub, który w momencie każdej zmiany w main przetwarza plik JSON i generuje statyczne pliki HTML i zapisuje do gh pages, który potem publikuje je na GitHubie
Generowanie HTML lokalnie (na własnym komputerze)
Rozważmy zawartość gałęzi main. Najważniejszy jest tam plik devices.json, oto przykład zawartości:
Kod: JSON
Opisuje on urządzenia, dla których chcemy wygenerować strony.
Teraz można dodać skrypt JS, który to będzie wykonywać.
Potrzebujemy kolejno:
- wyczyścić starą zawartość GH Pages
- wczytać plik devices.json
- ziterować każdy obiekt urządzenia i wygenerować dla niego HTML do osobnego pliku do gałęzi gh-pages
Minimalny przykład, sekcja usuwania:
Wczytanie JSON, utworzenie folderu dla urządzeń i iteracja:
Funkcja przetwarzająca urządzenie:
pageNameForDevice zamienia nazwę producenta i modelu urządzenia na nazwę strony, a sanitizeFilename upewnia się, czy jest ona przyjazna dla systemu plików. Nie wszystkie znaki mogą być w nazwach plików. Zostaje nam zrobić createDeviceHTML. Oto przykładowa implementacja:
Kod: Javascript
Dla przejrzystości usunąłem plik HTML - najnowszą wersję skryptu można zobaczyć tutaj: https://github.com/OpenBekenIOT/webapp/blob/main/generate-devices.js
Powyższy kod ma kilka dodanych ode mnie funkcji, takich jak wyświetlanie GPIO urządzenia, ale nie jest to istotne z punktu widzenia tego przykładu.
Teraz pora przetestować działanie systemu - uruchamiamy skrypt przez node.js:
node generate-devices.js
Całość powinna się poprawnie wykonać i wygenerować pliki dla naszej bazy JSON:
Generowanie HTML na GitHubie (automatycznie z każdą zmianą gałęzi main)
Opracowaliśmy już generowanie plików HTML dla każdego urządzenia z pliku JSON i potestowaliśmy to lokalnie z Node.js. Pora iść o krok dalej. GitHub pozwala na automatyczne wykonywanie operacji z każdą zmianą na danej gałęzi, możemy to wykorzystać by samoczynnie uruchamiać nasz skrypt i zapisywać rezultaty do gh pages. Dzięki temu każda, nawet najmniejsza zmiana danych w devices.json zaktualizuje naszą dokumentację.
Tworzymy plik .github/workflows/gh-pages.yml:
Kod: text
Rozważmy działanie tego skryptu. Skrypt wywoływany jest po wykonaniu push na gałąź main. Wtedy na maszynie z najnowszą wersją ubuntu najpierw instalowany jest Node.js a potem wykonywany jest opracowany wcześniej skrypt. Następnie rezultaty są zapisywane do gh-pages, czyli publikowane na GitHubie.
Weryfikacja działania na GitHub
Zobaczmy działanie tego w praktyce. Dodałem kilka zmian przez GitHub GUI:
Na GitHubie pojawia się commit z "ptaszkiem":
W podglądzie mamy informacje o powodzeniu operacji deploy:
W szczegółach mamy cały log operacji:
Teraz możemy zajrzeć na gałąź gh-pages:
Jest na niej wygenerowany folder devices:
Są też w nim nasze pliki HTML:
Końcowy efekt
Sam HTML jest poza zakresem tej prezentacji, więc napiszę tylko, że zmodyfikowałem też wyszukiwarkę by linkowała do wygenerowanych stron urządzeń. Efekt dostępny jest tutaj:
https://openbekeniot.github.io/webapp/devicesList.html
Przykładowa strona urządzenia:
https://openbekeniot.github.io/webapp/devices/Tuya_ATLO_SW1_TUYA.html
Od teraz każde urządzenie ma swoją stronę gdzie dodatkowo mogę wyświetlać wskazówki konfiguracji i linki do powiązanych tematów na Elektrodzie.
Bezpośredni link do repozytorium (do wglądu):
https://github.com/OpenBekenIOT/webapp
Podsumowanie
GitHub oferuje tylko hosting statycznego kodu HTML, ale sprytne użycie GitHub Actions pozwala na automatyczne pregenerowanie dowolnego rodzaju stron które potem imitują witrynę z dynamicznym zapleczem. Do drobniejszych prac z kolei można zaprząc JavaScript, na którym opiera się moja główna wyszukiwarka - w pełni po stronie klienta. Myślę, że to wygodne i praktyczne rozwiązanie i pewnie jeszcze nie raz z niego skorzystam.
PS: W osobnym temacie pokażę jak można wykorzystać ten sam mechanizm do automatycznego generowania dokumentacji projektu.
PS2: Uprzedzając pytanie - fragmenty kodu umieściłem jako obrazki ze względu na ograniczenia naszego forum. Javascript włączał ochronę antyspamową i nie mogłem zapisać tematu...
Fajne? Ranking DIY Pomogłem? Kup mi kawę.