You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GitHub App用安装令牌克隆私有仓库提示不存在,如何解决?

问题描述

我配置了一个监听GitHub事件的GitHub App,已安装到私有仓库且拥有读写权限。使用git clone https://x-access-token:<TOKEN>@github.com/owner/repo_name.git命令时出现致命错误,提示仓库不存在。我看过Stack Overflow上的《Authentication issue》问题,但它适用于客户端到服务器的请求,而非我的服务器到服务器场景。

我参考GitHub官方的App认证相关文档,编写了生成JWT、获取安装访问令牌、转换克隆URL的函数,最后用GitPython尝试克隆仓库,但始终报错。相关代码如下:

生成JWT的函数

def generate_jwt(app_id=APP_ID, private_key_path=APP_PRIVATE_KEY_PATH):
    """
    This function generates a JWT token for the GitHub App.

    Args:
        app_id (str): The GitHub App ID.
        private_key_path (pathlib.Path): The path to the private key file.

    Returns:
        str: The JWT token.
    """
    with open(private_key_path, 'r', encoding='utf-8') as key_file:
        private_key = key_file.read()

    payload = {
        'iat': datetime.now(),
        'exp': datetime.now() + timedelta(minutes=10),
        'iss': app_id
    }
    return jwt.encode(payload, private_key, algorithm='RS256')

获取安装访问令牌的函数

def get_installation_access_token(installation_id) -> str:
    """
    Get the installation access token for the GitHub App.

    Args:
        installation_id (str): The installation id access token.
    Returns:
        str: The installation access token.
    """
    if installation_id is None:
        return "No Installation ID"

    url = f"https://api.github.com/app/installations/{installation_id}/access_tokens"
    app_jwt = generate_jwt()

    # Make the API request
    headers = {
        "Authorization": f"Bearer {app_jwt}",
        "Accept": "application/vnd.github+json",
        "X-Github-Api-Version": "2022-11-28",
    }
    response = requests.post(url, headers=headers, timeout=10)

    # Extract the installation access token
    response.raise_for_status()
    installation_access_token = response.json()["token"]

    return installation_access_token

转换克隆URL的函数

def convert_clone_url_to_autenticated_url(clone_url: str, installation_access_token: str) -> str:
    """
    Convert the clone URL to an authenticated URL.

    Args:
        clone_url (str): The clone URL.
        installation_access_token (str): The installation access token.

    Returns:
        str: The authenticated URL.
    """
    # Parse the clone URL
    if not clone_url.startswith("https://github.com/"):
        raise ValueError("Invalid clone URL. It must start with 'https://github.com/'.")
    
    return clone_url.replace(
        "https://github.com/",
        f"https://x-access-token:{installation_access_token}@github.com/"
    )

GitPython克隆代码

# Clone the repository
repo = None
if not repo_dir.exists():
    repo = git.Repo.clone_from(authenticated_url, repo_dir)
else:
    repo = git.Repo(repo_dir)
解决方案
  • 修复JWT生成的时间戳格式
    datetime.now()生成的是本地时间对象,但JWT要求的是Unix时间戳(秒数)。需要替换为UTC时间并转换为时间戳:

    payload = {
        'iat': int(datetime.utcnow().timestamp()),
        'exp': int((datetime.utcnow() + timedelta(minutes=10)).timestamp()),
        'iss': app_id
    }
    

    错误的时间格式会导致JWT无效,进而获取到的安装令牌没有权限访问仓库,最终提示仓库不存在。

  • 检查安装令牌的权限范围
    确保GitHub App在安装时被授予了contents:read权限(私有仓库克隆需要该权限)。可以在GitHub App的设置页面确认权限配置,重新安装到仓库以应用权限变更。

  • 处理令牌中的特殊字符
    安装访问令牌可能包含斜杠、等号等特殊字符,直接拼接到URL中会导致URL解析错误。需要对令牌进行URL编码:

    import urllib.parse
    
    def convert_clone_url_to_autenticated_url(clone_url: str, installation_access_token: str) -> str:
        if not clone_url.startswith("https://github.com/"):
            raise ValueError("Invalid clone URL. It must start with 'https://github.com/'.")
        
        encoded_token = urllib.parse.quote_plus(installation_access_token)
        return clone_url.replace(
            "https://github.com/",
            f"https://x-access-token:{encoded_token}@github.com/"
        )
    
  • 验证安装令牌的有效性
    在获取令牌后,调用GitHub API验证令牌权限:

    def validate_token(token):
        headers = {
            "Authorization": f"token {token}",
            "Accept": "application/vnd.github+json",
            "X-Github-Api-Version": "2022-11-28",
        }
        # 替换为你的仓库API路径
        response = requests.get("https://api.github.com/repos/owner/repo_name", headers=headers)
        print(response.status_code, response.json())
    

    如果返回404,说明令牌没有访问该仓库的权限;如果返回200,说明令牌有效,问题出在克隆URL或GitPython的调用上。

  • 改用GitPython的认证参数传递方式
    避免直接拼接URL,改用GitPython的auth参数传递凭证,更安全且不易出错:

    from git import Repo
    from git.exc import GitCommandError
    import os
    
    repo_dir = "./repo"
    clone_url = "https://github.com/owner/repo_name.git"
    token = get_installation_access_token(INSTALLATION_ID)
    
    if not os.path.exists(repo_dir):
        try:
            # 使用HTTPBasicAuth传递凭证
            repo = Repo.clone_from(
                clone_url,
                repo_dir,
                auth=("x-access-token", token)
            )
        except GitCommandError as e:
            print(f"克隆失败: {e}")
    else:
        repo = Repo(repo_dir)
    

内容的提问来源于stack exchange,提问作者newkid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 13:43:15