mirror of
https://github.com/stablyai/orca.git
synced 2026-10-27 16:02:42 +00:00
Surface detailed commit failure summaries in Create PR flow
Instead of showing a generic commit failure message during the Create PR intent flow, leverage `summarizeCommitFailure` to extract the underlying reason (such as pre-commit hook or lint failures) and present it clearly. Also introduce `commitErrorsRef` to reliably track and retrieve the most up-to-date commit errors across renders without stale closure issues.
This commit is contained in:
@@ -254,6 +254,7 @@ import {
|
||||
createPrIntentCurrentTargetConflictsWithToken,
|
||||
createPrIntentGitStatusMatchesToken,
|
||||
createPrIntentRunTokenMatches,
|
||||
getCreatePrIntentCommitFailureNoticeMessage,
|
||||
getCreatePrIntentStagePaths,
|
||||
resolveCreatePrIntentReviewBase,
|
||||
resolveCreatePrIntentRemoteStep,
|
||||
@@ -1027,6 +1028,7 @@ function SourceControlInner(): React.JSX.Element {
|
||||
loadSessionCommitDrafts()
|
||||
)
|
||||
const commitDraftsRef = useRef<CommitDraftsByWorktree>(commitDrafts)
|
||||
const commitErrorsRef = useRef<Record<string, string | null>>({})
|
||||
const [commitErrors, setCommitErrors] = useState<Record<string, string | null>>({})
|
||||
const [remoteActionErrors, setRemoteActionErrors] = useState<
|
||||
Record<string, SourceControlActionError | null>
|
||||
@@ -1155,6 +1157,13 @@ function SourceControlInner(): React.JSX.Element {
|
||||
},
|
||||
[]
|
||||
)
|
||||
const setCommitErrorForWorktree = useCallback(
|
||||
(worktreeId: string, message: string | null): void => {
|
||||
commitErrorsRef.current = { ...commitErrorsRef.current, [worktreeId]: message }
|
||||
setCommitErrors((prev) => ({ ...prev, [worktreeId]: message }))
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
const isFolder = activeRepo ? isFolderRepo(activeRepo) : false
|
||||
const worktreePath = activeWorktree?.path ?? null
|
||||
@@ -1917,6 +1926,7 @@ function SourceControlInner(): React.JSX.Element {
|
||||
return changed ? next : prev
|
||||
}
|
||||
updateCommitDrafts((prev) => pruneRecord(prev))
|
||||
commitErrorsRef.current = pruneRecord(commitErrorsRef.current)
|
||||
setCommitErrors((prev) => pruneRecord(prev))
|
||||
setRemoteActionErrors((prev) => pruneRecord(prev))
|
||||
setCommitInFlightByWorktree((prev) => pruneRecord(prev))
|
||||
@@ -2037,7 +2047,7 @@ function SourceControlInner(): React.JSX.Element {
|
||||
commitInFlightRef.current[target.worktreeId] = true
|
||||
|
||||
setCommitInFlightByWorktree((prev) => ({ ...prev, [target.worktreeId]: true }))
|
||||
setCommitErrors((prev) => ({ ...prev, [target.worktreeId]: null }))
|
||||
setCommitErrorForWorktree(target.worktreeId, null)
|
||||
try {
|
||||
const commitResult = await commitRuntimeGit(
|
||||
{
|
||||
@@ -2050,10 +2060,7 @@ function SourceControlInner(): React.JSX.Element {
|
||||
message
|
||||
)
|
||||
if (!commitResult.success) {
|
||||
setCommitErrors((prev) => ({
|
||||
...prev,
|
||||
[target.worktreeId]: commitResult.error ?? 'Commit failed'
|
||||
}))
|
||||
setCommitErrorForWorktree(target.worktreeId, commitResult.error ?? 'Commit failed')
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -2071,7 +2078,7 @@ function SourceControlInner(): React.JSX.Element {
|
||||
}
|
||||
return writeCommitDraftForWorktree(prev, target.worktreeId, '')
|
||||
})
|
||||
setCommitErrors((prev) => ({ ...prev, [target.worktreeId]: null }))
|
||||
setCommitErrorForWorktree(target.worktreeId, null)
|
||||
if (!options?.target) {
|
||||
void refreshActiveGitStatusAfterMutation()
|
||||
}
|
||||
@@ -2103,10 +2110,10 @@ function SourceControlInner(): React.JSX.Element {
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
setCommitErrors((prev) => ({
|
||||
...prev,
|
||||
[target.worktreeId]: error instanceof Error ? error.message : 'Commit failed'
|
||||
}))
|
||||
setCommitErrorForWorktree(
|
||||
target.worktreeId,
|
||||
error instanceof Error ? error.message : 'Commit failed'
|
||||
)
|
||||
return false
|
||||
} finally {
|
||||
setCommitInFlightByWorktree((prev) => ({ ...prev, [target.worktreeId]: false }))
|
||||
@@ -2122,6 +2129,7 @@ function SourceControlInner(): React.JSX.Element {
|
||||
compareBaseRef,
|
||||
grouped.staged.length,
|
||||
refreshActiveGitStatusAfterMutation,
|
||||
setCommitErrorForWorktree,
|
||||
updateCommitDrafts,
|
||||
unresolvedConflicts.length,
|
||||
worktreePath
|
||||
@@ -3822,12 +3830,21 @@ function SourceControlInner(): React.JSX.Element {
|
||||
if (abortIfStale()) {
|
||||
return
|
||||
}
|
||||
const commitFailure = commitErrorsRef.current[token.worktreeId] ?? null
|
||||
setCreatePrIntentNoticeForWorktree(token.worktreeId, {
|
||||
tone: 'destructive',
|
||||
message: translate(
|
||||
'auto.components.right.sidebar.SourceControl.createPrIntentCommitFailed',
|
||||
'Could not commit changes. Fix the issue, then retry Create PR.'
|
||||
)
|
||||
message: getCreatePrIntentCommitFailureNoticeMessage(commitFailure, {
|
||||
fallback: translate(
|
||||
'auto.components.right.sidebar.SourceControl.createPrIntentCommitFailed',
|
||||
'Could not commit changes. Fix the issue, then retry Create PR.'
|
||||
),
|
||||
withSummary: (summary) =>
|
||||
translate(
|
||||
'auto.components.right.sidebar.SourceControl.createPrIntentCommitBlockedSummary',
|
||||
'Commit blocked: {{value0}} Fix the issue, then retry Create PR.',
|
||||
{ value0: summary }
|
||||
)
|
||||
})
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
+20
@@ -5,6 +5,7 @@ import {
|
||||
createPrIntentCurrentTargetConflictsWithToken,
|
||||
createPrIntentGitStatusMatchesToken,
|
||||
createPrIntentRunTokenMatches,
|
||||
getCreatePrIntentCommitFailureNoticeMessage,
|
||||
getCreatePrIntentStagePaths,
|
||||
resolveCreatePrIntentReviewBase,
|
||||
resolveCreatePrIntentRemoteStep
|
||||
@@ -241,4 +242,23 @@ describe('source-control Create PR intent flow helpers', () => {
|
||||
})
|
||||
).toBe('blocked')
|
||||
})
|
||||
|
||||
it('surfaces the commit failure summary in the Create PR intent notice', () => {
|
||||
expect(
|
||||
getCreatePrIntentCommitFailureNoticeMessage(
|
||||
'husky - pre-commit hook\neslint found 2 errors\nfull output'
|
||||
)
|
||||
).toBe('Commit blocked: Lint failed during commit. Fix the issue, then retry Create PR.')
|
||||
|
||||
expect(getCreatePrIntentCommitFailureNoticeMessage(null)).toBe(
|
||||
'Could not commit changes. Fix the issue, then retry Create PR.'
|
||||
)
|
||||
|
||||
expect(
|
||||
getCreatePrIntentCommitFailureNoticeMessage('pre-commit hook failed', {
|
||||
fallback: 'fallback',
|
||||
withSummary: (summary) => `localized ${summary}`
|
||||
})
|
||||
).toBe('localized Pre-commit hook failed.')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
normalizeHostedReviewHeadRef
|
||||
} from '../../../../shared/hosted-review-refs'
|
||||
import type { GitStatusEntry, GitUpstreamStatus } from '../../../../shared/types'
|
||||
import { summarizeCommitFailure } from './commit-failure-summary'
|
||||
import { getStageAllPaths } from './discard-all-sequence'
|
||||
|
||||
export type CreatePrIntentRemoteStep = 'publish' | 'push' | 'force_push' | 'blocked' | 'none'
|
||||
@@ -145,3 +146,23 @@ export function resolveCreatePrIntentRemoteStep({
|
||||
|
||||
return 'none'
|
||||
}
|
||||
|
||||
export function getCreatePrIntentCommitFailureNoticeMessage(
|
||||
commitError: string | null | undefined,
|
||||
copy: {
|
||||
fallback: string
|
||||
withSummary: (summary: string) => string
|
||||
} = {
|
||||
fallback: 'Could not commit changes. Fix the issue, then retry Create PR.',
|
||||
withSummary: (summary: string) =>
|
||||
`Commit blocked: ${summary} Fix the issue, then retry Create PR.`
|
||||
}
|
||||
): string {
|
||||
const summary = commitError ? summarizeCommitFailure(commitError) : null
|
||||
|
||||
if (!summary) {
|
||||
return copy.fallback
|
||||
}
|
||||
|
||||
return copy.withSummary(summary)
|
||||
}
|
||||
|
||||
@@ -9177,7 +9177,8 @@
|
||||
"b715ef615b": "{{value0}} commits ahead of {{value1}}",
|
||||
"c1a8f3e204": "1 commit behind {{value0}}",
|
||||
"d2b9g4f315": "{{value0}} commits behind {{value1}}",
|
||||
"e9c2a5d416": "Even with {{value0}}"
|
||||
"e9c2a5d416": "Even with {{value0}}",
|
||||
"createPrIntentCommitBlockedSummary": "Commit blocked: {{value0}} Fix the issue, then retry Create PR."
|
||||
},
|
||||
"SourceControlAgentActionDialog": {
|
||||
"8e856842d1": "Could not start the selected agent.",
|
||||
|
||||
@@ -9177,7 +9177,8 @@
|
||||
"b715ef615b": "{{value0}} commits por delante de {{value1}}",
|
||||
"c1a8f3e204": "1 commit por detrás de {{value0}}",
|
||||
"d2b9g4f315": "{{value0}} commits por detrás de {{value1}}",
|
||||
"e9c2a5d416": "A la par con {{value0}}"
|
||||
"e9c2a5d416": "A la par con {{value0}}",
|
||||
"createPrIntentCommitBlockedSummary": "Commit blocked: {{value0}} Fix the issue, then retry Create PR."
|
||||
},
|
||||
"SourceControlAgentActionDialog": {
|
||||
"8e856842d1": "No se pudo iniciar el agente seleccionado.",
|
||||
|
||||
@@ -9177,7 +9177,8 @@
|
||||
"b715ef615b": "{{value1}}より{{value0}}commits 進んでいます",
|
||||
"c1a8f3e204": "{{value0}}より1commit 遅れています",
|
||||
"d2b9g4f315": "{{value1}}より{{value0}}commits 遅れています",
|
||||
"e9c2a5d416": "{{value0}}と同じ状態"
|
||||
"e9c2a5d416": "{{value0}}と同じ状態",
|
||||
"createPrIntentCommitBlockedSummary": "Commit blocked: {{value0}} Fix the issue, then retry Create PR."
|
||||
},
|
||||
"SourceControlAgentActionDialog": {
|
||||
"8e856842d1": "選択した agent を開始できませんでした。",
|
||||
|
||||
@@ -9177,7 +9177,8 @@
|
||||
"b715ef615b": "{{value1}}보다 {{value0}} commits 앞서 있음",
|
||||
"c1a8f3e204": "{{value0}}보다 1 commit 뒤처짐",
|
||||
"d2b9g4f315": "{{value1}}보다 {{value0}} commits 뒤처짐",
|
||||
"e9c2a5d416": "{{value0}}와 동일한 상태"
|
||||
"e9c2a5d416": "{{value0}}와 동일한 상태",
|
||||
"createPrIntentCommitBlockedSummary": "Commit blocked: {{value0}} Fix the issue, then retry Create PR."
|
||||
},
|
||||
"SourceControlAgentActionDialog": {
|
||||
"8e856842d1": "선택한 agent를 시작할 수 없습니다.",
|
||||
|
||||
@@ -9177,7 +9177,8 @@
|
||||
"b715ef615b": "领先 {{value1}} {{value0}} 个提交",
|
||||
"c1a8f3e204": "落后 {{value0}} 1 个提交",
|
||||
"d2b9g4f315": "落后 {{value1}} {{value0}} 个提交",
|
||||
"e9c2a5d416": "与 {{value0}} 同步"
|
||||
"e9c2a5d416": "与 {{value0}} 同步",
|
||||
"createPrIntentCommitBlockedSummary": "Commit blocked: {{value0}} Fix the issue, then retry Create PR."
|
||||
},
|
||||
"SourceControlAgentActionDialog": {
|
||||
"8e856842d1": "无法启动选定的智能体。",
|
||||
|
||||
Reference in New Issue
Block a user