6 שלב בניית יכולות

GitHub Packages — הרגיסטרי הפרטי שלך

בפרק הזה תלמדו לפרסם npm packages ו-Docker images ישירות מ-GitHub — בלי לצאת מהפלטפורמה. תגדירו הרשאות גישה, תבנו workflow שמפרסם אוטומטית על כל release, ותבינו בדיוק מה חינמי ומה עולה כסף. בסוף הפרק תהיה לכם שליטה מלאה ברגיסטרי של הארגון — ותדעו לבנות pipeline שלם מ-commit ועד package מפורסם.

אם תיקחו רק דבר אחד מהפרק הזה

צרו Actions workflow שמפרסם Docker image ל-ghcr.io אוטומטית על כל release. זה חינמי, לוקח 10 דקות להגדרה, ומשנה את כל ה-deployment pipeline שלכם. כל release עתידי יפרסם image בלי שתיגעו בטרמינל.

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

איפה היינו: בפרק 5 (Projects) הקמתם מערכת ניהול פרויקטים מלאה עם לוחות, תצוגות, ואוטומציות. הפרויקט שלכם מנוהל — עכשיו צריך לספק את התוצרים.

מה עושים עכשיו: בפרק הזה אתם מוסיפים את שלב הפרסום ל-pipeline. במקום רק לבנות ולבדוק, עכשיו ה-pipeline גם מפרסם: npm package לצוות, או Docker image ל-production. כל release שאתם מסמנים ב-Projects — עכשיו מפרסם package אוטומטית.

לאן ממשיכים: בפרק 7 (Security) תלמדו להגן על ה-packages האלה מפגיעויות — Dependabot, CodeQL, secret scanning. כי package מפורסם צריך להיות גם בטוח.

