从GitHub存储库克隆/下载特定文件



GitHub上有一个名为platform_frameworks_base的Git存储库,其中包含部分Android源代码。
我编写了一个应用程序,它对该项目中的所有.aidl文件进行回复,因此它在第一次启动时将它们全部下载。
直到现在我都是通过下载Android文件来实现的。从该文件中提取所有以.aidl结尾的文件路径,然后逐个显式下载它们。

例如,如果我找到这个文件路径:

media/java/android/media/IAudioService.aidl

我知道我可以这样下载:

wget https://raw.githubusercontent.com/aosp-mirror/platform_frameworks_base/android-10.0.0_r47/media/java/android/media/IAudioService.aidl

在Android 10 (git tag:android-10.0.0_r47)之前可以正常工作。
从Android 11开始(例如git tag:android-11.0.0_r33),文件路径使用通配符而不是完整路径。查看Android.bp.

现在只包含通配符/glob文件路径,如:

media/java/**/*.aidl
location/java/**/*.aidl

等等……

我当前的解决方案";

  1. 克隆repo(只克隆我们关心的分支的最后一次提交):

    git clone --depth=1 -b android-11.0.0_r33 https://github.com/aosp-mirror/platform_frameworks_base.git

  2. 从Android.bp中提取通配符/glob路径。

    cat Android.bp | grep '.aidl"' | cut -d'"' -f2

  3. 查找所有匹配通配符/glob路径的文件。

    shopt -s globstar && echo media/java/**/*.aidl

但是下载过程太长了,因为存储库包含超过1 gb 二进制文件。即使我只是克隆我关心的分支的最后一次提交。

现在我的实际问题是:
我如何才能下载我真正关心的.aidl文件?(理想情况下无需解析GitHub中每个文件夹的HTML)

我如何下载/克隆没有所有二进制文件的存储库?

编辑:

我尝试使用GitHub API递归地遍历所有目录,但我立即得到一个API速率限制超过错误:

g_aidlFiles=""
# Recursively go through all directories and the paths to all found .aidl files in the global g_aidlFile variable
GetAidlFilesFromGithub() {
l_dirUrl="${1-}"
if [ "$l_dirUrl" == "" ]; then
echo "ERROR: Directory URL not provided in GetAidlFilesFromGithub"
exit 1
fi

echo "l_dirUrl: ${l_dirUrl}"

l_rawRes="$(curl -s -i $l_dirUrl)"
l_statusCode="$(echo "$l_rawRes" | grep HTTP | head -1 | cut -d' ' -f2)"
l_resBody="$(echo "$l_rawRes" | sed '1,/^s*$/d')"
if [[ $l_statusCode == 4* ]] || [[ $l_statusCode == 5* ]]; then
echo "ERROR: Request failed!"
echo "Response status: $l_statusCode"
echo "Reponse body:"
echo "$l_resBody"
exit 1
fi

l_currentDirJson="$(echo "$l_resBody")"
if [ "$l_currentDirJson" == "" ]; then
echo "ERROR: l_currentDirJson is empty"
exit 1
fi

l_newAidlFiles="$(echo "$l_currentDirJson" | jq '.[] | select(.type=="file") | select(.path | endswith(".aidl")) | .path')"

if [ "$l_newAidlFiles" != "" ]; then
echo "l_newAidlFiles: ${l_newAidlFiles}"
g_aidlFiles="${g_aidlFiles}n${l_newAidlFiles}"
fi
l_subDirUrls="$(echo "$l_currentDirJson" | jq '.[] | select(.type=="dir") | .url')"
if [ "$l_subDirUrls" != "" ]; then
echo "$l_subDirUrls" | while IFS= read -r l_subDirUrl ; do 
(GetAidlFilesFromGithub "$l_subDirUrl")
done
else
echo "No subdirs found."
fi
}
GetAidlFilesFromGithub "https://api.github.com/repos/aosp-mirror/platform_frameworks_base/contents?ref=android-11.0.0_r33"

据我所知,我的所有用户都必须创建一个GitHub帐户并创建一个OAUTH密钥来提高限制。那绝对不是我的选择。我希望我的应用程序易于使用。

由于repo在GitHub上,它支持过滤器,最简单的可能是使用它的过滤器支持。

git clone --no-checkout --depth=1 --filter=blob:none 
https://github.com/aosp-mirror/platform_frameworks_base
cd platform_frameworks_base
git reset -q -- *.aidl
git checkout-index -a

,这可能会被巧妙地处理一下,使文件以单个包的形式发送,而不是每次获取一次。

例如,把blob:none改成blob:limit=16384,这就把它们中的大多数放在了前面。

要在自己的代码中这样做,而不依赖于Git安装,您需要实现Git协议。下面是在线介绍,其中包含指向实际Git文档的指针。这并不难,你来回发送文本行,直到服务器吐出你想要的大量数据,然后你从中挑选。你不需要使用https, github支持普通的git协议。试着用GIT_TRACE=1 GIT_PACKET_TRACE=1运行克隆命令。

不确定这是否是你想要的:

#!/usr/bin/env bash

get_github_file_list(){
local user=$1 repo=$2 branch=$3
curl -s "https://api.github.com/repos/$user/$repo/git/trees/$branch?recursive=1"
}
get_github_file_list aosp-mirror platform_frameworks_base android-11.0.0_r33 |
jq -r '.tree|map(.path|select(test("\.aidl")))[]'

您可以使用GitHub API代码搜索端点来获取路径,但随后使用wget raw。Githubusercontent方法:

apiurlbase='https://api.github.com/search/code?per_page=100&q=repo:aosp-mirror/platform_frameworks_base+extension:aidl'
dlurlbase='https://raw.githubusercontent.com/aosp-mirror/platform_frameworks_base/android-10.0.0_r47/'
apiurl1="$apiurlbase+path:/media/java/"
apiurl2="$apiurlbase+path:/location/java/"
for apiurl in "$apiurl1" "$apiurl2"; do
page=1
while paths=$(
curl -s "$apiurl&page=$page" | grep '"path": ' | grep -o '[^"]+.aidl'
); do
# do your stuff with the $paths
page=$(($page + 1))
done
done

不幸的是,GitHub API代码搜索端点只搜索默认分支(在本例中是master),而你想要android-10.0.0_r47标签。android-10.0.0_r47中可能有文件,但master中没有,这段代码无法找到并下载这些文件。

另一个解决方案是对你感兴趣的每个标签做一个非常小的克隆,然后使用git ls-tree来获取每个标签的路径,例如

for tag in 'android-10.0.0_r47' 'android-11.0.0_r33'; do
git clone --branch "$tag" --depth=1 --bare --no-checkout 
--filter=blob:limit=0 git@github.com:aosp-mirror/platform_frameworks_base.git
# only a 1.8M download
mv platform_frameworks_base.git "$tag"
cd "$tag"
paths=$(git ls-tree -r HEAD --name-only | grep '.aidl$')
# do your stuff with the paths
cd ..
done

如果这是为了自己使用,我可能不会使用这两个方法中的任何一个。我会克隆整个庞大的仓库一次,然后在本地使用它,例如

if [ -e platform_frameworks_base ]; then
cd platform_frameworks_base
git pull
else
git clone git@github.com:aosp-mirror/platform_frameworks_base.git
cd platform_frameworks_base
fi
tags=$(git tag | grep '^android')
for tag in $tags; do
git checkout $tag
paths=$(git ls-tree -r HEAD --name-only | grep '.aidl$')
# do your stuff with the paths
done

假设我要维护一个文本文件,它会在每次提交之前自动更新为最新的repo文件树。

脚本应该易于编写,并且运行速度快,因为所有这些都是在本地发生的。它可以通过引入一个新的工作过程来手动调用,或者集成到您的测试/CI自动化过程中。

然后你知道在你的最终用户应用程序中该怎么做,首先下载这个文件,用Android.bp过滤它,然后用Github原始内容链接提取你想要的文件。

最新更新