From 6b4de72155b3bbea1aafe556166ac42e71e80217 Mon Sep 17 00:00:00 2001 From: dawn Date: Wed, 5 Aug 2026 14:53:44 +0300 Subject: [PATCH] web/components: add pipeline status indicators next to commits Signed-off-by: dawn --- web/src/lib/api/repoIndex.ts | 6 ---- .../repo/CommitHeader.stories.svelte | 8 +++++ .../lib/components/repo/CommitHeader.svelte | 28 +++++++++++++++- .../components/repo/CommitList.stories.svelte | 2 +- web/src/lib/components/repo/CommitList.svelte | 32 +++++++++---------- .../repo/CommitLogView.stories.svelte | 7 ++++ .../lib/components/repo/CommitLogView.svelte | 18 +++++++++++ web/src/lib/components/repo/CommitView.svelte | 7 ++-- .../repo/RepoIndexView.stories.svelte | 11 ++++--- .../lib/components/repo/RepoIndexView.svelte | 6 ++-- web/src/lib/components/repo/pipelines/mock.ts | 8 ++--- web/src/routes/[handle]/[repo]/+page.svelte | 12 ++++++- .../[handle]/[repo]/commit/[ref]/+page.svelte | 10 ++++++ .../[repo]/commits/[ref]/+page.svelte | 11 +++++++ .../[handle]/[repo]/tree/[ref]/+page.svelte | 12 ++++++- 15 files changed, 138 insertions(+), 40 deletions(-) diff --git a/web/src/lib/api/repoIndex.ts b/web/src/lib/api/repoIndex.ts index e8bfb9d31..772690240 100644 --- a/web/src/lib/api/repoIndex.ts +++ b/web/src/lib/api/repoIndex.ts @@ -18,7 +18,6 @@ import { toTreeCommitSummary, toTreeEntrySummary } from "$lib/api/repo"; -import { pipelinesByCommitHash } from "$lib/components/repo/pipelines/mock"; import { renderDocument } from "$lib/markup"; import type { LanguageSlice, RepoInfo } from "$lib/components/repo/types"; // `/tree/{ref}` is this same page at another ref, so they share a load @@ -168,11 +167,6 @@ export const loadRepoIndex = async ( readmeHtml, commits, tagsByCommit: tagsByCommitHash(commits, tags), - // TODO: mocked, like the pipelines tab. only repos with a spindle run CI, - // so the appview does not even ask for statuses without one - pipelinesByCommit: parent.repo.spindle - ? pipelinesByCommitHash(commits.map((commit) => commit.hash)) - : {}, totalCommits: results.log.value?.total ?? commits.length, branches: branches.slice(0, BRANCH_LIMIT), totalBranches: branches.length, diff --git a/web/src/lib/components/repo/CommitHeader.stories.svelte b/web/src/lib/components/repo/CommitHeader.stories.svelte index b1cede405..8494c336f 100644 --- a/web/src/lib/components/repo/CommitHeader.stories.svelte +++ b/web/src/lib/components/repo/CommitHeader.stories.svelte @@ -3,6 +3,7 @@ import { expect } from "storybook/test"; import type { CommitDetail } from "$lib/api/repo"; import CommitHeader from "./CommitHeader.svelte"; + import { pipelinesByCommitHash } from "./pipelines/mock"; const fullCommit: CommitDetail = { hash: "0123456789abcdef0123456789abcdef01234567", @@ -103,6 +104,13 @@ + + ; } - let { ownerHandle, repoName, commit, parent = "", changeId = "" }: Props = $props(); + let { + ownerHandle, + repoName, + commit, + parent = "", + changeId = "", + pipelineStatuses + }: Props = $props(); const base = $derived(`/${ownerHandle}/${repoName}`); @@ -113,4 +123,20 @@ {/if} + + {#if pipelineStatuses} + {#await pipelineStatuses then statuses} + {#if statuses[commit.hash]} +
+ +
+ {/if} + {/await} + {/if} diff --git a/web/src/lib/components/repo/CommitList.stories.svelte b/web/src/lib/components/repo/CommitList.stories.svelte index 9bd3d3b94..0c22fc729 100644 --- a/web/src/lib/components/repo/CommitList.stories.svelte +++ b/web/src/lib/components/repo/CommitList.stories.svelte @@ -49,7 +49,7 @@ tagsByCommit: { "0123456789abcdef0123456789abcdef01234567": ["v1.0.0"] }, - pipelinesByCommit: pipelinesByCommitHash(commits.map((commit) => commit.hash)) + pipelineStatuses: Promise.resolve(pipelinesByCommitHash(commits.map((commit) => commit.hash))) } }); diff --git a/web/src/lib/components/repo/CommitList.svelte b/web/src/lib/components/repo/CommitList.svelte index 3f9671221..5111c4c82 100644 --- a/web/src/lib/components/repo/CommitList.svelte +++ b/web/src/lib/components/repo/CommitList.svelte @@ -6,24 +6,18 @@ import User from "$lib/components/ui/User.svelte"; import Separator from "../ui/Separator.svelte"; import PipelineWorkflows from "./pipelines/PipelineWorkflows.svelte"; - import type { CommitSummary, PipelineSummary } from "./types"; + import type { CommitSummary } from "./types"; + import type { CommitStatuses } from "$lib/api/commitStatuses"; interface Props { ownerHandle: string; repoName: string; commits: CommitSummary[]; tagsByCommit?: Record; - /** only repos with a spindle have any, and only some commits get one */ - pipelinesByCommit?: Record; + pipelineStatuses?: Promise; } - let { - ownerHandle, - repoName, - commits, - tagsByCommit = {}, - pipelinesByCommit = {} - }: Props = $props(); + let { ownerHandle, repoName, commits, tagsByCommit = {}, pipelineStatuses }: Props = $props(); const base = $derived(`/${ownerHandle}/${repoName}`); let expanded = $state>({}); @@ -81,13 +75,17 @@ {name} {/each} {/if} - {#if pipelinesByCommit[commit.hash]} - + {#if pipelineStatuses} + {#await pipelineStatuses then statuses} + {#if statuses[commit.hash]} + + {/if} + {/await} {/if} diff --git a/web/src/lib/components/repo/CommitLogView.stories.svelte b/web/src/lib/components/repo/CommitLogView.stories.svelte index 8f4a4e274..0a1367d71 100644 --- a/web/src/lib/components/repo/CommitLogView.stories.svelte +++ b/web/src/lib/components/repo/CommitLogView.stories.svelte @@ -2,6 +2,7 @@ import { defineMeta, type StoryContext } from "@storybook/addon-svelte-csf"; import { expect, fn, userEvent } from "storybook/test"; import CommitLogView from "./CommitLogView.svelte"; + import { pipelinesByCommitHash } from "./pipelines/mock"; import type { CommitSummary } from "./types"; const commit = (index: number): CommitSummary => ({ @@ -82,6 +83,12 @@ + c.hash))) + }} +/> diff --git a/web/src/lib/components/repo/CommitLogView.svelte b/web/src/lib/components/repo/CommitLogView.svelte index a38d1f638..ee33d16bc 100644 --- a/web/src/lib/components/repo/CommitLogView.svelte +++ b/web/src/lib/components/repo/CommitLogView.svelte @@ -9,6 +9,8 @@ import Tag from "$lib/components/ui/Tag.svelte"; import TimeAgo from "$lib/components/ui/TimeAgo.svelte"; import { createCopyFeedback } from "$lib/copy.svelte"; + import type { CommitStatuses } from "$lib/api/commitStatuses"; + import PipelineWorkflows from "./pipelines/PipelineWorkflows.svelte"; import type { CommitSummary } from "./types"; interface Props { @@ -17,6 +19,7 @@ ref: string; commits: CommitSummary[]; tagsByCommit?: Record; + pipelineStatuses?: Promise; page: number; pageCount: number; onPageChange?: (page: number) => void; @@ -28,6 +31,7 @@ ref, commits, tagsByCommit = {}, + pipelineStatuses, page, pageCount, onPageChange @@ -114,6 +118,20 @@ {#each tagsByCommit[commit.hash] ?? [] as name (name)} {name} {/each} + {#if pipelineStatuses} + {#await pipelineStatuses then statuses} + {#if statuses[commit.hash]} + + + + {/if} + {/await} + {/if} {#if commit.body && expanded[commit.hash]}

diff --git a/web/src/lib/components/repo/CommitView.svelte b/web/src/lib/components/repo/CommitView.svelte index 2be385e89..68cfdfe86 100644 --- a/web/src/lib/components/repo/CommitView.svelte +++ b/web/src/lib/components/repo/CommitView.svelte @@ -7,6 +7,7 @@ import DiffView from "./DiffView.svelte"; import type { DiffStyle } from "./pierre"; import TabPanel from "$lib/components/ui/TabPanel.svelte"; + import type { CommitStatuses } from "$lib/api/commitStatuses"; interface Props { ownerHandle: string; @@ -23,6 +24,7 @@ diffStyle?: DiffStyle; // server-prerendered shadow dom per row key, only for the initial ssr prerendered?: Record; + pipelineStatuses?: Promise; } let { @@ -35,7 +37,8 @@ files, stat, diffStyle = "unified", - prerendered + prerendered, + pipelineStatuses }: Props = $props(); const base = $derived(`/${ownerHandle}/${repoName}`); @@ -54,7 +57,7 @@

- +
diff --git a/web/src/lib/components/repo/RepoIndexView.stories.svelte b/web/src/lib/components/repo/RepoIndexView.stories.svelte index 84149b4d9..ab68140b2 100644 --- a/web/src/lib/components/repo/RepoIndexView.stories.svelte +++ b/web/src/lib/components/repo/RepoIndexView.stories.svelte @@ -58,8 +58,6 @@ readmeHtml: "

tangled

", commits, tagsByCommit: {}, - // every third commit is left without a run, so all three cases show - pipelinesByCommit: pipelinesByCommitHash(commits.map((commit) => commit.hash)), totalCommits: 42, branches: [ { @@ -92,7 +90,6 @@ readmeHtml: null, commits: [], tagsByCommit: {}, - pipelinesByCommit: {}, totalCommits: 0, branches: [], totalBranches: 0, @@ -106,7 +103,13 @@ title: "Repo/RepoIndexView", component: RepoIndexView, tags: ["autodocs"], - args: { repo, data, bobbinUrl: "https://bobbin.example.test" } + args: { + repo, + data, + bobbinUrl: "https://bobbin.example.test", + // every third commit is left without a run, so all three cases show + pipelineStatuses: Promise.resolve(pipelinesByCommitHash(commits.map((commit) => commit.hash))) + } }); diff --git a/web/src/lib/components/repo/RepoIndexView.svelte b/web/src/lib/components/repo/RepoIndexView.svelte index 97552513a..a17372534 100644 --- a/web/src/lib/components/repo/RepoIndexView.svelte +++ b/web/src/lib/components/repo/RepoIndexView.svelte @@ -14,15 +14,17 @@ import TagList from "./TagList.svelte"; import TabPanel from "$lib/components/ui/TabPanel.svelte"; import type { loadRepoIndex } from "$lib/api/repoIndex"; + import type { CommitStatuses } from "$lib/api/commitStatuses"; import type { RepoInfo } from "./types"; interface Props { repo: RepoInfo; data: Awaited>; bobbinUrl: string; + pipelineStatuses?: Promise; } - let { repo, data, bobbinUrl }: Props = $props(); + let { repo, data, bobbinUrl, pipelineStatuses }: Props = $props(); const base = $derived(`/${repo.ownerHandle}/${repo.name}`); const encodedRef = $derived(encodeURIComponent(data.ref)); @@ -95,7 +97,7 @@ repoName={repo.name} commits={data.commits} tagsByCommit={data.tagsByCommit} - pipelinesByCommit={data.pipelinesByCommit} + {pipelineStatuses} /> {/if} diff --git a/web/src/lib/components/repo/pipelines/mock.ts b/web/src/lib/components/repo/pipelines/mock.ts index 111327558..14d1782f4 100644 --- a/web/src/lib/components/repo/pipelines/mock.ts +++ b/web/src/lib/components/repo/pipelines/mock.ts @@ -89,11 +89,9 @@ export const pipelines: PipelineSummary[] = [ } ]; -/** - * fake per-commit runs for the repo overview's commit list. the real call is - * the spindle's ci.queryPipelines with a commits filter, one run per commit. - * every third commit is left without a run so the gap renders too. - */ +// fake per-commit runs for the commit list and header stories. the real call +// is the spindle's ci.queryPipelines with a commits filter, one run per +// commit. every third commit is left without a run so the gap renders too. export const pipelinesByCommitHash = (hashes: string[]): Record => { const byHash: Record = {}; hashes.forEach((hash, index) => { diff --git a/web/src/routes/[handle]/[repo]/+page.svelte b/web/src/routes/[handle]/[repo]/+page.svelte index 3415ce397..7c469da9b 100644 --- a/web/src/routes/[handle]/[repo]/+page.svelte +++ b/web/src/routes/[handle]/[repo]/+page.svelte @@ -1,7 +1,17 @@ - + diff --git a/web/src/routes/[handle]/[repo]/commit/[ref]/+page.svelte b/web/src/routes/[handle]/[repo]/commit/[ref]/+page.svelte index 46647de99..33ff333b3 100644 --- a/web/src/routes/[handle]/[repo]/commit/[ref]/+page.svelte +++ b/web/src/routes/[handle]/[repo]/commit/[ref]/+page.svelte @@ -1,5 +1,6 @@ diff --git a/web/src/routes/[handle]/[repo]/commits/[ref]/+page.svelte b/web/src/routes/[handle]/[repo]/commits/[ref]/+page.svelte index bf9bcaba7..533eb4619 100644 --- a/web/src/routes/[handle]/[repo]/commits/[ref]/+page.svelte +++ b/web/src/routes/[handle]/[repo]/commits/[ref]/+page.svelte @@ -1,10 +1,20 @@ - + -- 2.51.2