Skip to content

Commit 8168ec2

Browse files
Store HEAD commit hash in DB. Also improve how we display dates
1 parent cd54eb0 commit 8168ec2

12 files changed

Lines changed: 184 additions & 54 deletions

File tree

packages/backend/src/git.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,4 +268,16 @@ export const getTags = async (path: string) => {
268268
const git = createGitClientForPath(path);
269269
const tags = await git.tags();
270270
return tags.all;
271+
}
272+
273+
export const getCommitHashForRefName = async ({
274+
path,
275+
refName,
276+
}: {
277+
path: string,
278+
refName: string,
279+
}) => {
280+
const git = createGitClientForPath(path);
281+
const rev = await git.revparse(refName);
282+
return rev;
271283
}

packages/backend/src/repoIndexManager.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Job, Queue, ReservedJob, Worker } from "groupmq";
77
import { Redis } from 'ioredis';
88
import { INDEX_CACHE_DIR } from './constants.js';
99
import { env } from './env.js';
10-
import { cloneRepository, fetchRepository, isPathAValidGitRepoRoot, unsetGitConfig, upsertGitConfig } from './git.js';
10+
import { cloneRepository, fetchRepository, getCommitHashForRefName, isPathAValidGitRepoRoot, unsetGitConfig, upsertGitConfig } from './git.js';
1111
import { PromClient } from './promClient.js';
1212
import { repoMetadataSchema, RepoWithConnections, Settings } from "./types.js";
1313
import { getAuthCredentialsForRepo, getRepoPath, getShardPrefix, groupmqLifecycleExceptionWrapper, measure } from './utils.js';
@@ -384,16 +384,26 @@ export class RepoIndexManager {
384384
data: {
385385
status: RepoIndexingJobStatus.COMPLETED,
386386
completedAt: new Date(),
387+
},
388+
include: {
389+
repo: true,
387390
}
388391
});
389392

390393
const jobTypeLabel = getJobTypePrometheusLabel(jobData.type);
391394

