mcpField
G
github

github

github.com/vercel-labsテスト・品質2.0k211 回インストール1 日前に更新
リポジトリ

github について

github はアプリケーションのテスト向けの、状態を保持する GitHub REST API エミュレーターです。変更はメモリ内に保存され、関連エンティティにも反映されます。リポジトリ、Issue、プルリクエスト、Actions などを扱います。

主な機能

  1. 01GitHub REST API の模擬
  2. 02関連エンティティにも反映するインメモリ状態
  3. 03リポジトリ、Issue、プルリクエストに対応
  4. 04Actions、Checks、Releases をカバー
  5. 05OAuth、アプリトークン、Webhook に対応

活用シーン

  1. 01GitHub API クライアントのテスト
  2. 02Issue とプルリクエストのワークフロー検証
  3. 03OAuth ログイン連携のテスト
  4. 04GitHub App と Webhook の動作確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/vercel-labs/emulate --skill github

インストール後、Claude Code で/githubを入力して読み込まれたか確認します。

詳細

GitHub API エミュレーター

状態を完全に保持する GitHub REST API エミュレーションです。作成、更新、削除はメモリ内に保存され、関連するエンティティにも反映されます。

起動

ローカルリスナーは、デフォルトで 127.0.0.1 にバインドされます。コンテナーやほかのマシンからアクセスするには、CLI に --host 0.0.0.0 を渡すか、createEmulator に hostname: '0.0.0.0' を渡します。クライアントからアクセス可能な URL を通知するには、--base-url または baseUrl を設定します。

# GitHub only
npx emulate --service github

# Default port
# http://localhost:4001

または、プログラムから実行します。

import { createEmulator } from 'emulate'

const github = await createEmulator({ service: 'github', port: 4001 })
// github.url === 'http://localhost:4001'

プログラムから GitHub App を使用する場合は、private_key を省略し、インスタンスから生成された RSA キーを読み取ります。

const github = await createEmulator({
  service: 'github',
  port: 4001,
  seed: {
    github: {
      users: [{ login: 'octocat' }],
      apps: [{
        app_id: 12345,
        slug: 'my-github-app',
        name: 'My GitHub App',
        installations: [{ installation_id: 100, account: 'octocat' }],
      }],
    },
  },
})

const privateKey = github.generatedSecrets.find(
  secret => secret.kind === 'github.app_private_key' && secret.id === '12345',
)?.value

キーは github.reset() を実行しても維持されます。明示的に指定したキーは generatedSecrets に含まれません。

Next.js および Nuxt のアダプターも、省略されたキーを生成します。返されたハンドラーを保持し、サーバー専用の generatedSecrets() メソッドを呼び出してください。永続化を設定すると、コールドスタート後も ID が維持されます。スナップショットには署名キーが含まれるため、非公開にしてください。また、カスタムの永続化バックエンドには、アトミックな initialize() セマンティクスの実装を必須としてください。

CLI では、プライベートな配信ファイルをリクエストする場合に限り、private_key を省略します。

npx emulate start --service github --seed emulate.config.yaml \
  --generated-secrets-file .emulate-secrets.json

保存先は存在していてはいけません。emulate は継承された ACL を削除し、実効的な所有者のみのアクセス権を検証してから、リスナーや portless エイリアスの起動前に完全な JSON を書き込みます。処理可能な起動エラーが発生した場合、実行中に作成されたファイルは削除されます。強制終了時には完全なファイルが残ることがあります。その場合、どの実行も使用していないことを確認してから、手動で削除してください。ファイルから generatedSecrets を読み取った後は、ソース管理の対象外にしてください。Linux では、acl パッケージの setfacl と getfacl が必要です。アクセス制御を検証できない場合、このフラグはフェイルクローズで動作し、Windows ではサポートされません。--generated-secrets-file を指定しない場合、CLI のシードファイルには引き続き private_key が必要です。

認証

トークンは Authorization: Bearer <token> または Authorization: token <token> として渡します。

curl http://localhost:4001/user \
  -H "Authorization: Bearer test_token_admin"

公開リポジトリのエンドポイントは、認証なしで利用できます。プライベートリポジトリと書き込み操作には、有効なトークンが必要です。トークンが指定されていない場合、リクエストは最初にシードされたユーザーにフォールバックします。

インストールアクセストークンは、リポジトリへの書き込み時に、設定された GitHub App ボットとして機能します。リポジトリの所有権、選択されたリポジトリへのアクセス権、要求された App の権限は引き続き適用されます。プルリクエストのマージには、ベースリポジトリで contents: write が必要です。プルリクエストのブランチ更新には、プルリクエストのリポジトリで pull_requests: write、ヘッドリポジトリで contents: write が必要です。

GitHub App JWT

生成されたシークレットを使用せずに CLI を使う場合は、明示的で有効な秘密鍵をシード設定で App に指定してください。{ iss: "<app_id>" } を含む JWT に RS256 で署名します。エミュレーターは署名を検証し、App を解決します。

github:
  apps:
    - app_id: 12345
      slug: my-github-app
      name: My GitHub App
      permissions:
        contents: read
        issues: write
      events: [push, pull_request]
      webhook_url: http://localhost:8080/github/webhook
      webhook_secret: my-webhook-secret
      description: My CI/CD bot
      installations:
        - installation_id: 100
          account: my-org
          repository_selection: all
          permissions:
            contents: read
          events: [push]
          repositories: [my-org/org-repo]

この例では、プログラムからの利用とアダプターでの利用を想定し、意図的に private_key を省略しています。この場合、エミュレーターが RSA キーを生成し、generatedSecrets を通じて公開します。CLI で使用する場合は、--generated-secrets-file <path> を指定してプライベートな配信ファイルをリクエストするか、有効なキーを独自に用意してください。このフラグを指定しない場合、CLI のシードファイルには private_key が必要です。プレースホルダーの PEM は使用しないでください。

アプリの接続先をエミュレーターに設定する

環境変数
GITHUB_EMULATOR_URL=http://localhost:4001
Octokit
import { Octokit } from '@octokit/rest'

const octokit = new Octokit({
  baseUrl: process.env.GITHUB_EMULATOR_URL ?? 'https://api.github.com',
  auth: 'test_token_admin',
})
OAuth URL のマッピング
GitHub の実際の URLエミュレーターの URL
https://github.com/login/oauth/authorize$GITHUB_EMULATOR_URL/login/oauth/authorize
https://github.com/login/oauth/access_token$GITHUB_EMULATOR_URL/login/oauth/access_token
https://api.github.com/user$GITHUB_EMULATOR_URL/user
Auth.js / NextAuth.js
import GitHub from '@auth/core/providers/github'

GitHub({
  clientId: process.env.GITHUB_CLIENT_ID,
  clientSecret: process.env.GITHUB_CLIENT_SECRET,
  authorization: {
    url: `${process.env.GITHUB_EMULATOR_URL}/login/oauth/authorize`,
  },
  token: {
    url: `${process.env.GITHUB_EMULATOR_URL}/login/oauth/access_token`,
  },
  userinfo: {
    url: `${process.env.GITHUB_EMULATOR_URL}/user`,
  },
})

シード設定

tokens:
  test_token_admin:
    login: admin
    scopes: [repo, user, admin:org, admin:repo_hook]

github:
  users:
    - login: octocat
      name: The Octocat
      email: [email protected]
      bio: I am the Octocat
      company: GitHub
      location: San Francisco
      blog: https://github.blog
      twitter_username: github
      site_admin: false
  orgs:
    - login: my-org
      name: My Organization
      description: A test organization
      email: [email protected]
      members:
        - login: octocat
          role: admin
  repos:
    - owner: octocat
      name: hello-world
      description: My first repository
      language: JavaScript
      topics: [hello, world]
      default_branch: main
      private: false
    - owner: my-org
      name: org-repo
      description: An organization repository
      language: TypeScript
  oauth_apps:
    - client_id: Iv1.abc123
      client_secret: secret_abc123
      name: My Web App
      redirect_uris:
        - http://localhost:3000/api/auth/callback/github

組織の members は省略可能です。各エントリは login でシード済みユーザーを参照します。role のデフォルト値は member で、admin は組織の管理者ロールに対応します。不明なユーザーは無視されます。シードされたメンバーシップは合成された members チームを使用し、組織のプライベートリポジトリへのアクセス権を付与します。

auto_init: false が設定されていない限り、リポジトリはコミット、ブランチ、README とともに自動的に初期化されます。

ページネーション

すべての一覧エンドポイントは、Link ヘッダーとともに page および per_page クエリパラメーターをサポートします。

curl "http://localhost:4001/repos/octocat/hello-world/issues?page=1&per_page=10" \
  -H "Authorization: Bearer $TOKEN"

API エンドポイント

ユーザー
# Authenticated user
curl http://localhost:4001/user -H "Authorization: Bearer $TOKEN"

# Update profile
curl -X PATCH http://localhost:4001/user \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"bio": "Hello!"}'

