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, "active": repository.active,
"connection": _create_connection_info(connection) if connection is not None else None, "connection": _create_connection_info(connection) if connection is not None else None,
"has_webhook_secret": bool(repository.webhook_secret), "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 [], "labels": repository.labels if repository.labels is not None else [],
"last_sync_at": repository.last_sync_at, "last_sync_at": repository.last_sync_at,
"last_sync_error": repository.last_sync_error, "last_sync_error": repository.last_sync_error,

View File

@ -5,6 +5,7 @@ from __future__ import annotations
from datetime import datetime from datetime import datetime
from uuid import UUID, uuid4 from uuid import UUID, uuid4
import sqlalchemy as sa
from sqlalchemy import JSON, Column from sqlalchemy import JSON, Column
from sqlmodel import Field, Index, SQLModel from sqlmodel import Field, Index, SQLModel
@ -24,13 +25,15 @@ class ForgejoIssue(SQLModel, table=True):
forgejo_issue_number: int = Field(index=True) forgejo_issue_number: int = Field(index=True)
title: str 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) 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) is_pull_request: bool = Field(default=False)
# JSON fields for complex data # JSON fields for complex data
labels: list[dict[str, object]] = Field(default_factory=list, sa_column=Column(JSON)) 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)) 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 author: str
html_url: str html_url: str

View File

@ -29,6 +29,13 @@ class ForgejoRepository(QueryModel, table=True):
default_branch: str = Field(default="main") default_branch: str = Field(default="main")
active: bool = Field(default=True) active: bool = Field(default=True)
webhook_secret: str | None = Field(default=None) 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( labels: list[dict[str, object]] = Field(
default_factory=list, default_factory=list,
sa_column=Column(JSON, nullable=False, server_default="[]"), sa_column=Column(JSON, nullable=False, server_default="[]"),

View File

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

View File

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

View File

@ -79,6 +79,7 @@ class IssueSyncService:
labels_data = [] labels_data = []
for label in (issue_data.get("labels") or []): for label in (issue_data.get("labels") or []):
labels_data.append({ labels_data.append({
"id": label.get("id"),
"name": label.get("name", ""), "name": label.get("name", ""),
"color": label.get("color", ""), "color": label.get("color", ""),
"description": label.get("description", ""), "description": label.get("description", ""),
@ -93,6 +94,24 @@ class IssueSyncService:
"avatar_url": assignee.get("avatar_url", ""), "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 # Parse dates
created_at = self._parse_iso_date(issue_data.get("created_at")) created_at = self._parse_iso_date(issue_data.get("created_at"))
updated_at = self._parse_iso_date(issue_data.get("updated_at")) updated_at = self._parse_iso_date(issue_data.get("updated_at"))
@ -107,11 +126,13 @@ class IssueSyncService:
repository_id=repository_id, repository_id=repository_id,
forgejo_issue_number=forgejo_number, forgejo_issue_number=forgejo_number,
title=issue_data.get("title", ""), title=issue_data.get("title", ""),
body_preview=(issue_data.get("body") or "")[:1000], body=body_full,
body_preview=body_preview,
state=state, state=state,
is_pull_request=False, is_pull_request=False,
labels=labels_data, labels=labels_data,
assignees=assignees_data, assignees=assignees_data,
milestone=milestone_data,
author=issue_data.get("user", {}).get("login", ""), author=issue_data.get("user", {}).get("login", ""),
html_url=issue_data.get("html_url", ""), html_url=issue_data.get("html_url", ""),
forgejo_created_at=created_at, forgejo_created_at=created_at,
@ -123,10 +144,12 @@ class IssueSyncService:
created += 1 created += 1
else: else:
existing.title = issue_data.get("title", "") 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.state = state
existing.labels = labels_data existing.labels = labels_data
existing.assignees = assignees_data existing.assignees = assignees_data
existing.milestone = milestone_data
existing.author = issue_data.get("user", {}).get("login", "") existing.author = issue_data.get("user", {}).get("login", "")
existing.html_url = issue_data.get("html_url", "") existing.html_url = issue_data.get("html_url", "")
existing.forgejo_created_at = created_at existing.forgejo_created_at = created_at
@ -170,6 +193,25 @@ class IssueSyncService:
error=str(exc), 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 # Update repository sync metadata
repository.last_sync_at = utcnow() repository.last_sync_at = utcnow()
repository.last_sync_error = None 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; default_branch: string;
active: boolean; active: boolean;
has_webhook_secret: boolean; has_webhook_secret: boolean;
description: string | null;
open_issues_count: number;
is_archived: boolean;
topics: string[];
labels: ForgejoRepoLabel[]; labels: ForgejoRepoLabel[];
connection: ForgejoConnection; connection: ForgejoConnection;
last_sync_at: string | null; last_sync_at: string | null;
@ -258,17 +262,28 @@ export interface ForgejoIssueLabel {
description?: string | null; 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 { export interface ForgejoIssue {
id: string; id: string;
organization_id: string; organization_id: string;
repository_id: string; repository_id: string;
forgejo_issue_number: number; forgejo_issue_number: number;
title: string; title: string;
body: string | null;
body_preview: string | null; body_preview: string | null;
state: string; state: string;
is_pull_request: boolean; is_pull_request: boolean;
labels: ForgejoIssueLabel[]; labels: ForgejoIssueLabel[];
assignees: Record<string, unknown>[]; assignees: Record<string, unknown>[];
milestone: ForgejoIssueMilestone | null;
author: string; author: string;
html_url: string; html_url: string;
forgejo_created_at: string; forgejo_created_at: string;