Address Greptile feedback: clear debounce timer, deduplicate util, fix completed-run display
- Clear the lastOutputAt debounce timer on run completion to avoid stale writes after the run finishes - Replace local formatRelativeTime with existing relativeTime util - Fix completed-run display in LiveRunWidget to show finish time instead of unconditionally showing lastOutputAt Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
b3bccf3648
commit
e58a2330a6
2 changed files with 17 additions and 13 deletions
|
|
@ -2502,6 +2502,7 @@ export function heartbeatService(db: Db) {
|
||||||
const currentUserRedactionOptions = await getCurrentUserRedactionOptions();
|
const currentUserRedactionOptions = await getCurrentUserRedactionOptions();
|
||||||
let lastOutputAtFlushPending = false;
|
let lastOutputAtFlushPending = false;
|
||||||
let lastOutputAtLatest: Date | null = null;
|
let lastOutputAtLatest: Date | null = null;
|
||||||
|
let lastOutputAtTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
const onLog = async (stream: "stdout" | "stderr", chunk: string) => {
|
const onLog = async (stream: "stdout" | "stderr", chunk: string) => {
|
||||||
const sanitizedChunk = redactCurrentUserText(chunk, currentUserRedactionOptions);
|
const sanitizedChunk = redactCurrentUserText(chunk, currentUserRedactionOptions);
|
||||||
if (stream === "stdout") stdoutExcerpt = appendExcerpt(stdoutExcerpt, sanitizedChunk);
|
if (stream === "stdout") stdoutExcerpt = appendExcerpt(stdoutExcerpt, sanitizedChunk);
|
||||||
|
|
@ -2516,8 +2517,9 @@ export function heartbeatService(db: Db) {
|
||||||
await db.update(heartbeatRuns)
|
await db.update(heartbeatRuns)
|
||||||
.set({ lastOutputAt: lastOutputAtLatest, updatedAt: new Date() })
|
.set({ lastOutputAt: lastOutputAtLatest, updatedAt: new Date() })
|
||||||
.where(eq(heartbeatRuns.id, runId));
|
.where(eq(heartbeatRuns.id, runId));
|
||||||
setTimeout(() => {
|
lastOutputAtTimer = setTimeout(() => {
|
||||||
lastOutputAtFlushPending = false;
|
lastOutputAtFlushPending = false;
|
||||||
|
lastOutputAtTimer = null;
|
||||||
if (!lastOutputAtLatest) return;
|
if (!lastOutputAtLatest) return;
|
||||||
db.update(heartbeatRuns)
|
db.update(heartbeatRuns)
|
||||||
.set({ lastOutputAt: lastOutputAtLatest, updatedAt: new Date() })
|
.set({ lastOutputAt: lastOutputAtLatest, updatedAt: new Date() })
|
||||||
|
|
@ -2649,6 +2651,8 @@ export function heartbeatService(db: Db) {
|
||||||
},
|
},
|
||||||
authToken: authToken ?? undefined,
|
authToken: authToken ?? undefined,
|
||||||
});
|
});
|
||||||
|
// Clear the debounce timer now that the run is complete
|
||||||
|
if (lastOutputAtTimer) { clearTimeout(lastOutputAtTimer); lastOutputAtTimer = null; }
|
||||||
const adapterManagedRuntimeServices = adapterResult.runtimeServices
|
const adapterManagedRuntimeServices = adapterResult.runtimeServices
|
||||||
? await persistAdapterManagedRuntimeServices({
|
? await persistAdapterManagedRuntimeServices({
|
||||||
db,
|
db,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { Link } from "@/lib/router";
|
||||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { heartbeatsApi, type LiveRunForIssue } from "../api/heartbeats";
|
import { heartbeatsApi, type LiveRunForIssue } from "../api/heartbeats";
|
||||||
import { queryKeys } from "../lib/queryKeys";
|
import { queryKeys } from "../lib/queryKeys";
|
||||||
import { formatDateTime } from "../lib/utils";
|
import { formatDateTime, relativeTime } from "../lib/utils";
|
||||||
import { AlertTriangle, ExternalLink, Square } from "lucide-react";
|
import { AlertTriangle, ExternalLink, Square } from "lucide-react";
|
||||||
import { Identity } from "./Identity";
|
import { Identity } from "./Identity";
|
||||||
import { StatusBadge } from "./StatusBadge";
|
import { StatusBadge } from "./StatusBadge";
|
||||||
|
|
@ -24,14 +24,6 @@ function isRunActive(status: string): boolean {
|
||||||
return status === "queued" || status === "running";
|
return status === "queued" || status === "running";
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatRelativeTime(iso: string): string {
|
|
||||||
const ms = Date.now() - new Date(iso).getTime();
|
|
||||||
if (ms < 60_000) return "just now";
|
|
||||||
const min = Math.floor(ms / 60_000);
|
|
||||||
if (min < 60) return `${min}m ago`;
|
|
||||||
const hr = Math.floor(min / 60);
|
|
||||||
return `${hr}h ${min % 60}m ago`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LiveRunWidget({ issueId, companyId }: LiveRunWidgetProps) {
|
export function LiveRunWidget({ issueId, companyId }: LiveRunWidgetProps) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
@ -133,12 +125,20 @@ export function LiveRunWidget({ issueId, companyId }: LiveRunWidgetProps) {
|
||||||
Idle
|
Idle
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{run.lastOutputAt && (
|
{run.lastOutputAt && isRunActive(run.status) && (
|
||||||
<span title={`Last output: ${formatDateTime(run.lastOutputAt)}`}>
|
<span title={`Last output: ${formatDateTime(run.lastOutputAt)}`}>
|
||||||
Last output {formatRelativeTime(run.lastOutputAt)}
|
Last output {relativeTime(run.lastOutputAt)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{!run.lastOutputAt && <span>{formatDateTime(run.startedAt ?? run.createdAt)}</span>}
|
{run.finishedAt && !isRunActive(run.status) && (
|
||||||
|
<span>{formatDateTime(run.finishedAt)}</span>
|
||||||
|
)}
|
||||||
|
{!run.lastOutputAt && isRunActive(run.status) && (
|
||||||
|
<span>{formatDateTime(run.startedAt ?? run.createdAt)}</span>
|
||||||
|
)}
|
||||||
|
{!run.finishedAt && !isRunActive(run.status) && !run.lastOutputAt && (
|
||||||
|
<span>{formatDateTime(run.startedAt ?? run.createdAt)}</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue