You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过Python调用GitHub API实现Node项目自动更新?

解决GitHub API调用与自动更新的完整方案

我明白你卡在GitHub API获取最新版本和文件下载这一步了,直接给你一套能跑通的Python脚本方案,把整个更新流程串起来:

首先,咱们得先理清楚几个核心步骤的实现逻辑:

1. 调用GitHub API获取最新Release的下载链接

GitHub的官方API提供了获取最新Release的端点,你只需要替换自己的用户名和仓库名就行。这里要注意必须加User-Agent请求头,不然GitHub会返回403拒绝访问(API限流要求)。用urllib的话,咱们这么写:

2. 用urlopen下载文件(替代wget)

直接用urlopen配合shutil.copyfileobj来分块下载,既符合你的要求,又能处理大文件不会占满内存。

3. 整合PM2操作、解压、清理的完整脚本

下面是完整的脚本代码,你只需要替换开头的几个配置项:

import urllib.request
import json
import subprocess
import zipfile
import os
import shutil

# -------------------------- 配置项 --------------------------
GITHUB_OWNER = "你的GitHub用户名"  # 比如:octocat
GITHUB_REPO = "你的仓库名"        # 比如:hello-world
PM2_APP_NAME = "你的PM2应用名称"   # 比如:my-node-app
RELEASE_ZIP_NAME = "release.zip"  # 你发布的压缩包文件名
# -----------------------------------------------------------

def main():
    try:
        # 1. 停止PM2中的Node服务
        print("正在停止Node服务...")
        subprocess.run(["pm2", "stop", PM2_APP_NAME], check=True, capture_output=True, text=True)
        print("服务已停止")

        # 2. 调用GitHub API获取最新Release信息
        print("正在获取最新版本信息...")
        api_url = f"https://api.github.com/repos/{GITHUB_OWNER}/{GITHUB_REPO}/releases/latest"
        # 必须设置User-Agent,否则GitHub API会拒绝请求
        headers = {"User-Agent": "Node-App-Auto-Update-Script"}
        req = urllib.request.Request(api_url, headers=headers)
        
        with urllib.request.urlopen(req) as response:
            if response.getcode() != 200:
                raise Exception(f"API请求失败,状态码:{response.getcode()}")
            release_data = json.load(response)

        # 找到对应压缩包的下载链接
        download_url = None
        for asset in release_data.get("assets", []):
            if asset.get("name") == RELEASE_ZIP_NAME:
                download_url = asset.get("browser_download_url")
                break
        
        if not download_url:
            raise Exception(f"在最新Release中未找到名为{RELEASE_ZIP_NAME}的文件")
        print(f"获取到最新版本下载链接:{download_url}")

        # 3. 下载压缩包到当前目录
        print("正在下载最新版本...")
        zip_file_path = os.path.join(os.getcwd(), RELEASE_ZIP_NAME)
        with urllib.request.urlopen(download_url) as response, open(zip_file_path, "wb") as out_file:
            shutil.copyfileobj(response, out_file)
        print("下载完成")

        # 4. 解压压缩包到当前目录(覆盖现有文件)
        print("正在解压文件...")
        with zipfile.ZipFile(zip_file_path, "r") as zip_ref:
            # 遍历所有文件,强制覆盖
            for member in zip_ref.infolist():
                target_path = os.path.join(os.getcwd(), member.filename)
                # 如果目标文件已存在,先删除
                if os.path.exists(target_path):
                    if os.path.isdir(target_path):
                        shutil.rmtree(target_path)
                    else:
                        os.remove(target_path)
                zip_ref.extract(member, os.getcwd())
        print("解压完成")

        # 5. 删除下载的压缩包
        print("正在清理临时文件...")
        os.remove(zip_file_path)
        print("清理完成")

        # 6. 重启PM2服务
        print("正在重启Node服务...")
        subprocess.run(["pm2", "start", PM2_APP_NAME], check=True, capture_output=True, text=True)
        print("服务已重启,更新完成!")

    except subprocess.CalledProcessError as e:
        print(f"PM2命令执行失败:{e.stderr}")
        # 可以在这里加回滚逻辑,比如重启原来的服务
        subprocess.run(["pm2", "start", PM2_APP_NAME], check=False)
    except Exception as e:
        print(f"更新失败:{str(e)}")
        # 异常时尝试重启服务
        subprocess.run(["pm2", "start", PM2_APP_NAME], check=False)

if __name__ == "__main__":
    main()

关键细节说明

  • GitHub API请求头:一定要加User-Agent,不然会触发API限流,返回403错误。
  • 文件覆盖逻辑:解压时先删除已存在的文件/目录,确保新文件完全覆盖旧内容。
  • 异常处理:脚本里加了异常捕获,即使更新失败也会尝试重启服务,避免服务挂掉。
  • PM2命令:用subprocess.runcheck=True来确保命令执行成功,如果失败会抛出异常。

你只需要把开头的配置项换成自己的信息,然后在Node里用child_process.exec或者spawn调用这个脚本就行,比如:

const { exec } = require('child_process');

exec('python3 update_script.py', (error, stdout, stderr) => {
  if (error) {
    console.error(`更新脚本执行失败: ${error.message}`);
    return;
  }
  if (stderr) {
    console.error(`脚本错误输出: ${stderr}`);
    return;
  }
  console.log(`更新脚本输出: ${stdout}`);
});

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

火山引擎 最新活动