1 שלב הבסיס

GitHub Actions — אוטומציה מקצה לקצה

בפרק הזה תבנו CI/CD pipeline מלא מאפס — בדיקות אוטומטיות, build, ופריסה. תלמדו לכתוב workflows ב-YAML, לנהל secrets, לייעל עם caching ו-matrix builds, וליצור reusable workflows שתשתפו בין פרויקטים. בסוף הפרק תהיה לכם מערכת אוטומציה שעובדת על כל push ו-PR.

מה יהיה לך בסוף הפרק הזה
מה תוכלו לעשות בסוף הפרק הזה
דרישות קדם
חוט הפרויקט

לאורך הקורס כולו אנחנו בונים שליטה מלאה ב-GitHub. בפרק הזה אתם בונים את מערכת ה-CI/CD — השכבה שמריצה בדיקות ופריסה אוטומטית. בפרק הבא (GitHub CLI) תלמדו לנטר ולנהל את ה-workflows האלה מהטרמינל, בלי לפתוח את הדפדפן.

הפרויקט שנבנה: pipeline אוטומטי שבודק קוד, בונה אותו ופורס — כל זה קורה בענן של GitHub, בלי שום שרת שלכם.

מילון מונחים
Workflow
תהליך אוטומטי שמוגדר בקובץ YAML בתיקיית .github/workflows/
Job
יחידת עבודה בתוך workflow שרצה על runner נפרד
Step
פעולה בודדת בתוך job — או action או פקודת shell
Action
יחידת קוד ניתנת לשימוש חוזר מה-marketplace או שבניתם בעצמכם
Runner
מכונה (וירטואלית או פיזית) שמריצה את ה-jobs
Trigger / Event
אירוע שגורם ל-workflow לרוץ — push, PR, schedule וכו'
YAML
פורמט טקסט מובנה לקבצי הגדרות — השפה של GitHub Actions
CI/CD
Continuous Integration / Continuous Deployment — בדיקות ופריסה אוטומטיים
Matrix Build
אסטרטגיה שמריצה job על מספר שילובים במקביל — OS, versions
Cache
שמירת תלויות בין הרצות כדי לחסוך זמן ודקות
Artifact
קובץ שנוצר ב-job אחד ואפשר להוריד אותו או להעביר ל-job אחר
Secret
ערך רגיש (מפתח API, סיסמה) שמאוחסן מוצפן ונגיש ב-workflow
Environment
הקשר פריסה עם כללי הגנה, secrets ייחודיים, ומגבלות branch
Concurrency Group
מנגנון שמבטיח שרק הרצה אחת של workflow פעילה בו-זמנית
Reusable Workflow
workflow שאפשר לקרוא לו מ-workflows אחרים עם workflow_call
הכנת פרויקט — לפני שמתחילים

לפני שנצלול לתוכן, בואו נוודא שיש לכם פרויקט עובד. אם כבר יש לכם ריפוזיטורי עם קוד Node.js — מצוין, השתמשו בו. אם לא, צרו אחד עכשיו:

# צרו תיקייה חדשה ואתחלו פרויקט
mkdir my-actions-lab && cd my-actions-lab
git init
npm init -y

# הוסיפו script פשוט של test ו-lint
npm install --save-dev eslint

# צרו קובץ index.js בסיסי
echo 'console.log("Hello Actions!");' > index.js

# עדכנו package.json — הוסיפו scripts:
#   "test": "node --test",
#   "lint": "eslint .",
#   "build": "echo 'build complete' && mkdir -p dist && cp index.js dist/"

# דחפו ל-GitHub
git add -A
git commit -m "Initial setup"
gh repo create my-actions-lab --public --source=. --push

חשוב: בדקו שיש לכם package.json עם scripts של test, lint ו-build. כל התרגילים בפרק מתבססים על זה. אם אתם עובדים עם Python או שפה אחרת, תוכלו להתאים — אבל הדוגמאות כתובות ל-Node.js.

1.1 מה זה GitHub Actions ולמה זה חשוב

GitHub Actions הוא מערכת אוטומציה מובנית ישירות ב-GitHub. כל פעולה שאתם עושים בריפוזיטורי — push, פתיחת PR, יצירת release, ואפילו תגובה על issue — יכולה להפעיל תהליך אוטומטי: בדיקות, build, פריסה, שליחת הודעות, ועוד. זו לא עוד מערכת CI/CD חיצונית שצריך לחבר — זה חלק אינטגרלי מ-GitHub עצמו.

נחשוב על דוגמה קונקרטית: אתם עובדים על פרויקט עם צוות. מישהו פותח Pull Request עם שינוי בקוד. מה קורה?

  1. GitHub Actions מריץ את הבדיקות אוטומטית — unit tests, linting, type checking
  2. אם הכל עובר — ה-PR מסומן בירוק ומוכן ל-review
  3. אחרי merge ל-main — Actions בונה את הפרויקט ופורס אותו לפרודקשן
  4. אם משהו נכשל — מייל או הודעת Slack נשלחים לצוות

כל זה קורה בלי שמישהו נוגע בכפתור. זו המהות של CI/CD — Continuous Integration ו-Continuous Deployment.

למה Actions ולא Jenkins, CircleCI או Travis CI?

שלוש סיבות עיקריות:

  1. מובנה. אין צורך להקים שרת חיצוני, לנהל Jenkins, או לחבר שירות צד שלישי. Actions חי בתוך GitHub, באותו מקום שבו הקוד, ה-PRs וה-issues. אפס תצורה חיצונית. הכל מתחיל מקובץ YAML אחד בריפוזיטורי שלכם.
  2. חינמי (לרוב הפרויקטים). 2,000 דקות בחודש בתוכנית החינמית. לרוב הפרויקטים הקטנים והבינוניים — זה מספיק בהחלט. ריפוזיטורים ציבוריים מקבלים דקות ללא הגבלה.
  3. אקוסיסטם ענק. מעל 25,000 actions מוכנים ב-marketplace. רוצים לפרוס ל-Vercel? יש action. לשלוח הודעה ב-Slack? יש action. לבדוק קוד עם CodeQL? מובנה. רוצים לסרוק dependencies חשודים? יש action גם לזה.

בפועל, GitHub Actions הפך לסטנדרט דה-פקטו עבור CI/CD בפרויקטי קוד פתוח ובחברות שעובדות עם GitHub. הפופולריות הזו יוצרת אפקט רשת — יותר משתמשים, יותר actions ב-marketplace, יותר תיעוד, יותר דוגמאות, ויותר ידע קהילתי שזמין בחיפוש פשוט.

השוואה מהירה לפלטפורמות אחרות

אם יש לכם ניסיון עם כלי CI/CD אחרים, הנה איך Actions משתווה:

מתי Actions הוא לא הבחירה הנכונה?

הוגנות חשובה. Actions לא מושלם לכל מצב:

לכל שאר המקרים — וזה 95% מהפרויקטים — Actions הוא כנראה הבחירה הנכונה. היתרון של "הכל באותו מקום" (קוד, CI, issues, PRs, packages, security scanning) קשה להפריז בחשיבותו. פחות כלים = פחות context switching = יותר פרודוקטיביות.

בואו נסתכל על מה שזה אומר בפועל. בלי Actions, כל מפתח צריך:

עם Actions, כל השלבים האלה אוטומטיים. טעות אנוש? לא רלוונטי. שכחתם להריץ tests? לא משנה — Actions מריץ בשבילכם. שכחתם לפרוס אחרי merge? Actions פורס אוטומטית. הלכתם הביתה? Actions ממשיך לעבוד.

