GitHub 协作者权限管理

> 通过 gh CLI 管理 GitHub 仓库协作者,理解组织仓库与个人仓库的权限模型。


目录


前置条件

GitHub CLI 安装


# Ubuntu/Debian
(type -p wget >/dev/null || sudo apt update && sudo apt install wget -y) \
  && sudo mkdir -p -m 755 /etc/apt/keyrings \
  && wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \
  && sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
  && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/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 -y

# 验证安装
gh --version

认证


# 基于浏览器的交互式认证(推荐)
gh auth login

# 基于令牌的认证(无头环境)
gh auth login --with-token < /home/user/.config/gh/token.txt

# 检查当前认证状态
gh auth status

管理协作者所需的最小令牌权限范围:

# 创建具有指定权限范围的令牌
gh auth token  # 显示当前令牌(已脱敏)


添加与移除协作者

邀请协作者加入个人仓库


# 添加协作者(发送邀请)
gh api \
  -X PUT \
  /repos/:owner/:repo/collaborators/:username \
  -f permission=push

# 实际示例
gh api \
-X PUT \
/repos/BuckHuang/WebServer/collaborators/developer-name \
-f permission=push

个人仓库的权限级别:
权限 访问范围
read 拉取代码、查看 issues/wiki
triage 读取 + 管理 issues/PR
write 推送代码、管理 issues/PR
maintain 写入 + 管理仓库设置(无法执行破坏性操作)
admin 完全控制

移除协作者


# 移除协作者
gh api \
  -X DELETE \
  /repos/:owner/:repo/collaborators/:username

# 实际示例
gh api -X DELETE /repos/BuckHuang/WebServer/collaborators/developer-name

列出当前协作者


# 列出所有协作者
gh api /repos/BuckHuang/WebServer/collaborators --jq '.[].login'

# 列出协作者及其权限
gh api /repos/BuckHuang/WebServer/collaborators \
--jq '.[] | "\(.login): \(.permissions | to_entries | map(select(.value == true) | .key) | join(", "))"'

# 列出待处理的邀请
gh api /repos/BuckHuang/WebServer/invitations --jq '.[].invitee.login'

检查待处理的邀请


# 查看仓库的所有待处理邀请
gh api /repos/BuckHuang/WebServer/invitations

# 接受邀请(以被邀请用户身份执行)
gh api -X PATCH /user/repository_invitations/:invitation_id

# 拒绝邀请
gh api -X DELETE /user/repository_invitations/:invitation_id


gh CLI 命令参考

通用仓库命令


# 查看仓库详情
gh repo view BuckHuang/WebServer

# 克隆仓库
gh repo clone BuckHuang/WebServer

# 创建新仓库
gh repo create my-new-repo --private --clone

# Fork 仓库
gh repo fork owner/repo --clone

通过 gh api 管理协作者


# 添加协作者(邀请)
gh api -X PUT repos/BuckHuang/WebServer/collaborators/username \
  -f permission=push

# 更新协作者权限
gh api -X PUT repos/BuckHuang/WebServer/collaborators/username \
-f permission=admin

# 移除协作者
gh api -X DELETE repos/BuckHuang/WebServer/collaborators/username

# 检查用户是否为协作者
gh api repos/BuckHuang/WebServer/collaborators/username
# 返回 204 表示是协作者,返回 404 表示不是

批量操作


#!/bin/bash
# batch-add-collaborators.sh -- 向所有 FPGA 仓库批量添加协作者

REPOS=("Common" "Top" "WebServer" "Sensor" "Camera")
OWNER="BuckHuang"
PERMISSION="push"

# 从文件中读取用户名(每行一个)
USERNAMES_FILE="$1"

if [ ! -f "$USERNAMES_FILE" ]; then
echo "用法:$0 <用户名文件>"
exit 1
fi

while IFS= read -r username; do
[ -z "$username" ] && continue
echo "=== 正在将 $username 添加到所有仓库 ==="
for repo in "${REPOS[@]}"; do
echo -n " BuckHuang/$repo ... "
response=$(gh api -X PUT "repos/$OWNER/$repo/collaborators/$username" \
-f permission="$PERMISSION" 2>&1)
rc=$?
if [ $rc -eq 0 ] || echo "$response" | grep -q "already"; then
echo "正常"
else
echo "失败:$(echo "$response" | head -1)"
fi
done
done < "$USERNAMES_FILE"

