docs(ISSUE-WP-0006): Forgejo-only language and projection boundary
Prefer Forgejo as the self-hosted forge product. Keep Gitea only as the Gitea-compatible API identifier (module backends/gitea, type string gitea). FORGEJO_TOKEN is preferred; GITEA_* remains a deprecated alias. INTENT/SCOPE quote ACT-ADR-005: issue-core is not the fleet ops claim queue. Connector docs describe repo work record → hub index → optional Forgejo projection, not activity-core → issue-core → harness. Assistant: grok Assistant-Session: 01a09dc6-3f0d-7c93-8b11-8e83c0623d49
This commit is contained in:
parent
f9d276dadf
commit
ee9b85215d
34 changed files with 410 additions and 263 deletions
|
|
@ -2,12 +2,12 @@
|
|||
issue-core — external issue-tracker connector
|
||||
|
||||
Backend-agnostic CLI, library, and optional REST surface for operating on
|
||||
third-party issue trackers (Gitea/Forgejo, local SQLite). Not the origin of
|
||||
fleet work records (see INTENT.md / SCOPE.md).
|
||||
third-party issue trackers (Forgejo via Gitea-compatible API, local SQLite).
|
||||
Not the origin of fleet work records (see INTENT.md / SCOPE.md).
|
||||
|
||||
Shipped:
|
||||
- Unified issue model and IssueBackend plugin architecture
|
||||
- Local SQLite + Gitea backends; bidirectional sync CLI
|
||||
- Local SQLite + Forgejo backends; bidirectional sync CLI
|
||||
- CLI (`issue` / `issue-core`) and optional FastAPI REST (`issue serve`)
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ API regardless of the underlying issue tracking system.
|
|||
|
||||
Available Backends:
|
||||
- local: SQLite-based local backend for offline use
|
||||
- gitea: Gitea API backend for GitHub-compatible systems
|
||||
- gitea: Forgejo backend (Gitea-compatible API)
|
||||
"""
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
"""
|
||||
Gitea Backend
|
||||
Forgejo backend (Gitea-compatible API).
|
||||
|
||||
A backend implementation for Gitea issue tracking systems.
|
||||
This backend provides integration with Gitea API for remote issue management.
|
||||
Talks to the self-hosted Forgejo HTTP API. Gitea is not a second supported
|
||||
product. The backend type string remains `gitea`.
|
||||
|
||||
Features:
|
||||
- Full Gitea API integration
|
||||
- GitHub-compatible operations
|
||||
- Full Forgejo / Gitea-compatible API integration
|
||||
- Remote synchronization
|
||||
- Authentication support
|
||||
- Rate limiting compliance
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
"""
|
||||
Gitea Backend Implementation
|
||||
Forgejo backend (Gitea-compatible API).
|
||||
|
||||
Provides integration with Gitea API for remote issue tracking.
|
||||
This backend adapts the Gitea API to our unified issue model.
|
||||
Talks to the self-hosted Forgejo HTTP API, which is Gitea-compatible.
|
||||
Gitea is not a second supported product. The backend type string remains
|
||||
`gitea` for compatibility.
|
||||
"""
|
||||
|
||||
import requests
|
||||
|
|
@ -16,7 +17,7 @@ from ...core.models import Issue, Label, User, Milestone, Comment, IssueState, P
|
|||
|
||||
|
||||
class GiteaAPIError(Exception):
|
||||
"""Gitea API specific errors."""
|
||||
"""Forgejo (Gitea-compatible API) errors."""
|
||||
pass
|
||||
|
||||
|
||||
|
|
@ -26,7 +27,7 @@ class GiteaRateLimitError(GiteaAPIError):
|
|||
|
||||
|
||||
class GiteaBackend(RemoteBackend, SyncableBackend):
|
||||
"""Gitea API backend for remote issue tracking."""
|
||||
"""Forgejo backend using the Gitea-compatible HTTP API."""
|
||||
|
||||
def __init__(self):
|
||||
self.base_url: Optional[str] = None
|
||||
|
|
@ -44,7 +45,7 @@ class GiteaBackend(RemoteBackend, SyncableBackend):
|
|||
supports_webhooks=True,
|
||||
supports_real_time=False,
|
||||
max_labels_per_issue=None,
|
||||
max_assignees_per_issue=10 # Gitea typical limit
|
||||
max_assignees_per_issue=10 # Forgejo / Gitea-compatible typical limit
|
||||
)
|
||||
|
||||
@property
|
||||
|
|
@ -56,7 +57,7 @@ class GiteaBackend(RemoteBackend, SyncableBackend):
|
|||
return self._capabilities
|
||||
|
||||
def connect(self, config: Dict[str, Any]) -> None:
|
||||
"""Connect to Gitea API."""
|
||||
"""Connect to Forgejo (Gitea-compatible API)."""
|
||||
self.base_url = config['base_url'].rstrip('/')
|
||||
self.token = config['token']
|
||||
self.owner = config['owner']
|
||||
|
|
@ -71,10 +72,10 @@ class GiteaBackend(RemoteBackend, SyncableBackend):
|
|||
|
||||
# Test connection
|
||||
if not self.test_connection():
|
||||
raise GiteaAPIError("Failed to connect to Gitea API")
|
||||
raise GiteaAPIError("Failed to connect to Forgejo (Gitea-compatible API)")
|
||||
|
||||
def disconnect(self) -> None:
|
||||
"""Disconnect from Gitea API."""
|
||||
"""Disconnect from Forgejo."""
|
||||
self.session.close()
|
||||
self.base_url = None
|
||||
self.token = None
|
||||
|
|
@ -195,20 +196,20 @@ class GiteaBackend(RemoteBackend, SyncableBackend):
|
|||
if issue.milestone:
|
||||
data['milestone'] = int(issue.milestone.backend_id) if issue.milestone.backend_id else None
|
||||
|
||||
# Gitea expects numeric label IDs on issue create/update. Name-only
|
||||
# labels are preserved in issue-core metadata but omitted from the API
|
||||
# payload until a label-resolution step exists.
|
||||
label_ids = []
|
||||
for label in issue.labels:
|
||||
if not label.backend_id:
|
||||
continue
|
||||
try:
|
||||
label_ids.append(int(label.backend_id))
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
if label_ids:
|
||||
data['labels'] = label_ids
|
||||
|
||||
# Gitea expects numeric label IDs on issue create/update. Name-only
|
||||
# labels are preserved in issue-core metadata but omitted from the API
|
||||
# payload until a label-resolution step exists.
|
||||
label_ids = []
|
||||
for label in issue.labels:
|
||||
if not label.backend_id:
|
||||
continue
|
||||
try:
|
||||
label_ids.append(int(label.backend_id))
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
if label_ids:
|
||||
data['labels'] = label_ids
|
||||
|
||||
return data
|
||||
|
||||
# Issue CRUD Operations
|
||||
|
|
|
|||
|
|
@ -12,6 +12,23 @@ from .utils import (
|
|||
echo_error, echo_warning, echo_info, confirm_action
|
||||
)
|
||||
|
||||
# Preferred FORGEJO_*; GITEA_* remain deprecated aliases (ISSUE-WP-0006).
|
||||
_FORGE_TOKEN_ENV_NAMES = (
|
||||
'FORGEJO_TOKEN',
|
||||
'FORGEJO_API_TOKEN',
|
||||
'GITEA_API_TOKEN',
|
||||
'GITEA_TOKEN',
|
||||
)
|
||||
|
||||
|
||||
def _forge_token_from_env():
|
||||
"""Return (env_name, token) for the first set Forgejo/legacy token var."""
|
||||
for name in _FORGE_TOKEN_ENV_NAMES:
|
||||
value = os.getenv(name)
|
||||
if value:
|
||||
return name, value
|
||||
return None, None
|
||||
|
||||
|
||||
@click.group()
|
||||
def backend_group():
|
||||
|
|
@ -46,14 +63,19 @@ def add_backend(ctx, name, backend_type):
|
|||
'db_path': str(db_path)
|
||||
}
|
||||
elif backend_type == 'gitea':
|
||||
base_url = click.prompt('Gitea base URL (e.g., https://git.example.com)')
|
||||
base_url = click.prompt('Forgejo base URL (Gitea-compatible API)')
|
||||
owner = click.prompt('Repository owner/organization')
|
||||
repo = click.prompt('Repository name')
|
||||
|
||||
# Check for API token in environment variable first
|
||||
env_token = os.getenv('GITEA_API_TOKEN')
|
||||
env_name, env_token = _forge_token_from_env()
|
||||
if env_token:
|
||||
click.echo(f"Using API token from GITEA_API_TOKEN environment variable")
|
||||
if env_name in ('GITEA_API_TOKEN', 'GITEA_TOKEN'):
|
||||
click.echo(
|
||||
f"Using API token from {env_name} "
|
||||
"(deprecated alias; prefer FORGEJO_TOKEN)"
|
||||
)
|
||||
else:
|
||||
click.echo(f"Using API token from {env_name} environment variable")
|
||||
token = env_token
|
||||
else:
|
||||
token = click.prompt('Access token', hide_input=True)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue