2 שלב הבסיס

GitHub CLI — שליטה מלאה מהטרמינל

בפרק הזה תגלו שיש עולם שלם מעבר ל-gh pr create. תלמדו ליצור aliases מותאמים, לגשת ישירות ל-API של GitHub, לסנן JSON עם JQ, לבנות סקריפטים אוטומטיים, וליצור extensions משלכם. בסוף הפרק — כל מה שעושים ב-GitHub.com תעשו מהטרמינל, מהר יותר ועם שליטה מלאה.

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

מאיפה באנו: בפרק 1 (Actions) בנינו CI/CD workflows שרצים אוטומטית. מה הפרק הזה מוסיף: שליטה מהטרמינל — ניטור של אותם workflows בזמן אמת עם gh run, ניהול PRs ו-issues בלי דפדפן, ואוטומציות scripting שמקשרות הכל. לאן ממשיכים: ה-aliases והסקריפטים שתבנו פה ילוו אתכם לאורך כל הקורס. בפרק 3 (Pages) נשתמש ב-CLI לפריסה, ובפרק 8 (API & Webhooks) נרחיב את יכולות ה-gh api שנלמד כאן.

מילון מונחים
CLI
Command Line Interface — ממשק שורת פקודה. GitHub CLI (gh) הוא הכלי הרשמי של GitHub לטרמינל
Alias
קיצור מותאם לפקודה ארוכה — נוצר עם gh alias set. חוסך הקלדה חוזרת
Shell Alias
alias שמריץ פקודת shell מלאה עם pipes, לולאות ולוגיקה — נוצר עם gh alias set --shell
JQ
שפת שאילתות ל-JSON — מסננת, ממפה ומעצבת נתונים. מובנית ב-gh דרך --jq
REST API
ממשק תכנותי מבוסס HTTP — קריאות GET, POST, PATCH, DELETE ל-endpoints ספציפיים
GraphQL
שפת שאילתות ל-API — מבקשים בדיוק את השדות שצריכים, בקריאה אחת
Extension
תוסף ל-gh שמרחיב יכולות — מותקן מהקהילה או שבניתם בעצמכם
PAT
Personal Access Token — מפתח אישי לגישה ל-API. ב-gh, ה-token מנוהל אוטומטית
Rate Limit
מגבלת קריאות ל-API — 5,000 קריאות לשעה עם אימות. בלי אימות: 60
Pagination
חלוקת תוצאות לדפים כשיש הרבה תוצאות. ב-gh: --paginate מטפל אוטומטית
OAuth
פרוטוקול אימות שמאפשר גישה ל-API בלי חשיפת סיסמה — כך gh auth עובד
Pipe
העברת פלט מפקודה אחת לקלט של אחרת עם | — הבסיס לשרשור פקודות בטרמינל

2.1 מעבר לבסיס — מה רוב המפתחים לא יודעים

שאלו מפתח ממוצע מה הוא יודע על GitHub CLI ותקבלו שתי פקודות: gh pr create ו-gh repo clone. אולי גם gh pr view אם הוא מתקדם. זה כמו להכיר רק Copy ו-Paste ב-Excel ולחשוב שמיצית את הפוטנציאל.

האמת היא שמתחת לשטח יש קרחון שלם של יכולות שרוב המפתחים אף פעם לא נוגעים בהן. GitHub CLI עבר מהפכה שקטה בשנים האחרונות — מכלי פשוט ליצירת PRs לפלטפורמה מלאה שמאפשרת לעשות כמעט כל דבר שאפשר לעשות ב-GitHub.com, ולעשות אותו מהר יותר:

הנתון הכי מפתיע: לפי הסקר השנתי של GitHub, רק כ-15% מהמפתחים שמשתמשים ב-gh CLI ממצים יותר מ-20% מהיכולות שלו. ב-85% המפתחים, השימוש מוגבל לפקודות הבסיסיות. הפער הזה הוא ההזדמנות שלכם.

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

עשו עכשיו

לפני שמתחילים — בדקו שאתם מוכנים. הריצו את שלוש הפקודות הבאות:

# בדיקת גרסה — צריך 2.60 ומעלה
gh --version

# בדיקת אימות — צריך להיות logged in
gh auth status

# רשימת aliases — כנראה ריק. בקרוב נמלא
gh alias list

אם gh לא מותקן: brew install gh (macOS), winget install GitHub.cli (Windows), או sudo apt install gh (Linux). אחרי ההתקנה: gh auth login ועקבו אחרי ההוראות.

אם הגרסה ישנה: gh upgrade (או brew upgrade gh / winget upgrade GitHub.cli).

מה נכסה בפרק הזה

הפרק בנוי בצורה הדרגתית — מתחילים מדברים פשוטים ומתקדמים:

  1. Aliases (סעיף 2.2) — נתחיל מהקל ביותר. קיצורים מותאמים שאפשר ליצור ב-10 שניות ולהשתמש מיד. שלוש רמות — מפשוט למורכב.
  2. gh api (סעיף 2.3) — נפתח את הדלת לכל ה-API של GitHub. REST ו-GraphQL, עם דוגמאות מעשיות שתוכלו להריץ מיד.
  3. JQ (סעיף 2.4) — נלמד לסנן JSON כמו מקצוענים. ספר המתכונים שנבנה כאן ילווה אתכם הרבה אחרי הפרק.
  4. PR, Issue, Repo (סעיף 2.5) — מחזורי חיים מלאים מהטרמינל. כל מה שעושים ב-GitHub.com, ב-CLI.
  5. gh run (סעיף 2.6) — ניטור CI/CD בזמן אמת. חיבור ישיר למה שלמדנו בפרק 1.
  6. Scripting (סעיף 2.7) — שם הכל מתחבר. סקריפטים שמשלבים את כל מה שלמדנו לאוטומציות אמיתיות.
  7. Extensions (סעיף 2.8) — תוספים מהקהילה ויצירת extensions משלכם.
  8. אוטומציות מורכבות (סעיף 2.9) — שני תרחישים מהעולם האמיתי שמשלבים הכל.

מוכנים? בואו נתחיל מהדבר שישנה לכם את החיים מיד — aliases.

2.2 Aliases — קיצורים מותאמים אישית

alias ב-gh הוא בדיוק מה שנשמע: שם קצר שמחליף פקודה ארוכה. אבל ב-gh יש שלוש רמות של aliases, מפשוט למורכב, וההבדל ביניהן הוא קריטי.

רמה 1 — alias פשוט

ברמה הבסיסית ביותר, alias הוא פשוט שם חדש לפקודה קיימת. זה חוסך הקלדה ומפשט פקודות ארוכות:

# הפקודה המקורית — 42 תווים:
gh pr list --state open --assignee @me

# אחרי alias — 10 תווים:
gh alias set my-prs 'pr list --state open --assignee @me'
gh my-prs

# עוד דוגמאות:
gh alias set my-issues 'issue list --assignee @me --state open'
gh alias set repos 'repo list --limit 20 --sort updated'

זה פשוט, אבל כבר חוסך זמן משמעותי ביום-יום. כל פקודה שאתם מריצים יותר מפעמיים ביום צריכה להיות alias.

רמה 2 — alias עם פרמטרים

פרמטרים הופכים את ה-alias לגמיש — אתם מעבירים ארגומנטים שמשתנים בכל הרצה:

# $1 הוא הארגומנט הראשון שתעבירו
gh alias set pr-for 'pr list --search "author:$1"'

# שימוש:
gh pr-for octocat       # PRs של octocat
gh pr-for nadav         # PRs של nadav

# alias עם שני פרמטרים:
gh alias set find-issue 'issue list --label "$1" --state "$2"'

# שימוש:
gh find-issue bug open           # issues פתוחים עם label "bug"
gh find-issue enhancement closed # issues סגורים עם label "enhancement"

הפרמטרים $1, $2, $3 מחליפים את הארגומנטים שתעבירו. זה מספיק לרוב המקרים.

