Files
APAW/.kilo/shared/gitea-api.md
¨NW¨ 573d9a641e fix(security): add rstrip('/') to get_target_repo for trailing-slash URLs
The regex r'[:/]([^/]+/[^/]+?)(?:\.git)?$' fails on URLs with trailing slashes
like 'https://git.softuniq.eu/UniqueSoft/APAW/' because the final '/' breaks
the pattern. Added .rstrip('/') in Python and sed 's:/*' in Bash to all
get_target_repo() implementations across 11 files.
2026-04-19 12:17:53 +01:00

4.2 KiB

Gitea API Client (Shared)

Common Gitea API functions for issue comments, checkbox updates, and issue management.

IMPORTANT: Target Project Resolution

NEVER hardcode UniqueSoft/APAW in API calls. Always detect the target project from git remote.

How to Detect Target Project

import re, subprocess

def get_target_repo():
    """Detect target project from git remote - NEVER hardcode"""
    result = subprocess.run(
        ['git', 'remote', 'get-url', 'origin'],
        capture_output=True, text=True
    )
    remote_url = result.stdout.strip().rstrip('/')
    
    # HTTPS: https://git.softuniq.eu/Owner/Repo.git
    # SSH: git@git.softuniq.eu:Owner/Repo.git
    match = re.search(r'[:/]([^/]+/[^/]+?)(?:\.git)?$', remote_url)
    if match:
        return match.group(1)
    
    # Fallback: use env var or default
    return os.environ.get('GITEA_TARGET_REPO', 'UniqueSoft/APAW')

Python Client

import urllib.request, json, base64, os, re, subprocess

def gitea_api(path, data=None, method='GET', repo=None):
    """Call Gitea API. Uses get_gitea_token() from gitea-auth.md. Auto-detects target repo."""
    target_repo = repo or get_target_repo()
    token = get_gitea_token()  # From .kilo/shared/gitea-auth.md
    url = f"{os.environ.get('GITEA_API_URL', 'https://git.softuniq.eu/api/v1')}/repos/{target_repo}{path}"
    headers = {'Content-Type': 'application/json', 'Authorization': f'token {token}'}
    req = urllib.request.Request(url, data=json.dumps(data).encode() if data else None,
        headers=headers, method=method)
    with urllib.request.urlopen(req) as r: return json.loads(r.read())

def get_target_repo():
    """Detect target project from git remote - NEVER hardcode"""
    try:
        result = subprocess.run(
            ['git', 'remote', 'get-url', 'origin'],
            capture_output=True, text=True
        )
        remote_url = result.stdout.strip().rstrip('/')
        match = re.search(r'[:/]([^/]+/[^/]+?)(?:\.git)?$', remote_url)
        if match:
            return match.group(1)
    except Exception:
        pass
    return os.environ.get('GITEA_TARGET_REPO', 'UniqueSoft/APAW')

def post_gitea_comment(issue_number, body, repo=None):
    """Post comment to Gitea issue in the correct project."""
    target_repo = repo or get_target_repo()
    return gitea_api(f"/issues/{issue_number}/comments", {"body": body}, 'POST', target_repo)

def update_issue_checkboxes(issue_number, repo=None):
    """Mark all checkboxes as done and close issue."""
    target_repo = repo or get_target_repo()
    import re
    issue = gitea_api(f"/issues/{issue_number}", repo=target_repo)
    body = issue['body']
    body = re.sub(r'- \[ \] ', '- [x] ', body)
    body = re.sub(r'\* \[ \] ', '* [x] ', body)
    gitea_api(f"/issues/{issue_number}", {"body": body, "state": "closed"}, 'PATCH', target_repo)

def close_issue(issue_number, repo=None):
    """Close a Gitea issue in the correct project."""
    target_repo = repo or get_target_repo()
    gitea_api(f"/issues/{issue_number}", {"state": "closed"}, 'PATCH', target_repo)

def create_issue(title, body, labels=None, repo=None):
    """Create a Gitea issue in the correct project."""
    target_repo = repo or get_target_repo()
    return gitea_api("/issues", {"title": title, "body": body, "labels": labels or []}, 'POST', target_repo)

Bash Client

# Auto-detect target repo
TARGET_REPO=$(git remote get-url origin | sed 's:/*$::' | sed -E 's|.*[:/]([^/]+/[^/]+?)(\.git)?$|\1|')

# Post comment
curl -X POST -H "Authorization: token ${GITEA_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"body":"comment body"}' \
  "https://git.softuniq.eu/api/v1/repos/${TARGET_REPO}/issues/{issue_number}/comments"

# Create issue
curl -X POST -H "Authorization: token ${GITEA_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"title":"Issue title","body":"Issue body"}' \
  "https://git.softuniq.eu/api/v1/repos/${TARGET_REPO}/issues"

CRITICAL REMINDERS

  1. NEVER hardcode UniqueSoft/APAW - always use get_target_repo()
  2. Issues belong in the target project - the project being worked on
  3. APAW is the agent framework - not the default target for all issues
  4. Use GITEA_TARGET_REPO env var for explicit override when needed