לדלג לתוכן

11.6 CI CD לפרונטאנד הרצאה

CI/CD לפרונטאנד - CI/CD for Frontend

CI/CD (Continuous Integration / Continuous Deployment) הוא תהליך אוטומטי שמבטיח שהקוד שלנו תמיד עובד ומוכן לפרודקשן. בשיעור זה נלמד כיצד לבנות pipeline מלא לפרויקט פרונטאנד.


מה זה CI/CD ולמה זה חשוב?

שילוב מתמשך - Continuous Integration (CI)

  • כל שינוי קוד עובר בדיקות אוטומטיות
  • זיהוי בעיות מוקדם, לפני שהן מגיעות לפרודקשן
  • מבטיח שהקוד מתקמפל, עובר linting, type-checking ובדיקות

פריסה מתמשכת - Continuous Deployment (CD)

  • לאחר שהבדיקות עוברות, הקוד נפרס אוטומטית
  • Preview deployments לכל Pull Request
  • פריסה אוטומטית ל-production כשמתמזגים ל-main

למה זה חשוב?

  • מזהה שגיאות מהר יותר
  • מפחית טעויות אנושיות
  • מייצר אמון בקוד - אם הבדיקות עברו, הקוד עובד
  • מאיץ את תהליך הפיתוח

GitHub Actions - יסודות

בGitHub Actions מאפשר להריץ workflows אוטומטיים בתגובה לאירועים ב-repository.

מבנה בסיסי

# .github/workflows/ci.yml
name: CI Pipeline       # שם ה-workflow

on:                      # מתי לרוץ
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:                    # עבודות לביצוע
  build:                 # שם העבודה
    runs-on: ubuntu-latest  # סביבת ריצה

    steps:               # שלבים
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build

מושגים חשובים

  • Workflow - תהליך אוטומטי שלם שמוגדר בקובץ YAML
  • Job - קבוצת שלבים שרצים על אותה מכונה
  • Step - פעולה בודדת (הרצת פקודה או שימוש ב-action)
  • Action - פעולה מוכנה מראש שניתן לשימוש חוזר
  • Trigger - אירוע שמפעיל את ה-workflow (push, PR, schedule)

בניית CI Pipeline מלא

# .github/workflows/ci.yml
name: CI Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

# ביטול ריצות קודמות של אותו branch
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  # שלב 1: בדיקת קוד
  lint-and-typecheck:
    name: Lint & Type Check
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci

      - name: ESLint
        run: npm run lint

      - name: TypeScript Check
        run: npx tsc --noEmit

      - name: Prettier Check
        run: npx prettier --check "src/**/*.{ts,tsx,css}"

  # שלב 2: בדיקות
  test:
    name: Tests
    runs-on: ubuntu-latest
    needs: lint-and-typecheck  # רץ רק אחרי lint

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci

      - name: Unit Tests
        run: npm test -- --coverage

      - name: Upload Coverage
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage/

  # שלב 3: בנייה
  build:
    name: Build
    runs-on: ubuntu-latest
    needs: [lint-and-typecheck, test]  # רץ אחרי שניהם

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci

      - name: Build
        run: npm run build

      - name: Upload Build
        uses: actions/upload-artifact@v4
        with:
          name: build-output
          path: .next/

  # שלב 4: בדיקות E2E (אופציונלי)
  e2e:
    name: E2E Tests
    runs-on: ubuntu-latest
    needs: build

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci

      - name: Install Playwright Browsers
        run: npx playwright install --with-deps chromium

      - name: Run E2E Tests
        run: npx playwright test

      - name: Upload Test Results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report
          path: playwright-report/

בדיקות אוטומטיות ב-CI

הגדרת סקריפטים ב-package.json

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint . --ext .ts,.tsx",
    "lint:fix": "eslint . --ext .ts,.tsx --fix",
    "type-check": "tsc --noEmit",
    "format": "prettier --write \"src/**/*.{ts,tsx,css}\"",
    "format:check": "prettier --check \"src/**/*.{ts,tsx,css}\"",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage",
    "test:e2e": "playwright test",
    "test:e2e:ui": "playwright test --ui"
  }
}

הרצת בדיקות עם מטריצה - Matrix Strategy

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20, 22]
        # בדיקה על מספר גרסאות Node.js

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      - run: npm ci
      - run: npm test

Preview Deployments - פריסות תצוגה מקדימה

Vercel - הגדרה אוטומטית

Vercel מזהה אוטומטית פרויקטי Next.js ויוצר preview deployment לכל PR:

# Vercel מטפל בזה אוטומטית, אבל ניתן להוסיף בדיקות לפני
jobs:
  checks:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npm run type-check
      - run: npm test

  # Vercel auto-deploys after checks pass

שילוב בדיקות Lighthouse עם Preview

jobs:
  lighthouse:
    name: Lighthouse Check
    runs-on: ubuntu-latest
    needs: deploy-preview  # אחרי שה-preview עלה

    steps:
      - uses: actions/checkout@v4

      - name: Wait for Vercel Preview
        uses: patrickedqvist/wait-for-vercel-preview@v1.3.1
        id: preview
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          max_timeout: 300

      - name: Lighthouse
        uses: treosh/lighthouse-ci-action@v11
        with:
          urls: ${{ steps.preview.outputs.url }}
          budgetPath: ./lighthouse-budget.json
          uploadArtifacts: true