רמה 3 — shell alias עם pipes ולוגיקה

הרמה הכי חזקה. --shell אומר ל-gh שהפקודה היא shell script, לא פקודת gh רגילה. זה פותח דלת לכל מה ש-shell יכול לעשות — pipes, לולאות, תנאים, ופקודות חיצוניות:

# alias שמריץ shell command מלא
gh alias set --shell stale-issues \
  'gh issue list --json number,title,updatedAt \
    --jq ".[] | select(now - (.updatedAt | fromdateiso8601) > 30*24*3600) | \"#\(.number) \(.title)\""'

# alias שמשלב כמה פקודות gh
gh alias set --shell pr-stats \
  'echo "Open PRs: $(gh pr list --json number --jq \"length\")"
   echo "My PRs: $(gh pr list --json number --assignee @me --jq \"length\")"
   echo "Draft PRs: $(gh pr list --json isDraft --jq \"[.[] | select(.isDraft)] | length\")"'

# alias עם תנאי
gh alias set --shell safe-merge \
  'PR=$1
   STATUS=$(gh pr checks "$PR" --json state --jq ".[].state" | sort -u)
   if echo "$STATUS" | grep -q "FAILURE"; then
     echo "Cannot merge — CI failed"
   else
     gh pr merge "$PR" --squash --delete-branch
   fi'

shell aliases הם חזקים, אבל דורשים זהירות. כל טעות בתחביר shell תגרום ל-alias לא לעבוד, וה-debugging לא תמיד פשוט. כלל אצבע:

ניהול aliases

ה-aliases שלכם נשמרים בקובץ קונפיגורציה של gh. אפשר לנהל אותם כך:

# רשימת כל ה-aliases שהגדרתם
gh alias list

# מחיקת alias
gh alias delete my-prs

# ייבוא aliases מקובץ YAML (שיתוף עם צוות!)
gh alias import aliases.yml

# צפייה בהגדרת alias ספציפי
gh alias list | grep my-prs

טיפ למתקדמים — גיבוי ושיתוף aliases: כל ה-aliases נשמרים בקובץ ~/.config/gh/config.yml (או %APPDATA%\GitHub CLI\config.yml ב-Windows). אפשר לייצא אותם עם gh alias list, לשמור בקובץ YAML ב-dotfiles repo, ולייבא במכונה חדשה עם gh alias import aliases.yml. כך תוכלו:

דוגמה לקובץ aliases.yml:

# aliases.yml — Team aliases
my-prs: pr list --state open --assignee @me
my-issues: issue list --assignee @me --state open
repos: repo list --limit 20 --sort updated

חמשת ה-aliases שכל מפתח צריך

אלה ה-aliases שנשמרים אצלי ואני משתמש בהם כל יום:

# 1. PRs שמחכים לי
gh alias set my-prs 'pr list --state open --assignee @me'

# 2. יצירת PR מהירה
gh alias set --shell quick-pr \
  'gh pr create --title "$1" --body "Auto-created via CLI" --assignee @me'

# 3. צפייה ב-CI של ה-PR הנוכחי
gh alias set --shell ci \
  'gh pr checks $(gh pr view --json number --jq .number)'

# 4. ניקוי branches ממוזגים
gh alias set --shell cleanup \
  'git branch --merged main | grep -v main | xargs -r git branch -d && echo "Cleaned!"'

# 5. דוח יומי
gh alias set --shell daily \
  'echo "=== My Open PRs ==="
   gh pr list --assignee @me --json number,title --jq ".[] | \"#\(.number) \(.title)\""
   echo ""
   echo "=== My Open Issues ==="
   gh issue list --assignee @me --json number,title --jq ".[] | \"#\(.number) \(.title)\""'
עשו עכשיו

צרו את שלושת ה-aliases הראשונים שלכם. העתיקו והריצו:

# alias 1: PRs שלי
gh alias set my-prs 'pr list --state open --assignee @me'

# alias 2: issues שלי
gh alias set my-issues 'issue list --assignee @me --state open'

# alias 3: יצירת PR מהירה
gh alias set --shell quick-pr \
  'gh pr create --title "$1" --body "Auto-created via CLI" --assignee @me'

# בדקו שהם נוצרו:
gh alias list

אם הריפוזיטורי שלכם ריק, gh my-prs יחזיר רשימה ריקה. זה בסדר — העיקר שהפקודה עובדת בלי שגיאה.

עשו עכשיו

עכשיו shell alias שבודק CI status:

gh alias set --shell ci-status \
  'echo "Last 5 workflow runs:"
   gh run list --limit 5 --json status,name,conclusion \
     --jq ".[] | \"\(.name): \(.status) (\(.conclusion // \"running\"))\""'

# נסו:
gh ci-status

אם אין לכם workflows (לא עשיתם את פרק 1), תקבלו רשימה ריקה. זה בסדר.

אזהרה: שמות aliases שמתנגשים

לעולם אל תקראו ל-alias בשם של פקודה קיימת. gh alias set pr ... ידרוס את gh pr המקורי ותאבדו גישה לכל תת-הפקודות שלו. כלל אצבע: תמיד השתמשו בשמות ייחודיים עם מקף — my-prs, quick-pr, ci-status. בדקו תמיד עם gh help <name> שהשם לא תפוס לפני שיוצרים alias.

2.3 gh api — כל ה-API של GitHub בפקודה אחת

gh api הוא הכלי החבוי הכי חזק ב-gh. בזמן שרוב המפתחים משתמשים ב-gh בשביל פקודות מוכנות כמו gh pr list, מי שמכיר gh api יכול לגשת לכל דבר שה-GitHub API מציע — כולל endpoints שאין להם פקודת gh ייעודית.

למה gh api ולא curl?

בואו נשווה שליפת מידע על ריפוזיטורי ב-curl מול gh api:

# curl — ארוך, מסורבל, חושף token:
curl -H "Authorization: Bearer ghp_xxxxxxxxxxxx" \
     -H "Accept: application/vnd.github+json" \
     https://api.github.com/repos/octocat/Hello-World

# gh api — קצר, מאובטח, אוטומטי:
gh api repos/octocat/Hello-World

ההבדל דרמטי, וזה רק ההתחלה. הנה היתרונות המלאים:

REST API — דוגמאות בסיסיות

# קבלת מידע על ריפוזיטורי
gh api repos/{owner}/{repo}

# רשימת collaborators
gh api repos/{owner}/{repo}/collaborators

# יצירת issue
gh api repos/{owner}/{repo}/issues -f title="Bug report" -f body="Details here"

# עדכון issue (הוספת label)
gh api repos/{owner}/{repo}/issues/42/labels -f "labels[]=bug" -f "labels[]=priority"

# מחיקת branch
gh api repos/{owner}/{repo}/git/refs/heads/old-branch -X DELETE

# רשימת כל ה-releases
gh api repos/{owner}/{repo}/releases --paginate --jq '.[].tag_name'

POST, PATCH, DELETE — כתיבה ל-API

ברירת המחדל של gh api היא GET. לפעולות כתיבה, משתמשים ב-flags:

# POST — יצירה (ברירת מחדל כשיש -f)
gh api repos/{owner}/{repo}/issues \
  -f title="New issue" \
  -f body="Created via gh api" \
  -f "labels[]=enhancement"

# PATCH — עדכון
gh api repos/{owner}/{repo}/issues/42 \
  -X PATCH \
  -f state="closed" \
  -f state_reason="completed"

# PUT — הוספת collaborator
gh api repos/{owner}/{repo}/collaborators/username \
  -X PUT \
  -f permission="push"

# DELETE — מחיקה
gh api repos/{owner}/{repo}/issues/42/labels/bug -X DELETE
gh api — הזרימה gh api endpoint + אימות אוטומטי GitHub API REST / GraphQL JSON Response נתונים גולמיים --jq סינון פלט נקי הכל קורה בפקודה אחת — בלי curl, בלי token ידני, בלי headers

GraphQL עם gh api