זה לא רק נוחות — זה אמינות. מערכת אוטומטית לא שוכחת, לא מתעייפת, ולא עושה טעויות כי היא ממהרת. CI/CD טוב הוא ההבדל בין "דחפנו קוד שבור לפרודקשן" ל-"Actions תפס את הבאג לפני ש-merge קרה".

עשו עכשיו

פתחו ריפוזיטורי שלכם ב-GitHub ולחצו על הטאב Actions. האם יש workflows? אם כן — לחצו על אחד ובדקו את ההיסטוריה שלו. אם לא — בדקו אם יש תיקיית .github/workflows/ בקוד. אם שניהם ריקים — מצוין, תיכף נבנה את ה-workflow הראשון.

1.2 אנטומיה של Workflow — מ-YAML לאוטומציה

כל workflow הוא קובץ YAML בתיקיית .github/workflows/. YAML הוא פורמט טקסט מובנה — פשוט, קריא, ורגיש ל-indentation (הזחה). אם עבדתם עם JSON, חשבו על YAML כ-JSON יותר קריא, בלי סוגריים מסולסלים.

בואו נפרק workflow שלם לחלקים ונבין כל שורה:

name: CI Pipeline              # שם ה-workflow — מופיע בטאב Actions
on:                            # מה מפעיל אותו (trigger)
  push:
    branches: [main]           # רק על push ל-main
  pull_request:
    branches: [main]           # ועל PR לכיוון main

jobs:                          # רשימת ה-jobs (יחידות עבודה)
  test:                        # שם ה-job הראשון
    runs-on: ubuntu-latest     # על איזה runner לרוץ
    steps:                     # רשימת הצעדים בתוך ה-job
      - uses: actions/checkout@v4     # action מוכן — מוריד את הקוד
      - uses: actions/setup-node@v4   # מגדיר Node.js
        with:
          node-version: 22            # פרמטר ל-action
      - run: npm install              # פקודת shell — התקנת תלויות
      - run: npm test                 # פקודת shell — הרצת בדיקות

ארבע שכבות, מלמעלה למטה:

שכבה 1: Trigger (on)

מה מפעיל את ה-workflow. הנפוצים ביותר:

אפשר (ומומלץ) לשלב כמה triggers:

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  workflow_dispatch:          # גם ידני, ליתר ביטחון
  schedule:
    - cron: '0 6 * * 1'      # כל שני בשש בבוקר UTC

שכבה 2: Jobs

יחידות עבודה עצמאיות. כל job רץ על runner נפרד — מכונה וירטואלית חדשה ונקייה. jobs יכולים לרוץ במקביל כברירת מחדל, או בסדר עם needs. כל job יכול לרוץ על מערכת הפעלה שונה.

שכבה 3: Steps

הפעולות בתוך job. כל step הוא או uses (action מוכן מה-marketplace) או run (פקודת shell שכתבתם). Steps רצים תמיד בסדר, אחד אחרי השני. אם step נכשל — ה-job עוצר (אלא אם הוספתם continue-on-error: true).

שכבה 4: Actions

יחידות קוד ניתנות לשימוש חוזר. actions/checkout מוריד את הקוד מהריפוזיטורי, actions/setup-node מתקין Node.js, וכו'. נדבר על actions בהרחבה בסעיף 1.4.

שימו לב להבדל חשוב:

אנטומיה של Workflow Trigger (on) Job: test Step 1: actions/checkout@v4 Step 2: actions/setup-node@v4 Step 3: npm test needs Job: deploy Step 1: deploy to production environment: production הזרימה: 1. אירוע מפעיל (push/PR) 2. Jobs רצים (מקבילי/סדרתי) 3. Steps רצים בסדר בתוך job 4. Actions = צעדים מוכנים needs: מגדיר תלויות בין jobs

expressions — הגישה לנתונים דינמיים

שמתם לב ל-${{ }} בקוד? זה syntax של expressions — הדרך לגשת לנתונים דינמיים ב-workflow:

expressions עובדים גם בתנאים. למשל, job שרץ רק על main:

  deploy:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest

YAML — הדברים שחשוב לדעת

YAML הוא רגיש ל-indentation. טעות של רווח אחד יכולה לשבור את כל ה-workflow. כמה כללים:

הנה טעויות נפוצות שישברו את ה-workflow:

# ❌ שגוי — indentation לא עקבי
jobs:
  test:
    steps:
    - run: echo "wrong"      # ← צריך להיות מוזח פנימה

# ✅ נכון
jobs:
  test:
    steps:
      - run: echo "correct"

# ❌ שגוי — חסר רווח אחרי נקודתיים
jobs:
  test:
    runs-on:ubuntu-latest     # ← חסר רווח

# ✅ נכון
jobs:
  test:
    runs-on: ubuntu-latest

טיפ: השתמשו ב-extension של YAML ב-VS Code (למשל redhat.vscode-yaml) — הוא מזהה שגיאות YAML בזמן אמת ויציל לכם שעות של debug. גם GitHub עצמו מציע validation ב-web editor — אם יש שגיאת syntax, תראו קו אדום.

Multi-line commands ב-run

כשצריך להריץ כמה פקודות ב-step אחד, השתמשו ב-pipe (|) ב-YAML:

- name: Build and verify
  run: |
    echo "Starting build..."
    npm run build
    echo "Build complete, checking output..."
    ls -la dist/
    echo "Files in dist: $(ls dist/ | wc -l)"

כל שורה היא פקודה נפרדת. אם אחת נכשלת (exit code שונה מ-0) — ה-step נכשל. זה התנהגות ברירת מחדל טובה.

Environment Variables — משתני סביבה

אפשר להגדיר משתני סביבה בשלוש רמות:

# ברמת workflow — זמין לכל ה-jobs
env:
  NODE_ENV: production

jobs:
  build:
    # ברמת job — זמין לכל ה-steps ב-job
    env:
      CI: true
    steps:
      # ברמת step — זמין רק ב-step הזה
      - run: echo $MY_VAR
        env:
          MY_VAR: hello

סדר העדיפויות: step > job > workflow. אם אותו משתנה מוגדר בכמה רמות, הרמה הספציפית ביותר מנצחת.

Workflow Status Badge

רוצים להציג את סטטוס ה-CI ב-README? GitHub מייצר badge אוטומטית לכל workflow. הוסיפו ל-README.md:

