feat: new git imports (desc, open, is_archived)

This commit is contained in:
null 2026-05-19 21:49:45 -05:00
parent 533f5079e1
commit 87802db0f4
8 changed files with 151 additions and 3 deletions

View File

@ -339,6 +339,10 @@ 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),
"description": repository.description,
"open_issues_count": repository.open_issues_count if repository.open_issues_count is not None else 0,
"is_archived": bool(repository.is_archived),
"topics": repository.topics if repository.topics is not None else [],
"labels": repository.labels if repository.labels is not None else [],
"last_sync_at": repository.last_sync_at,
"last_sync_error": repository.last_sync_error,

View File

@ -5,6 +5,7 @@ from __future__ import annotations
from datetime import datetime
from uuid import UUID, uuid4
import sqlalchemy as sa
from sqlalchemy import JSON, Column
from sqlmodel import Field, Index, SQLModel
@ -24,13 +25,15 @@ class ForgejoIssue(SQLModel, table=True):
forgejo_issue_number: int = Field(index=True)
title: str
body: str | None = Field(default=None, sa_column=Column(sa.Text, nullable=True))
body_preview: str | None = Field(default=None, max_length=1000)
state: str = Field(default="open") # open, closed, open
state: str = Field(default="open")
is_pull_request: bool = Field(default=False)
# JSON fields for complex data
labels: list[dict[str, object]] = Field(default_factory=list, sa_column=Column(JSON))
assignees: list[dict[str, object]] = Field(default_factory=list, sa_column=Column(JSON))
milestone: dict[str, object] | None = Field(default=None, sa_column=Column(JSON, nullable=True))
author: str
html_url: str

View File

