Menu

go mod init ומודולים ב-Go: go.mod, go get, go mod tidy

איך מודולים ב-Go עובדים: יצירת מודול עם go mod init, בחירת נתיב מודול, הוספת תלויות עם go get, ניקוי עם go mod tidy, למה משמש go.sum, ו-replace לפיתוח מקומי.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

מודול הוא עץ תיקיות של חבילות Go עם קובץ go.mod בשורש שלו. go.mod נותן שם למודול ומפרט את הגרסאות של כל מודול אחר שהוא תלוי בו. יוצרים מודול עם go mod init:

mkdir myapp
cd myapp
go mod init example.com/myapp
go: creating new go.mod: module example.com/myapp

ה-go.mod שנוצר:

module example.com/myapp

go 1.24.5

זה מספיק כדי לבנות. כל פרויקט Go החל מ-Go 1.16 הוא מודול, ו-go build, go run . ו-go test מסרבים לעבוד בלעדיו:

go: go.mod file not found in current directory or any parent directory; see 'go help modules'

בחירת נתיב מודול

נתיב המודול הוא הקידומת של כל נתיב import בתוך המודול. עם המודול example.com/myapp, חבילה בתיקייה internal/store מיובאת כ-example.com/myapp/internal/store.

מצבנתיב מודול
קוד שמתארח ב-GitHub ואחרים עשויים לייבאgithub.com/yourname/project
הקוד של החברה שלכםyourcompany.com/project או ה-URL של המאגר
תוכנית פרטית או תרגילexample.com/myapp או פשוט myapp
גרסה 2 ומעלה של מודול שפורסםgithub.com/yourname/project/v2

בספרייה, הנתיב חייב להתאים למקום שבו הקוד נמצא, כי go get משתמשת בו כדי למצוא את המאגר. בתוכנית שרק אתם מריצים, הנתיב הוא רק שם. הימנעו ממילה בודדת שזהה לשם של חבילה בספרייה הסטנדרטית, כמו go mod init fmt או go mod init strings: הבנייה נכשלת אז עם ambiguous import: found package fmt in multiple modules.

הרצת go mod init בלי ארגומנט נכשלת מחוץ למבנה ה-GOPATH הישן:

go: cannot determine module path for source directory /home/ana/myapp (outside GOPATH, module path must be specified)

תנו לה נתיב.

הוספת תלויות

כתבו את ה-import, ואז תנו ל-Go להוריד אותו. נניח ש-main.go מייבא את github.com/google/uuid. בנייה לפני שהמודול מכיר אותו נותנת הוראה ברורה:

main.go:6:2: no required module provides package github.com/google/uuid; to add it:
	go get github.com/google/uuid

כל אחת מהפקודות האלה פותרת את זה:

go get github.com/google/uuid
# or, to sync go.mod with every import in the module:
go mod tidy

go get מדווחת מה היא שינתה:

go: downloading github.com/google/uuid v1.6.0
go: added github.com/google/uuid v1.6.0

go mod tidy מדווחת איך היא פתרה את ה-import:

go: finding module for package github.com/google/uuid
go: downloading github.com/google/uuid v1.6.0
go: found github.com/google/uuid in github.com/google/uuid v1.6.0

ב-go.mod יש עכשיו שורת require:

module example.com/myapp

go 1.24.5

require github.com/google/uuid v1.6.0

go get עם גרסאות

פקודההשפעה
go get pkgהוספת pkg בגרסה האחרונה שלו, או שמירה על הגרסה הנוכחית אם הוא כבר נדרש
go get pkg@v1.5.0שימוש בדיוק ב-v1.5.0 (שדרוג או שנמוך)
go get pkg@latestמעבר לגרסה האחרונה
go get pkg@abc1234שימוש ב-commit מסוים (נרשם כ-pseudo-version)
go get -u ./...שדרוג כל תלות לגרסת ה-minor או ה-patch האחרונה שלה
go get -u=patch ./...שדרוג לגרסאות ה-patch האחרונות בלבד
go get pkg@noneהסרת הדרישה
go get go@1.24העלאת גרסת Go המינימלית של המודול