![CI Status](https://github.com/YOUR_USER/YOUR_REPO/actions/workflows/ci.yml/badge.svg)

ה-badge מתעדכן אוטומטית — ירוק כשהכל עובר, אדום כשיש כשלון.

עשו עכשיו

צרו קובץ .github/workflows/hello.yml בריפוזיטורי שלכם עם ה-workflow הבא:

name: Hello World
on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  greet:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Hello from GitHub Actions!"
      - run: echo "Branch: ${{ github.ref_name }}"
      - run: echo "Actor: ${{ github.actor }}"
      - run: echo "Event: ${{ github.event_name }}"
      - run: echo "Runner OS: ${{ runner.os }}"

עשו push, ואז לכו לטאב Actions — תראו את ה-workflow רץ. לחצו עליו לראות את הפלט של כל step. שימו לב ש-workflow_dispatch מאפשר גם הרצה ידנית.

עשו עכשיו

עכשיו הריצו את ה-workflow ידנית: לכו לטאב Actions, בחרו את "Hello World" בצד שמאל, לחצו על "Run workflow". בדקו — מה ההבדל בפלט בין event_name כשהרצתם ידנית לעומת push? (תשובה: workflow_dispatch לעומת push).

1.3 Runners — איפה הקוד שלך באמת רץ

כל job רץ על runner — מכונה וירטואלית שמספקת את סביבת ההרצה. ה-runner הוא מכונה חד-פעמית: נוצרת בתחילת ה-job, נמחקת בסופו. שום דבר לא נשמר בין הרצות (חוץ מ-cache, שנלמד בסעיף 1.8).

יש שלושה סוגים:

GitHub-hosted runners

מכונות שגיטהאב מנהל בשבילכם. אפס תחזוקה:

מפרט ריפוזיטורים ציבוריים: 4 vCPUs, 16GB RAM, 14GB SSD. מספיק לרוב ה-builds בנוחות.

שימו לב: ריפוזיטורים פרטיים מקבלים runners עם מפרט מופחת — 2 vCPU ו-8GB RAM. אם ה-build שלכם כבד ואתם על ריפו פרטי, ייתכן שתרגישו את ההבדל.

Self-hosted runners

מכונות שלכם — פיזיות או וירטואליות — שאתם מחברים ל-GitHub. יתרונות: שליטה מלאה בחומרה, תוכנה מותאמת, אפשרות ל-GPU. חסרונות: אתם אחראים על תחזוקה, אבטחה, ועדכונים. מתאים לבניות כבדות, תוכנה מיוחדת, או דרישות אבטחה מחמירות.

Larger runners

מכונות GitHub-hosted חזקות יותר — מ-2 עד 64 cores. זמינים ב-Team ו-Enterprise. מאפשרים builds מהירים יותר בלי לנהל מכונות בעצמכם. מחירים גבוהים יותר מ-standard runners, אבל עדיין אפס תחזוקה.

מה מגיע מותקן על GitHub-hosted runner?

השאלה הנפוצה: "האם אצטרך להתקין X?". התשובה: כנראה שלא. GitHub-hosted runners מגיעים עם עשרות כלים מותקנים מראש:

הרשימה המלאה זמינה ב-github.com/actions/runner-images. אם כלי חסר — התקינו אותו ב-step, או השתמשו ב-Docker container.

Services — מסדי נתונים ב-workflow

צריכים PostgreSQL לבדיקות? אפשר להרים service container ישירות ב-workflow:

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: testpass
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
    steps:
      - uses: actions/checkout@v4
      - run: npm test
        env:
          DATABASE_URL: postgres://postgres:testpass@localhost:5432/postgres

ה-service עולה לפני ה-steps ומוריד אחריהם. אפס תצורה, אפס ניקיון. Services זמינים רק ב-Linux runners.

Runner labels וגמישות

כל runner מזוהה על ידי labels. ב-GitHub-hosted, ה-labels הם שמות כמו ubuntu-latest, ubuntu-24.04, windows-latest. ב-self-hosted, אתם מגדירים labels משלכם:

jobs:
  build:
    runs-on: [self-hosted, linux, gpu]    # runner עם 3 labels

Labels מאפשרים לכוון jobs ל-runners ספציפיים. למשל, runner עם GPU לבניית models, או runner עם גישה ל-VPN פנימי. שימו לב שכל ה-labels חייבים להתקיים — זה AND, לא OR.

ubuntu-latest vs ubuntu-24.04: ubuntu-latest מצביע תמיד לגרסה האחרונה. כרגע זה Ubuntu 24.04. כשגרסה חדשה יצאה (26.04 בהמשך), ubuntu-latest ישתנה. אם אתם צריכים יציבות, הצמדו לגרסה ספציפית: ubuntu-24.04.

מסגרת החלטה: בחירת Runner
קריטריוןGitHub-hostedSelf-hostedLarger runners
עלותלפי דקות (free tier כלול)עלות חומרה שלכם + $0.002/דקה platform feeלפי דקות, יקר יותר
תחזוקהאפס — GitHub מנהלאתם אחראים לכל עדכון ותחזוקהאפס — GitHub מנהל
ביצועיםסטנדרטי (4 core public / 2 core private)לפי החומרה שלכם — ללא הגבלה2-64 cores לבחירתכם
אבטחהsandbox חד-פעמי — מושלםאתם אחראים — סיכון אם repo ציבוריsandbox חד-פעמי
מתאים ל-95% מהפרויקטיםbuilds כבדים, GPU, תוכנה ייחודיתbuilds כבדים בארגון Team/Enterprise

הכלל: התחילו תמיד עם ubuntu-latest. עברו ל-self-hosted או larger רק כשיש צורך מוכח — build שנמשך מעל 10 דקות, תוכנה שאי אפשר להתקין, או דרישות GPU.

עשו עכשיו

בדקו ב-workflow שלכם — על איזה runner הוא רץ? אם כתוב runs-on: ubuntu-latest, מצוין — זו הבחירה הכלכלית ביותר. אם יש macos-latest בלי סיבה טובה (כמו build ל-iOS), שנו ל-Ubuntu — תחסכו פי 10 בדקות. גם windows-latest הוא פי 2 מ-Linux — השתמשו בו רק אם באמת צריך.

1.4 Actions Marketplace — אל תמציאו את הגלגל

ב-marketplace יש מעל 25,000 actions מוכנים. לפני שאתם כותבים step עם run ו-10 שורות bash, בדקו אם יש action שעושה את זה — בדרך כלל יש, וכנראה שהוא טוב יותר ממה שתכתבו מאפס.

Actions רשמיים שחובה להכיר

GitHub מתחזקת סט actions רשמיים תחת הארגון actions/. אלה נבדקים, מתוחזקים, ובטוחים לשימוש:

Actions פופולריים של צד שלישי

איך מעריכים action לפני שימוש

לא כל action במרקטפלייס שווה שימוש. לפני שאתם מוסיפים action, בדקו:

  1. מספר כוכבים — מעל 100 כוכבים זה סימן טוב
  2. עדכון אחרון — אם לא עודכן חצי שנה, זהירות
  3. Verified creator — תג כחול אומר שגיטהאב אימתה את היוצר
  4. README ברור — תיעוד טוב = action מתוחזק
  5. Issues פתוחים — יותר מדי issues ללא מענה = action נטוש
אזהרה: אל תצמידו action ל-tag

לעולם אל תצמידו action ל-tag כמו v4 בפרודקשן. Tag יכול להשתנות — מישהו יכול לדחוף קוד זדוני ל-tag קיים (supply-chain attack). במקום זה, השתמשו ב-commit SHA מלא:

# ❌ לא בטוח — tag יכול להשתנות
- uses: actions/checkout@v4

# ✅ בטוח — SHA הוא immutable
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.7

ה-SHA הוא immutable — אי אפשר לשנות אותו. ההערה # v4.1.7 היא בשביל קריאות בלבד. לפרויקטי לימוד ותרגול, tag מספיק — אבל בפרודקשן, תמיד SHA.

איך מוצאים SHA? לכו לריפוזיטורי של ה-action ← Releases ← לחצו על הגרסה ← העתיקו את ה-commit SHA. או השתמשו ב-CLI: gh api repos/actions/checkout/commits/v4 --jq .sha.

Action Allowlisting — בקרה על actions מותרים

בארגונים גדולים, לא תמיד רוצים שכל מפתח ישתמש בכל action מהמרקטפלייס. GitHub מאפשר action allowlisting ברמת הארגון: אפשר להגביל שימוש רק ל-actions מ-creators מסוימים, או רק ל-actions שעברו סקירה פנימית. זה מנהל ב-Settings → Actions → General → Policies.

שלוש רמות הגבלה:

עשו עכשיו

כנסו ל-github.com/marketplace?type=actions וחפשו שלושה actions רלוונטיים לפרויקט שלכם. לכל אחד, רשמו: שם, כוכבים, ו-verified creator (כן/לא). העדיפו תמיד actions מ-creators מאומתים.

1.5 בונים CI/CD Pipeline — מאפס לפריסה

הגיע הזמן לבנות pipeline אמיתי. ניצור workflow עם 4 jobs: lint, test, build, deploy. לפני שנתחיל, כמה מושגים שנשתמש בהם בתרגיל (ומוסברים לעומק בהמשך הפרק):

אל תדאגו אם אתם לא מבינים את כל הפרטים עכשיו — העיקר לראות את התמונה המלאה. נחזור לכל מושג בנפרד.

CI/CD Pipeline — זרימת העבודה Lint eslint, prettier Test jest, vitest רצים במקביל Build npm run build + artifact Deploy environment: production needs needs
תרגיל 1: בניית CI/CD Pipeline

מטרה: ליצור workflow מלא עם 4 jobs שרצים בסדר הנכון.

דרישות: ריפוזיטורי עם package.json שכולל scripts של lint, test, ו-build. אם אין לכם — חזרו להכנת הפרויקט למעלה.

שלב 1: צרו קובץ .github/workflows/ci.yml:

name: CI/CD Pipeline
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'
      - run: npm ci
      - run: npm run lint

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'
      - run: npm ci
      - run: npm test

  build:
    needs: [lint, test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'
      - run: npm ci
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: build-output
          path: dist/

  deploy:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    environment: production
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: build-output
          path: dist/
      - run: echo "Deploying to production..."
      # Replace with your actual deploy command
      - run: ls -la dist/

שלב 2: עשו commit ו-push. לכו לטאב Actions וצפו:

  1. lint ו-test רצים במקביל (כי אין needs ביניהם)
  2. build ממתין לשניהם (בזכות needs: [lint, test])
  3. deploy ממתין ל-build, ורק על main (בגלל ה-if)

שלב 3: פתחו PR (לא ישירות ל-main) — תראו ש-deploy לא רץ. זה התנהגות נכונה — deploy רק ב-main.

מה למדנו:

  • needs יוצר תלויות בין jobs — מבטיח שהסדר נכון
  • if מאפשר תנאים — deploy רק ב-main, לא ב-PR
  • upload-artifact / download-artifact מעבירים קבצים בין jobs שרצים על runners שונים
  • environment מגדיר הקשר פריסה עם protection rules
  • cache: 'npm' ב-setup-node חוסך זמן התקנה משמעותי

Troubleshooting: אם ה-workflow נכשל, בדקו:

  1. האם package.json מכיל scripts של lint, test ו-build?
  2. האם package-lock.json קיים? (npm ci דורש אותו — אם חסר, השתמשו ב-npm install במקום)
  3. האם ה-indentation ב-YAML נכון? (2 רווחים, לא tabs)
  4. האם ה-build script באמת יוצר תיקיית dist/?
עשו עכשיו

דחפו את ה-workflow, פתחו PR, וצפו ב-Actions טאב. ודאו שאתם רואים את lint ו-test רצים במקביל. אם job נכשל — לחצו עליו ובדקו את הלוג. הסיבה הנפוצה: חסר script ב-package.json.

1.6 Secrets ו-Environments — אבטחה בפריסה

בכל pipeline רציני יש ערכים רגישים — מפתחות API, סיסמאות, tokens, connection strings. לעולם אל תשימו אותם בקוד — לא ב-YAML, לא בקובץ .env שנדחף ל-GitHub, ולא כ-hardcoded strings. GitHub מספק מנגנון secrets מוצפן ומאובטח.

שלוש רמות של Secrets

הגישה ל-secret ב-workflow היא דרך ${{ secrets.SECRET_NAME }}. GitHub מסתיר אוטומטית את הערך בלוגים — תראו *** במקום הערך האמיתי.

בנוסף, כל workflow מקבל אוטומטית GITHUB_TOKEN — token זמני עם הרשאות מוגבלות לריפוזיטורי הנוכחי. לא צריך להגדיר אותו — הוא קיים תמיד. לרוב מספיק לפעולות כמו יצירת comments, עדכון statuses, ויצירת releases.

Environments — שכבת אבטחה נוספת

Environment הוא הגדרה שמייצגת סביבת פריסה (staging, production, QA). מעבר ל-secrets ייחודיים לכל environment, אפשר להגדיר protection rules:

Environment מופיע ב-job עם מילת המפתח environment::

  deploy:
    runs-on: ubuntu-latest
    environment: production    # ← כאן מוגדר ה-environment
    steps:
      - run: echo "Deploying with ${{ secrets.DEPLOY_KEY }}"

ב-YAML הזה, ה-job deploy ישתמש ב-secrets של ה-environment production. אם הגדרתם required reviewers — ה-job יחכה לאישור ידני לפני שהוא רץ.

GITHUB_TOKEN — מה מגיע אוטומטית

כל workflow run מקבל אוטומטית GITHUB_TOKEN — token זמני שפג תוקפו בסוף ה-run. אפשר לגשת אליו דרך ${{ secrets.GITHUB_TOKEN }} או ${{ github.token }}. מה הוא יכול לעשות?

מה הוא לא יכול? לפעול על ריפוזיטורים אחרים, להפעיל workflows בריפוזיטורים אחרים (כדי למנוע infinite loops), ולגשת ל-secrets של ריפוזיטורים אחרים.

אפשר לשלוט בהרשאות של GITHUB_TOKEN ברמת ה-workflow:

permissions:
  contents: read       # קריאת קוד
  pull-requests: write # כתיבת comments על PRs
  issues: write        # כתיבת comments על issues

best practice: הגדירו permissions מינימליים. אם ה-workflow רק מריץ tests — הוא צריך רק contents: read. כלל הזהב: least privilege.

תבנית מומלצת: staging ו-production

הנה תבנית שעובדת לרוב הפרויקטים:

  1. Environment staging — בלי protection rules, deploy אוטומטי על כל merge ל-main
  2. Environment production — עם required reviewer ו-wait timer של 5 דקות, deploy רק אחרי אישור

ב-workflow:

  deploy-staging:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - run: echo "Deploy to staging"

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: production     # ← דורש אישור ידני
    steps:
      - run: echo "Deploy to production"

הרעיון: קודם מפרסים ל-staging (אוטומטי), בודקים שהכל עובד, ואז מאשרים ידנית את הפריסה לפרודקשן. פשוט, בטוח, ויעיל.

אזהרה: Secrets בקוד = אסון אבטחה

לעולם אל תשימו secrets בקובץ YAML! גם לא בקובץ .env שעולה ל-GitHub. השתמשו רק ב-Settings → Secrets and variables → Actions. גם אם זה נראה "יותר נוח" — מישהו יעשה fork ויראה הכל. GitHub סורקת באופן אוטומטי secrets שנדחפו בטעות (secret scanning), אבל עדיף לא להגיע לשם.

גם ב-PR logs: אם עשיתם echo ${{ secrets.MY_KEY }}, GitHub מסתיר את הערך. אבל אם חילקתם אותו לתווים בודדים עם bash — הוא עלול להופיע. אל תנסו לעקוף את ההסתרה.

עשו עכשיו

הגדירו secret: Settings → Secrets and variables → Actions → New repository secret. שם: TEST_SECRET, ערך: כל מה שתרצו (למשל hello-from-settings). עכשיו הוסיפו step ל-workflow:

- run: echo "Secret length: ${#SECRET}"
  env:
    SECRET: ${{ secrets.TEST_SECRET }}

שימו לב: GitHub מסתיר אוטומטית את הערך בלוגים — תראו ***. אבל אתם כן יכולים להשתמש בערך בפקודות (למשל לפריסה).

עשו עכשיו

צרו environment: Settings → Environments → New environment. שם: staging. הוסיפו wait timer של דקה אחת. עכשיו עדכנו job ב-workflow שלכם להשתמש בו: environment: staging. הריצו ותראו שה-job מחכה דקה לפני שרץ — זו ההגנה בפעולה.

1.7 Matrix Builds — בדיקה על כל שילוב

צריכים לבדוק שהקוד עובד על כמה גרסאות Node? או על Linux וגם Windows? בלי matrix, תצטרכו לכתוב job נפרד לכל שילוב. Matrix builds פותרים את זה — מגדירים את השילובים פעם אחת, ו-Actions מריץ jobs לכל שילוב במקביל:

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        node-version: [20, 22]
      fail-fast: false    # אל תעצור הכל אם אחד נכשל
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
      - run: npm ci
      - run: npm test

התוצאה: 4 jobs מקביליים — Ubuntu+Node20, Ubuntu+Node22, Windows+Node20, Windows+Node22. בלי matrix, הייתם צריכים לכתוב 4 jobs נפרדים עם קוד כמעט זהה. עם matrix — הגדרה אחת.

הגדרות מתקדמות

fail-fast — ברירת מחדל: true. כשהוא true, אם job אחד נכשל — כל שאר ה-matrix jobs מבוטלים. שנו ל-false כדי לראות את כל התוצאות:

strategy:
  fail-fast: false   # תמשיך לרוץ גם אם שילוב אחד נכשל

include — להוסיף שילובים ספציפיים שלא נוצרים מהמטריצה:

strategy:
  matrix:
    os: [ubuntu-latest]
    node-version: [20, 22]
    include:
      - os: macos-latest     # שילוב ספציפי נוסף
        node-version: 22

exclude — להוציא שילובים שלא רלוונטיים:

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node-version: [18, 20, 22]
    exclude:
      - os: macos-latest      # אין טעם לבדוק macOS עם Node 18
        node-version: 18

max-parallel — להגביל כמה jobs רצים במקביל. שימושי כשרוצים לחסוך minutes:

strategy:
  max-parallel: 2    # מקסימום 2 jobs מקביליים

חישוב עלות matrix

שימו לב: matrix מכפיל את צריכת הדקות. 3 OS × 3 Node versions = 9 jobs מקביליים. אם כל job רץ 3 דקות, זה 27 דקות actions. עם macOS בתמהיל (מכפיל x10), זה יכול לעלות מהר.

דוגמה: matrix של 3 OS (Linux, Windows, macOS) × 3 Node versions (18, 20, 22):

אם זה רץ 4 פעמים ביום × 22 ימי עבודה = 10,296 דקות בחודש. הרבה מעבר ל-free tier. תכננו בהתאם.

טיפ פרקטי: matrix מצומצם ב-PR, מלא ב-main

פתרון נפוץ: הריצו matrix מצומצם ב-PRs (רק Linux + Node latest), ואת ה-matrix המלא רק על push ל-main:

jobs:
  test:
    strategy:
      matrix:
        os: ${{ github.event_name == 'push' && fromJSON('["ubuntu-latest","windows-latest","macos-latest"]') || fromJSON('["ubuntu-latest"]') }}
        node-version: ${{ github.event_name == 'push' && fromJSON('[18,20,22]') || fromJSON('[22]') }}

ככה PRs רצים מהר (job אחד), ו-main נבדק על הכל.

עשו עכשיו

הוסיפו matrix build ל-workflow שלכם: ubuntu-latest ו-windows-latest עם Node 20 ו-22. הריצו וצפו ב-4 jobs רצים במקביל בטאב Actions. שאלו את עצמכם: האם באמת צריך את כל השילובים, או שמספיק לבדוק על Ubuntu בלבד?

1.8 Caching ו-Artifacts — חיסכון זמן וכסף

שני מנגנונים שחוסכים זמן ודקות: caching לתלויות ו-artifacts לקבצי build. חובה להכיר את שניהם.

Caching — שמירת תלויות בין הרצות

npm install לוקח 30-60 שניות? עם cache — 3-5 שניות. זה חוסך 60-80% מזמן ההתקנה בממוצע. הרעיון פשוט: שומרים את התלויות המותקנות, ומשחזרים אותן בהרצה הבאה.

- uses: actions/cache@v4
  with:
    path: ~/.npm                 # מה לשמור
    key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |              # fallback keys
      npm-${{ runner.os }}-

איך זה עובד:

  1. הרצה ראשונה: אין cache (miss) → התקנה מלאה → שמירה ל-cache
  2. הרצה שנייה: יש cache (hit) → שחזור מיידי → דילוג על התקנה
  3. שינוי ב-package-lock.json: key חדש → cache miss → התקנה מלאה → שמירה מחדש

ה-restore-keys הוא fallback: אם אין cache מדויק, GitHub מחפש cache עם prefix תואם. זה אומר שגם אחרי שינוי קטן ב-dependencies, תקבלו cache חלקי שעדיין חוסך זמן. לדוגמה: אם המפתח המדויק הוא npm-Linux-abc123 ולא נמצא, GitHub יחפש cache שמתחיל ב-npm-Linux- — ייתכן שהוא לא מדויק, אבל 95% מהתלויות כבר שם.

Cross-branch caching

Cache ב-GitHub Actions עובר בין branches — אבל בכיוון אחד. ה-default branch (בדרך כלל main) הוא המקור. כלומר:

זה אומר שחשוב שה-main branch יריץ workflows באופן קבוע כדי ליצור cache עדכני שכל ה-feature branches ייהנו ממנו.

דרך קלה יותר: actions/setup-node@v4 כבר כולל cache מובנה. מספיק להוסיף שורה אחת:

- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: 'npm'     # ← זה מספיק! לא צריך actions/cache נפרד

גם setup-python ו-setup-java תומכים ב-cache מובנה באותו אופן.

מה כדאי לשמור ב-cache

שימו לב: Cache ו-artifacts חולקים את אותו storage quota (10GB לריפוזיטורים חינמיים). אם ה-cache שלכם גדול מדי, הישנים נמחקים אוטומטית (FIFO).

Cache: לפני ואחרי בלי Cache npm install — 45 שניות npm install — 45 שניות (שוב!) עם Cache npm install — 45 שניות (פעם ראשונה) restore cache — 3 שניות

Artifacts — העברת קבצים בין jobs

Artifacts הם קבצי build (או כל קובץ אחר) שנוצרים ב-job אחד ואפשר:

השימוש הקלאסי: job אחד בונה (upload-artifact), job אחר פורס (download-artifact):

# ב-build job:
- uses: actions/upload-artifact@v4
  with:
    name: build-output
    path: dist/
    retention-days: 7       # כמה ימים לשמור

# ב-deploy job:
- uses: actions/download-artifact@v4
  with:
    name: build-output
    path: dist/

retention-days — כברירת מחדל, artifacts נשמרים 90 יום. שנו ל-7 או 14 כדי לחסוך storage. אפשר גם להגדיר ברמת הריפוזיטורי ב-Settings → Actions → General.

Artifacts vs Cache — מה ההבדל?

שני מנגנונים ששומרים קבצים, אבל לשימושים שונים:

קריטריוןCacheArtifact
מטרהשמירת תלויות (node_modules, pip)שמירת תוצרי build (dist/, reports)
בין הרצות?כן — זו המטרהבין jobs באותו run, או הורדה ידנית
שמירהנמחק אוטומטית אחרי 7 ימים ללא שימושנמחק לפי retention-days (ברירת מחדל 90)
גודלעד 10GB לריפו (חינמי)חולק את אותו quota
גישהאוטומטית לפי keyupload/download או UI

כלל אצבע: תלויות → cache. קבצי פלט → artifact.

דוגמה מלאה: build + test report כ-artifacts

  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'
      - run: npm ci
      - run: npm test -- --reporter=junit --output=test-results.xml
      - run: npm run build

      # שמירת build output
      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/
          retention-days: 7

      # שמירת test results (שימושי ל-debug)
      - uses: actions/upload-artifact@v4
        if: always()              # ← גם אם הבדיקות נכשלו!
        with:
          name: test-results
          path: test-results.xml
          retention-days: 14

שימו לב ל-if: always() — בלי זה, אם הבדיקות נכשלות ה-step של upload לא ירוץ, ותפסידו את דוח הבדיקות שאתם הכי צריכים.

עשו עכשיו

הוסיפו cache: 'npm' ל-actions/setup-node ב-workflow שלכם (אם עדיין לא הוספתם). הריצו פעמיים ובדקו את ההבדל בזמן — הרצה שנייה צריכה להיות מהירה בהרבה. בדקו בלוגים של ה-step — תראו הודעת "Cache restored successfully".

1.9 Reusable Workflows ו-Composite Actions — DRY בגדול

יש לכם 10 ריפוזיטורים עם אותו CI workflow? אל תעתיקו — שתפו. עקרון ה-DRY (Don't Repeat Yourself) חל גם על workflows. שתי דרכים עיקריות:

Reusable Workflows — pipeline שלם לשיתוף

Reusable workflow הוא workflow שלם שאפשר לקרוא לו מ-workflow אחר. המפתח: ה-trigger workflow_call שהופך workflow רגיל ל-reusable:

# .github/workflows/reusable-ci.yml (בריפו המשותף)
name: Reusable CI
on:
  workflow_call:             # ← זה מה שהופך אותו ל-reusable
    inputs:                  # פרמטרים שה-caller מעביר
      node-version:
        description: 'Node.js version to use'
        required: true
        type: string
        default: '22'
    secrets:                 # secrets שה-caller מעביר
      NPM_TOKEN:
        required: false

jobs:
  lint-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npm test

ה-caller משתמש ב-uses ברמת ה-job (לא ברמת ה-step):

# .github/workflows/ci.yml (בריפו שקורא)
name: CI
on: [push, pull_request]

jobs:
  call-ci:
    uses: my-org/shared-workflows/.github/workflows/reusable-ci.yml@main
    with:
      node-version: '22'
    secrets: inherit    # מעביר את כל ה-secrets

שימו לב:

Composite Actions — קטע steps לשיתוף

Composite action הוא action שמורכב מכמה steps. מתאים כשרוצים לשתף קטע מתוך workflow, לא workflow שלם:

# .github/actions/setup-and-test/action.yml
name: Setup and Test
description: 'Install deps and run tests'
inputs:
  node-version:
    description: 'Node.js version'
    required: false
    default: '22'
runs:
  using: composite
  steps:
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ inputs.node-version }}
        cache: 'npm'
    - run: npm ci
      shell: bash          # ← חובה ב-composite action
    - run: npm test
      shell: bash

שימוש ב-composite action מתוך workflow:

steps:
  - uses: actions/checkout@v4
  - uses: ./.github/actions/setup-and-test   # path מקומי
    with:
      node-version: '22'

דוגמה מציאותית: ארגון עם 50 ריפוזיטורים

נניח שיש לכם ארגון עם 50 ריפוזיטורים של Node.js. בלי reusable workflows, כל ריפוזיטורי מכיל עותק של אותו CI workflow. כשצריך לשנות משהו (למשל לעדכן Node version) — צריך לעדכן 50 קבצים. עם reusable workflow:

  1. ריפוזיטורי my-org/shared-workflows מכיל את ה-reusable workflow
  2. כל 50 הריפוזיטורים מצביעים אליו עם uses: my-org/shared-workflows/...
  3. שינוי ב-shared-workflows מתפשט אוטומטית לכולם

זה חוסך עשרות שעות תחזוקה בשנה ומבטיח consistency בכל הארגון. כל ריפוזיטורי חדש שנוצר מקבל CI שעובד מהיום הראשון — פשוט מעתיקים 5 שורות של caller workflow.

Versioning של reusable workflows

כמו actions, גם reusable workflows צריכים versioning. השיטה המומלצת:

ככה callers מקבלים bugfixes אוטומטית בלי לשנות קוד.

Outputs — להחזיר ערכים מ-reusable workflow

Reusable workflow יכול גם להחזיר ערכים ל-caller:

# ב-reusable workflow
on:
  workflow_call:
    outputs:
      version:
        description: 'The built version'
        value: ${{ jobs.build.outputs.version }}

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.version.outputs.version }}
    steps:
      - id: version
        run: echo "version=1.2.3" >> $GITHUB_OUTPUT

ב-caller:

jobs:
  ci:
    uses: ./.github/workflows/reusable-ci.yml
  notify:
    needs: ci
    runs-on: ubuntu-latest
    steps:
      - run: echo "Built version: ${{ needs.ci.outputs.version }}"
מסגרת החלטה: Reusable Workflow vs Composite Action vs Shared Script
קריטריוןReusable WorkflowComposite ActionShared Script
מתאים ל-pipeline שלם (CI/CD)קטע steps חוזרלוגיקה פשוטה (bash/python)
runnerמגדיר runner משלורץ על runner של ה-callerרץ על runner של ה-caller
secretsמקבל secrets כ-inputיורש secrets אוטומטיתיורש secrets אוטומטית
קינוןעד 4 רמותללא הגבלהללא הגבלה
שיתוף בין ריפוזיטוריםכן — uses: org/repo/path@refכן — עם ריפו נפרדכן — עם git submodule
דוגמה טיפוסיתCI pipeline לכל הריפוזיטוריםsetup + install + test משותףסקריפט deploy

הכלל: התחילו עם composite action לשיתוף steps. עברו ל-reusable workflow כשצריך pipeline שלם עם כמה jobs.

תרגיל 2: יצירת Reusable Workflow

מטרה: ליצור reusable workflow שניתן לקרוא לו מ-workflow אחר.