GitHub מציע שני APIs: REST ו-GraphQL. gh api graphql מאפשר לשלוח שאילתות GraphQL שמחזירות בדיוק את מה שצריכים — בלי שדות מיותרים:

# שאילתת GraphQL בסיסית — 5 ריפוזיטורים אחרונים
gh api graphql -f query='
  query {
    viewer {
      login
      repositories(first: 5, orderBy: {field: UPDATED_AT, direction: DESC}) {
        nodes { name stargazerCount }
      }
    }
  }'

# שאילתה עם משתנים — חיפוש PRs של משתמש
gh api graphql -f query='
  query($owner: String!, $repo: String!) {
    repository(owner: $owner, name: $repo) {
      pullRequests(first: 10, states: OPEN) {
        nodes {
          number
          title
          author { login }
          createdAt
        }
      }
    }
  }' -f owner="{owner}" -f repo="{repo}"

מתי GraphQL ומתי REST? זה תלוי בצורך שלכם:

כלל אצבע: התחילו עם REST. עברו ל-GraphQL רק כשנתקלתם במגבלה — יותר מדי קריאות, שדות חסרים, או ביצועים איטיים.

מסגרת החלטה: gh api vs curl vs Octokit
קריטריוןgh apicurl + PATOctokit (SDK)
מתאים ל-שאילתות מהירות, scripting, אוטומציותdebugging, one-offs, CI environmentsאפליקציות, GitHub Apps, קוד production
אימותאוטומטי (gh auth)ידני (PAT ב-header)PAT או GitHub App credentials
JQ מובנהכן (--jq)לא (pipe ל-jq חיצוני)לא (עיבוד בקוד)
Pagination--paginate (אוטומטי)ידני (Link headers)אוטומטי (SDK method)
שפת תכנותShell / Bashכל שפהJavaScript / TypeScript / Ruby
מתי לבחורכשעובדים מהטרמינלכשאין gh זמיןכשבונים אפליקציה

כלל אצבע: אם אתם בטרמינל — gh api. אם אתם בקוד — Octokit. אם אתם ב-CI בלי gh — curl + PAT.

עשו עכשיו

הריצו את שלוש הקריאות הבאות ובדקו שאתם מקבלים תוצאות:

# 1. פרטי הריפוזיטורי שלכם (החליפו {owner}/{repo})
gh api repos/{owner}/{repo} --jq '{name: .name, stars: .stargazers_count, language: .language}'

# 2. רשימת branches
gh api repos/{owner}/{repo}/branches --jq '.[].name'

# 3. בדיקת rate limit — כמה קריאות נשארו
gh api rate_limit --jq '.rate | "Used: \(.used)/\(.limit), Reset: \(.reset | strftime("%H:%M:%S"))"'

שימו לב כמה מהיר זה לעומת פתיחת הדפדפן, ניווט לריפוזיטורי, וחיפוש המידע הזה.

אזהרה: Rate limits

יש מגבלת 5,000 קריאות API לשעה עם אימות (ו-60 בלי אימות). זה נשמע הרבה, אבל סקריפט עם לולאה שרצה על 500 issues כבר אוכל 500 קריאות. הוסיפו sleep 0.5 בין קריאות בלולאות, או השתמשו ב-GraphQL שאוסף כמה שדות בקריאה אחת. תמיד בדקו עם gh api rate_limit --jq '.rate' כמה קריאות נשארו.

2.4 JQ — סינון ופרסור JSON כמו קסם

כמעט כל פלט של gh הוא JSON. בלי סינון, תקבלו מסה של נתונים שקשה לקרוא. JQ היא השפה שמסננת, ממפה ומעצבת JSON — וב-gh היא מובנית ישירות דרך --jq.

חשבו על JQ כ-SQL של JSON. כמו ש-SQL מאפשר לשלוף, לסנן ולמיין נתונים ממסד נתונים, JQ עושה את אותו הדבר עם JSON. ברגע שתלמדו 5-6 דפוסים בסיסיים, תוכלו לענות על כמעט כל שאלה שיש לכם על הנתונים שחוזרים מ-GitHub.

שתי דרכים לעבוד עם JQ ב-gh

דרך 1: --jq (מובנה — הכי נפוצה ומומלצת):

# שמות כל ה-PRs הפתוחים
gh pr list --json title --jq '.[].title'

# PRs שנוצרו על ידי משתמש ספציפי
gh pr list --json title,author --jq '.[] | select(.author.login == "octocat") | .title'

# פורמט מותאם — מספר + כותרת
gh pr list --json number,title --jq '.[] | "#\(.number) \(.title)"'

דרך 2: --json + pipe ל-jq חיצוני:

# כשצריכים JQ מתקדם יותר (sort_by, group_by, וכו')
gh pr list --json title,createdAt | jq 'sort_by(.createdAt) | reverse | .[0:5]'

# קיבוץ issues לפי label
gh issue list --json number,labels | jq 'group_by(.labels[0].name) | .[] | {label: .[0].labels[0].name, count: length}'

מתי --jq ומתי pipe? בשביל סינון בסיסי, --jq מספיק ועדיף (פחות תלויות). כש-pipe ל-jq חיצוני, יש גישה לפונקציות מתקדמות יותר כמו group_by ו-env.

ספר המתכונים — דפוסי JQ חיוניים

אלה הדפוסים שתשתמשו בהם שוב ושוב:

# 1. בחירת שדה אחד מכל אלמנט
--jq '.[].name'

# 2. בחירת כמה שדות
--jq '.[] | {name: .name, stars: .stargazers_count}'

# 3. סינון עם select
--jq '.[] | select(.state == "open")'
--jq '.[] | select(.labels | length > 0)'
--jq '.[] | select(.title | test("fix"; "i"))'     # regex case-insensitive

# 4. פורמט מותאם (string interpolation)
--jq '.[] | "#\(.number): \(.title) [\(.author.login)]"'

# 5. ספירה
--jq 'length'                                        # כמה אלמנטים
--jq '[.[] | select(.state == "open")] | length'     # כמה פתוחים

# 6. מיון
--jq 'sort_by(.stargazerCount) | reverse'            # מיון יורד
--jq 'sort_by(.createdAt)'                           # מיון לפי תאריך

# 7. חיתוך — רק N ראשונים
--jq '.[0:5]'                                        # 5 ראשונים
--jq 'sort_by(.updatedAt) | reverse | .[0:3]'        # 3 אחרונים

# 8. שרשור פעולות
--jq '[.[] | select(.state == "open")] | sort_by(.createdAt) | .[0] | "#\(.number) \(.title)"'
JQ — מ-JSON גולמי לפלט נקי [{"title":"Fix bug", "state":"open", "number":42}, ...] JSON גולמי select(.state=="open") | "#\(.number) \(.title)" #42 Fix bug #38 Add feature פלט נקי
מסגרת החלטה: --jq vs --json vs --template
דרךמתי להשתמשדוגמהיתרונות
--jqסינון מובנה — רוב המקרים--jq '.[].name'מהיר, בלי תלויות חיצוניות
--json + pipe ל-jqעיבוד מתקדם (group_by, env)--json fields | jq '...'גישה לכל פונקציות jq
--templateפורמט טקסט מורכב (Go templates)--template '{{.Name}}'שליטה מלאה בפורמט
--json (בלי סינון)debugging — רואים הכל--json number,titleרואים את כל השדות הזמינים

כלל אצבע: התחילו עם --jq. עברו ל-pipe + jq רק כשנתקלתם במגבלה. --template כמעט אף פעם לא צריך.

עשו עכשיו

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

# שלב 1: כל ה-PRs הפתוחים — רק מספר ושם
gh pr list --json number,title --jq '.[] | "#\(.number) \(.title)"'

# שלב 2: כמה PRs פתוחים יש?
gh pr list --json number --jq 'length'

# שלב 3: ה-PR הכי ישן (לפי תאריך יצירה)
gh pr list --json number,title,createdAt --jq 'sort_by(.createdAt) | .[0] | "#\(.number) \(.title) (created: \(.createdAt[:10]))"'

אם אין PRs, נסו על issues: gh issue list --json ...

עשו עכשיו

שאילתה מתקדמת — הריפוזיטורים שלכם מדורגים לפי כוכבים:

gh api user/repos --paginate --jq \
  'sort_by(.stargazers_count) | reverse | .[:5] | .[] | "\(.full_name): \(.stargazers_count) stars"'

נסו גם: gh api user/repos --paginate --jq '[.[] | select(.language == "JavaScript")] | length' — כמה ריפוזיטורים של JavaScript יש לכם?

2.5 gh pr, gh issue, gh repo — השליטה המלאה

שלוש קבוצות הפקודות המרכזיות של gh CLI הן pr, issue ו-repo. כל אחת מנהלת מחזור חיים שלם — מיצירה ועד סגירה. אלה הפקודות שתשתמשו בהן הכי הרבה ביום-יום, הרבה יותר מ-gh api או scripting. נעבור על כל קבוצה בפירוט, עם כל הפקודות החשובות, flags שימושיים, ודוגמאות מעשיות שתוכלו להריץ מיד.

gh pr — מחזור חיים מלא של Pull Request

זו קבוצת הפקודות שהכי משתמשים בה ביום-יום. כל שלב במחזור החיים של PR — מיצירה, דרך review, ועד merge — אפשר לעשות מהטרמינל. זה לא רק יותר מהיר — זה גם מאפשר אוטומציה. מה שלוקח 2 דקות בדפדפן (פתיחת tab, ניווט לריפו, לחיצה על New PR, מילוי פרטים, לחיצה על Create) לוקח 5 שניות ב-CLI.

מחזור חיים מלא של PR:

# שלב 1: יצירת PR
gh pr create --title "feat: add login page" --body "Description of changes"
gh pr create --fill                    # מלא אוטומטית מ-commits
gh pr create --draft                   # יצירת draft PR
gh pr create --reviewer user1,user2    # עם reviewers מיידית
gh pr create --label "feature" --milestone "v2.0"  # עם label ו-milestone

# שלב 2: סקירה וניהול
gh pr list --state open                # רשימת PRs פתוחים
gh pr view 42                          # צפייה ב-PR ספציפי
gh pr view 42 --web                    # פתיחה בדפדפן
gh pr checkout 42                      # checkout ל-branch של ה-PR
gh pr diff 42                          # צפייה בשינויים
gh pr checks 42                        # סטטוס CI

# שלב 3: Code review
gh pr review 42 --approve              # אישור
gh pr review 42 --request-changes --body "Please fix X"  # בקשת שינויים
gh pr review 42 --comment --body "Looks good overall"     # הערה

# שלב 4: מיזוג
gh pr merge 42 --squash --delete-branch   # squash merge + מחיקת branch
gh pr merge 42 --rebase                    # rebase merge
gh pr merge 42 --merge                     # merge commit

# פעולות נוספות
gh pr ready 42                          # הפיכת draft ל-ready
gh pr edit 42 --add-label "priority"    # הוספת label
gh pr edit 42 --add-reviewer octocat    # הוספת reviewer
gh pr close 42                          # סגירה בלי merge

שימו לב לעוצמה של gh pr create --fill — הוא לוקח את ה-commit messages ומשתמש בהם ככותרת ותיאור של ה-PR אוטומטית. אם ה-commits שלכם כתובים טוב, זה חוסך את הצורך לכתוב תיאור מחדש.

טיפ נוסף: gh pr checkout 42 עושה את מה ש-git לבד לא יכול — הוא יודע לעשות checkout ל-PR מ-fork אחר, לא רק מ-branches מקומיים. זה שימושי במיוחד ל-maintainers שרוצים לבדוק PR מ-contributor חיצוני.

gh issue — ניהול issues מקצה לקצה

# יצירה
gh issue create --title "Bug: login fails" --label bug
gh issue create --title "Feature: dark mode" --label enhancement --assignee @me
gh issue create --template bug_report    # שימוש ב-issue template

# סינון וחיפוש
gh issue list --label bug --assignee @me     # סינון לפי label ו-assignee
gh issue list --state closed --limit 10      # 10 issues סגורים אחרונים
gh issue list --search "login in:title"      # חיפוש טקסט חופשי
gh issue list --milestone "v2.0"             # issues לפי milestone

# ניהול
gh issue view 15                             # צפייה
gh issue close 15 --reason completed         # סגירה עם סיבה
gh issue reopen 15                           # פתיחה מחדש
gh issue pin 15                              # הצמדה (pinning)
gh issue unpin 15                            # הסרת הצמדה
gh issue transfer 15 other-repo              # העברה לריפו אחר
gh issue edit 15 --add-label "in-progress"   # הוספת label
gh issue edit 15 --add-assignee user1        # הוספת assignee

# הערות
gh issue comment 15 --body "Working on this"  # הוספת הערה
gh issue develop 15 --base main               # יצירת branch עבור issue

שימו לב לפקודה gh issue develop 15 --base main — היא יוצרת branch חדש מקושר ל-issue. כשתפתחו PR מה-branch הזה, GitHub יקשר אותו אוטומטית ל-issue. כשה-PR ימוזג, ה-issue ייסגר אוטומטית. זה pattern חזק שחוסך עבודה ידנית.

עוד פקודה שרבים לא מכירים: gh issue pin מצמידה issue לראש הרשימה ב-GitHub.com. שימושי ל-issues חשובים שאתם רוצים שכולם יראו — כמו roadmap או contributing guidelines.

gh repo — ניהול ריפוזיטורים

# יצירה
gh repo create my-project --public --clone             # יצירה + clone
gh repo create my-project --private --add-readme       # עם README
gh repo create org/project --team developers           # בארגון, עם צוות

# Clone ו-Fork
gh repo clone owner/repo                                # clone
gh repo fork owner/repo --clone                         # fork + clone מקומי
gh repo fork owner/repo --clone --remote                # fork + clone + remote

# מידע וצפייה
gh repo view                                            # מידע על הריפו הנוכחי
gh repo view owner/repo                                 # מידע על ריפו אחר
gh repo view --web                                      # פתיחה בדפדפן

# ניהול
gh repo rename new-name                                 # שינוי שם
gh repo edit --default-branch main                      # שינוי branch ברירת מחדל
gh repo edit --enable-issues=false                      # כיבוי issues
gh repo archive                                         # ארכוב (הפיכה ל-readonly)
gh repo delete --confirm                                # מחיקה (!)

# רשימות
gh repo list                                            # הריפוזיטורים שלי
gh repo list --source --no-archived --limit 30          # רק מקור, לא ארכיון
gh repo list org-name --limit 50                        # ריפוזיטורים של ארגון
עשו עכשיו

בצעו מחזור PR מלא מהטרמינל — מיצירת branch ועד merge (או סגירה):

# 1. צרו branch חדש
git checkout -b test-cli-workflow

# 2. צרו קובץ ו-commit
echo "# CLI Workflow Test" > cli-test.md
git add cli-test.md
git commit -m "test: CLI workflow demo"

# 3. דחפו ל-remote
git push -u origin test-cli-workflow

# 4. צרו PR
gh pr create --title "test: CLI workflow demo" --body "Created entirely from the terminal"

# 5. בדקו סטטוס CI (אם יש workflow)
gh pr checks

# 6. צפו ב-PR בדפדפן לוידוא
gh pr view --web

# 7. סגרו את ה-PR (זה רק תרגול)
gh pr close --comment "Test completed"

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

אזהרה: פקודות הרסניות

חלק מהפקודות הן בלתי הפיכות. gh repo delete מוחק ריפוזיטורי לצמיתות — כולל כל ה-issues, PRs, wiki, ו-actions history. gh pr merge ממזג ואין undo פשוט. לפני כל פעולה הרסנית: בדקו עם gh pr view או gh repo view שאתם על הריפו/PR הנכון. אם זמין, הוסיפו --dry-run.

2.6 gh run — ניטור Actions מהטרמינל

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

הפקודות הבסיסיות

# רשימת הרצות אחרונות
gh run list                                   # כל ההרצות
gh run list --workflow ci.yml                  # רק workflow ספציפי
gh run list --limit 5                          # 5 אחרונות
gh run list --status failure                   # רק כשלונות
gh run list --branch main                      # רק ב-main

# צפייה בהרצה ספציפית
gh run view 12345                              # פרטי הרצה
gh run view 12345 --log                        # לוגים מלאים
gh run view 12345 --log-failed                 # לוגים רק של מה שנכשל
gh run view --web                              # פתיחה בדפדפן

# צפייה בזמן אמת — הפקודה הכי שימושית
gh run watch                                   # צפייה ב-run האחרון
gh run watch 12345                             # צפייה ב-run ספציפי

# הרצה מחדש
gh run rerun 12345                             # הרצה מחדש מלאה
gh run rerun 12345 --failed                    # רק jobs שנכשלו (!)
gh run rerun 12345 --debug                     # עם debug logging

# Artifacts
gh run download 12345                          # הורדת כל ה-artifacts
gh run download 12345 -n my-artifact           # artifact ספציפי

הזרימה האידיאלית — push, watch, fix, rerun

ככה נראה workflow טיפוסי של מפתח שמשתמש ב-gh run:

# 1. דחפתם קוד
git push origin main

# 2. מצאו את ה-run שהתחיל
gh run list --limit 1

# 3. צפו בו בזמן אמת
gh run watch

# 4. אם נכשל — ראו מה נכשל
gh run view --log-failed

# 5. תקנו את הקוד, דחפו שוב
git push origin main

# 6. או — הריצו מחדש רק את מה שנכשל
gh run rerun --failed

הקסם הוא ב-gh run watch — זה פותח תצוגה חיה בטרמינל שמתעדכנת כל כמה שניות, עם סטטוס של כל job. כמו לצפות ב-progress bar, אבל ב-CI/CD. כשכל ה-jobs עוברים, תראו סטטוס ירוק ותוכלו להמשיך לעבוד. אם משהו נכשל, gh run view --log-failed מראה לכם בדיוק מה קרה — בלי לנווט דרך ממשק הוובוי, ללחוץ על job, ואז על step, ואז לגלול. הכל מגיע ישירות לטרמינל.

עוד יתרון משמעותי: gh run rerun --failed מריץ מחדש רק את ה-jobs שנכשלו, לא את כל ה-workflow. אם יש לכם matrix build עם 10 jobs ו-2 נכשלו, זה מריץ רק את ה-2 — חוסך זמן ו-Actions minutes.

דפוסים מתקדמים — שילוב עם scripting

# alias שדוחף ומיד צופה
gh alias set --shell push-watch \
  'git push origin HEAD && sleep 2 && gh run watch'

# בדיקה אם ה-CI האחרון עבר
gh alias set --shell ci-ok \
  'STATUS=$(gh run list --limit 1 --json conclusion --jq ".[0].conclusion")
   if [ "$STATUS" = "success" ]; then
     echo "CI passed"
   else
     echo "CI failed: $STATUS"
     exit 1
   fi'
עשו עכשיו

אם יש לכם workflow מוגדר (מפרק 1), דחפו שינוי וצפו:

# דחפו שינוי (אפילו קטן)
echo "# Updated $(date)" >> README.md
git add README.md && git commit -m "docs: trigger CI" && git push

# מיד אחרי — צפו ב-run
gh run watch

תראו את ה-workflow רץ בזמן אמת בטרמינל — עם סטטוס של כל job. לחצו Ctrl+C כדי לצאת מהצפייה.

אם אין לכם workflow, נסו: gh run list — גם אם הרשימה ריקה, הפקודה עצמה צריכה לעבוד.

2.7 Scripting Patterns — אוטומציות עם gh

הכוח האמיתי של gh CLI מתגלה כשמשלבים אותו עם shell scripting. עד עכשיו ראינו פקודות בודדות — alias שמריץ פקודה אחת, שאילתת API שמחזירה נתונים, סינון JQ שמעצב פלט. עכשיו נחבר את הכל לסקריפטים שלמים שמבצעים תהליכים מורכבים אוטומטית.

הדפוס הבסיסי הוא תמיד אותו דבר: שליפה, עיבוד, פעולה. שולפים נתונים מ-GitHub (עם gh list או gh api), מעבדים אותם עם JQ (סינון, מיון, בחירת שדות), ומבצעים פעולה על כל אלמנט (עם while read ו-gh edit / gh api). נראה חמישה דפוסים שמכסים כמעט כל תרחיש.

דפוס 1 — לולאה פשוטה על issues

#!/bin/bash
set -euo pipefail

# הוספת label "needs-triage" לכל issue פתוח בלי label
gh issue list --json number,labels --jq \
  '.[] | select(.labels | length == 0) | .number' | \
  while read -r num; do
    gh issue edit "$num" --add-label "needs-triage"
    echo "Labeled issue #$num"
  done

מה קורה פה? בואו נפרק את זה שורה אחרי שורה:

  1. gh issue list --json number,labels — שולף את כל ה-issues הפתוחים עם מספר ו-labels בפורמט JSON
  2. --jq '.[] | select(.labels | length == 0) | .number' — מסנן רק issues בלי labels, ומוציא רק את המספר
  3. while read -r num — לולאה שרצה על כל מספר שחוזר מהצינור (pipe)
  4. gh issue edit "$num" --add-label "needs-triage" — מוסיף label ל-issue הספציפי

הדפוס הזה — שליפת JSON, סינון עם JQ, לולאה עם פעולה — חוזר בכל סקריפט. ברגע שתבינו אותו, תוכלו לבנות כל אוטומציה.

דפוס 2 — batch operations על PRs

#!/bin/bash
set -euo pipefail