go get משנה את go.mod. היא כבר לא בונה או מתקינה תוכניות; החל מ-Go 1.18 זה התפקיד של go install pkg@version.

תלויות עקיפות

דרישות שמסומנות // indirect הן מודולים שהקוד שלכם לא מייבא ישירות אבל הבנייה צריכה. החל מ-Go 1.17, go.mod מפרט כל מודול שמספק חבילה לבנייה, כך שהתלויות של התלויות שלכם מופיעות כאן עם הסימון הזה. go mod tidy מנהלת את הסימונים האלה; אתם לא מוסיפים אותם בעצמכם.

go mod tidy

הריצו go mod tidy בכל פעם שאתם מוסיפים או מסירים imports. הפקודה:

  • מוסיפה דרישות עבור חבילות מיובאות שחסרות,
  • מסירה דרישות ששום דבר כבר לא מייבא,
  • מוסיפה את רשומות ה-go.sum שהבנייה צריכה ומסירה רשומות ישנות.

היא בודקת כל חבילה במודול, כולל בדיקות וכל שילוב של build tags, ולכן לפעמים היא שומרת תלות שאתם לא רואים שימוש בה בפלטפורמה שלכם. הרצת go mod tidy לפני כל commit, ובדיקה ב-CI שהיא לא מייצרת diff, שומרות על go.mod מדויק.

go.sum

go.sum רושם hash לכל גרסת מודול שהבנייה משתמשת בה:

github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=

השורה הראשונה היא hash של הקבצים של המודול, והשנייה רק של ה-go.mod שלו. כשהפקודה go מורידה מודול, היא בודקת את ה-hash מול go.sum ומול מסד ה-checksums הציבורי (sum.golang.org), כך שגרסה ששונתה בזדון או בשקט מכשילה את הבנייה. עשו commit ל-go.sum לצד go.mod, ואף פעם אל תערכו אותו ידנית.

איך Go בוחרת גרסאות

Go משתמשת ב-minimal version selection. כל מודול מפרט את הגרסה המינימלית של כל תלות שהוא צריך, והבנייה משתמשת בגבוהה מבין המינימומים האלה, אף פעם לא במשהו חדש יותר. אם המודול שלכם דורש uuid v1.5.0 ותלות דורשת uuid v1.6.0, הבנייה משתמשת ב-v1.6.0, גם אם v1.7.0 קיימת. שום דבר לא משתדרג אלא אם מישהו מבקש זאת עם go get.

התוצאה היא שבניות ניתנות לשחזור בלי קובץ lock: go.mod יחד עם גרף התלויות קובעים לחלוטין כל גרסה. go list -m all מדפיסה את התוצאה:

go list -m all
example.com/myapp
github.com/google/uuid v1.6.0

גרסאות major

מודול בגרסה v2 ומעלה חייב לכלול את גרסת ה-major בנתיב שלו: github.com/yourname/project/v2. נתיב ה-import משתנה יחד איתו, כך ש-project ו-project/v2 הם מודולים שונים ויכולים להיות שניהם באותה בנייה. זה הכלל של Go לשינויים שוברים, ובגלל זה רואים imports כמו github.com/jackc/pgx/v5.

replace: עבודה מקומית על תלות

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

module example.com/myapp

go 1.24.5

require example.com/mylib v1.2.0

replace example.com/mylib => ../mylib

או משורת הפקודה:

go mod edit -replace example.com/mylib=../mylib
go mod tidy

התיקייה חייבת להכיל go.mod משלה. replace חל רק כשבונים את המודול הזה ישירות, לא כשמישהו אחר תלוי בו, אז זכרו להסיר אותו לפני שאתם מתייגים גרסה.

כדי לערוך כמה מודולים בבת אחת בלי לגעת בקובצי ה-go.mod שלהם, Go 1.18 הוסיפה workspaces:

go work init . ../mylib

זה כותב קובץ go.work שגורם ל-mylib המקומי לקבל עדיפות. השאירו את go.work מחוץ לניהול הגרסאות, אלא אם כל הצוות משתמש באותו מבנה.

תלויות של כלים (Go 1.24)

Go 1.24 הוסיפה הנחיית tool, כך שמחוללי קוד ו-linters יכולים להיות מנוהלי גרסאות ב-go.mod במקום להיות מותקנים גלובלית:

go get -tool golang.org/x/tools/cmd/stringer
go tool stringer -type=Color

func init זה משהו אחר

חיפושים של "golang init" מתכוונים לעיתים קרובות לפונקציה init, שאין לה קשר ל-go mod init. כל חבילה יכולה להצהיר על func init(). היא לא מקבלת ארגומנטים, הקוד שלכם לא יכול לקרוא לה, והיא רצה פעם אחת, אוטומטית, אחרי שהמשתנים ברמת החבילה מוצבים ולפני main:

בקובץ יכולות להיות כמה פונקציות init, והן רצות לפי סדר הופעתן. חבילות מיובאות מסיימות קודם את האתחול שלהן. שמרו על init קטנה: עבודה שיכולה להיכשל קלה יותר לטיפול ולבדיקה כפונקציה רגילה שנקראת מ-main.

מודולים פרטיים ו-proxies

כברירת מחדל go מורידה מודולים דרך proxy.golang.org. ה-proxy הזה לא רואה מאגרים פרטיים, ולכן אמרו ל-Go אילו נתיבים פרטיים:

go env -w GOPRIVATE=github.com/yourcompany/*

מודולים שמתאימים ל-GOPRIVATE מורדים ישירות מהמאגר עם הרשאות ה-git שלכם ומדלגים על מסד ה-checksums.

שאלות נפוצות

מה עושה go mod init?

הפקודה יוצרת קובץ go.mod בתיקייה הנוכחית, מה שהופך את התיקייה הזאת לשורש של מודול. go mod init example.com/myapp כותבת את נתיב המודול ואת גרסת Go:

module example.com/myapp

go 1.24.5

הריצו אותה פעם אחת לכל פרויקט, לפני go build או go get.

במה להשתמש כנתיב המודול ב-go mod init?

בכתובת שממנה הקוד יורד אם אחרים מייבאים אותו, בדרך כלל נתיב המאגר: go mod init github.com/yourname/project. לתוכנית שאף אחד לא ייבא, כל שם עובד (go mod init myapp), אבל נתיב בסגנון דומיין עם נקודה כמו example.com/myapp מונע התנגשות עם שמות של חבילות בספרייה הסטנדרטית.

מה ההבדל בין go get ל-go mod tidy?

go get pkg@version מוסיפה תלות או משנה את הגרסה שלה. go mod tidy קוראת את קובצי המקור שלכם, מוסיפה כל מודול שה-imports שלכם צריכים וחסר ב-go.mod, מסירה דרישות ששום דבר לא מייבא, ומעדכנת את go.sum. זרימה נפוצה היא לכתוב את ה-import ואז להריץ go mod tidy.

האם לעשות commit ל-go.sum?

כן. go.sum מחזיק hashes קריפטוגרפיים של כל גרסת מודול שהבנייה שלכם משתמשת בה. commit שלו מאפשר לפקודה go לוודא שכולם מורידים תלויות זהות עד לרמת הבית. אף פעם אל תערכו אותו ידנית; go mod tidy מתחזקת אותו.

האם "golang init" זה אותו דבר כמו go mod init?

לא, אלה שני דברים שונים שחולקים מילה. go mod init היא פקודת טרמינל שיוצרת go.mod. func init() היא פונקציה שכותבים בקוד Go; היא רצה אוטומטית פעם אחת, לפני main, אחרי שהמשתנים של החבילה מאותחלים.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל