TL;DR
Dla projektu Next.js na Node 22, Next 15.5.18 i buildzie przez scripts/build.cjs rekomendujemy jeden pipeline GitHub Actions: każdy PR = jakość (lint + build), merge do main = deploy na Vercel. W tym repozytorium nie ma jeszcze katalogu .github/workflows — artykuł opisuje docelowy stan, który warto dodać od razu, zamiast polegać na ręcznym „u mnie działa”. Sekrety (VERCEL_TOKEN, DATABASE_URL, klucze API) trzymaj wyłącznie w GitHub Secrets.
Dla kogo to jest
- Zespołów 1–5 developerów na Next.js + Vercel
- Projektów z Prisma (
postinstall:prisma generate, migrate w skrypcie build) - Firm, które chcą audytowalnej historii wdrożeń i preview URL na każdy PR
- Monorepo lub wielu aplikacji w jednym repo (sekcja
paths:)
Fraza (SEO)
ci cd nextjs github actions, pipeline nextjs 2026, automatyczne wdrożenie vercel, github actions build nextjs, branch protection main, rollback vercel
Punkt wyjścia: uczciwy stan repozytorium
W wielu projektach produkcyjnych pipeline „istnieje w głowie zespołu”, a nie w repo. Tutaj:
package.jsonwymaga Node 22.x (engines.node),- Next.js 15.5.18, skrypty:
build→node scripts/build.cjs,lint→eslint,postinstall→prisma generate, - Brak plików
.github/workflows/*.yml— CI trzeba świadomie dodać.
To nie wada — to normalny etap. Wada pojawia się, gdy deploy z laptopa omija te same kroki co PR. Celem jest jeden artefakt: commit przeszedł lint i build w Actions → ten sam commit ląduje na Vercel.
Co robi scripts/build.cjs (i dlaczego CI musi to uwzględnić)
Zwykły next build w CI często nie wystarczy, gdy:
- Prisma — skrypt ładuje
.env/.env.local(w Actions nie ma ich w repo), uruchamiaprisma generate, a przy obecnościDATABASE_URLtakżeprisma migrate deploy, na końcunext build. - Lokalnie developer ma bazę; w CI build może przejść bez migracji, jeśli nie ustawisz sekretu — ale sam generate jest potrzebny po
npm ci(wspierany też przezpostinstall).
Wniosek dla pipeline: job build musi wywołać npm run build (nie surowe next build), z odpowiednimi zmiennymi środowiskowymi w GitHub Secrets / Environments. Dla PR bez bazy często wystarczy DATABASE_URL do shadow DB lub pominięcie migracji — zależnie od polityki zespołu; ważne, żeby prod miał pełny zestaw sekretów w environment production.
Strategie DATABASE_URL w CI (Prisma)
| Strategia | Kiedy | Plusy / minusy |
|---|---|---|
| Ten sam URL co staging | Mały zespół, jedna baza testowa | Szybkie; ryzyko kolizji migracji przy równoległych PR |
| Osobna baza per PR (Neon, Supabase branch) | Większy zespół | Izolacja; wyższy koszt / konfiguracja |
Brak DATABASE_URL w PR |
Tylko statyczny front | Build może pominąć migrate deploy (log w build.cjs); schema musi być aktualna z repo |
Pełny URL tylko na main |
Kompromis | PR: lint + tsc + build bez migracji; prod: pełny pipeline |
W DevStudio-style buildzie przy braku DATABASE_URL skrypt wypisuje komunikat i pomija prisma migrate deploy, ale i tak uruchamia prisma generate — to jest zgodne z postinstall i zapobiega błędom „Client not generated”.
Rekomendowany podział: PR vs main
| Krok | Pull request | Push na main |
|---|---|---|
npm ci |
tak | tak |
npm run lint (ESLint) |
tak | tak |
npx tsc --noEmit |
tak (jeśli TypeScript w projekcie) | tak |
npm run build |
tak | tak |
| Testy jednostkowe | tak, gdy dodasz | tak |
| Deploy Vercel preview | tak (integracja Git lub Actions) | — |
| Deploy Vercel production | — | tak |
| Lighthouse CI | opcjonalnie | opcjonalnie (np. cron) |
Zasada: nic nie merge’uj do main, co nie przeszło tego samego build co produkcja.
Pełny przykład workflow (.github/workflows/ci.yml)
Poniższy plik jest gotowy do skopiowania i dostosowania. Używa Node 22, cache npm, lint, typecheck i build zgodnego z tym repo.
name: CI
on:
pull_request:
branches: [main]
push:
branches: [main]
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
quality:
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js 22
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: ESLint
run: npm run lint
- name: TypeScript (no emit)
run: npx tsc --noEmit
- name: Build (Prisma + Next via scripts/build.cjs)
run: npm run build
env:
# Wymagane do pełnego buildu — ustaw w GitHub Secrets / Environment
DATABASE_URL: ${{ secrets.DATABASE_URL }}
NEXT_PUBLIC_SITE_URL: ${{ vars.NEXT_PUBLIC_SITE_URL || 'https://example.com' }}
# Dodaj pozostałe NEXT_PUBLIC_* i klucze API jak w Vercel Production
deploy-preview:
if: github.event_name == 'pull_request'
needs: quality
runs-on: ubuntu-latest
environment: preview
steps:
- uses: actions/checkout@v4
- uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
github-token: ${{ secrets.GITHUB_TOKEN }}
scope: ${{ secrets.VERCEL_ORG_ID }}
deploy-production:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: quality
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
github-token: ${{ secrets.GITHUB_TOKEN }}
scope: ${{ secrets.VERCEL_ORG_ID }}Alternatywa prostsza: włącz Vercel Git Integration — wtedy Vercel sam buduje preview na PR, a job quality w Actions zostaje „bramką” przed merge. Wiele zespołów łączy oba: Actions = lint/tsc/build, Vercel = hosting + URL.
Sekrety i zmienne — gdzie co trzymać
| Nazwa | Gdzie | Uwagi |
|---|---|---|
VERCEL_TOKEN |
GitHub Secret | Token z Vercel Account Settings |
VERCEL_ORG_ID, VERCEL_PROJECT_ID |
GitHub Secret | Z pliku .vercel/project.json po vercel link |
DATABASE_URL |
Secret + Vercel Env | Ta sama wartość co produkcja dla migrate deploy |
OPENAI_API_KEY, Stripe itd. |
Secret / Vercel | Nigdy w repo ani w logach Actions |
NEXT_PUBLIC_* |
Variables (niesecret) lub Vercel | Muszą być identyczne w PR build i prod, inaczej „u mnie działa” wraca |
W ustawieniach repo: Settings → Secrets and variables → Actions. Dla produkcji użyj Environment production z włączonym Required reviewers (opcjonalnie, 1–2 osoby).
Branch protection na main
Minimum, które warto włączyć od pierwszego dnia:
- Require pull request before merging
- Require status check quality (nazwa joba z workflow)
- Require branch to be up to date
- Do not allow bypassing (nawet admin, jeśli zespół ma dyscyplinę)
- Opcjonalnie: require signed commits
Dzięki temu nikt nie wrzuci builda, który nie przeszedł npm run lint i npm run build na Node 22.
Preview URL na każdy PR
Vercel Git Integration (zalecane na start):
- automatyczny URL typu
projekt-git-branch-xyz.vercel.app, - komentarz bota w PR z linkiem,
- QA i klient sprawdzają zmianę przed merge.
GitHub Actions + vercel-action (jak w yaml powyżej):
- pełna kontrola, ten sam token co deploy prod,
- przydatne, gdy chcesz jeden pipeline w YAML zamiast panelu Vercel.
W obu przypadkach upewnij się, że zmienne NEXT_PUBLIC_* na preview nie są puste — inaczej build PR „przechodzi”, a UI na preview jest inny niż produkcja.
Synchronizacja env: w Vercel skopiuj zestaw zmiennych z Production do Preview (lub użyj Vercel CLI vercel env pull). W GitHub dodaj te same NEXT_PUBLIC_* jako Variables (widoczne w logach, nie sekret) — wtedy job quality buduje ten sam kod co preview. Sekrety (DATABASE_URL, STRIPE_SECRET_KEY) zostaw wyłącznie w Secrets.
Monorepo: nie buduj wszystkiego przy każdym PR
Gdy w jednym repo masz np. apps/web i packages/ui, dodaj osobne workflow z filtrem ścieżek:
on:
pull_request:
paths:
- 'apps/web/**'
- 'packages/ui/**'
- 'package-lock.json'Możesz też użyć paths-ignore dla dokumentacji (docs/**, content/blog/**), żeby nie uruchamiać 8-minutowego builda przy poprawce literówki w artykule — o ile blog nie jest częścią tego samego pakietu Next.js (w monolicie marketingowym często i tak budujesz całość).
Rollback na Vercel w ~60 sekund
Deploy prod na Vercel to nowy deployment; poprzednie buildy zostają.
Procedura awaryjna:
- Vercel Dashboard → Deployments
- Znajdź ostatni działający deployment (poprzedni commit)
- ⋯ → Promote to Production
Czas: często poniżej minuty. Pipeline nie zastępuje rollbacku — zapewnia, że promowany build był kiedyś zielony na PR. Unikaj deployu z laptopa „na szybko” obok CI — wtedy rollback w panelu może przywrócić inną wersję niż myślisz.
Przy własnym serwerze (Docker): tag obrazu = SHA commita; rollback = poprzedni tag w orchestratorze. Zasada ta sama: jeden artefakt z CI.
Bezpieczeństwo w CI/CD
permissions: contents: readdomyślnie (jak w przykładzie) — minimalne uprawnienia tokenaGITHUB_TOKEN- Osobny environment
productionz approval - Dependabot /
npm auditw osobnym workflow tygodniowym - Nigdy nie
echosekretów ani pełnych.envw logach — Actions maskujesecrets.*, ale własne skrypty też muszą być czyste - Rotacja
VERCEL_TOKENi kluczy API co kwartał lub po odejściu członka zespołu
Najczęstsze błędy (i jak ich uniknąć)
| Błąd | Skutek | Naprawa |
|---|---|---|
Build PR bez NEXT_PUBLIC_* |
Inny bundle niż prod | Variables w GitHub + Vercel Preview |
next build zamiast npm run build |
Brak Prisma migrate/generate | Zawsze skrypt z repo |
Node 20 w Actions, 22 w engines |
Losowe błędy natywnych modułów | node-version: '22' |
| Brak cache npm | 8 min zamiast 3 | cache: 'npm' w setup-node |
| Deploy z laptopa + CI | Dwa źródła prawdy | Tylko merge → main → deploy |
| Brak branch protection | Złamanie prod w piątek wieczorem | Wymagany check quality |
FAQ
Czy GitHub Actions zastępuje Vercel?
Nie. Actions = kontrola jakości i opcjonalnie deploy CLI; Vercel = hosting, CDN, preview, rollback. Najczęściej: Actions na lint/build, Vercel na uruchomienie aplikacji.
Czy muszę uruchamiać prisma migrate deploy w CI?
W tym projekcie robi to scripts/build.cjs, gdy DATABASE_URL jest ustawione. Na PR możesz użyć osobnej bazy testowej lub pominąć migracje — ale wtedy upewnij się, że schema Prisma jest zgodna (generate i tak jest potrzebny). Na main/prod DATABASE_URL musi być kompletne.
Co jeśli nie mam testów?
Zacznij od lint + tsc + build. Testy dodaj, gdy masz krytyczne ścieżki (płatności, API umów). Pipeline i tak daje 80% wartości.
Monorepo z trzema aplikacjami?
Trzy workflow z paths: albo jeden workflow z macierzą strategy.matrix — nie jeden globalny build bez filtra.
Jak zsynchronizować wersję Node z Vercel?
W Vercel Project Settings ustaw Node.js 22.x, zgodnie z engines w package.json.
Troubleshooting: czerwony build w Actions
prisma generatefailed — sprawdź wersję Node (22), czynpm cidoszedł do końca, czy schema w repo jest poprawna.migrate deployfailed — URL bazy nieosiągalny z GitHub (firewall IP), zła migracja, konflikt z ręcznymi zmianami w DB.next buildOOM — zwiększ runner (rzadko) lub wyłącz zbędne analizy; sprawdź importy ciężkich bibliotek.- Lint fail na PR — uruchom lokalnie
npm run lintna Node 22; nie merge’uj z--no-verify. - Preview działa, prod nie — porównaj Environment Variables w Vercel Production vs Preview; często brakuje jednego
NEXT_PUBLIC_*.
Logi Actions pokazują dokładny krok — traktuj je jak jedyny audyt przed dotknięciem produkcji.
Plan wdrożenia w 1 dzień
- Dodać
.github/workflows/ci.yml(fragment powyżej). - Skonfigurować Secrets + Variables (DATABASE_URL, Vercel, NEXT_PUBLIC_*).
- Włączyć branch protection na
main. - Podłączyć repo do Vercel (jeśli jeszcze nie).
- Otworzyć testowy PR — sprawdzić zielony
quality+ preview URL. - Zmergować — sprawdzić production deployment i zapisać procedurę rollbacku w README zespołu.
Podsumowanie
CI/CD dla Next.js 15 na Node 22 to nie magia — to powtarzalny zestaw: npm ci → lint → typecheck → npm run build (Prisma + Next) na każdym PR, deploy na Vercel po merge, sekrety poza repo, preview do akceptacji, rollback przez Promote w panelu. Brak workflow w repozytorium to sygnał, że warto to dodać teraz — zanim ręczny deploy kosztuje więcej niż jeden dzień naprawy produkcji.
Chcesz pipeline pod swój projekt?
- Skontaktuj się — przejrzymy
package.json, Vercel i Prisma pod Twój case - Aplikacje web — Next.js, hosting i DevOps w jednym pakiecie