מילון מונחים
Registry
שרת שמאחסן ומגיש packages — כמו npm registry או Docker Hub, אבל של GitHub. כל registry תומך בפרוטוקול ספציפי (npm, Docker, Maven, וכו')
GitHub Packages
שירות רגיסטרי אוניברסלי משולב ב-GitHub — תומך ב-6 סוגי packages. הכל מנוהל ליד הקוד, ההרשאות, וה-Actions
Container Registry (GHCR)
רגיסטרי Docker/OCI images של GitHub בכתובת ghcr.io — כרגע חינמי לחלוטין, גם storage וגם bandwidth
Scope (@owner)
תחילית שמזהה בעלות על package ב-npm — למשל @myorg/my-package. חובה ב-GitHub Packages, חייב להתאים לשם המשתמש או הארגון
GITHUB_TOKEN
טוקן אוטומטי ב-Actions workflows — נוצר ומושמד בכל הרצה, מאפשר פרסום packages בלי PAT ידני. הדרך המועדפת לאימות
PAT (Personal Access Token)
טוקן ידני לאימות — נדרש לעבודה מחוץ ל-Actions (מהטרמינל המקומי). צריך scopes מתאימים ו-rotation תקופתי
Granular Permissions
הרשאות גישה עצמאיות מהריפוזיטורי — נתמך ב-Container, npm, NuGet, RubyGems. מאפשר לתת גישה ל-package בלי גישה לקוד
Visibility
האם package ציבורי (חינמי, כולם יכולים למשוך) או פרטי (מוגבל, צורך storage). אפשר לשנות בכל עת דרך Package Settings
publishConfig
שדה ב-package.json שמגדיר לאיזה registry npm publish שולח — קריטי כדי לא לפרסם בטעות ל-npmjs.com
OCI Image
תמונת container בפורמט Open Container Initiative — סטנדרט פתוח שנתמך ב-GHCR, Docker Hub, ו-cloud registries
Artifact Attestation
הוכחת מקור — מסמך דיגיטלי (חתום) שמאמת איפה ואיך package נבנה. חלק משרשרת האספקה הבטוחה (supply chain security)
.npmrc
קובץ הגדרות של npm — מגדיר registry URLs ו-authentication tokens. יכול להיות ברמת פרויקט (בשורש) או ברמת משתמש (~/.npmrc)

6.1 מה זה GitHub Packages ולמה צריך את זה

GitHub Packages הוא רגיסטרי אוניברסלי למשלוח קוד — משולב ישירות ב-GitHub, ליד הקוד, ה-Issues, ה-PRs, וה-Actions שלכם. במקום לנהל חשבונות נפרדים ב-npmjs.com, Docker Hub, ו-NuGet Gallery, הכל נמצא במקום אחד עם מערכת הרשאות אחידה.

בואו נבין מה הבעיה שזה פותר. נניח שיש לכם ארגון עם 5 צוותים. כל צוות מפתח microservices. כל microservice צריך ספריות משותפות (utils, auth, logging) ו-Docker images ל-deployment. בלי GitHub Packages, הנה מה שקורה:

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

  1. אינטגרציה מלאה עם GitHub. ה-package חי באותו ריפוזיטורי שבו הקוד. הרשאות, Actions workflows, ו-visibility — הכל מנוהל ממקום אחד. פרסום package הוא step ב-workflow, לא מערכת נפרדת. כשמישהו לוחץ על ה-package ב-GitHub, הוא רואה את הריפוזיטורי, ה-README, וה-versions — הכל מחובר.
  2. packages פרטיים בלי כאב ובלי עלות נוספת. ב-npmjs.com, packages פרטיים עולים $7/חודש למשתמש. ב-GitHub Packages, הם כלולים בתוכנית שלכם — 500MB חינם בתוכנית Free, 2GB ב-Pro. לרוב הצוותים הקטנים, זה מספיק.
  3. Container registry חינמי. נכון לאפריל 2026, ghcr.io — רגיסטרי ה-Docker של GitHub — חינמי לחלוטין, גם storage וגם bandwidth, גם ל-private images. בהשוואה, Docker Hub מגביל הורדות, דורש תשלום על repositories פרטיים, ואפילו מוחק images לא פעילים אחרי 6 חודשים בתוכנית חינמית.

GitHub Packages תומך ב-6 סוגי registries: npm, Container (Docker/OCI), Maven, Gradle, NuGet, ו-RubyGems. לא משנה באיזו שפה אתם עובדים — יש registry בשבילכם. ואם הפרויקט שלכם משלב כמה שפות (למשל frontend ב-TypeScript ו-backend ב-Java), כל ה-packages מנוהלים ממקום אחד.

השוואה מהירה: GitHub Packages מול החלופות

קריטריוןGitHub Packagesnpmjs.comDocker Hub
packages פרטייםכלול בתוכנית (500MB+)$7/חודש/משתמש$5/חודש (1 private repo)
אינטגרציה עם CInative עם Actionsצריך NPM_TOKEN secretצריך Docker credentials
הרשאותGitHub teams/rolesnpm teams (בתשלום)Docker Hub organizations
קישור לקודאוטומטי — באותו ריפוידני — לינק בלבדידני — לינק בלבד
discoverabilityנמוכה — אנשים לא מחפשים שםגבוהה — הריג'סטרי הראשיגבוהה — הריג'סטרי הראשי
container storageחינמי לחלוטיןלא רלוונטימוגבל, תשלום על extra

מתי GitHub Packages הוא לא הבחירה הנכונה? כשאתם מפרסמים package ציבורי לקהילה. אם אתם רוצים שמפתחים ימצאו את הספרייה שלכם בחיפוש, npmjs.com או Docker Hub הם עדיין המקום. ה-discoverability של GitHub Packages נמוכה — אנשים לא מחפשים packages שם. עוד מקרה: אם הצוות כבר משתמש בפתרון קיים (כמו Artifactory או Nexus) ואין סיבה להחליף.

לסיכום: GitHub Packages הוא הבחירה הנכונה עבור (1) packages פנימיים/ארגוניים, (2) Docker images (כי חינמי), ו-(3) כל מצב שבו אתם רוצים pipeline אחוד מ-commit ועד published package. לכל דבר ציבורי שמיועד לקהילה — השתמשו ב-registry הציבורי של השפה.

איך הפרק הזה בנוי: נתחיל בסקירת 6 ה-registries (סעיף 6.2), ואז נצלול לשני התרגילים המרכזיים — פרסום npm package (6.3) ופרסום Docker image (6.4). אחרי שנבין את הבסיס, נכסה אימות (6.5), הרשאות (6.6), ופרסום אוטומטי עם Actions (6.7). נסיים עם צריכת packages (6.8), עלויות (6.9), וניקוי גרסאות (6.10). כל סעיף בנוי על הקודם — אבל אם אתם ממוקדים רק ב-Docker, אפשר לדלג ישירות מ-6.2 ל-6.4.

GitHub Packages — האקוסיסטם GitHub Repository קוד + Issues + PRs + Actions npm Registry Container (ghcr.io) Maven / Gradle NuGet Registry RubyGems Actions Workflow פרסום אוטומטי רגיסטרי אוניברסלי — 6 סוגי packages, פלטפורמה אחת, הרשאות אחידות חינמי!
עשו עכשיו

כנסו לריפוזיטורי שלכם ב-GitHub. בצד ימין של העמוד (או בטאב) חפשו את Packages. האם יש packages מפורסמים? רוב המפתחים מעולם לא לחצו שם — היום זה משתנה. אם אין packages, זה מצוין — בסוף הפרק יהיו שם לפחות שניים.

6.2 שישה רגיסטרים, פלטפורמה אחת

GitHub Packages תומך בשישה סוגי registries. לכל אחד יש URL, כלי client, ומודל הרשאות משלו. לפני שמתחילים לפרסם, חשוב להבין את ההבדלים — כי הם משפיעים על איך אתם מגדירים אימות, הרשאות, וגישה.

Registryשפה / פלטפורמהURLClientהרשאות
npmJavaScript / TypeScriptnpm.pkg.github.comnpm / yarn / pnpmGranular
ContainerDocker / OCIghcr.ioDocker / PodmanGranular
MavenJava / Kotlinmaven.pkg.github.commvnRepo-scoped
GradleJava / Kotlinmaven.pkg.github.comgradleRepo-scoped
NuGet.NET / C#nuget.pkg.github.comdotnet CLIGranular
RubyGemsRubyrubygems.pkg.github.comgem / bundlerGranular

הבחנה חשובה: Granular vs Repo-scoped. ארבעה registries (Container, npm, NuGet, RubyGems) תומכים ב-granular permissions — הרשאות שאפשר להגדיר בנפרד מהריפוזיטורי. זה אומר שאפשר לתת למישהו גישת read ל-npm package בלי שיוכל לראות את קוד המקור. Maven ו-Gradle הם repo-scoped — ההרשאות יורשות את הרשאות הריפו. הבדל קריטי כשעובדים עם packages שמשרתים כמה ריפוזיטורים.

למה זה חשוב בפועל? נניח שיש לכם ספריית utils שבנויה בריפו A, אבל 10 ריפוזיטורים אחרים צריכים להשתמש בה. עם granular permissions (npm), אפשר לפרסם את ה-package כ-public ולאפשר לכולם למשוך — בלי לפתוח את הקוד. עם repo-scoped (Maven), הגישה ל-package תלויה בגישה לריפו A — כל מי שצריך את ה-JAR חייב גישת read לריפו.

עוד הבדל חשוב: Maven ו-Gradle משתמשים באותו URL (maven.pkg.github.com) כי שניהם עובדים עם Maven repositories. אלה לא שני registries נפרדים — אלה שני clients לאותו registry.

naming conventions: כל registry יש לו כללי שמות:

חשוב לדעת את הכללים לפני שמפרסמים — שינוי שם אחרי פרסום פירושו יצירת package חדש (השם הישן נשאר תפוס 30 ימים אחרי מחיקה).

מגבלות שכדאי לדעת:

מסגרת החלטה: איזה Registry להשתמש
מצבRegistryלמה
ספרייה פנימית ב-Node.jsnpmScoped package (@org/lib), npm install רגיל, granular permissions — הדרך הטבעית
microservice או appContainer (ghcr.io)Docker image, deploy ל-k8s/ECS/Cloud Run, חינמי לחלוטין — ברירת מחדל מומלצת
ספריית Java / KotlinMaven / Gradleפרסום JAR/AAR, repo-scoped — הרשאות לפי ריפו, מוכר למפתחי Java
NuGet packageNuGet.NET ecosystem, granular permissions, dotnet CLI integration
gem פנימיRubyGemsBundler integration, granular permissions, Gemfile מוכר
package ציבורי לקהילהnpmjs.com / Docker Hubdiscoverability — אנשים מחפשים שם, לא ב-GitHub Packages
לא בטוחיםContainer (ghcr.io)חינמי, פשוט, עובד עם כל שפה (כל דבר יכול להיות container)
עשו עכשיו

חשבו על הפרויקט הראשי שלכם. איזה registry מתאים לו? רשמו את התשובה: שם הפרויקט → Registry → URL. אם אתם עובדים עם Node.js — כנראה npm. אם יש Dockerfile — Container. אם לא בטוחים — Container הוא ברירת מחדל טובה כי הוא חינמי.

6.3 פרסום npm Package — צעד אחר צעד

נתחיל עם npm — הדרך המהירה ביותר לפרסם package ב-GitHub. התהליך דומה לפרסום ב-npmjs.com, עם שלושה הבדלים קריטיים: חובה scope, צריך .npmrc עם ה-registry URL של GitHub, וצריך publishConfig ב-package.json. בואו נעבור על כל שלב.

שלב 1: הגדרת package.json

ה-package חייב להיות scoped — עם תחילית @owner/ שמתאימה לשם המשתמש או הארגון ב-GitHub:

{
  "name": "@your-username/my-utils",
  "version": "1.0.0",
  "description": "ספריית עזר פנימית",
  "main": "index.js",
  "publishConfig": {
    "registry": "https://npm.pkg.github.com"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/your-username/my-utils.git"
  }
}

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

שלב 2: הגדרת .npmrc

צרו קובץ .npmrc בשורש הפרויקט. הקובץ הזה מגדיר ל-npm לאיפה לפנות כשהוא רואה packages עם ה-scope שלכם:

# .npmrc
@your-username:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}

שורה ראשונה: מכוונת packages עם scope @your-username ל-GitHub registry. כל npm install @your-username/something יפנה ל-GitHub, ולא ל-npmjs.com. packages ללא scope (כמו lodash או express) ימשיכו להגיע מ-npmjs.com כרגיל.

שורה שנייה: מגדירה את טוקן האימות. ${NPM_TOKEN} הוא environment variable — לא כותבים את הטוקן עצמו בקובץ! בתוך Actions, זה יהיה GITHUB_TOKEN. בטרמינל המקומי, תגדירו environment variable או ~/.npmrc עם PAT.

טיפ חשוב: אם יש לכם .npmrc גם ברמת פרויקט וגם ברמת משתמש (~/.npmrc), npm ממזג אותם — הגדרות ברמת פרויקט גוברות. זה מאפשר לשים את הגדרת ה-registry בפרויקט (ולעשות commit), ואת הטוקן ב-~/.npmrc (לא ב-commit).

שלב 3: אימות ופרסום

# אימות עם PAT (פעם אחת, מהטרמינל)
npm login --scope=@your-username --auth-type=legacy \
  --registry=https://npm.pkg.github.com

# פרסום
npm publish

# פרסום ציבורי (אם רוצים שכולם יוכלו להתקין)
npm publish --access public

ה-flag --access public חשוב: scoped packages הם private כברירת מחדל ב-npm. אם אתם רוצים package ציבורי (שלא דורש אימות להתקנה), צריך לציין במפורש. Package ציבורי גם לא צורך storage מה-free tier — כך שזה חוסך גם כסף.

עדכון גרסה: כשאתם רוצים לפרסם גרסה חדשה, שנו את version ב-package.json ותריצו npm publish שוב. npm לא מאפשר לדרוס גרסה קיימת — חייבים להעלות מספר. שימו לב: אם מחקתם גרסה, אי אפשר לפרסם מחדש עם אותו מספר (מגבלת npm, לא של GitHub).

Troubleshooting — בעיות נפוצות:

מה קורה אחרי publish מוצלח? npm מדפיס את שם ה-package, הגרסה, ו-SHA. בנוסף:

אזהרה

npm ב-GitHub Packages חייב scope. בלי @owner/ בשם ה-package, הפקודה npm publish תשלח את ה-package ל-npmjs.com — הרגיסטרי הציבורי! ודאו ששם ה-package מתחיל ב-@ ושיש publishConfig ב-package.json. טעות כזו חושפת קוד פנימי לכל העולם.

עשו עכשיו

פתחו את package.json בפרויקט שלכם. האם יש scope? אם לא, שנו את name ל-@your-username/project-name (אותיות קטנות בלבד). הוסיפו publishConfig עם ה-registry URL של GitHub. הוסיפו repository עם ה-URL של הריפוזיטורי.

תרגיל 1: פרסום npm package ראשון

פרסמו npm package ל-GitHub Packages. עקבו אחרי כל הצעדים:

  1. צרו ריפוזיטורי חדש ב-GitHub (או השתמשו בקיים) עם קובץ index.js פשוט:
    // index.js
    module.exports = {
      greet: (name) => `Hello, ${name}! Welcome to GitHub Packages.`,
      add: (a, b) => a + b,
      version: '1.0.0'
    };
  2. צרו package.json עם scope ו-publishConfig (כמו למעלה). ודאו ש-name מתחיל ב-@your-username/
  3. צרו .npmrc בשורש הפרויקט עם הגדרת registry
  4. צרו PAT (classic) עם scope write:packages ו-read:packages
  5. הגדירו את ה-PAT כ-NPM_TOKEN environment variable: export NPM_TOKEN=ghp_xxxxx
  6. הריצו npm publish (או npm publish --access public לציבורי)
  7. כנסו ל-GitHub ובדקו שה-package מופיע בטאב Packages בריפוזיטורי

הצלחה: אתם רואים את ה-package ב-GitHub עם גרסה 1.0.0, הוראות התקנה, לינק לריפוזיטורי, ואפשרות לנהל versions.

עשו עכשיו

אחרי שה-package מפורסם — לחצו עליו ב-GitHub. בדקו את הדף: האם יש הוראות התקנה? האם הריפוזיטורי מקושר? אם הריפו לא מקושר — ודאו שיש repository ב-package.json ופרסמו גרסה חדשה (שנו version ל-1.0.1).

6.4 Container Registry (ghcr.io) — Docker Images ב-GitHub

ה-Container registry של GitHub (ghcr.io) הוא חינמי לחלוטין — storage ו-bandwidth — ללא הגבלה, גם ל-private images. זה הופך אותו לבחירה מצוינת לכל Docker image. בהשוואה, Docker Hub מגביל pull rate (100 pulls/6 שעות למשתמשים אנונימיים), מוגבל ל-repository פרטי אחד בחינם, ומוחק images לא פעילים. ב-ghcr.io אין מגבלות כאלה.

שלב 1: יצירת Dockerfile

ניצור אפליקציה פשוטה לדוגמה. שימו לב ל-LABELs — הם קריטיים:

# Dockerfile
FROM node:22-alpine
WORKDIR /app
COPY package.json index.js ./
RUN npm install --production
EXPOSE 3000
CMD ["node", "index.js"]

# Labels חשובים — מקשרים את ה-image לריפוזיטורי
LABEL org.opencontainers.image.source="https://github.com/your-username/my-app"
LABEL org.opencontainers.image.description="My app container image"
LABEL org.opencontainers.image.licenses="MIT"

שלושת ה-LABELs חשובים מאוד — הנה למה:

שלב 2: בנייה ותיוג

# בנייה עם tag מלא כולל registry
docker build -t ghcr.io/your-username/my-app:1.0.0 .

# אפשר גם תגית latest (מומלץ לשמור את שתיהן)
docker tag ghcr.io/your-username/my-app:1.0.0 \
           ghcr.io/your-username/my-app:latest

המבנה תמיד: ghcr.io/NAMESPACE/IMAGE:TAG. ה-namespace הוא שם המשתמש או הארגון — אותיות קטנות. שם ה-image יכול להכיל קווים נוטים (slashes) לתתי-paths, למשל ghcr.io/myorg/backend/api-server:v2.

שלב 3: אימות ודחיפה

# אימות ל-ghcr.io (צריך PAT עם write:packages)
echo $CR_PAT | docker login ghcr.io -u your-username --password-stdin

# דחיפה — שולח את כל ה-layers
docker push ghcr.io/your-username/my-app:1.0.0
docker push ghcr.io/your-username/my-app:latest

מה קורה אחרי push מוצלח?

Visibility — public מול private:

כברירת מחדל, images חדשים הם private. כדי לשנות ל-public:

  1. כנסו לעמוד ה-image ב-GitHub (Your Profile → Packages → שם ה-image)
  2. לחצו על Package settings (בצד ימין)
  3. גללו ל-Danger ZoneChange package visibility
  4. בחרו Public והקלידו את שם ה-package לאישור

image ציבורי: כל אחד יכול לעשות docker pull בלי login. image פרטי: רק מי שיש לו PAT עם read:packages או GITHUB_TOKEN בתוך Actions.

Multi-platform builds: אם אתם צריכים images שעובדים גם על ARM (כמו Apple Silicon M-series או AWS Graviton instances), אפשר להשתמש ב-docker buildx:

# בנייה ל-amd64 ו-arm64 ביחד
docker buildx build --platform linux/amd64,linux/arm64 \
  -t ghcr.io/your-username/my-app:1.0.0 \
  --push .

ב-Actions, docker/build-push-action תומך ב-multi-platform ישירות:

      - uses: docker/build-push-action@...
        with:
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ steps.meta.outputs.tags }}

זה יוצר manifest list — Docker יודע למשוך אוטומטית את ה-layer המתאים לפלטפורמה. מי שעל Mac M3 יקבל arm64, מי שעל Linux server יקבל amd64. הכל שקוף למשתמש.