调试 API 调用


# 详细输出
gh api --verbose /repos/BuckHuang/WebServer/collaborators

# 显示响应头
gh api --include /repos/BuckHuang/WebServer

# 使用 curl 等效方式进行测试
gh api /repos/BuckHuang/WebServer/collaborators --jq '.'


个人访问令牌(PAT)权限范围管理

令牌类型

令牌类型 使用场景 过期时间
细粒度令牌(新版) 仓库专用、限定权限范围 最长 1 年
经典令牌(旧版) 跨所有仓库的广泛权限 自定义/不限过期
OAuth(gh auth login) 完整 CLI 访问权限 刷新令牌

创建细粒度令牌


仓库权限:
  - Contents(内容):        读和写
  - Metadata(元数据):       只读(必选)
  - Pull requests(拉取请求): 读和写
  - Administration(管理):    读和写(管理协作者所需)

组织权限(如果管理组织仓库):
- Members(成员): 读和写

检查当前令牌权限范围


# 检查令牌有效性和权限范围
gh auth status

# 验证特定权限
gh api /user --jq '.login'
gh api /repos/BuckHuang/WebServer --jq '.permissions'

# 检查协作者管理功能是否正常
gh api /repos/BuckHuang/WebServer/collaborators > /dev/null && echo "正常" || echo "权限不足"

令牌存储最佳实践


# gh 将令牌存储在配置文件中
ls -la /home/user/.config/gh/

# 手动令牌存储(用于脚本)
echo "ghp_xxxxxxxxxxxxxxxxxxxx" > /home/user/.config/gh/token.txt
chmod 600 /home/user/.config/gh/token.txt

# 使用环境变量
export GITHUB_TOKEN=$(cat /home/user/.config/gh/token.txt)
gh auth status

安全规则:

组织仓库与个人仓库权限模型对比

个人仓库模型


所有者:BuckHuang
  ├── 协作者 A(push)
  ├── 协作者 B(admin)
  └── 协作者 C(read)

添加协作者直接授予其仓库级别的访问权限。

组织仓库模型


组织:BuckHuang-Org
  ├── 团队:FPGA-Devs → 包含仓库:Common、Top、WebServer
  │     ├── 成员 A(maintain)
  │     └── 成员 B(write)
  ├── 团队:External → 包含仓库:Sensor
  │     └── 外部顾问(read)
  └── 团队:Admins → 包含所有仓库
        └── BuckHuang(admin)

组织权限在团队级别进行管理,而非按仓库逐个管理。

关键区别

特性 个人仓库 组织仓库
邀请方式 直接邀请到仓库 通过团队成员资格
粒度 按仓库 通过团队按仓库
管理员 admin 权限 团队维护者或组织所有者
外部协作者 直接添加到仓库 作为外部协作者添加到组织
GitHub 免费版 协作者数量不限 免费版功能受限
适用场景 个人开发/小团队 团队/公司项目

通过 CLI 管理组织团队


# 列出组织团队
gh api /orgs/BuckHuang-Org/teams --jq '.[].name'

# 列出团队成员
gh api /orgs/BuckHuang-Org/teams/fpga-devs/members --jq '.[].login'

# 向团队添加成员
gh api -X PUT /orgs/BuckHuang-Org/teams/fpga-devs/memberships/username \
-f role=member

# 向团队添加仓库
gh api -X PUT /orgs/BuckHuang-Org/teams/fpga-devs/repos/BuckHuang-Org/WebServer \
-f permission=push

# 检查团队的仓库访问权限
gh api /orgs/BuckHuang-Org/teams/fpga-devs/repos --jq '.[].full_name'


git-share Skill 封装逻辑

概述

git-share skill 提供一个简化的接口,用于向私有仓库添加/移除 GitHub 协作者。它将 gh api 命令封装在一个用户友好的界面之下。

Skill 调用


# 添加协作者
git-share add <用户名> --repo <仓库名> [--permission push]
git-share add john-doe --repo WebServer

# 移除协作者
git-share remove <用户名> --repo <仓库名>
git-share remove john-doe --repo WebServer

