diff --git a/apps/dokploy/__test__/drop/drop.test.ts b/apps/dokploy/__test__/drop/drop.test.ts index 301278dc3..a9bc178a2 100644 --- a/apps/dokploy/__test__/drop/drop.test.ts +++ b/apps/dokploy/__test__/drop/drop.test.ts @@ -27,6 +27,7 @@ if (typeof window === "undefined") { const baseApp: ApplicationNested = { railpackVersion: "0.2.2", applicationId: "", + previewLabels: [], herokuVersion: "", giteaBranch: "", giteaBuildPath: "", diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts index 8d9f78aba..ff8a99620 100644 --- a/apps/dokploy/__test__/traefik/traefik.test.ts +++ b/apps/dokploy/__test__/traefik/traefik.test.ts @@ -6,6 +6,7 @@ const baseApp: ApplicationNested = { railpackVersion: "0.2.2", rollbackActive: false, applicationId: "", + previewLabels: [], herokuVersion: "", giteaRepository: "", giteaOwner: "", diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx index 3605480a0..16c916d93 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx @@ -1,9 +1,10 @@ import { zodResolver } from "@hookform/resolvers/zod"; -import { Settings2 } from "lucide-react"; +import { HelpCircle, Plus, Settings2, X } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -33,6 +34,12 @@ import { SelectValue, } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { api } from "@/utils/api"; const schema = z @@ -42,6 +49,7 @@ const schema = z wildcardDomain: z.string(), port: z.number(), previewLimit: z.number(), + previewLabels: z.array(z.string()).optional(), previewHttps: z.boolean(), previewPath: z.string(), previewCertificateType: z.enum(["letsencrypt", "none", "custom"]), @@ -81,6 +89,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { wildcardDomain: "*.traefik.me", port: 3000, previewLimit: 3, + previewLabels: [], previewHttps: false, previewPath: "/", previewCertificateType: "none", @@ -102,6 +111,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { buildArgs: data.previewBuildArgs || "", wildcardDomain: data.previewWildcard || "*.traefik.me", port: data.previewPort || 3000, + previewLabels: data.previewLabels || [], previewLimit: data.previewLimit || 3, previewHttps: data.previewHttps || false, previewPath: data.previewPath || "/", @@ -119,6 +129,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { previewBuildArgs: formData.buildArgs, previewWildcard: formData.wildcardDomain, previewPort: formData.port, + previewLabels: formData.previewLabels, applicationId, previewLimit: formData.previewLimit, previewHttps: formData.previewHttps, @@ -200,6 +211,90 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { )} /> + ( + +
+ Preview Labels + + + + + + +

+ Add a labels that will trigger a preview + deployment for a pull request. If no labels + are specified, all pull requests will trigger + a preview deployment. +

+
+
+
+
+
+ {field.value?.map((label, index) => ( + + {label} + { + const newLabels = [...(field.value || [])]; + newLabels.splice(index, 1); + field.onChange(newLabels); + }} + /> + + ))} +
+
+ + { + if (e.key === "Enter") { + e.preventDefault(); + const input = e.currentTarget; + const label = input.value.trim(); + if (label) { + field.onChange([ + ...(field.value || []), + label, + ]); + input.value = ""; + } + } + }} + /> + + +
+ +
+ )} + /> 0) { + let hasLabel = false; + const labels = githubBody?.pull_request?.labels; + for (const label of labels) { + if (app?.previewLabels?.includes(label.name)) { + hasLabel = true; + break; + } + } + if (!hasLabel) continue; + } + const previewLimit = app?.previewLimit || 0; if (app?.previewDeployments?.length > previewLimit) { continue; diff --git a/packages/server/src/db/schema/application.ts b/packages/server/src/db/schema/application.ts index 1b03989ef..198611a77 100644 --- a/packages/server/src/db/schema/application.ts +++ b/packages/server/src/db/schema/application.ts @@ -79,6 +79,7 @@ export const applications = pgTable("application", { previewEnv: text("previewEnv"), watchPaths: text("watchPaths").array(), previewBuildArgs: text("previewBuildArgs"), + previewLabels: text("previewLabels").array(), previewWildcard: text("previewWildcard"), previewPort: integer("previewPort").default(3000), previewHttps: boolean("previewHttps").notNull().default(false), @@ -308,6 +309,7 @@ const createSchema = createInsertSchema(applications, { previewCertificateType: z.enum(["letsencrypt", "none", "custom"]).optional(), previewRequireCollaboratorPermissions: z.boolean().optional(), watchPaths: z.array(z.string()).optional(), + previewLabels: z.array(z.string()).optional(), cleanCache: z.boolean().optional(), }); diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts index 5427291ef..17c48d0ff 100644 --- a/packages/server/src/setup/traefik-setup.ts +++ b/packages/server/src/setup/traefik-setup.ts @@ -15,7 +15,6 @@ export const TRAEFIK_HTTP3_PORT = Number.parseInt(process.env.TRAEFIK_HTTP3_PORT!, 10) || 443; export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.5.0"; - export interface TraefikOptions { env?: string[]; serverId?: string;