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 הוא חלק בלתי נפרד מפיתוח מקצועי - הוא מבטיח שהקוד שלנו תמיד באיכות גבוהה ומוכן לפריסה.