Kurs vibe coding z AI · Moduł 13: Twitch Clone

CI/CD Pipeline - GitHub Actions

1 min czytania

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. 1. Co robi pipeline CI/CD z GitHub Actions opisany w lekcji?

  2. 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 }}

Przydatne artykuły