מחזור חיים של Docker Image Dockerfile קוד + הגדרות docker build בנייה מקומית docker tag ghcr.io/user/app docker push העלאה ל-ghcr.io ghcr.io Container Registry חינמי! docker pull מכל מכונה
עשו עכשיו

צרו Dockerfile פשוט. אפילו שתי שורות מספיקות: FROM nginx:alpine + COPY index.html /usr/share/nginx/html/. צרו קובץ index.html עם תוכן כלשהו ובנו image מקומי: docker build -t test-app .. ודאו שה-build מצליח לפני שממשיכים ל-push.

תרגיל 2: פרסום Docker Image ל-ghcr.io

פרסמו Docker image ל-Container Registry. עקבו בזהירות:

  1. צרו PAT (classic) עם scopes: write:packages, read:packages, ו-delete:packages
  2. שמרו את ה-PAT ב-environment variable: export CR_PAT=ghp_xxxxx
  3. התחברו ל-ghcr.io: echo $CR_PAT | docker login ghcr.io -u YOUR_USER --password-stdin
  4. ודאו שה-Dockerfile כולל LABEL org.opencontainers.image.source עם URL הריפו
  5. בנו עם tag מלא: docker build -t ghcr.io/YOUR_USER/my-app:1.0 .
  6. דחפו: docker push ghcr.io/YOUR_USER/my-app:1.0
  7. כנסו ל-GitHub → Your profile → Packages ובדקו שה-image מופיע
  8. אם ה-image לא מקושר לריפו — בדקו שה-LABEL נכון ודחפו שוב

הצלחה: ה-image נמצא ב-ghcr.io, מקושר לריפוזיטורי, ואפשר לעשות לו pull ממכונה אחרת.

עשו עכשיו

אחרי שה-image ב-ghcr.io — נסו למשוך אותו. פתחו טרמינל חדש (או מכונה אחרת) והריצו: docker pull ghcr.io/YOUR_USER/my-app:1.0. אם ה-image פרטי, תצטרכו להתחבר קודם. אם ציבורי — pull יעבוד בלי אימות. שנו את ה-visibility ל-public אם זה image לימודי.

6.5 אימות — GITHUB_TOKEN מול PAT

יש שתי דרכים לאימות מול GitHub Packages, וכל אחת מתאימה למצב שונה. הבחירה הנכונה ביניהן היא ההבדל בין pipeline בטוח ופשוט לבין סיבוכים מיותרים.

GITHUB_TOKEN — השיטה המועדפת בתוך Actions workflows

כל workflow ב-GitHub Actions מקבל אוטומטית GITHUB_TOKEN — טוקן זמני שמאפשר:

היתרונות ברורים: אין צורך ליצור secret, אין צורך לנהל rotation, אין סיכון שהטוקן ידלוף. הוא נוצר אוטומטית בתחילת כל הרצה ומושמד בסוף. ה-scope שלו מוגבל לריפוזיטורי הנוכחי — אי אפשר להשתמש בו כדי לגשת לריפוזיטורי אחר.

חשוב: כדי ש-GITHUB_TOKEN יוכל לפרסם packages, צריך להגדיר permissions ב-workflow:

permissions:
  contents: read      # לקרוא את הקוד
  packages: write     # לפרסם packages

בלי packages: write, הפרסום ייכשל עם שגיאת 403 Forbidden. זו טעות נפוצה — אנשים שוכחים את ה-permissions block כי ב-Actions הדיפולט הוא read-only.

PAT (Personal Access Token) — לעבודה מחוץ ל-Actions

כשאתם עובדים מהטרמינל המקומי — npm publish, docker push, docker pull של images פרטיים — צריך PAT. יש שני סוגים:

PAT (classic): הגישה הוותיקה. מגדירים scopes:

Fine-grained PAT: הגישה החדשה והמומלצת. מאפשר להגביל את הטוקן לריפוזיטוריים ספציפיים ולהגדיר הרשאות ברמת resource. למשל: "טוקן שיכול לפרסם packages רק בריפוזיטורי X, ולא בשום ריפו אחר". גם תומך ב-expiration dates — מה שמאפשר rotation אוטומטי. אם אתם יוצרים PAT חדש היום — השתמשו ב-fine-grained. PAT classic עדיין עובד ונפוץ בדוגמאות ישנות, אבל fine-grained בטוח יותר.

הגדרת PAT classic — שלב אחר שלב:

  1. כנסו ל-Settings → Developer settings → Personal access tokens → Tokens (classic)
  2. לחצו Generate new token (classic)
  3. תנו שם תיאורי (למשל "packages-local-dev")
  4. סמנו scopes: write:packages (כולל read אוטומטית)
  5. הגדירו expiration — מומלץ 90 ימים, לא "No expiration"
  6. העתיקו את הטוקן מיד — הוא לא יוצג שוב!
  7. שמרו ב-environment variable: export NPM_TOKEN=ghp_xxxxx (ב-~/.bashrc או ~/.zshrc)

מתי בכל זאת צריך PAT בתוך Actions? רק במקרה אחד: כשה-workflow צריך גישה ל-packages ב-ריפוזיטורי אחר. למשל, ריפו A צריך למשוך Docker image שפורסם מריפו B. GITHUB_TOKEN מוגבל לריפו הנוכחי, אז צריך PAT (או Organization-level token) שיש לו גישה לריפו B.

אזהרה

אם אתם בתוך Actions workflow ומשתמשים ב-PAT — עצרו ושאלו למה. ב-99% מהמקרים, GITHUB_TOKEN עושה את העבודה. PAT ב-Actions הוא נכון רק כשצריכים גישה cross-repo. שימוש ב-PAT כש-GITHUB_TOKEN מספיק יוצר סיכון אבטחה מיותר (הטוקן יכול לגשת ליותר ממה שנדרש) ועבודת תחזוקה (rotation, secrets management, notification על expiry).

Container registry: גישה אנונימית

יוצא מהכלל חשוב: images ציבוריים ב-ghcr.io אפשר למשוך בלי שום אימות. לא צריך PAT, לא צריך docker login. כל docker pull ghcr.io/org/public-image פשוט עובד. זה מאפשר לשלב images ב-CI/CD של פרויקטים חיצוניים בלי לנהל credentials.

עם npm זה אחרת: גם packages ציבוריים ב-GitHub Packages דורשים אימות ל-npm install. צריך לפחות PAT עם read:packages. זה חסרון ידוע של npm registry של GitHub — ואחת הסיבות שלצרכי open source עדיף להישאר ב-npmjs.com.

סיכום אימות — טבלה מהירה:

מצבשיטת אימותהערות
Actions → publishGITHUB_TOKENאוטומטי, צריך permissions: packages: write
Actions → install/pullGITHUB_TOKENאוטומטי, צריך permissions: packages: read
Actions → cross-repoPAT (secret)נדיר. רק כשצריך packages מריפו אחר
Terminal → npm publishPAT (classic/fine-grained)דרך NPM_TOKEN env var או ~/.npmrc
Terminal → docker pushPAT (classic)דרך docker login ghcr.io
Terminal → docker pull (public)לא צריךגישה אנונימית ל-images ציבוריים ב-ghcr.io
Terminal → npm install (public)PAT (read:packages)חסרון — npm ב-GitHub Packages תמיד דורש אימות
עשו עכשיו

בדקו: האם יש לכם PATs ישנים שנוצרו בשביל packages? כנסו ל-Settings → Developer settings → Personal access tokens. אם יש PATs ישנים — בדקו: האם הם fine-grained? מה ה-scopes שלהם? מתי פג תוקפם? מחקו כל PAT שלא בשימוש פעיל.