# 列出协作者
git-share list --repo <仓库名>
git-share list --repo WebServer

# 添加到所有 5 个 FPGA 仓库
git-share add john-doe --all-fpga

实现:/home/user/.local/bin/git-share


#!/bin/bash
# git-share -- 私有仓库的 GitHub 协作者管理
# 用法:git-share <add|remove|list> <用户名> --repo <仓库> [--permission push]

set -euo pipefail

# 配置
OWNER="BuckHuang"
DEFAULT_PERMISSION="push"
FPGA_REPOS=("Common" "Top" "WebServer" "Sensor" "Camera")
REPOS_CONF="/home/user/.local/share/multi-repo/repos.conf"

# 颜色定义
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'

usage() {
cat << EOF
用法:git-share <命令> [选项]

命令:
add <用户名> --repo <仓库> [--permission push|read|admin] 添加协作者
add <用户名> --all-fpga [--permission push] 添加到所有 FPGA 仓库
remove <用户名> --repo <仓库> 移除协作者
list --repo <仓库> 列出协作者
check --repo <仓库> [用户名] 检查协作者状态

选项:
--repo <名称> 仓库短名称(例如:WebServer、Common)
--all-fpga 应用到所有 5 个 FPGA 仓库
--permission <级别> push | read | triage | maintain | admin(默认:push)
EOF
exit 1
}

# 将短名称解析为完整的仓库 full_name
resolve_repo() {
local name="$1"
echo "$OWNER/$name"
}

# 向单个仓库添加协作者
add_collaborator() {
local username="$1"
local repo="$2"
local permission="$3"

local full_repo; full_repo="$(resolve_repo "$repo")"

echo -n " ${repo} ... "

# 检查是否已经是协作者
status=$(gh api "repos/$full_repo/collaborators/$username" 2>&1)
if [ $? -eq 0 ]; then
echo -e "${YELLOW}已存在${NC}"
return 0
fi

# 添加协作者
response=$(gh api -X PUT "repos/$full_repo/collaborators/$username" \
-f permission="$permission" 2>&1)
rc=$?

if [ $rc -eq 0 ]; then
echo -e "${GREEN}已邀请${NC}(权限:$permission)"
elif echo "$response" | grep -qi "already"; then
echo -e "${YELLOW}已存在${NC}"
else
echo -e "${RED}失败${NC}:$(echo "$response" | jq -r '.message // "未知错误"')"
return 1
fi
}

# 从单个仓库移除协作者
remove_collaborator() {
local username="$1"
local repo="$2"

local full_repo; full_repo="$(resolve_repo "$repo")"

echo -n " ${repo} ... "

response=$(gh api -X DELETE "repos/$full_repo/collaborators/$username" 2>&1)
rc=$?

if [ $rc -eq 0 ]; then
echo -e "${GREEN}已移除${NC}"
elif [ $rc -eq 4 ]; then
echo -e "${YELLOW}未找到${NC}"
else
echo -e "${RED}失败${NC}:$(echo "$response" | jq -r '.message // "未知错误"')"
return 1
fi
}

# 列出协作者
list_collaborators() {
local repo="$1"
local full_repo; full_repo="$(resolve_repo "$repo")"

echo -e "=== ${full_repo} 的协作者 ==="

gh api "repos/$full_repo/collaborators" \
--jq '.[] | " \(.login) (\(.permissions | to_entries | map(select(.value == true) | .key) | join(", ")))"'

echo ""
echo "=== 待处理邀请 ==="
invitations=$(gh api "repos/$full_repo/invitations" --jq '.[].invitee.login' 2>/dev/null)
if [ -z "$invitations" ]; then
echo " (无)"
else
echo "$invitations" | while read -r user; do
echo " $user(等待接受)"
done
fi
}

# 检查协作者状态
check_collaborator() {
local repo="$1"
local username="$2"
local full_repo; full_repo="$(resolve_repo "$repo")"

if gh api "repos/$full_repo/collaborators/$username" >/dev/null 2>&1; then
local perms; perms=$(gh api "repos/$full_repo/collaborators/$username" \
--jq '.permissions | to_entries | map(select(.value == true) | .key) | join(", ")')
echo -e "${GREEN}是${NC} — $username 是 $full_repo 的协作者"
echo " 权限:$perms"
else
echo -e "${RED}否${NC} — $username 不是 $full_repo 的协作者"
fi
}