שלב 1: צרו קובץ .github/workflows/reusable-ci.yml עם התוכן הבא:

name: Reusable CI
on:
  workflow_call:
    inputs:
      node-version:
        description: 'Node.js version'
        required: true
        type: string

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npm test

שלב 2: צרו (או עדכנו) קובץ .github/workflows/ci.yml שקורא ל-reusable:

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

jobs:
  call-reusable-ci:
    uses: ./.github/workflows/reusable-ci.yml
    with:
      node-version: '22'

שלב 3: דחפו ובדקו בטאב Actions — תראו שה-job call-reusable-ci מפעיל את ה-reusable workflow.

שימו לב לתחביר:

  • ב-reusable: ה-trigger חייב להיות workflow_call (לא push או pull_request)
  • ב-caller: uses ברמת job, לא ברמת step
  • with מעביר את ה-inputs שהגדרתם ב-reusable

1.10 עלויות וייעול — כל שקל חשוב

GitHub Actions הוא חינמי — עד גבול מסוים. ריפוזיטורים ציבוריים מקבלים דקות ללא הגבלה. ריפוזיטורים פרטיים מקבלים free tier, ומעבר לזה — משלמים:

Free Tier — כמה דקות מגיע לכם

תוכניתדקות חינמיות (Linux)מכפיל Windowsמכפיל macOS
Free2,000x2x10
Pro / Team3,000x2x10
Enterprise50,000x2x10