# Get user by username
curl http://localhost:4001/users/octocat

# List users
curl http://localhost:4001/users

# User repos / orgs / followers / following
curl http://localhost:4001/users/octocat/repos
curl http://localhost:4001/users/octocat/orgs
curl http://localhost:4001/users/octocat/followers
curl http://localhost:4001/users/octocat/following

# User hovercard
curl http://localhost:4001/users/octocat/hovercard

# User emails
curl http://localhost:4001/user/emails -H "Authorization: Bearer $TOKEN"
リポジトリ
# Get repo
curl http://localhost:4001/repos/octocat/hello-world

# Get repo by numeric ID
curl http://localhost:4001/repositories/1

# Create user repo
curl -X POST http://localhost:4001/user/repos \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "new-repo", "private": false}'

# Create org repo
curl -X POST http://localhost:4001/orgs/my-org/repos \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "org-project"}'

# Update repo
curl -X PATCH http://localhost:4001/repos/octocat/hello-world \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"description": "Updated description"}'

# Delete repo (cascades issues, PRs, etc.)
curl -X DELETE http://localhost:4001/repos/octocat/hello-world \
  -H "Authorization: Bearer $TOKEN"

# Topics, languages, contributors, forks, collaborators, tags, transfer
コンテンツとコミット履歴
# Read a file or list a directory at a branch, tag, or commit
curl "http://localhost:4001/repos/octocat/hello-world/contents/README.md?ref=main"

# Request raw bytes from a file Contents or README response with GitHub's raw media type
curl "http://localhost:4001/repos/octocat/hello-world/contents/README.md?ref=main" \
  -H "Accept: application/vnd.github.raw+json"

# Download raw file content from the URL advertised by contents and commit responses
curl http://localhost:4001/octocat/hello-world/raw/main/README.md

# Create or update a file and commit the change
curl -X PUT http://localhost:4001/repos/octocat/hello-world/contents/notes.txt \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"message": "Update notes", "content": "aGVsbG8K"}'

# List commits, get a commit with file stats, or compare refs
curl http://localhost:4001/repos/octocat/hello-world/commits
curl http://localhost:4001/repos/octocat/hello-world/commits/main
curl http://localhost:4001/repos/octocat/hello-world/compare/v1.0.0...main
Issue
# List issues (filter by state, labels, assignee, milestone, creator, since)
curl "http://localhost:4001/repos/octocat/hello-world/issues?state=open&labels=bug"

# Create issue
curl -X POST http://localhost:4001/repos/octocat/hello-world/issues \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "Bug report", "body": "Details here", "labels": ["bug"]}'

# Get / update / lock / unlock / timeline / events / assignees
プルリクエスト
# List PRs
curl "http://localhost:4001/repos/octocat/hello-world/pulls?state=open"

# Create PR
curl -X POST http://localhost:4001/repos/octocat/hello-world/pulls \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "Feature", "head": "feature-branch", "base": "main"}'

# Merge PR (enforces branch protection)
curl -X PUT http://localhost:4001/repos/octocat/hello-world/pulls/1/merge \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"merge_method": "squash"}'

# Commits, files, requested reviewers, update branch
コメント
# Issue comments: full CRUD
curl http://localhost:4001/repos/octocat/hello-world/issues/1/comments
curl -X POST http://localhost:4001/repos/octocat/hello-world/issues/1/comments \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"body": "Looks good!"}'

# Comment by ID (cross-resource)
curl http://localhost:4001/repos/octocat/hello-world/issues/comments/1

# PR review comments
curl http://localhost:4001/repos/octocat/hello-world/pulls/1/comments

# Commit comments
curl http://localhost:4001/repos/octocat/hello-world/commits/abc123/comments

# Repo-wide comment listings
curl http://localhost:4001/repos/octocat/hello-world/issues/comments
curl http://localhost:4001/repos/octocat/hello-world/pulls/comments
curl http://localhost:4001/repos/octocat/hello-world/comments
レビュー
# List / create / get / update / submit / dismiss reviews
curl http://localhost:4001/repos/octocat/hello-world/pulls/1/reviews
curl -X POST http://localhost:4001/repos/octocat/hello-world/pulls/1/reviews \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"event": "APPROVE", "body": "LGTM"}'
ラベルとマイルストーン

ラベルとマイルストーンの CRUD 操作をすべてサポートします。Issue へのラベルの追加と削除、すべてのラベルの置き換えができます。マイルストーンのラベル一覧を取得できます。

ブランチと Git データ
# List branches
curl http://localhost:4001/repos/octocat/hello-world/branches

# Branch protection CRUD (status checks, PR reviews, enforce admins)
curl -X PUT http://localhost:4001/repos/octocat/hello-world/branches/main/protection \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"required_status_checks": {"strict": true, "contexts": ["ci"]}}'

# Refs, commits, trees (recursive), blobs, tags, matching-refs
Organization と Team
# List all orgs / user's orgs / get org / update org
curl http://localhost:4001/organizations
curl http://localhost:4001/user/orgs -H "Authorization: Bearer $TOKEN"
curl http://localhost:4001/orgs/my-org
curl -X PATCH http://localhost:4001/orgs/my-org \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"description": "Updated org"}'

# Org members: list, get, remove
curl http://localhost:4001/orgs/my-org/members
curl http://localhost:4001/orgs/my-org/members/octocat
curl -X DELETE http://localhost:4001/orgs/my-org/members/octocat \
  -H "Authorization: Bearer $TOKEN"

# Org memberships: get, set (invite/update role)
curl http://localhost:4001/orgs/my-org/memberships/octocat -H "Authorization: Bearer $TOKEN"
curl -X PUT http://localhost:4001/orgs/my-org/memberships/octocat \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"role": "admin"}'

# Teams: CRUD
curl http://localhost:4001/orgs/my-org/teams
curl -X POST http://localhost:4001/orgs/my-org/teams \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "engineering", "privacy": "closed"}'

# Team members and memberships
curl http://localhost:4001/orgs/my-org/teams/engineering/members
curl -X PUT http://localhost:4001/orgs/my-org/teams/engineering/memberships/octocat \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"role": "maintainer"}'

# Team repos: list, add, remove
curl http://localhost:4001/orgs/my-org/teams/engineering/repos
curl -X PUT http://localhost:4001/orgs/my-org/teams/engineering/repos/my-org/org-repo \
  -H "Authorization: Bearer $TOKEN"

# Legacy team endpoints by ID
curl http://localhost:4001/teams/1
curl http://localhost:4001/teams/1/members
GitHub Apps
# Get authenticated app (requires JWT auth)
curl http://localhost:4001/app \
  -H "Authorization: Bearer <jwt>"

# List app installations
curl http://localhost:4001/app/installations \
  -H "Authorization: Bearer <jwt>"

# Get installation
curl http://localhost:4001/app/installations/100 \
  -H "Authorization: Bearer <jwt>"

# Create installation access token (mints ghs_... token)
curl -X POST http://localhost:4001/app/installations/100/access_tokens \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{"permissions": {"contents": "read"}}'

# Find installation for repo / org / user
curl http://localhost:4001/repos/my-org/org-repo/installation
curl http://localhost:4001/orgs/my-org/installation
curl http://localhost:4001/users/octocat/installation

App の Webhook 配信:イベントが発生すると、エミュレーターは設定された webhook_url に event_callback ペイロードを X-GitHub-Event および X-Hub-Signature-256 ヘッダーとともに POST します。

リリース
# Create release
curl -X POST http://localhost:4001/repos/octocat/hello-world/releases \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tag_name": "v1.0.0", "name": "v1.0.0"}'

# List, get, latest, by tag, generate notes

# Release assets: list, upload
curl http://localhost:4001/repos/octocat/hello-world/releases/1/assets
curl -X POST http://localhost:4001/repos/octocat/hello-world/releases/1/assets \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/octet-stream" \
  -H "name: binary.zip" \
  --data-binary @binary.zip
Webhook
# Create webhook (real HTTP delivery on state changes)
curl -X POST http://localhost:4001/repos/octocat/hello-world/hooks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"config": {"url": "http://localhost:8080/webhook"}, "events": ["push", "pull_request"]}'

# Full CRUD, ping, test, deliveries
# Org webhooks also supported
検索
# Search repositories
curl "http://localhost:4001/search/repositories?q=language:JavaScript+user:octocat"

# Search issues and PRs
curl "http://localhost:4001/search/issues?q=repo:octocat/hello-world+is:open"

# Search users, code, commits, topics, labels
Actions
# Workflows: list, get, enable/disable, dispatch
# Workflow runs: list, get, cancel, rerun, delete, logs
# Jobs: list, get, logs
# Artifacts: list, get, delete
# Secrets: repo + org CRUD
Checks
# Create check run
curl -X POST http://localhost:4001/repos/octocat/hello-world/check-runs \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "CI", "head_sha": "abc123", "status": "completed", "conclusion": "success"}'

