如何使用命令行从私有仓库下载GitHub Release

85

GitHub指南解释了两种授权方式,但看起来这两种方式都不能用于Release文件。

因此,导致:

curl -u 'username' -L -o a.tgz https://github.com/company/repository/releases/download/TAG-NAME/A.tgz

总是有类似的东西

<!DOCTYPE html>
<!--
Hello future GitHubber! ...

到目前为止我找到的最佳答案是这个:https://dev59.com/8V8e5IYBdhLWcg3wRIlQ - Aleš
2
gh release download ... - https://cli.github.com - Vladimir Vukanac
10个回答

89

要从私有仓库下载发布文件,可以使用个人访问令牌,该令牌可以在settings/tokens生成,需要授权完全控制私有存储库

然后使用curl命令下载资产(更改为适当的值):

curl -vLJO -H 'Authorization: token my_access_token' 'https://api.github.com/repos/:owner/:repo/releases/assets/:id'

或者,如果您正在使用OAuth应用程序,请使用:

curl -u my_client_id:my_client_secret https://api.github.com/repos/:owner/:repo/releases/assets/:id
  • :owner 代表您的用户或组织用户名;
  • :repo 代表您的仓库名称;
  • :id 是您的资产ID,可以在标签发布的URL中找到,例如:

https://api.github.com/repos/:owner/:repo/releases/tags/:tag 
  • :token 是您的个人访问令牌(可以在/settings/tokens创建);

  • 注意:使用access_token作为查询参数已被弃用

    参见:GitHub上的存储库API v3


    这是一个Bash脚本,它可以根据给定的文件名下载资产文件:

    #!/usr/bin/env bash
    # Script to download asset file from tag release using GitHub API v3.
    # See: https://dev59.com/MWIj5IYBdhLWcg3wUjkX#35688093    
    CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"
    
    # Check dependencies.
    set -e
    type curl grep sed tr >&2
    xargs=$(which gxargs || which xargs)
    
    # Validate settings.
    [ -f ~/.secrets ] && source ~/.secrets
    [ "$GITHUB_API_TOKEN" ] || { echo "Error: Please define GITHUB_API_TOKEN variable." >&2; exit 1; }
    [ $# -ne 4 ] && { echo "Usage: $0 [owner] [repo] [tag] [name]"; exit 1; }
    [ "$TRACE" ] && set -x
    read owner repo tag name <<<$@
    
    # Define variables.
    GH_API="https://api.github.com"
    GH_REPO="$GH_API/repos/$owner/$repo"
    GH_TAGS="$GH_REPO/releases/tags/$tag"
    AUTH="Authorization: token $GITHUB_API_TOKEN"
    WGET_ARGS="--content-disposition --auth-no-challenge --no-cookie"
    CURL_ARGS="-LJO#"
    
    # Validate token.
    curl -o /dev/null -sH "$AUTH" $GH_REPO || { echo "Error: Invalid repo, token or network issue!";  exit 1; }
    
    # Read asset tags.
    response=$(curl -sH "$AUTH" $GH_TAGS)
    # Get ID of the asset based on given name.
    eval $(echo "$response" | grep -C3 "name.:.\+$name" | grep -w id | tr : = | tr -cd '[[:alnum:]]=')
    #id=$(echo "$response" | jq --arg name "$name" '.assets[] | select(.name == $name).id') # If jq is installed, this can be used instead. 
    [ "$id" ] || { echo "Error: Failed to get asset id, response: $response" | awk 'length($0)<100' >&2; exit 1; }
    GH_ASSET="$GH_REPO/releases/assets/$id"
    
    # Download asset file.
    echo "Downloading asset..." >&2
    curl $CURL_ARGS -H "Authorization: token $GITHUB_API_TOKEN" -H 'Accept: application/octet-stream' "$GH_ASSET"
    echo "$0 done." >&2
    

    在运行之前,您需要使用GitHub令牌设置您的GITHUB_API_TOKEN(请参见GH上的/settings/tokens)。这可以放在您的~/.secrets文件中,例如:

    GITHUB_API_TOKEN=XXX
    

    示例脚本用法:

    ./get_gh_asset.sh :owner :repo :tag :name
    

    这里的 name 是你的文件名(或部分文件名)。在脚本前加上 TRACE=1 来进行调试。


    如果你想知道为什么有时候使用 curl 会失败(如其他答案中所提到的):

    Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified.

    运行以下命令时出现该错误:

    curl -vLJ -H 'Authorization: token <token>' -H 'Accept: application/octet-stream' https://api.github.com/repos/:owner/:repo/releases/assets/<id>
    
    这是因为您同时指定了多个机制,因此S3服务器不知道要使用哪一个,因此您必须只选择一个,例如:
    • X-Amz-Algorithm查询参数
    • 签名查询字符串参数(X-Amz-Signature
    • 授权标头(Authorization: token <token>
    由于GitHub会在从资源页面重定向时自动填充查询字符串中的凭据(当请求application/octet-stream时),因此curl在请求标头中传递相同的凭据(您已指定),因此它们会发生冲突。因此,作为解决方法,您可以使用access_token代替。

    1
    感谢@kenorb提供的脚本,我将使用它允许我的ansible playbook从私有仓库中获取发布.deb文件。非常感谢。 - Russell Ormes
    1
    感谢提供脚本。如果已安装 jq JSON 工具,则可以使用 ASSET_ID=$(echo "$response" | jq --arg name "$name" '.assets[] | select(.name == $name).id') 或类似的方式大大简化提取资产 ID 的过程。 - Max
    1
    @joonas.fi 谢谢您的建议,我已经更新了我的回答。 - kenorb
    16
    获取私有仓库中的发布数据需要“完全控制私有仓库”权限吗?是否有其他权限可以使用,因为我不想授权所有权限,只想授权“仅限发布”。 - Ecto Ruseff
    1
    同意@EctoRuseff的观点,这个访问级别做这件事情真的很可怕:o - eglasius
    显示剩余5条评论

    27
    我们经常需要从私有 GitHub 存储库下载发行版资源,因此我们创建了 fetch。该工具是一个开源、跨平台的工具,使得从公共和私有 GitHub 存储库的 git tag、commit 或 branch 下载源代码文件和发行版资源变得非常容易。
    例如,要将私有 GitHub 存储库版本 0.1.3 中的发行版资源 foo.exe 下载到 /tmp,应执行以下操作:
    GITHUB_OAUTH_TOKEN="your token"
    fetch --repo="https://github.com/foo/bar" --tag="0.1.3" --release-asset="foo.exe" /tmp
    

    5
    在这里添加一个评论,fetch是一个独立的可执行文件,完全可以满足原帖作者的要求。我也将其纳入了我们团队的构建流程中。 - Steve Hollasch
    2
    只有这个答案对我有效。我试图从私人仓库的Github发布中获取一个特定的资产,只有这个方法起作用。 - jimh
    我实际上必须指定 --github-oauth-token=${GITHUB_OAUTH_TOKEN} 标志。然后运行得非常出色。 - M. Rodo

    10

    看起来这两种身份验证方法只适用于API端点。有一个API端点可用于下载发布资产(文档):

    GET /repos/:owner/:repo/releases/assets/:id
    

    但是,要做到这一点,人们需要知道资产的数字ID。 我向他们的支持团队询问是否可以添加一个API端点来按名称下载发布的资产(就像他们为tarballs提供的那样)。


    更新:来自Github支持团队的回复:

      

    我们不提供您建议的任何API。 我们试图避免脆弱的URL,如果发布资产的标签或文件名更改,它们将发生变化。 我会将您的反馈传达给团队进一步讨论。


    我正在寻找一种简单的方法来获取最新的资产。只要我理解正确,我需要获取发布列表,选择最新的id,获取具有资产id的发布,并最终下载资产。是否有一种通过标签名称下载资产的方法? - Simon
    这里有一种技术的示例:http://brantiffy.axisj.com/archives/215,https://github.com/brant-hwang/get-git-private-repo-latest-release-zip。你可以调整代码以使用标签名称而不是ID等。 - David

    7
    使用gh release download命令可以轻松脚本下载。在执行前请先使用gh auth login授权。
    要下载示例URL https://github.com/company/repository/releases/download/TAG-NAME/A.tgz,请使用以下命令: gh release download --repo company/repository TAG-NAME -p 'A.tgz'

    6
    这里是一个使用 curljq 的一行命令:
    CURL="curl -H 'Authorization: token <auth_token>' \
          https://api.github.com/repos/<owner>/<repo>/releases"; \
    ASSET_ID=$(eval "$CURL/tags/<tag>" | jq .assets[0].id); \
    eval "$CURL/assets/$ASSET_ID -LJOH 'Accept: application/octet-stream'"
    

    将用<>括起来的部分更改为您的数据。 要生成auth_token,请前往github.com/settings/tokens

    如果您想使用密码登录,请使用此选项(请注意,它会要求您输入两次密码):

    CURL="curl -u <github_user> https://api.github.com/repos/<owner>/<repo>/releases"; \
    ASSET_ID=$(eval "$CURL/tags/<tag>" | jq .assets[0].id); \
    eval "$CURL/assets/$ASSET_ID -LJOH 'Accept: application/octet-stream'"
    

    1
    您可以将 tags/<tag> 替换为 latest 以获取最新版本的发布。 - Miguel
    最新版本似乎只需要 /latest,即 "$CURL/latest" - fooquency
    新的Github令牌系统似乎需要使用-u方法,即“-u用户名:令牌”。 - fooquency
    你假设你的目标始终是.assets数组中的第一项。但事实可能并非如此。这就是为什么其他人使用jqselect()来搜索正确的.name的原因。 - Cliff
    1
    谢谢 @Cliff!我编辑了我的回答,允许通过名称指定资产,并删除了密码验证,因为不支持。 - undefined

    6
    我在这个评论中找到了答案:https://github.com/request/request/pull/1058#issuecomment-55285276 curl将身份验证标头转发到存储Github发布资产的AmazonS3存储桶中的请求中。 来自S3的错误响应:
    <?xml version="1.0" encoding="UTF-8"?>
    <Error>
     <Code>InvalidArgument</Code>
     <Message>
       Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified
     </Message>
     <ArgumentName>Authorization</ArgumentName>
     <ArgumentValue>token <yourtoken> </ArgumentValue><RequestId>4BEDDBA630688865</RequestId> <HostId>SXLsRKgKM6tPa/K7g7tSOWmQEqowG/4kf6cwOmnpObXrSzUt4bzOFuihmzxK6+gx</HostId>
    </Error>
    

    一行wget解决方案:
    wget --auth-no-challenge --header='Accept:application/octet-stream' https://<token>:@api.github.com/repos/:owner/:repo/releases/assets/:id -O app.zip
    

    尝试执行以下命令:curl -i -H "Authorization: token <token>" -H "Accept:application/octet-stream" https://<token>:@api.github.com/repos/:owner/:repo/releases/assets/:id,以获取更多详细信息。如果想要查看S3错误消息,请添加-L参数。


    你能解释一下在你的解决方案中如何生成 <token> 吗?如果这是一个愚蠢的问题,我很抱歉。 - Dominic Sayers
    1
    我想我已经回答了自己的问题。Github API身份验证在这里有文档:https://developer.github.com/v3/#authentication - Dominic Sayers
    如果您使用curl,则解决方法是将身份验证放在标题之外。例如作为查询字符串参数方法(根据API文档,"URL?access_token=TOKEN")。您能否在curl中执行"http://TOKEN:@api.github.com/..."? - David

    4

    以下是一个使用wget进行HTTP请求并使用python进行JSON解析的"一行代码":

    (export AUTH_TOKEN=<oauth-token>; \
     export ASSET_ID=$(wget -O - https://api.github.com/repos/<owner>/<repo>/releases/tags/<tag>?access_token=$AUTH_TOKEN | python -c 'import sys, json; print json.load(sys.stdin)["assets"][0]["id"]'); \
     wget --header='Accept:application/octet-stream' -O <download-name> https://api.github.com/repos/<owner>/<repo>/releases/assets/$ASSET_ID?access_token=$AUTH_TOKEN)
    

    要使用它,只需将<oauth-token><owner><repo><tag><download-name>替换为适当的值。
    说明:
    - 第一条语句(export AUTH_TOKEN=<oauth-token>)设置了GitHub OAuth令牌,该令牌由后续的wget命令使用。 - 第二条语句有两部分: - wget -O - https://api.github.com/repos/<owner>/<repo>/releases/tags/<tag>?access_token=$AUTH_TOKEN部分获取了GitHub发布信息的标签名称,并将其打印在stdout上。 - python -c 'import sys, json; print json.load(sys.stdin)["assets"][0]["id"]'部分从stdin解析JSON并提取第一个发布资产的id。 - 第三条语句(wget --header='Accept:application/octet-stream' -O <tarball-name>.tar.gz https://api.github.com/repos/<owner>/<repo>/releases/assets/$ASSET_ID?access_token=$AUTH_TOKEN通过id获取单个GitHub发布资产并将其存储到文件中。 - 外部括号创建了一个子shell,并确保导出的环境变量在之后被丢弃。

    https://developer.github.com/changes/2020-02-10-deprecating-auth-through-query-param - ניר

    4
    更简单的解决方案是使用 .netrc 存储凭据。这样,curl 不会将凭据转发到 Amazon S3 Bucket。
    在 ~/.netrc 文件中(应该用 0600 文件权限创建):
    machine api.github.com
    login yourusername
    password yourpassword
    

    然后使用curl -n选项来使用.netrc文件:

    curl -L -O -J -n -H "Accept:application/octet-stream" https://api.github.com/repos/:owner/:repo/releases/assets/:id
    

    很遗憾,当你在动态主机/配置上运行时,这个选项会更麻烦一些,因为它们默认没有 .netrc 文件,比如新生成的虚拟机/环境,你无法定义预配置的镜像。例如 CircleCI。 - David
    @David 嗯,你也可以在脚本的头部以编程方式将这3行写入 .netrc 文件中,这仍然是最简单的解决方案。 - FF_Dev
    它也可以在Windows上运行,但netrc文件可能被命名为/_netrc而不是/.netrc。 - FF_Dev
    作为一种警告:不建议将令牌放入服务器上的.netrc文件中。潜在的黑客可以利用这一点来获取对GitHub上所有用户私有存储库的完全访问权限。 - pfrenssen

    2

    像 @dwayne 一样,我使用了 gh CLI 来解决这个问题,你也需要安装。因为我在 Docker 容器中,所以我需要安装 curldirmngr,然后安装 gh CLI 并按以下方式下载发布版。下面还展示了如何使用个人令牌对 gh CLI 进行身份验证,并解压缩发布文件(在我的情况下是一个 tarball)。

    FROM debian:10.9 as base
    RUN apt update \
     # Install package dependencies
     && apt install -y \
        build-essential \
        wget \
        dirmngr \
        curl
    
    # Install GH CLI - see https://github.com/cli/cli/blob/trunk/docs/install_linux.md
    RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | gpg --dearmor -o /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" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null && \
       apt update && \
       apt install -y gh
    
    # auth to github with PAT and download release
    ARG RELEASE_TOKEN
    RUN echo $RELEASE_TOKEN | gh auth login --with-token && \
       cd /opt && \
       gh release download YOUR_TAG_HERE --repo https://github.com/SOME_ORG/SOME-REPO && \
       tar -xjf SOME-RELEASE-FILES.tar.bz2 -C /opt && \
       rm SOME-RELEASE-FILES.tar.bz2
    

    0

    只使用curl的一行方式来完成这个操作,其中:

    # YOUR_TOKEN = your access token e.g. ghp_xxxxxxxxxxxxx
    # YOUR_GITHUBCOM_URL = the portion of the URL that comes after https://github.com/ e.g. myuser/myrepo/assets/127345322/d0ad7915-a6a3-4aef-adf7-369c95e13316
    # YOUR_DESTINATION = the location on your local drive to save the result to e.g. /tmp/x.png
    
    curl "$(TOKEN=YOUR_TOKEN GITHUBCOM_URL=YOUR_GITHUBCOM_URL && curl -i -H "Authorization: token $TOKEN" https://github.com/$GITHUBCOM_URL | grep '^location: ' | sed 's/location: //' | tr -d '\r' )" > YOUR_DESTINATION
    

    在MacOS上进行测试

    解释:

    1. 内部的curl使用您的令牌从github.com获取元数据,其中包含一个包含要获取的实际内容的已签名URL
    2. 解析元数据以检索以location: 开头的行
    3. 删除location: 前缀和结尾的\r后缀,只保留URL
    4. 外部的curl使用该URL检索资源

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接