מה זה "מכפיל"? דקת Windows נספרת כ-2 דקות Linux מה-free tier. דקת macOS = 10 דקות Linux. אז 2,000 דקות Linux = רק 200 דקות macOS.

מה קורה מעבר ל-free tier

עלויות חריגה (overage) לריפוזיטורים פרטיים:

נשמע זול, אבל בואו נחשב: 10 workflows ביום x 5 דקות x 30 יום = 1,500 דקות בחודש. זה כבר 75% מה-free tier. עם צוות של 5 מפתחים שכל אחד דוחף 3-4 פעמים ביום — אתם חורגים מהר.

דוגמת חישוב: סטארטאפ קטן

נניח צוות של 5 מפתחים, 3 ריפוזיטורים פרטיים:

חישוב: 5 מפתחים x 4 pushes x 3 דקות x 22 ימים = 1,320 דקות. עם תוכנית Free (2,000 דקות) — בסדר, אבל בלי הרבה מרווח. הוסיפו matrix build עם Windows ופתאום אתם ב-2,640 דקות equivalent.

אזהרה: Concurrency — השורה שחוסכת לכם כסף

בלי concurrency group, כל push מייצר הרצה חדשה. 10 pushes מהירים (למשל fixups ברצף) = 10 הרצות מקביליות שאוכלות minutes. הוסיפו תמיד:

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

