Files
ZiPingEightChar/scripts/share_push.py
T
moxiaonian 26a83df3c4 授权模型切换为个人副本签发;share 脱敏共建;自更新通道
- 公众版不再共享 1000 次授权池:移除 data/.license.json,
  改置 data/.public_edition.json 标记,首次排盘本机签发个人副本
  (初始 20 次、不限期);推送脱敏档案每盘 +10 次(同盘只计一次)
- 新增 share/ 共建:结构化脱敏导出(share_export.py)+
  双远端推送(share_push.py)+ 隐私协议(share/README.md);
  推送前必须展示脱敏包全文并获用户授权
- 新增自更新:VERSION + manifest.json(SHA256 清单)+
  scripts/self_update.py(24h 缓存检查、先验后换、保留授权与档案);
  排盘成功后 stderr 提示新版本
- SKILL.md 增「档案共建(share/)」「版本与自更新」两节;
  LICENSE.md 增第六条(档案权属与撤回)
- 测试 100 项全绿
2026-10-09 21:24:25 +08:00

130 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""脱敏包推送 —— 把 share/pending/ 的包 git commit+push 到共建远端。
用法:python share_push.py [--root 技能根目录]
远端取自 share/config.json 的 github_remote / gitea_remote(占位为空则跳过,
由授权方填写;亦可用环境变量 ZIPING_SHARE_GITHUB_REMOTE /
ZIPING_SHARE_GITEA_REMOTE 覆盖)。两个远端逐个尝试,至少一个成功即算成功。
推送成功后:调 _license_gate.grant_uses(root, 10, 盘hash) 计励(同一盘只计一次),
并把该包移入 share/pushed/。推送失败保留 pending,退出码 3。
无 git 或无网络安静失败(不打栈)。
退出码:0 至少一包推送成功或无包可推;2 输入不可用;3 推送失败。
"""
from __future__ import annotations
import argparse
import json
import os
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
import _license_gate as gate
GRANT_USES = 10 # 每盘推送成功的计励次数
ENV_GITHUB = "ZIPING_SHARE_GITHUB_REMOTE"
ENV_GITEA = "ZIPING_SHARE_GITEA_REMOTE"
_GIT_ENV = {**os.environ, "GIT_TERMINAL_PROMPT": "0", "GIT_ASKPASS": "true"}
def _git(*args: str, cwd: str | None = None) -> subprocess.CompletedProcess:
return subprocess.run(["git", *args], cwd=cwd, env=_GIT_ENV,
capture_output=True, text=True, timeout=60)
def _push_to_remote(remote: str, pkg: Path, workdir: str) -> bool:
"""克隆远端 → 放入脱敏包 → commit+push。任一步失败返回 False(安静)。"""
try:
if _git("clone", "--depth", "50", remote, workdir).returncode != 0:
return False
dst = Path(workdir) / pkg.name
shutil.copy2(pkg, dst)
if _git("add", pkg.name, cwd=workdir).returncode != 0:
return False
commit = _git("-c", "user.name=ziping-share",
"-c", "user.email=ziping-share@localhost",
"commit", "--allow-empty", "-m", f"share: {pkg.stem}",
cwd=workdir)
if commit.returncode != 0:
return False
return _git("push", "origin", "HEAD:main", cwd=workdir).returncode == 0
except (OSError, subprocess.SubprocessError):
return False
def _remotes(root: Path) -> list[tuple[str, str]]:
cfg = {}
cfg_path = root / "share" / "config.json"
if cfg_path.is_file():
try:
cfg = json.loads(cfg_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
cfg = {}
out = []
for key, env in (("github_remote", ENV_GITHUB), ("gitea_remote", ENV_GITEA)):
val = os.environ.get(env) or str(cfg.get(key) or "").strip()
if val:
out.append((key, val))
return out
def main() -> int:
ap = argparse.ArgumentParser(prog="share_push.py",
description="把 share/pending/ 的脱敏包推送到共建远端,"
"成功后计励 +10 次并移入 share/pushed/")
ap.add_argument("--root", default=None,
help="技能根目录(默认取脚本所在技能根,测试覆盖用)")
a = ap.parse_args()
root = (Path(a.root).expanduser().resolve() if a.root
else Path(__file__).resolve().parents[1])
if shutil.which("git") is None:
print("ERROR: 未找到 git,无法推送;pending 保留。", file=sys.stderr)
return 3
remotes = _remotes(root)
if not remotes:
print("ERROR: share/config.json 未配置远端(github_remote/gitea_remote "
"由授权方填写);pending 保留。", file=sys.stderr)
return 3
pending = root / "share" / "pending"
pushed = root / "share" / "pushed"
packages = sorted(pending.glob("*.json")) if pending.is_dir() else []
if not packages:
print("share/pending/ 无待推送脱敏包。")
return 0
pushed.mkdir(parents=True, exist_ok=True)
n_ok = 0
for pkg in packages:
chart_hash = pkg.stem
ok_remote = None
for name, remote in remotes:
with tempfile.TemporaryDirectory(prefix="ziping-share-") as td:
if _push_to_remote(remote, pkg, td):
ok_remote = name
break
if ok_remote is None:
print(f"FAIL {pkg.name}:所有远端推送失败(无网络或远端不可达),"
f"pending 保留。", file=sys.stderr)
continue
granted = gate.grant_uses(root, GRANT_USES, chart_hash)
shutil.move(str(pkg), str(pushed / pkg.name))
note = (f"+{GRANT_USES} 次(新增计励)" if granted
else "该盘已计励过,本次不重复加次")
print(f"OK {pkg.name} → {ok_remote};{note}")
n_ok += 1
return 0 if n_ok else 3
if __name__ == "__main__":
raise SystemExit(main())