6.6 הרשאות גישה — מי רואה, מי מושך, מי מפרסם

הרשאות ב-GitHub Packages עובדות בשני מודלים, וההבדל ביניהם משפיע על איך אתם מנהלים גישה בארגון. בחירה נכונה חוסכת כאב ראש — בחירה לא נכונה גורמת לפתיחת גישה רחבה מדי או לחסימת צוותים.

מודל 1: Granular Permissions (Container, npm, NuGet, RubyGems)

ההרשאות של ה-package עצמאיות מהריפוזיטורי. זה אומר שאפשר לתת למישהו read access ל-package בלי שיוכל לראות את קוד המקור. שלוש רמות:

ניתן להוסיף הרשאות ברמת משתמש, צוות (team), או ריפוזיטורי. למשל: "צוות Frontend יכול לקרוא את @org/design-system, רק צוות Design System יכול לפרסם גרסאות, ורק Team Lead יכול למחוק גרסאות."

מודל 2: Repo-scoped (Maven, Gradle)

ה-package יורש את ההרשאות של הריפוזיטורי. מי שיכול לקרוא את הריפו — יכול למשוך packages. מי שיכול לכתוב — יכול לפרסם. אין הפרדה. זה פשוט יותר לנהל, אבל פחות גמיש.

Visibility: Public vs Private

ברמת ארגון: Organization owners יכולים להגדיר מדיניות defaults — למשל, לחסום יצירת public packages, או לדרוש שכל packages חדשים יהיו private. אפשר גם להגדיר default visibility וברירות מחדל להרשאות.

תרחיש מעשי: ספרייה משותפת בארגון

נניח שיש לכם ספריית @myorg/auth-client שמשמשת 15 ריפוזיטורים. איך מגדירים הרשאות?

  1. Package visibility: אם כל הריפוזיטורים באותו ארגון — הפכו את ה-package ל-internal (visible לכל חבר ארגון) או תנו read access לצוותות הרלוונטיים
  2. Write access: רק ל-team שמתחזק את הספרייה. לא לכולם — אחרת כל מפתח יכול לפרסם גרסה שבורה
  3. Admin access: רק ל-team lead או ל-DevOps — מי שיכול למחוק גרסאות ולשנות visibility

חיבור package לריפוזיטורי:

ב-granular permissions, אפשר לחבר (link) package לריפוזיטורי. זה לא רק לצורך תצוגה — חיבור מאפשר ל-Actions workflow של הריפו לגשת ל-package עם GITHUB_TOKEN. בלי חיבור, ה-workflow יצטרך PAT. חיבור אוטומטי קורה כשמפרסמים מ-Actions של הריפו. חיבור ידני: Package Settings → Manage repositories → Add repository.

inheritance — ירושת הרשאות:

אפשר להגדיר ש-package יורש הרשאות מהריפוזיטורי שהוא מחובר אליו. זה נוח — כל מי שיש לו write access לריפו יקבל write access ל-package. אבל אם רוצים הרשאות שונות (למשל: כולם קוראים את הקוד, רק DevOps מפרסמים), צריך לנתק את ה-inheritance ולהגדיר ידנית.

מודל ההרשאות — Granular מול Repo-scoped Granular Permissions Container, npm, NuGet, RubyGems Package הרשאות עצמאיות Repository הרשאות נפרדות | Read → Write → Admin אפשר לתת גישה ל-package בלי גישה לקוד Repo-scoped Maven, Gradle Package = Repository אותן הרשאות, אותו visibility גישה לריפו = גישה ל-package אין הפרדה — מי שרואה את הקוד רואה גם את ה-package
מסגרת החלטה: Public מול Private
קריטריוןPublic PackagePrivate Package
עלותחינמי לחלוטין — storage ו-bandwidthצורך storage מ-free tier (משותף עם Actions)
גישהכולם (containers: אנונימי, npm: צריך PAT)רק מי שהורשה במפורש
שימוש טיפוסיקוד פתוח, ספריות ציבוריות, tools משותפיםpackages פנימיים, ספריות ארגוניות, קוד רגיש
discoveryנמצא בחיפוש GitHub, מופיע בפרופילמוסתר מחיפוש, רק למי שיודע את השם
המלצהברירת מחדל אם אין סיבה להסתיררק לקוד פנימי/רגיש/מסחרי
עשו עכשיו

אם פרסמתם package בסעיפים הקודמים — בדקו את ההרשאות שלו. כנסו לעמוד ה-package ← Package settingsManage access. מי יכול למשוך? מי יכול לפרסם? מי Admin? אם זה package לימודי — שקלו להפוך אותו ל-public (חוסך storage ומאפשר גישה קלה).

6.7 פרסום אוטומטי עם Actions — workflow על release

הכוח האמיתי של GitHub Packages הוא אוטומציה. במקום לרוץ npm publish או docker push ידנית, ה-workflow עושה את זה עבורכם בכל release. כמו שלמדנו בפרק 1 (Actions), ה-workflow מתחבר ל-GitHub events — ו-release הוא event מושלם לפרסום.

נבנה שני workflows — אחד ל-npm ואחד ל-Docker. בפרויקט אמיתי, תשתמשו בזה שמתאים לכם (או בשניהם).

Workflow 1: פרסום npm package על release

name: Publish npm Package
on:
  release:
    types: [published]

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v5

      - uses: actions/setup-node@v4
        with:
          node-version: '22'
          registry-url: 'https://npm.pkg.github.com'
          scope: '@your-username'

      - run: npm ci
      - run: npm publish --provenance --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

נקודות חשובות:

Workflow 2: פרסום Docker image על release

name: Publish Docker Image
on:
  release:
    types: [published]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      attestations: write
      id-token: write
    steps:
      - uses: actions/checkout@v5

      - name: Log in to Container registry
        uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata (tags, labels)
        id: meta
        uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

      - name: Build and push Docker image
        id: push
        uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

      - name: Generate artifact attestation
        uses: actions/attest@v4
        with:
          subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          subject-digest: ${{ steps.push.outputs.digest }}
          push-to-registry: true

שימו לב שהשתמשנו ב-commit SHA ולא ב-tags (כמו @v3) לכל ה-actions החיצוניים. כפי שלמדנו בפרק 1, tag יכול להשתנות — SHA לא. אם מישהו משנה את docker/login-action@v3, הקוד שלכם ישתנה בלי שתדעו. עם SHA, אתם נעולים לגרסה ספציפית.

מה קורה כאן בפירוט:

  1. docker/login-action — מתחבר ל-ghcr.io עם GITHUB_TOKEN. שימו לב: username הוא github.actor (המשתמש שיצר את ה-release), לא שם קבוע.
  2. docker/metadata-action — יוצר tags חכמים מה-release: שם הגרסה (v1.0.0), SHA קצר, latest, ועוד. גם labels לפי OCI standard. לא צריך לכתוב tags ידנית.
  3. docker/build-push-action — בונה ודוחף ב-step אחד. push: true מבטיח שה-image עולה ל-registry מיד אחרי build מוצלח.
  4. actions/attest — מוסיף artifact attestation. מסמך חתום שמעיד שה-image נבנה מריפוזיטורי X, ב-commit Y, על ידי workflow Z. חלק משרשרת אספקה בטוחה (supply chain security).

