Kurs vibe coding z AI · Moduł 13: Twitch Clone
CI/CD Pipeline - GitHub Actions
Automatyczny deployment.
GitHub Actions Workflow
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '20'
- run: npm ci
- run: npm test
- run: npm run build
- uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}Do zobaczenia!
Kod do tej lekcji: App.tsx
'use client';
import { useState } from 'react';
interface Category {
id: string;
name: string;
boxArtUrl: string;
viewerCount: number;
tags: string[];
}
const mockCategories: Category[] = [
{
id: '1',
name: 'Tworzenie aplikacji i gier',
boxArtUrl: 'https://images.unsplash.com/photo-1517694712202-14dd9538aa97?w=300&h=400&fit=crop',
viewerCount: 12547,
tags: ['Programowanie', 'Poradnik']
},
{
id: '2',
name: 'Nauka i technologia',
boxArtUrl: 'https://images.unsplash.com/photo-1518770660439-4636190af475?w=300&h=400&fit=crop',
viewerCount: 8932,
tags: ['Edukacja', 'Technologia']
},
{
id: '3',
name: 'Pogawędki',
boxArtUrl: 'https://images.unsplash.com/photo-1519389950473-47ba0277781c?w=300&h=400&fit=crop',
viewerCount: 45123,
tags: ['IRL', 'Talk-show']
},
{
id: '4',
name: 'Muzyka i sztuki sceniczne',
boxArtUrl: 'https://images.unsplash.com/photo-1511379938547-c1f69419868d?w=300&h=400&fit=crop',
viewerCount: 6789,
tags: ['Muzyka', 'Występy']
},
{
id: '5',
name: 'Sztuka',
boxArtUrl: 'https://images.unsplash.com/photo-1460661419201-fd4cecdf8a8b?w=300&h=400&fit=crop',
viewerCount: 4321,
tags: ['Twórczość', 'Rysunek']
},
{
id: '6',
name: 'Twórcy i rękodzieło',
boxArtUrl: 'https://images.unsplash.com/photo-1452860606245-08befc0ff44b?w=300&h=400&fit=crop',
viewerCount: 2156,
tags: ['DIY', 'Robótki']
}
];
export default function BrowsePage() {
const [activeTab, setActiveTab] = useState<'categories' | 'live'>('categories');
return (
<div className="min-h-screen bg-gray-950 text-white p-8">
<h1 className="text-3xl font-bold mb-6">Przeglądaj</h1>
{/* Zakładki */}
<div className="flex gap-6 mb-8 border-b border-gray-800">
{[
{ key: 'categories', label: 'Kategorie' },
{ key: 'live', label: 'Kanały na żywo' }
].map(tab => (
<button
key={tab.key}
onClick={() => setActiveTab(tab.key as any)}
className={`pb-3 font-semibold ${
activeTab === tab.key
? 'border-b-2 border-purple-500'
: 'text-gray-400 hover:text-white'
}`}
>
{tab.label}
</button>
))}
</div>
{/* Siatka kategorii */}
{activeTab === 'categories' && (
<div className="grid grid-cols-6 gap-4">
{mockCategories.map(category => (
<div key={category.id} className="cursor-pointer group">
<div className="relative mb-2 overflow-hidden rounded">
<img
src={category.boxArtUrl}
alt={category.name}
className="w-full aspect-[3/4] object-cover group-hover:scale-110 transition"
/>
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black to-transparent p-2">
<div className="text-xs text-gray-300">
{category.viewerCount.toLocaleString()} widzów
</div>
</div>
</div>
<h3 className="font-semibold text-sm mb-1">{category.name}</h3>
<div className="flex flex-wrap gap-1">
{category.tags.map(tag => (
<span key={tag} className="text-xs bg-gray-800 px-2 py-0.5 rounded">
{tag}
</span>
))}
</div>
</div>
))}
</div>
)}
</div>
);
}Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Co robi pipeline CI/CD z GitHub Actions opisany w lekcji?
2. Jakie zdarzenie uruchamia workflow Deploy (sekcja on:)?
Zadania praktyczne w grze
- Układanie w pionie
Uporządkuj kroki (steps) joba deploy w workflow:
- Układanie w poziomie
Ułóż odwołanie do tokena Vercel z sekretów (jedna linia): ${{ secrets.VERCEL_TOKEN }}