Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import {
CommandList,
} from "@superset/ui/command";
import { Popover, PopoverContent, PopoverTrigger } from "@superset/ui/popover";
import { toast } from "@superset/ui/sonner";
import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
import { useQuery } from "@tanstack/react-query";
import type { ReactNode } from "react";
import { useId, useState } from "react";
import { useEffect, useId, useRef, useState } from "react";
import { useHostUrl } from "renderer/hooks/host-service/useHostTargetUrl";
import { useDebouncedValue } from "renderer/hooks/useDebouncedValue";
import { getHostServiceClientByUrl } from "renderer/lib/host-service-client";
Expand Down Expand Up @@ -58,7 +59,7 @@ export function GitHubIssueLinkCommand({
const debouncedTrimmed = debouncedQuery.trim();
const isPendingDebounce = trimmedQuery !== debouncedTrimmed;

const { data, isFetching } = useQuery({
const { data, isFetching, error } = useQuery({
queryKey: [
"workspaceCreation",
"searchGitHubIssues",
Expand All @@ -78,8 +79,21 @@ export function GitHubIssueLinkCommand({
});
},
enabled: !!projectId && !!hostUrl && open,
retry: false,
});

const lastToastedError = useRef<string | null>(null);
useEffect(() => {
const msg = error instanceof Error ? error.message : null;
if (!msg) {
lastToastedError.current = null;
return;
}
if (lastToastedError.current === msg) return;
lastToastedError.current = msg;
toast.error(`Couldn't load issues: ${msg}`);
}, [error]);

const searchResults = data?.issues ?? [];
const repoMismatch =
data && "repoMismatch" in data ? data.repoMismatch : null;
Expand Down Expand Up @@ -142,19 +156,29 @@ export function GitHubIssueLinkCommand({
<CommandList className="max-h-[280px]">
{searchResults.length === 0 && (
<CommandEmpty>
{isLoading
? debouncedTrimmed
? "Searching..."
: "Loading..."
: repoMismatch
? `Issue URL must match ${repoMismatch}.`
: debouncedTrimmed
? showClosed
? "No issues found."
: "No open issues found."
: showClosed
? "No issues found."
: "No open issues found."}
{isLoading ? (
debouncedTrimmed ? (
"Searching..."
) : (
"Loading..."
)
) : error instanceof Error ? (
<span className="select-text cursor-text text-destructive">
{error.message}
</span>
) : repoMismatch ? (
`Issue URL must match ${repoMismatch}.`
) : debouncedTrimmed ? (
showClosed ? (
"No issues found."
) : (
"No open issues found."
)
) : showClosed ? (
"No issues found."
) : (
"No open issues found."
)}
</CommandEmpty>
)}
{searchResults.length > 0 && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import {
CommandList,
} from "@superset/ui/command";
import { Popover, PopoverContent, PopoverTrigger } from "@superset/ui/popover";
import { toast } from "@superset/ui/sonner";
import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
import { useQuery } from "@tanstack/react-query";
import type { ReactNode } from "react";
import { useId, useState } from "react";
import { useEffect, useId, useRef, useState } from "react";
import { useHostUrl } from "renderer/hooks/host-service/useHostTargetUrl";
import { useDebouncedValue } from "renderer/hooks/useDebouncedValue";
import { getHostServiceClientByUrl } from "renderer/lib/host-service-client";
Expand Down Expand Up @@ -58,7 +59,7 @@ export function PRLinkCommand({
const debouncedTrimmed = debouncedQuery.trim();
const isPendingDebounce = trimmedQuery !== debouncedTrimmed;

const { data, isFetching } = useQuery({
const { data, isFetching, error } = useQuery({
queryKey: [
"workspaceCreation",
"searchPullRequests",
Expand All @@ -78,8 +79,23 @@ export function PRLinkCommand({
});
},
enabled: !!projectId && !!hostUrl && open,
retry: false,
});

// One toast per error transition — without this, the dropdown's
// empty-state silently hides upstream tRPC failures.
const lastToastedError = useRef<string | null>(null);
useEffect(() => {
const msg = error instanceof Error ? error.message : null;
if (!msg) {
lastToastedError.current = null;
return;
}
if (lastToastedError.current === msg) return;
lastToastedError.current = msg;
toast.error(`Couldn't load pull requests: ${msg}`);
}, [error]);

const pullRequests = data?.pullRequests ?? [];
const repoMismatch =
data && "repoMismatch" in data ? data.repoMismatch : null;
Expand Down Expand Up @@ -142,19 +158,29 @@ export function PRLinkCommand({
<CommandList className="max-h-[280px]">
{pullRequests.length === 0 && (
<CommandEmpty>
{isLoading
? debouncedTrimmed
? "Searching..."
: "Loading..."
: repoMismatch
? `PR URL must match ${repoMismatch}.`
: debouncedTrimmed
? showClosed
? "No pull requests found."
: "No open pull requests found."
: showClosed
? "No pull requests found."
: "No open pull requests."}
{isLoading ? (
debouncedTrimmed ? (
"Searching..."
) : (
"Loading..."
)
) : error instanceof Error ? (
<span className="select-text cursor-text text-destructive">
{error.message}
</span>
) : repoMismatch ? (
`PR URL must match ${repoMismatch}.`
) : debouncedTrimmed ? (
showClosed ? (
"No pull requests found."
) : (
"No open pull requests found."
)
) : showClosed ? (
"No pull requests found."
) : (
"No open pull requests."
)}
</CommandEmpty>
)}
{pullRequests.length > 0 && (
Expand Down
7 changes: 7 additions & 0 deletions packages/host-service/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import { runMainWorkspaceSweep } from "./runtime/main-workspace-sweep";
import { PullRequestRuntimeManager } from "./runtime/pull-requests";
import { registerWorkspaceTerminalRoute } from "./terminal/terminal";
import { appRouter } from "./trpc/router";
import {
execGh as defaultExecGh,
type ExecGh,
} from "./trpc/router/workspace-creation/utils/exec-gh";
import type { ApiClient } from "./types";

export interface CreateAppOptions {
Expand Down Expand Up @@ -46,6 +50,7 @@ export interface CreateAppOptions {
db?: HostDb;
api?: ApiClient;
github?: () => Promise<Octokit>;
execGh?: ExecGh;
chatRuntime?: ChatRuntimeManager;
chatService?: ChatService;
}
Expand Down Expand Up @@ -76,6 +81,7 @@ export function createApp(options: CreateAppOptions): CreateAppResult {
}
return new Octokit({ auth: token });
});
const execGh: ExecGh = options.execGh ?? defaultExecGh;

const filesystem = new WorkspaceFilesystemManager({ db });
// GitWatcher is the single source of truth for `.git/` and worktree fs
Expand Down Expand Up @@ -161,6 +167,7 @@ export function createApp(options: CreateAppOptions): CreateAppResult {
return {
git,
github,
execGh,
api,
db,
runtime,
Expand Down
33 changes: 17 additions & 16 deletions packages/host-service/src/trpc/router/git/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { isAbsolute, join, normalize, sep } from "node:path";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { z } from "zod";
import { projects, pullRequests, workspaces } from "../../../db/schema";
import { pullRequests, workspaces } from "../../../db/schema";
import { protectedProcedure, queryProcedure, router } from "../../index";
import { resolveGithubRepo } from "../workspace-creation/shared/project-helpers";
import type {
ChangedFile,
CheckConclusionState,
Expand Down Expand Up @@ -721,17 +722,17 @@ export const gitRouter = router({
});
}

const project = ctx.db.query.projects
.findFirst({ where: eq(projects.id, workspace.projectId) })
.sync();
if (!project) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: `Project ${workspace.projectId} not found in database`,
});
}
if (!project.repoOwner || !project.repoName) {
return { reviewThreads: [], conversationComments: [] };
let repo: { owner: string; name: string };
try {
repo = await resolveGithubRepo(ctx, workspace.projectId);
} catch (err) {
// Expected resolver failures (project not set up locally, no
// GitHub remote) degrade silently — the review tab just stays
// empty. Anything else is a real bug; propagate it.
if (err instanceof TRPCError) {
return { reviewThreads: [], conversationComments: [] };
}
throw err;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const octokit = await ctx.github();
Expand All @@ -741,8 +742,8 @@ export const gitRouter = router({
const result: GraphQLThreadsResult = await octokit.graphql(
REVIEW_THREADS_QUERY,
{
owner: project.repoOwner,
name: project.repoName,
owner: repo.owner,
name: repo.name,
prNumber: pr.prNumber,
},
);
Expand All @@ -760,8 +761,8 @@ export const gitRouter = router({
let hasMore = true;
while (hasMore) {
const { data: comments } = await octokit.issues.listComments({
owner: project.repoOwner,
repo: project.repoName,
owner: repo.owner,
repo: repo.name,
issue_number: pr.prNumber,
per_page: 100,
page,
Expand Down
Loading
Loading