persistence on auto PR
This commit is contained in:
@@ -920,6 +920,91 @@ export function registerAutoFixHandlers(
|
||||
abortController: AbortController;
|
||||
}>();
|
||||
|
||||
/**
|
||||
* Load persisted auto-PR review states on startup.
|
||||
* This restores the in-memory state from disk so that active reviews
|
||||
* are visible in the UI after app restart.
|
||||
*/
|
||||
function loadPersistedAutoPRReviewStates(): void {
|
||||
const project = findProjectForAutoPRReview();
|
||||
if (!project) {
|
||||
debugLog('No project available for loading persisted Auto-PR-Review states');
|
||||
return;
|
||||
}
|
||||
|
||||
const stateDir = path.join(getGitHubDir(project), 'pr_review_state');
|
||||
const indexFile = path.join(stateDir, 'index.json');
|
||||
|
||||
try {
|
||||
const indexData = JSON.parse(fs.readFileSync(indexFile, 'utf-8'));
|
||||
const reviews = indexData.reviews || [];
|
||||
|
||||
for (const entry of reviews) {
|
||||
// Only load active (non-terminal) states
|
||||
const terminalStatuses = ['completed', 'failed', 'cancelled', 'max_iterations', 'ready_to_merge'];
|
||||
if (terminalStatuses.includes(entry.status)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load full state from individual file
|
||||
const stateFile = path.join(stateDir, `pr_${entry.pr_number}.json`);
|
||||
try {
|
||||
const stateData = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
||||
const key = `${stateData.repo}#${entry.pr_number}`;
|
||||
|
||||
// Map backend status to frontend status
|
||||
const statusMap: Record<string, AutoPRReviewProgress['status']> = {
|
||||
'pending': 'awaiting_checks',
|
||||
'awaiting_checks': 'awaiting_checks',
|
||||
'reviewing': 'pr_reviewing',
|
||||
'fixing': 'pr_fixing',
|
||||
'ready_to_merge': 'pr_ready_to_merge',
|
||||
};
|
||||
|
||||
const progress: AutoPRReviewProgress = {
|
||||
prNumber: entry.pr_number,
|
||||
repository: stateData.repo,
|
||||
status: statusMap[entry.status] || 'awaiting_checks',
|
||||
currentIteration: entry.current_iteration || 0,
|
||||
maxIterations: stateData.max_iterations || 5,
|
||||
startedAt: entry.started_at || new Date().toISOString(),
|
||||
elapsedMs: 0,
|
||||
ciChecks: stateData.ci_checks || [],
|
||||
ciSummary: { total: 0, passed: 0, failed: 0, pending: 0 },
|
||||
externalBots: [],
|
||||
fixedFindingsCount: stateData.resolved_finding_ids?.length || 0,
|
||||
remainingFindingsCount: stateData.pending_finding_ids?.length || 0,
|
||||
isCancellable: false, // Recovered states are not cancellable (no subprocess)
|
||||
currentActivity: 'Recovered from previous session - restart review to continue',
|
||||
};
|
||||
|
||||
activeAutoPRReviews.set(key, {
|
||||
progress,
|
||||
abortController: new AbortController(), // Placeholder, not connected to any subprocess
|
||||
});
|
||||
|
||||
debugLog('Loaded persisted Auto-PR-Review state', {
|
||||
key,
|
||||
status: progress.status,
|
||||
iteration: progress.currentIteration,
|
||||
});
|
||||
} catch {
|
||||
// Skip invalid state files
|
||||
}
|
||||
}
|
||||
|
||||
debugLog('Loaded persisted Auto-PR-Review states', {
|
||||
count: activeAutoPRReviews.size,
|
||||
});
|
||||
} catch {
|
||||
// Index file doesn't exist or is invalid - no states to load
|
||||
debugLog('No persisted Auto-PR-Review states to load');
|
||||
}
|
||||
}
|
||||
|
||||
// Load persisted states on startup
|
||||
loadPersistedAutoPRReviewStates();
|
||||
|
||||
/**
|
||||
* Get Auto-PR-Review configuration for a project
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@ import { GitPullRequest, RefreshCw, ExternalLink, Settings } from "lucide-react"
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useProjectStore } from "../../stores/project-store";
|
||||
import { useGitHubPRs, usePRFiltering } from "./hooks";
|
||||
import { useAutoPRReview } from "../github-issues/hooks/useAutoPRReview";
|
||||
import { PRList, PRDetail, PRFilterBar } from "./components";
|
||||
import { Button } from "../ui/button";
|
||||
import { ResizablePanels } from "../ui/resizable-panels";
|
||||
@@ -100,6 +101,12 @@ export function GitHubPRs({ onOpenSettings, isActive = false }: GitHubPRsProps)
|
||||
hasActiveFilters,
|
||||
} = usePRFiltering(prs, getReviewStateForPR);
|
||||
|
||||
// Auto-PR Review state for list indicator
|
||||
const {
|
||||
isReviewActive: isAutoPRReviewActive,
|
||||
getReviewProgress: getAutoPRReviewProgress,
|
||||
} = useAutoPRReview();
|
||||
|
||||
const handleRunReview = useCallback(() => {
|
||||
if (selectedPRNumber) {
|
||||
runReview(selectedPRNumber);
|
||||
@@ -233,6 +240,9 @@ export function GitHubPRs({ onOpenSettings, isActive = false }: GitHubPRsProps)
|
||||
getReviewStateForPR={getReviewStateForPR}
|
||||
onSelectPR={selectPR}
|
||||
onLoadMore={loadMore}
|
||||
repoFullName={repoFullName}
|
||||
isAutoPRReviewActive={isAutoPRReviewActive}
|
||||
getAutoPRReviewProgress={getAutoPRReviewProgress}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useRef, useEffect, useCallback } from 'react';
|
||||
import { GitPullRequest, User, Clock, FileDiff, Loader2 } from 'lucide-react';
|
||||
import { GitPullRequest, User, Clock, FileDiff, Loader2, Bot } from 'lucide-react';
|
||||
import { ScrollArea } from '../../ui/scroll-area';
|
||||
import { Badge } from '../../ui/badge';
|
||||
import { cn } from '../../../lib/utils';
|
||||
import type { PRData, PRReviewProgress, PRReviewResult } from '../hooks/useGitHubPRs';
|
||||
import type { NewCommitsCheck } from '../../../../preload/api/modules/github-api';
|
||||
import type { AutoPRReviewProgress } from '../../github-issues/types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
/**
|
||||
@@ -173,6 +174,12 @@ interface PRListProps {
|
||||
getReviewStateForPR: (prNumber: number) => PRReviewInfo | null;
|
||||
onSelectPR: (prNumber: number) => void;
|
||||
onLoadMore: () => void;
|
||||
/** Repository full name for auto-PR review lookup */
|
||||
repoFullName?: string | null;
|
||||
/** Check if auto-PR review is active for a PR */
|
||||
isAutoPRReviewActive?: (repoFullName: string, prNumber: number) => boolean;
|
||||
/** Get auto-PR review progress for a PR */
|
||||
getAutoPRReviewProgress?: (repoFullName: string, prNumber: number) => AutoPRReviewProgress | undefined;
|
||||
}
|
||||
|
||||
function formatDate(dateString: string): string {
|
||||
@@ -204,7 +211,10 @@ export function PRList({
|
||||
error,
|
||||
getReviewStateForPR,
|
||||
onSelectPR,
|
||||
onLoadMore
|
||||
onLoadMore,
|
||||
repoFullName,
|
||||
isAutoPRReviewActive,
|
||||
getAutoPRReviewProgress,
|
||||
}: PRListProps) {
|
||||
const { t } = useTranslation('common');
|
||||
const scrollAreaRef = useRef<HTMLDivElement>(null);
|
||||
@@ -315,6 +325,16 @@ export function PRList({
|
||||
hasCommitsAfterPosting={reviewState?.newCommitsCheck?.hasCommitsAfterPosting ?? false}
|
||||
t={t}
|
||||
/>
|
||||
{/* Auto-PR Review Active Indicator */}
|
||||
{repoFullName && isAutoPRReviewActive && isAutoPRReviewActive(repoFullName, pr.number) && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-xs border-primary/50 text-primary bg-primary/10 animate-pulse"
|
||||
>
|
||||
<Bot className="h-3 w-3 mr-1" />
|
||||
{t('prReview.autoReviewActive')}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<h3 className="font-medium text-sm truncate">{pr.title}</h3>
|
||||
<div className="flex items-center gap-3 mt-2 text-xs text-muted-foreground">
|
||||
|
||||
@@ -190,6 +190,7 @@
|
||||
"runFollowup": "Run Follow-up",
|
||||
"aiReviewInProgress": "AI Review in Progress",
|
||||
"waitingForChanges": "Waiting for Changes",
|
||||
"autoReviewActive": "Auto-Review",
|
||||
"reviewComplete": "Review Complete",
|
||||
"reviewStatus": "Review Status",
|
||||
"files": "files",
|
||||
|
||||
@@ -190,6 +190,7 @@
|
||||
"runFollowup": "Lancer le suivi",
|
||||
"aiReviewInProgress": "Révision IA en cours",
|
||||
"waitingForChanges": "En attente de modifications",
|
||||
"autoReviewActive": "Auto-Révision",
|
||||
"reviewComplete": "Révision terminée",
|
||||
"reviewStatus": "Statut de révision",
|
||||
"files": "fichiers",
|
||||
|
||||
Reference in New Issue
Block a user