@ -29,6 +29,13 @@ class ForgejoRepository(QueryModel, table=True):
default_branch: str = Field(default="main")
active: bool = Field(default=True)
webhook_secret: str | None = Field(default=None)
description: str | None = Field(default=None)
open_issues_count: int = Field(default=0)
is_archived: bool = Field(default=False)
topics: list[str] = Field(
default_factory=list,
sa_column=Column(JSON, nullable=False, server_default="[]"),
)
labels: list[dict[str, object]] = Field(
default_factory=list,
sa_column=Column(JSON, nullable=False, server_default="[]"),

View File

@ -14,11 +14,13 @@ class ForgejoIssueBase(SQLModel):
forgejo_issue_number: int
title: str
body: str | None = None
body_preview: str | None = None
state: str
is_pull_request: bool
labels: list[dict[str, Any]] = []
assignees: list[dict[str, Any]] = []
milestone: dict[str, Any] | None = None
author: str
html_url: str
forgejo_created_at: datetime

View File

@ -108,6 +108,10 @@ class ForgejoRepositoryRead(ForgejoRepositoryBase):
connection_id: UUID
connection: ForgejoRepositoryConnectionInfo
has_webhook_secret: bool = False
description: str | None = None
open_issues_count: int = 0
is_archived: bool = False
topics: list[str] = Field(default_factory=list)
labels: list[dict[str, object]] = Field(default_factory=list)
last_sync_at: datetime | None
last_sync_error: str | None

View File

@ -79,6 +79,7 @@ class IssueSyncService:
labels_data = []
for label in (issue_data.get("labels") or []):
labels_data.append({
"id": label.get("id"),
"name": label.get("name", ""),
"color": label.get("color", ""),
"description": label.get("description", ""),
@ -93,6 +94,24 @@ class IssueSyncService:
"avatar_url": assignee.get("avatar_url", ""),
})
# Parse milestone
milestone_data = None
raw_milestone = issue_data.get("milestone")
if raw_milestone and isinstance(raw_milestone, dict):
milestone_data = {
"id": raw_milestone.get("id"),
"title": raw_milestone.get("title", ""),
"state": raw_milestone.get("state", "open"),
"description": raw_milestone.get("description") or None,
"due_on": raw_milestone.get("due_on") or None,
"closed_at": raw_milestone.get("closed_at") or None,
}
# Full body and preview
raw_body = issue_data.get("body") or ""
body_full = raw_body if raw_body else None
body_preview = raw_body[:1000] if raw_body else None
# Parse dates
created_at = self._parse_iso_date(issue_data.get("created_at"))
updated_at = self._parse_iso_date(issue_data.get("updated_at"))
@ -107,11 +126,13 @@ class IssueSyncService:
repository_id=repository_id,
forgejo_issue_number=forgejo_number,
title=issue_data.get("title", ""),
body_preview=(issue_data.get("body") or "")[:1000],
body=body_full,
body_preview=body_preview,
state=state,
is_pull_request=False,
labels=labels_data,
assignees=assignees_data,
milestone=milestone_data,
author=issue_data.get("user", {}).get("login", ""),
html_url=issue_data.get("html_url", ""),
forgejo_created_at=created_at,
@ -123,10 +144,12 @@ class IssueSyncService:
created += 1
else:
existing.title = issue_data.get("title", "")
existing.body_preview = (issue_data.get("body") or "")[:1000]
existing.body = body_full
existing.body_preview = body_preview
existing.state = state
existing.labels = labels_data
existing.assignees = assignees_data
existing.milestone = milestone_data
existing.author = issue_data.get("user", {}).get("login", "")
existing.html_url = issue_data.get("html_url", "")
existing.forgejo_created_at = created_at
@ -170,6 +193,25 @@ class IssueSyncService:
error=str(exc),
)
# Sync repository remote metadata (description, archived, topics, issue count)
try:
async with get_forgejo_client(connection) as client:
repo_meta = await client.get_repository(
owner=repository.owner,
repo=repository.repo,
)
repository.description = repo_meta.get("description") or None
repository.open_issues_count = int(repo_meta.get("open_issues_count") or 0)
repository.is_archived = bool(repo_meta.get("archived", False))
raw_topics = repo_meta.get("topics")
repository.topics = list(raw_topics) if isinstance(raw_topics, list) else []
except Exception as exc:
logger.warning(
"repo_metadata_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,71 @@
"""add body and milestone to issues; description, open_issues_count, is_archived, topics to repos
Revision ID: b2c3d4e5f6a7
Revises: a1b2c3d4e5f7
Create Date: 2026-05-19 00:00:00.000000
"""
from __future__ import annotations
import sqlalchemy as sa
from alembic import op
revision = "b2c3d4e5f6a7"
down_revision = "a1b2c3d4e5f7"
branch_labels = None
depends_on = None
def upgrade() -> None:
# forgejo_issues — full body text and milestone JSON
op.add_column(
"forgejo_issues",
sa.Column("body", sa.Text(), nullable=True),
)
op.add_column(
"forgejo_issues",
sa.Column("milestone", sa.JSON(), nullable=True),
)
# forgejo_repositories — remote metadata fields
op.add_column(
"forgejo_repositories",
sa.Column("description", sa.String(), nullable=True),
)
op.add_column(
"forgejo_repositories",
sa.Column(
"open_issues_count",
sa.Integer(),
nullable=False,
server_default="0",
),
)
op.add_column(
"forgejo_repositories",
sa.Column(
"is_archived",
sa.Boolean(),
nullable=False,
server_default="false",
),
)
op.add_column(
"forgejo_repositories",
sa.Column(
"topics",
sa.JSON(),
nullable=False,
server_default="[]",
),
)
def downgrade() -> None:
op.drop_column("forgejo_repositories", "topics")
op.drop_column("forgejo_repositories", "is_archived")
op.drop_column("forgejo_repositories", "open_issues_count")
op.drop_column("forgejo_repositories", "description")
op.drop_column("forgejo_issues", "milestone")
op.drop_column("forgejo_issues", "body")

View File

@ -44,6 +44,10 @@ export interface ForgejoRepository {
default_branch: string;
active: boolean;
has_webhook_secret: boolean;
description: string | null;
open_issues_count: number;
is_archived: boolean;
topics: string[];
labels: ForgejoRepoLabel[];
connection: ForgejoConnection;
last_sync_at: string | null;
@ -258,17 +262,28 @@ export interface ForgejoIssueLabel {
description?: string | null;
}
export interface ForgejoIssueMilestone {
id: number | null;
title: string;
state: string;
description: string | null;
due_on: string | null;
closed_at: string | null;
}
export interface ForgejoIssue {
id: string;
organization_id: string;
repository_id: string;
forgejo_issue_number: number;
title: string;
body: string | null;
body_preview: string | null;
state: string;
is_pull_request: boolean;
labels: ForgejoIssueLabel[];
assignees: Record<string, unknown>[];
milestone: ForgejoIssueMilestone | null;
author: string;
html_url: string;
forgejo_created_at: string;