github proxy clone and download util
·
因为github老是既clone不到,也download release经常失败
- 好在有ghproxy.cn等网站做代理,包括ghproxy.net, ghproxy.cc(虽然不知道多久死链)
- 还有github api可以帮助我查看release tag内容
github_proxy_util.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define PROXY_PREFIX "https://ghproxy.cn/"
#define GITHUB_BASE "https://github.com/"
#define REPO_SUFFIX ".git"
#define GITHUB_API_BASE "https://api.github.com/repos/"
// 检查Windows系统中是否存在指定命令
int has_command(const char *cmd) {
char command[256];
snprintf(command, sizeof(command), "where %s > nul 2>&1", cmd);
return system(command) == 0;
}
// 代理克隆仓库
int proxy_git_clone(const char *owner, const char *repo) {
if (!owner || !repo || !*owner || !*repo) {
fprintf(stderr, "错误: 仓库所有者和名称不能为空\n");
return 1;
}
char proxy_url[512];
snprintf(proxy_url, sizeof(proxy_url), "%s%s%s/%s%s",
PROXY_PREFIX, GITHUB_BASE, owner, repo, REPO_SUFFIX);
char command[1024];
snprintf(command, sizeof(command), "git clone \"%s\"", proxy_url);
printf("正在通过代理克隆仓库...\n");
printf("执行命令: %s\n", command);
int result = system(command);
if (result != 0) {
fprintf(stderr, "错误: 克隆仓库失败\n");
return 3;
}
printf("仓库克隆成功\n");
return 0;
}
// 代理下载文件
int proxy_download(const char *owner, const char *repo, const char *file_path) {
if (!owner || !repo || !file_path || !*owner || !*repo || !*file_path) {
fprintf(stderr, "错误: 仓库所有者、名称和文件路径不能为空\n");
return 1;
}
char original_url[512];
snprintf(original_url, sizeof(original_url), "%s%s/%s/releases/download/%s",
GITHUB_BASE, owner, repo, file_path);
char proxy_url[1024];
snprintf(proxy_url, sizeof(proxy_url), "%s%s", PROXY_PREFIX, original_url);
const char *filename = strrchr(file_path, '/');
if (!filename) filename = file_path;
else filename++;
const char *downloader = NULL;
char download_args[128] = "";
if (has_command("curl")) {
downloader = "curl";
snprintf(download_args, sizeof(download_args), "-JLO \"%s\"", proxy_url);
} else if (has_command("wget")) {
downloader = "wget";
snprintf(download_args, sizeof(download_args), "-O \"%s\" \"%s\"", filename, proxy_url);
} else {
fprintf(stderr, "错误: 未找到wget或curl,无法下载文件\n");
return 2;
}
char command[2048];
snprintf(command, sizeof(command), "%s %s", downloader, download_args);
printf("正在通过代理下载文件: %s\n", filename);
printf("执行命令: %s\n", command);
int result = system(command);
if (result != 0) {
fprintf(stderr, "错误: 下载文件失败\n");
return 3;
}
printf("文件下载成功: %s\n", filename);
return 0;
}
/**
* 保存API响应到临时文件
*/
int save_api_response(const char *url, const char *filename) {
const char *downloader = NULL;
char download_args[128] = "";
if (has_command("curl")) {
downloader = "curl";
snprintf(download_args, sizeof(download_args), "-H \"User-Agent: curl/7.68.0\" -s \"%s\" -o \"%s\"", url, filename);
} else if (has_command("wget")) {
downloader = "wget";
snprintf(download_args, sizeof(download_args), "--user-agent=\"wget\" -q \"%s\" -O \"%s\"", url, filename);
} else {
fprintf(stderr, "错误: 未找到wget或curl,无法查询信息\n");
return 0;
}
char command[2048];
snprintf(command, sizeof(command), "%s %s", downloader, download_args);
return system(command) == 0;
}
/**
* 解析并打印release文件列表
*/
void parse_and_print_files(const char *json_data) {
if (!json_data) {
fprintf(stderr, "错误: 没有JSON数据可解析\n");
return;
}
// 先尝试找到"assets"数组
const char *assets_start = strstr(json_data, "\"assets\":[");
if (!assets_start) {
printf("未找到资产列表,请检查API响应\n");
return;
}
assets_start += strlen("\"assets\":[");
const char *assets_end = strstr(assets_start, "]");
if (!assets_end) {
printf("未找到资产列表的结束位置\n");
return;
}
printf("找到的文件列表:\n");
const char *ptr = assets_start;
int count = 1;
while (ptr < assets_end && (ptr = strstr(ptr, "\"name\":")) != NULL) {
ptr += strlen("\"name\":");
while (ptr < assets_end && isspace((unsigned char)*ptr)) {
ptr++;
}
if (ptr < assets_end && *ptr == '"') {
ptr++;
const char *name_start = ptr;
while (ptr < assets_end && *ptr != '"') {
ptr++;
}
if (ptr < assets_end && *ptr == '"') {
printf("%d. %.*s\n", count++, (int)(ptr - name_start), name_start);
}
}
ptr++;
}
if (count == 1) {
printf("在资产列表中未找到任何文件\n");
}
}
/**
* 解析并打印tags列表
*/
void parse_and_print_tags(const char *json_data) {
if (!json_data) {
fprintf(stderr, "错误: 没有JSON数据可解析\n");
return;
}
// tags是一个数组,寻找数组开始
const char *array_start = strstr(json_data, "[");
const char *array_end = strstr(json_data, "]");
if (!array_start || !array_end || array_start >= array_end) {
printf("未找到有效的tags数组\n");
return;
}
printf("找到的tags列表:\n");
const char *ptr = array_start;
int count = 1;
// 搜索所有"name"字段
while (ptr < array_end && (ptr = strstr(ptr, "\"name\":")) != NULL) {
ptr += strlen("\"name\":");
while (ptr < array_end && isspace((unsigned char)*ptr)) {
ptr++;
}
if (ptr < array_end && *ptr == '"') {
ptr++;
const char *name_start = ptr;
while (ptr < array_end && *ptr != '"') {
ptr++;
}
if (ptr < array_end && *ptr == '"') {
printf("%d. %.*s\n", count++, (int)(ptr - name_start), name_start);
}
}
ptr++;
}
if (count == 1) {
printf("未找到任何tags\n");
}
}
// 列出release文件
int list_release_files(const char *owner, const char *repo, const char *release_tag) {
if (!owner || !repo || !release_tag || !*owner || !*repo || !*release_tag) {
fprintf(stderr, "错误: 仓库所有者、名称和release标签不能为空\n");
return 1;
}
// 构建API URL - 不使用代理
char api_url[512];
snprintf(api_url, sizeof(api_url), "%s%s/%s/releases/tags/%s",
GITHUB_API_BASE, owner, repo, release_tag);
printf("API查询地址: %s\n", api_url);
// 保存完整响应到非隐藏文件
const char *temp_file = "github_release.tmp.json";
if (!save_api_response(api_url, temp_file)) {
fprintf(stderr, "错误: 查询release信息失败\n");
return 3;
}
// 读取临时文件内容
FILE *file = fopen(temp_file, "r");
if (!file) {
fprintf(stderr, "错误: 无法打开临时文件 %s\n", temp_file);
return 4;
}
// 获取文件大小
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
// 读取文件内容
char *json_data = malloc(size + 1);
if (!json_data) {
fprintf(stderr, "错误: 内存分配失败\n");
fclose(file);
return 5;
}
fread(json_data, 1, size, file);
json_data[size] = '\0';
fclose(file);
// 解析并打印文件列表
parse_and_print_files(json_data);
printf("完整API响应已保存到 %s\n", temp_file);
free(json_data);
return 0;
}
// 列出所有tags
int list_tags(const char *owner, const char *repo) {
if (!owner || !repo || !*owner || !*repo) {
fprintf(stderr, "错误: 仓库所有者和名称不能为空\n");
return 1;
}
// 构建tags API URL - GitHub获取tags的API
char api_url[512];
snprintf(api_url, sizeof(api_url), "%s%s/%s/tags",
GITHUB_API_BASE, owner, repo);
printf("API查询地址: %s\n", api_url);
// 保存完整响应到非隐藏文件
const char *temp_file = "github_tags.tmp.json";
if (!save_api_response(api_url, temp_file)) {
fprintf(stderr, "错误: 查询tags信息失败\n");
return 3;
}
// 读取临时文件内容
FILE *file = fopen(temp_file, "r");
if (!file) {
fprintf(stderr, "错误: 无法打开临时文件 %s\n", temp_file);
return 4;
}
// 获取文件大小
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
// 读取文件内容
char *json_data = malloc(size + 1);
if (!json_data) {
fprintf(stderr, "错误: 内存分配失败\n");
fclose(file);
return 5;
}
fread(json_data, 1, size, file);
json_data[size] = '\0';
fclose(file);
// 解析并打印tags列表
parse_and_print_tags(json_data);
printf("完整API响应已保存到 %s\n", temp_file);
free(json_data);
return 0;
}
void print_usage(const char *program_name) {
fprintf(stderr, "用法: %s <命令> [参数...]\n", program_name);
fprintf(stderr, "命令:\n");
fprintf(stderr, " clone <owner> <repo> - 通过代理克隆GitHub仓库\n");
fprintf(stderr, " download <owner> <repo> <path> - 通过代理下载GitHub release文件\n");
fprintf(stderr, " list <owner> <repo> <tag> - 列出GitHub release中的文件\n");
fprintf(stderr, " tags <owner> <repo> - 列出GitHub仓库的所有tags\n");
fprintf(stderr, "\n示例:\n");
fprintf(stderr, " %s clone tsoding markut\n", program_name);
fprintf(stderr, " %s tags erlang otp\n", program_name);
fprintf(stderr, " %s list erlang otp OTP-28.0.2\n", program_name);
fprintf(stderr, " %s download erlang otp OTP-28.0.2/otp_win64_28.0.2.exe\n", program_name);
}
int main(int argc, char *argv[]) {
if (argc < 2) {
print_usage(argv[0]);
return 1;
}
int result = 1;
if (strcmp(argv[1], "clone") == 0) {
if (argc != 4) {
fprintf(stderr, "错误: clone命令需要2个参数: owner 和 repo\n");
print_usage(argv[0]);
} else {
result = proxy_git_clone(argv[2], argv[3]);
}
} else if (strcmp(argv[1], "download") == 0) {
if (argc != 5) {
fprintf(stderr, "错误: download命令需要3个参数: owner, repo 和 file_path\n");
print_usage(argv[0]);
} else {
result = proxy_download(argv[2], argv[3], argv[4]);
}
} else if (strcmp(argv[1], "list") == 0) {
if (argc != 5) {
fprintf(stderr, "错误: list命令需要3个参数: owner, repo 和 release_tag\n");
print_usage(argv[0]);
} else {
result = list_release_files(argv[2], argv[3], argv[4]);
}
} else if (strcmp(argv[1], "tags") == 0) {
if (argc != 4) {
fprintf(stderr, "错误: tags命令需要2个参数: owner 和 repo\n");
print_usage(argv[0]);
} else {
result = list_tags(argv[2], argv[3]);
}
} else {
fprintf(stderr, "错误: 未知命令: %s\n", argv[1]);
print_usage(argv[0]);
}
return result;
}
需要查找windows的wget或curl命令
测试
a.exe list erlang otp OTP-27.0.1
API查询地址: https://api.github.com/repos/erlang/otp/releases/tags/OTP-27.0.1
未找到资产列表,请检查API响应
完整API响应已保存到 github_release.tmp.json
a.exe download erlang otp OTP-27.0.1/otp_win64_27.0.1.exe
正在通过代理下载文件: otp_win64_27.0.1.exe
执行命令: curl -JLO "https://ghproxy.cn/https://github.com/erlang/otp/releases/download/OTP-27.0.1/otp_win64_27.0.1.exe"
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:00:01 --:--:-- 0
1 132M 1 2303k 0 0 329k 0 0:06:52 0:00:06 0:06:46 512k^C
a.exe clone tsoding markut
正在通过代理克隆仓库...
执行命令: git clone "https://ghproxy.cn/https://github.com/tsoding/markut.git"
Cloning into 'markut'...
warning: redirecting to https://ghproxy.cfd/https:/github.com/tsoding/markut.git/
a.exe tags erlang otp
API查询地址: https://api.github.com/repos/erlang/otp/tags
找到的tags列表:
1. patch-base-27
2. patch-base-26
3. patch-base-25
4. patch-base-24
5. erl_1211-bp
6. R16B02_yielding_binary_to_term
7. OTP-28.0.2
8. OTP-28.0.1
9. OTP-28.0
10. OTP-28.0-rc4
11. OTP-28.0-rc3
12. OTP-28.0-rc2
13. OTP-28.0-rc1
14. OTP-27.3.4.2
15. OTP-27.3.4.1
16. OTP-27.3.4
17. OTP-27.3.3
18. OTP-27.3.2
19. OTP-27.3.1
20. OTP-27.3
21. OTP-27.2.4
22. OTP-27.2.3
23. OTP-27.2.2
24. OTP-27.2.1
25. OTP-27.2
26. OTP-27.1.3
27. OTP-27.1.2
28. OTP-27.1.1
29. OTP-27.1
30. OTP-27.0.1
完整API响应已保存到 github_tags.tmp.json
自己看json文件查找内容吧,比如exe就搜索exe, 能clone,下载就行
更多推荐

所有评论(0)