# ======== 主程序 ========

COMMAND="${1:-}"
shift || true

case "$COMMAND" in
add)
USERNAME="$1"; shift
PERMISSION="$DEFAULT_PERMISSION"
TARGET_REPO=""
ALL_FPGA=false

while [ $# -gt 0 ]; do
case "$1" in
--repo) TARGET_REPO="$2"; shift 2 ;;
--permission) PERMISSION="$2"; shift 2 ;;
--all-fpga) ALL_FPGA=true; shift ;;
*) usage ;;
esac
done

if $ALL_FPGA; then
echo -e "正在将 ${USERNAME} 共享到所有 5 个 FPGA 仓库(${PERMISSION})..."
for repo in "${FPGA_REPOS[@]}"; do
add_collaborator "$USERNAME" "$repo" "$PERMISSION"
done
elif [ -n "$TARGET_REPO" ]; then
echo -e "正在将 ${TARGET_REPO} 共享给 ${USERNAME}(${PERMISSION})..."
add_collaborator "$USERNAME" "$TARGET_REPO" "$PERMISSION"
else
usage
fi
;;

remove)
USERNAME="$1"; shift
TARGET_REPO=""

while [ $# -gt 0 ]; do
case "$1" in
--repo) TARGET_REPO="$2"; shift 2 ;;
*) usage ;;
esac
done

if [ -n "$TARGET_REPO" ]; then
echo -e "正在从 ${TARGET_REPO} 移除 ${USERNAME}..."
remove_collaborator "$USERNAME" "$TARGET_REPO"
else
usage
fi
;;

list)
TARGET_REPO=""
while [ $# -gt 0 ]; do
case "$1" in
--repo) TARGET_REPO="$2"; shift 2 ;;
*) usage ;;
esac
done
[ -z "$TARGET_REPO" ] && usage
list_collaborators "$TARGET_REPO"
;;

check)
TARGET_REPO=""
USERNAME=""
while [ $# -gt 0 ]; do
case "$1" in
--repo) TARGET_REPO="$2"; shift 2 ;;
*) USERNAME="$1"; shift ;;
esac
done
[ -z "$TARGET_REPO" ] && usage
check_collaborator "$TARGET_REPO" "$USERNAME"
;;

*)
usage
;;
esac


chmod +x /home/user/.local/bin/git-share

与 Claude Skills 的集成

Claude 的 git-share skill 封装了这个脚本:


Skill:git-share
用途:管理 GitHub 私有仓库的协作者共享

参数:通过自然语言描述要执行的操作
例如:"将 john 添加为 WebServer 的管理员"
"从所有仓库中移除 alice"
"列出 Common 的协作者"

实现方式:
1. 解析用户请求以提取:操作、用户名、仓库、权限
2. 使用 repos.conf 映射解析仓库短名称
3. 使用适当的参数执行 git-share 脚本
4. 向用户报告结果

故障排查

错误 原因 解决方法
gh auth status 显示"未登录" 令牌已过期或未设置 gh auth login
Resource not accessible by integration 细粒度令牌缺少仓库访问权限 在令牌设置中添加仓库
Must have admin rights 当前用户没有管理员权限 需要仓库所有者授予权限
Invitation already exists 用户已收到邀请(等待处理中) 重新发送或检查待处理列表
Not Found (404) 仓库名拼写错误或仓库是私有的 检查 owner/repo 拼写

验证一切是否正常工作


# 端到端测试
git-share list --repo WebServer
git-share check --repo WebServer some-user
git-share add test-collaborator --repo WebServer --permission read
git-share list --repo WebServer   # 应该显示新的邀请
git-share remove test-collaborator --repo WebServer


快速参考卡片


# 查看当前登录用户
gh auth status

# 列出我的仓库
gh repo list BuckHuang --limit 30

# 向单个仓库添加协作者
git-share add <用户> --repo <名称> [--permission push]

# 向所有 FPGA 仓库添加协作者
git-share add <用户> --all-fpga

# 移除协作者
git-share remove <用户> --repo <名称>

# 列出协作者
git-share list --repo <名称>

# 检查某人是否有访问权限
git-share check --repo <名称> <用户>