צרו Actions workflow שמפרסם Docker image ל-ghcr.io אוטומטית על כל release. זה חינמי, לוקח 10 דקות להגדרה, ומשנה את כל ה-deployment pipeline שלכם. כל release עתידי יפרסם image בלי שתיגעו בטרמינל.
- npm package מפורסם ב-GitHub Packages — עם scoped name, גרסאות, ועמוד package ב-GitHub
- Docker image ב-container registry — מפורסם ב-ghcr.io עם תגיות, labels, ו-attestation
- Actions workflow לפרסום אוטומטי — מפרסם package או image על כל release/tag
- טבלת החלטה: איזה registry בשביל מה, ומתי GitHub Packages עדיף על npm/Docker Hub
- מחשבון עלויות — כמה storage אתם צורכים, מה משותף עם Actions, והאם אתם חורגים
- workflow ניקוי גרסאות — שומר על storage נקי ומוחק גרסאות ישנות אוטומטית
- .npmrc מוכן — קובץ הגדרות שמאפשר צריכה של packages מ-GitHub Packages בכל פרויקט
- מילון מונחים — 12 מונחי מפתח לעבודה עם registries
- תוכלו לפרסם npm package ו-Docker image ל-GitHub Packages מהטרמינל ומ-Actions
- תוכלו להגדיר הרשאות גישה ברמת repo וארגון — ולהבדיל בין granular ל-repo-scoped
- תוכלו לבנות Actions workflow שמפרסם package אוטומטית על tag/release עם attestation
- תוכלו לחשב עלויות אחסון ולנהל מגבלות free tier בצורה חכמה
- תוכלו להקים workflow ניקוי שמוחק גרסאות ישנות ושומר על storage נקי
- פרק 1 (Actions): workflows, jobs, steps, secrets, permissions — נשתמש בהם לפרסום אוטומטי
- פרק 2 (Repos): הבנה בסיסית של ריפוזיטורים, branches, ו-releases
- פרק 5 (Projects): הקשר בין packages לניהול פרויקטים — versions כ-milestones
- כלים: חשבון GitHub עם ריפוזיטורי, Node.js או Docker מותקנים (אחד מהם מספיק)
- ידע: מה זה npm package או Docker 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שמגדיר לאיזה registrynpm 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, הנה מה שקורה:
- חשבון npmjs.com לניהול ספריות פנימיות — $7/חודש למשתמש ל-private packages
- חשבון Docker Hub לניהול images — מוגבל ל-image אחד פרטי בחינם
- הרשאות מנוהלות בשלושה מקומות: GitHub, npm, Docker Hub
- Secrets שונים בכל מערכת, rotation בכל מקום
- אין קשר בין ה-package לקוד המקור — צריך לחפש ידנית
עם GitHub Packages, כל זה מתרכז ב-מקום אחד. הנה שלוש הסיבות העיקריות לשימוש:
- אינטגרציה מלאה עם GitHub. ה-package חי באותו ריפוזיטורי שבו הקוד. הרשאות, Actions workflows, ו-visibility — הכל מנוהל ממקום אחד. פרסום package הוא step ב-workflow, לא מערכת נפרדת. כשמישהו לוחץ על ה-package ב-GitHub, הוא רואה את הריפוזיטורי, ה-README, וה-versions — הכל מחובר.
- packages פרטיים בלי כאב ובלי עלות נוספת. ב-npmjs.com, packages פרטיים עולים $7/חודש למשתמש. ב-GitHub Packages, הם כלולים בתוכנית שלכם — 500MB חינם בתוכנית Free, 2GB ב-Pro. לרוב הצוותים הקטנים, זה מספיק.
- 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 Packages | npmjs.com | Docker Hub |
|---|---|---|---|
| packages פרטיים | כלול בתוכנית (500MB+) | $7/חודש/משתמש | $5/חודש (1 private repo) |
| אינטגרציה עם CI | native עם Actions | צריך NPM_TOKEN secret | צריך Docker credentials |
| הרשאות | GitHub teams/roles | npm 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. האם יש packages מפורסמים? רוב המפתחים מעולם לא לחצו שם — היום זה משתנה. אם אין packages, זה מצוין — בסוף הפרק יהיו שם לפחות שניים.
6.2 שישה רגיסטרים, פלטפורמה אחת
GitHub Packages תומך בשישה סוגי registries. לכל אחד יש URL, כלי client, ומודל הרשאות משלו. לפני שמתחילים לפרסם, חשוב להבין את ההבדלים — כי הם משפיעים על איך אתם מגדירים אימות, הרשאות, וגישה.
| Registry | שפה / פלטפורמה | URL | Client | הרשאות |
|---|---|---|---|---|
| npm | JavaScript / TypeScript | npm.pkg.github.com | npm / yarn / pnpm | Granular |
| Container | Docker / OCI | ghcr.io | Docker / Podman | Granular |
| Maven | Java / Kotlin | maven.pkg.github.com | mvn | Repo-scoped |
| Gradle | Java / Kotlin | maven.pkg.github.com | gradle | Repo-scoped |
| NuGet | .NET / C# | nuget.pkg.github.com | dotnet CLI | Granular |
| RubyGems | Ruby | rubygems.pkg.github.com | gem / bundler | Granular |
הבחנה חשובה: 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 יש לו כללי שמות:
- npm: חייב scope (
@owner/name), אותיות קטנות בלבד, מקפים מותרים - Container:
ghcr.io/OWNER/NAME, תומך בתתי-paths (ghcr.io/org/team/app), אותיות קטנות - Maven:
com.github.OWNERכ-groupId, שם חופשי כ-artifactId - NuGet: שם חופשי, אבל מומלץ prefix עם שם הארגון
- RubyGems: scope עם שם המשתמש/ארגון כ-prefix
חשוב לדעת את הכללים לפני שמפרסמים — שינוי שם אחרי פרסום פירושו יצירת package חדש (השם הישן נשאר תפוס 30 ימים אחרי מחיקה).
מגבלות שכדאי לדעת:
- npm: מקסימום 1,000 versions ל-package. גודל מקסימלי ל-package: 256MB. חייב scope.
- Container: אין מגבלת versions. אין מגבלת גודל (מעשית). ה-registry חינמי.
- Maven/Gradle: שם ה-package לא יכול להשתנות — הוא מבוסס על groupId ו-artifactId.
- כללי: packages שנמחקים ניתנים לשחזור תוך 30 יום. אחרי 30 יום — נמחקים לצמיתות.
| מצב | Registry | למה |
|---|---|---|
| ספרייה פנימית ב-Node.js | npm | Scoped package (@org/lib), npm install רגיל, granular permissions — הדרך הטבעית |
| microservice או app | Container (ghcr.io) | Docker image, deploy ל-k8s/ECS/Cloud Run, חינמי לחלוטין — ברירת מחדל מומלצת |
| ספריית Java / Kotlin | Maven / Gradle | פרסום JAR/AAR, repo-scoped — הרשאות לפי ריפו, מוכר למפתחי Java |
| NuGet package | NuGet | .NET ecosystem, granular permissions, dotnet CLI integration |
| gem פנימי | RubyGems | Bundler integration, granular permissions, Gemfile מוכר |
| package ציבורי לקהילה | npmjs.com / Docker Hub | discoverability — אנשים מחפשים שם, לא ב-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"
}
}
בואו נפרק את השדות הקריטיים:
"name": "@your-username/my-utils"— ה-scope (@your-username) חייב להתאים בדיוק לשם המשתמש או הארגון ב-GitHub. אותיות קטנות בלבד — GitHub Packages לא מקבל אותיות גדולות ב-scope. אם שם המשתמש שלכם הואMyUser, ה-scope יהיה@myuser."publishConfig"— מכוון אתnpm publishל-GitHub במקום ל-npmjs.com. בלי השדה הזה, npm ישלח את ה-package ל-registry הדיפולטי — שהוא npmjs.com."repository"— מקשר את ה-package לריפוזיטורי ב-GitHub. בלי זה, עמוד ה-package ב-GitHub לא יציג קישור לקוד המקור.
שלב 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 — בעיות נפוצות:
- שגיאת 401 Unauthorized: הטוקן לא תקף או פג תוקף. בדקו שה-PAT פעיל, שיש לו
write:packages, ושה-environment variableNPM_TOKENמוגדר. - שגיאת 403 Forbidden: הטוקן תקף אבל אין הרשאה. בדקו שה-scope ב-
package.jsonתואם לשם המשתמש/ארגון שה-PAT שייך אליו. - שגיאת 422 "Package name invalid": ה-scope לא תואם. ודאו שה-scope באותיות קטנות ושהוא תואם בדיוק ל-GitHub username/org.
- ה-package הלך ל-npmjs.com: חסר
publishConfigאו שה-.npmrcלא מוגדר נכון. בדקו את שני הקבצים.
מה קורה אחרי publish מוצלח? npm מדפיס את שם ה-package, הגרסה, ו-SHA. בנוסף:
- עמוד package נוצר אוטומטית ב-GitHub עם הוראות התקנה
- ה-package מופיע בטאב Packages של הריפוזיטורי (אם
repositoryמוגדר) - ה-package מופיע גם בפרופיל המשתמש/ארגון תחת Packages
- אפשר לנהל versions, הרשאות, ו-visibility דרך Package Settings
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 של הריפוזיטורי.
פרסמו npm package ל-GitHub Packages. עקבו אחרי כל הצעדים:
- צרו ריפוזיטורי חדש ב-GitHub (או השתמשו בקיים) עם קובץ
index.jsפשוט:// index.js module.exports = { greet: (name) => `Hello, ${name}! Welcome to GitHub Packages.`, add: (a, b) => a + b, version: '1.0.0' }; - צרו
package.jsonעם scope ו-publishConfig(כמו למעלה). ודאו ש-nameמתחיל ב-@your-username/ - צרו
.npmrcבשורש הפרויקט עם הגדרת registry - צרו PAT (classic) עם scope
write:packagesו-read:packages - הגדירו את ה-PAT כ-
NPM_TOKENenvironment variable:export NPM_TOKEN=ghp_xxxxx - הריצו
npm publish(אוnpm publish --access publicלציבורי) - כנסו ל-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 חשובים מאוד — הנה למה:
org.opencontainers.image.source— קריטי. מקשר את ה-image לריפוזיטורי ב-GitHub. בלי זה, ה-image "יתומים" — לא מקושר לשום ריפו, ולא ניתן לנהל הרשאות דרך הריפו. ה-image יופיע רק בפרופיל המשתמש, לא בריפוזיטורי.org.opencontainers.image.description— מופיע בעמוד ה-image ב-GitHub. עוזר לאנשים להבין מה ה-image עושה.org.opencontainers.image.licenses— מציג את הרישיון. חשוב ל-images ציבוריים.
שלב 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 מוצלח?
- Docker מדפיס את ה-digest (SHA256) של ה-image
- ה-image מופיע בפרופיל שלכם תחת Packages
- אם הוספתם את ה-LABEL
org.opencontainers.image.source, ה-image מקושר לריפוזיטורי - כברירת מחדל ה-image פרטי — רק אתם יכולים למשוך אותו
- אפשר לשנות ל-public דרך Package Settings → Danger Zone → Change visibility
Visibility — public מול private:
כברירת מחדל, images חדשים הם private. כדי לשנות ל-public:
- כנסו לעמוד ה-image ב-GitHub (Your Profile → Packages → שם ה-image)
- לחצו על Package settings (בצד ימין)
- גללו ל-Danger Zone → Change package visibility
- בחרו 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. הכל שקוף למשתמש.
צרו Dockerfile פשוט. אפילו שתי שורות מספיקות: FROM nginx:alpine + COPY index.html /usr/share/nginx/html/. צרו קובץ index.html עם תוכן כלשהו ובנו image מקומי: docker build -t test-app .. ודאו שה-build מצליח לפני שממשיכים ל-push.
פרסמו Docker image ל-Container Registry. עקבו בזהירות:
- צרו PAT (classic) עם scopes:
write:packages,read:packages, ו-delete:packages - שמרו את ה-PAT ב-environment variable:
export CR_PAT=ghp_xxxxx - התחברו ל-ghcr.io:
echo $CR_PAT | docker login ghcr.io -u YOUR_USER --password-stdin - ודאו שה-Dockerfile כולל LABEL
org.opencontainers.image.sourceעם URL הריפו - בנו עם tag מלא:
docker build -t ghcr.io/YOUR_USER/my-app:1.0 . - דחפו:
docker push ghcr.io/YOUR_USER/my-app:1.0 - כנסו ל-GitHub → Your profile → Packages ובדקו שה-image מופיע
- אם ה-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 — טוקן זמני שמאפשר:
- פרסום packages לריפוזיטורי הנוכחי
- התקנה/משיכה של packages מהריפוזיטורי
- מחיקה ושחזור של packages (עם permission מתאים)
היתרונות ברורים: אין צורך ליצור 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:
read:packages— התקנה/משיכה של packages בלבדwrite:packages— פרסום packages חדשים וגרסאות (כולל read)delete:packages— מחיקת packages או גרסאות (כולל write)
Fine-grained PAT: הגישה החדשה והמומלצת. מאפשר להגביל את הטוקן לריפוזיטוריים ספציפיים ולהגדיר הרשאות ברמת resource. למשל: "טוקן שיכול לפרסם packages רק בריפוזיטורי X, ולא בשום ריפו אחר". גם תומך ב-expiration dates — מה שמאפשר rotation אוטומטי. אם אתם יוצרים PAT חדש היום — השתמשו ב-fine-grained. PAT classic עדיין עובד ונפוץ בדוגמאות ישנות, אבל fine-grained בטוח יותר.
הגדרת PAT classic — שלב אחר שלב:
- כנסו ל-Settings → Developer settings → Personal access tokens → Tokens (classic)
- לחצו Generate new token (classic)
- תנו שם תיאורי (למשל "packages-local-dev")
- סמנו scopes:
write:packages(כולל read אוטומטית) - הגדירו expiration — מומלץ 90 ימים, לא "No expiration"
- העתיקו את הטוקן מיד — הוא לא יוצג שוב!
- שמרו ב-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 → publish | GITHUB_TOKEN | אוטומטי, צריך permissions: packages: write |
| Actions → install/pull | GITHUB_TOKEN | אוטומטי, צריך permissions: packages: read |
| Actions → cross-repo | PAT (secret) | נדיר. רק כשצריך packages מריפו אחר |
| Terminal → npm publish | PAT (classic/fine-grained) | דרך NPM_TOKEN env var או ~/.npmrc |
| Terminal → docker push | PAT (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 בלי שיוכל לראות את קוד המקור. שלוש רמות:
- Read — משיכה/התקנה של ה-package, צפייה במטא-דאטה וגרסאות
- Write — פרסום גרסאות חדשות + כל מה ש-Read מאפשר
- Admin — מחיקה, שינוי הרשאות, שינוי visibility + כל מה ש-Write מאפשר
ניתן להוסיף הרשאות ברמת משתמש, צוות (team), או ריפוזיטורי. למשל: "צוות Frontend יכול לקרוא את @org/design-system, רק צוות Design System יכול לפרסם גרסאות, ורק Team Lead יכול למחוק גרסאות."
מודל 2: Repo-scoped (Maven, Gradle)
ה-package יורש את ההרשאות של הריפוזיטורי. מי שיכול לקרוא את הריפו — יכול למשוך packages. מי שיכול לכתוב — יכול לפרסם. אין הפרדה. זה פשוט יותר לנהל, אבל פחות גמיש.
Visibility: Public vs Private
- Public — חינמי לחלוטין (storage ו-bandwidth). ב-containers: גישה אנונימית (בלי login). ב-npm: צריך אימות גם לקריאה. נהדר לקוד פתוח ולספריות שרוצים לשתף.
- Private — רק למי שיש הרשאה. צורך storage מה-free tier. ה-default עבור packages חדשים. מתאים לקוד פנימי וספריות ארגוניות.
ברמת ארגון: Organization owners יכולים להגדיר מדיניות defaults — למשל, לחסום יצירת public packages, או לדרוש שכל packages חדשים יהיו private. אפשר גם להגדיר default visibility וברירות מחדל להרשאות.
תרחיש מעשי: ספרייה משותפת בארגון
נניח שיש לכם ספריית @myorg/auth-client שמשמשת 15 ריפוזיטורים. איך מגדירים הרשאות?
- Package visibility: אם כל הריפוזיטורים באותו ארגון — הפכו את ה-package ל-internal (visible לכל חבר ארגון) או תנו read access לצוותות הרלוונטיים
- Write access: רק ל-team שמתחזק את הספרייה. לא לכולם — אחרת כל מפתח יכול לפרסם גרסה שבורה
- 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 ולהגדיר ידנית.
| קריטריון | Public Package | Private Package |
|---|---|---|
| עלות | חינמי לחלוטין — storage ו-bandwidth | צורך storage מ-free tier (משותף עם Actions) |
| גישה | כולם (containers: אנונימי, npm: צריך PAT) | רק מי שהורשה במפורש |
| שימוש טיפוסי | קוד פתוח, ספריות ציבוריות, tools משותפים | packages פנימיים, ספריות ארגוניות, קוד רגיש |
| discovery | נמצא בחיפוש GitHub, מופיע בפרופיל | מוסתר מחיפוש, רק למי שיודע את השם |
| המלצה | ברירת מחדל אם אין סיבה להסתיר | רק לקוד פנימי/רגיש/מסחרי |
אם פרסמתם package בסעיפים הקודמים — בדקו את ההרשאות שלו. כנסו לעמוד ה-package ← Package settings ← Manage 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 }}
נקודות חשובות:
on: release: types: [published]— ה-workflow רץ רק כשיוצרים release חדש (לא draft, לא deleted). זה מבטיח שפרסום קורה רק כשאתם מוכנים.permissions: packages: write— חובה! בלי זה,GITHUB_TOKENלא יוכל לפרסם. שכחתם? תקבלו 403 Forbidden.registry-urlב-setup-node— מגדיר ל-setup-node לכוון ל-GitHub Packages ולייצר.npmrcאוטומטית.--provenance— מוסיף הוכחת מקור (artifact attestation). מומלץ מאוד — מראה שה-package נבנה ב-CI ולא על מכונה פרטית.--access public— חשוב ל-scoped packages שרוצים להיות ציבוריים. בלי זה, ה-package יהיה private.NODE_AUTH_TOKEN— ה-environment variable ש-setup-nodeמכניס ל-.npmrcהאוטומטי. לא צריך ליצור secret —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, אתם נעולים לגרסה ספציפית.
מה קורה כאן בפירוט:
docker/login-action— מתחבר ל-ghcr.io עםGITHUB_TOKEN. שימו לב:usernameהואgithub.actor(המשתמש שיצר את ה-release), לא שם קבוע.docker/metadata-action— יוצר tags חכמים מה-release: שם הגרסה (v1.0.0), SHA קצר, latest, ועוד. גם labels לפי OCI standard. לא צריך לכתוב tags ידנית.docker/build-push-action— בונה ודוחף ב-step אחד.push: trueמבטיח שה-image עולה ל-registry מיד אחרי build מוצלח.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 מומלץ | למה |
|---|---|---|
| ספרייה עם versioning | on: release | שליטה מלאה — מפרסמים רק כשמוכנים, עם release notes |
| app ל-deployment | on: push: branches: [main] | כל merge ל-main הוא deployable — continuous delivery |
| pre-release / beta | on: push: tags: ['v*-beta*'] | tag pattern שמבדיל בין stable ל-beta |
| monorepo עם כמה packages | on: push: paths: ['packages/X/**'] | מפרסם רק את ה-package שהשתנה, לא את כולם |
| לא בטוחים | on: release | הכי בטוח — רק release מכוון מפרסם. מומלץ להתחלה |
צרו workflow שמפרסם Docker image אוטומטית:
- צרו קובץ
.github/workflows/publish-image.ymlבריפוזיטורי - העתיקו את ה-workflow למעלה
- ודאו שיש
Dockerfileבשורש הריפוזיטורי (אם אין — צרו פשוט עםFROM nginx:alpine) - עשו commit ו-push לקובץ ה-workflow
- כנסו ל-GitHub ← Releases ← Draft a new release
- צרו tag חדש (למשל
v1.0.0), כתבו release notes, ולחצו Publish release - עברו לטאב Actions ועקבו אחרי ה-workflow — כל step צריך להיות ירוק
- בדקו שה-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:
- npm install נכשל עם 401: ה-
.npmrcלא מוגדר, או ה-NPM_TOKENלא נמצא. ודאו שה-environment variable קיים:echo $NPM_TOKEN(צריך להדפיס ערך). - npm install מחפש ב-npmjs.com במקום ב-GitHub: ה-scope ב-
.npmrcלא תואם לשם ה-package. ודאו שהשורה@your-org:registry=...מתאימה ל-scope ב-package.json. - docker pull נכשל עם "manifest unknown": ה-tag לא קיים ב-registry. בדקו את ה-tag הנכון בעמוד ה-package ב-GitHub.
- docker pull איטי מאוד: layers גדולים. שקלו multi-stage build שמקטין את ה-image, או base image קטן יותר (alpine במקום debian).
שימוש ב-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. בואו נפרק את זה.
| תוכנית | Storage | Bandwidth (חודשי) | מחיר חריגה — Storage | מחיר חריגה — Bandwidth |
|---|---|---|---|---|
| Free | 500 MB | 1 GB | $0.25/GB | $0.50/GB |
| Pro | 2 GB | 10 GB | $0.25/GB | $0.50/GB |
| Team | 2 GB | 10 GB | $0.25/GB | $0.50/GB |
| Enterprise | 50 GB | 100 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.
מה חינמי לגמרי (לא נספר לגבול):
- Public packages — storage ו-bandwidth חינמיים לחלוטין, בכל הרגיסטרים. אם אפשר להפוך ל-public — עשו את זה.
- Container registry (ghcr.io) — חינמי לחלוטין נכון לאפריל 2026, גם storage וגם bandwidth, גם ל-private images. GitHub התחייבו להודיע חודש מראש לפני שינוי מדיניות. זו הסיבה שאנחנו ממליצים על ghcr.io כברירת מחדל.
- Bandwidth בתוך Actions — משיכת packages בתוך workflow לא נספרת ל-bandwidth. רק downloads מחוץ ל-Actions נספרים.
הגדרת spending limit: כדי למנוע חיוב בלתי צפוי, אפשר להגדיר spending limit ב-Settings → Billing → Spending limits. ברירת מחדל: $0 — כלומר, אם חורגים מה-free tier, ה-packages ייחסמו (לא ייגבה תשלום). אפשר להעלות את הגבול אם רוצים לשלם, או להשאיר ב-$0 ולהסתמך על ניקוי.
דוגמה מעשית — חישוב עלות:
צוות עם 3 מפתחים, תוכנית Free:
- 4 npm packages פרטיים, ממוצע 2MB לגרסה, 4 גרסאות בשבוע = 32MB/חודש
- Actions caches: 200MB
- Actions artifacts: 50MB
- סה"כ: 282MB מתוך 500MB — בסדר, אבל קרוב
- פתרון: Docker images ב-ghcr.io (לא נספרים), ניקוי גרסאות npm ישנות שבועי
טיפ מעשי: אם יש לכם Docker images ואתם קרובים לגבול ה-storage — אל תשלמו. העבירו ל-ghcr.io. Docker images ב-Container registry לא נספרים ל-quota. אם יש לכם npm packages שאפשר להפוך ל-public — גם הם יפסיקו לצרוך storage.
Container registry חינמי — אבל לא בהכרח לנצח. נכון לאפריל 2026, ghcr.io חינמי לחלוטין. GitHub התחייבו להודיע חודש מראש לפני שינוי מדיניות. אם אתם בונים תהליך שתלוי ב"חינמי" — היו מוכנים לשינוי. בינתיים, תהנו מזה — אבל אל תבנו storage strategy שתתמוטט אם ghcr.io יתחיל לעלות כסף.
חשבו את העלות החודשית של Packages עבור הפרויקט שלכם. מלאו את הטבלה:
- כמה packages יש לכם? ____ npm, ____ Docker, ____ אחר
- גודל ממוצע לגרסה: ____ MB
- כמה גרסאות חדשות בחודש: ____
- סוג packages: public / private
- storage מצטבר חודשי: ____ MB
- האם Container images? כן → חינמי (לא נספר). לא → ראו טבלה למעלה
- כמה מתוך ה-free tier ה-Actions שלכם צורכים? ____ MB (בדקו ב-Billing)
- נשאר ל-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 עושה:
min-versions-to-keep: 10— שומר את 10 הגרסאות האחרונות, מוחק את השאר. בחרו מספר שמתאים לצרכים שלכם — 5 לפרויקט קטן, 20 לפרויקט גדול.delete-only-untagged-versions: true— מוחק רק versions בלי tag. ב-Docker, build intermediates ו-layers ישנים נשארים בלי tag — הם בטוחים למחיקה.- מקסימום 100 גרסאות נמחקות בהרצה אחת (מגבלת API). אם יש 500 גרסאות לנקות, ה-workflow יצטרך לרוץ 5 פעמים.
- תומך בכל סוגי ה-packages: container, npm, maven, nuget, rubygems
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 שנמחק ניתן לשחזור תוך 30 יום — כל עוד ה-namespace לא נתפס מחדש. אין צורך לפחד ממחיקה בטעות.
- npm: מגבלת 1,000 גרסאות. מעל 1,000 גרסאות ל-package, תתחילו לראות בעיות ביצועים בעמוד ה-package וב-API. נקו לפני שמגיעים לשם.
- Container images: untagged versions (בלי tag כמו
latestאוv1.0) הם בדרך כלל שאריות build שאפשר למחוק בבטחה. tagged versions ששמשו production — שימרו. - Org-level cleanup: ב-organizations, אפשר להגדיר cleanup workflow אחד שמנקה packages מכמה ריפוזיטורים. צריך PAT עם
delete:packagesו-org access.
ניקוי ידני מהממשק: אם צריך למחוק גרסה ספציפית, כנסו לעמוד ה-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". זה דורש יותר קוד, אבל נותן שליטה מלאה.
אסטרטגיית ניקוי מומלצת:
- Container images: השאירו 10-20 tagged versions אחרונים. מחקו את כל ה-untagged. הריצו שבועית.
- npm packages: השאירו 5-10 versions אחרונים. מחקו pre-releases ישנים. הריצו חודשית.
- לפני release: הריצו cleanup ידני (workflow_dispatch) כדי לפנות storage לפני פרסום גרסה חדשה כבדה.
- מעקב: בדקו ב-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
- מה ההבדל בין Granular permissions ל-Repo-scoped permissions? תנו דוגמה למצב שבו Granular חשוב.
- למה
GITHUB_TOKENעדיף על PAT ב-Actions workflows? מתי בכל זאת צריך PAT? - מה קורה אם מפרסמים npm package בלי scope (
@owner/) וגם בליpublishConfig? - למה Container registry (ghcr.io) שווה שימוש גם אם יש לכם Docker Hub? תנו שלוש סיבות.
- איך ה-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 (Actions): CI — build, test, lint על כל PR
- פרק 2 (Repos): ניהול קוד — branches, PRs, reviews
- פרק 5 (Projects): ניהול פרויקט — boards, milestones, tracking
- פרק 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 שנבנה עליו.