welcome/.gitea/sync_beads.py
rootiest 4f9cbe7c33
All checks were successful
Sync Beads to Gitea Issues / sync-beads (push) Successful in 7s
update beads sync conditions
2026-01-19 21:27:48 -05:00

102 lines
3.3 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.

import json
import os
import requests
import sys
# Configuration from Environment
TOKEN = os.getenv("GITEA_TOKEN")
URL = os.getenv("GITEA_URL")
REPO = os.getenv("REPO_NAME")
HEADERS = {"Authorization": f"token {TOKEN}", "Content-Type": "application/json"}
def sync():
beads_path = ".beads/issues.jsonl"
if not os.path.exists(beads_path):
print(f"❌ ERROR: {beads_path} not found.")
sys.exit(1)
print(f"🔍 Reading Beads from: {beads_path}")
# 1. Fetch existing issues from Gitea
try:
api_url = f"{URL}/api/v1/repos/{REPO}/issues?state=all"
resp = requests.get(api_url, headers=HEADERS)
resp.raise_for_status()
existing = {
i["title"].split("]")[0][1:]: i for i in resp.json() if "]" in i["title"]
}
print(f"📡 Found {len(existing)} existing issues in Gitea.")
except Exception as e:
print(f"❌ Gitea API Connection Failed: {e}")
sys.exit(1)
# 2. Parse the Beads JSONL file
processed_count = 0
skipped_count = 0
with open(beads_path, "r") as f:
for line in f:
line = line.strip()
if not line:
continue
try:
data = json.loads(line)
bid = data.get("id")
status = data.get("status")
title = data.get("title")
itype = data.get("issue_type", "unknown")
# --- THE FIX: FILTER TOMBSTONES ---
if status == "tombstone":
print(f"👻 Skipping tombstone: {bid} ({title})")
skipped_count += 1
continue
# ----------------------------------
if not bid:
print("⚠️ Skipping line: No ID found.")
continue
processed_count += 1
unique_title = f"[{bid}] {title}"
payload = {
"title": unique_title,
"body": f"{data.get('description', 'No description provided.')}\n\n---\n**Beads ID:** `{bid}`\n**Type:** `{itype}`",
"state": "closed"
if status in ["closed", "done"]
else "open",
}
if bid in existing:
print(f"✅ Updating Gitea Issue #{existing[bid]['number']} for {bid}")
requests.patch(
f"{URL}/api/v1/repos/{REPO}/issues/{existing[bid]['number']}",
headers=HEADERS,
json=payload,
)
else:
print(f" Creating New Gitea Issue for {bid}")
r = requests.post(
f"{URL}/api/v1/repos/{REPO}/issues",
headers=HEADERS,
json=payload,
)
r.raise_for_status()
except Exception as e:
print(f"⚠️ Error processing bead: {e}")
print(f"🏁 Finished. Active: {processed_count}, Skipped: {skipped_count}")
# We check if we have ANY beads (active or tombstone) to validate the file isn't broken
if (processed_count + skipped_count) == 0:
print("❌ ERROR: File was found but it was EMPTY.")
sys.exit(1)
if __name__ == "__main__":
sync()