Ship OpenDota counter-stats reordering for feared items, finalize SITE_VERSION/docs for rankings/streamers/trends/matches/mechanics and draft archetypes, and ignore regenerable Web data caches. Co-authored-by: Cursor <cursoragent@cursor.com>
154 lines
4.3 KiB
Python
154 lines
4.3 KiB
Python
"""Push climperor and dispatch site-traffic-notify via Gitea API.
|
|
|
|
Env: KEYZOO_ASSET_SECRET_PERSONAL_ACCESS_TOKEN_GITEA_1 (or GITEA_TOKEN),
|
|
optional KEYZOO_ASSET_META_URL.
|
|
Never prints the token.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import urllib.error
|
|
import urllib.request
|
|
|
|
OWNER = "refining"
|
|
REPO = "climperor"
|
|
WORKFLOW_FILE = "site-traffic-notify.yml"
|
|
|
|
|
|
def token() -> str:
|
|
for name in (
|
|
"KEYZOO_ASSET_SECRET_PERSONAL_ACCESS_TOKEN_GITEA_1",
|
|
"KEYZOO_ASSET_SECRET_PERSONAL_ACCESS_TOKEN__GITEA_1",
|
|
"KEYZOO_ASSET_TOKEN",
|
|
"GITEA_TOKEN",
|
|
):
|
|
v = os.environ.get(name)
|
|
if v:
|
|
return v
|
|
raise SystemExit("missing Gitea token")
|
|
|
|
|
|
def base() -> str:
|
|
return (
|
|
os.environ.get("GITEA_URL")
|
|
or os.environ.get("KEYZOO_ASSET_META_URL")
|
|
or "https://gitea.refining.dev"
|
|
).rstrip("/")
|
|
|
|
|
|
def api(method: str, path: str, body: dict | None = None) -> tuple[int, dict | list | str]:
|
|
url = base() + path
|
|
data = None if body is None else json.dumps(body).encode()
|
|
req = urllib.request.Request(
|
|
url,
|
|
data=data,
|
|
method=method,
|
|
headers={
|
|
"Authorization": f"token {token()}",
|
|
"Content-Type": "application/json",
|
|
"Accept": "application/json",
|
|
},
|
|
)
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=120) as r:
|
|
raw = r.read().decode()
|
|
if not raw:
|
|
return r.status, {}
|
|
try:
|
|
return r.status, json.loads(raw)
|
|
except json.JSONDecodeError:
|
|
return r.status, raw
|
|
except urllib.error.HTTPError as e:
|
|
raw = e.read().decode(errors="replace")
|
|
try:
|
|
return e.code, json.loads(raw)
|
|
except json.JSONDecodeError:
|
|
return e.code, raw
|
|
|
|
|
|
def push() -> None:
|
|
# Prefer HTTPS with token in URL for non-interactive push.
|
|
tok = token()
|
|
remote = f"https://oauth2:{tok}@gitea.refining.dev/{OWNER}/{REPO}.git"
|
|
env = os.environ.copy()
|
|
# Avoid leaking via git trace
|
|
env.pop("GIT_TRACE", None)
|
|
r = subprocess.run(
|
|
["git", "push", remote, "HEAD:main"],
|
|
cwd=os.path.dirname(os.path.abspath(__file__)) or ".",
|
|
capture_output=True,
|
|
text=True,
|
|
env=env,
|
|
timeout=180,
|
|
)
|
|
# Redact token if it somehow appears
|
|
out = (r.stdout or "") + (r.stderr or "")
|
|
out = out.replace(tok, "***")
|
|
print(out)
|
|
if r.returncode != 0:
|
|
raise SystemExit(f"git push failed: {r.returncode}")
|
|
|
|
|
|
def dispatch(day: str = "") -> None:
|
|
# Gitea: POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches
|
|
# workflow_id can be filename
|
|
body: dict = {"ref": "main"}
|
|
if day:
|
|
body["inputs"] = {"day": day}
|
|
code, resp = api(
|
|
"POST",
|
|
f"/api/v1/repos/{OWNER}/{REPO}/actions/workflows/{WORKFLOW_FILE}/dispatches",
|
|
body,
|
|
)
|
|
print(f"dispatch HTTP {code}: {resp}")
|
|
if code not in (200, 201, 204):
|
|
# Fallback: list workflows to find id
|
|
c2, workflows = api("GET", f"/api/v1/repos/{OWNER}/{REPO}/actions/workflows")
|
|
print(f"workflows HTTP {c2}: {workflows}")
|
|
raise SystemExit(1)
|
|
|
|
|
|
def latest_runs() -> None:
|
|
code, resp = api("GET", f"/api/v1/repos/{OWNER}/{REPO}/actions/runs?limit=5")
|
|
print(f"runs HTTP {code}")
|
|
if isinstance(resp, dict):
|
|
rows = resp.get("workflow_runs") or resp.get("runs") or []
|
|
elif isinstance(resp, list):
|
|
rows = resp
|
|
else:
|
|
print(resp)
|
|
return
|
|
for r in rows[:5]:
|
|
if isinstance(r, dict):
|
|
print(
|
|
f" - id={r.get('id')} name={r.get('name') or r.get('workflow_id')} "
|
|
f"status={r.get('status')} conclusion={r.get('conclusion')} "
|
|
f"event={r.get('event')} created={r.get('created_at')}"
|
|
)
|
|
|
|
|
|
def main() -> int:
|
|
cmd = sys.argv[1] if len(sys.argv) > 1 else "all"
|
|
if cmd == "push":
|
|
push()
|
|
elif cmd == "dispatch":
|
|
day = sys.argv[2] if len(sys.argv) > 2 else ""
|
|
dispatch(day)
|
|
elif cmd == "runs":
|
|
latest_runs()
|
|
elif cmd == "all":
|
|
push()
|
|
dispatch("2026-07-27")
|
|
latest_runs()
|
|
else:
|
|
raise SystemExit("usage: push | dispatch [day] | runs | all")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|