Vibe coding with AI course · Module 13: Twitch Clone
CI/CD Pipeline - GitHub Actions
Automated 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 }}See you soon!
Code for this lesson: 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: 'Software & Game Development',
boxArtUrl: 'https://images.unsplash.com/photo-1517694712202-14dd9538aa97?w=300&h=400&fit=crop',
viewerCount: 12547,
tags: ['Programming', 'Tutorial']
},
{
id: '2',
name: 'Science & Technology',
boxArtUrl: 'https://images.unsplash.com/photo-1518770660439-4636190af475?w=300&h=400&fit=crop',
viewerCount: 8932,
tags: ['Educational', 'Tech']
},
{
id: '3',
name: 'Just Chatting',
boxArtUrl: 'https://images.unsplash.com/photo-1519389950473-47ba0277781c?w=300&h=400&fit=crop',
viewerCount: 45123,
tags: ['IRL', 'Talk Show']
},
{
id: '4',
name: 'Music & Performing Arts',
boxArtUrl: 'https://images.unsplash.com/photo-1511379938547-c1f69419868d?w=300&h=400&fit=crop',
viewerCount: 6789,
tags: ['Music', 'Performance']
},
{
id: '5',
name: 'Art',
boxArtUrl: 'https://images.unsplash.com/photo-1460661419201-fd4cecdf8a8b?w=300&h=400&fit=crop',
viewerCount: 4321,
tags: ['Creative', 'Drawing']
},
{
id: '6',
name: 'Makers & Crafting',
boxArtUrl: 'https://images.unsplash.com/photo-1452860606245-08befc0ff44b?w=300&h=400&fit=crop',
viewerCount: 2156,
tags: ['DIY', 'Crafts']
}
];
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">Browse</h1>
{/* Tabs */}
<div className="flex gap-6 mb-8 border-b border-gray-800">
{[
{ key: 'categories', label: 'Categories' },
{ key: 'live', label: 'Live Channels' }
].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>
{/* Categories Grid */}
{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()} viewers
</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>
);
}Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What does the CI/CD pipeline with GitHub Actions described in the lesson do?
2. Which event triggers the Deploy workflow (the on: section)?
Hands-on tasks in the game
- Vertical ordering
Order the steps of the deploy job in the workflow:
- Horizontal ordering
Arrange the reference to the Vercel token from secrets (one line): ${{ secrets.VERCEL_TOKEN }}