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 میکند:
راهاندازی
- Settings → Webhooks → Add webhook
- Payload URL:
https://your-server.com/webhook - Content type:
application/json - Secret: یک رمز قوی
- 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 برای قابلیتهای بیشتر
در فصل پایانی، یک پروژه واقعی با همه این مفاهیم میسازیم.