forked from github-starred/komodo
* inc version * Komodo interp in ui compose file * fix auto update when image doesn't specify tag by defaulting to latest * Pull image buttons don't need safety dialog * WIP crosscompile * rename * entrypoint * fix copy * remove example/* from workspace * add targets * multiarch pkg config * use specific COPY * update deps * multiarch build command * pre compile deps * cross compile * enable-linger * remove spammed log when server doesn't have docker * add multiarch.Dockerfile * fix casing * fix tag * try not let COPY fail * try * ARG TARGETPLATFORM * use /app for consistency * try * delete cross-compile approach * add multiarch core build * multiarch Deno * single arch multi arch * typeshare cli note * new typeshare * remove note about aarch64 image * test configs * fix config file headers * binaries dockerfile * update cargo build * docs * simple * just simple * use -p * add configurable binaries tag * add multi-arch * allow copy to fail * fix binary paths * frontend Dockerfiel * use dedicated static frontend build * auto retry getting instance state from aws * retry 5 times * cleanup * simplify binary build * try alpine and musl * install alpine deps * back to debian, try rustls * move fully to rustls * single arch builds using single binary image * default IMAGE_TAG * cleanup * try caching deps * single arch add frontend build * rustls::crypto::ring::default_provider() * back to simple * comment dockerfile * add select options prop, render checkboxes if present * add allowSelectedIf to enable / disable rows where necessary * rename allowSelectIf to isSelectable, allow false as global disable, disable checkboxes when not allowed * rename isSelectable to disableRow (it works the oppsite way lol) * selected resources hook, start deployment batch execute component * add deployment group actions * add deployment group actions * add default (empty) group actions for other resources * fix checkbox header styles * explicitly check if disableRow is passed (this prop is cursed) * don't disable row selection for deployments table * don't need id for groupactions * add group actions to resources page * fix row checkbox (prop not cursed, i dumb) * re-implement group action list using dropdown menu * only make group actions clickable when at least one row selected * add loading indicator * gap betwen new resource and group actions * refactor group actions * remove "Batch" from action labels * add group actions for relevant resources * fix hardcode * add selectOptions to relevant tables * select by name not id * expect selected to be names * add note re selection state init for future reference * multi select working nicely for all resources * configure server health check timeout * config message * refresh processes remove dead processes * simplify the build args * default timeout seconds 3 --------- Co-authored-by: kv <karamvir.singh98@gmail.com>
269 lines
8.1 KiB
TypeScript
269 lines
8.1 KiB
TypeScript
import { useInvalidate, useRead, useWrite } from "@lib/hooks";
|
|
import { RequiredResourceComponents } from "@types";
|
|
import { Card } from "@ui/card";
|
|
import { FolderGit, GitBranch, Loader2, RefreshCcw } from "lucide-react";
|
|
import { RepoConfig } from "./config";
|
|
import { BuildRepo, CloneRepo, PullRepo } from "./actions";
|
|
import { DeleteResource, NewResource, ResourceLink } from "../common";
|
|
import { RepoTable } from "./table";
|
|
import {
|
|
repo_state_intention,
|
|
stroke_color_class_by_intention,
|
|
} from "@lib/color";
|
|
import { cn } from "@lib/utils";
|
|
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@ui/hover-card";
|
|
import { useServer } from "../server";
|
|
import { Types } from "komodo_client";
|
|
import { DashboardPieChart } from "@pages/home/dashboard";
|
|
import { ResourcePageHeader, StatusBadge } from "@components/util";
|
|
import { Badge } from "@ui/badge";
|
|
import { useToast } from "@ui/use-toast";
|
|
import { Button } from "@ui/button";
|
|
import { useBuilder } from "../builder";
|
|
import { RenameResource } from "@components/config/util";
|
|
import { GroupActions } from "@components/group-actions";
|
|
|
|
export const useRepo = (id?: string) =>
|
|
useRead("ListRepos", {}, { refetchInterval: 10_000 }).data?.find(
|
|
(d) => d.id === id
|
|
);
|
|
|
|
export const useFullRepo = (id: string) =>
|
|
useRead("GetRepo", { repo: id }, { refetchInterval: 10_000 }).data;
|
|
|
|
const RepoIcon = ({ id, size }: { id?: string; size: number }) => {
|
|
const state = useRepo(id)?.info.state;
|
|
const color = stroke_color_class_by_intention(repo_state_intention(state));
|
|
return <GitBranch className={cn(`w-${size} h-${size}`, state && color)} />;
|
|
};
|
|
|
|
export const RepoComponents: RequiredResourceComponents = {
|
|
list_item: (id) => useRepo(id),
|
|
resource_links: (resource) => (resource.config as Types.RepoConfig).links,
|
|
|
|
Description: () => <>Build using custom scripts. Or anything else.</>,
|
|
|
|
Dashboard: () => {
|
|
const summary = useRead("GetReposSummary", {}).data;
|
|
return (
|
|
<DashboardPieChart
|
|
data={[
|
|
{ intention: "Good", value: summary?.ok ?? 0, title: "Ok" },
|
|
{
|
|
intention: "Warning",
|
|
value: (summary?.cloning ?? 0) + (summary?.pulling ?? 0),
|
|
title: "Pulling",
|
|
},
|
|
{
|
|
intention: "Critical",
|
|
value: summary?.failed ?? 0,
|
|
title: "Failed",
|
|
},
|
|
{
|
|
intention: "Unknown",
|
|
value: summary?.unknown ?? 0,
|
|
title: "Unknown",
|
|
},
|
|
]}
|
|
/>
|
|
);
|
|
},
|
|
|
|
New: ({ server_id }) => <NewResource type="Repo" server_id={server_id} />,
|
|
|
|
GroupActions: () => (
|
|
<GroupActions
|
|
type="Repo"
|
|
actions={["PullRepo", "CloneRepo", "BuildRepo"]}
|
|
/>
|
|
),
|
|
|
|
Table: ({ resources }) => (
|
|
<RepoTable repos={resources as Types.RepoListItem[]} />
|
|
),
|
|
|
|
Icon: ({ id }) => <RepoIcon id={id} size={4} />,
|
|
BigIcon: ({ id }) => <RepoIcon id={id} size={8} />,
|
|
|
|
State: ({ id }) => {
|
|
const state = useRepo(id)?.info.state;
|
|
return <StatusBadge text={state} intent={repo_state_intention(state)} />;
|
|
},
|
|
|
|
Status: {
|
|
Cloned: ({ id }) => {
|
|
const info = useRepo(id)?.info;
|
|
if (!info?.cloned_hash || info.cloned_hash === info.latest_hash) {
|
|
return null;
|
|
}
|
|
return (
|
|
<HoverCard openDelay={200}>
|
|
<HoverCardTrigger asChild>
|
|
<Card className="px-3 py-2 hover:bg-accent/50 transition-colors cursor-pointer">
|
|
<div className="text-muted-foreground text-sm text-nowrap overflow-hidden overflow-ellipsis">
|
|
cloned: {info.cloned_hash}
|
|
</div>
|
|
</Card>
|
|
</HoverCardTrigger>
|
|
<HoverCardContent align="start">
|
|
<div className="grid">
|
|
<div className="text-muted-foreground">commit message:</div>
|
|
{info.cloned_message}
|
|
</div>
|
|
</HoverCardContent>
|
|
</HoverCard>
|
|
);
|
|
},
|
|
Built: ({ id }) => {
|
|
const info = useRepo(id)?.info;
|
|
const fullInfo = useFullRepo(id)?.info;
|
|
if (!info?.built_hash || info.built_hash === info.latest_hash) {
|
|
return null;
|
|
}
|
|
return (
|
|
<HoverCard openDelay={200}>
|
|
<HoverCardTrigger asChild>
|
|
<Card className="px-3 py-2 hover:bg-accent/50 transition-colors cursor-pointer">
|
|
<div className="text-muted-foreground text-sm text-nowrap overflow-hidden overflow-ellipsis">
|
|
built: {info.built_hash}
|
|
</div>
|
|
</Card>
|
|
</HoverCardTrigger>
|
|
<HoverCardContent align="start">
|
|
<div className="grid gap-2">
|
|
<Badge
|
|
variant="secondary"
|
|
className="w-fit text-muted-foreground"
|
|
>
|
|
commit message
|
|
</Badge>
|
|
{fullInfo?.built_message}
|
|
</div>
|
|
</HoverCardContent>
|
|
</HoverCard>
|
|
);
|
|
},
|
|
Latest: ({ id }) => {
|
|
const info = useRepo(id)?.info;
|
|
const fullInfo = useFullRepo(id)?.info;
|
|
if (!info?.latest_hash) {
|
|
return null;
|
|
}
|
|
return (
|
|
<HoverCard openDelay={200}>
|
|
<HoverCardTrigger asChild>
|
|
<Card className="px-3 py-2 hover:bg-accent/50 transition-colors cursor-pointer">
|
|
<div className="text-muted-foreground text-sm text-nowrap overflow-hidden overflow-ellipsis">
|
|
latest: {info.latest_hash}
|
|
</div>
|
|
</Card>
|
|
</HoverCardTrigger>
|
|
<HoverCardContent align="start">
|
|
<div className="grid gap-2">
|
|
<Badge
|
|
variant="secondary"
|
|
className="w-fit text-muted-foreground"
|
|
>
|
|
commit message
|
|
</Badge>
|
|
{fullInfo?.latest_message}
|
|
</div>
|
|
</HoverCardContent>
|
|
</HoverCard>
|
|
);
|
|
},
|
|
Refresh: ({ id }) => {
|
|
const { toast } = useToast();
|
|
const inv = useInvalidate();
|
|
const { mutate, isPending } = useWrite("RefreshRepoCache", {
|
|
onSuccess: () => {
|
|
inv(["ListRepos"], ["GetRepo", { repo: id }]);
|
|
toast({ title: "Refreshed repo status cache" });
|
|
},
|
|
});
|
|
return (
|
|
<Button
|
|
variant="outline"
|
|
size="icon"
|
|
onClick={() => {
|
|
mutate({ repo: id });
|
|
toast({ title: "Triggered refresh of repo status cache" });
|
|
}}
|
|
>
|
|
{isPending ? (
|
|
<Loader2 className="w-4 h-4 animate-spin" />
|
|
) : (
|
|
<RefreshCcw className="w-4 h-4" />
|
|
)}
|
|
</Button>
|
|
);
|
|
},
|
|
},
|
|
|
|
Info: {
|
|
Target: ({ id }) => {
|
|
const info = useRepo(id)?.info;
|
|
const server = useServer(info?.server_id);
|
|
const builder = useBuilder(info?.builder_id);
|
|
return (
|
|
<div className="flex items-center gap-x-4 gap-y-2 flex-wrap">
|
|
{server?.id &&
|
|
(builder?.id ? (
|
|
<div className="pr-4 text-sm border-r">
|
|
<ResourceLink type="Server" id={server.id} />
|
|
</div>
|
|
) : (
|
|
<ResourceLink type="Server" id={server.id} />
|
|
))}
|
|
{builder?.id && <ResourceLink type="Builder" id={builder.id} />}
|
|
</div>
|
|
);
|
|
},
|
|
Repo: ({ id }) => {
|
|
const repo = useRepo(id)?.info.repo;
|
|
return (
|
|
<div className="flex items-center gap-2">
|
|
<FolderGit className="w-4 h-4" />
|
|
{repo}
|
|
</div>
|
|
);
|
|
},
|
|
Branch: ({ id }) => {
|
|
const branch = useRepo(id)?.info.branch;
|
|
return (
|
|
<div className="flex items-center gap-2">
|
|
<GitBranch className="w-4 h-4" />
|
|
{branch}
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
|
|
Actions: { BuildRepo, PullRepo, CloneRepo },
|
|
|
|
Page: {},
|
|
|
|
Config: RepoConfig,
|
|
|
|
DangerZone: ({ id }) => (
|
|
<>
|
|
<RenameResource type="Repo" id={id} />
|
|
<DeleteResource type="Repo" id={id} />
|
|
</>
|
|
),
|
|
|
|
ResourcePageHeader: ({ id }) => {
|
|
const repo = useRepo(id);
|
|
|
|
return (
|
|
<ResourcePageHeader
|
|
intent={repo_state_intention(repo?.info.state)}
|
|
icon={<RepoIcon id={id} size={8} />}
|
|
name={repo?.name}
|
|
state={repo?.info.state}
|
|
status=""
|
|
/>
|
|
);
|
|
},
|
|
};
|