Skip to content

Commit 0e59a30

Browse files
committed
Pin default-branch release and label external commits
1 parent 0281675 commit 0e59a30

2 files changed

Lines changed: 87 additions & 29 deletions

File tree

packages/app/app/components/Commits.vue

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,17 @@ async function goPrevPage() {
203203
{{ useTimeAgo(commit.authoredDate) }}
204204
</span>
205205
<span class="flex-1" />
206+
<UButton
207+
v-if="commit.unverified"
208+
color="neutral"
209+
variant="subtle"
210+
size="xs"
211+
:ui="{
212+
base: 'font-mono pointer-events-none',
213+
}"
214+
>
215+
External
216+
</UButton>
206217
<UButton
207218
:to="commit.url"
208219
target="_blank"

packages/app/server/api/repo/commits.get.ts

Lines changed: 76 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
import type { H3Event } from "h3";
12
import { z } from "zod";
23
import type { PackageManager } from "@pkg-pr-new/utils";
34
import { generateCommitPublishMessage } from "../../utils/markdown";
4-
import { useBinding, usePackagesBucket } from "../../utils/bucket";
5+
import {
6+
useBinding,
7+
useCursorsBucket,
8+
usePackagesBucket,
9+
} from "../../utils/bucket";
10+
import { useOctokitInstallation } from "../../utils/octokit";
511

612
const querySchema = z.object({
713
owner: z.string(),
@@ -18,27 +24,36 @@ interface ReleaseRow {
1824
}
1925

2026
async function getCommitMessages(
21-
event: Parameters<typeof defineEventHandler>[0] extends (
22-
event: infer T,
23-
) => any
24-
? T
25-
: any,
27+
installation: Awaited<ReturnType<typeof useOctokitInstallation>>,
2628
owner: string,
2729
repo: string,
2830
shas: string[],
2931
) {
30-
const { ghBaseUrl = "https://api.github.com" } = useRuntimeConfig(event);
32+
async function fetchCommitTitle(sha: string) {
33+
try {
34+
const { data } = await installation.request(
35+
"GET /repos/{owner}/{repo}/commits/{ref}",
36+
{
37+
owner,
38+
repo,
39+
ref: sha,
40+
},
41+
);
42+
const message = data.commit?.message?.split("\n")[0]?.trim();
43+
if (message) {
44+
return message;
45+
}
46+
} catch {
47+
return null;
48+
}
49+
50+
return null;
51+
}
52+
3153
const entries = await Promise.all(
3254
shas.map(async (sha) => {
33-
try {
34-
const commit = await $fetch<{ commit?: { message?: string } }>(
35-
`${ghBaseUrl}/repos/${owner}/${repo}/commits/${sha}`,
36-
);
37-
const message = commit.commit?.message?.split("\n")[0]?.trim();
38-
return message ? ([sha, message] as const) : null;
39-
} catch {
40-
return null;
41-
}
55+
const message = await fetchCommitTitle(sha);
56+
return message ? ([sha, message] as const) : null;
4257
}),
4358
);
4459

@@ -47,14 +62,26 @@ async function getCommitMessages(
4762
);
4863
}
4964

50-
function makeReleaseMessage(packages: string[], abbreviatedSha: string) {
51-
if (packages.length === 0) {
52-
return `Release ${abbreviatedSha}`;
53-
}
54-
if (packages.length === 1) {
55-
return `Release ${packages[0]} (${abbreviatedSha})`;
56-
}
57-
return `Release ${packages.length} packages (${abbreviatedSha})`;
65+
async function getDefaultBranchPinnedSha(
66+
event: H3Event,
67+
installation: Awaited<ReturnType<typeof useOctokitInstallation>>,
68+
owner: string,
69+
repo: string,
70+
) {
71+
const {
72+
data: { default_branch: defaultBranch },
73+
} = await installation.request("GET /repos/{owner}/{repo}", {
74+
owner,
75+
repo,
76+
});
77+
78+
const cursorBucket = useCursorsBucket(
79+
event as Parameters<typeof useCursorsBucket>[0],
80+
);
81+
const cursor = await cursorBucket.getItem(
82+
`${owner}:${repo}:${defaultBranch}`,
83+
);
84+
return cursor?.sha ?? null;
5885
}
5986

6087
function makeReleaseText(
@@ -94,7 +121,7 @@ export default defineEventHandler(async (event) => {
94121
? Number.parseInt(query.cursor, 10)
95122
: 1;
96123

97-
const binding = useBinding(event as any);
124+
const binding = useBinding(event as Parameters<typeof useBinding>[0]);
98125
const prefix = `${usePackagesBucket.base}:${query.owner}:${query.repo}:`;
99126
const rows = new Map<string, ReleaseRow>();
100127
let listCursor: string | undefined;
@@ -132,8 +159,28 @@ export default defineEventHandler(async (event) => {
132159
listCursor = response.truncated ? response.cursor : undefined;
133160
} while (listCursor);
134161

162+
const installation = await useOctokitInstallation(
163+
event,
164+
query.owner,
165+
query.repo,
166+
);
167+
const pinnedSha = await getDefaultBranchPinnedSha(
168+
event,
169+
installation,
170+
query.owner,
171+
query.repo,
172+
);
173+
135174
// Server-side ordering guarantees pagination consistency.
136175
const releases = [...rows.values()].sort((a, b) => {
176+
if (pinnedSha) {
177+
if (a.sha === pinnedSha && b.sha !== pinnedSha) {
178+
return -1;
179+
}
180+
if (b.sha === pinnedSha && a.sha !== pinnedSha) {
181+
return 1;
182+
}
183+
}
137184
if (b.uploadedAt !== a.uploadedAt) {
138185
return b.uploadedAt - a.uploadedAt;
139186
}
@@ -148,7 +195,7 @@ export default defineEventHandler(async (event) => {
148195
const totalPages = Math.max(1, Math.ceil(totalCount / perPage));
149196
const origin = getRequestURL(event).origin;
150197
const commitMessages = await getCommitMessages(
151-
event,
198+
installation,
152199
query.owner,
153200
query.repo,
154201
pageItems.map((row) => row.sha),
@@ -163,13 +210,13 @@ export default defineEventHandler(async (event) => {
163210
nodes: pageItems.map((row) => {
164211
const abbreviatedOid = row.sha.slice(0, 7);
165212
const sortedPackages = [...row.packages].sort();
213+
const commitTitle = commitMessages.get(row.sha);
166214
return {
167215
id: row.sha,
168216
oid: row.sha,
169217
abbreviatedOid,
170-
message:
171-
commitMessages.get(row.sha) ??
172-
makeReleaseMessage(sortedPackages, abbreviatedOid),
218+
message: commitTitle ?? row.sha,
219+
unverified: !commitTitle,
173220
authoredDate: new Date(row.uploadedAt).toISOString(),
174221
url: `https://github.com/${query.owner}/${query.repo}/commit/${row.sha}`,
175222
statusCheckRollup: {

0 commit comments

Comments
 (0)