# Check suites: create, get, rerequest, preferences, list by ref. Ref based lookups accept branch and tag refs containing slashes.
# Check runs: list for suite, annotations. Ref based lookups accept branch and tag refs containing slashes.
# Automatic suite status rollup from check run results

ref および suite ごとの check-run 一覧は、ページネーション用の Link ヘッダーとともに page および per_page(デフォルトは 30、最大は 100)をサポートします。ref の一覧では、ページネーションの前に check_name、status、filter が適用されます。total_count はフィルター適用後の全件数を示します。

installation token で作成された Checks には、API レスポンスと Webhook に GitHub App が含まれます。suite は App とコミットごとに分けられ、filter=latest を指定すると、App と check 名ごとに最新の run のみが返されます。ユーザートークンで作成された Checks の app は null になります。リクエストボディの app_id では、帰属先を設定または変更できません。

OAuth
# Authorize (browser flow, shows user picker)
# GET /login/oauth/authorize?client_id=...&redirect_uri=...&scope=...&state=...

# Token exchange
curl -X POST http://localhost:4001/login/oauth/access_token \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"client_id": "Iv1.abc123", "client_secret": "secret_abc123", "code": "<code>"}'

# User emails
curl http://localhost:4001/user/emails -H "Authorization: Bearer $TOKEN"

# OAuth app management (settings)
curl http://localhost:4001/settings/applications -H "Authorization: Bearer $TOKEN"
curl http://localhost:4001/settings/connections/applications/Iv1.abc123 -H "Authorization: Bearer $TOKEN"

# Revoke OAuth app
curl -X POST http://localhost:4001/settings/connections/applications/Iv1.abc123/revoke \
  -H "Authorization: Bearer $TOKEN"
その他
curl http://localhost:4001/rate_limit
curl http://localhost:4001/meta
curl http://localhost:4001/emojis
curl http://localhost:4001/versions
curl http://localhost:4001/octocat
curl http://localhost:4001/zen

一般的なパターン

リポジトリ、Issue、プルリクエストの作成
TOKEN="test_token_admin"
BASE="http://localhost:4001"

# Create repo
curl -X POST $BASE/user/repos \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-project"}'

# Create issue
curl -X POST $BASE/repos/admin/my-project/issues \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "First issue"}'

# Create PR
curl -X POST $BASE/repos/admin/my-project/pulls \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "First PR", "head": "feature", "base": "main"}'
GitHub App のインストールトークンフロー
# 1. Sign a JWT with { iss: "12345" } using the app's private key (RS256)
# 2. Create an installation access token
curl -X POST $BASE/app/installations/100/access_tokens \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{"permissions": {"contents": "read", "issues": "write"}}'
# Returns { "token": "ghs_...", ... }

# 3. Use the installation token to call API endpoints
curl $BASE/repos/my-org/org-repo \
  -H "Authorization: Bearer ghs_..."

# Inspect minted installation-token metadata without token values
curl $BASE/_emulate/installation-tokens

検査用ルートはエミュレーター固有のものです。トークン値やトークン由来の識別子を受け取ったり返したりすることなく、App、インストール、アカウント、権限、リポジトリアクセス、発行、期限切れ、ライフサイクルのステータスを一覧表示します。有効期限は情報として表示され、認可の動作には影響しません。

OAuth フロー
  1. ユーザーを $GITHUB_EMULATOR_URL/login/oauth/authorize?client_id=...&redirect_uri=...&scope=user+repo&state=... にリダイレクトします
  2. ユーザーはエミュレーターの UI でシード済みユーザーを選択します
  3. エミュレーターは ?code=...&state=... を付けてリダイレクトします
  4. POST /login/oauth/access_token でコードをトークンに交換します
  5. トークンを使って API エンドポイントを呼び出します

ビルトインと並行して使うカスタムエミュレーター

npx emulate init --custom inventory を使って、サードパーティ API エミュレーターとテスト用のひな形を作成します。emulate の defineConfig を使い、{ emulator: "github" } などのビルトイン項目とともに emulate.config.ts に登録します。npx emulate start --watch を実行すると、インポートが再読み込みされ、表示された /_emulate URL でカスタム状態を確認できます。再読み込みが成功すると、実行状態はシードにリセットされます。既存のフラット形式のシード設定も引き続き使用できます。--config は明示的に指定したファイルを選択します。サードパーティ API エミュレーターの作成とテストについては、https://emulate.dev/docs/custom-emulators を参照してください。

コメント