feat(app,server): add pull request pane to the workspace sidebar
Introduce a PR tab in the explorer sidebar that shows status checks, reviews, and the merged timeline for the current branch's pull request. Extend checkout PR status with number, draft flag, repo identity, and per-check workflow/duration; add a new pull_request_timeline request/response pair backed by a GraphQL query; enrich the workspace git snapshot to carry the fields through to the client; and extend the checkout-git-actions store so timeline queries are invalidated alongside status when mutations land.
This commit is contained in:
@@ -24,7 +24,6 @@ import {
|
||||
pushCurrentBranch,
|
||||
resolveRepositoryDefaultBranch,
|
||||
parseWorktreeList,
|
||||
parseStatusCheckRollup,
|
||||
isPaseoWorktreePath,
|
||||
isDescendantPath,
|
||||
searchGitHubIssuesAndPrs,
|
||||
@@ -981,101 +980,6 @@ const x = 1;
|
||||
});
|
||||
});
|
||||
|
||||
it("parses real gh status check rollup output and dedupes by latest check run", () => {
|
||||
expect(
|
||||
parseStatusCheckRollup([
|
||||
{
|
||||
__typename: "CheckRun",
|
||||
completedAt: "2026-04-02T13:53:59Z",
|
||||
conclusion: "SUCCESS",
|
||||
detailsUrl: "https://github.com/org/repo/actions/runs/123",
|
||||
name: "review_app",
|
||||
startedAt: "2026-04-02T13:49:31Z",
|
||||
status: "COMPLETED",
|
||||
workflowName: "Deploy PR Preview",
|
||||
},
|
||||
{
|
||||
__typename: "CheckRun",
|
||||
completedAt: "2026-04-02T13:58:59Z",
|
||||
conclusion: "FAILURE",
|
||||
detailsUrl: "https://github.com/org/repo/actions/runs/124",
|
||||
name: "review_app",
|
||||
startedAt: "2026-04-02T13:55:31Z",
|
||||
status: "COMPLETED",
|
||||
},
|
||||
]),
|
||||
).toEqual([
|
||||
{
|
||||
name: "review_app",
|
||||
status: "failure",
|
||||
url: "https://github.com/org/repo/actions/runs/124",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("parses mixed check run and status context entries", () => {
|
||||
expect(
|
||||
parseStatusCheckRollup([
|
||||
{
|
||||
__typename: "CheckRun",
|
||||
name: "unit-tests",
|
||||
status: "IN_PROGRESS",
|
||||
conclusion: null,
|
||||
detailsUrl: "https://github.com/org/repo/actions/runs/200",
|
||||
startedAt: "2026-04-02T13:49:31Z",
|
||||
},
|
||||
{
|
||||
__typename: "StatusContext",
|
||||
context: "lint",
|
||||
state: "SUCCESS",
|
||||
targetUrl: "https://github.com/org/repo/status/300",
|
||||
createdAt: "2026-04-02T13:48:00Z",
|
||||
},
|
||||
]),
|
||||
).toEqual([
|
||||
{
|
||||
name: "unit-tests",
|
||||
status: "pending",
|
||||
url: "https://github.com/org/repo/actions/runs/200",
|
||||
},
|
||||
{
|
||||
name: "lint",
|
||||
status: "success",
|
||||
url: "https://github.com/org/repo/status/300",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("returns an empty list for nullish or empty status check rollups", () => {
|
||||
expect(parseStatusCheckRollup(undefined)).toEqual([]);
|
||||
expect(parseStatusCheckRollup(null)).toEqual([]);
|
||||
expect(parseStatusCheckRollup([])).toEqual([]);
|
||||
});
|
||||
|
||||
it("ignores unknown status check rollup node types", () => {
|
||||
expect(
|
||||
parseStatusCheckRollup([
|
||||
{
|
||||
__typename: "Commit",
|
||||
oid: "abc123",
|
||||
},
|
||||
{
|
||||
__typename: "CheckRun",
|
||||
name: "build",
|
||||
status: "COMPLETED",
|
||||
conclusion: "SUCCESS",
|
||||
detailsUrl: "https://github.com/org/repo/actions/runs/500",
|
||||
},
|
||||
]),
|
||||
).toEqual([
|
||||
{
|
||||
name: "build",
|
||||
status: "success",
|
||||
url: "https://github.com/org/repo/actions/runs/500",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("returns merged PR status when no open PR exists for the current branch", async () => {
|
||||
execSync("git checkout -b feature", { cwd: repoDir });
|
||||
execSync("git remote add origin https://github.com/getpaseo/paseo.git", { cwd: repoDir });
|
||||
@@ -1098,6 +1002,55 @@ const x = 1;
|
||||
expect(status.status?.state).toBe("merged");
|
||||
});
|
||||
|
||||
it("propagates S1 PR metadata and check display fields through checkout PR status", async () => {
|
||||
execSync("git checkout -b feature", { cwd: repoDir });
|
||||
execSync("git remote add origin https://github.com/getpaseo/paseo.git", { cwd: repoDir });
|
||||
|
||||
const status = await getPullRequestStatus(
|
||||
repoDir,
|
||||
createGitHubServiceForStatus(
|
||||
createPullRequestStatus({
|
||||
number: 123,
|
||||
isDraft: true,
|
||||
checks: [
|
||||
{
|
||||
name: "server-tests",
|
||||
status: "success",
|
||||
url: "https://github.com/getpaseo/paseo/actions/runs/123",
|
||||
workflow: "Server CI",
|
||||
duration: "2m 14s",
|
||||
},
|
||||
],
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
expect(status).toEqual({
|
||||
githubFeaturesEnabled: true,
|
||||
status: {
|
||||
number: 123,
|
||||
url: "https://github.com/getpaseo/paseo/pull/123",
|
||||
title: "Ship feature",
|
||||
state: "open",
|
||||
baseRefName: "main",
|
||||
headRefName: "feature",
|
||||
isMerged: false,
|
||||
isDraft: true,
|
||||
checks: [
|
||||
{
|
||||
name: "server-tests",
|
||||
status: "success",
|
||||
url: "https://github.com/getpaseo/paseo/actions/runs/123",
|
||||
workflow: "Server CI",
|
||||
duration: "2m 14s",
|
||||
},
|
||||
],
|
||||
checksStatus: "none",
|
||||
reviewDecision: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("returns closed-unmerged PR status without marking it as merged", async () => {
|
||||
execSync("git checkout -b feature", { cwd: repoDir });
|
||||
execSync("git remote add origin https://github.com/getpaseo/paseo.git", { cwd: repoDir });
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
resolveGitHubRepo,
|
||||
type GitHubService,
|
||||
} from "../services/github-service.js";
|
||||
export { parseStatusCheckRollup } from "../services/github-service.js";
|
||||
import { parseGitRevParsePath, resolveGitRevParsePath } from "./git-rev-parse-path.js";
|
||||
import { runGitCommand } from "./run-git-command.js";
|
||||
import { isPaseoOwnedWorktreeCwd } from "./worktree.js";
|
||||
@@ -1819,12 +1818,16 @@ export interface CreatePullRequestOptions {
|
||||
}
|
||||
|
||||
export interface PullRequestStatus {
|
||||
number?: number;
|
||||
repoOwner?: string;
|
||||
repoName?: string;
|
||||
url: string;
|
||||
title: string;
|
||||
state: string;
|
||||
baseRefName: string;
|
||||
headRefName: string;
|
||||
isMerged: boolean;
|
||||
isDraft?: boolean;
|
||||
checks?: PullRequestCheck[];
|
||||
checksStatus?: ChecksStatus;
|
||||
reviewDecision?: ReviewDecision;
|
||||
@@ -1839,6 +1842,8 @@ export type PullRequestCheck = {
|
||||
name: string;
|
||||
status: "success" | "failure" | "pending" | "skipped" | "cancelled";
|
||||
url: string | null;
|
||||
workflow?: string;
|
||||
duration?: string;
|
||||
};
|
||||
|
||||
export type ChecksStatus = "none" | "pending" | "success" | "failure";
|
||||
|
||||
Reference in New Issue
Block a user