permissions נוספים: שימו לב ל-attestations: write ו-id-token: write — הם נדרשים עבור ה-attestation step. בלי זה, ה-build יצליח אבל ה-attestation ייכשל.

פרסום על push ל-main (לא על release):

לפעמים רוצים לפרסם image על כל push ל-main, לא רק על release. זה מתאים למצב שבו כל merge ל-main הוא deployable. שנו את ה-trigger:

on:
  push:
    branches: [main]
  release:
    types: [published]

עם docker/metadata-action, ה-tags ישתנו אוטומטית: push ל-main → tag main + SHA. Release → tag v1.0.0 + latest. לא צריך לשנות שום דבר אחר ב-workflow.

Matrix builds — כמה images מ-workflow אחד:

אם יש לכם monorepo עם כמה services, אפשר לפרסם את כולם מ-workflow אחד:

strategy:
  matrix:
    service: [api, worker, frontend]
steps:
  - uses: docker/build-push-action@...
    with:
      context: ./services/${{ matrix.service }}
      tags: ghcr.io/your-org/${{ matrix.service }}:latest

כל service נבנה ב-job נפרד, ב-parallel. שלושה images מפורסמים ב-workflow אחד.

Caching — בנייה מהירה יותר:

Docker builds יכולים לקחת דקות ארוכות. אפשר לשמור cache של layers ב-GitHub Actions cache:

      - uses: docker/build-push-action@...
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

type=gha משתמש ב-GitHub Actions cache לשמירת Docker layers. ב-build הבא, layers שלא השתנו ייטענו מהcache — וה-build יהיה הרבה יותר מהיר (דקות במקום 10+ דקות). שימו לב: ה-cache צורך storage מה-free tier (משותף עם Actions).

מסגרת החלטה: מתי ואיך לפרסם אוטומטית
מצבtrigger מומלץלמה
ספרייה עם versioningon: releaseשליטה מלאה — מפרסמים רק כשמוכנים, עם release notes
app ל-deploymenton: push: branches: [main]כל merge ל-main הוא deployable — continuous delivery
pre-release / betaon: push: tags: ['v*-beta*']tag pattern שמבדיל בין stable ל-beta
monorepo עם כמה packageson: push: paths: ['packages/X/**']מפרסם רק את ה-package שהשתנה, לא את כולם
לא בטוחיםon: releaseהכי בטוח — רק release מכוון מפרסם. מומלץ להתחלה
תרגיל 3: בניית Auto-Publish Workflow

צרו workflow שמפרסם Docker image אוטומטית:

  1. צרו קובץ .github/workflows/publish-image.yml בריפוזיטורי
  2. העתיקו את ה-workflow למעלה
  3. ודאו שיש Dockerfile בשורש הריפוזיטורי (אם אין — צרו פשוט עם FROM nginx:alpine)
  4. עשו commit ו-push לקובץ ה-workflow
  5. כנסו ל-GitHub ← ReleasesDraft a new release
  6. צרו tag חדש (למשל v1.0.0), כתבו release notes, ולחצו Publish release
  7. עברו לטאב Actions ועקבו אחרי ה-workflow — כל step צריך להיות ירוק
  8. בדקו שה-image מופיע בטאב Packages עם ה-tag הנכון

הצלחה: כל release עתידי יפרסם image אוטומטית. אין צורך ב-docker push ידני יותר. ה-pipeline שלכם הוא: commit → PR → merge → release → image published.

עשו עכשיו

צרו release חדש בריפוזיטורי וודאו שה-workflow רץ. בטאב Actions, תראו את ה-workflow פועל. לחצו על ה-job לראות את הלוגים — כל step צריך להיות ירוק. אם step נכשל, קראו את ה-error message — הסיבה הנפוצה ביותר היא missing permissions.

עשו עכשיו

אחרי שה-workflow הצליח — נסו למשוך את ה-image ממכונה אחרת (או מהטרמינל המקומי): docker pull ghcr.io/YOUR_USER/YOUR_REPO:TAG. ודאו שאתם מקבלים את ה-image הנכון. אם ה-image private, תצטרכו docker login קודם.

6.8 צריכת Packages — install ו-pull

פרסמנו packages — עכשיו צריך להשתמש בהם. זה החלק שבו ה-packages שלכם מתחילים לספק ערך: צוותים אחרים מתקינים את הספרייה שפרסמתם, או מושכים את ה-Docker image ל-production. יש שני תרחישים עיקריים: התקנת npm packages בפרויקט אחר, ומשיכת Docker images ל-deployment. בואו נכסה את שניהם בפירוט.

npm packages: התקנה מ-GitHub Packages

צרו .npmrc בפרויקט שצורך את ה-package:

# .npmrc
@your-org:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}

ואז פשוט:

npm install @your-org/shared-utils

npm יודע לפנות ל-GitHub Packages עבור packages עם scope @your-org, ול-npmjs.com עבור כל השאר. שני ה-registries עובדים במקביל — packages ציבוריים מ-npm ו-packages פרטיים מ-GitHub. lodash מגיע מ-npmjs.com, @your-org/utils מגיע מ-GitHub. זה עובד גם עם yarn ו-pnpm — אותו .npmrc עובד לכולם.

ב-CI/CD (Actions): ב-workflow, ההגדרה קצת שונה. setup-node יוצר .npmrc אוטומטית:

      - uses: actions/setup-node@v4
        with:
          node-version: '22'
          registry-url: 'https://npm.pkg.github.com'
          scope: '@your-org'

      - run: npm ci
        env:
          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

לא צריך ליצור .npmrc ידנית ב-Actions — setup-node עושה את זה. NODE_AUTH_TOKEN הוא ה-environment variable ש-npm מחפש (מוגדר על ידי setup-node).

packages מארגונים אחרים: אם אתם צריכים packages מכמה ארגונים, הוסיפו שורות ב-.npmrc:

@your-org:registry=https://npm.pkg.github.com
@other-org:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}

ה-PAT שלכם צריך הרשאות read לשני הארגונים.

Docker images: משיכה מ-ghcr.io

# image ציבורי — בלי אימות
docker pull ghcr.io/some-org/public-app:latest

# image פרטי — צריך login קודם
echo $CR_PAT | docker login ghcr.io -u USERNAME --password-stdin
docker pull ghcr.io/your-org/private-app:1.2.3

שימוש ב-Actions workflows:

בתוך workflow, המשיכה קלה יותר כי GITHUB_TOKEN זמין אוטומטית:

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      packages: read
    steps:
      - name: Log in to Container registry
        uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Pull and run
        run: |
          docker pull ghcr.io/${{ github.repository }}:latest
          docker run -d -p 80:80 ghcr.io/${{ github.repository }}:latest

טיפ: Dockerfile שמשתמש ב-private image

אם ה-Dockerfile שלכם מתחיל ב-FROM ghcr.io/your-org/base-image:latest (image פרטי), תצטרכו docker login לפני docker build גם ב-CI וגם מקומית. ב-Actions, שימו את docker/login-action לפני docker/build-push-action.

Troubleshooting — בעיות נפוצות בצריכת packages:

שימוש ב-package כ-dependency ב-package.json:

אחרי שהגדרתם .npmrc, השימוש ב-package מ-GitHub Packages זהה לחלוטין לכל package אחר:

// package.json
{
  "dependencies": {
    "@your-org/shared-utils": "^1.0.0",
    "@your-org/api-client": "^2.3.0",
    "express": "^4.18.0"  // מ-npmjs.com
  }
}