// lighthouse-budget.json
[
  {
    "path": "/*",
    "timings": [
      { "metric": "first-contentful-paint", "budget": 2000 },
      { "metric": "largest-contentful-paint", "budget": 3000 },
      { "metric": "cumulative-layout-shift", "budget": 0.1 },
      { "metric": "total-blocking-time", "budget": 300 }
    ],
    "resourceSizes": [
      { "resourceType": "script", "budget": 300 },
      { "resourceType": "total", "budget": 500 }
    ]
  }
]

משתני סביבה ב-CI

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      # משתנים ברמת ה-job
      NODE_ENV: production

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - run: npm ci

      - name: Build
        run: npm run build
        env:
          # משתנים ברמת ה-step
          NEXT_PUBLIC_API_URL: ${{ secrets.API_URL }}
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
          NEXT_PUBLIC_GA_ID: ${{ vars.GA_ID }}

הגדרת Secrets ב-GitHub

Repository Settings -> Secrets and variables -> Actions

Secrets (מוצפנים, לא גלויים):
- DATABASE_URL
- API_KEY
- JWT_SECRET

Variables (גלויים, לא רגישים):
- NEXT_PUBLIC_API_URL
- NODE_ENV

שימוש ב-Environment-specific secrets

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging  # סביבת staging
    steps:
      - run: echo "Deploying to ${{ vars.DEPLOY_URL }}"
        # משתמש במשתנים של סביבת staging

  deploy-production:
    runs-on: ubuntu-latest
    environment: production
    needs: deploy-staging
    steps:
      - run: echo "Deploying to ${{ vars.DEPLOY_URL }}"
        # משתמש במשתנים של סביבת production

הגנת Branch ו-Code Review

הגדרת Branch Protection ב-GitHub

Repository Settings -> Branches -> Branch protection rules

main:
- Require pull request reviews (1 reviewer)
- Require status checks to pass:
  - lint-and-typecheck
  - test
  - build
- Require branches to be up to date
- Require conversation resolution

Workflow לבדיקת PR

# .github/workflows/pr-check.yml
name: PR Check

on:
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  pr-validation:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # בדיקת גודל ה-PR
      - name: Check PR Size
        uses: actions/github-script@v7
        with:
          script: |
            const { data: files } = await github.rest.pulls.listFiles({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.issue.number,
            });

            const totalChanges = files.reduce(
              (sum, file) => sum + file.changes, 0
            );

            if (totalChanges > 500) {
              core.warning(
                `PR contains ${totalChanges} changes. Consider splitting into smaller PRs.`
              );
            }

      # בדיקה ש-PR כולל תיאור
      - name: Check PR Description
        uses: actions/github-script@v7
        with:
          script: |
            const pr = context.payload.pull_request;
            if (!pr.body || pr.body.length < 10) {
              core.setFailed('PR must include a description');
            }

ניטור והתראות - Monitoring and Alerting

התראות על כשלון CI

jobs:
  notify-on-failure:
    runs-on: ubuntu-latest
    needs: [lint-and-typecheck, test, build]
    if: failure()

    steps:
      - name: Notify Slack
        uses: slackapi/slack-github-action@v1.25.0
        with:
          payload: |
            {
              "text": "CI Failed on ${{ github.ref_name }}",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "*CI Pipeline Failed*\nBranch: `${{ github.ref_name }}`\nCommit: ${{ github.sha }}\nAuthor: ${{ github.actor }}\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>"
                  }
                }
              ]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

דוח Bundle Size

  bundle-analysis:
    runs-on: ubuntu-latest
    needs: build
    if: github.event_name == 'pull_request'

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run build

      - name: Analyze Bundle
        uses: hashicorp/nextjs-bundle-analysis@v1
        with:
          workflow-id: ci.yml
          token: ${{ secrets.GITHUB_TOKEN }}

Workflow מלא לפרויקט Next.js

# .github/workflows/main.yml
name: Main Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  NODE_VERSION: '20'

jobs:
  quality:
    name: Code Quality
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npx tsc --noEmit
      - run: npx prettier --check "src/**/*.{ts,tsx}"

  test:
    name: Tests
    runs-on: ubuntu-latest
    needs: quality
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      - run: npm ci
      - run: npm test -- --coverage --ci
      - uses: actions/upload-artifact@v4
        with:
          name: coverage
          path: coverage/lcov.info

  build:
    name: Build
    runs-on: ubuntu-latest
    needs: quality
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      - run: npm ci
      - run: npm run build
        env:
          NEXT_PUBLIC_API_URL: ${{ secrets.API_URL }}

  e2e:
    name: E2E Tests
    runs-on: ubuntu-latest
    needs: build
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      - run: npm ci
      - run: npx playwright install --with-deps chromium
      - run: npx playwright test
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: e2e-report
          path: playwright-report/

  deploy:
    name: Deploy to Production
    runs-on: ubuntu-latest
    needs: [test, build]
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    environment: production
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to Vercel
        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'

סיכום

בשיעור זה למדנו על:

  • CI/CD - מה זה, למה זה חשוב, וההבדל בין CI ל-CD
  • GitHub Actions - workflows, jobs, steps, triggers
  • CI Pipeline מלא - lint, type-check, test, build, E2E
  • בדיקות ב-CI - הרצת בדיקות אוטומטיות, matrix strategy
  • Preview Deployments - פריסת תצוגה מקדימה לכל PR
  • משתני סביבה - secrets, variables, environments
  • Branch Protection - הגנה על branches ותהליך code review
  • ניטור והתראות - Slack notifications, bundle analysis

CI/CD הוא חלק בלתי נפרד מפיתוח מקצועי - הוא מבטיח שהקוד שלנו תמיד באיכות גבוהה ומוכן לפריסה.