🐛 fix(vscode): 接管流程 getIssue 纳入兜底,补关闭工单与不存在工单的拒绝
Claude-Session: https://claude.ai/code/session_011cEyL6k351U2BzX1Qmygph
This commit is contained in:
@@ -161,6 +161,18 @@ export async function handleHandoffStart(panel: KanbanWebviewPanel, issueNumber:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// column 兜底成 'todo' 时挡不住「Gitea 已关闭但从没拖进看板」的工单——
|
||||||
|
// 再拉一次远端 issue.state 兜底。
|
||||||
|
const issue = await getIssue({ host, token, owner, repo, index: issueNumber })
|
||||||
|
if (!issue) {
|
||||||
|
toast(panel, 'error', `工单 #${issueNumber} 不存在或已删除`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (issue.state === 'closed') {
|
||||||
|
toast(panel, 'error', `#${issueNumber} 已关闭,不能移交`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const branch = str(state.branch)
|
const branch = str(state.branch)
|
||||||
const worktreePath = str(state.worktreePath)
|
const worktreePath = str(state.worktreePath)
|
||||||
const sessions: HandoffSessions = {
|
const sessions: HandoffSessions = {
|
||||||
@@ -386,18 +398,29 @@ export async function handleHandoffAccept(panel: KanbanWebviewPanel, issueNumber
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// spinner / extracted 声明在 try 外面:getIssue、mkdtemp 任一失败都要靠
|
||||||
|
// 「是否已创建」来决定 catch/finally 里能不能 dismiss/rm,不能假设已存在。
|
||||||
|
let spinner: string | undefined
|
||||||
|
let extracted: string | undefined
|
||||||
|
try {
|
||||||
|
spinner = toast(panel, 'info', `正在接管 #${issueNumber}…`, { spinner: true, dismissOnTimer: 120_000 })
|
||||||
|
|
||||||
// 前置复查:webview 按钮的显示条件(assignees 含 me)可能已经过期(工单被
|
// 前置复查:webview 按钮的显示条件(assignees 含 me)可能已经过期(工单被
|
||||||
// 重新指派),用刚从 Gitea 拉到的最新 issue 再判一次 canAcceptHandoff。
|
// 重新指派、被删),用刚从 Gitea 拉到的最新 issue 再判一次 canAcceptHandoff。
|
||||||
const issue = await getIssue({ host, token, owner, repo, index: issueNumber })
|
const issue = await getIssue({ host, token, owner, repo, index: issueNumber })
|
||||||
if (!issue || !canAcceptHandoff({ handoffAttachmentId: String(attachmentId), assignees: (issue.assignees ?? []).map(a => a.login) }, me)) {
|
if (!issue) {
|
||||||
|
dismiss(panel, spinner)
|
||||||
|
toast(panel, 'error', `工单 #${issueNumber} 不存在或已删除`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!canAcceptHandoff({ handoffAttachmentId: String(attachmentId), assignees: (issue.assignees ?? []).map(a => a.login) }, me)) {
|
||||||
|
dismiss(panel, spinner)
|
||||||
toast(panel, 'error', `#${issueNumber} 未指派给你,不能接管`)
|
toast(panel, 'error', `#${issueNumber} 未指派给你,不能接管`)
|
||||||
finish()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const spinner = toast(panel, 'info', `正在接管 #${issueNumber}…`, { spinner: true, dismissOnTimer: 120_000 })
|
extracted = await scratchDir('spx-takeover-')
|
||||||
const extracted = await scratchDir('spx-takeover-')
|
|
||||||
try {
|
|
||||||
// ① 下载 + 解包 + 校验清单
|
// ① 下载 + 解包 + 校验清单
|
||||||
const meta = await getIssueAttachment({ host, token, owner, repo, index: issueNumber, attachmentId })
|
const meta = await getIssueAttachment({ host, token, owner, repo, index: issueNumber, attachmentId })
|
||||||
const data = await downloadAttachment({ token, url: meta.browser_download_url })
|
const data = await downloadAttachment({ token, url: meta.browser_download_url })
|
||||||
@@ -454,6 +477,7 @@ export async function handleHandoffAccept(panel: KanbanWebviewPanel, issueNumber
|
|||||||
catch (err) {
|
catch (err) {
|
||||||
const message = err instanceof Error ? err.message : String(err)
|
const message = err instanceof Error ? err.message : String(err)
|
||||||
logger.add({ level: 'error', source: 'panel', message: `接管 #${issueNumber} 失败`, details: message })
|
logger.add({ level: 'error', source: 'panel', message: `接管 #${issueNumber} 失败`, details: message })
|
||||||
|
if (spinner)
|
||||||
dismiss(panel, spinner)
|
dismiss(panel, spinner)
|
||||||
toast(panel, 'error', `接管失败:${message}`)
|
toast(panel, 'error', `接管失败:${message}`)
|
||||||
}
|
}
|
||||||
@@ -461,6 +485,7 @@ export async function handleHandoffAccept(panel: KanbanWebviewPanel, issueNumber
|
|||||||
// finish 先发:下面的 rm 失败不该让弹窗的 spinner 卡住不收。
|
// finish 先发:下面的 rm 失败不该让弹窗的 spinner 卡住不收。
|
||||||
finish()
|
finish()
|
||||||
// rm 失败不该抛出掩盖上面 try/catch 已经判定好的成败——scratch 目录只是垃圾。
|
// rm 失败不该抛出掩盖上面 try/catch 已经判定好的成败——scratch 目录只是垃圾。
|
||||||
|
if (extracted)
|
||||||
await fsp.rm(extracted, { recursive: true, force: true }).catch(() => {})
|
await fsp.rm(extracted, { recursive: true, force: true }).catch(() => {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user