diff --git a/.blueprint/modules/gitea-integration.md b/.blueprint/modules/gitea-integration.md index 2c225fd..15db23e 100644 --- a/.blueprint/modules/gitea-integration.md +++ b/.blueprint/modules/gitea-integration.md @@ -124,8 +124,8 @@ Gitea push → POST /webhook/gitea/:project_id - complexity: M - acceptance: TBD —— remote 切换 + 推送,安全增量,不丢历史 -### 📋 G7. 新建仓库后一键接线(本地 init + 首次 push) -- status: todo +### ✅ G7. 新建仓库后一键接线(本地 init + 首次 push) +- status: done - complexity: M - depends: G2 - files: src/components/dashboard/GiteaPanel.tsx, src/lib/commands.ts diff --git a/src/components/dashboard/BlueprintModal.tsx b/src/components/dashboard/BlueprintModal.tsx index 94e767f..f46909f 100644 --- a/src/components/dashboard/BlueprintModal.tsx +++ b/src/components/dashboard/BlueprintModal.tsx @@ -1372,7 +1372,7 @@ function GovernancePanel({ {/* ── Gitea 集成(统一 git 后端)─────────────────────────── */} - {projectId && } + {projectId && } {/* 项目信息 */}
diff --git a/src/components/dashboard/GiteaPanel.tsx b/src/components/dashboard/GiteaPanel.tsx index 568330f..0d88be9 100644 --- a/src/components/dashboard/GiteaPanel.tsx +++ b/src/components/dashboard/GiteaPanel.tsx @@ -18,6 +18,10 @@ import { giteaSetupWebhook, giteaUnlinkRepo, giteaPollNow, + scanLocalGit, + gitInitAndCommit, + gitRemoteSet, + gitPush, type GiteaInstance, } from "../../lib/commands"; import { useUIStore } from "../../store/ui"; @@ -186,9 +190,10 @@ export function GiteaInstancesSection() { interface Props { projectId: string; projectName: string; + projectPath?: string; } -export function GiteaPanel({ projectId, projectName }: Props) { +export function GiteaPanel({ projectId, projectName, projectPath }: Props) { const qc = useQueryClient(); const showToast = useUIStore((s) => s.showToast); @@ -207,6 +212,36 @@ export function GiteaPanel({ projectId, projectName }: Props) { queryFn: giteaListLinkedRepos, }); + // G7 一键接线:绑定后检测本地 git 状态(无 git / 无 origin / 已接线) + const { data: localGit, refetch: refetchLocalGit } = useQuery({ + queryKey: ["gitea-local-git", projectId], + queryFn: () => scanLocalGit(projectPath!), + enabled: !!link && !!projectPath, + staleTime: 30000, + retry: false, + }); + const hasOrigin = localGit?.remotes.some((r) => r.name === "origin") ?? false; + const needsWiring = !!link && !!projectPath && localGit != null && (!localGit.has_git || !hasOrigin); + const [wiring, setWiring] = useState(false); + + const handleWireUp = async () => { + if (!projectPath || !link?.clone_url) return; + setWiring(true); + try { + if (!localGit?.has_git || !localGit?.has_commits) { + await gitInitAndCommit(projectPath, "chore: 项目初始化(炼境接线)"); + } + await gitRemoteSet(projectPath, link.clone_url); + const msg = await gitPush(projectPath); + showToast(`✓ 接线完成:${msg}`); + refetchLocalGit(); + } catch (e) { + showToast(`❌ 接线失败: ${e}`); + } finally { + setWiring(false); + } + }; + const refresh = () => { qc.invalidateQueries({ queryKey: ["gitea-link", projectId] }); qc.invalidateQueries({ queryKey: ["gitea-linked-repos"] }); @@ -326,6 +361,32 @@ export function GiteaPanel({ projectId, projectName }: Props) {
+ {/* G7 本地接线:仓库已建但本地未连通时的一次性引导 */} + {needsWiring && ( +
+

+ {!localGit?.has_git ? "本地目录还不是 git 仓库" : "本地仓库尚未关联远端 origin"} +

+
+

+ {!localGit?.has_git + ? "将执行:git init + 首次提交 + 关联 origin + 推送" + : "将执行:关联 origin + 推送当前分支"} +

+ +
+

+ 接线是一次性动作;日常 commit / push 在项目自己的 AI 会话中进行。 +

+
+ )} + {/* webhook 状态与注册 */}