LZ-SG/embedded-dev-tutorials
0
1#!/usr/bin/env python32"""通过GitHub Contents API批量推送文件到远程仓库"""3import os, base64, json, urllib.request, urllib.error, time4 5TOKEN = "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"6REPO = "SG06231219-boop/embedded-dev-tutorials"7BRANCH = "main"8API_BASE = f"https://api.github.com/repos/{REPO}/contents"9 10# 需要推送的文件列表(相对于项目根目录)11FILES_TO_PUSH = [12 "static/tutorials/esp32.html",13 "static/tutorials/flash-debug.html",14 "static/tutorials/freertos.html",15 "static/tutorials/main.html",16 "static/tutorials/protocols.html",17 "static/tutorials/proj-led-button.html",18 "static/tutorials/proj-uart-hello.html",19 "static/tutorials/proj-oled-display.html",20 "static/tutorials/proj-dht11-monitor.html",21 "static/tutorials/proj-pwm-motor.html",22 "static/tutorials/proj-servo-control.html",23 "static/tutorials/proj-ir-remote.html",24 "static/tutorials/proj-ultrasonic.html",25 "static/tutorials/proj-stepper-motor.html",26 "static/tutorials/proj-freertos-station.html",27 "static/tutorials/proj-smart-car.html",28 "static/tutorials/proj-can-bus.html",29 "static/tutorials/proj-sd-logger.html",30 "static/tutorials/proj-esp32-iot.html",31 "static/tutorials/proj-low-power.html",32 "static/tutorials/proj-bootloader-iap.html",33 "static/tutorials/proj-dma-adc.html",34 "static/tutorials/proj-usb-hid.html",35 "static/tutorials/proj-s32k144-auto.html",36 "static/tutorials/proj-oscilloscope-debug.html",37]38 39BASE_DIR = os.path.dirname(os.path.abspath(__file__))40 41def get_remote_sha(path):42 """获取远程文件的SHA"""43 url = f"{API_BASE}/{path}?ref={BRANCH}"44 req = urllib.request.Request(url, headers={45 "Authorization": f"token {TOKEN}",46 "Accept": "application/vnd.github.v3+json",47 "User-Agent": "push-script"48 })49 try:50 with urllib.request.urlopen(req, timeout=15) as resp:51 data = json.loads(resp.read())52 return data.get("sha")53 except urllib.error.HTTPError as e:54 if e.code == 404:55 return None56 raise57 58def push_file(path, message):59 """推送单个文件到GitHub"""60 local_path = os.path.join(BASE_DIR, path.replace("/", os.sep))61 if not os.path.exists(local_path):62 print(f" SKIP {path} (not found locally)")63 return False64 65 with open(local_path, "rb") as f:66 content = base64.b64encode(f.read()).decode("utf-8")67 68 sha = get_remote_sha(path)69 70 payload = {71 "message": message,72 "content": content,73 "branch": BRANCH,74 }75 if sha:76 payload["sha"] = sha77 78 url = f"{API_BASE}/{path}"79 data = json.dumps(payload).encode("utf-8")80 req = urllib.request.Request(url, data=data, headers={81 "Authorization": f"token {TOKEN}",82 "Accept": "application/vnd.github.v3+json",83 "User-Agent": "push-script",84 "Content-Type": "application/json"85 }, method="PUT")86 87 try:88 with urllib.request.urlopen(req, timeout=30) as resp:89 result = json.loads(resp.read())90 commit_sha = result.get("commit", {}).get("sha", "?")[:7]91 print(f" OK {path} (commit {commit_sha})")92 return True93 except urllib.error.HTTPError as e:94 body = e.read().decode("utf-8", errors="replace")95 print(f" FAIL {path}: {e.code} {body[:200]}")96 return False97 98def main():99 total = len(FILES_TO_PUSH)100 ok = 0101 fail = 0102 103 print(f"Pushing {total} files to {REPO}:{BRANCH}...")104 105 for i, fpath in enumerate(FILES_TO_PUSH, 1):106 print(f"[{i}/{total}] {fpath}")107 msg = f"feat: v1.5.0 - FAQ+cross-refs for {os.path.basename(fpath)}"108 if push_file(fpath, msg):109 ok += 1110 else:111 fail += 1112 # Rate limit: 逐文件推送间隔1秒113 if i < total:114 time.sleep(1)115 116 print(f"\nDone: {ok} OK, {fail} FAIL out of {total}")117 118if __name__ == "__main__":119 main()120 