~/icsd.ir — bash
SYSTEM_ONLINE

GitHub CLI و REST/GraphQL API

برای automation و scripting، GitHub دو ابزار قدرتمند دارد: gh CLI برای دستورات terminal، و REST/GraphQL API برای ادغام در کد. در این فصل هر دو را پوشش می‌دهیم.

برای automation و scripting، GitHub دو ابزار قدرتمند دارد: gh CLI برای دستورات terminal، و REST/GraphQL API برای ادغام در کد. در این فصل هر دو را پوشش می‌دهیم.

نصب GitHub CLI

# Linux (Debian/Ubuntu)
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg 
  | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] 
  https://cli.github.com/packages stable main" 
  | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null

sudo apt update
sudo apt install gh

# macOS
brew install gh

# Windows (Scoop)
scoop install gh
# یا (winget)
winget install GitHub.cli

# تأیید
gh --version

احراز هویت

gh auth login

# ? What account do you want to log into?
# > GitHub.com
# 
# ? What is your preferred protocol?
# > HTTPS (یا SSH)
# 
# ? Authenticate Git with your GitHub credentials? (Y/n) Y
# 
# ? How would you like to authenticate?
# > Login with a web browser

# تأیید
gh auth status
# ✓ Logged in to github.com as USERNAME

دستورات Repository

# ساخت ریپو جدید
gh repo create my-project --public --description "A new project"
gh repo create my-org/my-project --private --add-readme --license MIT

# clone
gh repo clone USER/repo

# fork
gh repo fork USER/repo
gh repo fork USER/repo --clone

# لیست ریپوهای خود
gh repo list
gh repo list USER --limit 50

# اطلاعات ریپو
gh repo view
gh repo view USER/repo

# باز کردن در browser
gh repo view --web

# تنظیمات
gh repo edit --description "New description"
gh repo edit --add-topic python --add-topic django
gh repo edit --visibility private

# rename
gh repo rename new-name

# حذف (با تأیید)
gh repo delete USER/repo --confirm

دستورات Issues

# لیست
gh issue list
gh issue list --state open
gh issue list --label "bug"
gh issue list --assignee @me
gh issue list --search "in:title bug"

# مشاهده
gh issue view 42
gh issue view 42 --web        # در browser
gh issue view 42 --comments

# ساخت
gh issue create --title "Fix login bug" --body "Description here"

# با template
gh issue create --title "Bug: Login fails" 
                --body-file bug-report.md 
                --label bug 
                --assignee alice

# interactive
gh issue create

# ویرایش
gh issue edit 42 --title "New title"
gh issue edit 42 --add-label "priority:high"
gh issue edit 42 --milestone "v1.2.0"

# بستن/باز کردن
gh issue close 42 --comment "Fixed in #45"
gh issue reopen 42

# کامنت
gh issue comment 42 --body "Working on this..."

دستورات Pull Requests

# لیست
gh pr list
gh pr list --state merged
gh pr list --author alice

# مشاهده
gh pr view 123
gh pr view 123 --web

# ساخت PR از branch فعلی
gh pr create

# با همه چیز
gh pr create 
  --title "feat: add dark mode" 
  --body "Implements dark mode..." 
  --base main 
  --reviewer alice,bob 
  --label feature 
  --milestone "v1.2.0" 
  --assignee @me 
  --draft

# checkout یک PR محلی
gh pr checkout 123
# (شما در branch آن PR هستید)

# diff
gh pr diff 123

# review
gh pr review 123 --approve --body "LGTM!"
gh pr review 123 --request-changes --body "Please fix..."
gh pr review 123 --comment --body "Question..."

# merge
gh pr merge 123 --merge        # merge commit
gh pr merge 123 --squash       # squash
gh pr merge 123 --rebase       # rebase
gh pr merge 123 --auto --squash --delete-branch

# بستن
gh pr close 123 --comment "Wrong approach"

# checks (CI status)
gh pr checks 123

دستورات Release

# لیست
gh release list

# مشاهده
gh release view v1.0.0