זה מבטל הרצות ישנות על אותו branch כשמגיע push חדש. במקום 10 הרצות, רק אחת (האחרונה) רצה. זה לבד יכול לחסוך 30-50% מצריכת הדקות.

Platform fee ל-self-hosted runners

שינוי חשוב ממרץ 2026: גם self-hosted runners נושאים עכשיו platform fee של $0.002 לדקה. זה לא הרבה, אבל שינוי מנטלי — self-hosted כבר לא "חינמי לגמרי". עדיין זול בהרבה מ-hosted runners, אבל חשוב לדעת שזה קיים.

עלויות storage

מעבר לדקות, יש גם עלות storage ל-artifacts ו-cache:

ניהול retention-days ו-cleanup של artifacts ישנים חוסך כאן. הגדירו retention-days: 7 על artifacts שלא צריכים לשמור.

8 טיפים לחיסכון בדקות

  1. Path filters — אל תריצו CI על שינויים ב-README או docs:
    on:
      push:
        paths-ignore:
          - '**.md'
          - 'docs/**'
          - '.github/ISSUE_TEMPLATE/**'
  2. Skip CI — הוסיפו [skip ci] או [ci skip] בהודעת commit לדלג על Actions לגמרי. שימושי ל-commits של documentation או formatting.
  3. Caching — כבר למדנו, חוסך 60-80% מזמן התקנה. שורה אחת (cache: 'npm') שחוסכת מאות דקות.
  4. Linux תמיד — אלא אם באמת חייבים Windows/macOS. Linux הוא הזול ביותר בפער ענק (x10 מ-macOS!).
  5. Concurrency groups — מבטלים הרצות מיותרות על אותו branch. חיסכון של 30-50%.
  6. Timeout — הגדירו timeout-minutes כדי למנוע workflows תקועים שאוכלים דקות:
    jobs:
      test:
        runs-on: ubuntu-latest
        timeout-minutes: 10    # מקסימום 10 דקות
  7. Matrix מצומצם ב-PR — כמו שהראינו בסעיף 1.7, הריצו matrix מלא רק על main, לא על כל PR.
  8. Conditional jobs — דלגו על deploy jobs ב-PRs עם if: github.ref == 'refs/heads/main'. חוסך את זמן ה-setup של jobs מיותרים.

Concurrency Groups לעומק

Concurrency groups הם אחד הכלים החזקים ביותר לחיסכון. בואו נבין בדיוק איך הם עובדים:

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

מה קורה כאן:

בלי cancel-in-progress, ההרצה החדשה ממתינה לישנה (queued). עם cancel-in-progress, הישנה מבוטלת והחדשה רצה מיד. לרוב CI, cancel-in-progress הוא מה שרוצים.

זהירות עם deploy: עבור deploy jobs, כנראה לא רוצים cancel-in-progress. deploy חצי-גמור יכול לשבור את הפרודקשן. במקום זה, תנו ל-deploys להמתין בתור:

concurrency:
  group: deploy-production
  cancel-in-progress: false    # ← המתן, אל תבטל
מסגרת החלטה: אסטרטגיית חיסכון בדקות
אסטרטגיהחיסכון משוערקושי יישוםמתי להשתמש
Concurrency groups30-50%קל — 3 שורותתמיד, בכל workflow
Caching60-80% מזמן installקל — שורה אחתתמיד כשיש npm/pip install
Path filters10-30%קל — 3 שורותכשיש docs/readme שמשתנים הרבה
Matrix מצומצם ב-PR50-80% מ-matrixבינוני — expressionכש-matrix גדול (3+ שילובים)
Linux-only50-90%אפסכשלא חייבים Win/macOS
Timeoutמונע בזבוז קיצוניקל — שורה אחתתמיד, כרשת ביטחון

סדר עדיפויות: התחילו מ-concurrency + caching (הכי קל, הכי משפיע), והוסיפו את השאר בהדרגה.

אזהרה: Caching שלא מוגדר = כסף שנזרק

כל workflow שמריץ npm install או pip install בלי caching מבזבז 30-60 שניות על כל הרצה. על פני חודש, זה מאות דקות מיותרות. הוספת cache: 'npm' ל-setup-node לוקחת שורה אחת ויכולה לחסוך מאות דקות בחודש.

תרגיל 3: חישוב עלות Actions

