feat: git label import

This commit is contained in:
null 2026-05-19 21:34:11 -05:00
parent d6d094a67d
commit 017ab4951a
7 changed files with 99 additions and 0 deletions

View File

@ -339,6 +339,7 @@ def _mask_repository(repository: ForgejoRepository, connection: ForgejoConnectio
"active": repository.active,
"connection": _create_connection_info(connection) if connection is not None else None,
"has_webhook_secret": bool(repository.webhook_secret),
"labels": repository.labels if repository.labels is not None else [],
"last_sync_at": repository.last_sync_at,
"last_sync_error": repository.last_sync_error,
"created_at": repository.created_at,

View File

@ -5,6 +5,8 @@ from __future__ import annotations
from datetime import datetime
from uuid import UUID, uuid4
from sqlalchemy import Column
from sqlalchemy import JSON
from sqlmodel import Field
from app.core.time import utcnow
@ -27,6 +29,10 @@ class ForgejoRepository(QueryModel, table=True):
default_branch: str = Field(default="main")
active: bool = Field(default=True)
webhook_secret: str | None = Field(default=None)
labels: list[dict[str, object]] = Field(
default_factory=list,
sa_column=Column(JSON, nullable=False, server_default="[]"),
)
last_sync_at: datetime | None = Field(default=None)
last_sync_error: str | None = Field(default=None)
created_at: datetime = Field(default_factory=utcnow)

View File

@ -108,6 +108,7 @@ class ForgejoRepositoryRead(ForgejoRepositoryBase):
connection_id: UUID
connection: ForgejoRepositoryConnectionInfo
has_webhook_secret: bool = False
labels: list[dict[str, object]] = Field(default_factory=list)
last_sync_at: datetime | None
last_sync_error: str | None
created_at: datetime

View File

@ -157,6 +157,32 @@ class ForgejoAPIClient:
response.raise_for_status()
return response.json()
async def list_labels(
self,
owner: str,
repo: str,
limit: int = 50,
) -> list[dict[str, object]]:
"""
List all labels defined on a repository.
Args:
owner: Repository owner
repo: Repository name
limit: Max labels to fetch per page
Returns:
List of label dicts with id, name, color, description.
"""
client = await self._get_client()
response = await client.get(
f"/api/v1/repos/{owner}/{repo}/labels",
params={"limit": limit, "page": 1},
)
response.raise_for_status()
data = response.json()
return list(data) if isinstance(data, list) else []
async def list_user_repos(
self,
limit: int = 50,

View File

@ -146,6 +146,30 @@ class IssueSyncService:
break
current_page += 1
# Sync repository label catalog
try:
async with get_forgejo_client(connection) as client:
fetched_labels = await client.list_labels(
owner=repository.owner,
repo=repository.repo,
)
repository.labels = [
{
"id": lbl.get("id"),
"name": lbl.get("name", ""),
"color": lbl.get("color", ""),
"description": lbl.get("description") or "",
}
for lbl in fetched_labels
if lbl.get("name")
]
except Exception as exc:
logger.warning(
"label_sync_failed",
repository_id=str(repository_id),
error=str(exc),
)
# Update repository sync metadata
repository.last_sync_at = utcnow()
repository.last_sync_error = None

View File

@ -0,0 +1,33 @@
"""add labels column to forgejo_repositories
Revision ID: a1b2c3d4e5f7
Revises: f5a2b3c4d5e6
Create Date: 2026-05-19 00:00:00.000000
"""
from __future__ import annotations
import sqlalchemy as sa
from alembic import op
revision = "a1b2c3d4e5f7"
down_revision = "f5a2b3c4d5e6"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column(
"forgejo_repositories",
sa.Column(
"labels",
sa.JSON(),
nullable=False,
server_default="[]",
),
)
def downgrade() -> None:
op.drop_column("forgejo_repositories", "labels")

View File

@ -27,6 +27,13 @@ export interface ForgejoConnectionUpdate {
}
// Forgejo Repository types
export interface ForgejoRepoLabel {
id: number | null;
name: string;
color: string;
description: string;
}
export interface ForgejoRepository {
id: string;
organization_id: string;
@ -37,6 +44,7 @@ export interface ForgejoRepository {
default_branch: string;
active: boolean;
has_webhook_secret: boolean;
labels: ForgejoRepoLabel[];
connection: ForgejoConnection;
last_sync_at: string | null;
last_sync_error: string | null;