Skonfiguruj automatyczne wdrożenia preview i testy. Zobacz, jak za pomocą GitHub Actions oraz Lighthouse CI zablokować zepsuty i powolny kod przed produkcją.
Maciej Sala
Founder StriveLab
6 min czytaniaOpublikowano 10 stycznia 2026 (Aktualizacja 14 lipca 2026)
Po co CI/CD w projekcie Next.js?
Najmniejsza obietnica brzmi tak: nigdy nie wdrożysz kodu, który nie przechodzi testów albo nie buduje się poprawnie. I to już wystarczy, żeby się opłacało.
Diagram
Struktura pipeline'u: quality jako bramka wejściowa, testy E2E i Lighthouse równolegle, deploy tylko z gałęzi main po przejściu całości.
Podstawowy pipeline CI/CD: lint, test i build
Code
# .github/workflows/ci.ymlname: CIon: push: branches: [main] pull_request: branches: [main]# Kolejny push do PR-a anuluje poprzedni, wciąż trwający przebiegconcurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true# Token z minimalnymi uprawnieniami i joby, które potrzebują więcej,# deklarują to u siebiepermissions: contents: readjobs: quality: name: Code Quality runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: version: 9 - uses: actions/setup-node@v4 with: node-version: 20 cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Lint run: pnpm lint - name: Type check run: pnpm tsc --noEmit - name: Unit tests run: pnpm test - name: Build run: pnpm build env: DATABASE_URL: ${{ secrets.DATABASE_URL }} NEXT_PUBLIC_APP_URL: https://example.com
Ten pipeline uruchamia się na każdym PR i pushu na main. Jeśli lint, type check, testy lub build failują, to PR jest blokowany.
Dwa bloki na górze pliku wyglądają jak biurokracja, a robią realną robotę. concurrency z cancel-in-progress sprawia, że trzy szybkie poprawki do PR-a nie mielą trzech równoległych pipeline'ów na nieaktualnym kodzie, a na prywatnym repo to wprost oszczędność płatnych minut. permissions: contents: read ogranicza token workflow do odczytu; joby, które potrzebują więcej (jak komentowanie PR-a w sekcji o preview deployments), deklarują to jawnie u siebie. To zasada najmniejszych uprawnień zastosowana do CI, ponieważ gdyby cokolwiek w pipeline zostało skompromitowane, token nie pozwoli na wiele.
Cache zależności i build cache w GitHub Actions
Bez cache każdy przebieg instaluje zależności i buduje od zera, a z cache pipeline skraca się o 50–70%, a przy niezmienionych plikach z minut do krótkiej chwili:
Zauważyłeś, że aplikacja buduje się tu drugi raz, a w sekcji Lighthouse, nawet trzeci? To dlatego, że joby na osobnych maszynach nie współdzielą systemu plików, a z cache .next/cache powtórny build niezmienionego kodu trwa ułamek pierwszego. Alternatywa w postaci przesyłania całego folderu .next jako artefaktu bywa wolniejsza niż ponowne budowanie, ponieważ katalog potrafi ważyć setki megabajtów. Zacznij od prostszego rozwiązania z pamięcią podręczną, a przesyłanie artefaktów rozważ dopiero wtedy, gdy budowanie stanie się bardzo ciężkie, a liczba zadań się powiększy.
Jeśli wynik Performance spadnie poniżej 90 to wtedy pipeline pada, więc wyłapiesz regresje wydajności jeszcze zanim trafią na produkcję. Lighthouse CI sam uruchamia serwer aplikacji dzięki startServerCommand w konfiguracji powyżej, więc nie musisz robić tego osobno.
Jedno zastrzeżenie do progów: współdzielone runnery GitHuba mają zmienną wydajność, więc wynik Performance potrafi się wahać o kilka punktów między przebiegami bez żadnej zmiany w kodzie. Stąd numberOfRuns: 3 (Lighthouse bierze medianę) i stąd rada, by próg ustawić z zapasem względem realnego wyniku — gate na 0.9 przy stronie oscylującej wokół 0.91 będzie failował losowo i nauczy zespół ignorować czerwony pipeline, a to gorsze niż brak gate'a. Dla kategorii, które dopiero doprowadzasz do porządku, użyj poziomu "warn" zamiast "error" — wynik widać w raporcie, ale nie blokuje merge'a.
Przy okazji warto domknąć konfigurację testów E2E z poprzedniej sekcji — żeby Playwright miał uruchomiony serwer, najczyściej zdefiniować webServer w playwright.config.ts:
Wdrożenie z użyciem obrazu ma istotną przewagę nad git pull na serwerze: cały proces budowania odbywa się w CI, a na produkcję trafia gotowy, zweryfikowany artefakt. Kluczem do optymalnego obrazu jest ustawienie output: 'standalone' w pliku next.config.ts. Dzięki temu Next.js pakuje w katalogu .next/standalone wyłącznie niezbędne pliki oraz zależności, a rozmiar obrazu spada z ponad gigabajta do około 150 MB:
Trzy istotne zastrzeżenia. Sekwencja stop oraz run oznacza kilka sekund niedostępności przy każdym wdrożeniu, co dla większości stron firmowych jest akceptowalne, jednak przy wymogu braku przerw w działaniu lepiej zastosować narzędzie z wdrożeniem krokowym, takie jak Coolify lub Kamal. Użycie tagu :${{ github.sha }} obok :latest umożliwia natychmiastowy powrót do konkretnej wersji, ponieważ tag :latest oznacza jedynie ostatni zbudowany obraz. Z kolei polecenie docker image prune -f usuwa stare warstwy, zapobiegając sytuacji, w której brak wolnego miejsca na dysku VPS blokuje kolejne wdrożenia.
Preview deployments na Vercel w CI/CD
Każdy PR automatycznie dostaje na Vercel. Aby dodać komentarz z linkiem do PR:
Zwróć uwagę na blok permissions w tym jobie, bo workflow globalnie ma token tylko do odczytu, a dodanie komentarza wymaga pull-requests: write. Bez tej deklaracji job skończy się błędem „Resource not accessible by integration", który po prostu oznacza brak uprawnień.
Branch protection: bez tego pipeline to tylko sugestia
Cały pipeline ma sens dopiero po domknięciu ostatniego kroku, o którym artykuły często zapominają: czerwony build sam z siebie niczego nie blokuje. Każdy z uprawnieniami do zapisu może zmergować PR-a z failującymi testami, jeśli repozytorium tego nie zabrania.
W ustawieniach repozytorium (Settings → Branches → Add branch ruleset dla main) włącz dwie rzeczy: Require a pull request before merging, czyli koniec z pushowaniem prosto na main, oraz Require status checks to pass, gdzie z listy wybierasz joby z tego artykułu: Code Quality, E2E Tests, ewentualnie Lighthouse CI. Od tej chwili zielony pipeline przestaje być informacją, a staje się warunkiem merge'a.
Pamiętaj, by wymagać stabilnych jobów. Gdy testy Lighthouse na współdzielonych runnerach bywają niestabilne, ustaw je jako opcjonalne, a jako wymagane sprawdzenia zostaw lint, typy, build i testy. W przeciwnym razie zespół szybko przyzwyczai się do omijania ostrzeżeń, a ochrona kodu stanie się pustym rytuałem.
Z włączonym cache podstawowy zestaw (lint, type check, build, testy jednostkowe) zamyka się zwykle w kilku minutach. Bez cache te same kroki potrafią zająć dwa-trzy razy dłużej, bo za każdym razem instalują zależności i budują od zera. Testy E2E i Lighthouse CI dokładają kolejne kilka minut, bo wymagają uruchomienia aplikacji. Dlatego cache zależności i .next/cache to jedna z pierwszych rzeczy, które warto skonfigurować.
Czy potrzebuję CI/CD dla jednoosobowego projektu?
Tak, choć w okrojonej formie, bo nawet sam lint plus sprawdzenie buildu chroni przed wypchnięciem kodu, który się nie kompiluje albo nie buduje. Takie błędy zdarzają się każdemu, niezależnie od wielkości zespołu. Konfiguracja podstawowego pipeline'u to kwestia kilkunastu minut, a potrafi oszczędzić godziny debugowania na produkcji. Pełny zestaw z E2E i Lighthouse dokładaj wtedy, gdy projekt rośnie i ma realnych użytkowników.
Jak bezpiecznie zarządzać sekretami w GitHub Actions?
Przez wbudowany mechanizm: Settings → Secrets and variables → Actions, gdzie dodajesz wartości takie jak DATABASE_URL, SSH_PRIVATE_KEY czy SERVER_HOST jako zaszyfrowane sekrety. W workflow odwołujesz się do nich przez ${{ secrets.NAZWA }}, a GitHub maskuje je w logach. Uważaj, by nigdy nie dodawać sekretów do commitów, ponieważ raz wypchnięty sekret traktuj jako skompromitowany i zrotuj go, nawet jeśli usuniesz go z historii.
Czy na Vercel potrzebuję osobnego pipeline do deployu?
Nie. Vercel automatycznie wdraża każdy push na main oraz tworzy preview deployment dla każdego pull requesta, więc nie musisz pisać workflow deployującego. GitHub Actions wykorzystujesz wtedy wyłącznie do kontroli jakości (lint, testy, Lighthouse), które mają zablokować merge, jeśli coś jest nie tak. Osobny job deploy piszesz dopiero przy samodzielnym hostowaniu, gdzie to Ty odpowiadasz za dostarczenie kodu na serwer.
Dlaczego job komentujący PR kończy się błędem „Resource not accessible"?
Bo domyślny GITHUB_TOKEN ma coraz częściej uprawnienia tylko do odczytu, a dodanie komentarza wymaga zapisu. Dodaj do joba blok permissions z pull-requests: write (i contents: read). Jawne deklarowanie uprawnień to też dobra praktyka bezpieczeństwa, ponieważ wtedy job dostaje dokładnie to, czego potrzebuje.
Po co concurrency w workflow?
Żeby kolejny push do tego samego PR-a anulował poprzedni, wciąż mielący przebieg. Bez tego po trzech szybkich poprawkach masz trzy równoległe pipeline'y, z których dwa testują nieaktualny kod, a na prywatnych repozytoriach płacisz za każdą zmarnowaną minutę.
Co powinno być wymaganym sprawdzeniem przed mergem?
Minimum to lint, type check i build, ponieważ bez nich nie ma sensu wpuszczać kodu do main. W dojrzalszym projekcie dokładasz testy jednostkowe i E2E, a często także Lighthouse CI jako performance gate. Kluczowe jest oznaczenie tych jobów jako wymaganych sprawdzeń statusu w regułach ochrony gałęzi, bo dopiero wtedy zielony pipeline staje się warunkiem mergowania.
O autorze
Maciej Sala
Maciej Sala — Product Manager i Frontend Developer z bogatym doświadczeniem w marketingu internetowym oraz SEO. Na co dzień pracuje z Reactem, Next.js i TypeScriptem, a ostatnio także z Astro i narzędziami do automatyzacji procesów AI. Sprawnie łączy perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat był związany z branżą gier wideo jako project manager i game designer. Absolwent historii na Uniwersytecie Jagiellońskim oraz studiów podyplomowych z marketingu internetowego na AGH w Krakowie. Po godzinach trenuje na siłowni, maluje figurki i rozwijam własne projekty.
Playwright idealnie sprawdza się w projektach opartych na Next.js App Router, ponieważ testuje aplikację dokładnie tak, jak widzi ją użytkownik: od uwzględnienia streamingu HTML, przez płynną nawigację, aż po formularze i interakcje. Wbudowane funkcje, takie jak automatyczne oczekiwanie, fixtures, zaawansowane zarządzanie żądaniami oraz testy wizualne, pozwalają kompleksowo zabezpieczyć jakość kodu bez konieczności dokładania kolejnych, zewnętrznych narzędzi.
Maciej Sala
Founder StriveLab
Jedna linijka w niewłaściwym miejscu — , która miała zostać tylko na środowisku testowym — i strona znika z Google na tygodnie. To jeden z najdroższych błędów w SEO technicznym, ponieważ długo pozostaje niewidoczny: build przechodzi, strona działa, użytkownicy niczego nie zauważają, a ruch organiczny po cichu się osuwa. Dobra wiadomość jest taka, że tę klasę błędów da się złapać automatycznie, zanim kod w ogóle trafi na produkcję. W tym artykule pokazuję, jak zbudować testy regresji SEO w Playwright i wpiąć je w GitHub Actions , żeby pull request z zepsutym linkiem kanonicznym czy przypadkowym noindexem po prostu nie przeszedł.
Maciej Sala
Founder StriveLab
Next.js można wdrożyć jako statyczny eksport, pojedynczy serwer Node.js, kontener Docker albo aplikację na zarządzanej platformie. Wybór hostingu nie polega więc na pytaniu, czy framework „zadziała”, lecz kto będzie odpowiadał za wdrożenia, cache, skalowanie, bezpieczeństwo, koszty oraz przetwarzanie danych.