# ساخت
gh release create v1.0.0 
  --title "v1.0.0 - Initial Release" 
  --notes "Release notes..." 
  ./dist/*.zip

# با generate notes
gh release create v1.0.0 --generate-notes

# pre-release
gh release create v1.0.0-beta.1 --prerelease

# ویرایش
gh release edit v1.0.0 --title "New title"

# آپلود asset
gh release upload v1.0.0 ./new-file.zip

# دانلود
gh release download v1.0.0
gh release download v1.0.0 --pattern "*.zip"

# حذف
gh release delete v1.0.0

دستورات Workflow (Actions)

# لیست workflows
gh workflow list

# مشاهده
gh workflow view test.yml
gh workflow view --web

# اجرای workflow_dispatch
gh workflow run deploy.yml
gh workflow run deploy.yml -f environment=staging

# لیست runs
gh run list
gh run list --workflow=test.yml --limit 10

# مشاهده run
gh run view RUN_ID
gh run view RUN_ID --log         # log کامل

# rerun
gh run rerun RUN_ID
gh run rerun RUN_ID --failed     # فقط job‌های failed

# cancel
gh run cancel RUN_ID

# دانلود artifacts
gh run download RUN_ID

# watch real-time
gh run watch

Gist Commands

# ساخت gist از فایل
gh gist create file.py
gh gist create --public file.py
gh gist create --desc "My snippet" file.py

# از stdin
echo "hello" | gh gist create -

# لیست
gh gist list

# مشاهده
gh gist view GIST_ID

# ویرایش
gh gist edit GIST_ID

# clone
gh gist clone GIST_ID

دستور gh api

قوی‌ترین دستور – برای فراخوانی هر endpoint API:

# GET
gh api /user
gh api /repos/USER/repo
gh api /repos/USER/repo/issues

# با pagination
gh api --paginate /repos/USER/repo/issues

# JSON path query
gh api /user --jq .login
gh api /repos/USER/repo --jq '.stargazers_count'

# POST
gh api -X POST /repos/USER/repo/issues 
  -f title="New issue" 
  -f body="Description"

# PATCH
gh api -X PATCH /repos/USER/repo/issues/42 
  -f state=closed

# DELETE
gh api -X DELETE /repos/USER/repo/issues/comments/123

# GraphQL
gh api graphql -f query='
  query {
    viewer {
      login
      repositories(first: 5) {
        nodes {
          name
        }
      }
    }
  }
'

GitHub REST API

مستندات: docs.github.com/rest

احراز هویت

# با curl
curl -H "Authorization: Bearer ghp_YOUR_TOKEN" 
     -H "Accept: application/vnd.github+json" 
     -H "X-GitHub-Api-Version: 2022-11-28" 
     https://api.github.com/user

اطلاعات کاربر

curl https://api.github.com/users/torvalds

# خروجی JSON
{
  "login": "torvalds",
  "name": "Linus Torvalds",
  "public_repos": 7,
  "followers": 200000,
  ...
}

Rate Limit

# بدون احراز: 60 req/hour
# با احراز: 5000 req/hour

curl -I -H "Authorization: Bearer ghp_xxx" 
     https://api.github.com/user

# headers جالب:
# X-RateLimit-Limit: 5000
# X-RateLimit-Remaining: 4985
# X-RateLimit-Reset: 1714572000

پایتون – PyGitHub

from github import Github

# auth
g = Github("ghp_YOUR_TOKEN")

# اطلاعات کاربر
user = g.get_user()
print(user.login, user.name)

# لیست ریپوها
for repo in user.get_repos():
    print(repo.name, repo.stargazers_count)

# ساخت ریپو
new_repo = user.create_repo(
    "test-repo",
    description="Created from API",
    private=False,
)

# ساخت Issue
repo = g.get_repo("USER/repo")
issue = repo.create_issue(
    title="Bug found",
    body="Description...",
    labels=["bug"]
)

# Pull Requests
for pr in repo.get_pulls(state="open"):
    print(pr.number, pr.title)

# جستجو
for repo in g.search_repositories(query="language:python stars:>1000"):
    print(repo.full_name, repo.stargazers_count)

Node.js – Octokit

import { Octokit } from "@octokit/rest";

const octokit = new Octokit({
  auth: "ghp_YOUR_TOKEN",
});

// اطلاعات کاربر
const { data: user } = await octokit.users.getAuthenticated();
console.log(user.login);

// ساخت Issue
const { data: issue } = await octokit.issues.create({
  owner: "USER",
  repo: "repo",
  title: "Bug found",
  body: "Description...",
  labels: ["bug"],
});

// لیست ریپوها
const { data: repos } = await octokit.repos.listForAuthenticatedUser({
  per_page: 100,
});

// با pagination
const allRepos = await octokit.paginate(
  octokit.repos.listForAuthenticatedUser,
  { per_page: 100 }
);

GraphQL API

قوی‌تر از REST – فقط داده‌ای که می‌خواهید را می‌گیرید:

curl -H "Authorization: Bearer ghp_xxx" 
     -X POST 
     -d '{"query":"{ viewer { login } }"}' 
     https://api.github.com/graphql

Query پیچیده

query {
  repository(owner: "USER", name: "repo") {
    name
    description
    stargazerCount
    forkCount
    
    pullRequests(first: 10, states: OPEN) {
      nodes {
        number
        title
        author {
          login
        }
        commits(first: 1) {
          nodes {
            commit {
              message
            }
          }
        }
      }
    }
    
    issues(first: 5, states: OPEN, labels: ["bug"]) {
      nodes {
        number
        title
      }
    }
  }
  
  rateLimit {
    remaining
    resetAt
  }
}

Mutation – تغییر داده

mutation {
  addReaction(input: {
    subjectId: "ISSUE_NODE_ID",
    content: THUMBS_UP
  }) {
    reaction {
      content
    }
  }
}

پایتون با GraphQL

import requests

query = """
query($owner: String!, $name: String!) {
  repository(owner: $owner, name: $name) {
    stargazerCount
    pullRequests(first: 10, states: OPEN) {
      nodes {
        number
        title
      }
    }
  }
}
"""

variables = {"owner": "USER", "name": "repo"}

response = requests.post(
    "https://api.github.com/graphql",
    json={"query": query, "variables": variables},
    headers={"Authorization": "Bearer ghp_YOUR_TOKEN"}
)

data = response.json()["data"]["repository"]
print(f"Stars: {data['stargazerCount']}")
for pr in data["pullRequests"]["nodes"]:
    print(f"#{pr['number']}: {pr['title']}")

Webhooks

وقتی event رخ دهد، GitHub به URL شما POST می‌کند:

راه‌اندازی

  1. Settings → Webhooks → Add webhook
  2. Payload URL: https://your-server.com/webhook
  3. Content type: application/json
  4. Secret: یک رمز قوی
  5. Events: انتخاب کنید (push، PR، …)

دریافت webhook (Flask)

import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = b"your-secret"

def verify_signature(payload_body, signature):
    expected = "sha256=" + hmac.new(
        SECRET, payload_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

@app.route("/webhook", methods=["POST"])
def webhook():
    signature = request.headers.get("X-Hub-Signature-256")
    if not signature or not verify_signature(request.data, signature):
        abort(401)
    
    event = request.headers.get("X-GitHub-Event")
    payload = request.json
    
    if event == "push":
        print(f"Push to {payload['ref']} by {payload['pusher']['name']}")
    elif event == "pull_request":
        action = payload["action"]
        pr = payload["pull_request"]
        print(f"PR #{pr['number']} {action}: {pr['title']}")
    
    return "OK", 200

if __name__ == "__main__":
    app.run(port=5000)

مثال‌های عملی

۱. Backup همه ریپوها

#!/bin/bash
# backup-all.sh

mkdir -p ~/github-backup
cd ~/github-backup

gh repo list --limit 1000 --json nameWithOwner -q '.[].nameWithOwner' | 
while read repo; do
    echo "Backing up $repo..."
    if [ -d "$(basename $repo).git" ]; then
        cd "$(basename $repo).git" && git fetch --all && cd ..
    else
        git clone --mirror "git@github.com:$repo.git"
    fi
done

۲. آمار repository

#!/bin/bash
# stats.sh

REPO="USER/repo"

echo "=== Stats for $REPO ==="
echo "Stars:    $(gh api /repos/$REPO --jq .stargazers_count)"
echo "Forks:    $(gh api /repos/$REPO --jq .forks_count)"
echo "Issues:   $(gh api /repos/$REPO --jq .open_issues_count)"
echo "Watchers: $(gh api /repos/$REPO --jq .subscribers_count)"
echo "Language: $(gh api /repos/$REPO --jq .language)"
echo "Size:     $(gh api /repos/$REPO --jq .size) KB"

۳. close issue‌های stale

# issue‌های که 60 روز update نشده‌اند را close کن
gh issue list --state open --json number,updatedAt --limit 1000 | 
  jq -r '.[] | select(.updatedAt < "'$(date -d "60 days ago" -I)'") | .number' | 
  while read num; do
    gh issue close $num --comment "Closing as stale (no activity for 60 days)"
  done

۴. PR daily digest

import requests
from datetime import datetime, timedelta

token = "ghp_YOUR_TOKEN"
repo = "USER/repo"

yesterday = (datetime.utcnow() - timedelta(days=1)).isoformat()

r = requests.get(
    f"https://api.github.com/repos/{repo}/pulls",
    params={"state": "all", "since": yesterday},
    headers={"Authorization": f"Bearer {token}"}
)

prs = r.json()
print(f"PR Digest for {repo}")
print(f"Total: {len(prs)}")
for pr in prs:
    state = "✅ merged" if pr.get("merged_at") else pr["state"]
    print(f"  #{pr['number']} [{state}] {pr['title']}")

Alias و Extensions

Alias

# میانبر برای دستورات
gh alias set prl 'pr list --state open --author @me'
gh alias set co 'pr checkout'

# استفاده
gh prl
gh co 123

Extensions

# لیست extensions موجود
gh extension list

# نصب extension‌های محبوب
gh extension install dlvhdr/gh-dash         # dashboard TUI
gh extension install seachicken/gh-poi      # حذف branch‌های merged
gh extension install vilmibm/gh-screensaver # تفریح!

# استفاده
gh dash

بهترین شیوه‌ها

  • gh CLI برای automation روزمره
  • API برای ادغام در نرم‌افزار
  • GraphQL وقتی query پیچیده دارید
  • REST وقتی ساده‌تر است
  • هرگز token را hardcode نکنید
  • Rate limit را بررسی کنید
  • pagination را برای لیست‌های بزرگ
  • webhook با signature verification
  • caching برای کاهش API calls

جمع‌بندی

  • gh CLI برای 90% کارها
  • دستورات repo، issue، pr، release، workflow
  • gh api برای endpoint‌های دلخواه
  • REST API: ساده، RESTful
  • GraphQL API: قدرتمند، query دقیق
  • PyGitHub، Octokit برای wrapper‌های زبان
  • Webhooks برای real-time event
  • Extensions برای قابلیت‌های بیشتر

در فصل پایانی، یک پروژه واقعی با همه این مفاهیم می‌سازیم.

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.