npm install יודע לפי ה-scope ב-.npmrc מאיפה למשוך כל package. @your-org/* מ-GitHub, כל השאר מ-npmjs.com. npm ci (שנשתמש בו ב-CI) עובד אותו דבר.

שילוב npm ו-Docker ביחד:

תרחיש נפוץ: יש לכם npm package פרטי (@your-org/api-client) ו-Docker image שמשתמש בו. ב-Dockerfile, תצטרכו .npmrc עם authentication כדי ש-npm install בתוך ה-build יצליח. הדרך הבטוחה: build argument:

# Dockerfile
FROM node:22-alpine
ARG NPM_TOKEN
WORKDIR /app
COPY .npmrc.docker .npmrc
COPY package*.json ./
RUN npm ci
RUN rm -f .npmrc    # מוחק את הטוקן מה-image!
COPY . .
CMD ["node", "index.js"]

וב-build:

docker build --build-arg NPM_TOKEN=$NPM_TOKEN -t my-app .

חשוב: שימו לב לשורה RUN rm -f .npmrc — היא מוחקת את קובץ ה-.npmrc (שמכיל את הטוקן) מה-image. בלי זה, כל מי שמושך את ה-image יכול לחלץ את הטוקן שלכם. בפרק 7 (Security) נרחיב על סוגי הסיכונים האלה.

עשו עכשיו

התקינו את ה-npm package שפרסמתם בפרויקט אחר. צרו תיקייה חדשה, npm init -y, צרו .npmrc עם ה-scope הנכון, הריצו npm install @your-username/my-utils, ובדקו ב-node_modules/@your-username/my-utils שה-package הגיע מ-GitHub.

6.9 עלויות ו-free tier — כל שקל חשוב

GitHub Packages הוא חינמי — עד גבול. הגבולות משתנים לפי תוכנית, והנתון החשוב ביותר הוא שה-storage משותף עם GitHub Actions. בואו נפרק את זה.

תוכניתStorageBandwidth (חודשי)מחיר חריגה — Storageמחיר חריגה — Bandwidth
Free500 MB1 GB$0.25/GB$0.50/GB
Pro2 GB10 GB$0.25/GB$0.50/GB
Team2 GB10 GB$0.25/GB$0.50/GB
Enterprise50 GB100 GB$0.25/GB$0.50/GB
אזהרה

ה-storage של Packages משותף עם GitHub Actions. הקצאת ה-500MB (בתוכנית Free) מתחלקת בין Packages storage, Actions artifacts, ו-Actions caches. אם ה-Actions שלכם צורכים 400MB ב-caches ו-artifacts — נשארו לכם רק 100MB ל-packages! זה הדבר הראשון שצריך לבדוק כשנגמר ה-storage. בדקו את הצריכה המשולבת ב-Settings → Billing.

מה חינמי לגמרי (לא נספר לגבול):

הגדרת spending limit: כדי למנוע חיוב בלתי צפוי, אפשר להגדיר spending limit ב-Settings → Billing → Spending limits. ברירת מחדל: $0 — כלומר, אם חורגים מה-free tier, ה-packages ייחסמו (לא ייגבה תשלום). אפשר להעלות את הגבול אם רוצים לשלם, או להשאיר ב-$0 ולהסתמך על ניקוי.

דוגמה מעשית — חישוב עלות:

צוות עם 3 מפתחים, תוכנית Free:

טיפ מעשי: אם יש לכם Docker images ואתם קרובים לגבול ה-storage — אל תשלמו. העבירו ל-ghcr.io. Docker images ב-Container registry לא נספרים ל-quota. אם יש לכם npm packages שאפשר להפוך ל-public — גם הם יפסיקו לצרוך storage.

אזהרה

Container registry חינמי — אבל לא בהכרח לנצח. נכון לאפריל 2026, ghcr.io חינמי לחלוטין. GitHub התחייבו להודיע חודש מראש לפני שינוי מדיניות. אם אתם בונים תהליך שתלוי ב"חינמי" — היו מוכנים לשינוי. בינתיים, תהנו מזה — אבל אל תבנו storage strategy שתתמוטט אם ghcr.io יתחיל לעלות כסף.

תרגיל 4: חישוב עלות Packages

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

  1. כמה packages יש לכם? ____ npm, ____ Docker, ____ אחר
  2. גודל ממוצע לגרסה: ____ MB
  3. כמה גרסאות חדשות בחודש: ____
  4. סוג packages: public / private
  5. storage מצטבר חודשי: ____ MB
  6. האם Container images? כן → חינמי (לא נספר). לא → ראו טבלה למעלה
  7. כמה מתוך ה-free tier ה-Actions שלכם צורכים? ____ MB (בדקו ב-Billing)
  8. נשאר ל-packages: ____ MB. האם מספיק ל-6 חודשים קדימה?

אם התשובה היא "לא מספיק": שלושה פתרונות לפי סדר עדיפות: (1) הפכו packages ל-public, (2) העבירו Docker images ל-ghcr.io, (3) הוסיפו workflow ניקוי (ראו סעיף 6.10).

עשו עכשיו

לכו ל-Settings → Billing and plans. גללו ל-Packages ובדקו: כמה storage אתם צורכים? כמה bandwidth? אם אין נתונים — מצוין, אתם בתוך ה-free tier. אם יש — רשמו את המספרים ובדקו גם את Actions usage באותו עמוד.

6.10 ניהול גרסאות — ניקוי שחוסך כסף

כל פעם שמפרסמים גרסה חדשה — הישנה נשארת. עם הזמן, הגרסאות מצטברות ואוכלות storage. זה במיוחד בעייתי עם Docker images שיכולים לשקול מאות MB לגרסה. ניקוי אוטומטי הוא לא רק "best practice" — הוא חוסך כסף ומונע הפתעות.

הבעיה במספרים: נניח שאתם מפרסמים Docker image של 200MB פעם ביום (כל merge ל-main). אחרי חודש — 6GB. אחרי שנה — 73GB. רוב ה-images האלה לא רלוונטיים יותר — רק האחרונים חשובים. עם npm packages הבעיה קטנה יותר (packages טיפוסיים הם 50KB-5MB), אבל עם זמן גם 100 גרסאות של npm packages מצטברות.

למה ניקוי אוטומטי ולא ידני? כי אנשים שוכחים. ניקוי ידני עובד שבוע, אולי חודש — ואז מפסיקים. Scheduled workflow רץ כל שבוע בלי שתחשבו על זה. הגדירו פעם אחת ושכחו.

actions/delete-package-versions@v5 — ה-action הרשמי לניקוי:

name: Cleanup old package versions
on:
  schedule:
    - cron: '0 2 * * 0'    # כל יום ראשון בשעה 2:00
  workflow_dispatch:        # הרצה ידנית

jobs:
  cleanup:
    runs-on: ubuntu-latest
    permissions:
      packages: write
    steps:
      - uses: actions/delete-package-versions@v5
        with:
          package-name: 'my-app'
          package-type: 'container'
          min-versions-to-keep: 10
          delete-only-untagged-versions: true

מה ה-action עושה:

cleanup עם pre-release filtering:

אם אתם מפרסמים pre-releases (כמו 1.0.0-beta.3), אפשר לנקות רק אותם:

      - uses: actions/delete-package-versions@v5
        with:
          package-name: 'my-utils'
          package-type: 'npm'
          min-versions-to-keep: 5
          delete-only-pre-release-versions: true

נקודות חשובות:

ניקוי ידני מהממשק: אם צריך למחוק גרסה ספציפית, כנסו לעמוד ה-package ← גרסאות ← לחצו על הגרסה ← Delete this version. פשוט, אבל לא scalable.

ניקוי דרך API: לפעמים ה-action לא מספיק (למשל, צריכים לוגיקה מותאמת). אפשר להשתמש ב-GitHub REST API ישירות:

# רשימת versions של container package
gh api /user/packages/container/my-app/versions

# מחיקת version ספציפי
gh api --method DELETE /user/packages/container/my-app/versions/12345

עם gh api (GitHub CLI) אפשר לבנות scripts מותאמים — למשל, "מחק כל version שישן מ-90 יום ואין לו tag". זה דורש יותר קוד, אבל נותן שליטה מלאה.

אסטרטגיית ניקוי מומלצת:

  1. Container images: השאירו 10-20 tagged versions אחרונים. מחקו את כל ה-untagged. הריצו שבועית.
  2. npm packages: השאירו 5-10 versions אחרונים. מחקו pre-releases ישנים. הריצו חודשית.
  3. לפני release: הריצו cleanup ידני (workflow_dispatch) כדי לפנות storage לפני פרסום גרסה חדשה כבדה.
  4. מעקב: בדקו ב-Billing כמה storage פנה אחרי כל cleanup. אם לא מספיק — הורידו את min-versions-to-keep.
עשו עכשיו

בדקו כמה versions יש ל-packages שלכם. כנסו לעמוד ה-package ולחצו על "X versions". אם יש יותר מ-10 גרסאות ישנות — הוסיפו את ה-cleanup workflow למעלה לריפוזיטורי. גם אם עכשיו אין בעיה — הוסיפו. ניקוי אוטומטי מונע בעיות עתידיות.

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

סיכום הפרק
  • GitHub Packages הוא רגיסטרי אוניברסלי — 6 סוגי packages במקום אחד, משולב ב-GitHub ליד הקוד, ההרשאות, וה-Actions
  • 6 registries: npm, Container (ghcr.io), Maven, Gradle, NuGet, RubyGems — לכל שפה יש פתרון, ושניים מהם (Container, npm) הם הנפוצים ביותר
  • npm packages דורשים scope (@owner/name), .npmrc עם registry URL, ו-publishConfig ב-package.json. בלי scope — ה-package הולך ל-npmjs.com
  • Container registry (ghcr.io) חינמי לחלוטין — storage ו-bandwidth — גם ל-private images, נכון לאפריל 2026
  • אימות: GITHUB_TOKEN ב-Actions (תמיד עדיף, אוטומטי, בטוח), PAT מחוץ ל-Actions (ידני, צריך rotation)
  • הרשאות: Granular (Container, npm, NuGet, RubyGems — עצמאיות מהריפו) מול Repo-scoped (Maven, Gradle — יורשות הרשאות ריפו)
  • Auto-publish: workflow על release עם docker/build-push-action או npm publish, כולל artifact attestation
  • Free tier: 500MB storage (משותף עם Actions), 1GB bandwidth. Public packages וContainer images חינמיים לחלוטין
  • ניקוי: actions/delete-package-versions@v5 שומר על storage נקי — הגדירו כ-scheduled workflow
בדקו את עצמכם
  1. מה ההבדל בין Granular permissions ל-Repo-scoped permissions? תנו דוגמה למצב שבו Granular חשוב.
  2. למה GITHUB_TOKEN עדיף על PAT ב-Actions workflows? מתי בכל זאת צריך PAT?
  3. מה קורה אם מפרסמים npm package בלי scope (@owner/) וגם בלי publishConfig?
  4. למה Container registry (ghcr.io) שווה שימוש גם אם יש לכם Docker Hub? תנו שלוש סיבות.
  5. איך ה-storage של Packages קשור ל-Actions? צוות שלכם חורג מ-500MB — מה שלושת הצעדים הראשונים?
שגרת עבודה מומלצת
  • בכל release: ודאו שה-auto-publish workflow רץ והצליח. בדקו בטאב Actions — כל step ירוק. לחצו על ה-package ב-Packages ובדקו שהגרסה הנכונה מפורסמת.
  • שבועית: בדקו את Packages tab — האם הגרסאות הנכונות מפורסמות? האם ה-cleanup workflow רץ? האם יש packages עם הרשאות לא נכונות?
  • חודשית: כנסו ל-Settings → Billing ובדקו storage usage — Packages + Actions ביחד. נקו גרסאות ישנות. בדקו אם יש packages שאפשר להפוך ל-public. בדקו שכל ה-PATs עדיין תקפים.
  • רבעונית: סקרו את ה-packages שלכם — האם כולם עדיין בשימוש? מחקו packages לא פעילים. ודאו ש-cleanup workflows מכסים את כל ה-packages.
צ'קליסט — מה עשינו בפרק הזה
  • ☐ הבנתי מה זה GitHub Packages ולמה להשתמש במקום npm registry / Docker Hub
  • ☐ זיהיתי את 6 ה-registries הנתמכים וידעתי איזה מתאים לפרויקט שלי
  • ☐ הגדרתי package.json עם scope ו-publishConfig
  • ☐ צרתי .npmrc עם registry URL ו-authentication
  • ☐ פרסמתי npm package ל-GitHub Packages (תרגיל 1)
  • ☐ בניתי Docker image עם OCI labels ופרסמתי ל-ghcr.io (תרגיל 2)
  • ☐ הבנתי את ההבדל בין GITHUB_TOKEN ל-PAT וידעתי מתי להשתמש בכל אחד
  • ☐ הבנתי את ההבדל בין Granular ל-Repo-scoped permissions
  • ☐ בדקתי הרשאות ו-visibility של packages שלי
  • ☐ יצרתי workflow שמפרסם Docker image אוטומטית על release (תרגיל 3)
  • ☐ התקנתי package מ-GitHub Packages בפרויקט אחר
  • ☐ בדקתי storage usage ב-Billing וחישבתי עלות (תרגיל 4)
  • ☐ הוספתי workflow ניקוי גרסאות ישנות
  • ☐ קראתי את כל 12 מונחי המילון
  • ☐ עניתי על 5 שאלות "בדקו את עצמכם"

מה למדנו ביחס ל-pipeline המלא:

אחרי הפרקים הקודמים, ה-pipeline שלכם נראה כך:

  1. פרק 1 (Actions): CI — build, test, lint על כל PR
  2. פרק 2 (Repos): ניהול קוד — branches, PRs, reviews
  3. פרק 5 (Projects): ניהול פרויקט — boards, milestones, tracking
  4. פרק 6 (Packages): CD — פרסום packages ו-images אוטומטית על release

כל חלק מתחבר לקודם. ה-Actions workflow מפרק 1 עכשיו גם מפרסם packages. ה-releases מפרק 2 trigger את הפרסום. ה-milestones מפרק 5 מסמנים מתי release מוכן. זה ה-pipeline המלא — מ-commit ועד published package.

בפרק הבא: נלמד להגן על הקוד וה-packages שלכם עם GitHub Security — Dependabot, CodeQL, secret scanning, ו-push protection. ה-package שפרסמתם בפרק הזה — בפרק הבא תוודאו שהוא בטוח, שה-dependencies שלו מעודכנים, ושלא דלפו secrets לתוכו. ה-artifact attestation שהוספנו ב-workflow? זה הבסיס ל-supply chain security שנבנה עליו.