diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/github-release-mirror/config-demo.yaml b/github-release-mirror/config-demo.yaml new file mode 100644 index 0000000..3c57c99 --- /dev/null +++ b/github-release-mirror/config-demo.yaml @@ -0,0 +1,76 @@ +--- +## 权限修改 +# 是否以 root 运行。如果是,并且 fix-chown 为 true,则会根据 apply-uid 和 apply-gid修改所有产生的文件的所有者和组;如果不是,则不进行任何文件权限相关的修改。 +run-as-root: true +fix-chown: true +apply-uid: 1000 +apply-gid: 100 +## 代理设置 +proxy: + enabled: false # 是否启用代理 + http: "http://127.0.0.1:7890" # HTTP 代理地址 + https: "http://127.0.0.1:7890" # HTTPS 代理地址 +## 文件相关配置 +saved-path: "/home/flintylemming/share/github-release-mirror" +## 跟踪配置 +# 标题 +track-list: + # netbird: + # # (必填)Repo 的作者和项目名称,不需要完整地址 + # repo-info: "netbirdio/netbird" + # # (选填)保存文件夹的名称,创建在 saved-path 下,如果不设置,使用该节标题名 + # folder-name: "Netbird" + # # (选填)设置是否导出 What's Changed 为 md 文件,默认为 true + # get-changes-info: true + # # (选填)设置指定的后缀,只下载 Release 中指定后缀的文件 + # file-type: + # - msi + # - deb + # - pkg + opentrace: + repo-info: "Archeb/opentrace" + folder-name: "OpenTrace" + get-changes-info: true + regex: "(osx|win)" + powertoys: + repo-info: "microsoft/PowerToys" + folder-name: "PowerToys" + get-changes-info: true + # rustdesk: + # repo-info: "rustdesk/rustdesk" + # folder-name: "RustDesk" + # file-type: + # - exe + # - dmg + smartporxy: + repo-info: "salarcode/SmartProxy" + folder-name: "SmartProxy" + ddns-go: + repo-info: "jeessy2/ddns-go" + folder-name: "ddns-go" + sms-forwarder: + repo-info: "pppscn/SmsForwarder" + folder-name: "SmsForwarder" + nezha-agent: + repo-info: "nezhahq/agent" + folder-name: "Nezha Agent" + # massCode: + # repo-info: "massCodeIO/massCode" + # folder-name: "massCode" + flclash: + repo-info: "chen08209/FlClash" + folder-name: "FlClash" + immich: + repo-info: "immich-app/immich" + folder-name: "immich" + file-type: + - apk + surge: + repo-info: "LanYunDev/InjectLib_bak" + folder-name: "Qurge" + beszel: + repo-info: "henrygd/beszel" + folder-name: "Beszel" + easytier: + repo-info: "EasyTier/EasyTier" + folder-name: "EasyTier" diff --git a/github-release-mirror/main.py b/github-release-mirror/main.py new file mode 100644 index 0000000..4a9fcc4 --- /dev/null +++ b/github-release-mirror/main.py @@ -0,0 +1,115 @@ +import os +import re +import requests +import yaml +import logging +from pathlib import Path + +# 设置日志 +logging.basicConfig(level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler("/tmp/github-release-mirror-log.txt"), + logging.StreamHandler() + ]) + +# 获取当前脚本所在目录的绝对路径 +script_dir = os.path.dirname(os.path.abspath(__file__)) +config_path = os.path.join(script_dir, "config.yaml") + +# 读取配置文件 +with open(config_path, 'r') as file: + config = yaml.safe_load(file) + +# 配置代理 +proxies = {} +if 'proxy' in config and config['proxy'].get('enabled', False): + if 'http' in config['proxy']: + proxies['http'] = config['proxy']['http'] + if 'https' in config['proxy']: + proxies['https'] = config['proxy']['https'] + + if proxies: + logging.info(f"已启用代理: {proxies}") + +def download_file(url, filename): + """下载文件到指定路径""" + request_kwargs = {'stream': True} + if proxies: + request_kwargs['proxies'] = proxies + + with requests.get(url, **request_kwargs) as r: + with open(filename, 'wb') as f: + for chunk in r.iter_content(chunk_size=8192): + f.write(chunk) + logging.info(f"文件下载完成:{filename}") + +def change_ownership(path, uid, gid): + """更改文件或目录的所有者""" + os.chown(path, uid, gid) + for root, dirs, files in os.walk(path): + for d in dirs: + os.chown(os.path.join(root, d), uid, gid) + for f in files: + os.chown(os.path.join(root, f), uid, gid) + logging.info(f"已更改所有权:{path}") + +def process_repo(repo_config, base_path, uid, gid): + repo_info = repo_config['repo-info'] + folder_name = repo_config.get('folder-name', repo_info.split('/')[-1]) + get_changes_info = repo_config.get('get-changes-info', True) + file_type = repo_config.get('file-type', []) + regex_pattern = repo_config.get('regex', None) + + logging.info(f"\n正在处理项目:{folder_name}") + logging.info(f"仓库名称:{repo_info}") + + API_URL = f"https://api.github.com/repos/{repo_info}/releases/latest" + + request_kwargs = {} + if proxies: + request_kwargs['proxies'] = proxies + + response = requests.get(API_URL, **request_kwargs) + latest_release = response.json() + latest_version = latest_release['tag_name'] + release_notes = latest_release['body'] + assets = latest_release['assets'] + + version_dir = base_path / folder_name / latest_version + if not version_dir.exists(): + logging.info(f"发现新版本:{latest_version},正在下载...") + version_dir.mkdir(parents=True) + + if get_changes_info: + with open(version_dir / "CHANGELOG.md", "w") as f: + f.write(release_notes) + logging.info("已保存变更日志。") + + for asset in assets: + if file_type and not any(asset['name'].endswith(ext) for ext in file_type): + continue + if regex_pattern and not re.match(regex_pattern, asset['name']): + continue + + download_url = asset['browser_download_url'] + filename = version_dir / asset['name'] + download_file(download_url, filename) + + if config['run-as-root'] and config['fix-chown']: + change_ownership(version_dir, uid, gid) + logging.info(f"版本 {latest_version} 下载完成。") + else: + logging.info(f"最新版本 {latest_version} 已存在。") + +def main(): + saved_path = Path(config['saved-path']) + uid = config['apply-uid'] + gid = config['apply-gid'] + + for track_item, track_config in config['track-list'].items(): + process_repo(track_config, saved_path, uid, gid) + +if __name__ == "__main__": + main() + \ No newline at end of file