# סגירת כל PRs ישנים (יותר מ-90 יום)
CUTOFF=$(date -d "90 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-90d +%Y-%m-%dT%H:%M:%SZ)

gh pr list --json number,createdAt --jq \
  ".[] | select(.createdAt < \"$CUTOFF\") | .number" | \
  while read -r num; do
    gh pr close "$num" --comment "Auto-closed: stale PR (90+ days with no activity)"
    echo "Closed PR #$num"
    sleep 0.5   # מניעת rate limit
  done

דפוס 3 — pipeline מלא (שליפה, עיבוד, הצגה)

#!/bin/bash
set -euo pipefail

# דוח contributors מדורג
echo "=== Top 10 Contributors ==="
gh api repos/{owner}/{repo}/contributors --paginate \
  --jq '.[] | "\(.login): \(.contributions) commits"' | \
  sort -t: -k2 -rn | head -10

echo ""
echo "=== Repo Stats ==="
gh api repos/{owner}/{repo} --jq \
  '"Stars: \(.stargazers_count) | Forks: \(.forks_count) | Issues: \(.open_issues_count)"'

דפוס 4 — תנאים ולוגיקה

#!/bin/bash
set -euo pipefail

# Auto-assign issues לפי label
gh issue list --state open --json number,labels --jq \
  '.[] | {num: .number, labels: [.labels[].name]}' | jq -c '.' | \
  while read -r issue; do
    NUM=$(echo "$issue" | jq -r '.num')
    LABELS=$(echo "$issue" | jq -r '.labels[]')

    if echo "$LABELS" | grep -q "bug"; then
      gh issue edit "$NUM" --add-assignee "bug-team-lead"
      echo "#$NUM (bug) -> assigned to bug-team-lead"
    elif echo "$LABELS" | grep -q "feature"; then
      gh issue edit "$NUM" --add-assignee "feature-team-lead"
      echo "#$NUM (feature) -> assigned to feature-team-lead"
    fi
  done

דפוס 5 — Error handling נכון

#!/bin/bash
set -euo pipefail

# סקריפט עם error handling מלא
LOG_FILE="/tmp/gh-automation-$(date +%Y%m%d).log"

log() {
  echo "[$(date +%H:%M:%S)] $*" | tee -a "$LOG_FILE"
}

# בדיקת prerequisites
if ! gh auth status &>/dev/null; then
  log "ERROR: Not authenticated. Run 'gh auth login' first."
  exit 1
fi

REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner' 2>/dev/null)
if [ -z "$REPO" ]; then
  log "ERROR: Not in a git repository."
  exit 1
fi

log "Starting automation for $REPO"

# פעולה עם retry
perform_with_retry() {
  local cmd="$1"
  local max_retries=3
  local attempt=1

  while [ $attempt -le $max_retries ]; do
    if eval "$cmd" 2>/dev/null; then
      return 0
    fi
    log "WARN: Attempt $attempt failed, retrying..."
    attempt=$((attempt + 1))
    sleep 2
  done

  log "ERROR: Failed after $max_retries attempts: $cmd"
  return 1
}

# שימוש
gh issue list --json number --jq '.[].number' | \
  while read -r num; do
    perform_with_retry "gh issue edit $num --add-label needs-review" && \
      log "Labeled #$num" || \
      log "Failed to label #$num"
  done

log "Automation complete. Log: $LOG_FILE"
דפוס אוטומציה: שליפה, עיבוד, פעולה gh api / gh list --jq סינון + עיבוד while read | gh edit תוצאה כל הזרימה בשורות ספורות של shell script — שליפה, סינון, פעולה
מסגרת החלטה: Alias vs Script vs Extension
קריטריוןAliasScript (bash)Extension
אורך1-3 שורות4-100 שורותכל גודל
מורכבותפקודה אחת או pipe פשוטלולאות, תנאים, error handlingCLI מלא עם flags, help, versions
שיתוףקובץ YAML (gh alias import)קובץ bash (git repo)gh extension install (GitHub)
מתי לבחורקיצור פקודה חוזרתאוטומציה עם לוגיקהכלי שרוצים לשתף עם הקהילה
דוגמהgh my-prs./triage.shgh my-stats

כלל אצבע: התחילו תמיד מ-alias. אם ה-alias נהיה מסובך (יותר מ-3 שורות) — העבירו לסקריפט. אם הסקריפט שימושי גם לאחרים — הפכו ל-extension.

עשו עכשיו

כתבו את הלולאה הראשונה שלכם. העתיקו והריצו:

# דוח issues — מספר, כותרת, labels
gh issue list --json number,title,labels --jq \
  '.[] | "#\(.number) \(.title) [\(.labels | map(.name) | join(", "))]"'

אם עבד — הוסיפו pipe ל-wc -l כדי לספור כמה issues יש לכם. זה הדפוס הבסיסי: שליפה, עיבוד, הצגה.

תרגיל 1: סקריפט דוח PRs

כתבו סקריפט בשם pr-report.sh שמדפיס דוח PRs מפורט:

  1. צרו קובץ pr-report.sh עם #!/bin/bash ו-set -euo pipefail בראש
  2. הוסיפו שורה שמדפיסה כמה PRs פתוחים יש (gh pr list --json number --jq 'length')
  3. הוסיפו רשימת PRs עם מספר, כותרת, author ותאריך יצירה
  4. הוסיפו שורה שמוצאת את ה-PR הכי ותיק (הישן ביותר)
  5. הוסיפו chmod +x pr-report.sh והריצו

פתרון לבדיקה עצמית:

#!/bin/bash
set -euo pipefail

echo "=== PR Report for $(gh repo view --json nameWithOwner --jq '.nameWithOwner') ==="
echo ""

COUNT=$(gh pr list --json number --jq 'length')
echo "Open PRs: $COUNT"
echo ""

if [ "$COUNT" -gt 0 ]; then
  echo "--- PR List ---"
  gh pr list --json number,title,author,createdAt --jq \
    '.[] | "#\(.number) \(.title) by \(.author.login) (\(.createdAt[:10]))"'
  echo ""

  echo "--- Oldest PR ---"
  gh pr list --json number,title,createdAt --jq \
    'sort_by(.createdAt) | .[0] | "#\(.number) \(.title) (created \(.createdAt[:10]))"'
else
  echo "No open PRs!"
fi
תרגיל 2: סקריפט PR automation מלא

כתבו סקריפט auto-pr.sh שמבצע את כל תהליך ה-PR בפקודה אחת:

  1. מקבל כפרמטר ראשון את שם ה-branch ($1) וכפרמטר שני את כותרת ה-PR ($2)
  2. יוצר branch חדש (git checkout -b $1)
  3. עושה commit לכל הקבצים שנערכו (git add -A && git commit)
  4. דוחף ל-remote (git push -u origin $1)
  5. יוצר PR עם הכותרת שהתקבלה (gh pr create)
  6. מוסיף label אוטומטי (gh pr edit --add-label)
  7. מדפיס את ה-URL של ה-PR

טיפים:

  • הוסיפו בדיקה ב-if שיש staged changes לפני ה-commit
  • השתמשו ב-set -euo pipefail בתחילת הסקריפט
  • שמרו את ה-PR number מ-gh pr create --json number --jq '.number'
אזהרה: Error handling בסקריפטים

תמיד הוסיפו set -euo pipefail בתחילת כל סקריפט. בלי זה: (1) set -e — שגיאה בפקודה אחת לא עוצרת את הסקריפט, והפקודות הבאות רצות על נתונים שגויים. (2) set -u — משתנה לא מוגדר לא מחזיר שגיאה ומוחלף בריק. (3) set -o pipefail — שגיאה באמצע pipe לא נתפסת. שלושת אלה ביחד מונעים את רוב הבאגים בסקריפטי shell.

2.8 Extensions — הרחבת gh עם תוספים

Extensions הם תוספים שמרחיבים את gh עם יכולות שלא קיימות בגרסה הבסיסית. כל extension מוסיף פקודת gh חדשה — כאילו היא חלק מהכלי המקורי. יש מאות extensions מהקהילה, מ-dashboards אינטראקטיביים דרך כלי ניקוי branches ועד generators של changelogs. והחלק הכי טוב: ליצור extension משלכם זה עניין של דקות, לא ימים.

Extension יכול להיות כתוב בכל שפה — Bash, Go, Python, Ruby, ואפילו compiled binary. כשמתקינים extension, gh מוריד אותו ל-directory מיוחד ומנגיש אותו כפקודה. אין צורך ב-PATH ידני או קונפיגורציה — הכל אוטומטי.

מציאה והתקנה של extensions

# חיפוש extension לפי מילת מפתח
gh extension search dashboard
gh extension search changelog
gh extension search "branch cleanup"

# התקנה
gh extension install dlvhdr/gh-dash        # dashboard אינטראקטיבי (TUI)
gh extension install seachicken/gh-poi     # ניקוי branches ממוזגים
gh extension install vilmibm/gh-changelog  # יצירת changelog אוטומטי
gh extension install mislav/gh-branch      # ניהול branches מתקדם
gh extension install gennaro-tedesco/gh-f  # fuzzy finder לריפו

# שימוש — פשוט מריצים
gh dash                                    # פותח dashboard
gh poi                                     # מנקה branches ישנים
gh changelog                               # מייצר changelog
gh f                                       # fuzzy find

ניהול extensions מותקנים

# רשימת כל ה-extensions המותקנים
gh extension list

# עדכון extension ספציפי
gh extension upgrade gh-dash

# עדכון כל ה-extensions בבת אחת
gh extension upgrade --all

# הסרה
gh extension remove gh-dash

Extensions מומלצים

Extensionפקודהמה זה עושהלמי מתאים
dlvhdr/gh-dashgh dashDashboard אינטראקטיבי (TUI) לניהול PRs ו-issuesכולם
seachicken/gh-poigh poiניקוי branches ממוזגים שכבר לא צריךכולם
vilmibm/gh-changeloggh changelogיצירת changelog אוטומטי מ-PRs ו-commitsmaintainers
mislav/gh-branchgh branchניהול branches מתקדם — fuzzy search, checkoutמי שעובד עם הרבה branches
github/gh-copilotgh copilotCopilot CLI — explain ו-suggest מהטרמינלמי שיש לו Copilot
עשו עכשיו

התקינו את gh-dash — ה-extension הכי פופולרי. זה dashboard אינטראקטיבי שמציג PRs ו-issues:

gh extension install dlvhdr/gh-dash
gh dash

זה יפתח ממשק TUI (Text User Interface) עשיר בטרמינל. אפשר לנווט עם חצים, לפתוח PR עם Enter, לראות diff, ולנהל reviews — הכל בלי דפדפן. לחצו q כדי לצאת.

יצירת extension משלכם

יצירת extension ב-gh היא פשוטה להפתיע. בבסיסו, extension הוא פשוט סקריפט executable שמתחיל ב-gh-. אפשר לכתוב אותו ב-Bash, Go, Python, או כל שפה אחרת.

# 1. יצירת extension חדש (Bash)
gh extension create my-stats
cd gh-my-stats

# 2. זה יצר מבנה תיקיות עם קובץ gh-my-stats
# ערכו אותו:
#!/bin/bash
# gh-my-stats — Extension שמציג סטטיסטיקות ריפו
set -euo pipefail

REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner' 2>/dev/null)
if [ -z "$REPO" ]; then
  echo "Error: not in a git repository"
  exit 1
fi

echo "=== Stats for $REPO ==="
echo ""

# נתונים בסיסיים
gh api repos/{owner}/{repo} --jq \
  '"Stars:    \(.stargazers_count)
Forks:    \(.forks_count)
Watchers: \(.subscribers_count)
Language: \(.language)
License:  \(.license.spdx_id // "None")"'

echo ""

# PRs ו-Issues
echo "Open PRs:     $(gh pr list --json number --jq 'length')"
echo "Open Issues:  $(gh issue list --json number --jq 'length')"

echo ""

# Top 5 contributors
echo "--- Top 5 Contributors ---"
gh api repos/{owner}/{repo}/contributors --jq \
  '.[:5] | .[] | "  \(.login): \(.contributions) commits"'
# 3. הפכו ל-executable
chmod +x gh-my-stats

# 4. התקינו מהתיקייה המקומית
gh extension install .

# 5. עכשיו אפשר להשתמש
gh my-stats

שימו לב למבנה: extension הוא פשוט קובץ executable שמתחיל ב-gh-. כשמריצים gh my-stats, gh מחפש קובץ בשם gh-my-stats בתיקיית ה-extensions ומריץ אותו. כל מה שהסקריפט מדפיס ל-stdout נראה למשתמש.

אם ה-extension עובד טוב ואתם רוצים לשתף אותו עם הקהילה:

# צרו ריפוזיטורי ב-GitHub (שם חייב להתחיל ב-gh-)
gh repo create gh-my-stats --public --source . --push

# עכשיו כולם יכולים להתקין:
# gh extension install your-username/gh-my-stats
תרגיל 3: בניית Extension מותאם

צרו extension בשם gh-health שבודק את "בריאות" הריפוזיטורי:

  1. הריצו gh extension create health
  2. ערכו את הסקריפט כדי שיבדוק:
    • כמה PRs פתוחים יותר מ-7 ימים (ישנים)
    • כמה issues בלי label
    • כמה issues בלי assignee
    • האם יש README.md
    • האם יש LICENSE
  3. הוסיפו "ציון בריאות" (0-100) על בסיס הבדיקות
  4. התקינו מקומית עם gh extension install .
  5. הריצו gh health ובדקו את הפלט

בונוס: פרסמו את ה-extension ב-GitHub repo ציבורי.

2.9 אוטומציות מורכבות — Release ו-Issue Triage

עכשיו שיש לנו aliases, gh api, JQ, scripting ו-extensions — הגיע הזמן לשלב הכל לאוטומציות מורכבות שפותרות בעיות אמיתיות. הסעיפים הקודמים נתנו לנו את חלקי הלגו; עכשיו נבנה מהם מבנים שלמים.

נראה שני תרחישים שכל מפתח או maintainer נתקל בהם באופן קבוע: release workflow (שחרור גרסה) ו-issue triage (מיון issues). שני התרחישים האלה כוללים כמה צעדים שחוזרים על עצמם, דורשים זהירות, וקל לטעות בהם ידנית — בדיוק סוג המשימות שכדאי לאטמט.

אוטומציה 1: Release workflow מלא

Release הוא תהליך שמערב כמה צעדים מוגדרים: יצירת tag, דחיפה ל-remote, יצירת release עם changelog, ולפעמים גם עדכון documentation ושליחת התראות. כשעושים את זה ידנית, קל לשכוח צעד (למשל, לשכוח לדחוף את ה-tag), או לעשות טעות (למשל, ליצור tag על branch לא נכון). סקריפט release אוטומטי מצמצם את כל התהליך לפקודה אחת ומבטיח שכל הצעדים מתבצעים בסדר הנכון עם הבדיקות הנכונות:

#!/bin/bash
set -euo pipefail

# release.sh — Release automation
# שימוש: ./release.sh 1.2.0

VERSION=$1

if [ -z "$VERSION" ]; then
  echo "Usage: ./release.sh "
  echo "Example: ./release.sh 1.2.0"
  exit 1
fi

# בדיקה שאנחנו על main
BRANCH=$(git branch --show-current)
if [ "$BRANCH" != "main" ]; then
  echo "Error: Must be on 'main' branch (currently on '$BRANCH')"
  exit 1
fi

# בדיקה שאין שינויים לא שמורים
if ! git diff --quiet HEAD; then
  echo "Error: Uncommitted changes. Commit or stash first."
  exit 1
fi

echo "Creating release v${VERSION}..."

# יצירת tag
git tag -a "v${VERSION}" -m "Release v${VERSION}"
echo "  Tag v${VERSION} created"

# דחיפה
git push origin main --tags
echo "  Pushed to remote"

# יצירת release עם changelog אוטומטי
gh release create "v${VERSION}" \
  --title "Release v${VERSION}" \
  --generate-notes \
  --latest
echo "  Release created"

# הדפסת URL
URL=$(gh release view "v${VERSION}" --json url --jq '.url')
echo ""
echo "Release v${VERSION} published!"
echo "URL: $URL"

שימו לב לבדיקות בתחילת הסקריפט — הן מונעות טעויות נפוצות. בלעדיהן, אפשר בטעות ליצור release מ-branch לא נכון, או עם שינויים לא שמורים שלא ייכנסו ל-release. זה דוגמה מצוינת ל-"defensive scripting" — הסקריפט מגן על עצמו מפני שגיאות אנוש.

עכשיו במקום תהליך ידני של 5-10 דקות (שבמהלכו אפשר לשכוח צעד), זה שורה אחת:

./release.sh 1.2.0

ואם רוצים לשלב את הסקריפט עם GitHub Actions — אפשר ליצור workflow dispatch שמקבל את מספר הגרסה כ-input ומריץ את אותם צעדים. בפרק 8 (API & Webhooks) נראה איך לעשות את זה.

אוטומציה 2: Issue triage אוטומטי

ריפוזיטורים פעילים צוברים issues בקצב מהיר — ללא label, בלי assignee, ובלי אף אחד שטיפל בהם. אחרי כמה שבועות יש עשרות (או מאות) issues לא מטופלים, ואי אפשר להבין מה דחוף ומה לא. סקריפט triage אוטומטי עובר על כל ה-issues, מסווג אותם, מקצה אחראיים, ומסמן issues ישנים. במקום לבזבז שעה בשבוע על triage ידני, הסקריפט עושה את זה ב-30 שניות:

#!/bin/bash
set -euo pipefail

# triage.sh — Issue triage automation
echo "=== Issue Triage for $(gh repo view --json nameWithOwner --jq '.nameWithOwner') ==="
echo "Started at $(date)"
echo ""

TOTAL=0

# 1. Issues בלי label — סימון ל-triage
echo "--- Unlabeled Issues ---"
gh issue list --json number,labels --jq \
  '.[] | select(.labels | length == 0) | .number' | \
  while read -r num; do
    gh issue edit "$num" --add-label "needs-triage"
    echo "  #$num -> needs-triage"
    TOTAL=$((TOTAL + 1))
  done

# 2. Issues עם label "bug" בלי assignee — הקצאה אוטומטית
echo ""
echo "--- Unassigned Bugs ---"
gh issue list --label bug --json number,assignees --jq \
  '.[] | select(.assignees | length == 0) | .number' | \
  while read -r num; do
    gh issue edit "$num" --add-assignee @me
    echo "  #$num -> assigned to me"
    TOTAL=$((TOTAL + 1))
  done

# 3. Issues ישנים מאוד (יותר מ-180 יום) — הוספת label "stale"
echo ""
echo "--- Stale Issues (180+ days) ---"
CUTOFF=$(date -d "180 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-180d +%Y-%m-%dT%H:%M:%SZ)
gh issue list --json number,updatedAt --jq \
  ".[] | select(.updatedAt < \"$CUTOFF\") | .number" | \
  while read -r num; do
    gh issue edit "$num" --add-label "stale"
    gh issue comment "$num" --body "This issue has been open for 180+ days without activity. Marking as stale."
    echo "  #$num -> stale"
    TOTAL=$((TOTAL + 1))
  done

echo ""
echo "=== Triage complete ==="
echo "Processed actions: $TOTAL"

שילוב: Alias שמפעיל את הסקריפטים

הדבר האחרון שנעשה הוא לחבר את הסקריפטים ל-aliases. למה? כי aliases זמינים מכל ריפוזיטורי — לא צריכים שהסקריפט יהיה בתיקייה הנוכחית. שמרו את הסקריפטים במקום קבוע (למשל ~/scripts/) והפנו אליהם מ-aliases:

# alias ל-release
gh alias set --shell release \
  'bash ~/scripts/release.sh "$1"'

# alias ל-triage
gh alias set --shell triage \
  'bash ~/scripts/triage.sh'

# שימוש:
gh release 1.2.0
gh triage
תרגיל 4: Release automation מותאם

צרו סקריפט release.sh משלכם שעושה את כל הצעדים:

  1. מקבל מספר גרסה כפרמטר ($1)
  2. מוודא שאנחנו על branch main
  3. מוודא שאין uncommitted changes
  4. יוצר annotated tag (git tag -a)
  5. דוחף ל-remote
  6. יוצר release עם --generate-notes
  7. מדפיס URL של ה-release

בונוס: הוסיפו flag --pre-release שיוצר pre-release (עם gh release create --prerelease).

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

סיכום הפרק
  • Aliases — שלוש רמות (פשוט, פרמטרים, shell) שהופכות תהליכים חוזרים לפקודה אחת. ה-5 aliases שהגדרנו הם הבסיס לעבודה יומיומית
  • gh api — גישה ישירה לכל endpoint של GitHub API, גם REST וגם GraphQL. אימות אוטומטי, pagination, ו-JQ מובנה. עדיף על curl כמעט תמיד
  • JQ — שפת הסינון שהופכת JSON גולמי לפלט שימושי. דפוסי select, map, sort ו-string interpolation מכסים 95% מהמקרים
  • gh pr / issue / repo — מחזור חיים מלא מהטרמינל. מיצירה דרך review ועד merge/close. הפקודות הכי שימושיות: create, list, view, merge, edit
  • gh run — ניטור GitHub Actions בזמן אמת. gh run watch היא הפקודה הכי חזקה — צופים ב-CI בלי דפדפן
  • Scripting — הדפוס "שליפה, עיבוד, פעולה" הוא הבסיס לכל אוטומציה. set -euo pipefail חובה בכל סקריפט
  • Extensions — אקוסיסטם שלם של תוספים (gh-dash, gh-poi, gh-changelog) ויכולת ליצור extension משלכם תוך דקות
  • אוטומציות מורכבות — שילוב של aliases + scripting + extensions יוצר כלים כמו release automation ו-issue triage אוטומטי
אם אתם עושים רק דבר אחד מהפרק הזה

צרו את ה-alias my-prs עכשיוgh alias set my-prs 'pr list --state open --assignee @me' — והתחילו כל בוקר עם gh my-prs. זה לוקח 10 שניות להגדרה וחוסך דקות כל יום. ברגע שתרגישו את הקסם של alias אחד, תרצו עוד — וזה הצעד הראשון לשליטה מלאה מהטרמינל.

בדקו את עצמכם
  1. מה ההבדל בין alias רגיל (gh alias set) ל-shell alias (gh alias set --shell)? מתי משתמשים בכל אחד?
  2. למה gh api עדיף על curl לשאילתות GitHub? ציינו לפחות 3 יתרונות.
  3. מה עושה --paginate ב-gh api? למה זה חשוב כשמשלפים רשימות ארוכות?
  4. כתבו ביטוי JQ שמסנן רק issues פתוחים ומדפיס את המספר והכותרת שלהם בפורמט #42 title.
  5. מה הפקודה לצפייה בזמן אמת ב-GitHub Actions workflow run? מה קורה אם ה-run נכשל — איך רואים מה נכשל ואיך מריצים מחדש?
שגרת עבודה מומלצת
  • יומית (בוקר): gh my-prs — בדקו PRs שמחכים לכם. gh my-issues — בדקו issues פתוחים שלכם
  • יומית (אחרי push): gh run watch — ודאו ש-CI עובר. אם נכשל: gh run view --log-failed
  • יומית (לפני merge): gh pr checks — ודאו שכל הבדיקות עברו לפני מיזוג
  • שבועית: הריצו את סקריפט ה-triage — וודאו שאין issues ללא label או assignee
  • שבועית: gh poi (או cleanup alias) — ניקוי branches ממוזגים שכבר לא צריך
  • חודשית: gh extension upgrade --all — עדכנו extensions לגרסאות אחרונות
  • חודשית: סקרו את ה-aliases שלכם — יש כאלה שלא השתמשתם בהם? מחקו. חסר alias? הוסיפו
צ'קליסט — מה עשינו בפרק הזה
  • ☐ בדקתי ש-gh מותקן ומאומת (gh --version, gh auth status)
  • ☐ יצרתי alias פשוט (gh alias set my-prs)
  • ☐ יצרתי alias עם פרמטרים ($1)
  • ☐ יצרתי shell alias עם --shell
  • ☐ ביצעתי קריאת gh api ראשונה (REST)
  • ☐ ניסיתי GraphQL עם gh api graphql
  • ☐ סיננתי JSON עם --jq (select, map, string interpolation)
  • ☐ ביצעתי מחזור PR מלא מהטרמינל (create, view, close/merge)
  • ☐ ניטרתי workflow עם gh run watch (או בדקתי ש-gh run list עובד)
  • ☐ כתבתי סקריפט דוח PRs (pr-report.sh)
  • ☐ כתבתי סקריפט אוטומציה (auto-pr.sh או triage)
  • ☐ התקנתי extension (gh-dash)
  • ☐ יצרתי extension מותאם (gh-my-stats או gh-health)
  • ☐ יש לי לפחות 5 aliases מוגדרים (gh alias list)

מה ללמוד הלאה

הפרק הזה כיסה את הבסיס והבינוני של gh CLI. מכאן יש כמה כיוונים להמשיך:

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

בפרק הבא: נלמד להקים אתרים חינמיים עם GitHub Pages — ונשתמש ב-Actions (פרק 1) ו-CLI (הפרק הנוכחי) כדי לפרסם אותם אוטומטית. אם יצרתם aliases בפרק הזה, הם כבר יעזרו לכם שם.