חשבו את העלות החודשית של Actions עבור הפרויקט שלכם. מלאו את הטבלה:

  1. כמה workflows יש לכם? ____
  2. כמה פעמים כל אחד רץ ביום (בממוצע)? ____
  3. כמה דקות כל הרצה (בדקו בטאב Actions)? ____
  4. על איזה runner (Linux/Windows/macOS)? ____
  5. מכפיל OS (Linux=1, Windows=2, macOS=10): ____
  6. סה"כ דקות בחודש: ____ workflows x ____ runs/day x ____ min x 22 days x ____ multiplier = ____
  7. האם חורגים מ-free tier? כן / לא
  8. אם כן, עלות חריגה: (סה"כ - free tier) x $0.006 = $____

טיפ: לכו ל-Settings → Billing → Actions כדי לראות את הצריכה בפועל.

תרגיל 4: ייעול workflow קיים

קחו את ה-CI workflow שבניתם ב-תרגיל 1 והוסיפו את כל אמצעי החיסכון:

  1. הוסיפו concurrency group עם cancel-in-progress: true
  2. הוסיפו paths-ignore לקבצי documentation
  3. ודאו ש-caching מוגדר (cache: 'npm')
  4. הוסיפו timeout-minutes: 10 לכל job

ה-workflow המייעל צריך להיראות ככה בראש הקובץ:

name: CI/CD Pipeline
on:
  push:
    branches: [main]
    paths-ignore:
      - '**.md'
      - 'docs/**'
  pull_request:
    branches: [main]
    paths-ignore:
      - '**.md'
      - 'docs/**'

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
עשו עכשיו

לכו ל-Settings → Billing → Actions ובדקו כמה דקות השתמשתם החודש. אם אין לכם גישה (ריפו אישי), בדקו בטאב Actions כמה זמן כל workflow לוקח. הריצו workflow עם concurrency ובלי — ודאו שהישן מבוטל כשדוחפים שוב.

1.11 סיכום ומבט קדימה

סיכום הפרק
  • GitHub Actions הוא מערכת CI/CD מובנית ב-GitHub — workflows, jobs, steps, actions. אין צורך בשרת חיצוני.
  • Workflows מוגדרים בקבצי YAML בתיקיית .github/workflows/. ארבע שכבות: trigger → jobs → steps → actions.
  • Triggers קובעים מתי workflow רץ — push, PR, schedule, dispatch, release.
  • Runners הם המכונות שמריצות — GitHub-hosted (הכי נוח, 95% מהמקרים) או self-hosted (שליטה מלאה). ריפוזיטורים ציבוריים: 4 vCPU/16GB. פרטיים: 2 vCPU/8GB.
  • Marketplace — מעל 25,000 actions מוכנים. תמיד צמדו ל-SHA בפרודקשן, tag מספיק ללימוד.
  • Secrets ו-Environments — ניהול ערכים רגישים בשלוש רמות (repo/environment/org) עם protection rules.
  • Matrix builds — בדיקה על כל שילוב של OS וגרסאות, במקביל, מהגדרה אחת.
  • Caching — חיסכון של 60-80% בזמן התקנה. cache: 'npm' ב-setup-node מספיק.
  • Artifacts — העברת קבצי build בין jobs עם upload/download-artifact.
  • Reusable workflows — שיתוף pipelines שלמים בין ריפוזיטורים. Composite actions לשיתוף steps.
  • עלויות — 2,000 דקות חינם (Linux, פרטי). Concurrency groups, path filters, caching ו-timeout חוסכים.
אם אתם זוכרים רק דבר אחד

הוסיפו concurrency group לכל workflow. שלוש שורות שחוסכות עשרות אחוזים בדקות, מונעות הרצות כפולות, ומוודאות שרק הגרסה האחרונה של הקוד נבדקת. זה השינוי עם ה-ROI הגבוה ביותר.

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
בדקו את עצמכם
  1. מה ההבדל בין needs ל-if ב-job? (רמז: needs מגדיר סדר, if מגדיר תנאי)
  2. למה עדיף להצמיד action ל-SHA ולא ל-tag? (רמז: חשבו על supply-chain attacks)
  3. מה קורה כש-fail-fast: true (ברירת מחדל) ב-matrix ו-job אחד נכשל? (רמז: מה קורה לשאר ה-jobs?)
  4. מה ההבדל בין repository secret ל-environment secret? (רמז: חשבו על staging vs production)
  5. מתי תבחרו Reusable Workflow ומתי Composite Action? (רמז: pipeline שלם vs קטע steps)
שגרת עבודה מומלצת
  • יומית: בדקו את טאב Actions אחרי כל push — וודאו ש-CI עובר ירוק. אם נכשל — תקנו לפני שממשיכים לעבוד על פיצ'ר חדש. Failing CI שמתעלמים ממנו הופך ל-"שגרה", ואז כשבאמת משהו נשבר — אף אחד לא שם לב.
  • שבועית: בדקו usage ב-Settings → Billing → Actions — האם אתם בתחום ה-free tier? סרקו workflows שנכשלים בקביעות ותקנו את שורש הבעיה (לא רק re-run). בדקו אם יש runs שנתקעים מעבר ל-timeout.
  • חודשית: סקרו את כל ה-workflows — האם יש מיותרים שאפשר למחוק? האם caching עדכני והמפתחות נכונים? האם actions צריכים עדכון גרסה (במיוחד security patches)? האם יש workflows ללא concurrency group? האם SHAs של actions עדכניים? ודאו ש-retention-days על artifacts לא גבוה מדי.

טיפ לצוותים: הגדירו branch protection rule שדורש CI ירוק לפני merge. זה מבטיח שאף אחד לא עוקף את הבדיקות. ב-Settings → Branches → Branch protection rules → Require status checks to pass.

צ'קליסט — מה עשינו בפרק הזה
  • ☐ הבנתי את מבנה workflow: trigger → jobs → steps → actions
  • ☐ מכיר/ה את הסוגים של triggers: push, PR, schedule, dispatch, release
  • ☐ יצרתי workflow ראשון (Hello World) שרץ בהצלחה
  • ☐ הרצתי workflow ידנית עם workflow_dispatch
  • ☐ בניתי CI/CD pipeline עם lint, test, build, deploy
  • ☐ הבנתי את ההבדל בין runners: GitHub-hosted vs self-hosted vs larger
  • ☐ חיפשתי ומצאתי actions ב-marketplace
  • ☐ הגדרתי secret ב-repository
  • ☐ יצרתי environment עם protection rules
  • ☐ הפעלתי matrix build על כמה OS/versions
  • ☐ הוספתי caching ומדדתי את ההבדל בזמן
  • ☐ יצרתי reusable workflow וקראתי לו מ-workflow אחר
  • ☐ הוספתי concurrency group
  • ☐ הוספתי path filters ו-timeout
  • ☐ חישבתי את עלות ה-Actions שלי

טעויות נפוצות — סיכום

לפני שממשיכים, הנה ארבע הטעויות הנפוצות ביותר שראינו בפרק — וודאו שאתם לא עושים אותן:

הטעותלמה מפתההפתרון
לא להגדיר concurrency groupברירת מחדל — לא צריך לעשות כלום3 שורות concurrency בראש כל workflow
לשמור secrets ב-YAML / .envיותר מהיר ונוחSettings → Secrets. תמיד.
להתעלם מ-caching"עובד בלי, אז למה להוסיף"cache: 'npm' — שורה אחת, 60-80% חיסכון
להצמד ל-tag במקום SHAv4 יותר קריא מ-SHA ארוךSHA + הערת version: @abc123 # v4.1.7

Debugging workflows — כשמשהו נשבר

כמה כלים ל-debugging שכדאי להכיר:

מבט קדימה

בפרק הבא: נלמד לנהל את כל מה שבנינו — ועוד הרבה יותר — מהטרמינל, עם GitHub CLI (gh). תגלו שהטאב Actions הוא רק חלק מהסיפור: אפשר להריץ workflows, לבדוק סטטוס, להוריד logs, ולנהל PRs — הכל מהטרמינל, בלי לפתוח דפדפן.

מה שמגיע בהמשך הקורס:

אל תמתינו לפרק הבא: אם יש לכם פרויקט אמיתי, קחו את ה-CI/CD pipeline שבניתם וודאו שהוא רץ בהצלחה. זה הבסיס שעליו נבנה בכל שאר הקורס.