392395
if (jobData.type === RepoIndexingJobType.INDEX) {
396+
const { path: repoPath } = getRepoPath(jobData.repo);
397+
const commitHash = await getCommitHashForRefName({
398+
path: repoPath,
399+
refName: 'HEAD',
400+
});
401+
393402
const repo = await this.db.repo.update({
394403
where: { id: jobData.repoId },
395404
data: {
396405
indexedAt: new Date(),
406+
indexedCommitHash: commitHash,
397407
}
398408
});
399409

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE "Repo" ADD COLUMN "indexedCommitHash" TEXT;

packages/db/prisma/schema.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ model Repo {
5050
5151
jobs RepoIndexingJob[]
5252
indexedAt DateTime? /// When the repo was last indexed successfully.
53+
indexedCommitHash String? /// The commit hash of the last indexed commit (on HEAD).
5354
5455
external_id String /// The id of the repo in the external service
5556
external_codeHostType String /// The type of the external service (e.g., github, gitlab, etc.)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { getFormattedDate } from "@/lib/utils"
2+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
3+
4+
const formatFullDate = (date: Date) => {
5+
return new Intl.DateTimeFormat("en-US", {
6+
month: "long",
7+
day: "numeric",
8+
year: "numeric",
9+
hour: "numeric",
10+
minute: "2-digit",
11+
second: "2-digit",
12+
timeZoneName: "short",
13+
}).format(date)
14+
}
15+
16+
interface DisplayDateProps {
17+
date: Date
18+
className?: string
19+
}
20+
21+
export const DisplayDate = ({ date, className }: DisplayDateProps) => {
22+
return (
23+
<TooltipProvider>
24+
<Tooltip>
25+
<TooltipTrigger asChild>
26+
<span className={className}>
27+
{getFormattedDate(date)}
28+
</span>
29+
</TooltipTrigger>
30+
<TooltipContent>
31+
<p>{formatFullDate(date)}</p>
32+
</TooltipContent>
33+
</Tooltip>
34+
</TooltipProvider>
35+
)
36+
}

packages/web/src/app/[domain]/components/searchBar/useSuggestionsData.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
VscSymbolVariable
2020
} from "react-icons/vsc";
2121
import { useSearchHistory } from "@/hooks/useSearchHistory";
22-
import { getDisplayTime, isServiceError, unwrapServiceError } from "@/lib/utils";
22+
import { getFormattedDate, isServiceError, unwrapServiceError } from "@/lib/utils";
2323
import { useDomain } from "@/hooks/useDomain";
2424

2525

@@ -139,7 +139,7 @@ export const useSuggestionsData = ({
139139
const searchHistorySuggestions = useMemo(() => {
140140
return searchHistory.map(search => ({
141141
value: search.query,
142-
description: getDisplayTime(new Date(search.date)),
142+
description: getFormattedDate(new Date(search.date)),
143143
} satisfies Suggestion));
144144
}, [searchHistory]);
145145

packages/web/src/app/[domain]/repos/[id]/page.tsx

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,7 @@ import { Suspense } from "react"
1616
import { RepoJobsTable } from "../components/repoJobsTable"
1717
import { getConfigSettings } from "@sourcebot/shared"
1818
import { env } from "@/env.mjs"
19-
20-
function formatDate(date: Date | null) {
21-
if (!date) return "Never"
22-
return new Intl.DateTimeFormat("en-US", {
23-
month: "short",
24-
day: "numeric",
25-
year: "numeric",
26-
hour: "2-digit",
27-
minute: "2-digit",
28-
}).format(date)
29-
}
19+
import { DisplayDate } from "../../components/DisplayDate"
3020

3121
export default async function RepoDetailPage({ params }: { params: Promise<{ id: string }> }) {
3222
const { id } = await params
@@ -109,7 +99,7 @@ export default async function RepoDetailPage({ params }: { params: Promise<{ id:
10999
</CardTitle>
110100
</CardHeader>
111101
<CardContent>
112-
<div className="text-2xl font-semibold">{formatDate(repo.createdAt)}</div>
102+
<DisplayDate date={repo.createdAt} className="text-2xl font-semibold"/>
113103
</CardContent>
114104
</Card>
115105

@@ -128,7 +118,7 @@ export default async function RepoDetailPage({ params }: { params: Promise<{ id:
128118
</CardTitle>
129119
</CardHeader>
130120
<CardContent>
131-
<div className="text-2xl font-semibold">{repo.indexedAt ? formatDate(repo.indexedAt) : "Never"}</div>
121+
{repo.indexedAt ? <DisplayDate date={repo.indexedAt} className="text-2xl font-semibold"/> : "Never" }
132122
</CardContent>
133123
</Card>
134124

@@ -147,7 +137,7 @@ export default async function RepoDetailPage({ params }: { params: Promise<{ id:
147137
</CardTitle>
148138
</CardHeader>
149139
<CardContent>
150-
<div className="text-2xl font-semibold">{nextIndexAttempt ? formatDate(nextIndexAttempt) : "-"}</div>
140+
{nextIndexAttempt ? <DisplayDate date={nextIndexAttempt} className="text-2xl font-semibold"/> : "-" }
151141
</CardContent>
152142
</Card>
153143
</div>

packages/web/src/app/[domain]/repos/components/repoJobsTable.tsx

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { useMemo } from "react"
2525
import { LightweightCodeHighlighter } from "../../components/lightweightCodeHighlighter"
2626
import { useRouter } from "next/navigation"
2727
import { useToast } from "@/components/hooks/use-toast"
28+
import { DisplayDate } from "../../components/DisplayDate"
2829

2930
// @see: https://v0.app/chat/repo-indexing-status-uhjdDim8OUS
3031

@@ -68,17 +69,6 @@ const getTypeBadge = (type: RepoIndexingJob["type"]) => {
6869
)
6970
}
7071

71-
const formatDate = (date: Date | null) => {
72-
if (!date) return "-"
73-
return new Intl.DateTimeFormat("en-US", {
74-
month: "short",
75-
day: "numeric",
76-
year: "numeric",
77-
hour: "2-digit",
78-
minute: "2-digit",
79-
}).format(date)
80-
}
81-
8272
const getDuration = (start: Date, end: Date | null) => {
8373
if (!end) return "-"
8474
const diff = end.getTime() - start.getTime()
@@ -139,7 +129,7 @@ export const columns: ColumnDef<RepoIndexingJob>[] = [
139129
</Button>
140130
)
141131
},
142-
cell: ({ row }) => formatDate(row.getValue("createdAt")),
132+
cell: ({ row }) => <DisplayDate date={row.getValue("createdAt") as Date} className="ml-3"/>,
143133
},
144134
{
145135
accessorKey: "completedAt",
@@ -151,7 +141,14 @@ export const columns: ColumnDef<RepoIndexingJob>[] = [
151141
</Button>
152142
)
153143
},
154-
cell: ({ row }) => formatDate(row.getValue("completedAt")),
144+
cell: ({ row }) => {
145+
const completedAt = row.getValue("completedAt") as Date | null;
146+
if (!completedAt) {
147+
return "-";
148+
}
149+
150+
return <DisplayDate date={completedAt} className="ml-3"/>
151+
},
155152
},
156153
{
157154
id: "duration",

packages/web/src/app/[domain]/repos/components/reposTable.tsx

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { Input } from "@/components/ui/input"
1414
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
1515
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
1616
import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants"
17-
import { getCodeHostInfoForRepo, getRepoImageSrc } from "@/lib/utils"
17+
import { CodeHostType, getCodeHostCommitUrl, getCodeHostInfoForRepo, getFormattedDate, getRepoImageSrc } from "@/lib/utils"
1818
import {
1919
type ColumnDef,
2020
type ColumnFiltersState,
@@ -35,6 +35,7 @@ import { useMemo, useState } from "react"
3535
import { getBrowsePath } from "../../browse/hooks/utils"
3636
import { useRouter } from "next/navigation"
3737
import { useToast } from "@/components/hooks/use-toast";
38+
import { DisplayDate } from "../../components/DisplayDate"
3839

3940
// @see: https://v0.app/chat/repo-indexing-status-uhjdDim8OUS
4041

@@ -49,6 +50,7 @@ export type Repo = {
4950
webUrl: string | null
5051
codeHostType: string
5152
imageUrl: string | null
53+
indexedCommitHash: string | null
5254
latestJobStatus: "PENDING" | "IN_PROGRESS" | "COMPLETED" | "FAILED" | null
5355
}
5456

@@ -81,13 +83,7 @@ const getStatusBadge = (status: Repo["latestJobStatus"]) => {
8183

8284
const formatDate = (date: Date | null) => {
8385
if (!date) return "Never"
84-
return new Intl.DateTimeFormat("en-US", {
85-
month: "short",
86-
day: "numeric",
87-
year: "numeric",
88-
hour: "2-digit",
89-
minute: "2-digit",
90-
}).format(date)
86+
return getFormattedDate(date);
9187
}
9288

9389
export const columns: ColumnDef<Repo>[] = [
@@ -132,20 +128,64 @@ export const columns: ColumnDef<Repo>[] = [
132128
},
133129
{
134130
accessorKey: "latestJobStatus",
135-
header: "Status",
131+
header: "Lastest status",
136132
cell: ({ row }) => getStatusBadge(row.getValue("latestJobStatus")),
137133
},
138134
{
139135
accessorKey: "indexedAt",
140136
header: ({ column }) => {
141137
return (
142-
<Button variant="ghost" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
143-
Last Indexed
138+
<Button
139+
variant="ghost"
140+
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
141+
>
142+
Last synced
144143
<ArrowUpDown className="ml-2 h-4 w-4" />
145144
</Button>
146145
)
147146
},
148-
cell: ({ row }) => formatDate(row.getValue("indexedAt")),
147+
cell: ({ row }) => {
148+
const indexedAt = row.getValue("indexedAt") as Date | null;
149+
if (!indexedAt) {
150+
return "-";
151+
}
152+
153+
return (
154+
<DisplayDate date={indexedAt} className="ml-3"/>
155+
)
156+
}
157+
},
158+
{
159+
accessorKey: "indexedCommitHash",
160+
header: "Last commit",
161+
cell: ({ row }) => {
162+
const hash = row.getValue("indexedCommitHash") as string | null;
163+
if (!hash) {
164+
return "-";
165+
}
166+
167+
const smallHash = hash.slice(0, 7);
168+
const repo = row.original;
169+
const codeHostType = repo.codeHostType as CodeHostType;
170+
const webUrl = repo.webUrl;
171+
172+
const commitUrl = getCodeHostCommitUrl({
173+
webUrl,
174+
codeHostType,
175+
commitHash: hash,
176+
});
177+
178+
if (!commitUrl) {
179+
return <span className="font-mono text-sm">{smallHash}</span>
180+
}
181+
182+
return <Link
183+
href={commitUrl}
184+
className="font-mono text-sm text-link hover:underline"
185+
>
186+
{smallHash}
187+
</Link>
188+
},
149189
},
150190
{
151191
id: "actions",

packages/web/src/app/[domain]/repos/page.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export default async function ReposPage() {
2929
imageUrl: repo.imageUrl,
3030
latestJobStatus: repo.jobs.length > 0 ? repo.jobs[0].status : null,
3131
codeHostType: repo.external_codeHostType,
32+
indexedCommitHash: repo.indexedCommitHash,
3233
}))} />
3334
</div>
3435
)
@@ -44,6 +45,9 @@ const getReposWithLatestJob = async () => sew(() =>
4445
},
4546
take: 1
4647
}
48+
},
49+
orderBy: {
50+
name: 'asc'
4751
}
4852
});
4953
return repos;

0 commit comments

Comments
 (0)