mirror of
https://github.com/moghtech/komodo.git
synced 2026-04-29 12:43:26 -05:00
add github webhook copiers
This commit is contained in:
@@ -19,7 +19,7 @@ import {
|
||||
import { Textarea } from "@ui/textarea";
|
||||
import { MinusCircle, PlusCircle } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { LabelsConfig, ResourceSelector } from "../common";
|
||||
import { CopyGithubWebhook, LabelsConfig, ResourceSelector } from "../common";
|
||||
|
||||
export const BuildConfig = ({ id }: { id: string }) => {
|
||||
const perms = useRead("GetPermissionLevel", {
|
||||
@@ -155,6 +155,13 @@ export const BuildConfig = ({ id }: { id: string }) => {
|
||||
/>
|
||||
),
|
||||
},
|
||||
github_webhooks: {
|
||||
["build" as any]: () => (
|
||||
<ConfigItem label="Build">
|
||||
<CopyGithubWebhook path={`/build/${id}`} />
|
||||
</ConfigItem>
|
||||
),
|
||||
},
|
||||
},
|
||||
"Build Args": {
|
||||
"Build Args": {
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
ActionButton,
|
||||
ActionWithDialog,
|
||||
ConfirmButton,
|
||||
CopyButton,
|
||||
TextUpdateMenu,
|
||||
} from "@components/util";
|
||||
import { useInvalidate, useRead, useWrite } from "@lib/hooks";
|
||||
@@ -309,3 +310,18 @@ export const LabelsConfig = ({
|
||||
/>
|
||||
</ConfigItem>
|
||||
);
|
||||
|
||||
export const CopyGithubWebhook = ({
|
||||
path,
|
||||
}: {
|
||||
path: string;
|
||||
}) => {
|
||||
const base_url = useRead("GetCoreInfo", {}).data?.github_webhook_base_url;
|
||||
const url = base_url + "/listener/github" + path;
|
||||
return (
|
||||
<div className="flex gap-2 items-center">
|
||||
<Input className="w-[400px] max-w-[70vw]" value={url} disabled />
|
||||
<CopyButton content={url} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -39,7 +39,11 @@ import {
|
||||
Trash2,
|
||||
} from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { ResourceSelector } from "../common";
|
||||
import { CopyGithubWebhook, ResourceSelector } from "../common";
|
||||
import { ConfigItem } from "@components/config/util";
|
||||
import { Input } from "@ui/input";
|
||||
import { Section } from "@components/layouts";
|
||||
import { Card, CardHeader } from "@ui/card";
|
||||
|
||||
export const ProcedureConfig = ({ id }: { id: string }) => {
|
||||
const procedure = useRead("GetProcedure", { procedure: id }).data;
|
||||
@@ -55,6 +59,7 @@ const ProcedureConfigInner = ({
|
||||
const perms = useRead("GetPermissionLevel", {
|
||||
target: { type: "Procedure", id: procedure._id?.$oid! },
|
||||
}).data;
|
||||
const [branch, setBranch] = useState("main");
|
||||
const [config, setConfig] = useState<Partial<Types.ProcedureConfig>>({});
|
||||
const { mutateAsync } = useWrite("UpdateProcedure");
|
||||
const executions = config.executions || procedure.config.executions || [];
|
||||
@@ -62,276 +67,301 @@ const ProcedureConfigInner = ({
|
||||
const disabled = perms !== Types.PermissionLevel.Write;
|
||||
|
||||
return (
|
||||
<ConfigLayout
|
||||
disabled={disabled}
|
||||
config={config as any}
|
||||
onConfirm={async () => {
|
||||
await mutateAsync({ id: procedure._id!.$oid, config });
|
||||
setConfig({});
|
||||
}}
|
||||
onReset={() => setConfig(procedure.config)}
|
||||
selector={
|
||||
<div className="flex gap-2 items-center text-sm">
|
||||
Procedure Type:
|
||||
<Select
|
||||
value={config.procedure_type || procedure.config.procedure_type}
|
||||
onValueChange={(type) =>
|
||||
setConfig({ ...config, procedure_type: type as any })
|
||||
}
|
||||
disabled={disabled}
|
||||
>
|
||||
<SelectTrigger className="w-32 capitalize" disabled={disabled}>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="w-32">
|
||||
{["Sequence", "Parallel"].map((key) => (
|
||||
<SelectItem value={key} key={key} className="capitalize">
|
||||
{key}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="grid gap-4">
|
||||
<div className="text-muted-foreground">
|
||||
{config.procedure_type === Types.ProcedureType.Parallel
|
||||
? "Type Parallel: All of these executions will be started at the same time"
|
||||
: "Type Sequence: These executions will be started only after the previous one finishes"}
|
||||
</div>
|
||||
<DataTable
|
||||
tableKey="procedure-stages"
|
||||
data={executions}
|
||||
noResults={
|
||||
<Button
|
||||
onClick={() =>
|
||||
setConfig({
|
||||
...config,
|
||||
executions: [default_enabled_execution()],
|
||||
})
|
||||
<>
|
||||
<ConfigLayout
|
||||
disabled={disabled}
|
||||
config={config as any}
|
||||
onConfirm={async () => {
|
||||
await mutateAsync({ id: procedure._id!.$oid, config });
|
||||
setConfig({});
|
||||
}}
|
||||
onReset={() => setConfig(procedure.config)}
|
||||
selector={
|
||||
<div className="flex gap-2 items-center text-sm">
|
||||
Procedure Type:
|
||||
<Select
|
||||
value={config.procedure_type || procedure.config.procedure_type}
|
||||
onValueChange={(type) =>
|
||||
setConfig({ ...config, procedure_type: type as any })
|
||||
}
|
||||
disabled={disabled}
|
||||
>
|
||||
Create Stage
|
||||
</Button>
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
header: "Enabled",
|
||||
cell: ({
|
||||
row: {
|
||||
original: { enabled },
|
||||
index,
|
||||
},
|
||||
}) => {
|
||||
return (
|
||||
<Switch
|
||||
checked={enabled}
|
||||
onClick={() =>
|
||||
setConfig({
|
||||
...config,
|
||||
executions: executions.map((item, i) =>
|
||||
i === index ? { ...item, enabled: !enabled } : item
|
||||
),
|
||||
})
|
||||
}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Execution",
|
||||
cell: ({ row: { original, index } }) => (
|
||||
<ExecutionTypeSelector
|
||||
disabled={disabled}
|
||||
type={original.execution.type}
|
||||
onSelect={(type) =>
|
||||
setConfig({
|
||||
...config,
|
||||
executions: executions.map((item, i) =>
|
||||
i === index
|
||||
? ({
|
||||
...item,
|
||||
execution: {
|
||||
type,
|
||||
params:
|
||||
TARGET_COMPONENTS[
|
||||
type as Types.Execution["type"]
|
||||
].params,
|
||||
},
|
||||
} as Types.EnabledExecution)
|
||||
: item
|
||||
),
|
||||
})
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: "Target",
|
||||
cell: ({
|
||||
row: {
|
||||
original: {
|
||||
execution: { type, params },
|
||||
<SelectTrigger className="w-32 capitalize" disabled={disabled}>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="w-32">
|
||||
{["Sequence", "Parallel"].map((key) => (
|
||||
<SelectItem value={key} key={key} className="capitalize">
|
||||
{key}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="grid gap-4">
|
||||
<div className="text-muted-foreground">
|
||||
{config.procedure_type === Types.ProcedureType.Parallel
|
||||
? "Type Parallel: All of these executions will be started at the same time"
|
||||
: "Type Sequence: These executions will be started only after the previous one finishes"}
|
||||
</div>
|
||||
<DataTable
|
||||
tableKey="procedure-stages"
|
||||
data={executions}
|
||||
noResults={
|
||||
<Button
|
||||
onClick={() =>
|
||||
setConfig({
|
||||
...config,
|
||||
executions: [default_enabled_execution()],
|
||||
})
|
||||
}
|
||||
disabled={disabled}
|
||||
>
|
||||
Create Stage
|
||||
</Button>
|
||||
}
|
||||
columns={[
|
||||
{
|
||||
header: "Enabled",
|
||||
cell: ({
|
||||
row: {
|
||||
original: { enabled },
|
||||
index,
|
||||
},
|
||||
index,
|
||||
}) => {
|
||||
return (
|
||||
<Switch
|
||||
checked={enabled}
|
||||
onClick={() =>
|
||||
setConfig({
|
||||
...config,
|
||||
executions: executions.map((item, i) =>
|
||||
i === index ? { ...item, enabled: !enabled } : item
|
||||
),
|
||||
})
|
||||
}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
},
|
||||
}) => {
|
||||
const Component = TARGET_COMPONENTS[type].Component;
|
||||
return (
|
||||
<Component
|
||||
},
|
||||
{
|
||||
header: "Execution",
|
||||
cell: ({ row: { original, index } }) => (
|
||||
<ExecutionTypeSelector
|
||||
disabled={disabled}
|
||||
params={params as any}
|
||||
setParams={(params: any) =>
|
||||
type={original.execution.type}
|
||||
onSelect={(type) =>
|
||||
setConfig({
|
||||
...config,
|
||||
executions: executions.map((item, i) =>
|
||||
i === index
|
||||
? {
|
||||
? ({
|
||||
...item,
|
||||
execution: { type, params },
|
||||
}
|
||||
execution: {
|
||||
type,
|
||||
params:
|
||||
TARGET_COMPONENTS[
|
||||
type as Types.Execution["type"]
|
||||
].params,
|
||||
},
|
||||
} as Types.EnabledExecution)
|
||||
: item
|
||||
),
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Modify",
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild disabled={disabled}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="h-8 w-8 p-0"
|
||||
disabled={disabled}
|
||||
>
|
||||
<span className="sr-only">Open menu</span>
|
||||
<DotsHorizontalIcon className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
{row.index ? (
|
||||
{
|
||||
header: "Target",
|
||||
cell: ({
|
||||
row: {
|
||||
original: {
|
||||
execution: { type, params },
|
||||
},
|
||||
index,
|
||||
},
|
||||
}) => {
|
||||
const Component = TARGET_COMPONENTS[type].Component;
|
||||
return (
|
||||
<Component
|
||||
disabled={disabled}
|
||||
params={params as any}
|
||||
setParams={(params: any) =>
|
||||
setConfig({
|
||||
...config,
|
||||
executions: executions.map((item, i) =>
|
||||
i === index
|
||||
? {
|
||||
...item,
|
||||
execution: { type, params },
|
||||
}
|
||||
: item
|
||||
),
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Modify",
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild disabled={disabled}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="h-8 w-8 p-0"
|
||||
disabled={disabled}
|
||||
>
|
||||
<span className="sr-only">Open menu</span>
|
||||
<DotsHorizontalIcon className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
{row.index ? (
|
||||
<DropdownMenuItem
|
||||
className="flex gap-4 justify-between cursor-pointer"
|
||||
onClick={() =>
|
||||
setConfig({
|
||||
...config,
|
||||
executions: executions.map((item, i) => {
|
||||
// Make sure its not the first row
|
||||
if (i === row.index && row.index !== 0) {
|
||||
return executions[row.index - 1];
|
||||
} else if (i === row.index - 1) {
|
||||
// Reverse the entry, moving this row "Up"
|
||||
return executions[row.index];
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
}),
|
||||
})
|
||||
}
|
||||
>
|
||||
Move Up <ArrowUp className="w-4 h-4" />
|
||||
</DropdownMenuItem>
|
||||
) : undefined}
|
||||
{row.index < executions.length - 1 && (
|
||||
<DropdownMenuItem
|
||||
className="flex gap-4 justify-between cursor-pointer"
|
||||
onClick={() =>
|
||||
setConfig({
|
||||
...config,
|
||||
executions: executions.map((item, i) => {
|
||||
// The index also cannot be the last index, which cannot be moved down
|
||||
if (
|
||||
i === row.index &&
|
||||
row.index !== executions.length - 1
|
||||
) {
|
||||
return executions[row.index + 1];
|
||||
} else if (i === row.index + 1) {
|
||||
// Move the row "Down"
|
||||
return executions[row.index];
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
}),
|
||||
})
|
||||
}
|
||||
>
|
||||
Move Down <ArrowDown className="w-4 h-4" />
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="flex gap-4 justify-between cursor-pointer"
|
||||
onClick={() =>
|
||||
setConfig({
|
||||
...config,
|
||||
executions: executions.map((item, i) => {
|
||||
// Make sure its not the first row
|
||||
if (i === row.index && row.index !== 0) {
|
||||
return executions[row.index - 1];
|
||||
} else if (i === row.index - 1) {
|
||||
// Reverse the entry, moving this row "Up"
|
||||
return executions[row.index];
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
}),
|
||||
executions: [
|
||||
...executions.slice(0, row.index),
|
||||
default_enabled_execution(),
|
||||
...executions.slice(row.index),
|
||||
],
|
||||
})
|
||||
}
|
||||
>
|
||||
Move Up <ArrowUp className="w-4 h-4" />
|
||||
Insert Above{" "}
|
||||
<div className="flex">
|
||||
<ArrowUp className="w-4 h-4" />
|
||||
<Plus className="w-4 h-4" />
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
) : undefined}
|
||||
{row.index < executions.length - 1 && (
|
||||
<DropdownMenuItem
|
||||
className="flex gap-4 justify-between cursor-pointer"
|
||||
onClick={() =>
|
||||
setConfig({
|
||||
...config,
|
||||
executions: executions.map((item, i) => {
|
||||
// The index also cannot be the last index, which cannot be moved down
|
||||
if (
|
||||
i === row.index &&
|
||||
row.index !== executions.length - 1
|
||||
) {
|
||||
return executions[row.index + 1];
|
||||
} else if (i === row.index + 1) {
|
||||
// Move the row "Down"
|
||||
return executions[row.index];
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
}),
|
||||
executions: [
|
||||
...executions.slice(0, row.index + 1),
|
||||
default_enabled_execution(),
|
||||
...executions.slice(row.index + 1),
|
||||
],
|
||||
})
|
||||
}
|
||||
>
|
||||
Move Down <ArrowDown className="w-4 h-4" />
|
||||
Insert Below{" "}
|
||||
<div className="flex">
|
||||
<ArrowDown className="w-4 h-4" />
|
||||
<Plus className="w-4 h-4" />
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="flex gap-4 justify-between cursor-pointer"
|
||||
onClick={() =>
|
||||
setConfig({
|
||||
...config,
|
||||
executions: [
|
||||
...executions.slice(0, row.index),
|
||||
default_enabled_execution(),
|
||||
...executions.slice(row.index),
|
||||
],
|
||||
})
|
||||
}
|
||||
>
|
||||
Insert Above{" "}
|
||||
<div className="flex">
|
||||
<ArrowUp className="w-4 h-4" />
|
||||
<Plus className="w-4 h-4" />
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="flex gap-4 justify-between cursor-pointer"
|
||||
onClick={() =>
|
||||
setConfig({
|
||||
...config,
|
||||
executions: [
|
||||
...executions.slice(0, row.index + 1),
|
||||
default_enabled_execution(),
|
||||
...executions.slice(row.index + 1),
|
||||
],
|
||||
})
|
||||
}
|
||||
>
|
||||
Insert Below{" "}
|
||||
<div className="flex">
|
||||
<ArrowDown className="w-4 h-4" />
|
||||
<Plus className="w-4 h-4" />
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Delete",
|
||||
cell: ({ row: { index } }) => (
|
||||
<ConfirmButton
|
||||
title="Delete Row"
|
||||
icon={<Trash2 className="w-4 h-4" />}
|
||||
onClick={() =>
|
||||
setConfig({
|
||||
...config,
|
||||
executions: executions.filter((_, i) => i !== index),
|
||||
})
|
||||
}
|
||||
disabled={disabled}
|
||||
{
|
||||
header: "Delete",
|
||||
cell: ({ row: { index } }) => (
|
||||
<ConfirmButton
|
||||
title="Delete Row"
|
||||
icon={<Trash2 className="w-4 h-4" />}
|
||||
onClick={() =>
|
||||
setConfig({
|
||||
...config,
|
||||
executions: executions.filter((_, i) => i !== index),
|
||||
})
|
||||
}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</ConfigLayout>
|
||||
<Section>
|
||||
<Card>
|
||||
<CardHeader className="p-4">
|
||||
<ConfigItem label="Github Webhook" className="items-start">
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="text-nowrap text-muted-foreground">
|
||||
Listen on branch:
|
||||
</div>
|
||||
<Input
|
||||
placeholder="Branch"
|
||||
value={branch}
|
||||
onChange={(e) => setBranch(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<CopyGithubWebhook
|
||||
path={`/procedure/${procedure._id?.$oid!}/${branch}`}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</ConfigLayout>
|
||||
</div>
|
||||
</ConfigItem>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</Section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
SelectValue,
|
||||
} from "@ui/select";
|
||||
import { useState } from "react";
|
||||
import { ResourceSelector } from "../common";
|
||||
import { CopyGithubWebhook, ResourceSelector } from "../common";
|
||||
|
||||
export const RepoConfig = ({ id }: { id: string }) => {
|
||||
const perms = useRead("GetPermissionLevel", {
|
||||
@@ -76,6 +76,18 @@ export const RepoConfig = ({ id }: { id: string }) => {
|
||||
/>
|
||||
),
|
||||
},
|
||||
github_webhooks: {
|
||||
["clone" as any]: () => (
|
||||
<ConfigItem label="Clone">
|
||||
<CopyGithubWebhook path={`/repo/${id}/clone`} />
|
||||
</ConfigItem>
|
||||
),
|
||||
["pull" as any]: () => (
|
||||
<ConfigItem label="Pull">
|
||||
<CopyGithubWebhook path={`/repo/${id}/pull`} />
|
||||
</ConfigItem>
|
||||
),
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user